From afb1ebf42e6094ecd79862edee4b457bdf51b1ad Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 26 Jul 2016 23:19:41 -0700 Subject: [PATCH 001/332] Revolutionizing ao --- .../src/AmbientOcclusionEffect.cpp | 207 ++++++++++++------ .../render-utils/src/AmbientOcclusionEffect.h | 51 ++++- .../render-utils/src/DebugDeferredBuffer.cpp | 17 +- .../render-utils/src/DebugDeferredBuffer.h | 3 +- .../src/DeferredLightingEffect.cpp | 11 +- .../render-utils/src/DeferredLightingEffect.h | 5 +- .../render-utils/src/FramebufferCache.cpp | 70 ------ libraries/render-utils/src/FramebufferCache.h | 17 -- .../render-utils/src/RenderDeferredTask.cpp | 7 +- .../render-utils/src/SurfaceGeometryPass.cpp | 2 +- libraries/render-utils/src/ssao.slh | 3 + .../render-utils/src/ssao_makeOcclusion.slf | 18 +- 12 files changed, 221 insertions(+), 190 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 4b283731d2..929f4896b3 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -33,6 +33,91 @@ #include "ssao_makeHorizontalBlur_frag.h" #include "ssao_makeVerticalBlur_frag.h" + +AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { +} + +void AmbientOcclusionFramebuffer::updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer) { + //If the depth buffer or size changed, we need to delete our FBOs + bool reset = false; + if ((_linearDepthTexture != linearDepthBuffer)) { + _linearDepthTexture = linearDepthBuffer; + reset = true; + } + if (_linearDepthTexture) { + auto newFrameSize = glm::ivec2(_linearDepthTexture->getDimensions()); + if (_frameSize != newFrameSize) { + _frameSize = newFrameSize; + reset = true; + } + } + + if (reset) { + clear(); + } +} + +void AmbientOcclusionFramebuffer::clear() { + _occlusionFramebuffer.reset(); + _occlusionTexture.reset(); + _occlusionBlurredFramebuffer.reset(); + _occlusionBlurredTexture.reset(); +} + +gpu::TexturePointer AmbientOcclusionFramebuffer::getLinearDepthTexture() { + return _linearDepthTexture; +} + +void AmbientOcclusionFramebuffer::allocate() { + + auto width = _frameSize.x; + auto height = _frameSize.y; + + _occlusionTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); + + _occlusionBlurredTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); +} + +gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionFramebuffer() { + if (!_occlusionFramebuffer) { + allocate(); + } + return _occlusionFramebuffer; +} + +gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionTexture() { + if (!_occlusionTexture) { + allocate(); + } + return _occlusionTexture; +} + +gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionBlurredFramebuffer() { + if (!_occlusionBlurredFramebuffer) { + allocate(); + } + return _occlusionBlurredFramebuffer; +} + +gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionBlurredTexture() { + if (!_occlusionBlurredTexture) { + allocate(); + } + return _occlusionBlurredTexture; +} + +void AmbientOcclusionFramebuffer::setResolutionLevel(int resolutionLevel) { + if (resolutionLevel != getResolutionLevel()) { + clear(); + _resolutionLevel = resolutionLevel; + } +} + + class GaussianDistribution { public: @@ -90,8 +175,7 @@ public: const int AmbientOcclusionEffect_FrameTransformSlot = 0; const int AmbientOcclusionEffect_ParamsSlot = 1; -const int AmbientOcclusionEffect_DepthMapSlot = 0; -const int AmbientOcclusionEffect_PyramidMapSlot = 0; +const int AmbientOcclusionEffect_LinearDepthMapSlot = 0; const int AmbientOcclusionEffect_OcclusionMapSlot = 0; AmbientOcclusionEffect::AmbientOcclusionEffect() { @@ -147,13 +231,22 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.y = 1.0f / config.numSamples; } + if (!_framebuffer) { + _framebuffer = std::make_shared(); + } + + _framebuffer->setResolutionLevel(config.resolutionLevel); + if (config.resolutionLevel != getResolutionLevel()) { + _parametersBuffer.edit().resolutionInfo.w = config.resolutionLevel; + } + const auto& resolutionLevel = config.resolutionLevel; if (resolutionLevel != getResolutionLevel()) { auto& current = _parametersBuffer.edit().resolutionInfo; current.x = (float)resolutionLevel; // Communicate the change to the Framebuffer cache - DependencyManager::get()->setAmbientOcclusionResolutionLevel(resolutionLevel); + // DependencyManager::get()->setAmbientOcclusionResolutionLevel(resolutionLevel); } if (config.blurRadius != getBlurRadius()) { @@ -177,32 +270,6 @@ void AmbientOcclusionEffect::configure(const Config& config) { } } -const gpu::PipelinePointer& AmbientOcclusionEffect::getPyramidPipeline() { - if (!_pyramidPipeline) { - auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(ssao_makePyramid_frag)); - gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); - - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionFrameTransformBuffer"), AmbientOcclusionEffect_FrameTransformSlot)); - slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot)); - slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), AmbientOcclusionEffect_DepthMapSlot)); - gpu::Shader::makeProgram(*program, slotBindings); - - - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - - // Stencil test the pyramid passe for objects pixels only, not the background - state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); - - state->setColorWriteMask(true, false, false, false); - - // Good to go add the brand new pipeline - _pyramidPipeline = gpu::Pipeline::create(program, state); - } - return _pyramidPipeline; -} - const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { if (!_occlusionPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); @@ -212,7 +279,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionFrameTransformBuffer"), AmbientOcclusionEffect_FrameTransformSlot)); slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot)); - slotBindings.insert(gpu::Shader::Binding(std::string("pyramidMap"), AmbientOcclusionEffect_PyramidMapSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("pyramidMap"), AmbientOcclusionEffect_LinearDepthMapSlot)); gpu::Shader::makeProgram(*program, slotBindings); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); @@ -282,8 +349,7 @@ void AmbientOcclusionEffect::updateGaussianDistribution() { GaussianDistribution::evalSampling(coefs, Parameters::GAUSSIAN_COEFS_LENGTH, getBlurRadius(), getBlurDeviation()); } -void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { -#ifdef FIX_THE_FRAMEBUFFER_CACHE +void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); @@ -294,18 +360,39 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext return; } - auto framebufferCache = DependencyManager::get(); - auto depthBuffer = framebufferCache->getPrimaryDepthTexture(); - auto normalBuffer = framebufferCache->getDeferredNormalTexture(); - auto pyramidFBO = framebufferCache->getDepthPyramidFramebuffer(); - auto occlusionFBO = framebufferCache->getOcclusionFramebuffer(); - auto occlusionBlurredFBO = framebufferCache->getOcclusionBlurredFramebuffer(); + const auto frameTransform = inputs.get0(); + const auto deferredFramebuffer = inputs.get1(); + const auto linearDepthFramebuffer = inputs.get2(); + + auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); + auto normalTexture = deferredFramebuffer->getDeferredNormalTexture(); + auto sourceViewport = args->_viewport; + auto occlusionViewport = sourceViewport; - QSize framebufferSize = framebufferCache->getFrameBufferSize(); - float sMin = args->_viewport.x / (float)framebufferSize.width(); - float sWidth = args->_viewport.z / (float)framebufferSize.width(); - float tMin = args->_viewport.y / (float)framebufferSize.height(); - float tHeight = args->_viewport.w / (float)framebufferSize.height(); + if (!_framebuffer) { + _framebuffer = std::make_shared(); + } + + if (_framebuffer->getResolutionLevel() > 0) { + linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); + normalTexture = linearDepthFramebuffer->getHalfNormalTexture(); + occlusionViewport = occlusionViewport >> _framebuffer->getResolutionLevel(); + } + + _framebuffer->updateLinearDepth(linearDepthTexture); + + + auto occlusionFBO = _framebuffer->getOcclusionFramebuffer(); + auto occlusionBlurredFBO = _framebuffer->getOcclusionBlurredFramebuffer(); + + outputs = _framebuffer; + + auto framebufferSize = _framebuffer->getSourceFrameSize(); + + float sMin = args->_viewport.x / (float)framebufferSize.x; + float sWidth = args->_viewport.z / (float)framebufferSize.x; + float tMin = args->_viewport.y / (float)framebufferSize.y; + float tHeight = args->_viewport.w / (float)framebufferSize.y; auto resolutionLevel = getResolutionLevel(); @@ -341,7 +428,6 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext } - auto pyramidPipeline = getPyramidPipeline(); auto occlusionPipeline = getOcclusionPipeline(); auto firstHBlurPipeline = getHBlurPipeline(); auto lastVBlurPipeline = getVBlurPipeline(); @@ -351,7 +437,7 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext _gpuTimer.begin(batch); - batch.setViewportTransform(args->_viewport); + batch.setViewportTransform(occlusionViewport); batch.setProjectionTransform(glm::mat4()); batch.setViewTransform(Transform()); @@ -363,32 +449,18 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext batch.setUniformBuffer(AmbientOcclusionEffect_FrameTransformSlot, _frameTransformBuffer); batch.setUniformBuffer(AmbientOcclusionEffect_ParamsSlot, _parametersBuffer); - - // Pyramid pass - batch.setFramebuffer(pyramidFBO); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(args->getViewFrustum().getFarClip(), 0.0f, 0.0f, 0.0f)); - batch.setPipeline(pyramidPipeline); - batch.setResourceTexture(AmbientOcclusionEffect_DepthMapSlot, depthBuffer); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - // Make pyramid mips - batch.generateTextureMips(pyramidFBO->getRenderBuffer(0)); - - // Adjust Viewport for rendering resolution - if (resolutionLevel > 0) { - glm::ivec4 viewport(args->_viewport.x, args->_viewport.y, args->_viewport.z >> resolutionLevel, args->_viewport.w >> resolutionLevel); - batch.setViewportTransform(viewport); - } - + + // batch.generateTextureMips(linearDepthTexture); + // Occlusion pass batch.setFramebuffer(occlusionFBO); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(1.0f)); batch.setPipeline(occlusionPipeline); - batch.setResourceTexture(AmbientOcclusionEffect_PyramidMapSlot, pyramidFBO->getRenderBuffer(0)); + batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, linearDepthTexture); batch.draw(gpu::TRIANGLE_STRIP, 4); - if (getBlurRadius() > 0) { + /* if (getBlurRadius() > 0) { // Blur 1st pass batch.setFramebuffer(occlusionBlurredFBO); batch.setPipeline(firstHBlurPipeline); @@ -400,12 +472,15 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext batch.setPipeline(lastVBlurPipeline); batch.setResourceTexture(AmbientOcclusionEffect_OcclusionMapSlot, occlusionBlurredFBO->getRenderBuffer(0)); batch.draw(gpu::TRIANGLE_STRIP, 4); - } + }*/ + + + batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, nullptr); + batch.setResourceTexture(AmbientOcclusionEffect_OcclusionMapSlot, nullptr); _gpuTimer.end(batch); }); // Update the timer std::static_pointer_cast(renderContext->jobConfig)->gpuTime = _gpuTimer.getAverage(); -#endif } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 717c9dc4fc..8c458a5879 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -16,6 +16,47 @@ #include "render/DrawTask.h" +#include "DeferredFrameTransform.h" +#include "DeferredFramebuffer.h" +#include "SurfaceGeometryPass.h" + +class AmbientOcclusionFramebuffer { +public: + AmbientOcclusionFramebuffer(); + + gpu::FramebufferPointer getOcclusionFramebuffer(); + gpu::TexturePointer getOcclusionTexture(); + + gpu::FramebufferPointer getOcclusionBlurredFramebuffer(); + gpu::TexturePointer getOcclusionBlurredTexture(); + + // Update the source framebuffer size which will drive the allocation of all the other resources. + void updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer); + gpu::TexturePointer getLinearDepthTexture(); + const glm::ivec2& getSourceFrameSize() const { return _frameSize; } + + void setResolutionLevel(int level); + int getResolutionLevel() const { return _resolutionLevel; } + +protected: + void clear(); + void allocate(); + + gpu::TexturePointer _linearDepthTexture; + + gpu::FramebufferPointer _occlusionFramebuffer; + gpu::TexturePointer _occlusionTexture; + + gpu::FramebufferPointer _occlusionBlurredFramebuffer; + gpu::TexturePointer _occlusionBlurredTexture; + + + glm::ivec2 _frameSize; + int _resolutionLevel{ 0 }; +}; + +using AmbientOcclusionFramebufferPointer = std::shared_ptr; + class AmbientOcclusionEffectConfig : public render::Job::Config::Persistent { Q_OBJECT Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty) @@ -67,13 +108,15 @@ signals: class AmbientOcclusionEffect { public: + using Inputs = render::VaryingSet3; + using Outputs = AmbientOcclusionFramebufferPointer; using Config = AmbientOcclusionEffectConfig; - using JobModel = render::Job::Model; + using JobModel = render::Job::ModelIO; AmbientOcclusionEffect(); void configure(const Config& config); - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); float getRadius() const { return _parametersBuffer.get().radiusInfo.x; } float getObscuranceLevel() const { return _parametersBuffer.get().radiusInfo.w; } @@ -131,16 +174,16 @@ private: }; gpu::BufferView _parametersBuffer; - const gpu::PipelinePointer& getPyramidPipeline(); const gpu::PipelinePointer& getOcclusionPipeline(); const gpu::PipelinePointer& getHBlurPipeline(); // first const gpu::PipelinePointer& getVBlurPipeline(); // second - gpu::PipelinePointer _pyramidPipeline; gpu::PipelinePointer _occlusionPipeline; gpu::PipelinePointer _hBlurPipeline; gpu::PipelinePointer _vBlurPipeline; + AmbientOcclusionFramebufferPointer _framebuffer; + gpu::RangeTimer _gpuTimer; }; diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 8118df5435..ed4eba972f 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -202,14 +202,14 @@ static const std::string DEFAULT_DEBUG_SCATTERING_SHADER{ static const std::string DEFAULT_AMBIENT_OCCLUSION_SHADER{ "vec4 getFragmentColor() {" - " return vec4(vec3(texture(obscuranceMap, uv).x), 1.0);" + " return vec4(vec3(texture(obscuranceMap, uv).xyz), 1.0);" // When drawing color " return vec4(vec3(texture(occlusionMap, uv).xyz), 1.0);" - // when drawing normal " return vec4(normalize(texture(occlusionMap, uv).xyz * 2.0 - vec3(1.0)), 1.0);" + // when drawing normal" return vec4(normalize(texture(occlusionMap, uv).xyz * 2.0 - vec3(1.0)), 1.0);" " }" }; static const std::string DEFAULT_AMBIENT_OCCLUSION_BLURRED_SHADER{ "vec4 getFragmentColor() {" - " return vec4(vec3(texture(occlusionBlurredMap, uv).x), 1.0);" + " return vec4(vec3(texture(occlusionBlurredMap, uv).xyz), 1.0);" " }" }; @@ -379,6 +379,7 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren auto& deferredFramebuffer = inputs.get0(); auto& linearDepthTarget = inputs.get1(); auto& surfaceGeometryFramebuffer = inputs.get2(); + auto& ambientOcclusionFramebuffer = inputs.get3(); gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); @@ -414,13 +415,9 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren batch.setResourceTexture(Curvature, surfaceGeometryFramebuffer->getCurvatureTexture()); batch.setResourceTexture(DiffusedCurvature, surfaceGeometryFramebuffer->getLowCurvatureTexture()); - if (DependencyManager::get()->isAmbientOcclusionEnabled()) { - batch.setResourceTexture(AmbientOcclusion, framebufferCache->getOcclusionTexture()); - } else { - // need to assign the white texture if ao is off - batch.setResourceTexture(AmbientOcclusion, textureCache->getWhiteTexture()); - } - batch.setResourceTexture(AmbientOcclusionBlurred, framebufferCache->getOcclusionBlurredTexture()); + + batch.setResourceTexture(AmbientOcclusion, ambientOcclusionFramebuffer->getOcclusionTexture()); + batch.setResourceTexture(AmbientOcclusionBlurred, ambientOcclusionFramebuffer->getOcclusionBlurredTexture()); const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); const glm::vec2 bottomLeft(_size.x, _size.y); diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index d6005de349..da78ac081a 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -17,6 +17,7 @@ #include #include "DeferredFramebuffer.h" #include "SurfaceGeometryPass.h" +#include "AmbientOcclusionEffect.h" class DebugDeferredBufferConfig : public render::Job::Config { Q_OBJECT @@ -36,7 +37,7 @@ signals: class DebugDeferredBuffer { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet4; using Config = DebugDeferredBufferConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 6f202f6200..9038b8bada 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -400,7 +400,7 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c const DeferredFramebufferPointer& deferredFramebuffer, const LightingModelPointer& lightingModel, const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer, - const gpu::FramebufferPointer& lowCurvatureNormalFramebuffer, + const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer, const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource) { auto args = renderContext->args; @@ -431,7 +431,7 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c // FIXME: Different render modes should have different tasks if (args->_renderMode == RenderArgs::DEFAULT_RENDER_MODE && deferredLightingEffect->isAmbientOcclusionEnabled()) { - batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, framebufferCache->getOcclusionTexture()); + batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, ambientOcclusionFramebuffer->getOcclusionTexture()); } else { // need to assign the white texture if ao is off batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, textureCache->getWhiteTexture()); @@ -446,9 +446,6 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c // Subsurface scattering specific if (surfaceGeometryFramebuffer) { batch.setResourceTexture(DEFERRED_BUFFER_CURVATURE_UNIT, surfaceGeometryFramebuffer->getCurvatureTexture()); - } - if (lowCurvatureNormalFramebuffer) { - // batch.setResourceTexture(DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT, lowCurvatureNormalFramebuffer->getRenderBuffer(0)); batch.setResourceTexture(DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT, surfaceGeometryFramebuffer->getLowCurvatureTexture()); } if (subsurfaceScatteringResource) { @@ -693,7 +690,7 @@ void RenderDeferred::run(const SceneContextPointer& sceneContext, const RenderCo auto deferredFramebuffer = inputs.get1(); auto lightingModel = inputs.get2(); auto surfaceGeometryFramebuffer = inputs.get3(); - auto lowCurvatureNormalFramebuffer = inputs.get4(); + auto ssaoFramebuffer = inputs.get4(); auto subsurfaceScatteringResource = inputs.get5(); auto args = renderContext->args; @@ -701,7 +698,7 @@ void RenderDeferred::run(const SceneContextPointer& sceneContext, const RenderCo _gpuTimer.begin(batch); }); - setupJob.run(sceneContext, renderContext, deferredTransform, deferredFramebuffer, lightingModel, surfaceGeometryFramebuffer, lowCurvatureNormalFramebuffer, subsurfaceScatteringResource); + setupJob.run(sceneContext, renderContext, deferredTransform, deferredFramebuffer, lightingModel, surfaceGeometryFramebuffer, ssaoFramebuffer, subsurfaceScatteringResource); lightsJob.run(sceneContext, renderContext, deferredTransform, deferredFramebuffer, lightingModel); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index b309299be9..466c58c36e 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -30,6 +30,7 @@ #include "LightStage.h" #include "SurfaceGeometryPass.h" #include "SubsurfaceScattering.h" +#include "AmbientOcclusionEffect.h" class RenderArgs; struct LightLocations; @@ -138,7 +139,7 @@ public: const DeferredFramebufferPointer& deferredFramebuffer, const LightingModelPointer& lightingModel, const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer, - const gpu::FramebufferPointer& lowCurvatureNormalFramebuffer, + const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer, const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource); }; @@ -178,7 +179,7 @@ signals: class RenderDeferred { public: - using Inputs = render::VaryingSet6 < DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, SurfaceGeometryFramebufferPointer, gpu::FramebufferPointer, SubsurfaceScatteringResourcePointer>; + using Inputs = render::VaryingSet6 < DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, SurfaceGeometryFramebufferPointer, AmbientOcclusionFramebufferPointer, SubsurfaceScatteringResourcePointer>; using Config = RenderDeferredConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp index 5375de273a..ff0fb8db50 100644 --- a/libraries/render-utils/src/FramebufferCache.cpp +++ b/libraries/render-utils/src/FramebufferCache.cpp @@ -35,10 +35,6 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) { _frameBufferSize = frameBufferSize; _selfieFramebuffer.reset(); _cachedFramebuffers.clear(); - _occlusionFramebuffer.reset(); - _occlusionTexture.reset(); - _occlusionBlurredFramebuffer.reset(); - _occlusionBlurredTexture.reset(); } } @@ -54,35 +50,6 @@ void FramebufferCache::createPrimaryFramebuffer() { _selfieFramebuffer->setRenderBuffer(0, tex); auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); - - - - resizeAmbientOcclusionBuffers(); -} - - -void FramebufferCache::resizeAmbientOcclusionBuffers() { - _occlusionFramebuffer.reset(); - _occlusionTexture.reset(); - _occlusionBlurredFramebuffer.reset(); - _occlusionBlurredTexture.reset(); - - - auto width = _frameBufferSize.width() >> _AOResolutionLevel; - auto height = _frameBufferSize.height() >> _AOResolutionLevel; - auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGB); - auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); - // auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format - - _occlusionTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); - _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); - // _occlusionFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); - - _occlusionBlurredTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); - _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); - // _occlusionBlurredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); } @@ -107,40 +74,3 @@ gpu::FramebufferPointer FramebufferCache::getSelfieFramebuffer() { } return _selfieFramebuffer; } - -void FramebufferCache::setAmbientOcclusionResolutionLevel(int level) { - const int MAX_AO_RESOLUTION_LEVEL = 4; - level = std::max(0, std::min(level, MAX_AO_RESOLUTION_LEVEL)); - if (level != _AOResolutionLevel) { - _AOResolutionLevel = level; - resizeAmbientOcclusionBuffers(); - } -} - -gpu::FramebufferPointer FramebufferCache::getOcclusionFramebuffer() { - if (!_occlusionFramebuffer) { - resizeAmbientOcclusionBuffers(); - } - return _occlusionFramebuffer; -} - -gpu::TexturePointer FramebufferCache::getOcclusionTexture() { - if (!_occlusionTexture) { - resizeAmbientOcclusionBuffers(); - } - return _occlusionTexture; -} - -gpu::FramebufferPointer FramebufferCache::getOcclusionBlurredFramebuffer() { - if (!_occlusionBlurredFramebuffer) { - resizeAmbientOcclusionBuffers(); - } - return _occlusionBlurredFramebuffer; -} - -gpu::TexturePointer FramebufferCache::getOcclusionBlurredTexture() { - if (!_occlusionBlurredTexture) { - resizeAmbientOcclusionBuffers(); - } - return _occlusionBlurredTexture; -} \ No newline at end of file diff --git a/libraries/render-utils/src/FramebufferCache.h b/libraries/render-utils/src/FramebufferCache.h index d3d26c35b0..2f521d7677 100644 --- a/libraries/render-utils/src/FramebufferCache.h +++ b/libraries/render-utils/src/FramebufferCache.h @@ -30,12 +30,6 @@ public: void setFrameBufferSize(QSize frameBufferSize); const QSize& getFrameBufferSize() const { return _frameBufferSize; } - void setAmbientOcclusionResolutionLevel(int level); - gpu::FramebufferPointer getOcclusionFramebuffer(); - gpu::TexturePointer getOcclusionTexture(); - gpu::FramebufferPointer getOcclusionBlurredFramebuffer(); - gpu::TexturePointer getOcclusionBlurredTexture(); - /// Returns the framebuffer object used to render selfie maps; gpu::FramebufferPointer getSelfieFramebuffer(); @@ -56,18 +50,7 @@ private: gpu::FramebufferPointer _selfieFramebuffer; - gpu::FramebufferPointer _occlusionFramebuffer; - gpu::TexturePointer _occlusionTexture; - - gpu::FramebufferPointer _occlusionBlurredFramebuffer; - gpu::TexturePointer _occlusionBlurredTexture; - QSize _frameBufferSize{ 100, 100 }; - int _AOResolutionLevel = 1; // AO perform at half res - - // Resize/reallocate the buffers used for AO - // the size of the AO buffers is scaled by the AOResolutionScale; - void resizeAmbientOcclusionBuffers(); }; #endif // hifi_FramebufferCache_h diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index bb7adf3f80..afff4bac72 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -141,13 +141,14 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { const auto scatteringResource = addJob("Scattering"); // AO job - addJob("AmbientOcclusion"); + const auto ambientOcclusionInputs = AmbientOcclusionEffect::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget).hasVarying(); + const auto ambientOcclusionFramebuffer = addJob("AmbientOcclusion", ambientOcclusionInputs); // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now. addJob("DrawLight", lights); const auto deferredLightingInputs = RenderDeferred::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, - surfaceGeometryFramebuffer, lowCurvatureNormalFramebuffer, scatteringResource).hasVarying(); + surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource).hasVarying(); // DeferredBuffer is complete, now let's shade it into the LightingBuffer addJob("RenderDeferred", deferredLightingInputs); @@ -178,7 +179,7 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { addJob("DebugScattering", deferredLightingInputs); // Debugging Deferred buffer job - const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, lowCurvatureNormalFramebuffer)); + const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); addJob("DebugDeferredBuffer", debugFramebuffers); // Scene Octree Debuging job diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index fd778d30be..6dc1cb2f9b 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -193,7 +193,7 @@ void LinearDepthPass::run(const render::SceneContextPointer& sceneContext, const batch.setResourceTexture(DepthLinearPass_NormalMapSlot, normalTexture); batch.setPipeline(downsamplePipeline); batch.draw(gpu::TRIANGLE_STRIP, 4); - + _gpuTimer.end(batch); }); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 5fcd224e9d..21e9a5a297 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -76,6 +76,9 @@ bool isStereo() { float getStereoSideWidth() { return float(int(frameTransform._stereoInfo.y) >> getResolutionLevel()); } +float getStereoSideHeight() { + return float(int(frameTransform._pixelInfo.w) >> getResolutionLevel()); +} ivec3 getStereoSideInfo(int xPos) { int sideWidth = int(getStereoSideWidth()); diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 01e44d0bb9..7512d6568c 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -21,7 +21,7 @@ const int MAX_MIP_LEVEL = 5; uniform sampler2D pyramidMap; float getZEye(ivec2 pixel) { - return -texelFetch(pyramidMap, pixel, getResolutionLevel()).x; + return -texelFetch(pyramidMap, pixel, 0/*getResolutionLevel()*/).x; } vec3 evalEyePositionFromZeye(int side, float Zeye, vec2 texcoord) { @@ -112,7 +112,7 @@ void main(void) { // From now on, ssC is the pixel pos in the side ssC.x -= side.y; - vec2 fragPos = (vec2(ssC) + 0.5) / getStereoSideWidth(); + vec2 fragPos = (vec2(ssC) + 0.5) / vec2(getStereoSideWidth(), getStereoSideHeight()); // The position and normal of the pixel fragment in Eye space vec3 Cp = evalEyePositionFromZeye(side.x, Zeye, fragPos); @@ -146,16 +146,16 @@ void main(void) { outFragColor = vec4(packOcclusionDepth(A, CSZToDephtKey(Cp.z)), 1.0); - + // vec3 v = normalize(Q - Cp); + //outFragColor = vec4((v + vec3(1.0))* 0.5, 1.0); + // outFragColor = vec4((Cn + vec3(1.0))* 0.5, 1.0); } From ed811d0431405bf960d6a1bb91813f731e72ff27 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 28 Jul 2016 15:01:21 -0700 Subject: [PATCH 002/332] replace Row with ListView, but otherwise same user-functionality --- interface/resources/qml/AddressBarDialog.qml | 58 ++++++++++---------- interface/resources/qml/hifi/Card.qml | 11 ++-- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 792410c59d..df34295a0f 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -46,7 +46,7 @@ Window { } function goCard(card) { - addressLine.text = card.userStory.name; + addressLine.text = card.path; toggleOrGo(true); } property var allDomains: []; @@ -60,32 +60,27 @@ Window { implicitWidth: backgroundImage.width implicitHeight: backgroundImage.height - Row { - width: backgroundImage.width; + ListModel { id: suggestions } + + ListView { + width: (3 * cardWidth) + (2 * hifi.layout.spacing); + height: cardHeight; + spacing: hifi.layout.spacing; anchors { bottom: backgroundImage.top; bottomMargin: 2 * hifi.layout.spacing; right: backgroundImage.right; - rightMargin: -104; // FIXME } - spacing: hifi.layout.spacing; - Card { - id: s0; + model: suggestions; + orientation: ListView.Horizontal; + delegate: Card { width: cardWidth; height: cardHeight; - goFunction: goCard - } - Card { - id: s1; - width: cardWidth; - height: cardHeight; - goFunction: goCard - } - Card { - id: s2; - width: cardWidth; - height: cardHeight; - goFunction: goCard + goFunction: goCard; + path: model.name; + thumbnail: model.thumbnail; + placeText: model.name; + usersText: model.online_users + ((model.online_users === 1) ? ' user' : ' users') } } @@ -225,6 +220,7 @@ Window { function addPictureToDomain(domainInfo, cb) { // asynchronously add thumbnail and lobby to domainInfo, if available, and cb(error) // This requests data for all the names at once, and just uses the first one to come back. // We might change this to check one at a time, which would be less requests and more latency. + domainInfo.thumbnail = ''; // Regardless of whether we fill it in later, qt models must start with the all values they will have. asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(identity), function (name, icb) { var url = "https://metaverse.highfidelity.com/api/v1/places/" + name; getRequest(url, function (error, json) { @@ -280,17 +276,19 @@ Window { } function filterChoicesByText() { - function fill1(target, data) { + function fill1(targetIndex, data) { if (!data) { - target.visible = false; + if (targetIndex < suggestions.count) { + suggestions.remove(targetIndex); + } return; } console.log('suggestion:', JSON.stringify(data)); - target.userStory = data; - target.image.source = data.lobby || target.defaultPicture; - target.placeText = data.name; - target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); - target.visible = true; + if (suggestions.count <= targetIndex) { + suggestions.append(data); + } else { + suggestions.set(targetIndex, data); + } } var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity); var filtered = !words.length ? suggestionChoices : allDomains.filter(function (domain) { @@ -303,9 +301,9 @@ Window { return text.indexOf(word) >= 0; }); }); - fill1(s0, filtered[0]); - fill1(s1, filtered[1]); - fill1(s2, filtered[2]); + fill1(0, filtered[0]); + fill1(1, filtered[1]); + fill1(2, filtered[2]); } function fillDestinations() { diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 7758c5800a..4996bc9789 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -18,27 +18,28 @@ import "../styles-uit" Rectangle { property var goFunction: null; - property var userStory: null; property alias image: lobby; property alias placeText: place.text; property alias usersText: users.text; property int textPadding: 20; property int textSize: 24; - property string defaultPicture: "../../images/default-domain.gif"; + property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif"); + property string thumbnail: defaultThumbnail; + property string path: ""; HifiConstants { id: hifi } Image { id: lobby; width: parent.width; height: parent.height; - source: defaultPicture; + source: thumbnail || defaultThumbnail; fillMode: Image.PreserveAspectCrop; // source gets filled in later anchors.verticalCenter: parent.verticalCenter; anchors.left: parent.left; onStatusChanged: { if (status == Image.Error) { - console.log("source: " + source + ": failed to load " + JSON.stringify(userStory)); - source = defaultPicture; + console.log("source: " + source + ": failed to load " + path); + source = defaultThumbnail; } } } From ff17761769cd688a7fc01a935e620f39d4d2be2e Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 28 Jul 2016 15:46:32 -0700 Subject: [PATCH 003/332] basic minority-report scroll (fixed initial data, but infinite cards) --- interface/resources/qml/AddressBarDialog.qml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index df34295a0f..6d60ba7749 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -66,6 +66,7 @@ Window { width: (3 * cardWidth) + (2 * hifi.layout.spacing); height: cardHeight; spacing: hifi.layout.spacing; + clip: true; anchors { bottom: backgroundImage.top; bottomMargin: 2 * hifi.layout.spacing; @@ -245,7 +246,7 @@ Window { if (!domainsBaseUrl) { var domainsOptions = [ 'open', // published hours handle now - 'active', // has at least one person connected. FIXME: really want any place that is verified accessible. + // fixme hrs restore 'active', // has at least one person connected. FIXME: really want any place that is verified accessible. // FIXME: really want places I'm allowed in, not just open ones. 'restriction=open', // Not by whitelist, etc. FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. // FIXME add maturity @@ -276,7 +277,8 @@ Window { } function filterChoicesByText() { - function fill1(targetIndex, data) { + function fill1(targetIndex) { + var data = filtered[targetIndex]; if (!data) { if (targetIndex < suggestions.count) { suggestions.remove(targetIndex); @@ -301,9 +303,7 @@ Window { return text.indexOf(word) >= 0; }); }); - fill1(0, filtered[0]); - fill1(1, filtered[1]); - fill1(2, filtered[2]); + for (var index in filtered) { fill1(index); } } function fillDestinations() { @@ -316,13 +316,13 @@ Window { var here = AddressManager.hostname; // don't show where we are now. allDomains = domains.filter(function (domain) { return domain.name !== here; }); // Whittle down suggestions to those that have at least one user, and try to get pictures. - suggestionChoices = allDomains.filter(function (domain) { return domain.online_users; }); + suggestionChoices = allDomains.filter(function (domain) { return true/*fixme hrs restore domain.online_users*/; }); asyncEach(domains, addPictureToDomain, function (error) { if (error) { console.log('place picture query failed:', error); } // Whittle down more by requiring a picture. - suggestionChoices = suggestionChoices.filter(function (domain) { return domain.lobby; }); + // fixme hrs restore suggestionChoices = suggestionChoices.filter(function (domain) { return domain.lobby; }); filterChoicesByText(); }); }); From ae9421f1f69652ee590fbdfd7d92fd587515f8bf Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 29 Jul 2016 15:22:08 -0700 Subject: [PATCH 004/332] smaller user label, and use the word "people" --- interface/resources/qml/AddressBarDialog.qml | 2 +- interface/resources/qml/hifi/Card.qml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 6d60ba7749..ef8f297a75 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -81,7 +81,7 @@ Window { path: model.name; thumbnail: model.thumbnail; placeText: model.name; - usersText: model.online_users + ((model.online_users === 1) ? ' user' : ' users') + usersText: model.online_users + ((model.online_users === 1) ? ' person' : ' people'); } } diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 4996bc9789..ba26c51bf8 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -23,6 +23,7 @@ Rectangle { property alias usersText: users.text; property int textPadding: 20; property int textSize: 24; + property int textSizeSmall: 18; property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif"); property string thumbnail: defaultThumbnail; property string path: ""; @@ -80,7 +81,7 @@ Rectangle { } RalewayRegular { id: users; - size: textSize; + size: textSizeSmall; color: hifi.colors.white; anchors { bottom: parent.bottom; From 611223f0134c14f8d75ed8047866db35130e98be Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 29 Jul 2016 15:49:20 -0700 Subject: [PATCH 005/332] highlight to show that the cards can be clicked --- interface/resources/qml/AddressBarDialog.qml | 3 +++ interface/resources/qml/hifi/Card.qml | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index ef8f297a75..6da55c59d4 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -82,7 +82,10 @@ Window { thumbnail: model.thumbnail; placeText: model.name; usersText: model.online_users + ((model.online_users === 1) ? ' person' : ' people'); + hoverThunk: function () { ListView.view.currentIndex = index; } + unhoverThunk: function () { ListView.view.currentIndex = -1; } } + highlight: Rectangle { color: "transparent"; border.width: 2; border.color: "#1FA5E8"; z: 1; } } Image { diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index ba26c51bf8..5fae628283 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -89,10 +89,15 @@ Rectangle { margins: textPadding; } } + property var hoverThunk: function () { }; + property var unhoverThunk: function () { }; MouseArea { + id: zmouseArea; anchors.fill: parent; acceptedButtons: Qt.LeftButton; onClicked: goFunction(parent); hoverEnabled: true; + onEntered: hoverThunk(); + onExited: unhoverThunk(); } } From cf7450beb72b74fe9b8bd871425b693fd224d64a Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 29 Jul 2016 17:42:41 -0700 Subject: [PATCH 006/332] handControllerGrab and web entity input integration --- interface/src/Application.cpp | 178 ++++++++++------ interface/src/Application.h | 13 +- .../src/display-plugins/CompositorHelper.cpp | 22 ++ .../src/display-plugins/CompositorHelper.h | 8 + .../src/RenderableWebEntityItem.cpp | 142 ++++++------- .../src/RenderableWebEntityItem.h | 5 +- .../system/controllers/handControllerGrab.js | 191 ++++++++++-------- 7 files changed, 340 insertions(+), 219 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5d50a1c9fe..aefbeff1c3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1045,70 +1045,24 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it auto entityScriptingInterface = DependencyManager::get(); connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, - [this, entityScriptingInterface](const EntityItemID& entityItemID, const MouseEvent& event) { - if (_keyboardFocusedItem != entityItemID) { - _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - auto properties = entityScriptingInterface->getEntityProperties(entityItemID); - if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { - auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); - RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); - if (webEntity) { - webEntity->setProxyWindow(_window->windowHandle()); - if (_keyboardMouseDevice->isActive()) { - _keyboardMouseDevice->pluginFocusOutEvent(); - } - _keyboardFocusedItem = entityItemID; - _lastAcceptedKeyPress = usecTimestampNow(); - if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) { - _keyboardFocusHighlight = new Cube3DOverlay(); - _keyboardFocusHighlight->setAlpha(1.0f); - _keyboardFocusHighlight->setBorderSize(1.0f); - _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 }); - _keyboardFocusHighlight->setIsSolid(false); - _keyboardFocusHighlight->setPulseMin(0.5); - _keyboardFocusHighlight->setPulseMax(1.0); - _keyboardFocusHighlight->setColorPulse(1.0); - _keyboardFocusHighlight->setIgnoreRayIntersection(true); - _keyboardFocusHighlight->setDrawInFront(true); - } - _keyboardFocusHighlight->setRotation(webEntity->getRotation()); - _keyboardFocusHighlight->setPosition(webEntity->getPosition()); - _keyboardFocusHighlight->setDimensions(webEntity->getDimensions() * 1.05f); - _keyboardFocusHighlight->setVisible(true); - _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight); - } - } - if (_keyboardFocusedItem == UNKNOWN_ENTITY_ID && _keyboardFocusHighlight) { - _keyboardFocusHighlight->setVisible(false); - } - - } + [this](const EntityItemID& entityItemID, const MouseEvent& event) { + setKeyboardFocusEntity(entityItemID); }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, - [=](const EntityItemID& entityItemID) { - if (entityItemID == _keyboardFocusedItem) { - _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - if (_keyboardFocusHighlight) { - _keyboardFocusHighlight->setVisible(false); - } + connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, [=](const EntityItemID& entityItemID) { + if (entityItemID == _keyboardFocusedItem.get()) { + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); } }); // If the user clicks somewhere where there is NO entity at all, we will release focus connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity, [=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event) { - _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - if (_keyboardFocusHighlight) { - _keyboardFocusHighlight->setVisible(false); - } + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); connect(this, &Application::aboutToQuit, [=]() { - _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - if (_keyboardFocusHighlight) { - _keyboardFocusHighlight->setVisible(false); - } + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); // Add periodic checks to send user activity data @@ -1334,11 +1288,13 @@ void Application::cleanupBeforeQuit() { // FIXME: once we move to shared pointer for the INputDevice we shoud remove this naked delete: _applicationStateDevice.reset(); - if (_keyboardFocusHighlightID > 0) { - getOverlays().deleteOverlay(_keyboardFocusHighlightID); - _keyboardFocusHighlightID = -1; + { + if (_keyboardFocusHighlightID > 0) { + getOverlays().deleteOverlay(_keyboardFocusHighlightID); + _keyboardFocusHighlightID = -1; + } + _keyboardFocusHighlight = nullptr; } - _keyboardFocusHighlight = nullptr; auto nodeList = DependencyManager::get(); @@ -2063,12 +2019,13 @@ bool Application::event(QEvent* event) { return true; } - if (!_keyboardFocusedItem.isInvalidID()) { - switch (event->type()) { + { + if (!_keyboardFocusedItem.get().isInvalidID()) { + switch (event->type()) { case QEvent::KeyPress: case QEvent::KeyRelease: { auto entityScriptingInterface = DependencyManager::get(); - auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem); + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem.get()); RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); if (webEntity && webEntity->getEventHandler()) { event->setAccepted(false); @@ -2083,6 +2040,7 @@ bool Application::event(QEvent* event) { default: break; + } } } @@ -2920,12 +2878,24 @@ void Application::idle(float nsecsElapsed) { _simCounter.increment(); PerformanceTimer perfTimer("idle"); + // Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds - if (!_keyboardFocusedItem.isInvalidID()) { - const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus - quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; - if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { - _keyboardFocusedItem = UNKNOWN_ENTITY_ID; + { + if (!_keyboardFocusedItem.get().isInvalidID()) { + const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus + quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; + if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); + } else { + // update position of highlight overlay + auto entityScriptingInterface = DependencyManager::get(); + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem.get()); + RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); + if (webEntity && _keyboardFocusHighlight) { + _keyboardFocusHighlight->setRotation(webEntity->getRotation()); + _keyboardFocusHighlight->setPosition(webEntity->getPosition()); + } + } } } @@ -3505,6 +3475,84 @@ void Application::rotationModeChanged() const { } } +QUuid Application::getKeyboardFocusEntity() const { + return _keyboardFocusedItem.get(); +} + +void Application::setKeyboardFocusEntity(QUuid id) { + EntityItemID entityItemID(id); + setKeyboardFocusEntity(entityItemID); +} + +void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { + auto entityScriptingInterface = DependencyManager::get(); + if (_keyboardFocusedItem.get() != entityItemID) { + _keyboardFocusedItem.set(UNKNOWN_ENTITY_ID); + auto properties = entityScriptingInterface->getEntityProperties(entityItemID); + if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); + RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); + if (webEntity) { + webEntity->setProxyWindow(_window->windowHandle()); + if (_keyboardMouseDevice->isActive()) { + _keyboardMouseDevice->pluginFocusOutEvent(); + } + _keyboardFocusedItem.set(entityItemID); + _lastAcceptedKeyPress = usecTimestampNow(); + if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) { + _keyboardFocusHighlight = new Cube3DOverlay(); + _keyboardFocusHighlight->setAlpha(1.0f); + _keyboardFocusHighlight->setBorderSize(1.0f); + _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 }); + _keyboardFocusHighlight->setIsSolid(false); + _keyboardFocusHighlight->setPulseMin(0.5); + _keyboardFocusHighlight->setPulseMax(1.0); + _keyboardFocusHighlight->setColorPulse(1.0); + _keyboardFocusHighlight->setIgnoreRayIntersection(true); + _keyboardFocusHighlight->setDrawInFront(true); + } + _keyboardFocusHighlight->setRotation(webEntity->getRotation()); + _keyboardFocusHighlight->setPosition(webEntity->getPosition()); + _keyboardFocusHighlight->setDimensions(webEntity->getDimensions() * 1.05f); + _keyboardFocusHighlight->setVisible(true); + _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight); + } + } + if (_keyboardFocusedItem.get() == UNKNOWN_ENTITY_ID && _keyboardFocusHighlight) { + _keyboardFocusHighlight->setVisible(false); + } + } +} + +void Application::sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint) { + QMouseEvent mouseEvent(QEvent::MouseMove, QPoint(0, 0), QPoint(0, 0), + Qt::NoButton, Qt::NoButton, Qt::NoModifier); + sendEntityMouseEvent(id, mouseEvent, intersectionPoint); +} + +void Application::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint) { + QMouseEvent mouseEvent(QEvent::MouseButtonPress, QPoint(0, 0), QPoint(0, 0), + Qt::LeftButton, Qt::NoButton, Qt::NoModifier); + sendEntityMouseEvent(id, mouseEvent, intersectionPoint); +} + +void Application::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, QPoint(0, 0), QPoint(0, 0), + Qt::LeftButton, Qt::NoButton, Qt::NoModifier); + sendEntityMouseEvent(id, mouseEvent, intersectionPoint); +} + +void Application::sendEntityMouseEvent(const QUuid& id, const QMouseEvent& mouseEvent, const glm::vec3& intersectionPoint) { + auto entityScriptingInterface = DependencyManager::get(); + EntityItemID entityItemID(id); + auto properties = entityScriptingInterface->getEntityProperties(entityItemID); + if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); + RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); + webEntity->handleMouseEvent(mouseEvent, intersectionPoint); + } +} + void Application::updateDialogs(float deltaTime) const { PerformanceTimer perfTimer("updateDialogs"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); diff --git a/interface/src/Application.h b/interface/src/Application.h index 0af65f665f..139efb4ba7 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -43,6 +43,7 @@ #include #include #include +#include #include "avatar/MyAvatar.h" #include "Bookmarks.h" @@ -313,6 +314,16 @@ public slots: static void runTests(); + QUuid getKeyboardFocusEntity() const; // thread-safe + void setKeyboardFocusEntity(QUuid id); + void setKeyboardFocusEntity(EntityItemID entityItemID); + void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); + void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); + void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); + +private: + void sendEntityMouseEvent(const QUuid& id, const QMouseEvent& mouseEvent, const glm::vec3& intersectionPoint); + private slots: void showDesktop(); void clearDomainOctreeDetails(); @@ -526,7 +537,7 @@ private: DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); - EntityItemID _keyboardFocusedItem; + ThreadSafeValueCache _keyboardFocusedItem; quint64 _lastAcceptedKeyPress = 0; bool _isForeground = true; // starts out assumed to be in foreground bool _inPaint = false; diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index 56be8e1cf9..e460fa6648 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -451,3 +451,25 @@ QVariant ReticleInterface::getPosition() const { void ReticleInterface::setPosition(QVariant position) { _compositor->setReticlePosition(vec2FromVariant(position)); } + +QUuid ReticleInterface::getKeyboardFocusEntity() const { + QUuid result; + QMetaObject::invokeMethod(qApp, "getKeyboardFocusEntity", Qt::DirectConnection, Q_RETURN_ARG(QUuid, result)); + return result; +} + +void ReticleInterface::setKeyboardFocusEntity(QUuid id) { + QMetaObject::invokeMethod(qApp, "setKeyboardFocusEntity", Qt::QueuedConnection, Q_ARG(QUuid, id)); +} + +void ReticleInterface::sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityMouseMoveEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +} + +void ReticleInterface::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseDownEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +} + +void ReticleInterface::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseUpEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +} diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h index b0b96d86be..b93ffeb67b 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -181,6 +181,7 @@ class ReticleInterface : public QObject { Q_PROPERTY(bool mouseCaptured READ isMouseCaptured) Q_PROPERTY(bool allowMouseCapture READ getAllowMouseCapture WRITE setAllowMouseCapture) Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay) + Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity) public: ReticleInterface(CompositorHelper* outer) : QObject(outer), _compositor(outer) {} @@ -203,6 +204,13 @@ public: Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); } + Q_INVOKABLE QUuid getKeyboardFocusEntity() const; + Q_INVOKABLE void setKeyboardFocusEntity(QUuid id); + Q_INVOKABLE void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); + // TODO: right mouse + double click + private: CompositorHelper* _compositor; }; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 82c142db37..2b01de9beb 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -74,77 +74,8 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { currentContext->makeCurrent(currentSurface); auto forwardMouseEvent = [=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) { - // Ignore mouse interaction if we're locked - if (this->getLocked()) { - return; - } - - if (event->button() == Qt::MouseButton::RightButton) { - if (event->type() == QEvent::MouseButtonPress) { - const QMouseEvent* mouseEvent = static_cast(event); - _lastPress = toGlm(mouseEvent->pos()); - } - } - if (intersection.entityID == getID()) { - if (event->button() == Qt::MouseButton::RightButton) { - if (event->type() == QEvent::MouseButtonRelease) { - const QMouseEvent* mouseEvent = static_cast(event); - ivec2 dist = glm::abs(toGlm(mouseEvent->pos()) - _lastPress); - if (!glm::any(glm::greaterThan(dist, ivec2(1)))) { - AbstractViewStateInterface::instance()->postLambdaEvent([this] { - QMetaObject::invokeMethod(_webSurface->getRootItem(), "goBack"); - }); - } - _lastPress = ivec2(INT_MIN); - } - return; - } - - // FIXME doesn't work... double click events not received - if (event->type() == QEvent::MouseButtonDblClick) { - AbstractViewStateInterface::instance()->postLambdaEvent([this] { - _webSurface->getRootItem()->setProperty("url", _sourceUrl); - }); - } - - if (event->button() == Qt::MouseButton::MiddleButton) { - if (event->type() == QEvent::MouseButtonRelease) { - AbstractViewStateInterface::instance()->postLambdaEvent([this] { - _webSurface->getRootItem()->setProperty("url", _sourceUrl); - }); - } - return; - } - - // Map the intersection point to an actual offscreen pixel - glm::vec3 point = intersection.intersection; - glm::vec3 dimensions = getDimensions(); - point -= getPosition(); - point = glm::inverse(getRotation()) * point; - point /= dimensions; - point += 0.5f; - point.y = 1.0f - point.y; - point *= dimensions * (METERS_TO_INCHES * DPI); - - if (event->button() == Qt::MouseButton::LeftButton) { - if (event->type() == QEvent::MouseButtonPress) { - this->_pressed = true; - this->_lastMove = ivec2((int)point.x, (int)point.y); - } else if (event->type() == QEvent::MouseButtonRelease) { - this->_pressed = false; - } - } - if (event->type() == QEvent::MouseMove) { - this->_lastMove = ivec2((int)point.x, (int)point.y); - } - - // Forward the mouse event. - QMouseEvent mappedEvent(event->type(), - QPoint((int)point.x, (int)point.y), - event->screenPos(), event->button(), - event->buttons(), event->modifiers()); - QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); + handleMouseEvent(*event, intersection.intersection); } }; _mousePressConnection = QObject::connect(renderer, &EntityTreeRenderer::mousePressOnEntity, forwardMouseEvent); @@ -236,6 +167,77 @@ QObject* RenderableWebEntityItem::getEventHandler() { return _webSurface->getEventHandler(); } +void RenderableWebEntityItem::handleMouseEvent(QMouseEvent event, glm::vec3 intersectionPoint) { + // Ignore mouse interaction if we're locked + if (getLocked()) { + return; + } + + if (event.button() == Qt::MouseButton::RightButton) { + if (event.type() == QEvent::MouseButtonPress) { + _lastPress = toGlm(event.pos()); + } + } + + if (event.button() == Qt::MouseButton::RightButton) { + if (event.type() == QEvent::MouseButtonRelease) { + ivec2 dist = glm::abs(toGlm(event.pos()) - _lastPress); + if (!glm::any(glm::greaterThan(dist, ivec2(1)))) { + AbstractViewStateInterface::instance()->postLambdaEvent([this] { + QMetaObject::invokeMethod(_webSurface->getRootItem(), "goBack"); + }); + } + _lastPress = ivec2(INT_MIN); + } + return; + } + + // FIXME doesn't work... double click events not received + if (event.type() == QEvent::MouseButtonDblClick) { + AbstractViewStateInterface::instance()->postLambdaEvent([this] { + _webSurface->getRootItem()->setProperty("url", _sourceUrl); + }); + } + + if (event.button() == Qt::MouseButton::MiddleButton) { + if (event.type() == QEvent::MouseButtonRelease) { + AbstractViewStateInterface::instance()->postLambdaEvent([this] { + _webSurface->getRootItem()->setProperty("url", _sourceUrl); + }); + } + return; + } + + // Map the intersection point to an actual offscreen pixel + glm::vec3 point = intersectionPoint; + glm::vec3 dimensions = getDimensions(); + point -= getPosition(); + point = glm::inverse(getRotation()) * point; + point /= dimensions; + point += 0.5f; + point.y = 1.0f - point.y; + point *= dimensions * (METERS_TO_INCHES * DPI); + + if (event.button() == Qt::MouseButton::LeftButton) { + if (event.type() == QEvent::MouseButtonPress) { + _pressed = true; + _lastMove = ivec2((int)point.x, (int)point.y); + } else if (event.type() == QEvent::MouseButtonRelease) { + _pressed = false; + } + } + if (event.type() == QEvent::MouseMove) { + _lastMove = ivec2((int)point.x, (int)point.y); + } + + // Forward the mouse event. + QMouseEvent mappedEvent(event.type(), + QPoint((int)point.x, (int)point.y), + event.screenPos(), event.button(), + event.buttons(), event.modifiers()); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); +} + void RenderableWebEntityItem::destroyWebSurface() { if (_webSurface) { --_currentWebCount; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 4125be61dd..b20a9af928 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -10,6 +10,7 @@ #define hifi_RenderableWebEntityItem_h #include +#include #include @@ -28,10 +29,12 @@ public: virtual void render(RenderArgs* args) override; virtual void setSourceUrl(const QString& value) override; - + void setProxyWindow(QWindow* proxyWindow); QObject* getEventHandler(); + void handleMouseEvent(QMouseEvent event, glm::vec3 intersectionPoint); + void update(const quint64& now) override; bool needsToCallUpdate() const override { return _webSurface != nullptr; } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index c023278a3b..3d4c9ac8b0 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1019,7 +1019,8 @@ function MyController(hand) { entityID: intersection.entityID, overlayID: intersection.overlayID, searchRay: pickRay, - distance: Vec3.distance(pickRay.origin, intersection.intersection) + distance: Vec3.distance(pickRay.origin, intersection.intersection), + intersection: intersection.intersection }; } else { return result; @@ -1259,106 +1260,132 @@ function MyController(hand) { var handPosition = this.getHandPosition(); - var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); - entityPropertiesCache.addEntities(candidateEntities); + var rayPickInfo = this.calcRayPickInfo(this.hand); - var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); - if (potentialEquipHotspot) { - if (this.triggerSmoothedGrab()) { - this.grabbedHotspot = potentialEquipHotspot; - this.grabbedEntity = potentialEquipHotspot.entityID; - this.setState(STATE_HOLD, "eqipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); - return; - } + if (rayPickInfo.entityID) { + entityPropertiesCache.addEntity(rayPickInfo.entityID); } - var grabbableEntities = candidateEntities.filter(function(entity) { - return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE); - }); + // if the line probe hits a non-grabbable web entity or a web entity that is grabbed by the other hand. + // route simulated mouse events to that entity. + if (rayPickInfo.entityID && entityPropertiesCache.getProps(rayPickInfo.entityID).type === "Web" && + (!this.entityIsGrabbable(rayPickInfo.entityID) || this.getOtherHandController().grabbedEntity == rayPickInfo.entityID)) { - var rayPickInfo = this.calcRayPickInfo(this.hand); - if (rayPickInfo.entityID) { - this.intersectionDistance = rayPickInfo.distance; - entityPropertiesCache.addEntity(rayPickInfo.entityID); - if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) { - grabbableEntities.push(rayPickInfo.entityID); + if (Reticle.keyboardFocusEntity != rayPickInfo.entityID) { + Reticle.keyboardFocusEntity = rayPickInfo.entityID; } - } else if (rayPickInfo.overlayID) { + Reticle.sendEntityMouseMoveEvent(rayPickInfo.entityID, rayPickInfo.intersection); + if (this.triggerSmoothedGrab() && !this.lastTriggerSmoothedGrab) { + print("AJT: mouse down"); + Reticle.sendEntityLeftMouseDownEvent(rayPickInfo.entityID, rayPickInfo.intersection); + } + if (!this.triggerSmoothedGrab() && this.lastTriggerSmoothedGrab) { + print("AJT: mouse up"); + Reticle.sendEntityLeftMouseUpEvent(rayPickInfo.entityID, rayPickInfo.intersection); + } + this.lastTriggerSmoothedGrab = this.triggerSmoothedGrab(); + equipHotspotBuddy.updateHotspots([], timestamp); this.intersectionDistance = rayPickInfo.distance; } else { - this.intersectionDistance = 0; - } - var entity; - if (grabbableEntities.length > 0) { - // sort by distance - grabbableEntities.sort(function(a, b) { - var aDistance = Vec3.distance(entityPropertiesCache.getProps(a).position, handPosition); - var bDistance = Vec3.distance(entityPropertiesCache.getProps(b).position, handPosition); - return aDistance - bDistance; + var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); + entityPropertiesCache.addEntities(candidateEntities); + + var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); + if (potentialEquipHotspot) { + if (this.triggerSmoothedGrab()) { + this.grabbedHotspot = potentialEquipHotspot; + this.grabbedEntity = potentialEquipHotspot.entityID; + this.setState(STATE_HOLD, "eqipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); + return; + } + } + + var grabbableEntities = candidateEntities.filter(function(entity) { + return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE); }); - entity = grabbableEntities[0]; - name = entityPropertiesCache.getProps(entity).name; - this.grabbedEntity = entity; - if (this.entityWantsTrigger(entity)) { - if (this.triggerSmoothedGrab()) { - this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'"); - return; - } else { - // potentialNearTriggerEntity = entity; + + if (rayPickInfo.entityID) { + this.intersectionDistance = rayPickInfo.distance; + if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) { + grabbableEntities.push(rayPickInfo.entityID); } + } else if (rayPickInfo.overlayID) { + this.intersectionDistance = rayPickInfo.distance; } else { - if (this.triggerSmoothedGrab()) { - var props = entityPropertiesCache.getProps(entity); - var grabProps = entityPropertiesCache.getGrabProps(entity); - var refCount = grabProps.refCount ? grabProps.refCount : 0; - if (refCount >= 1) { - // if another person is holding the object, remember to restore the - // parent info, when we are finished grabbing it. - this.shouldResetParentOnRelease = true; - this.previousParentID = props.parentID; - this.previousParentJointIndex = props.parentJointIndex; + this.intersectionDistance = 0; + } + + var entity; + if (grabbableEntities.length > 0) { + // sort by distance + grabbableEntities.sort(function(a, b) { + var aDistance = Vec3.distance(entityPropertiesCache.getProps(a).position, handPosition); + var bDistance = Vec3.distance(entityPropertiesCache.getProps(b).position, handPosition); + return aDistance - bDistance; + }); + entity = grabbableEntities[0]; + name = entityPropertiesCache.getProps(entity).name; + this.grabbedEntity = entity; + if (this.entityWantsTrigger(entity)) { + if (this.triggerSmoothedGrab()) { + this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'"); + return; + } else { + // potentialNearTriggerEntity = entity; } - - this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); - return; } else { - // potentialNearGrabEntity = entity; + if (this.triggerSmoothedGrab()) { + var props = entityPropertiesCache.getProps(entity); + var grabProps = entityPropertiesCache.getGrabProps(entity); + var refCount = grabProps.refCount ? grabProps.refCount : 0; + if (refCount >= 1) { + // if another person is holding the object, remember to restore the + // parent info, when we are finished grabbing it. + this.shouldResetParentOnRelease = true; + this.previousParentID = props.parentID; + this.previousParentJointIndex = props.parentJointIndex; + } + + this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); + return; + } else { + // potentialNearGrabEntity = entity; + } } } - } - if (rayPickInfo.entityID) { - entity = rayPickInfo.entityID; - name = entityPropertiesCache.getProps(entity).name; - if (this.entityWantsTrigger(entity)) { - if (this.triggerSmoothedGrab()) { - this.grabbedEntity = entity; - this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'"); - return; - } else { - // potentialFarTriggerEntity = entity; - } - } else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) { - if (this.triggerSmoothedGrab() && !isEditing()) { - this.grabbedEntity = entity; - this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'"); - return; - } else { - // potentialFarGrabEntity = entity; + if (rayPickInfo.entityID) { + entity = rayPickInfo.entityID; + name = entityPropertiesCache.getProps(entity).name; + if (this.entityWantsTrigger(entity)) { + if (this.triggerSmoothedGrab()) { + this.grabbedEntity = entity; + this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'"); + return; + } else { + // potentialFarTriggerEntity = entity; + } + } else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) { + if (this.triggerSmoothedGrab() && !isEditing()) { + this.grabbedEntity = entity; + this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'"); + return; + } else { + // potentialFarGrabEntity = entity; + } } } + + this.updateEquipHaptics(potentialEquipHotspot, handPosition); + + var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); + equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); + if (potentialEquipHotspot) { + equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); + } } - this.updateEquipHaptics(potentialEquipHotspot, handPosition); - - var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); - equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); - if (potentialEquipHotspot) { - equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); - } - - this.searchIndicatorOn(rayPickInfo.searchRay); Reticle.setVisible(false); }; @@ -2395,4 +2422,4 @@ function cleanup() { } Script.scriptEnding.connect(cleanup); -Script.update.connect(update); \ No newline at end of file +Script.update.connect(update); From beb42332ef8b0a102854b689b9015228acadb97f Mon Sep 17 00:00:00 2001 From: samcake Date: Sun, 31 Jul 2016 16:05:25 -0700 Subject: [PATCH 007/332] save the current state of adding debug and evolving the ao job --- .../src/AmbientOcclusionEffect.cpp | 227 ++++++++++++------ .../render-utils/src/AmbientOcclusionEffect.h | 131 +++++++--- .../render-utils/src/DeferredTransform.slh | 3 + .../render-utils/src/RenderDeferredTask.cpp | 7 +- libraries/render-utils/src/ssao.slh | 62 +---- .../render-utils/src/ssao_debugOcclusion.slf | 162 +++++++++++++ .../render-utils/src/ssao_makeOcclusion.slf | 17 +- .../utilities/render/ambientOcclusionPass.qml | 50 ++++ .../render/debugAmbientOcclusionPass.js | 19 ++ 9 files changed, 506 insertions(+), 172 deletions(-) create mode 100644 libraries/render-utils/src/ssao_debugOcclusion.slf create mode 100644 scripts/developer/utilities/render/ambientOcclusionPass.qml create mode 100644 scripts/developer/utilities/render/debugAmbientOcclusionPass.js diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 929f4896b3..0d339c71ed 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -30,6 +30,7 @@ #include "ssao_makePyramid_frag.h" #include "ssao_makeOcclusion_frag.h" +#include "ssao_debugOcclusion_frag.h" #include "ssao_makeHorizontalBlur_frag.h" #include "ssao_makeVerticalBlur_frag.h" @@ -179,10 +180,6 @@ const int AmbientOcclusionEffect_LinearDepthMapSlot = 0; const int AmbientOcclusionEffect_OcclusionMapSlot = 0; AmbientOcclusionEffect::AmbientOcclusionEffect() { - FrameTransform frameTransform; - _frameTransformBuffer = gpu::BufferView(std::make_shared(sizeof(FrameTransform), (const gpu::Byte*) &frameTransform)); - Parameters parameters; - _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Parameters), (const gpu::Byte*) ¶meters)); } void AmbientOcclusionEffect::configure(const Config& config) { @@ -192,41 +189,41 @@ void AmbientOcclusionEffect::configure(const Config& config) { const double RADIUS_POWER = 6.0; const auto& radius = config.radius; - if (radius != getRadius()) { - auto& current = _parametersBuffer.edit().radiusInfo; + if (radius != _parametersBuffer->getRadius()) { + auto& current = _parametersBuffer->radiusInfo; current.x = radius; current.y = radius * radius; current.z = (float)(1.0 / pow((double)radius, RADIUS_POWER)); } - if (config.obscuranceLevel != getObscuranceLevel()) { - auto& current = _parametersBuffer.edit().radiusInfo; + if (config.obscuranceLevel != _parametersBuffer->getObscuranceLevel()) { + auto& current = _parametersBuffer->radiusInfo; current.w = config.obscuranceLevel; } - if (config.falloffBias != getFalloffBias()) { - auto& current = _parametersBuffer.edit().ditheringInfo; + if (config.falloffBias != _parametersBuffer->getFalloffBias()) { + auto& current = _parametersBuffer->ditheringInfo; current.z = config.falloffBias; } - if (config.edgeSharpness != getEdgeSharpness()) { - auto& current = _parametersBuffer.edit().blurInfo; + if (config.edgeSharpness != _parametersBuffer->getEdgeSharpness()) { + auto& current = _parametersBuffer->blurInfo; current.x = config.edgeSharpness; } - if (config.blurDeviation != getBlurDeviation()) { - auto& current = _parametersBuffer.edit().blurInfo; + if (config.blurDeviation != _parametersBuffer->getBlurDeviation()) { + auto& current = _parametersBuffer->blurInfo; current.z = config.blurDeviation; shouldUpdateGaussian = true; } - if (config.numSpiralTurns != getNumSpiralTurns()) { - auto& current = _parametersBuffer.edit().sampleInfo; + if (config.numSpiralTurns != _parametersBuffer->getNumSpiralTurns()) { + auto& current = _parametersBuffer->sampleInfo; current.z = config.numSpiralTurns; } - if (config.numSamples != getNumSamples()) { - auto& current = _parametersBuffer.edit().sampleInfo; + if (config.numSamples != _parametersBuffer->getNumSamples()) { + auto& current = _parametersBuffer->sampleInfo; current.x = config.numSamples; current.y = 1.0f / config.numSamples; } @@ -235,33 +232,36 @@ void AmbientOcclusionEffect::configure(const Config& config) { _framebuffer = std::make_shared(); } + if (config.perspectiveScale != _parametersBuffer->getPerspectiveScale()) { + _parametersBuffer->resolutionInfo.z = config.perspectiveScale; + } _framebuffer->setResolutionLevel(config.resolutionLevel); - if (config.resolutionLevel != getResolutionLevel()) { - _parametersBuffer.edit().resolutionInfo.w = config.resolutionLevel; + if (config.resolutionLevel != _parametersBuffer->getResolutionLevel()) { + _parametersBuffer->resolutionInfo.w = config.resolutionLevel; } const auto& resolutionLevel = config.resolutionLevel; - if (resolutionLevel != getResolutionLevel()) { - auto& current = _parametersBuffer.edit().resolutionInfo; + if (resolutionLevel != _parametersBuffer->getResolutionLevel()) { + auto& current = _parametersBuffer->resolutionInfo; current.x = (float)resolutionLevel; // Communicate the change to the Framebuffer cache // DependencyManager::get()->setAmbientOcclusionResolutionLevel(resolutionLevel); } - if (config.blurRadius != getBlurRadius()) { - auto& current = _parametersBuffer.edit().blurInfo; + if (config.blurRadius != _parametersBuffer->getBlurRadius()) { + auto& current = _parametersBuffer->blurInfo; current.y = (float)config.blurRadius; shouldUpdateGaussian = true; } - if (config.ditheringEnabled != isDitheringEnabled()) { - auto& current = _parametersBuffer.edit().ditheringInfo; + if (config.ditheringEnabled != _parametersBuffer->isDitheringEnabled()) { + auto& current = _parametersBuffer->ditheringInfo; current.x = (float)config.ditheringEnabled; } - if (config.borderingEnabled != isBorderingEnabled()) { - auto& current = _parametersBuffer.edit().ditheringInfo; + if (config.borderingEnabled != _parametersBuffer->isBorderingEnabled()) { + auto& current = _parametersBuffer->ditheringInfo; current.w = (float)config.borderingEnabled; } @@ -277,7 +277,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionFrameTransformBuffer"), AmbientOcclusionEffect_FrameTransformSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), AmbientOcclusionEffect_FrameTransformSlot)); slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot)); slotBindings.insert(gpu::Shader::Binding(std::string("pyramidMap"), AmbientOcclusionEffect_LinearDepthMapSlot)); gpu::Shader::makeProgram(*program, slotBindings); @@ -339,14 +339,9 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() { return _vBlurPipeline; } - -void AmbientOcclusionEffect::setDepthInfo(float nearZ, float farZ) { - _frameTransformBuffer.edit().depthInfo = glm::vec4(nearZ*farZ, farZ -nearZ, -farZ, 0.0f); -} - void AmbientOcclusionEffect::updateGaussianDistribution() { - auto coefs = _parametersBuffer.edit()._gaussianCoefs; - GaussianDistribution::evalSampling(coefs, Parameters::GAUSSIAN_COEFS_LENGTH, getBlurRadius(), getBlurDeviation()); + auto coefs = _parametersBuffer->_gaussianCoefs; + GaussianDistribution::evalSampling(coefs, Parameters::GAUSSIAN_COEFS_LENGTH, _parametersBuffer->getBlurRadius(), _parametersBuffer->getBlurDeviation()); } void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { @@ -356,9 +351,9 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext RenderArgs* args = renderContext->args; // FIXME: Different render modes should have different tasks - if (args->_renderMode != RenderArgs::DEFAULT_RENDER_MODE) { - return; - } +// if (args->_renderMode != RenderArgs::DEFAULT_RENDER_MODE) { +// return; +// } const auto frameTransform = inputs.get0(); const auto deferredFramebuffer = inputs.get1(); @@ -385,7 +380,8 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext auto occlusionFBO = _framebuffer->getOcclusionFramebuffer(); auto occlusionBlurredFBO = _framebuffer->getOcclusionBlurredFramebuffer(); - outputs = _framebuffer; + outputs.edit0() = _framebuffer; + outputs.edit1() = _parametersBuffer; auto framebufferSize = _framebuffer->getSourceFrameSize(); @@ -394,39 +390,13 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext float tMin = args->_viewport.y / (float)framebufferSize.y; float tHeight = args->_viewport.w / (float)framebufferSize.y; - auto resolutionLevel = getResolutionLevel(); + auto resolutionLevel = _parametersBuffer->getResolutionLevel(); - // Update the depth info with near and far (same for stereo) - setDepthInfo(args->getViewFrustum().getNearClip(), args->getViewFrustum().getFarClip()); - _frameTransformBuffer.edit().pixelInfo = args->_viewport; //_parametersBuffer.edit()._ditheringInfo.y += 0.25f; // Running in stero ? bool isStereo = args->_context->isStereo(); - if (!isStereo) { - // Eval the mono projection - mat4 monoProjMat; - args->getViewFrustum().evalProjectionMatrix(monoProjMat); - _frameTransformBuffer.edit().projection[0] = monoProjMat; - _frameTransformBuffer.edit().stereoInfo = glm::vec4(0.0f, (float)args->_viewport.z, 0.0f, 0.0f); - - } else { - - mat4 projMats[2]; - mat4 eyeViews[2]; - args->_context->getStereoProjections(projMats); - args->_context->getStereoViews(eyeViews); - - for (int i = 0; i < 2; i++) { - // Compose the mono Eye space to Stereo clip space Projection Matrix - auto sideViewMat = projMats[i] * eyeViews[i]; - _frameTransformBuffer.edit().projection[i] = sideViewMat; - } - - _frameTransformBuffer.edit().stereoInfo = glm::vec4(1.0f, (float)(args->_viewport.z >> 1), 0.0f, 1.0f); - - } auto occlusionPipeline = getOcclusionPipeline(); auto firstHBlurPipeline = getHBlurPipeline(); @@ -446,11 +416,12 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext model.setScale(glm::vec3(sWidth, tHeight, 1.0f)); batch.setModelTransform(model); - batch.setUniformBuffer(AmbientOcclusionEffect_FrameTransformSlot, _frameTransformBuffer); + batch.setUniformBuffer(AmbientOcclusionEffect_FrameTransformSlot, frameTransform->getFrameTransformBuffer()); batch.setUniformBuffer(AmbientOcclusionEffect_ParamsSlot, _parametersBuffer); - - // batch.generateTextureMips(linearDepthTexture); + + // We need this with the mips levels + batch.generateTextureMips(linearDepthTexture); // Occlusion pass batch.setFramebuffer(occlusionFBO); @@ -460,7 +431,7 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext batch.draw(gpu::TRIANGLE_STRIP, 4); - /* if (getBlurRadius() > 0) { + if (_parametersBuffer->getBlurRadius() > 0) { // Blur 1st pass batch.setFramebuffer(occlusionBlurredFBO); batch.setPipeline(firstHBlurPipeline); @@ -472,7 +443,7 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext batch.setPipeline(lastVBlurPipeline); batch.setResourceTexture(AmbientOcclusionEffect_OcclusionMapSlot, occlusionBlurredFBO->getRenderBuffer(0)); batch.draw(gpu::TRIANGLE_STRIP, 4); - }*/ + } batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, nullptr); @@ -484,3 +455,115 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext // Update the timer std::static_pointer_cast(renderContext->jobConfig)->gpuTime = _gpuTimer.getAverage(); } + + + +DebugAmbientOcclusion::DebugAmbientOcclusion() { +} + +void DebugAmbientOcclusion::configure(const Config& config) { + + _showCursorPixel = config.showCursorPixel; + + auto cursorPos = glm::vec2(_parametersBuffer->pixelInfo); + if (cursorPos != config.debugCursorTexcoord) { + _parametersBuffer->pixelInfo = glm::vec4(config.debugCursorTexcoord, 0.0f, 0.0f); + } +} + +const gpu::PipelinePointer& DebugAmbientOcclusion::getDebugPipeline() { + if (!_debugPipeline) { + auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(ssao_debugOcclusion_frag)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), AmbientOcclusionEffect_FrameTransformSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("pyramidMap"), AmbientOcclusionEffect_LinearDepthMapSlot)); + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + state->setColorWriteMask(true, true, true, false); + + // Good to go add the brand new pipeline + _debugPipeline = gpu::Pipeline::create(program, state); + } + return _debugPipeline; +} + +void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + + RenderArgs* args = renderContext->args; + + // FIXME: Different render modes should have different tasks +// if (args->_renderMode != RenderArgs::DEFAULT_RENDER_MODE) { +// return; +// } + + const auto frameTransform = inputs.get0(); + const auto deferredFramebuffer = inputs.get1(); + const auto linearDepthFramebuffer = inputs.get2(); + const auto ambientOcclusionUniforms = inputs.get3(); + + auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); + auto normalTexture = deferredFramebuffer->getDeferredNormalTexture(); + auto sourceViewport = args->_viewport; + auto occlusionViewport = sourceViewport; + + auto resolutionLevel = ambientOcclusionUniforms->getResolutionLevel(); + + if (resolutionLevel > 0) { + linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); + normalTexture = linearDepthFramebuffer->getHalfNormalTexture(); + occlusionViewport = occlusionViewport >> ambientOcclusionUniforms->getResolutionLevel(); + } + + + auto framebufferSize = glm::ivec2(linearDepthTexture->getDimensions()); + + float sMin = args->_viewport.x / (float)framebufferSize.x; + float sWidth = args->_viewport.z / (float)framebufferSize.x; + float tMin = args->_viewport.y / (float)framebufferSize.y; + float tHeight = args->_viewport.w / (float)framebufferSize.y; + + + // Running in stero ? + bool isStereo = args->_context->isStereo(); + + auto debugPipeline = getDebugPipeline(); + + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + batch.enableStereo(false); + + batch.setViewportTransform(sourceViewport); + batch.setProjectionTransform(glm::mat4()); + batch.setViewTransform(Transform()); + + Transform model; + model.setTranslation(glm::vec3(sMin, tMin, 0.0f)); + model.setScale(glm::vec3(sWidth, tHeight, 1.0f)); + batch.setModelTransform(model); + + batch.setUniformBuffer(AmbientOcclusionEffect_FrameTransformSlot, frameTransform->getFrameTransformBuffer()); + batch.setUniformBuffer(AmbientOcclusionEffect_ParamsSlot, ambientOcclusionUniforms); + + + // We need this with the mips levels + batch.generateTextureMips(linearDepthTexture); + + batch.setPipeline(debugPipeline); + batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, linearDepthTexture); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + + batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, nullptr); + batch.setResourceTexture(AmbientOcclusionEffect_OcclusionMapSlot, nullptr); + + }); + +} + \ No newline at end of file diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 8c458a5879..ffc2c4d3e3 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -63,6 +63,7 @@ class AmbientOcclusionEffectConfig : public render::Job::Config::Persistent { Q_PROPERTY(bool ditheringEnabled MEMBER ditheringEnabled NOTIFY dirty) Q_PROPERTY(bool borderingEnabled MEMBER borderingEnabled NOTIFY dirty) Q_PROPERTY(float radius MEMBER radius WRITE setRadius) + Q_PROPERTY(float perspectiveScale MEMBER perspectiveScale WRITE setPerspectiveScale) Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel) Q_PROPERTY(float falloffBias MEMBER falloffBias WRITE setFalloffBias) Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness WRITE setEdgeSharpness) @@ -79,6 +80,7 @@ public: const int MAX_BLUR_RADIUS = 6; void setRadius(float newRadius) { radius = std::max(0.01f, newRadius); emit dirty(); } + void setPerspectiveScale(float scale) { perspectiveScale = scale; emit dirty(); } void setObscuranceLevel(float level) { obscuranceLevel = std::max(0.01f, level); emit dirty(); } void setFalloffBias(float bias) { falloffBias = std::max(0.0f, std::min(bias, 0.2f)); emit dirty(); } void setEdgeSharpness(float sharpness) { edgeSharpness = std::max(0.0f, (float)sharpness); emit dirty(); } @@ -90,14 +92,15 @@ public: double getGpuTime() { return gpuTime; } float radius{ 0.5f }; + float perspectiveScale{ 1.0f }; float obscuranceLevel{ 0.5f }; // intensify or dim down the obscurance effect float falloffBias{ 0.01f }; float edgeSharpness{ 1.0f }; float blurDeviation{ 2.5f }; float numSpiralTurns{ 7.0f }; // defining an angle span to distribute the samples ray directions int numSamples{ 11 }; - int resolutionLevel{ 1 }; - int blurRadius{ 4 }; // 0 means no blurring + int resolutionLevel{ 0 }; + int blurRadius{ 0 }; // 0 means no blurring bool ditheringEnabled{ true }; // randomize the distribution of rays per pixel, should always be true bool borderingEnabled{ true }; // avoid evaluating information from non existing pixels out of the frame, should always be true double gpuTime{ 0.0 }; @@ -106,10 +109,25 @@ signals: void dirty(); }; + +namespace gpu { +template class UniformBuffer : public gpu::BufferView { + public: + ~UniformBuffer() {}; + UniformBuffer() : gpu::BufferView(std::make_shared(sizeof(T), (const gpu::Byte*) &T())) {} + + const T* operator ->() const { return &get(); } + T* operator ->() { return &edit(); } + +}; + + +} + class AmbientOcclusionEffect { public: using Inputs = render::VaryingSet3; - using Outputs = AmbientOcclusionFramebufferPointer; + using Outputs = render::VaryingSet2; using Config = AmbientOcclusionEffectConfig; using JobModel = render::Job::ModelIO; @@ -118,46 +136,12 @@ public: void configure(const Config& config); void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); - float getRadius() const { return _parametersBuffer.get().radiusInfo.x; } - float getObscuranceLevel() const { return _parametersBuffer.get().radiusInfo.w; } - float getFalloffBias() const { return (float)_parametersBuffer.get().ditheringInfo.z; } - float getEdgeSharpness() const { return (float)_parametersBuffer.get().blurInfo.x; } - float getBlurDeviation() const { return _parametersBuffer.get().blurInfo.z; } - float getNumSpiralTurns() const { return _parametersBuffer.get().sampleInfo.z; } - int getNumSamples() const { return (int)_parametersBuffer.get().sampleInfo.x; } - int getResolutionLevel() const { return _parametersBuffer.get().resolutionInfo.x; } - int getBlurRadius() const { return (int)_parametersBuffer.get().blurInfo.y; } - bool isDitheringEnabled() const { return _parametersBuffer.get().ditheringInfo.x; } - bool isBorderingEnabled() const { return _parametersBuffer.get().ditheringInfo.w; } - -private: - void updateGaussianDistribution(); - void setDepthInfo(float nearZ, float farZ); - - typedef gpu::BufferView UniformBufferView; - // Class describing the uniform buffer with the transform info common to the AO shaders - // It s changing every frame - class FrameTransform { - public: - // Pixel info is { viemport width height and stereo on off} - glm::vec4 pixelInfo; - // Depth info is { n.f, f - n, -f} - glm::vec4 depthInfo; - // Stereo info - 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]; - - FrameTransform() {} - }; - gpu::BufferView _frameTransformBuffer; - // Class describing the uniform buffer with all the parameters common to the AO shaders class Parameters { public: // Resolution info - glm::vec4 resolutionInfo { -1.0f, 0.0f, 0.0f, 0.0f }; + glm::vec4 resolutionInfo { -1.0f, 0.0f, 1.0f, 0.0f }; // radius info is { R, R^2, 1 / R^6, ObscuranceScale} glm::vec4 radiusInfo{ 0.5f, 0.5f * 0.5f, 1.0f / (0.25f * 0.25f * 0.25f), 1.0f }; // Dithering info @@ -169,10 +153,28 @@ private: // gaussian distribution coefficients first is the sampling radius (max is 6) const static int GAUSSIAN_COEFS_LENGTH = 8; float _gaussianCoefs[GAUSSIAN_COEFS_LENGTH]; - + Parameters() {} + + int getResolutionLevel() const { return resolutionInfo.x; } + float getRadius() const { return radiusInfo.x; } + float getPerspectiveScale() const { return resolutionInfo.z; } + float getObscuranceLevel() const { return radiusInfo.w; } + float getFalloffBias() const { return (float)ditheringInfo.z; } + float getEdgeSharpness() const { return (float)blurInfo.x; } + float getBlurDeviation() const { return blurInfo.z; } + float getNumSpiralTurns() const { return sampleInfo.z; } + int getNumSamples() const { return (int)sampleInfo.x; } + int getBlurRadius() const { return (int)blurInfo.y; } + bool isDitheringEnabled() const { return ditheringInfo.x; } + bool isBorderingEnabled() const { return ditheringInfo.w; } }; - gpu::BufferView _parametersBuffer; + using ParametersBuffer = gpu::UniformBuffer; + +private: + void updateGaussianDistribution(); + + ParametersBuffer _parametersBuffer; const gpu::PipelinePointer& getOcclusionPipeline(); const gpu::PipelinePointer& getHBlurPipeline(); // first @@ -185,6 +187,55 @@ private: AmbientOcclusionFramebufferPointer _framebuffer; gpu::RangeTimer _gpuTimer; + + friend class DebugAmbientOcclusion; +}; + + +class DebugAmbientOcclusionConfig : public render::Job::Config { + Q_OBJECT + + Q_PROPERTY(bool showCursorPixel MEMBER showCursorPixel NOTIFY dirty) + Q_PROPERTY(glm::vec2 debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty) +public: + DebugAmbientOcclusionConfig() : render::Job::Config(true) {} + + bool showCursorPixel{ false }; + glm::vec2 debugCursorTexcoord{ 0.5, 0.5 }; + +signals: + void dirty(); +}; + + +class DebugAmbientOcclusion { +public: + using Inputs = render::VaryingSet4; + using Config = DebugAmbientOcclusionConfig; + using JobModel = render::Job::ModelI; + + DebugAmbientOcclusion(); + + void configure(const Config& config); + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs); + +private: + + // Class describing the uniform buffer with all the parameters common to the debug AO shaders + class Parameters { + public: + // Pixel info + glm::vec4 pixelInfo { 0.0f, 0.0f, 0.0f, 0.0f }; + + Parameters() {} + }; + gpu::UniformBuffer _parametersBuffer; + + const gpu::PipelinePointer& getDebugPipeline(); + + gpu::PipelinePointer _debugPipeline; + + bool _showCursorPixel{ false }; }; #endif // hifi_AmbientOcclusionEffect_h diff --git a/libraries/render-utils/src/DeferredTransform.slh b/libraries/render-utils/src/DeferredTransform.slh index b3881c4c71..ec8cdac8c1 100644 --- a/libraries/render-utils/src/DeferredTransform.slh +++ b/libraries/render-utils/src/DeferredTransform.slh @@ -81,6 +81,9 @@ bool isStereo() { float getStereoSideWidth(int resolutionLevel) { return float(int(frameTransform._stereoInfo.y) >> resolutionLevel); } +float getStereoSideHeight(int resolutionLevel) { + return float(int(frameTransform._pixelInfo.w) >> resolutionLevel); +} ivec4 getStereoSideInfo(int xPos, int resolutionLevel) { int sideWidth = int(getStereoSideWidth(resolutionLevel)); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index afff4bac72..0a4f5a9b8e 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -142,7 +142,9 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { // AO job const auto ambientOcclusionInputs = AmbientOcclusionEffect::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget).hasVarying(); - const auto ambientOcclusionFramebuffer = addJob("AmbientOcclusion", ambientOcclusionInputs); + const auto ambientOcclusionOutputs = addJob("AmbientOcclusion", ambientOcclusionInputs); + const auto ambientOcclusionFramebuffer = ambientOcclusionOutputs.getN(0); + const auto ambientOcclusionUniforms = ambientOcclusionOutputs.getN(1); // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now. addJob("DrawLight", lights); @@ -178,6 +180,9 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { { addJob("DebugScattering", deferredLightingInputs); + const auto debugAmbientOcclusionInputs = DebugAmbientOcclusion::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget, ambientOcclusionUniforms).hasVarying(); + addJob("DebugAmbientOcclusion", debugAmbientOcclusionInputs); + // Debugging Deferred buffer job const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); addJob("DebugDeferredBuffer", debugFramebuffers); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 21e9a5a297..01be0e463a 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -30,13 +30,8 @@ vec2 unpackOcclusionDepth(vec3 raw) { <@endfunc@> <@func declareAmbientOcclusion()@> - -struct AmbientOcclusionFrameTransform { - vec4 _pixelInfo; - vec4 _depthInfo; - vec4 _stereoInfo; - mat4 _projection[2]; -}; +<@include DeferredTransform.slh@> +<$declareDeferredFrameTransform()$> struct AmbientOcclusionParams { vec4 _resolutionInfo; @@ -47,55 +42,20 @@ struct AmbientOcclusionParams { float _gaussianCoefs[8]; }; -uniform ambientOcclusionFrameTransformBuffer { - AmbientOcclusionFrameTransform frameTransform; -}; uniform ambientOcclusionParamsBuffer { AmbientOcclusionParams params; }; +float getPerspectiveScale() { + + return (params._resolutionInfo.z); +} int getResolutionLevel() { + return int(params._resolutionInfo.x); } -vec2 getWidthHeight() { - return vec2(ivec2(frameTransform._pixelInfo.zw) >> getResolutionLevel()); -} -float getProjScale() { - return getWidthHeight().y * frameTransform._projection[0][1][1] * 0.5; -} -mat4 getProjection(int side) { - return frameTransform._projection[side]; -} - -bool isStereo() { - return frameTransform._stereoInfo.x > 0.0f; -} - -float getStereoSideWidth() { - return float(int(frameTransform._stereoInfo.y) >> getResolutionLevel()); -} -float getStereoSideHeight() { - return float(int(frameTransform._pixelInfo.w) >> getResolutionLevel()); -} - -ivec3 getStereoSideInfo(int xPos) { - int sideWidth = int(getStereoSideWidth()); - return ivec3(xPos < sideWidth ? ivec2(0, 0) : ivec2(1, sideWidth), sideWidth); -} - - -float evalZeyeFromZdb(float depth) { - return frameTransform._depthInfo.x / (depth * frameTransform._depthInfo.y + frameTransform._depthInfo.z); -} - -vec3 evalEyeNormal(vec3 C) { - //return normalize(cross(dFdy(C), dFdx(C))); - return normalize(cross(dFdx(C), dFdy(C))); -} - - float getRadius() { return params._radiusInfo.x; } @@ -191,7 +151,7 @@ vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 ssC, float key) { ivec2 tapOffset = <$axis$> * (r * RADIUS_SCALE); ivec2 ssP = (ssC + tapOffset); - if ((ssP.x < side.y || ssP.x >= side.z + side.y) || (ssP.y < 0 || ssP.y >= int(getWidthHeight().y))) { + if ((ssP.x < side.y || ssP.x >= side.z + side.y) || (ssP.y < 0 || ssP.y >= int(getWidthHeight(getResolutionLevel()).y))) { return vec2(0.0); } vec2 tapOZ = fetchOcclusionDepth(ssC + tapOffset); @@ -209,7 +169,7 @@ vec3 getBlurredOcclusion(vec2 coord) { ivec2 ssC = ivec2(coord); // Stereo side info - ivec3 side = getStereoSideInfo(ssC.x); + ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel()); vec3 rawSample; vec2 occlusionDepth = fetchOcclusionDepthRaw(ssC, rawSample); @@ -223,11 +183,11 @@ vec3 getBlurredOcclusion(vec2 coord) { int blurRadius = getBlurRadius(); // negative side first for (int r = -blurRadius; r <= -1; ++r) { - weightedSums += evalTapWeightedValue(side, r, ssC, key); + weightedSums += evalTapWeightedValue(side.xyz, r, ssC, key); } // then positive side for (int r = 1; r <= blurRadius; ++r) { - weightedSums += evalTapWeightedValue(side, r, ssC, key); + weightedSums += evalTapWeightedValue(side.xyz, r, ssC, key); } // Final normalization diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf new file mode 100644 index 0000000000..7bad1420bf --- /dev/null +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -0,0 +1,162 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by Sam Gateau on 1/1/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include ssao.slh@> +<$declareAmbientOcclusion()$> +<$declarePackOcclusionDepth()$> + + +const int LOG_MAX_OFFSET = 3; +const int MAX_MIP_LEVEL = 5; + +// the depth pyramid texture +uniform sampler2D pyramidMap; + +float getZEye(ivec2 pixel) { + return -texelFetch(pyramidMap, pixel, getResolutionLevel()).x; +} +/* +vec3 evalEyePositionFromZeye(int side, float Zeye, vec2 texcoord) { + // compute the view space position using the depth + // basically manually pick the proj matrix components to do the inverse + float Xe = (-Zeye * (texcoord.x * 2.0 - 1.0) - Zeye * frameTransform._projection[side][2][0] - frameTransform._projection[side][3][0]) / frameTransform._projection[side][0][0]; + float Ye = (-Zeye * (texcoord.y * 2.0 - 1.0) - Zeye * frameTransform._projection[side][2][1] - frameTransform._projection[side][3][1]) / frameTransform._projection[side][1][1]; + return vec3(Xe, Ye, Zeye); +} +*/ +out vec4 outFragColor; + +uniform sampler2D normalMap; + +float getAngleDithering(in ivec2 pixelPos) { + // Hash function used in the AlchemyAO paper + return isDitheringEnabled() * (3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10 + getFrameDithering(); +} + +const float TWO_PI = 6.28; + +vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR){ + // Radius relative to ssR + float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); + float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle; + + ssR = alpha; + return vec2(cos(angle), sin(angle)); +} + +vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec2 unitOffset, float ssR) { + // Derivation: + // mipLevel = floor(log(ssR / MAX_OFFSET)); + int mipLevel = clamp(findMSB(int(ssR)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); + + ivec2 ssOffset = ivec2(ssR * unitOffset); + ivec2 ssP = ssOffset + ssC; + if (bool(isBorderingEnabled())) { + ssP.x = ((ssP.x < 0 || ssP.x >= side.z) ? ssC.x - ssOffset.x : ssP.x); + ssP.y = ((ssP.y < 0 || ssP.y >= int(getWidthHeight(getResolutionLevel()).y)) ? ssC.y - ssOffset.y : ssP.y); + } + + ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); + + vec3 P; + + // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. + // Manually clamp to the texture size because texelFetch bypasses the texture unit + ivec2 mipP = clamp(ssPFull >> mipLevel, ivec2(0), textureSize(pyramidMap, getResolutionLevel() + mipLevel) - ivec2(1)); + P.z = -texelFetch(pyramidMap, mipP, getResolutionLevel() + mipLevel).r; + + // Offset to pixel center + vec2 tapUV = (vec2(ssP) + vec2(0.5)) / float(side.z); + P = evalEyePositionFromZeye(side.x, P.z, tapUV); + return P; +} + +float sampleAO(in ivec3 side, in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssDiskRadius, in int tapIndex, in float randomPatternRotationAngle) { + // Offset on the unit disk, spun for this pixel + float ssR; + vec2 unitOffset = tapLocation(tapIndex, randomPatternRotationAngle, ssR); + ssR *= ssDiskRadius; + + + + // The occluding point in camera space + vec3 Q = getOffsetPosition(side, ssC, unitOffset, ssR); + + vec3 v = Q - C; + float vv = dot(v, v); + float vn = dot(v, n_C); + + // Fall off function as recommended in SAO paper + const float epsilon = 0.01; + float f = max(getRadius2() - vv, 0.0); + return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0); +} + +void main(void) { + // Pixel being shaded + ivec2 ssC = ivec2(gl_FragCoord.xy); + + // Fetch the z under the pixel (stereo or not) + float Zeye = getZEye(ssC); + + // Stereo side info + ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel()); + + // From now on, ssC is the pixel pos in the side + ssC.x -= side.y; + vec2 fragPos = (vec2(ssC) + vec2(0.5)) / vec2(getStereoSideWidth(getResolutionLevel()), getStereoSideHeight(getResolutionLevel())); + + // The position and normal of the pixel fragment in Eye space + vec3 Cp = evalEyePositionFromZeye(side.x, Zeye, fragPos); + vec3 Cn = evalEyeNormal(Cp); + + // Choose the screen-space sample radius + // proportional to the projected area of the sphere + float ssDiskRadius = -( getProjScale(getResolutionLevel()) * getRadius() / Cp.z ) * getPerspectiveScale(); + + // Let's make noise + float randomPatternRotationAngle = getAngleDithering(ssC); + + // Accumulate the Obscurance for each samples + float sum = 0.0; + for (int i = 0; i < getNumSamples(); ++i) { + sum += sampleAO(side.xyz, ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle); + } + + float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); + + + + outFragColor = vec4(packOcclusionDepth(A, CSZToDephtKey(Cp.z)), 1.0); + + // KEEP IT for Debugging + // Debug Normal: outFragColor = vec4((Cn + vec3(1.0))* 0.5, 1.0); + // Debug Radius outFragColor = vec4(vec3(ssDiskRadius / 100.0), 1.0); + // Debug MaxMiplevel outFragColor = vec4(1.0 - vec3(float(clamp(findMSB(int(ssDiskRadius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL))/ float(MAX_MIP_LEVEL)), 1.0); + // Debug OffsetPosition + // float ssR; + // vec2 unitOffset = tapLocation(int(getNumSamples() - 1), 0, ssR); + // vec3 Q = getOffsetPosition(side, ssC, unitOffset, ssR * ssDiskRadius); + //outFragColor = vec4(vec3(Q.x / 10.0, Q.y / 2.0, -Q.z/ 3.0), 1.0); + // vec3 v = normalize(Q - Cp); + //outFragColor = vec4((v + vec3(1.0))* 0.5, 1.0); + // outFragColor = vec4((Cn + vec3(1.0))* 0.5, 1.0); + //outFragColor = vec4(vec3(ssDiskRadius / 100.0), 1.0); +} diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 7512d6568c..7bad1420bf 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -21,9 +21,9 @@ const int MAX_MIP_LEVEL = 5; uniform sampler2D pyramidMap; float getZEye(ivec2 pixel) { - return -texelFetch(pyramidMap, pixel, 0/*getResolutionLevel()*/).x; + return -texelFetch(pyramidMap, pixel, getResolutionLevel()).x; } - +/* vec3 evalEyePositionFromZeye(int side, float Zeye, vec2 texcoord) { // compute the view space position using the depth // basically manually pick the proj matrix components to do the inverse @@ -31,7 +31,7 @@ vec3 evalEyePositionFromZeye(int side, float Zeye, vec2 texcoord) { float Ye = (-Zeye * (texcoord.y * 2.0 - 1.0) - Zeye * frameTransform._projection[side][2][1] - frameTransform._projection[side][3][1]) / frameTransform._projection[side][1][1]; return vec3(Xe, Ye, Zeye); } - +*/ out vec4 outFragColor; uniform sampler2D normalMap; @@ -61,7 +61,7 @@ vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec2 unitOffset, float ssR) { ivec2 ssP = ssOffset + ssC; if (bool(isBorderingEnabled())) { ssP.x = ((ssP.x < 0 || ssP.x >= side.z) ? ssC.x - ssOffset.x : ssP.x); - ssP.y = ((ssP.y < 0 || ssP.y >= int(getWidthHeight().y)) ? ssC.y - ssOffset.y : ssP.y); + ssP.y = ((ssP.y < 0 || ssP.y >= int(getWidthHeight(getResolutionLevel()).y)) ? ssC.y - ssOffset.y : ssP.y); } ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); @@ -108,11 +108,11 @@ void main(void) { float Zeye = getZEye(ssC); // Stereo side info - ivec3 side = getStereoSideInfo(ssC.x); + ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel()); // From now on, ssC is the pixel pos in the side ssC.x -= side.y; - vec2 fragPos = (vec2(ssC) + 0.5) / vec2(getStereoSideWidth(), getStereoSideHeight()); + vec2 fragPos = (vec2(ssC) + vec2(0.5)) / vec2(getStereoSideWidth(getResolutionLevel()), getStereoSideHeight(getResolutionLevel())); // The position and normal of the pixel fragment in Eye space vec3 Cp = evalEyePositionFromZeye(side.x, Zeye, fragPos); @@ -120,7 +120,7 @@ void main(void) { // Choose the screen-space sample radius // proportional to the projected area of the sphere - float ssDiskRadius = -getProjScale() * getRadius() / Cp.z; + float ssDiskRadius = -( getProjScale(getResolutionLevel()) * getRadius() / Cp.z ) * getPerspectiveScale(); // Let's make noise float randomPatternRotationAngle = getAngleDithering(ssC); @@ -128,7 +128,7 @@ void main(void) { // Accumulate the Obscurance for each samples float sum = 0.0; for (int i = 0; i < getNumSamples(); ++i) { - sum += sampleAO(side, ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle); + sum += sampleAO(side.xyz, ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle); } float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); @@ -158,4 +158,5 @@ void main(void) { // vec3 v = normalize(Q - Cp); //outFragColor = vec4((v + vec3(1.0))* 0.5, 1.0); // outFragColor = vec4((Cn + vec3(1.0))* 0.5, 1.0); + //outFragColor = vec4(vec3(ssDiskRadius / 100.0), 1.0); } diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml new file mode 100644 index 0000000000..ae4ec7770b --- /dev/null +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -0,0 +1,50 @@ +// +// surfaceGeometryPass.qml +// +// Created by Sam Gateau on 6/6/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "configSlider" + +Column { + spacing: 8 + Column { + id: surfaceGeometry + spacing: 10 + + Column{ + Repeater { + model: [ + "Radius:radius:2.0:false", + "Level:obscuranceLevel:1.0:false", + "Scale:perspectiveScale:2.0:false", + ] + ConfigSlider { + label: qsTr(modelData.split(":")[0]) + integral: (modelData.split(":")[3] == 'true') + config: Render.getConfig("AmbientOcclusion") + property: modelData.split(":")[1] + max: modelData.split(":")[2] + min: 0.0 + } + } + Repeater { + model: [ + "resolutionLevel:resolutionLevel", + "ditheringEnabled:ditheringEnabled", + "borderingEnabled:borderingEnabled", + ] + CheckBox { + text: qsTr(modelData.split(":")[0]) + checked: Render.getConfig("AmbientOcclusion")[modelData.split(":")[1]] + onCheckedChanged: { Render.getConfig("AmbientOcclusion")[modelData.split(":")[1]] = checked } + } + } + } + } +} diff --git a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js new file mode 100644 index 0000000000..fbd1e10099 --- /dev/null +++ b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js @@ -0,0 +1,19 @@ +// +// debugSurfaceGeometryPass.js +// +// Created by Sam Gateau on 6/6/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +// Set up the qml ui +var qml = Script.resolvePath('ambientOcclusionPass.qml'); +var window = new OverlayWindow({ + title: 'Ambient Occlusion Pass', + source: qml, + width: 400, height: 170, +}); +window.setPosition(Window.innerWidth - 420, 50 + 550 + 50); +window.closed.connect(function() { Script.stop(); }); From 7cf41d441ecbfa76210cde844fa584c223c5d713 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 1 Aug 2016 12:28:57 -0700 Subject: [PATCH 008/332] filtering by place --- interface/resources/qml/AddressBarDialog.qml | 237 ++++++++++--------- 1 file changed, 128 insertions(+), 109 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 6da55c59d4..b80ab579c8 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -49,9 +49,7 @@ Window { addressLine.text = card.path; toggleOrGo(true); } - property var allDomains: []; - property var suggestionChoices: []; - property var domainsBaseUrl: null; + property var allPlaces: []; property int cardWidth: 200; property int cardHeight: 152; @@ -78,7 +76,7 @@ Window { width: cardWidth; height: cardHeight; goFunction: goCard; - path: model.name; + path: model.name + model.path; thumbnail: model.thumbnail; placeText: model.name; usersText: model.online_users + ((model.online_users === 1) ? ' person' : ' people'); @@ -199,135 +197,156 @@ Window { request.open("GET", url, true); request.send(); } - // call iterator(element, icb) once for each element of array, and then cb(error) when icb(error) has been called by each iterator. - // short-circuits if error. Note that iterator MUST be an asynchronous function. (Use setTimeout if necessary.) - function asyncEach(array, iterator, cb) { - var count = array.length; - function icb(error) { - if (!--count || error) { - count = -1; // don't cb multiple times (e.g., if error) - cb(error); - } - } + function asyncMap(array, iterator, cb) { + // call iterator(element, icb) once for each element of array, and then cb(error, mappedResult) + // when icb(error, mappedElement) has been called by each iterator. + // Calls to iterator are overlapped and map call icb in any order, but the mappedResults are collected in the same + // order as the elements of the array. + // short-circuits if error. Note that iterator MUST be an asynchronous function. (Use setTimeout if necessary.) + var count = array.length, results = []; if (!count) { - return cb(); + return cb(null, results); } - array.forEach(function (element) { - iterator(element, icb); + array.forEach(function (element, index) { + if (count < 0) { // don't keep iterating after we short-circuit + return; + } + iterator(element, function (error, mapped) { + results[index] = mapped; + if (error || !--count) { + count = 1; // don't cb multiple times if error + cb(error, results); + } + }); }); } + // Example: + /*asyncMap([0, 1, 2, 3, 4, 5, 6], function (elt, icb) { + console.log('called', elt); + setTimeout(function () { + console.log('answering', elt); + icb(null, elt); + }, Math.random() * 1000); + }, console.log); */ function identity(x) { return x; } - function addPictureToDomain(domainInfo, cb) { // asynchronously add thumbnail and lobby to domainInfo, if available, and cb(error) - // This requests data for all the names at once, and just uses the first one to come back. - // We might change this to check one at a time, which would be less requests and more latency. - domainInfo.thumbnail = ''; // Regardless of whether we fill it in later, qt models must start with the all values they will have. - asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(identity), function (name, icb) { - var url = "https://metaverse.highfidelity.com/api/v1/places/" + name; - getRequest(url, function (error, json) { - var previews = !error && json.data.place.previews; - if (previews) { - if (!domainInfo.thumbnail) { // just grab the first one - domainInfo.thumbnail = previews.thumbnail; - } - if (!domainInfo.lobby) { - domainInfo.lobby = previews.lobby; - } - } - icb(error); - }); - }, cb); + function handleError(error, data, cb) { // cb(error) and answer truthy if needed, else falsey + if (!error && (data.status === 'success')) { + return; + } + cb(error || new Error(data.status + ': ' + data.error)); + return true; } - function getDomains(options, cb) { // cb(error, arrayOfData) - if (!options.page) { - options.page = 1; - } - if (!domainsBaseUrl) { - var domainsOptions = [ - 'open', // published hours handle now - // fixme hrs restore 'active', // has at least one person connected. FIXME: really want any place that is verified accessible. - // FIXME: really want places I'm allowed in, not just open ones. - 'restriction=open', // Not by whitelist, etc. FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. - // FIXME add maturity - 'protocol=' + encodeURIComponent(AddressManager.protocolVersion()), - 'sort_by=users', - 'sort_order=desc', - ]; - domainsBaseUrl = "https://metaverse.highfidelity.com/api/v1/domains/all?" + domainsOptions.join('&'); - } - var url = domainsBaseUrl + "&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; - getRequest(url, function (error, json) { - if (!error && (json.status !== 'success')) { - error = new Error("Bad response: " + JSON.stringify(json)); - } - if (error) { - error.message += ' for ' + url; - return cb(error); - } - var domains = json.data.domains; - if (json.current_page < json.total_pages) { - options.page++; - return getDomains(options, function (error, others) { - cb(error, domains.concat(others)); - }); - } - cb(null, domains); - }); - } - - function filterChoicesByText() { - function fill1(targetIndex) { - var data = filtered[targetIndex]; - if (!data) { - if (targetIndex < suggestions.count) { - suggestions.remove(targetIndex); - } + function getPlace(placeData, cb) { // cb(error, side-effected-placeData), after adding path, thumbnails, and description + getRequest('https://metaverse.highfidelity.com/api/v1/places/' + placeData.name, function (error, data) { + if (handleError(error, data, cb)) { return; } - console.log('suggestion:', JSON.stringify(data)); - if (suggestions.count <= targetIndex) { - suggestions.append(data); - } else { - suggestions.set(targetIndex, data); + var place = data.data.place, previews = place.previews; + placeData.path = place.path; + if (previews && previews.thumbnail) { + placeData.thumbnail = previews.thumbnail; } + if (place.description) { + placeData.description = place.description; + placeData.searchText += ' ' + place.description.toUpperCase(); + } + cb(error, placeData); + }); + } + function mapDomainPlaces(domain, cb) { // cb(error, arrayOfDomainPlaceData) + function addPlace(name, icb) { + getPlace({ + name: name, + tags: domain.tags, + thumbnail: "", + description: "", + path: "", + searchText: [name].concat(domain.tags).join(' ').toUpperCase(), + online_users: domain.online_users + }, icb); } - var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity); - var filtered = !words.length ? suggestionChoices : allDomains.filter(function (domain) { - var text = domain.names.concat(domain.tags).join(' '); - if (domain.description) { - text += domain.description; + // IWBNI we could get these results in order with most-recent-entered first. + // In any case, we don't really need to preserve the domain.names order in the results. + asyncMap(domain.names, addPlace, cb); + } + + function suggestable(place) { + return (place.name !== AddressManager.hostname) // Not our entry, but do show other entry points to current domain. + && place.thumbnail + && place.online_users; // at least one present means it's actually online + } + function getDomainPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model + // Each page of results is processed completely before we start on the next page. + // For each page of domains, we process each domain in parallel, and for each domain, process each place name in parallel. + // This gives us minimum latency within the page, but we do preserve the order within the page by using asyncMap and + // only appending the collected results. + var params = [ + 'open', // published hours handle now + // FIXME: should determine if place is actually running + 'restriction=open', // Not by whitelist, etc. FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. + // FIXME add maturity + 'protocol=' + encodeURIComponent(AddressManager.protocolVersion()), + 'sort_by=users', + 'sort_order=desc', + 'page=' + pageNumber + ]; + getRequest('https://metaverse.highfidelity.com/api/v1/domains/all?' + params.join('&'), function (error, data) { + if (handleError(error, data, cb)) { + return; } - text = text.toUpperCase(); - return words.every(function (word) { - return text.indexOf(word) >= 0; + asyncMap(data.data.domains, mapDomainPlaces, function (error, pageResults) { + if (error) { + return cb(error); + } + // pageResults is now [ [ placeDataOneForDomainOne, placeDataTwoForDomainOne, ...], [ placeDataTwoForDomainTwo...] ] + pageResults.forEach(function (domainResults) { + allPlaces = allPlaces.concat(domainResults); + if (!addressLine.text) { // Don't add if the user is already filtering + domainResults.forEach(function (place) { + if (suggestable(place)) { + suggestions.append(place); + } + }); + } + }); + if (data.current_page < data.total_pages) { + return getDomainPage(pageNumber + 1, cb); + } + cb(); }); }); - for (var index in filtered) { fill1(index); } + } + function filterChoicesByText() { + suggestions.clear(); + var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity); + function matches(place) { + if (!words.length) { + return suggestable(place); + } + return words.every(function (word) { + return place.searchText.indexOf(word) >= 0; + }); + } + allPlaces.forEach(function (place) { + if (matches(place)) { + suggestions.append(place); + } + }); } function fillDestinations() { - allDomains = suggestionChoices = []; - getDomains({minUsers: 0, maxUsers: 20}, function (error, domains) { + allPlaces = []; + suggestions.clear(); + getDomainPage(1, function (error) { if (error) { console.log('domain query failed:', error); - return filterChoicesByText(); } - var here = AddressManager.hostname; // don't show where we are now. - allDomains = domains.filter(function (domain) { return domain.name !== here; }); - // Whittle down suggestions to those that have at least one user, and try to get pictures. - suggestionChoices = allDomains.filter(function (domain) { return true/*fixme hrs restore domain.online_users*/; }); - asyncEach(domains, addPictureToDomain, function (error) { - if (error) { - console.log('place picture query failed:', error); - } - // Whittle down more by requiring a picture. - // fixme hrs restore suggestionChoices = suggestionChoices.filter(function (domain) { return domain.lobby; }); - filterChoicesByText(); - }); + console.log('domain query finished', allPlaces.length); }); } From c3bf52267d4c57af4aa2b291cfc8ae6d8f33a894 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 1 Aug 2016 15:27:29 -0700 Subject: [PATCH 009/332] Don't suggest loaded places. --- interface/resources/qml/AddressBarDialog.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index b80ab579c8..9a8cfddc02 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -278,7 +278,8 @@ Window { function suggestable(place) { return (place.name !== AddressManager.hostname) // Not our entry, but do show other entry points to current domain. && place.thumbnail - && place.online_users; // at least one present means it's actually online + && place.online_users // at least one present means it's actually online + && place.online_users <= 20; } function getDomainPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model // Each page of results is processed completely before we start on the next page. From 1d7cdff2bcaf18e3d4835e063002dfd224936205 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 1 Aug 2016 13:24:22 -0700 Subject: [PATCH 010/332] Snapshot button plus script Minor change to allow snapshots to not notify (though the old way with CTRL+S still does). Added button to do it, saves them to disk. The plan is for us to add UI to share (or not) the snapshot. That's why we are not going through the notifications. Also, the script makes sure to hide/unhide hud and overlays. Next we will upload the pick to AWS via data-web (if you are logged in), and _then_ make the share UI. --- interface/src/Application.cpp | 6 +- interface/src/Application.h | 4 +- .../scripting/WindowScriptingInterface.cpp | 7 ++ .../src/scripting/WindowScriptingInterface.h | 3 +- scripts/system/assets/images/tools/snap.svg | 109 ++++++++++++++++++ scripts/system/notifications.js | 12 +- scripts/system/snapshot.js | 66 +++++++++++ 7 files changed, 196 insertions(+), 11 deletions(-) create mode 100755 scripts/system/assets/images/tools/snap.svg create mode 100644 scripts/system/snapshot.js diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e294ae38ad..abc0b91b2d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2281,7 +2281,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } else if (isOption && !isShifted && !isMeta) { Menu::getInstance()->triggerOption(MenuOption::ScriptEditor); } else if (!isOption && !isShifted && isMeta) { - takeSnapshot(); + takeSnapshot(true); } break; @@ -5070,7 +5070,7 @@ void Application::toggleLogDialog() { } } -void Application::takeSnapshot() { +void Application::takeSnapshot(bool notify) { QMediaPlayer* player = new QMediaPlayer(); QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav"); player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath())); @@ -5078,7 +5078,7 @@ void Application::takeSnapshot() { QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot()); - emit DependencyManager::get()->snapshotTaken(path); + emit DependencyManager::get()->snapshotTaken(path, notify); } float Application::getRenderResolutionScale() const { diff --git a/interface/src/Application.h b/interface/src/Application.h index 0af65f665f..ea6a117cfa 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -247,6 +247,8 @@ public: float getAvatarSimrate() const { return _avatarSimCounter.rate(); } float getAverageSimsPerSecond() const { return _simCounter.rate(); } + + void takeSnapshot(bool notify); signals: void svoImportRequested(const QString& url); @@ -373,8 +375,6 @@ private: int sendNackPackets(); - void takeSnapshot(); - MyAvatar* getMyAvatar() const; void checkSkeleton() const; diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index b165cda135..fb7be86096 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -199,3 +199,10 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) { qDebug() << "Copying"; QApplication::clipboard()->setText(text); } + +void WindowScriptingInterface::takeSnapshot(bool notify) { + // only evil-doers call takeSnapshot from a random thread + qApp->postLambdaEvent([&] { + qApp->takeSnapshot(notify); + }); +} diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 9d73111333..1abcb9db35 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -54,12 +54,13 @@ public slots: QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); void copyToClipboard(const QString& text); + void takeSnapshot(bool notify); signals: void domainChanged(const QString& domainHostname); void svoImportRequested(const QString& url); void domainConnectionRefused(const QString& reasonMessage, int reasonCode); - void snapshotTaken(const QString& path); + void snapshotTaken(const QString& path, bool notify); private slots: WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height); diff --git a/scripts/system/assets/images/tools/snap.svg b/scripts/system/assets/images/tools/snap.svg new file mode 100755 index 0000000000..c540f307ae --- /dev/null +++ b/scripts/system/assets/images/tools/snap.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 98a31d708c..34f9814d8a 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -527,12 +527,14 @@ function onDomainConnectionRefused(reason) { createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED); } -function onSnapshotTaken(path) { - var imageProperties = { - path: Script.resolvePath("file:///" + path), - aspectRatio: Window.innerWidth / Window.innerHeight +function onSnapshotTaken(path, notify) { + if (notify) { + var imageProperties = { + path: Script.resolvePath("file:///" + path), + aspectRatio: Window.innerWidth / Window.innerHeight + } + createNotification(wordWrap("Snapshot saved to " + path), NotificationType.SNAPSHOT, imageProperties); } - createNotification(wordWrap("Snapshot saved to " + path), NotificationType.SNAPSHOT, imageProperties); } // handles mouse clicks on buttons diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js new file mode 100644 index 0000000000..87ea3f29ec --- /dev/null +++ b/scripts/system/snapshot.js @@ -0,0 +1,66 @@ +// +// snapshot.js +// +// Created by David Kelly on 1 August 2016 +// Copyright 2016 High Fidelity, Inc +// +// Distributed under the Apache License, Version 2.0 +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +var SNAPSHOT_DELAY = 500; // 500ms +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); +var resetOverlays; +var button = toolBar.addButton({ + objectName: "snapshot", + imageURL: Script.resolvePath("assets/images/tools/snap.svg"), + visible: true, + buttonState: 1, + defaultState: 1, + hoverState: 1, + alpha: 0.9, +}); + +function onClicked() { + // update button states + resetOverlays = Menu.isOptionChecked("Overlays"); + Window.snapshotTaken.connect(resetButtons); + + button.writeProperty("buttonState", 0); + button.writeProperty("defaultState", 0); + button.writeProperty("hoverState", 2); + + // hide overlays if they are on + if (resetOverlays) { + Menu.setIsOptionChecked("Overlays", false); + } + + // hide hud + toolBar.writeProperty("visible", false); + + // take snapshot (with no notification) + Script.setTimeout(function () { + Window.takeSnapshot(false); + }, SNAPSHOT_DELAY); +} + +function resetButtons(path, notify) { + // show overlays if they were on + if (resetOverlays) { + Menu.setIsOptionChecked("Overlays", true); + } + // show hud + toolBar.writeProperty("visible", true); + + // update button states + button.writeProperty("buttonState", 1); + button.writeProperty("defaultState", 1); + button.writeProperty("hoverState", 3); + Window.snapshotTaken.disconnect(resetButtons); +} + +button.clicked.connect(onClicked); + +Script.scriptEnding.connect(function () { + toolBar.removeButton("snapshot"); + button.clicked.disconnect(onClicked); +}); From fcd2947b19fbd50998fccfce979594e43d37b287 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 1 Aug 2016 18:37:22 -0700 Subject: [PATCH 011/332] Doh -- forgot to push this same logic as before to get a mono image in the HMD. --- .../src/display-plugins/OpenGLDisplayPlugin.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index e0c87fbbed..a4db2811e0 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -662,10 +662,15 @@ void OpenGLDisplayPlugin::withMainThreadContext(std::function f) const { QImage OpenGLDisplayPlugin::getScreenshot() const { using namespace oglplus; - QImage screenshot(_compositeFramebuffer->size.x, _compositeFramebuffer->size.y, QImage::Format_RGBA8888); + auto width = _compositeFramebuffer->size.x; + if (isHmd()) { + width /= 2; + } + + QImage screenshot(width, _compositeFramebuffer->size.y, QImage::Format_RGBA8888); withMainThreadContext([&] { Framebuffer::Bind(Framebuffer::Target::Read, _compositeFramebuffer->fbo); - Context::ReadPixels(0, 0, _compositeFramebuffer->size.x, _compositeFramebuffer->size.y, enums::PixelDataFormat::RGBA, enums::PixelDataType::UnsignedByte, screenshot.bits()); + Context::ReadPixels(0, 0, width, _compositeFramebuffer->size.y, enums::PixelDataFormat::RGBA, enums::PixelDataType::UnsignedByte, screenshot.bits()); }); return screenshot.mirrored(false, true); } From 4ec971b449259be9d71ef010bcb08ea0a353105c Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 1 Aug 2016 23:17:32 -0700 Subject: [PATCH 012/332] MOre debugging --- .../src/AmbientOcclusionEffect.cpp | 2 + .../render-utils/src/ssao_debugOcclusion.slf | 51 +++++++++++++++---- .../render/debugAmbientOcclusionPass.js | 19 +++++++ 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 0d339c71ed..71222ee671 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -480,6 +480,7 @@ const gpu::PipelinePointer& DebugAmbientOcclusion::getDebugPipeline() { gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), AmbientOcclusionEffect_FrameTransformSlot)); slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("debugAmbientOcclusionBuffer"), 2)); slotBindings.insert(gpu::Shader::Binding(std::string("pyramidMap"), AmbientOcclusionEffect_LinearDepthMapSlot)); gpu::Shader::makeProgram(*program, slotBindings); @@ -550,6 +551,7 @@ void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, batch.setUniformBuffer(AmbientOcclusionEffect_FrameTransformSlot, frameTransform->getFrameTransformBuffer()); batch.setUniformBuffer(AmbientOcclusionEffect_ParamsSlot, ambientOcclusionUniforms); + batch.setUniformBuffer(2, _parametersBuffer); // We need this with the mips levels diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index 7bad1420bf..0c40042ba2 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -13,7 +13,17 @@ <$declareAmbientOcclusion()$> <$declarePackOcclusionDepth()$> +struct DebugParams{ + vec4 pixelInfo; +}; +uniform debugAmbientOcclusionBuffer { + DebugParams debugParams; +}; + +vec2 getDebugCursorTexcoord(){ + return debugParams.pixelInfo.xy; +} const int LOG_MAX_OFFSET = 3; const int MAX_MIP_LEVEL = 5; @@ -23,15 +33,7 @@ uniform sampler2D pyramidMap; float getZEye(ivec2 pixel) { return -texelFetch(pyramidMap, pixel, getResolutionLevel()).x; } -/* -vec3 evalEyePositionFromZeye(int side, float Zeye, vec2 texcoord) { - // compute the view space position using the depth - // basically manually pick the proj matrix components to do the inverse - float Xe = (-Zeye * (texcoord.x * 2.0 - 1.0) - Zeye * frameTransform._projection[side][2][0] - frameTransform._projection[side][3][0]) / frameTransform._projection[side][0][0]; - float Ye = (-Zeye * (texcoord.y * 2.0 - 1.0) - Zeye * frameTransform._projection[side][2][1] - frameTransform._projection[side][3][1]) / frameTransform._projection[side][1][1]; - return vec3(Xe, Ye, Zeye); -} -*/ + out vec4 outFragColor; uniform sampler2D normalMap; @@ -52,6 +54,13 @@ vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR){ return vec2(cos(angle), sin(angle)); } +vec3 getTapLocation(int sampleNumber, float spinAngle, float outerRadius) { + // Radius relative to ssR + float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); + float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle; + return vec2(cos(angle), sin(angle), alpha * outerRadius); +} + vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec2 unitOffset, float ssR) { // Derivation: // mipLevel = floor(log(ssR / MAX_OFFSET)); @@ -79,6 +88,7 @@ vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec2 unitOffset, float ssR) { return P; } + float sampleAO(in ivec3 side, in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssDiskRadius, in int tapIndex, in float randomPatternRotationAngle) { // Offset on the unit disk, spun for this pixel float ssR; @@ -100,9 +110,13 @@ float sampleAO(in ivec3 side, in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssD return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0); } + void main(void) { + // Pixel Debugged + vec2 cursorUV = getDebugCursorTexcoord(); + ivec2 ssC = ivec2(cursorUV * vec2(getStereoSideWidth(getResolutionLevel()), getStereoSideHeight(getResolutionLevel()))); // Pixel being shaded - ivec2 ssC = ivec2(gl_FragCoord.xy); + //ivec2 ssC = ivec2(gl_FragCoord.xy); // Fetch the z under the pixel (stereo or not) float Zeye = getZEye(ssC); @@ -122,15 +136,32 @@ void main(void) { // proportional to the projected area of the sphere float ssDiskRadius = -( getProjScale(getResolutionLevel()) * getRadius() / Cp.z ) * getPerspectiveScale(); + vec2 fragToCursor = cursorUV * vec2(getStereoSideWidth(getResolutionLevel()), getStereoSideHeight(getResolutionLevel())) - gl_FragCoord.xy; + if (dot(fragToCursor,fragToCursor) > ssDiskRadius * ssDiskRadius) { + discard; + } + // Let's make noise float randomPatternRotationAngle = getAngleDithering(ssC); // Accumulate the Obscurance for each samples float sum = 0.0; + float keepTapRadius = 2.0; + bool keep = (dot(fragToCursor,fragToCursor) < keepTapRadius); for (int i = 0; i < getNumSamples(); ++i) { + vec3 tap = getTapLocation(i, randomPatternRotationAngle, outerRadius); + + vec2 fragToTap = vec2(ssC) + tap.xy * tap.z - gl_FragCoord.xy; + if (dot(fragToTap,fragToTap) < keepTapRadius) { + keep = true; + } sum += sampleAO(side.xyz, ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle); } + if (!keep) { + discard; + } + float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); Date: Tue, 2 Aug 2016 10:13:35 -0700 Subject: [PATCH 013/332] keep qml text fields up to date with vive virtual keyboard --- plugins/openvr/src/OpenVrHelpers.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index c93a2178b5..6f5c1b9511 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -170,8 +170,7 @@ void showOpenVrKeyboard(bool show = true) { } } -void finishOpenVrKeyboardInput() { - auto offscreenUi = DependencyManager::get(); +void updateFromOpenVrKeyboardInput() { auto chars = _overlay->GetKeyboardText(textArray, 8192); auto newText = QString(QByteArray(textArray, chars)); _keyboardFocusObject->setProperty("text", newText); @@ -181,6 +180,10 @@ void finishOpenVrKeyboardInput() { //QInputMethodEvent event(_existingText, QList()); //event.setCommitString(newText, 0, _existingText.size()); //qApp->sendEvent(_keyboardFocusObject, &event); +} +void finishOpenVrKeyboardInput() { + auto offscreenUi = DependencyManager::get(); + updateFromOpenVrKeyboardInput(); // Simulate an enter press on the top level window to trigger the action if (0 == (_currentHints & Qt::ImhMultiLine)) { qApp->sendEvent(offscreenUi->getWindow(), &QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::KeyboardModifiers(), QString("\n"))); @@ -261,6 +264,11 @@ void handleOpenVrEvents() { QMetaObject::invokeMethod(qApp, "quit"); break; + case vr::VREvent_KeyboardCharInput: + // Make the focused field match the keyboard results, inclusive of combining characters and such. + updateFromOpenVrKeyboardInput(); + break; + case vr::VREvent_KeyboardDone: finishOpenVrKeyboardInput(); From 70e07f329918ebf3883c217639dbd032eeec3234 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 2 Aug 2016 13:56:01 -0700 Subject: [PATCH 014/332] remove highlight move animation --- interface/resources/qml/AddressBarDialog.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 9a8cfddc02..b9e7941b76 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -83,6 +83,8 @@ Window { hoverThunk: function () { ListView.view.currentIndex = index; } unhoverThunk: function () { ListView.view.currentIndex = -1; } } + highlightMoveDuration: -1; + highlightMoveVelocity: -1; highlight: Rectangle { color: "transparent"; border.width: 2; border.color: "#1FA5E8"; z: 1; } } @@ -272,7 +274,7 @@ Window { } // IWBNI we could get these results in order with most-recent-entered first. // In any case, we don't really need to preserve the domain.names order in the results. - asyncMap(domain.names, addPlace, cb); + asyncMap(domain.names || [], addPlace, cb); } function suggestable(place) { From d34e72512bd4c6debee3957a715b3da7480b75e4 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 2 Aug 2016 15:13:48 -0700 Subject: [PATCH 015/332] Add snapshot.js to defaultScripts --- scripts/defaultScripts.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 817d63582d..2e707d95ce 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -27,3 +27,4 @@ Script.load("system/controllers/grab.js"); Script.load("system/controllers/teleport.js"); Script.load("system/dialTone.js"); Script.load("system/firstPersonHMD.js"); +Script.load("system/snapshot.js"); From 6d768d832723f5894d10c85b978e7796f9946d2d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Aug 2016 15:23:46 -0700 Subject: [PATCH 016/332] hand controllers send touch events instead of mouse events to web entities This gives a much better experience when scrolling web content. --- interface/src/Application.cpp | 41 +++++++++++ interface/src/Application.h | 5 ++ .../src/display-plugins/CompositorHelper.cpp | 12 ++++ .../src/display-plugins/CompositorHelper.h | 5 +- .../src/RenderableWebEntityItem.cpp | 32 +++++++++ .../src/RenderableWebEntityItem.h | 1 + .../system/controllers/handControllerGrab.js | 68 +++++++++++++++---- 7 files changed, 150 insertions(+), 14 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index aefbeff1c3..3d1b28ada8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3553,6 +3553,47 @@ void Application::sendEntityMouseEvent(const QUuid& id, const QMouseEvent& mouse } } +void Application::sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QTouchEvent::TouchPoint point; + point.setId(fingerID); + point.setState(Qt::TouchPointMoved); + QList touchPoints; + touchPoints.push_back(point); + QTouchEvent touchEvent(QEvent::TouchUpdate, nullptr, Qt::NoModifier, Qt::TouchPointMoved, touchPoints); + sendEntityTouchEvent(entityID, touchEvent, intersectionPoint); +} + +void Application::sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QTouchEvent::TouchPoint point; + point.setId(fingerID); + point.setState(Qt::TouchPointPressed); + QList touchPoints; + touchPoints.push_back(point); + QTouchEvent touchEvent(QEvent::TouchBegin, nullptr, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); + sendEntityTouchEvent(entityID, touchEvent, intersectionPoint); +} + +void Application::sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QTouchEvent::TouchPoint point; + point.setId(fingerID); + point.setState(Qt::TouchPointReleased); + QList touchPoints; + touchPoints.push_back(point); + QTouchEvent touchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased, touchPoints); + sendEntityTouchEvent(entityID, touchEvent, intersectionPoint); +} + +void Application::sendEntityTouchEvent(const QUuid& id, const QTouchEvent& touchEvent, const glm::vec3& intersectionPoint) { + auto entityScriptingInterface = DependencyManager::get(); + EntityItemID entityItemID(id); + auto properties = entityScriptingInterface->getEntityProperties(entityItemID); + if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); + RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); + webEntity->handleTouchEvent(touchEvent, intersectionPoint); + } +} + void Application::updateDialogs(float deltaTime) const { PerformanceTimer perfTimer("updateDialogs"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); diff --git a/interface/src/Application.h b/interface/src/Application.h index 139efb4ba7..bfa0856103 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -321,7 +321,12 @@ public slots: void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); + void sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + void sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + void sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + private: + void sendEntityTouchEvent(const QUuid& id, const QTouchEvent& touchEvent, const glm::vec3& intersectionPoint); void sendEntityMouseEvent(const QUuid& id, const QMouseEvent& mouseEvent, const glm::vec3& intersectionPoint); private slots: diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index e460fa6648..3a154af462 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -473,3 +473,15 @@ void ReticleInterface::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersec void ReticleInterface::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseUpEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); } + +void ReticleInterface::sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityTouchUpdateEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +} + +void ReticleInterface::sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityTouchBeginEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +} + +void ReticleInterface::sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityTouchEndEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +} diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h index b93ffeb67b..78d4a10bf0 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -209,7 +209,10 @@ public: Q_INVOKABLE void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); Q_INVOKABLE void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); Q_INVOKABLE void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); - // TODO: right mouse + double click + + Q_INVOKABLE void sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); private: CompositorHelper* _compositor; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 2b01de9beb..20e921cd16 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -238,6 +238,38 @@ void RenderableWebEntityItem::handleMouseEvent(QMouseEvent event, glm::vec3 inte QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); } +void RenderableWebEntityItem::handleTouchEvent(QTouchEvent event, glm::vec3 intersectionPoint) { + // Ignore mouse interaction if we're locked + if (getLocked()) { + return; + } + + // Map the intersection point to an actual offscreen pixel + glm::vec3 point = intersectionPoint; + glm::vec3 dimensions = getDimensions(); + point -= getPosition(); + point = glm::inverse(getRotation()) * point; + point /= dimensions; + point += 0.5f; + point.y = 1.0f - point.y; + point *= dimensions * (METERS_TO_INCHES * DPI); + + QList touchPoints = event.touchPoints(); + for (auto& touchPoint : touchPoints) { + touchPoint.setPos(QPointF(point.x, point.y)); + touchPoint.setScreenPos(QPointF(point.x, point.y)); + } + + // Forward the touch event. + QTouchEvent mappedEvent(event.type(), + event.device(), + event.modifiers(), + event.touchPointStates(), + touchPoints); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); +} + + void RenderableWebEntityItem::destroyWebSurface() { if (_webSurface) { --_currentWebCount; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index b20a9af928..c6afacdf5f 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -34,6 +34,7 @@ public: QObject* getEventHandler(); void handleMouseEvent(QMouseEvent event, glm::vec3 intersectionPoint); + void handleTouchEvent(QTouchEvent event, glm::vec3 intersectionPoint); void update(const quint64& now) override; bool needsToCallUpdate() const override { return _webSurface != nullptr; } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 3d4c9ac8b0..6a3f0ca949 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -192,6 +192,7 @@ CONTROLLER_STATE_MACHINE[STATE_OFF] = { }; CONTROLLER_STATE_MACHINE[STATE_SEARCHING] = { name: "searching", + enterMethod: "searchEnter", updateMethod: "search" }; CONTROLLER_STATE_MACHINE[STATE_DISTANCE_HOLDING] = { @@ -220,6 +221,18 @@ CONTROLLER_STATE_MACHINE[STATE_FAR_TRIGGER] = { updateMethod: "farTrigger" }; +function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) { + var rayDirectionDotPlaneNormal = Vec3.dot(rayDirection, planeNormal); + if (rayDirectionDotPlaneNormal > 0.00001 || rayDirectionDotPlaneNormal < -0.00001) { + var rayStartDotPlaneNormal = Vec3.dot(Vec3.subtract(planePosition, rayStart), planeNormal); + var distance = rayStartDotPlaneNormal / rayDirectionDotPlaneNormal; + return {hit: true, distance: distance}; + } else { + // ray is parallel to the plane + return {hit: false, distance: 0}; + } +} + function stateToName(state) { return CONTROLLER_STATE_MACHINE[state] ? CONTROLLER_STATE_MACHINE[state].name : "???"; } @@ -965,7 +978,7 @@ function MyController(hand) { } else if (potentialEquipHotspot && Vec3.distance(this.lastHapticPulseLocation, currentLocation) > HAPTIC_TEXTURE_DISTANCE) { Controller.triggerHapticPulse(HAPTIC_TEXTURE_STRENGTH, HAPTIC_TEXTURE_DURATION, this.hand); this.lastHapticPulseLocation = currentLocation; - } + } this.prevPotentialEquipHotspot = potentialEquipHotspot; }; @@ -1243,6 +1256,10 @@ function MyController(hand) { } }; + this.searchEnter = function() { + this.capturedWebEntity = null; + }; + this.search = function(deltaTime, timestamp) { var _this = this; var name; @@ -1266,26 +1283,51 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - // if the line probe hits a non-grabbable web entity or a web entity that is grabbed by the other hand. - // route simulated mouse events to that entity. - if (rayPickInfo.entityID && entityPropertiesCache.getProps(rayPickInfo.entityID).type === "Web" && - (!this.entityIsGrabbable(rayPickInfo.entityID) || this.getOtherHandController().grabbedEntity == rayPickInfo.entityID)) { + // route simulated touch events to a webEntity. + if (this.capturedWebEntity || + (rayPickInfo.entityID && entityPropertiesCache.getProps(rayPickInfo.entityID).type === "Web" && + (!this.entityIsGrabbable(rayPickInfo.entityID) || this.getOtherHandController().grabbedEntity == rayPickInfo.entityID))) { - if (Reticle.keyboardFocusEntity != rayPickInfo.entityID) { - Reticle.keyboardFocusEntity = rayPickInfo.entityID; + var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var pose = Controller.getPoseValue(standardControllerValue); + var worldHandPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position); + var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation); + + var focusedEntity = this.capturedWebEntity || rayPickInfo.entityID; + entityPropertiesCache.addEntity(focusedEntity); + + var props = entityPropertiesCache.getProps(focusedEntity); + var planePosition = props.position + var planeNormal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1.0}); + var rayStart = worldHandPosition; + var rayDirection = Quat.getUp(worldHandRotation); + var intersectionInfo = rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection); + + var intersectionPoint = planePosition; + if (intersectionInfo.hit && intersectionInfo.distance > 0) { + intersectionPoint = Vec3.sum(rayStart, Vec3.multiply(intersectionInfo.distance, rayDirection)); + } else { + intersectionPoint = planePosition; } - Reticle.sendEntityMouseMoveEvent(rayPickInfo.entityID, rayPickInfo.intersection); + + if (Reticle.keyboardFocusEntity != focusedEntity) { + Reticle.keyboardFocusEntity = focusedEntity; + } + + Reticle.sendEntityTouchUpdateEvent(focusedEntity, this.hand, intersectionPoint); + if (this.triggerSmoothedGrab() && !this.lastTriggerSmoothedGrab) { - print("AJT: mouse down"); - Reticle.sendEntityLeftMouseDownEvent(rayPickInfo.entityID, rayPickInfo.intersection); + Reticle.sendEntityTouchBeginEvent(focusedEntity, this.hand, intersectionPoint); + this.capturedWebEntity = focusedEntity; } if (!this.triggerSmoothedGrab() && this.lastTriggerSmoothedGrab) { - print("AJT: mouse up"); - Reticle.sendEntityLeftMouseUpEvent(rayPickInfo.entityID, rayPickInfo.intersection); + Reticle.sendEntityTouchEndEvent(focusedEntity, this.hand, intersectionPoint); + this.capturedWebEntity = null; } this.lastTriggerSmoothedGrab = this.triggerSmoothedGrab(); + equipHotspotBuddy.updateHotspots([], timestamp); - this.intersectionDistance = rayPickInfo.distance; + this.intersectionDistance = intersectionInfo.distance; } else { var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); From 0ab15f0dcdc82757367136e9b44e8f1b6abcf0a3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Aug 2016 15:35:59 -0700 Subject: [PATCH 017/332] fixed one frame lag on webEntity keyboard highlight overlay. Also, made overlay not draw in front. --- interface/src/Application.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3d1b28ada8..7a2487b366 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2879,6 +2879,20 @@ void Application::idle(float nsecsElapsed) { PerformanceTimer perfTimer("idle"); + // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing + // details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing + // details normally. + bool showWarnings = getLogger()->extraDebugging(); + PerformanceWarning warn(showWarnings, "idle()"); + + { + PerformanceTimer perfTimer("update"); + PerformanceWarning warn(showWarnings, "Application::idle()... update()"); + static const float BIGGEST_DELTA_TIME_SECS = 0.25f; + update(glm::clamp(secondsSinceLastUpdate, 0.0f, BIGGEST_DELTA_TIME_SECS)); + } + + // Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds { if (!_keyboardFocusedItem.get().isInvalidID()) { @@ -2899,18 +2913,6 @@ void Application::idle(float nsecsElapsed) { } } - // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing - // details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing - // details normally. - bool showWarnings = getLogger()->extraDebugging(); - PerformanceWarning warn(showWarnings, "idle()"); - - { - PerformanceTimer perfTimer("update"); - PerformanceWarning warn(showWarnings, "Application::idle()... update()"); - static const float BIGGEST_DELTA_TIME_SECS = 0.25f; - update(glm::clamp(secondsSinceLastUpdate, 0.0f, BIGGEST_DELTA_TIME_SECS)); - } { PerformanceTimer perfTimer("pluginIdle"); PerformanceWarning warn(showWarnings, "Application::idle()... pluginIdle()"); @@ -3509,7 +3511,7 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { _keyboardFocusHighlight->setPulseMax(1.0); _keyboardFocusHighlight->setColorPulse(1.0); _keyboardFocusHighlight->setIgnoreRayIntersection(true); - _keyboardFocusHighlight->setDrawInFront(true); + _keyboardFocusHighlight->setDrawInFront(false); } _keyboardFocusHighlight->setRotation(webEntity->getRotation()); _keyboardFocusHighlight->setPosition(webEntity->getPosition()); From e593077b4baa5ce1879d68bacaf702dc7532ff6c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 3 Aug 2016 09:39:27 -0700 Subject: [PATCH 018/332] basic display of user story feed (hardcoded switch place names vs feed) --- interface/resources/qml/AddressBarDialog.qml | 130 ++++++++++++++----- 1 file changed, 101 insertions(+), 29 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index b9e7941b76..df6a5837ac 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -49,9 +49,31 @@ Window { addressLine.text = card.path; toggleOrGo(true); } + property bool useFeed: false; property var allPlaces: []; + property var allStories: []; property int cardWidth: 200; property int cardHeight: 152; + function pastTime(timestamp) { // Answer a descriptive string + timestamp = new Date(timestamp); + var then = timestamp.getTime(), + now = Date.now(), + since = now - then, + ONE_MINUTE = 1000 * 60, + ONE_HOUR = ONE_MINUTE * 60, + hours = since / ONE_HOUR, + minutes = (hours % 1) * 60; + if (hours > 24) { + return timestamp.toDateString(); + } + if (hours > 1) { + return Math.floor(hours).toString() + ' hr ' + Math.floor(minutes) + ' min ago'; + } + if (minutes >= 2) { + return Math.floor(minutes).toString() + ' min ago'; + } + return 'about a minute ago'; + } AddressBarDialog { id: addressBarDialog @@ -76,10 +98,10 @@ Window { width: cardWidth; height: cardHeight; goFunction: goCard; - path: model.name + model.path; - thumbnail: model.thumbnail; - placeText: model.name; - usersText: model.online_users + ((model.online_users === 1) ? ' person' : ' people'); + path: model.place_name + model.path; + thumbnail: model.thumbnail_url; + placeText: model.place_name; + usersText: (model.created_at ? pastTime(model.created_at) : (model.online_users + ((model.online_users === 1) ? ' person' : ' people'))); hoverThunk: function () { ListView.view.currentIndex = index; } unhoverThunk: function () { ListView.view.currentIndex = -1; } } @@ -235,23 +257,31 @@ Window { return x; } - function handleError(error, data, cb) { // cb(error) and answer truthy if needed, else falsey + function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey if (!error && (data.status === 'success')) { return; } - cb(error || new Error(data.status + ': ' + data.error)); + if (!error) { // Create a message from the data + error = data.status + ': ' + data.error; + } + if (typeof(error) === 'string') { // Make a proper Error object + error = new Error(error); + } + error.message += ' in ' + url; // Include the url. + cb(error); return true; } function getPlace(placeData, cb) { // cb(error, side-effected-placeData), after adding path, thumbnails, and description - getRequest('https://metaverse.highfidelity.com/api/v1/places/' + placeData.name, function (error, data) { - if (handleError(error, data, cb)) { + var url = 'https://metaverse.highfidelity.com/api/v1/places/' + placeData.place_name; + getRequest(url, function (error, data) { + if (handleError(url, error, data, cb)) { return; } var place = data.data.place, previews = place.previews; placeData.path = place.path; if (previews && previews.thumbnail) { - placeData.thumbnail = previews.thumbnail; + placeData.thumbnail_url = previews.thumbnail; } if (place.description) { placeData.description = place.description; @@ -260,17 +290,28 @@ Window { cb(error, placeData); }); } + function makeModelData(data, optionalPlaceName) { // create a new obj from data + // ListModel elements will only ever have those properties that are defined by the first obj that is added. + // So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story. + var name = optionalPlaceName || data.place_name, + tags = data.tags || [data.action], + description = data.description || ""; + return { + place_name: name, + path: data.path || "", + created_at: data.create_at || "8/3/2016", + thumbnail_url: data.thumbnail_url || "", + + tags: tags, + description: description, + online_users: data.online_users, + + searchText: [name].concat(tags, description).join(' ').toUpperCase() + } + } function mapDomainPlaces(domain, cb) { // cb(error, arrayOfDomainPlaceData) function addPlace(name, icb) { - getPlace({ - name: name, - tags: domain.tags, - thumbnail: "", - description: "", - path: "", - searchText: [name].concat(domain.tags).join(' ').toUpperCase(), - online_users: domain.online_users - }, icb); + getPlace(makeModelData(domain, name), icb); } // IWBNI we could get these results in order with most-recent-entered first. // In any case, we don't really need to preserve the domain.names order in the results. @@ -278,8 +319,11 @@ Window { } function suggestable(place) { - return (place.name !== AddressManager.hostname) // Not our entry, but do show other entry points to current domain. - && place.thumbnail + if (useFeed) { + return true; + } + return (place.place_name !== AddressManager.hostname) // Not our entry, but do show other entry points to current domain. + && place.thumbnail_url && place.online_users // at least one present means it's actually online && place.online_users <= 20; } @@ -298,8 +342,9 @@ Window { 'sort_order=desc', 'page=' + pageNumber ]; - getRequest('https://metaverse.highfidelity.com/api/v1/domains/all?' + params.join('&'), function (error, data) { - if (handleError(error, data, cb)) { + var url = 'https://metaverse.highfidelity.com/api/v1/domains/all?' + params.join('&'); + getRequest(url, function (error, data) { + if (handleError(url, error, data, cb)) { return; } asyncMap(data.data.domains, mapDomainPlaces, function (error, pageResults) { @@ -309,7 +354,7 @@ Window { // pageResults is now [ [ placeDataOneForDomainOne, placeDataTwoForDomainOne, ...], [ placeDataTwoForDomainTwo...] ] pageResults.forEach(function (domainResults) { allPlaces = allPlaces.concat(domainResults); - if (!addressLine.text) { // Don't add if the user is already filtering + if (!addressLine.text && !useFeed) { // Don't add if the user is already filtering domainResults.forEach(function (place) { if (suggestable(place)) { suggestions.append(place); @@ -324,9 +369,35 @@ Window { }); }); } + function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model + var url = 'https://metaverse.highfidelity.com/api/v1/user_stories?page=' + pageNumber; + getRequest(url, function (error, data) { + if (handleError(url, error, data, cb)) { + return; + } + // FIXME: For debugging until we have real data + data = {user_stories: [ + {created_at: "8/3/2016", action: "snapshot", path: "/4257.1,126.084,1335.45/0,-0.857368,0,0.514705", place_name: "SpiritMoving", thumbnail_url:"https://hifi-metaverse.s3-us-west-1.amazonaws.com/images/places/previews/c28/a26/f0-/thumbnail/hifi-place-c28a26f0-6991-4654-9c2b-e64228c06954.jpg?1456878797"}, + {created_at: "8/3/2016", action: "snapshot", path: "/10077.4,4003.6,9972.56/0,-0.410351,0,0.911928", place_name: "Ventura", thumbnail_url:"https://hifi-metaverse.s3-us-west-1.amazonaws.com/images/places/previews/1f5/e6b/00-/thumbnail/hifi-place-1f5e6b00-2bf0-4319-b9ae-a2344a72354c.png?1454321596"} + ]}; + + var stories = data.user_stories.map(makeModelData); + allStories = allStories.concat(stories); + if (!addressLine.text && useFeed) { // Don't add if the user is already filtering + stories.forEach(function (story) { + suggestions.append(story); + }); + } + if (data.current_page < data.total_pages) { + return getUserStoryPage(pageNumber + 1, cb); + } + cb(); + }); + } function filterChoicesByText() { suggestions.clear(); - var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity); + var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity), + data = useFeed ? allStories : allPlaces; function matches(place) { if (!words.length) { return suggestable(place); @@ -335,7 +406,7 @@ Window { return place.searchText.indexOf(word) >= 0; }); } - allPlaces.forEach(function (place) { + data.forEach(function (place) { if (matches(place)) { suggestions.append(place); } @@ -344,12 +415,13 @@ Window { function fillDestinations() { allPlaces = []; + allStories = []; suggestions.clear(); getDomainPage(1, function (error) { - if (error) { - console.log('domain query failed:', error); - } - console.log('domain query finished', allPlaces.length); + console.log('domain query', error, allPlaces.length); + }); + getUserStoryPage(1, function (error) { + console.log('user stories query', error, allStories.length); }); } From 75b88b0794e0aac2a468930092ea6bd8b8d8dc49 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 3 Aug 2016 10:17:51 -0700 Subject: [PATCH 019/332] WebEntities now support custom hifi url handling WebEntities now use qml/controls/WebView.qml instead of qml/WebEntity.qml. marketplace urls and hifi urls should work properly in web entities. --- interface/resources/qml/WebEntity.qml | 10 ----- interface/src/ui/overlays/Web3DOverlay.cpp | 4 +- .../src/RenderableWebEntityItem.cpp | 4 +- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 40 +++++++++++++++++++ libraries/ui/src/OffscreenUi.cpp | 34 ---------------- 5 files changed, 44 insertions(+), 48 deletions(-) delete mode 100644 interface/resources/qml/WebEntity.qml diff --git a/interface/resources/qml/WebEntity.qml b/interface/resources/qml/WebEntity.qml deleted file mode 100644 index ae94105672..0000000000 --- a/interface/resources/qml/WebEntity.qml +++ /dev/null @@ -1,10 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtWebEngine 1.1 - -WebEngineView { - id: root - anchors.fill: parent - objectName: "webview" - url: "about:blank" -} diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 1c84e71fa7..4b9267a31b 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -70,8 +70,8 @@ void Web3DOverlay::render(RenderArgs* args) { if (!_webSurface) { _webSurface = new OffscreenQmlSurface(); _webSurface->create(currentContext); - _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); - _webSurface->load("WebEntity.qml"); + _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/controls/")); + _webSurface->load("WebView.qml"); _webSurface->resume(); _webSurface->getRootItem()->setProperty("url", _url); _webSurface->resize(QSize(_resolution.x, _resolution.y)); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 20e921cd16..0c40020051 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -63,8 +63,8 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { QSurface * currentSurface = currentContext->surface(); _webSurface = new OffscreenQmlSurface(); _webSurface->create(currentContext); - _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); - _webSurface->load("WebEntity.qml"); + _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/controls/")); + _webSurface->load("WebView.qml"); _webSurface->resume(); _webSurface->getRootItem()->setProperty("url", _sourceUrl); _connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) { diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 8c167fafdc..3c2bbc2469 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -25,12 +25,47 @@ #include #include #include +#include +#include #include "OffscreenGLCanvas.h" #include "GLEscrow.h" #include "GLHelpers.h" +QString fixupHifiUrl(const QString& urlString) { + static const QString ACCESS_TOKEN_PARAMETER = "access_token"; + static const QString ALLOWED_HOST = "metaverse.highfidelity.com"; + QUrl url(urlString); + QUrlQuery query(url); + if (url.host() == ALLOWED_HOST && query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) { + auto accountManager = DependencyManager::get(); + query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager->getAccountInfo().getAccessToken().token); + url.setQuery(query.query()); + return url.toString(); + } + return urlString; +} + +class UrlHandler : public QObject { + Q_OBJECT +public: + Q_INVOKABLE bool canHandleUrl(const QString& url) { + static auto handler = dynamic_cast(qApp); + return handler->canAcceptURL(url); + } + + Q_INVOKABLE bool handleUrl(const QString& url) { + static auto handler = dynamic_cast(qApp); + return handler->acceptURL(url); + } + + // FIXME hack for authentication, remove when we migrate to Qt 5.6 + Q_INVOKABLE QString fixupUrl(const QString& originalUrl) { + return fixupHifiUrl(originalUrl); + } +}; + // Time between receiving a request to render the offscreen UI actually triggering // the render. Could possibly be increased depending on the framerate we expect to // achieve. @@ -419,6 +454,9 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &OffscreenQmlSurface::onAboutToQuit); _updateTimer.setInterval(MIN_TIMER_MS); _updateTimer.start(); + + auto rootContext = getRootContext(); + rootContext->setContextProperty("urlHandler", new UrlHandler()); } void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) { @@ -767,3 +805,5 @@ void OffscreenQmlSurface::setFocusText(bool newFocusText) { emit focusTextChanged(_focusText); } } + +#include "OffscreenQmlSurface.moc" diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 06ef456006..8cae137f67 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -60,39 +60,6 @@ private: bool _navigationFocusDisabled{ false }; }; -QString fixupHifiUrl(const QString& urlString) { - static const QString ACCESS_TOKEN_PARAMETER = "access_token"; - static const QString ALLOWED_HOST = "metaverse.highfidelity.com"; - QUrl url(urlString); - QUrlQuery query(url); - if (url.host() == ALLOWED_HOST && query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) { - auto accountManager = DependencyManager::get(); - query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager->getAccountInfo().getAccessToken().token); - url.setQuery(query.query()); - return url.toString(); - } - return urlString; -} - -class UrlHandler : public QObject { - Q_OBJECT -public: - Q_INVOKABLE bool canHandleUrl(const QString& url) { - static auto handler = dynamic_cast(qApp); - return handler->canAcceptURL(url); - } - - Q_INVOKABLE bool handleUrl(const QString& url) { - static auto handler = dynamic_cast(qApp); - return handler->acceptURL(url); - } - - // FIXME hack for authentication, remove when we migrate to Qt 5.6 - Q_INVOKABLE QString fixupUrl(const QString& originalUrl) { - return fixupHifiUrl(originalUrl); - } -}; - static OffscreenFlags* offscreenFlags { nullptr }; // This hack allows the QML UI to work with keys that are also bound as @@ -126,7 +93,6 @@ void OffscreenUi::create(QOpenGLContext* context) { rootContext->setContextProperty("OffscreenUi", this); rootContext->setContextProperty("offscreenFlags", offscreenFlags = new OffscreenFlags()); - rootContext->setContextProperty("urlHandler", new UrlHandler()); rootContext->setContextProperty("fileDialogHelper", new FileDialogHelper()); } From 188e525efcdea070d2b227be4ae09abc2b918538 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 3 Aug 2016 11:48:58 -0700 Subject: [PATCH 020/332] basic ugly feed/places toggle button --- interface/resources/qml/AddressBarDialog.qml | 21 ++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index df6a5837ac..de2e16ddfc 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -193,7 +193,24 @@ Window { helperText: "Go to: place, @user, /path, network address" onTextChanged: filterChoicesByText() } - + Rectangle { + color: "red"; + height: addressLine.height; + width: addressLine.height; + anchors { + left: addressLine.right; + bottom: addressLine.bottom; + } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + onClicked: { + console.log('fixme hrs toggle'); + useFeed = !useFeed; + filterChoicesByText(); + } + } + } } } @@ -299,7 +316,7 @@ Window { return { place_name: name, path: data.path || "", - created_at: data.create_at || "8/3/2016", + created_at: data.create_at || "", thumbnail_url: data.thumbnail_url || "", tags: tags, From b4e5b606566c8e2847540b8d88e4e38a4d63adb1 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 3 Aug 2016 15:45:22 -0700 Subject: [PATCH 021/332] fixing ao --- .../src/AmbientOcclusionEffect.cpp | 2 +- .../render-utils/src/AmbientOcclusionEffect.h | 2 +- .../render-utils/src/DeferredTransform.slh | 4 + .../render-utils/src/RenderDeferredTask.cpp | 8 +- libraries/render-utils/src/ssao.slh | 109 ++++++++++++++++++ .../render-utils/src/ssao_debugOcclusion.slf | 100 ++++------------ .../render-utils/src/ssao_makeOcclusion.slf | 59 +++------- .../utilities/render/ambientOcclusionPass.qml | 4 +- 8 files changed, 159 insertions(+), 129 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 71222ee671..97132a0fc7 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -487,7 +487,7 @@ const gpu::PipelinePointer& DebugAmbientOcclusion::getDebugPipeline() { gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setColorWriteMask(true, true, true, false); - + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); // Good to go add the brand new pipeline _debugPipeline = gpu::Pipeline::create(program, state); } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index ffc2c4d3e3..5a0e7da587 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -100,7 +100,7 @@ public: float numSpiralTurns{ 7.0f }; // defining an angle span to distribute the samples ray directions int numSamples{ 11 }; int resolutionLevel{ 0 }; - int blurRadius{ 0 }; // 0 means no blurring + int blurRadius{ 3 }; // 0 means no blurring bool ditheringEnabled{ true }; // randomize the distribution of rays per pixel, should always be true bool borderingEnabled{ true }; // avoid evaluating information from non existing pixels out of the frame, should always be true double gpuTime{ 0.0 }; diff --git a/libraries/render-utils/src/DeferredTransform.slh b/libraries/render-utils/src/DeferredTransform.slh index ec8cdac8c1..44f1073702 100644 --- a/libraries/render-utils/src/DeferredTransform.slh +++ b/libraries/render-utils/src/DeferredTransform.slh @@ -85,6 +85,10 @@ float getStereoSideHeight(int resolutionLevel) { return float(int(frameTransform._pixelInfo.w) >> resolutionLevel); } +vec2 getSideImageSize(int resolutionLevel) { + return vec2(float(int(frameTransform._stereoInfo.y) >> resolutionLevel), float(int(frameTransform._pixelInfo.w) >> resolutionLevel)); +} + ivec4 getStereoSideInfo(int xPos, int resolutionLevel) { int sideWidth = int(getStereoSideWidth(resolutionLevel)); return ivec4(xPos < sideWidth ? ivec2(0, 0) : ivec2(1, sideWidth), sideWidth, isStereo()); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index bfb61b9dfb..7ea81eb706 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -178,14 +178,16 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { // Debugging stages { + // Debugging Deferred buffer job + const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); + addJob("DebugDeferredBuffer", debugFramebuffers); + addJob("DebugScattering", deferredLightingInputs); const auto debugAmbientOcclusionInputs = DebugAmbientOcclusion::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget, ambientOcclusionUniforms).hasVarying(); addJob("DebugAmbientOcclusion", debugAmbientOcclusionInputs); - // Debugging Deferred buffer job - const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); - addJob("DebugDeferredBuffer", debugFramebuffers); + // Scene Octree Debuging job { diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 01be0e463a..3a3203e2e3 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -126,6 +126,115 @@ float getBlurCoef(int c) { <@endfunc@> +<@func declareSamplingDisk()@> + +float evalDiskRadius(float Zeye, vec2 imageSize) { + // Choose the screen-space sample radius + // proportional to the projected area of the sphere + float ssDiskRadius = -( getProjScale(getResolutionLevel()) * getRadius() / Zeye ) * getPerspectiveScale(); + + // clamp the disk to fit in the image otherwise too many unknown + ssDiskRadius = min(ssDiskRadius, imageSize.y * 0.5); + + return ssDiskRadius; +} + +const float TWO_PI = 6.28; + +vec3 getUnitTapLocation(int sampleNumber, float spinAngle){ + // Radius relative to ssR + float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); + float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle; + return vec3(cos(angle), sin(angle), alpha); +} + +vec3 getTapLocation(int sampleNumber, float spinAngle, float outerRadius) { + vec3 tap = getUnitTapLocation(sampleNumber, spinAngle); + tap.xy *= tap.z; + tap *= outerRadius; + return tap; +} + +vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, vec2 pixelPos, vec2 imageSize) { + vec3 tap = getTapLocation(sampleNumber, spinAngle, outerRadius); + vec2 tapPos = pixelPos + tap.xy; + + if (!(isBorderingEnabled() > 0.0)) { + return tap; + } + bool redoTap = false; + + if ((tapPos.x < 0.5)) { + tapPos.x = -tapPos.x; + redoTap = true; + } else if ((tapPos.x > imageSize.x - 0.5)) { + tapPos.x -= (imageSize.x - tapPos.x); + redoTap = true; + } + + if ((tapPos.y < 0.5)) { + tapPos.y = -tapPos.y; + redoTap = true; + } else if ((tapPos.y > imageSize.y - 0.5)) { + tapPos.y -= (imageSize.y - tapPos.y); + redoTap = true; + } + + if (redoTap) { + tap.xy = tapPos - pixelPos; + tap.z = length(tap.xy); + } +/* + if ((tapPos.x < 0.0) || (tapPos.x >= imageSize.x)) { + // tapPos.x = pixelPos.x - tapVec.x; + tap.x = -tap.x; + } + if ((tapPos.y < 0.0) || (tapPos.y >= imageSize.y)) { + // tapPos.y = pixelPos.y - tapVec.y; + tap.y = -tap.y; + }*/ + + return tap; +} + +<@endfunc@> + + +<@func declareFetchDepthPyramidMap()@> + +const int LOG_MAX_OFFSET = 3; +const int MAX_MIP_LEVEL = 5; + +// the depth pyramid texture +uniform sampler2D pyramidMap; + +float getZEye(ivec2 pixel) { + return -texelFetch(pyramidMap, pixel, getResolutionLevel()).x; +} + +vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec3 tap, vec2 imageSize) { + // Derivation: + // mipLevel = floor(log(ssR / MAX_OFFSET)); + int mipLevel = clamp(findMSB(int(tap.z)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); + + ivec2 ssP = ivec2(tap.xy) + ssC; + ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); + + vec3 P; + + // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. + // Manually clamp to the texture size because texelFetch bypasses the texture unit + ivec2 mipP = clamp(ssPFull >> mipLevel, ivec2(0), textureSize(pyramidMap, getResolutionLevel() + mipLevel) - ivec2(1)); + P.z = -texelFetch(pyramidMap, mipP, getResolutionLevel() + mipLevel).r; + + // Offset to pixel center + vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize; + P = evalEyePositionFromZeye(side.x, P.z, tapUV); + return P; +} + +<@endfunc@> + <@func declareBlurPass(axis)@> <$declarePackOcclusionDepth()$> diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index 0c40042ba2..7e6a792c3b 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -11,6 +11,9 @@ <@include ssao.slh@> <$declareAmbientOcclusion()$> +<$declareFetchDepthPyramidMap()$> +<$declareSamplingDisk()$> + <$declarePackOcclusionDepth()$> struct DebugParams{ @@ -24,15 +27,6 @@ uniform debugAmbientOcclusionBuffer { vec2 getDebugCursorTexcoord(){ return debugParams.pixelInfo.xy; } -const int LOG_MAX_OFFSET = 3; -const int MAX_MIP_LEVEL = 5; - -// the depth pyramid texture -uniform sampler2D pyramidMap; - -float getZEye(ivec2 pixel) { - return -texelFetch(pyramidMap, pixel, getResolutionLevel()).x; -} out vec4 outFragColor; @@ -43,63 +37,7 @@ float getAngleDithering(in ivec2 pixelPos) { return isDitheringEnabled() * (3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10 + getFrameDithering(); } -const float TWO_PI = 6.28; - -vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR){ - // Radius relative to ssR - float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); - float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle; - - ssR = alpha; - return vec2(cos(angle), sin(angle)); -} - -vec3 getTapLocation(int sampleNumber, float spinAngle, float outerRadius) { - // Radius relative to ssR - float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); - float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle; - return vec2(cos(angle), sin(angle), alpha * outerRadius); -} - -vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec2 unitOffset, float ssR) { - // Derivation: - // mipLevel = floor(log(ssR / MAX_OFFSET)); - int mipLevel = clamp(findMSB(int(ssR)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); - - ivec2 ssOffset = ivec2(ssR * unitOffset); - ivec2 ssP = ssOffset + ssC; - if (bool(isBorderingEnabled())) { - ssP.x = ((ssP.x < 0 || ssP.x >= side.z) ? ssC.x - ssOffset.x : ssP.x); - ssP.y = ((ssP.y < 0 || ssP.y >= int(getWidthHeight(getResolutionLevel()).y)) ? ssC.y - ssOffset.y : ssP.y); - } - - ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); - - vec3 P; - - // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. - // Manually clamp to the texture size because texelFetch bypasses the texture unit - ivec2 mipP = clamp(ssPFull >> mipLevel, ivec2(0), textureSize(pyramidMap, getResolutionLevel() + mipLevel) - ivec2(1)); - P.z = -texelFetch(pyramidMap, mipP, getResolutionLevel() + mipLevel).r; - - // Offset to pixel center - vec2 tapUV = (vec2(ssP) + vec2(0.5)) / float(side.z); - P = evalEyePositionFromZeye(side.x, P.z, tapUV); - return P; -} - - -float sampleAO(in ivec3 side, in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssDiskRadius, in int tapIndex, in float randomPatternRotationAngle) { - // Offset on the unit disk, spun for this pixel - float ssR; - vec2 unitOffset = tapLocation(tapIndex, randomPatternRotationAngle, ssR); - ssR *= ssDiskRadius; - - - - // The occluding point in camera space - vec3 Q = getOffsetPosition(side, ssC, unitOffset, ssR); - +float evalAO(in vec3 C, in vec3 n_C, vec3 Q) { vec3 v = Q - C; float vv = dot(v, v); float vn = dot(v, n_C); @@ -114,7 +52,9 @@ float sampleAO(in ivec3 side, in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssD void main(void) { // Pixel Debugged vec2 cursorUV = getDebugCursorTexcoord(); - ivec2 ssC = ivec2(cursorUV * vec2(getStereoSideWidth(getResolutionLevel()), getStereoSideHeight(getResolutionLevel()))); + vec2 imageSize = getSideImageSize(getResolutionLevel()); + vec2 cursorPixelPos = cursorUV * imageSize; + ivec2 ssC = ivec2(cursorPixelPos); // Pixel being shaded //ivec2 ssC = ivec2(gl_FragCoord.xy); @@ -126,17 +66,16 @@ void main(void) { // From now on, ssC is the pixel pos in the side ssC.x -= side.y; - vec2 fragPos = (vec2(ssC) + vec2(0.5)) / vec2(getStereoSideWidth(getResolutionLevel()), getStereoSideHeight(getResolutionLevel())); + vec2 fragPos = (vec2(ssC) + vec2(0.5)) / imageSize; // The position and normal of the pixel fragment in Eye space vec3 Cp = evalEyePositionFromZeye(side.x, Zeye, fragPos); vec3 Cn = evalEyeNormal(Cp); // Choose the screen-space sample radius - // proportional to the projected area of the sphere - float ssDiskRadius = -( getProjScale(getResolutionLevel()) * getRadius() / Cp.z ) * getPerspectiveScale(); + float ssDiskRadius = evalDiskRadius(Cp.z, imageSize); - vec2 fragToCursor = cursorUV * vec2(getStereoSideWidth(getResolutionLevel()), getStereoSideHeight(getResolutionLevel())) - gl_FragCoord.xy; + vec2 fragToCursor = cursorPixelPos - gl_FragCoord.xy; if (dot(fragToCursor,fragToCursor) > ssDiskRadius * ssDiskRadius) { discard; } @@ -149,18 +88,18 @@ void main(void) { float keepTapRadius = 2.0; bool keep = (dot(fragToCursor,fragToCursor) < keepTapRadius); for (int i = 0; i < getNumSamples(); ++i) { - vec3 tap = getTapLocation(i, randomPatternRotationAngle, outerRadius); - - vec2 fragToTap = vec2(ssC) + tap.xy * tap.z - gl_FragCoord.xy; + vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize); + // The occluding point in camera space + vec2 fragToTap = vec2(ssC) + tap.xy - gl_FragCoord.xy; if (dot(fragToTap,fragToTap) < keepTapRadius) { keep = true; } - sum += sampleAO(side.xyz, ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle); + + vec3 Q = getOffsetPosition(side.xyz, ssC, tap, imageSize); + + sum += evalAO(Cp, Cn, Q); } - if (!keep) { - discard; - } float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); @@ -190,4 +129,9 @@ void main(void) { //outFragColor = vec4((v + vec3(1.0))* 0.5, 1.0); // outFragColor = vec4((Cn + vec3(1.0))* 0.5, 1.0); //outFragColor = vec4(vec3(ssDiskRadius / 100.0), 1.0); + + + if (!keep) { + outFragColor = vec4(0.1); + } } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 7bad1420bf..739dc1ddcb 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -11,27 +11,12 @@ <@include ssao.slh@> <$declareAmbientOcclusion()$> +<$declareFetchDepthPyramidMap()$> +<$declareSamplingDisk()$> + <$declarePackOcclusionDepth()$> -const int LOG_MAX_OFFSET = 3; -const int MAX_MIP_LEVEL = 5; - -// the depth pyramid texture -uniform sampler2D pyramidMap; - -float getZEye(ivec2 pixel) { - return -texelFetch(pyramidMap, pixel, getResolutionLevel()).x; -} -/* -vec3 evalEyePositionFromZeye(int side, float Zeye, vec2 texcoord) { - // compute the view space position using the depth - // basically manually pick the proj matrix components to do the inverse - float Xe = (-Zeye * (texcoord.x * 2.0 - 1.0) - Zeye * frameTransform._projection[side][2][0] - frameTransform._projection[side][3][0]) / frameTransform._projection[side][0][0]; - float Ye = (-Zeye * (texcoord.y * 2.0 - 1.0) - Zeye * frameTransform._projection[side][2][1] - frameTransform._projection[side][3][1]) / frameTransform._projection[side][1][1]; - return vec3(Xe, Ye, Zeye); -} -*/ out vec4 outFragColor; uniform sampler2D normalMap; @@ -40,18 +25,7 @@ float getAngleDithering(in ivec2 pixelPos) { // Hash function used in the AlchemyAO paper return isDitheringEnabled() * (3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10 + getFrameDithering(); } - -const float TWO_PI = 6.28; - -vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR){ - // Radius relative to ssR - float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); - float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle; - - ssR = alpha; - return vec2(cos(angle), sin(angle)); -} - +/* vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec2 unitOffset, float ssR) { // Derivation: // mipLevel = floor(log(ssR / MAX_OFFSET)); @@ -77,19 +51,9 @@ vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec2 unitOffset, float ssR) { vec2 tapUV = (vec2(ssP) + vec2(0.5)) / float(side.z); P = evalEyePositionFromZeye(side.x, P.z, tapUV); return P; -} - -float sampleAO(in ivec3 side, in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssDiskRadius, in int tapIndex, in float randomPatternRotationAngle) { - // Offset on the unit disk, spun for this pixel - float ssR; - vec2 unitOffset = tapLocation(tapIndex, randomPatternRotationAngle, ssR); - ssR *= ssDiskRadius; - - - - // The occluding point in camera space - vec3 Q = getOffsetPosition(side, ssC, unitOffset, ssR); +}*/ +float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) { vec3 v = Q - C; float vv = dot(v, v); float vn = dot(v, n_C); @@ -104,6 +68,8 @@ void main(void) { // Pixel being shaded ivec2 ssC = ivec2(gl_FragCoord.xy); + vec2 imageSize = getSideImageSize(getResolutionLevel()); + // Fetch the z under the pixel (stereo or not) float Zeye = getZEye(ssC); @@ -119,8 +85,7 @@ void main(void) { vec3 Cn = evalEyeNormal(Cp); // Choose the screen-space sample radius - // proportional to the projected area of the sphere - float ssDiskRadius = -( getProjScale(getResolutionLevel()) * getRadius() / Cp.z ) * getPerspectiveScale(); + float ssDiskRadius = evalDiskRadius(Cp.z, imageSize); // Let's make noise float randomPatternRotationAngle = getAngleDithering(ssC); @@ -128,7 +93,11 @@ void main(void) { // Accumulate the Obscurance for each samples float sum = 0.0; for (int i = 0; i < getNumSamples(); ++i) { - sum += sampleAO(side.xyz, ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle); + vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, ssC, imageSize); + + vec3 Q = getOffsetPosition(side.xyz, ssC, tap, imageSize); + + sum += evalAO(Cp, Cn, Q); } float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index ae4ec7770b..527c8d8cd0 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -22,7 +22,9 @@ Column { model: [ "Radius:radius:2.0:false", "Level:obscuranceLevel:1.0:false", - "Scale:perspectiveScale:2.0:false", + "Num Taps:numSamples:32:true", + "Taps Spiral:numSpiralTurns:10.0:false", + "Blur Radius:blurRadius:10.0:false", ] ConfigSlider { label: qsTr(modelData.split(":")[0]) From 30327d81357316a5880cae84b2af3f241f08924f Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 3 Aug 2016 16:02:22 -0700 Subject: [PATCH 022/332] New buttons, with hover behavior. Also don't impose test data if the api gives data. --- interface/resources/images/backward.svg | 28 ++++ interface/resources/images/forward.svg | 28 ++++ interface/resources/images/home.svg | 46 +++++++ interface/resources/images/places.svg | 33 +++++ interface/resources/images/snap-feed.svg | 41 ++++++ interface/resources/qml/AddressBarDialog.qml | 131 ++++++++++--------- 6 files changed, 242 insertions(+), 65 deletions(-) create mode 100644 interface/resources/images/backward.svg create mode 100644 interface/resources/images/forward.svg create mode 100644 interface/resources/images/home.svg create mode 100644 interface/resources/images/places.svg create mode 100644 interface/resources/images/snap-feed.svg diff --git a/interface/resources/images/backward.svg b/interface/resources/images/backward.svg new file mode 100644 index 0000000000..9d36778aa3 --- /dev/null +++ b/interface/resources/images/backward.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff --git a/interface/resources/images/forward.svg b/interface/resources/images/forward.svg new file mode 100644 index 0000000000..65ec62f947 --- /dev/null +++ b/interface/resources/images/forward.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff --git a/interface/resources/images/home.svg b/interface/resources/images/home.svg new file mode 100644 index 0000000000..1e201a1a9d --- /dev/null +++ b/interface/resources/images/home.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/images/places.svg b/interface/resources/images/places.svg new file mode 100644 index 0000000000..6336ab111f --- /dev/null +++ b/interface/resources/images/places.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + diff --git a/interface/resources/images/snap-feed.svg b/interface/resources/images/snap-feed.svg new file mode 100644 index 0000000000..3eae156cd6 --- /dev/null +++ b/interface/resources/images/snap-feed.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index de2e16ddfc..5a92360488 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -14,6 +14,7 @@ import "controls" import "styles" import "windows" import "hifi" +import "hifi/toolbars" Window { id: root @@ -118,64 +119,43 @@ Window { property int inputAreaHeight: 56.0 * root.scale // Height of the background's input area property int inputAreaStep: (height - inputAreaHeight) / 2 - Image { + ToolbarButton { id: homeButton - source: "../images/home-button.svg" - width: 29 - height: 26 + imageURL: "../images/home.svg" + buttonState: 1 + defaultState: 1 + hoverState: 2 + onClicked: addressBarDialog.loadHome(); anchors { left: parent.left - leftMargin: parent.height + 2 * hifi.layout.spacing + leftMargin: parent.height verticalCenter: parent.verticalCenter } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - onClicked: { - addressBarDialog.loadHome() - } - } } - Image { - id: backArrow - source: addressBarDialog.backEnabled ? "../images/left-arrow.svg" : "../images/left-arrow-disabled.svg" - width: 22 - height: 26 + ToolbarButton { + id: backArrow; + imageURL: "../images/backward.svg"; + hoverState: addressBarDialog.backEnabled ? 2 : 0; + defaultState: addressBarDialog.backEnabled ? 1 : 0; + buttonState: addressBarDialog.backEnabled ? 1 : 0; // fixme: needs work + onClicked: addressBarDialog.loadBack(); anchors { left: homeButton.right - leftMargin: 2 * hifi.layout.spacing verticalCenter: parent.verticalCenter } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - onClicked: { - addressBarDialog.loadBack() - } - } } - - Image { - id: forwardArrow - source: addressBarDialog.forwardEnabled ? "../images/right-arrow.svg" : "../images/right-arrow-disabled.svg" - width: 22 - height: 26 + ToolbarButton { + id: forwardArrow; + imageURL: "../images/forward.svg"; + hoverState: addressBarDialog.forwardEnabled ? 2 : 0; + defaultState: addressBarDialog.forwardEnabled ? 1 : 0; + buttonState: addressBarDialog.forwardEnabled ? 1 : 0; // fixme: needs work + onClicked: addressBarDialog.loadForward(); anchors { left: backArrow.right - leftMargin: 2 * hifi.layout.spacing verticalCenter: parent.verticalCenter } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - onClicked: { - addressBarDialog.loadForward() - } - } } // FIXME replace with TextField @@ -183,37 +163,54 @@ Window { id: addressLine focus: true anchors { - fill: parent + top: parent.top + left: parent.left + bottom: parent.bottom + right: placesButton.left leftMargin: parent.height + parent.height + hifi.layout.spacing * 7 - rightMargin: hifi.layout.spacing * 2 + rightMargin: hifi.layout.spacing topMargin: parent.inputAreaStep + hifi.layout.spacing bottomMargin: parent.inputAreaStep + hifi.layout.spacing } font.pixelSize: hifi.fonts.pixelSize * root.scale * 0.75 - helperText: "Go to: place, @user, /path, network address" + helperText: "Go to: place, @user, /path" //, network address" onTextChanged: filterChoicesByText() } - Rectangle { - color: "red"; - height: addressLine.height; - width: addressLine.height; + // These two are radio buttons. + ToolbarButton { + id: placesButton + imageURL: "../images/places.svg" + buttonState: 1 + defaultState: useFeed ? 0 : 1; + hoverState: useFeed ? 2 : -1; + onClicked: useFeed ? toggleFeed() : identity() anchors { - left: addressLine.right; + right: feedButton.left; bottom: addressLine.bottom; } - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - onClicked: { - console.log('fixme hrs toggle'); - useFeed = !useFeed; - filterChoicesByText(); - } + } + ToolbarButton { + id: feedButton; + imageURL: "../images/snap-feed.svg"; + buttonState: 0 + defaultState: useFeed ? 1 : 0; + hoverState: useFeed ? -1 : 2; + onClicked: useFeed ? identity() : toggleFeed(); + anchors { + right: parent.right; + bottom: addressLine.bottom; + rightMargin: hifi.layout.spacing; } } } } + function toggleFeed () { + useFeed = !useFeed; + placesButton.buttonState = useFeed ? 0 : 1; + feedButton.buttonState = useFeed ? 1 : 0; + filterChoicesByText(); + } function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects. // TODO: make available to other .qml. var request = new XMLHttpRequest(); @@ -393,12 +390,16 @@ Window { return; } // FIXME: For debugging until we have real data - data = {user_stories: [ - {created_at: "8/3/2016", action: "snapshot", path: "/4257.1,126.084,1335.45/0,-0.857368,0,0.514705", place_name: "SpiritMoving", thumbnail_url:"https://hifi-metaverse.s3-us-west-1.amazonaws.com/images/places/previews/c28/a26/f0-/thumbnail/hifi-place-c28a26f0-6991-4654-9c2b-e64228c06954.jpg?1456878797"}, - {created_at: "8/3/2016", action: "snapshot", path: "/10077.4,4003.6,9972.56/0,-0.410351,0,0.911928", place_name: "Ventura", thumbnail_url:"https://hifi-metaverse.s3-us-west-1.amazonaws.com/images/places/previews/1f5/e6b/00-/thumbnail/hifi-place-1f5e6b00-2bf0-4319-b9ae-a2344a72354c.png?1454321596"} - ]}; + if (!data.user_stories.length) { + data.user_stories = [ + {created_at: "8/3/2016", action: "snapshot", path: "/4257.1,126.084,1335.45/0,-0.857368,0,0.514705", place_name: "SpiritMoving", thumbnail_url:"https://hifi-metaverse.s3-us-west-1.amazonaws.com/images/places/previews/c28/a26/f0-/thumbnail/hifi-place-c28a26f0-6991-4654-9c2b-e64228c06954.jpg?1456878797"}, + {created_at: "8/3/2016", action: "snapshot", path: "/10077.4,4003.6,9972.56/0,-0.410351,0,0.911928", place_name: "Ventura", thumbnail_url:"https://hifi-metaverse.s3-us-west-1.amazonaws.com/images/places/previews/1f5/e6b/00-/thumbnail/hifi-place-1f5e6b00-2bf0-4319-b9ae-a2344a72354c.png?1454321596"} + ]; + } - var stories = data.user_stories.map(makeModelData); + var stories = data.user_stories.map(function (story) { // explicit single-argument function + return makeModelData(story); + }); allStories = allStories.concat(stories); if (!addressLine.text && useFeed) { // Don't add if the user is already filtering stories.forEach(function (story) { @@ -435,10 +436,10 @@ Window { allStories = []; suggestions.clear(); getDomainPage(1, function (error) { - console.log('domain query', error, allPlaces.length); + console.log('domain query', error || 'ok', allPlaces.length); }); getUserStoryPage(1, function (error) { - console.log('user stories query', error, allStories.length); + console.log('user stories query', error || 'ok', allStories.length); }); } From 8c6744e5e8308d4a212bf304b8332b9c347b450a Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 3 Aug 2016 16:18:41 -0700 Subject: [PATCH 023/332] new address bar graphic --- interface/resources/images/address-bar.svg | 97 ++++---------------- interface/resources/qml/AddressBarDialog.qml | 9 +- 2 files changed, 22 insertions(+), 84 deletions(-) diff --git a/interface/resources/images/address-bar.svg b/interface/resources/images/address-bar.svg index 56dc4f028c..76ad7c0547 100644 --- a/interface/resources/images/address-bar.svg +++ b/interface/resources/images/address-bar.svg @@ -1,81 +1,18 @@ - - - - - - image/svg+xml - - - - - - - - - - - + + + + + + + diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 5a92360488..0ae4d6d9ff 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -128,7 +128,7 @@ Window { onClicked: addressBarDialog.loadHome(); anchors { left: parent.left - leftMargin: parent.height + leftMargin: hifi.layout.spacing * 2 verticalCenter: parent.verticalCenter } } @@ -164,10 +164,10 @@ Window { focus: true anchors { top: parent.top - left: parent.left bottom: parent.bottom + left: forwardArrow.right right: placesButton.left - leftMargin: parent.height + parent.height + hifi.layout.spacing * 7 + leftMargin: hifi.layout.spacing * 4 rightMargin: hifi.layout.spacing topMargin: parent.inputAreaStep + hifi.layout.spacing bottomMargin: parent.inputAreaStep + hifi.layout.spacing @@ -186,6 +186,7 @@ Window { onClicked: useFeed ? toggleFeed() : identity() anchors { right: feedButton.left; + rightMargin: hifi.layout.spacing * 3; bottom: addressLine.bottom; } } @@ -199,7 +200,7 @@ Window { anchors { right: parent.right; bottom: addressLine.bottom; - rightMargin: hifi.layout.spacing; + rightMargin: hifi.layout.spacing * 3; } } } From 4d0c5c5d19cc9febe1e456e0d306976056c47a1b Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 3 Aug 2016 16:22:46 -0700 Subject: [PATCH 024/332] fix snapshot button hover --- scripts/system/snapshot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 87ea3f29ec..280bd3b5d0 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -16,7 +16,7 @@ var button = toolBar.addButton({ visible: true, buttonState: 1, defaultState: 1, - hoverState: 1, + hoverState: 2, alpha: 0.9, }); From c44ce0f5b59ad3f9249110f7def4fc8322a46723 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 3 Aug 2016 17:03:27 -0700 Subject: [PATCH 025/332] Fix forward/back button states. --- interface/resources/qml/AddressBarDialog.qml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 0ae4d6d9ff..fe44607b53 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -80,6 +80,9 @@ Window { id: addressBarDialog implicitWidth: backgroundImage.width implicitHeight: backgroundImage.height + // The buttons have their button state changed on hover, so we have to manually fix them up here + onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0; + onForwardEnabledChanged: forwardArrow.buttonState = addressBarDialog.forwardEnabled ? 1 : 0; ListModel { id: suggestions } @@ -138,7 +141,7 @@ Window { imageURL: "../images/backward.svg"; hoverState: addressBarDialog.backEnabled ? 2 : 0; defaultState: addressBarDialog.backEnabled ? 1 : 0; - buttonState: addressBarDialog.backEnabled ? 1 : 0; // fixme: needs work + buttonState: addressBarDialog.backEnabled ? 1 : 0; onClicked: addressBarDialog.loadBack(); anchors { left: homeButton.right @@ -150,7 +153,7 @@ Window { imageURL: "../images/forward.svg"; hoverState: addressBarDialog.forwardEnabled ? 2 : 0; defaultState: addressBarDialog.forwardEnabled ? 1 : 0; - buttonState: addressBarDialog.forwardEnabled ? 1 : 0; // fixme: needs work + buttonState: addressBarDialog.forwardEnabled ? 1 : 0; onClicked: addressBarDialog.loadForward(); anchors { left: backArrow.right From 421707c5d84aaac954309b2945822aa51ea7042e Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 4 Aug 2016 10:07:33 -0700 Subject: [PATCH 026/332] fix typo that was making user stories not show date --- interface/resources/qml/AddressBarDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index fe44607b53..fd2abd01f5 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -317,7 +317,7 @@ Window { return { place_name: name, path: data.path || "", - created_at: data.create_at || "", + created_at: data.created_at || "", thumbnail_url: data.thumbnail_url || "", tags: tags, From d39446f340157e3bb4ed1b666219d3d39217c367 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 4 Aug 2016 10:36:17 -0700 Subject: [PATCH 027/332] no place name over user-story scroll items --- interface/resources/qml/AddressBarDialog.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index fd2abd01f5..0c6288c828 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -104,8 +104,8 @@ Window { goFunction: goCard; path: model.place_name + model.path; thumbnail: model.thumbnail_url; - placeText: model.place_name; - usersText: (model.created_at ? pastTime(model.created_at) : (model.online_users + ((model.online_users === 1) ? ' person' : ' people'))); + placeText: model.created_at ? "" : model.place_name; + usersText: model.created_at ? pastTime(model.created_at) : (model.online_users + ((model.online_users === 1) ? ' person' : ' people')); hoverThunk: function () { ListView.view.currentIndex = index; } unhoverThunk: function () { ListView.view.currentIndex = -1; } } From f5d07f418679df39140ed9b9dffd11eef53928f3 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 4 Aug 2016 11:55:31 -0700 Subject: [PATCH 028/332] helper text size and italic --- interface/resources/qml/AddressBarDialog.qml | 4 +++- interface/resources/qml/controls/TextInput.qml | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 0c6288c828..29cba76f1d 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -176,7 +176,9 @@ Window { bottomMargin: parent.inputAreaStep + hifi.layout.spacing } font.pixelSize: hifi.fonts.pixelSize * root.scale * 0.75 - helperText: "Go to: place, @user, /path" //, network address" + helperText: "Go to: place, @user, /path, network address" + helperPixelSize: font.pixelSize * 0.75 + helperItalic: true onTextChanged: filterChoicesByText() } // These two are radio buttons. diff --git a/interface/resources/qml/controls/TextInput.qml b/interface/resources/qml/controls/TextInput.qml index b7ca6d2c1b..77e11177e1 100644 --- a/interface/resources/qml/controls/TextInput.qml +++ b/interface/resources/qml/controls/TextInput.qml @@ -12,6 +12,8 @@ Original.TextInput { verticalAlignment: Original.TextInput.AlignVCenter font.family: hifi.fonts.fontFamily font.pixelSize: hifi.fonts.pixelSize + property int helperPixelSize: font.pixelSize + property bool helperItalic: false /* Original.Rectangle { @@ -23,7 +25,8 @@ Original.TextInput { */ Text { anchors.fill: parent - font.pixelSize: parent.font.pixelSize + font.pixelSize: helperPixelSize + font.italic: helperItalic font.family: parent.font.family verticalAlignment: parent.verticalAlignment horizontalAlignment: parent.horizontalAlignment From 1ecb01feacea805d41f8ce39db9d47f00b785576 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 4 Aug 2016 12:06:07 -0700 Subject: [PATCH 029/332] center the scroll --- interface/resources/qml/AddressBarDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 29cba76f1d..3e676b7340 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -94,7 +94,7 @@ Window { anchors { bottom: backgroundImage.top; bottomMargin: 2 * hifi.layout.spacing; - right: backgroundImage.right; + horizontalCenter: backgroundImage.horizontalCenter } model: suggestions; orientation: ListView.Horizontal; From 04bd98b15ffd9a7f73966bd543a5896179eafffc Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 4 Aug 2016 12:33:50 -0700 Subject: [PATCH 030/332] new assets. remove margins between buttons (letting the graphics do the work) --- interface/resources/images/address-bar.svg | 12 ++-- interface/resources/images/backward.svg | 26 ++++---- interface/resources/images/forward.svg | 26 ++++---- interface/resources/images/home.svg | 62 +++++++++++--------- interface/resources/images/places.svg | 36 ++++++------ interface/resources/images/snap-feed.svg | 48 +++++++-------- interface/resources/qml/AddressBarDialog.qml | 9 ++- 7 files changed, 111 insertions(+), 108 deletions(-) diff --git a/interface/resources/images/address-bar.svg b/interface/resources/images/address-bar.svg index 76ad7c0547..a8cb158492 100644 --- a/interface/resources/images/address-bar.svg +++ b/interface/resources/images/address-bar.svg @@ -9,10 +9,10 @@ .st1{fill:#E6E7E8;} .st2{fill:#FFFFFF;} - - - + + + diff --git a/interface/resources/images/backward.svg b/interface/resources/images/backward.svg index 9d36778aa3..e4502fa80e 100644 --- a/interface/resources/images/backward.svg +++ b/interface/resources/images/backward.svg @@ -9,19 +9,19 @@ .st3{fill:#31D8FF;} - - - - + + + + diff --git a/interface/resources/images/forward.svg b/interface/resources/images/forward.svg index 65ec62f947..0c5cbe3d0c 100644 --- a/interface/resources/images/forward.svg +++ b/interface/resources/images/forward.svg @@ -9,19 +9,19 @@ .st3{fill:#31D8FF;} - - - - + + + + diff --git a/interface/resources/images/home.svg b/interface/resources/images/home.svg index 1e201a1a9d..7740dc9568 100644 --- a/interface/resources/images/home.svg +++ b/interface/resources/images/home.svg @@ -9,37 +9,41 @@ .st3{fill:#31D8FF;} - - - - - - + + - - + + + + + + + + + + diff --git a/interface/resources/images/places.svg b/interface/resources/images/places.svg index 6336ab111f..f70695d606 100644 --- a/interface/resources/images/places.svg +++ b/interface/resources/images/places.svg @@ -9,24 +9,24 @@ .st3{fill:#31D8FF;} - - - - - - - + + + + + + + diff --git a/interface/resources/images/snap-feed.svg b/interface/resources/images/snap-feed.svg index 3eae156cd6..c2dede6e0f 100644 --- a/interface/resources/images/snap-feed.svg +++ b/interface/resources/images/snap-feed.svg @@ -9,31 +9,31 @@ .st3{fill:#31D8FF;} - - - - - - - - + + + + + + + + - - + + diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 3e676b7340..7eb4de6bba 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -131,7 +131,7 @@ Window { onClicked: addressBarDialog.loadHome(); anchors { left: parent.left - leftMargin: hifi.layout.spacing * 2 + leftMargin: homeButton.width / 2 verticalCenter: parent.verticalCenter } } @@ -170,8 +170,8 @@ Window { bottom: parent.bottom left: forwardArrow.right right: placesButton.left - leftMargin: hifi.layout.spacing * 4 - rightMargin: hifi.layout.spacing + leftMargin: forwardArrow.width + rightMargin: placesButton.width topMargin: parent.inputAreaStep + hifi.layout.spacing bottomMargin: parent.inputAreaStep + hifi.layout.spacing } @@ -191,7 +191,6 @@ Window { onClicked: useFeed ? toggleFeed() : identity() anchors { right: feedButton.left; - rightMargin: hifi.layout.spacing * 3; bottom: addressLine.bottom; } } @@ -205,7 +204,7 @@ Window { anchors { right: parent.right; bottom: addressLine.bottom; - rightMargin: hifi.layout.spacing * 3; + rightMargin: feedButton.width / 2 } } } From 8a7bbbaaed84921548f4cfb8a8fbc45ca8b416ac Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 4 Aug 2016 12:37:09 -0700 Subject: [PATCH 031/332] limit user stories to first 100 --- interface/resources/qml/AddressBarDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 7eb4de6bba..5883872c95 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -411,7 +411,7 @@ Window { suggestions.append(story); }); } - if (data.current_page < data.total_pages) { + if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now return getUserStoryPage(pageNumber + 1, cb); } cb(); From fed775ff83b5211350931a2be98f8623aa622db0 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 4 Aug 2016 15:52:39 -0700 Subject: [PATCH 032/332] simple snapshot confirmation --- interface/src/scripting/WindowScriptingInterface.cpp | 2 +- scripts/system/snapshot.js | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index fb7be86096..026818ec82 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -96,7 +96,7 @@ void WindowScriptingInterface::alert(const QString& message) { /// \param const QString& message message to display /// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise QScriptValue WindowScriptingInterface::confirm(const QString& message) { - return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message))); + return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No))); } /// Display a prompt with a text box diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 87ea3f29ec..92ce36e935 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -20,6 +20,13 @@ var button = toolBar.addButton({ alpha: 0.9, }); +function confirmShare(data) { + if (!Window.confirm("Share snapshot " + data.localPath + "?")) { // This dialog will be more elaborate... + return; + } + Window.alert("Pretending to upload. That code will go here."); +} + function onClicked() { // update button states resetOverlays = Menu.isOptionChecked("Overlays"); @@ -56,7 +63,8 @@ function resetButtons(path, notify) { button.writeProperty("defaultState", 1); button.writeProperty("hoverState", 3); Window.snapshotTaken.disconnect(resetButtons); -} + confirmShare({localPath: path}); + } button.clicked.connect(onClicked); From 4f33e1502f19835460b007891c33c13dd2d6b90f Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Mon, 27 Jun 2016 17:41:21 -0700 Subject: [PATCH 033/332] starting the zip project --- interface/src/Application.cpp | 7 +++++++ .../src/scripting/WindowScriptingInterface.cpp | 13 +++++++++++++ scripts/system/edit.js | 13 +++++++++++++ 3 files changed, 33 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5d50a1c9fe..08788cb5bc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2015,6 +2015,13 @@ bool Application::importSVOFromURL(const QString& urlString) { return true; } +// attempt to start ZIP download project +bool Appplication::importZIPFromURL(const QString& urlString) { + emit zipImportRequested(urlString); + return true; +} +// end attempt + bool Application::event(QEvent* event) { if (!Menu::getInstance()) { diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index b165cda135..4a4f4b1025 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -59,6 +59,19 @@ WindowScriptingInterface::WindowScriptingInterface() { OffscreenUi::warning("Import SVO Error", "You need to be running edit.js to import entities."); } }); + // attempt to start ZIP download project + connect(qApp, &Application::zipImportRequested, [this](const QString& urlString) { + static const QMetaMethod zipImportRequestedSignal = + QMetaMethod::fromSignal(&WindowScriptingInterface::zipImportRequested); + + if (isSignalConnected(zipImportRequestedSignal)) { + QUrl url(urlString); + emit zipImportRequested(url.url()); + } else { + OffscreenUi::warning("Import ZIP Error", "You need to be running edit.js to import entities."); + } + // end attempt + }); } WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height) { diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 25c25a9a7e..18a76eb45f 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1205,6 +1205,19 @@ function importSVO(importURL) { } Window.svoImportRequested.connect(importSVO); +// attempt to start ZIP download project +function importZIP(importURL) { + print("Import ZIP requested: " + importURL); + if (!Entities.canAdjustLocks()) { + Window.alert(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); + return; + } + + +} +Window.zipImportRequested.connect(importZIP); +//end attempt + Menu.menuItemEvent.connect(handeMenuEvent); Controller.keyPressEvent.connect(function (event) { From f277a019bff5aadf8f8b809cd8b3eb44d28dd821 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Tue, 28 Jun 2016 17:56:28 -0700 Subject: [PATCH 034/332] FileScriptingInterface Created new .cpp and .h for the download and unzipping of a model --- .../src/scripting/WindowScriptingInterface.h | 1 + .../src/FileScriptingInterface.cpp | 51 +++++++++++++++++++ .../src/FileScriptingInterface.h | 30 +++++++++++ libraries/script-engine/src/ScriptEngine.cpp | 5 ++ 4 files changed, 87 insertions(+) create mode 100644 libraries/script-engine/src/FileScriptingInterface.cpp create mode 100644 libraries/script-engine/src/FileScriptingInterface.h diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 9d73111333..b33da4ef4d 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -58,6 +58,7 @@ public slots: signals: void domainChanged(const QString& domainHostname); void svoImportRequested(const QString& url); + void zipImportRequested(const QString& url); // zip project void domainConnectionRefused(const QString& reasonMessage, int reasonCode); void snapshotTaken(const QString& path); diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp new file mode 100644 index 0000000000..3ecb1bfd22 --- /dev/null +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -0,0 +1,51 @@ +// +// FileScriptingInterface.cpp +// interface/src/scripting +// +// Created by Elisa Lupin-Jimenez on 6/28/16. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include "ResourceManager.h" + +#include "FileScriptingInterface.h" + + +FileScriptingInterface::FileScriptingInterface(QObject* parent) { + // nothing for now +} + +void FileScriptingInterface::downloadZip() { + QUrl url(*parent); + auto request = ResourceManager::createResourceRequest(nullptr, url); + connect(request, &ResourceRequest::finished, this, &FileScriptingInterface::unzipFile); + request->send(); +} + +// clement's help :D +bool FileScriptingInterface::unzipFile() { + ResourceRequest* request = qobject_cast(sender()); + + // Get the file URL + QUrl url = request->getUrl(); + + if (request->getResult() == ResourceRequest::Success) { + qDebug() << "Success =)"; + + QByteArray fileContent = request->getData(); // <-- Downloaded file is in here + // Do stuff + // + // unzipFile(fileContent); + + } else { + qDebug() << "Could not download the file =("; + } + +} + diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h new file mode 100644 index 0000000000..67ea207aba --- /dev/null +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -0,0 +1,30 @@ +// +// FileScriptingInterface.h +// interface/src/scripting +// +// Created by Elisa Lupin-Jimenez on 6/28/16. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_FileScriptingInterface_h +#define hifi_FileScriptingInterface_h + +#include + +class FileScriptingInterface : public QObject { + Q_OBJECT + +public: + FileScriptingInterface(QObject* parent); + +public slots: + QScriptValue hasFocus(); + +}; + + + +#endif // hifi_FileScriptingInterface_h \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f98b07478b..fe7e1c4036 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -49,6 +49,7 @@ #include "BatchLoader.h" #include "DataViewClass.h" #include "EventTypes.h" +#include "FileScriptingInterface.h" // unzip project #include "MenuItemProperties.h" #include "ScriptAudioInjector.h" #include "ScriptCache.h" @@ -501,6 +502,10 @@ void ScriptEngine::init() { registerGlobalObject("Mat4", &_mat4Library); registerGlobalObject("Uuid", &_uuidLibrary); registerGlobalObject("Messages", DependencyManager::get().data()); + + // unzip project + registerGlobalObject("File", new FileScriptingInterface(this)); + qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); From 0e79aa9dbe6b132cd57e26b6355a97975a2b2e82 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Wed, 29 Jun 2016 18:10:51 -0700 Subject: [PATCH 035/332] Added quazip for unzipping functions --- BUILD.md | 1 + CMakeLists.txt | 1 + libraries/script-engine/CMakeLists.txt | 6 ++ .../src/FileScriptingInterface.cpp | 85 ++++++++++++++++--- .../src/FileScriptingInterface.h | 11 ++- 5 files changed, 89 insertions(+), 15 deletions(-) diff --git a/BUILD.md b/BUILD.md index c1ccd3193e..4ff45a0b1e 100644 --- a/BUILD.md +++ b/BUILD.md @@ -5,6 +5,7 @@ * [OpenSSL](https://www.openssl.org/community/binaries.html) ~> 1.0.1m * IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities. * [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) +* [QuaZip](http://sourceforge.net/projects/quazip/files/quazip/) ~> 0.7.1 ####CMake External Project Dependencies diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d42be3d95..c0e11a72be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -201,6 +201,7 @@ set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX}) setup_externals_binary_dir() option(USE_NSIGHT "Attempt to find the nSight libraries" 1) +option(GET_QUAZIP "Get QuaZip library automatically as external project" 1) if (WIN32) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 48fda99b9d..79fa7a1504 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,3 +1,9 @@ set(TARGET_NAME script-engine) setup_hifi_library(Gui Network Script ScriptTools WebSockets Widgets) + +add_dependency_external_projects(quazip) +find_package(QuaZip REQUIRED) +target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${QUAZIP_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES}) + link_hifi_libraries(shared networking octree gpu ui procedural model model-networking recording avatars fbx entities controllers animation audio physics) diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 3ecb1bfd22..6e9652e134 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -3,7 +3,7 @@ // interface/src/scripting // // Created by Elisa Lupin-Jimenez on 6/28/16. -// Copyright 2014 High Fidelity, Inc. +// Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -12,6 +12,10 @@ #include #include #include +#include +#include +#include +#include #include "ResourceManager.h" #include "FileScriptingInterface.h" @@ -29,23 +33,78 @@ void FileScriptingInterface::downloadZip() { } // clement's help :D -bool FileScriptingInterface::unzipFile() { +void FileScriptingInterface::unzipFile() { ResourceRequest* request = qobject_cast(sender()); + QUrl url = request->getUrl(); - // Get the file URL - QUrl url = request->getUrl(); + if (request->getResult() == ResourceRequest::Success) { + qDebug() << "Zip file was downloaded"; + QTemporaryDir dir; + QByteArray compressedFileContent = request->getData(); // <-- Downloaded file is in here + QBuffer buffer(&compressedFileContent); + buffer.open(QIODevice::ReadOnly); - if (request->getResult() == ResourceRequest::Success) { - qDebug() << "Success =)"; + QString dirName = dir.path(); + JICompress::extractDir(buffer, dirName); - QByteArray fileContent = request->getData(); // <-- Downloaded file is in here - // Do stuff - // - // unzipFile(fileContent); - - } else { - qDebug() << "Could not download the file =("; + QFileInfoList files = dir.entryInfoList(); + foreach (QFileInfo file, files) { + recursiveFileScan(file); } + + /*foreach (QFileInfo file, files) { + if (file.isDir()) { + if (file.fileName().contains(".zip")) { + qDebug() << "Zip file expanded: " + file.fileName(); + } + + qDebug() << "Regular file logged: " + file.fileName(); + } + }*/ + + + //QString zipFileName = QFile::decodeName(&compressedFileContent); + + //QFile file = + //need to convert this byte array to a file + /*QuaZip zip(zipFileName); + + if (zip.open(QuaZip::mdUnzip)) { + qDebug() << "Opened"; + + for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile()) { + // do something + qDebug() << zip.getCurrentFileName(); + } + if (zip.getZipError() == UNZ_OK) { + // ok, there was no error + }*/ + + + buffer.close(); + } else { + qDebug() << "Could not download the zip file"; + } + } +void FileScriptingInterface::recursiveFileScan(QFileInfo file) { + if (!file.isDir()) { + qDebug() << "Regular file logged:" + file.fileName(); + return; + } + if (file.fileName().contains(".zip")) { + + } + + QFileInfoList files = file.entryInfoList(); + if (files.empty()) { + files = JlCompress::getFileList(file.fileName()); + } + foreach (QFileInfo file, files) { + if (file.fileName().contains(".zip")) { + + } + } +} diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index 67ea207aba..89e3434382 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -3,7 +3,7 @@ // interface/src/scripting // // Created by Elisa Lupin-Jimenez on 6/28/16. -// Copyright 2014 High Fidelity, Inc. +// Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -21,10 +21,17 @@ public: FileScriptingInterface(QObject* parent); public slots: - QScriptValue hasFocus(); + void unzipFile(); + +signals: + void downloadZip(); }; +private: + void downloadZip(); + void unzipFile(); + #endif // hifi_FileScriptingInterface_h \ No newline at end of file From c9c965b1e80a6081d043d64093785fb857a8e743 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Wed, 29 Jun 2016 18:12:52 -0700 Subject: [PATCH 036/332] added new files for quazip --- cmake/externals/quazip/CMakeLists.txt | 37 +++++++++++++++++++++++++++ cmake/modules/FindQuaZip.cmake | 33 ++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 cmake/externals/quazip/CMakeLists.txt create mode 100644 cmake/modules/FindQuaZip.cmake diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt new file mode 100644 index 0000000000..5795e6bc5d --- /dev/null +++ b/cmake/externals/quazip/CMakeLists.txt @@ -0,0 +1,37 @@ +set(EXTERNAL_NAME quazip) + +if (ANDROID) + set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") +endif () + +include(ExternalProject) +ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://headache.hungry.com/~seth/hifi/quazip-0.7.1.tar.gz + URL_MD5 e4a14ad41b9a1ce494f8dd9add56429e + # BUILD_COMMAND ${MAKE_COMMAND} tbb_os=macos + # CONFIGURE_COMMAND "" + # INSTALL_COMMAND ${PLATFORM_BUILD_COMMAND} + CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= + BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 +) + +# Hide this external target (for ide users) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + +if (WIN32) + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/QUAZIP_LIB.lib CACHE FILEPATH "Path to QuaZip debug library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/QUAZIP_LIB.lib CACHE FILEPATH "Path to QuaZip release library") +else () + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to QuaZip debug library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip.so CACHE FILEPATH "Path to QuaZip release library") +endif () + +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to QuaZip include directory") \ No newline at end of file diff --git a/cmake/modules/FindQuaZip.cmake b/cmake/modules/FindQuaZip.cmake new file mode 100644 index 0000000000..a9f53ec788 --- /dev/null +++ b/cmake/modules/FindQuaZip.cmake @@ -0,0 +1,33 @@ +# +# FindQuaZip.cmake +# +# Once done this will define +# +# QUAZIP_FOUND - system found QuaZip +# QUAZIP_INCLUDE_DIRS - the QuaZip include directory +# QUAZIP_LIBRARIES - link to this to use QuaZip +# +# Created on 2015-8-1 by Seth Alves +# Copyright 2015 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# + +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("quazip") + +find_path(QUAZIP_INCLUDE_DIRS quazip.h PATH_SUFFIXES include HINTS ${QUAZIP_SEARCH_DIRS}) + +find_library(QUAZIP_LIBRARY_DEBUG NAMES QUAZIP QUAZIP_LIB PATH_SUFFIXES lib/Debug HINTS ${QUAZIP_SEARCH_DIRS}) +find_library(QUAZIP_LIBRARY_RELEASE NAMES QUAZIP QUAZIP_LIB PATH_SUFFIXES lib/Release lib HINTS ${QUAZIP_SEARCH_DIRS}) + +include(SelectLibraryConfigurations) +select_library_configurations(QUAZIP) + +set(QUAZIP_LIBRARIES ${QUAZIP_LIBRARY}) + +find_package_handle_standard_args(QUAZIP "Could NOT find QuaZip, try to set the path to QuaZip root folder in the system variable QUAZIP_ROOT_DIR or create a directory quazip in HIFI_LIB_DIR and paste the necessary files there" + QUAZIP_INCLUDE_DIRS QUAZIP_LIBRARIES) + +mark_as_advanced(QUAZIP_INCLUDE_DIRS QUAZIP_LIBRARIES QUAZIP_SEARCH_DIRS) \ No newline at end of file From 87cbb79d673433d949b1f49abdadda0fd1882022 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Tue, 5 Jul 2016 10:34:14 -0700 Subject: [PATCH 037/332] fixing up quazip --- cmake/macros/TargetQuazip.cmake | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 cmake/macros/TargetQuazip.cmake diff --git a/cmake/macros/TargetQuazip.cmake b/cmake/macros/TargetQuazip.cmake new file mode 100644 index 0000000000..f704f03050 --- /dev/null +++ b/cmake/macros/TargetQuazip.cmake @@ -0,0 +1,16 @@ +# +# Copyright 2015 High Fidelity, Inc. +# Created by Leonardo Murillo on 2015/11/20 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_QUAZIP) + add_dependency_external_projects(quazip) + find_package(QuaZip REQUIRED) + target_include_directories(${TARGET_NAME} PUBLIC ${QUAZIP_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES}) + if (WIN32) + add_paths_to_fixup_libs(${QUAZIP_DLL_PATH}) + endif () +endmacro() \ No newline at end of file From 212e4f9ccaddf80d8bc9f8a09dc48bd4270c0578 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Tue, 5 Jul 2016 10:38:35 -0700 Subject: [PATCH 038/332] making quazip work --- cmake/externals/quazip/CMakeLists.txt | 53 ++++++++++++------- cmake/modules/FindQuaZip.cmake | 46 ++++++++-------- interface/src/Application.cpp | 2 +- interface/src/Application.h | 2 + libraries/script-engine/CMakeLists.txt | 2 + .../src/FileScriptingInterface.cpp | 35 +++++++----- .../src/FileScriptingInterface.h | 12 ++--- 7 files changed, 87 insertions(+), 65 deletions(-) diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index 5795e6bc5d..413a07efec 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -1,37 +1,52 @@ set(EXTERNAL_NAME quazip) - -if (ANDROID) - set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") -endif () +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +cmake_policy(SET CMP0046 OLD) include(ExternalProject) + +if (WIN32) + # windows shell does not like backslashes expanded on the command line, + # so convert all backslashes in the QT path to forward slashes + string(REPLACE \\ / QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH}) +elseif ($ENV{QT_CMAKE_PREFIX_PATH}) + set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH}) +endif () + ExternalProject_Add( ${EXTERNAL_NAME} - URL http://headache.hungry.com/~seth/hifi/quazip-0.7.1.tar.gz - URL_MD5 e4a14ad41b9a1ce494f8dd9add56429e - # BUILD_COMMAND ${MAKE_COMMAND} tbb_os=macos - # CONFIGURE_COMMAND "" - # INSTALL_COMMAND ${PLATFORM_BUILD_COMMAND} - CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= + URL http://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.6.2.zip + URL_MD5 514851970f1a14d815bdc3ad6267af4d BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=/lib -DZLIB_ROOT=${ZLIB_ROOT} LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 ) +add_dependencies(quazip zlib) + # Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") +set_target_properties(${EXTERNAL_NAME} PROPERTIES + FOLDER "hidden/externals" + INSTALL_NAME_DIR ${INSTALL_DIR}/lib + BUILD_WITH_INSTALL_RPATH True) ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include CACHE PATH "List of QuaZip include directories") +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${${EXTERNAL_NAME_UPPER}_INCLUDE_DIR} CACHE PATH "List of QuaZip include directories") +set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/lib CACHE FILEPATH "Location of QuaZip DLL") -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - -if (WIN32) - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/QUAZIP_LIB.lib CACHE FILEPATH "Path to QuaZip debug library") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/QUAZIP_LIB.lib CACHE FILEPATH "Path to QuaZip release library") +if (APPLE) + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") +elseif (WIN32) + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/quazip.lib CACHE FILEPATH "Location of QuaZip release library") else () - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to QuaZip debug library") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip.so CACHE FILEPATH "Path to QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip.so CACHE FILEPATH "Location of QuaZip release library") endif () -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to QuaZip include directory") \ No newline at end of file +include(SelectLibraryConfigurations) +select_library_configurations(${EXTERNAL_NAME_UPPER}) + +# Force selected libraries into the cache +set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE FILEPATH "Location of QuaZip libraries") +set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of QuaZip libraries") \ No newline at end of file diff --git a/cmake/modules/FindQuaZip.cmake b/cmake/modules/FindQuaZip.cmake index a9f53ec788..2b2a577919 100644 --- a/cmake/modules/FindQuaZip.cmake +++ b/cmake/modules/FindQuaZip.cmake @@ -1,33 +1,29 @@ # -# FindQuaZip.cmake -# -# Once done this will define -# -# QUAZIP_FOUND - system found QuaZip -# QUAZIP_INCLUDE_DIRS - the QuaZip include directory -# QUAZIP_LIBRARIES - link to this to use QuaZip -# -# Created on 2015-8-1 by Seth Alves -# Copyright 2015 High Fidelity, Inc. -# -# Distributed under the Apache License, Version 2.0. -# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# FindQuaZip.h +# StackManagerQt/cmake/modules # +# Created by Mohammed Nafees. +# Copyright (c) 2014 High Fidelity. All rights reserved. +# + +# QUAZIP_FOUND - QuaZip library was found +# QUAZIP_INCLUDE_DIR - Path to QuaZip include dir +# QUAZIP_INCLUDE_DIRS - Path to QuaZip and zlib include dir (combined from QUAZIP_INCLUDE_DIR + ZLIB_INCLUDE_DIR) +# QUAZIP_LIBRARIES - List of QuaZip libraries +# QUAZIP_ZLIB_INCLUDE_DIR - The include dir of zlib headers include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") hifi_library_search_hints("quazip") -find_path(QUAZIP_INCLUDE_DIRS quazip.h PATH_SUFFIXES include HINTS ${QUAZIP_SEARCH_DIRS}) +if (WIN32) + find_path(QUAZIP_INCLUDE_DIRS quazip.h PATH_SUFFIXES include/quazip HINTS ${QUAZIP_SEARCH_DIRS}) +elseif (APPLE) + find_path(QUAZIP_INCLUDE_DIRS quazip.h PATH_SUFFIXES include/quazip HINTS ${QUAZIP_SEARCH_DIRS}) +else () + find_path(QUAZIP_INCLUDE_DIRS quazip.h PATH_SUFFIXES quazip HINTS ${QUAZIP_SEARCH_DIRS}) +endif () -find_library(QUAZIP_LIBRARY_DEBUG NAMES QUAZIP QUAZIP_LIB PATH_SUFFIXES lib/Debug HINTS ${QUAZIP_SEARCH_DIRS}) -find_library(QUAZIP_LIBRARY_RELEASE NAMES QUAZIP QUAZIP_LIB PATH_SUFFIXES lib/Release lib HINTS ${QUAZIP_SEARCH_DIRS}) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(QUAZIP DEFAULT_MSG QUAZIP_INCLUDE_DIRS) -include(SelectLibraryConfigurations) -select_library_configurations(QUAZIP) - -set(QUAZIP_LIBRARIES ${QUAZIP_LIBRARY}) - -find_package_handle_standard_args(QUAZIP "Could NOT find QuaZip, try to set the path to QuaZip root folder in the system variable QUAZIP_ROOT_DIR or create a directory quazip in HIFI_LIB_DIR and paste the necessary files there" - QUAZIP_INCLUDE_DIRS QUAZIP_LIBRARIES) - -mark_as_advanced(QUAZIP_INCLUDE_DIRS QUAZIP_LIBRARIES QUAZIP_SEARCH_DIRS) \ No newline at end of file +mark_as_advanced(QUAZIP_INCLUDE_DIRS QUAZIP_SEARCH_DIRS) \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 08788cb5bc..4be9a3c5c1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2016,7 +2016,7 @@ bool Application::importSVOFromURL(const QString& urlString) { } // attempt to start ZIP download project -bool Appplication::importZIPFromURL(const QString& urlString) { +bool Application::importZIPFromURL(const QString& urlString) { emit zipImportRequested(urlString); return true; } diff --git a/interface/src/Application.h b/interface/src/Application.h index 0af65f665f..d8f02f41b7 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -250,6 +250,7 @@ public: signals: void svoImportRequested(const QString& url); + void zipImportRequested(const QString& url); void fullAvatarURLChanged(const QString& newValue, const QString& modelName); @@ -385,6 +386,7 @@ private: bool importJSONFromURL(const QString& urlString); bool importSVOFromURL(const QString& urlString); + bool importZIPFromURL(const QString& urlString); bool nearbyEntitiesAreReadyForPhysics(); int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode); diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 79fa7a1504..b484937ea5 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,6 +1,8 @@ set(TARGET_NAME script-engine) setup_hifi_library(Gui Network Script ScriptTools WebSockets Widgets) +target_zlib() + add_dependency_external_projects(quazip) find_package(QuaZip REQUIRED) target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${QUAZIP_INCLUDE_DIRS}) diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 6e9652e134..017c3f83e1 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -11,17 +11,20 @@ #include #include +#include +#include #include #include #include +#include #include -#include +#include #include "ResourceManager.h" #include "FileScriptingInterface.h" -FileScriptingInterface::FileScriptingInterface(QObject* parent) { +FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent) { // nothing for now } @@ -45,11 +48,11 @@ void FileScriptingInterface::unzipFile() { buffer.open(QIODevice::ReadOnly); QString dirName = dir.path(); - JICompress::extractDir(buffer, dirName); + JlCompress::extractDir(buffer, dirName); QFileInfoList files = dir.entryInfoList(); foreach (QFileInfo file, files) { - recursiveFileScan(file); + recursiveFileScan(file, &dirName); } @@ -89,22 +92,26 @@ void FileScriptingInterface::unzipFile() { } -void FileScriptingInterface::recursiveFileScan(QFileInfo file) { +void FileScriptingInterface::recursiveFileScan(QFileInfo file, QString* dirName) { if (!file.isDir()) { - qDebug() << "Regular file logged:" + file.fileName(); + qDebug() << "Regular file logged: " + file.fileName(); return; } + QFileInfoList files; + if (file.fileName().contains(".zip")) { - - } + JlCompress::extractDir(file); + qDebug() << "Extracting archive: " + file.fileName(); + } + files = file.entryInfoList(); - QFileInfoList files = file.entryInfoList(); - if (files.empty()) { + /*if (files.empty()) { files = JlCompress::getFileList(file.fileName()); - } - foreach (QFileInfo file, files) { - if (file.fileName().contains(".zip")) { + }*/ - } + foreach (QFileInfo file, files) { + qDebug() << "Looking into file: " + file.fileName(); + recursiveFileScan(file, &dirName); } + return; } diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index 89e3434382..cf965afa34 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -13,6 +13,7 @@ #define hifi_FileScriptingInterface_h #include +#include class FileScriptingInterface : public QObject { Q_OBJECT @@ -26,12 +27,11 @@ public slots: signals: void downloadZip(); +private: + //void downloadZip(); + //void unzipFile(); + void recursiveFileScan(QFileInfo file, QString* dirName); + }; -private: - void downloadZip(); - void unzipFile(); - - - #endif // hifi_FileScriptingInterface_h \ No newline at end of file From 9833ba19878bcc2b5e493ba00b69b587eecc2c88 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Wed, 6 Jul 2016 16:43:23 -0700 Subject: [PATCH 039/332] No errors upon build --- cmake/externals/quazip/CMakeLists.txt | 10 ++--- libraries/script-engine/CMakeLists.txt | 4 ++ .../src/FileScriptingInterface.cpp | 45 +++++++++++++------ .../src/FileScriptingInterface.h | 9 ++-- scripts/system/edit.js | 7 ++- 5 files changed, 52 insertions(+), 23 deletions(-) diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index 413a07efec..bf51a52fc5 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -14,8 +14,8 @@ endif () ExternalProject_Add( ${EXTERNAL_NAME} - URL http://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.6.2.zip - URL_MD5 514851970f1a14d815bdc3ad6267af4d + URL https://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.7.2.zip + URL_MD5 2955176048a31262c09259ca8d309d19 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=/lib -DZLIB_ROOT=${ZLIB_ROOT} LOG_DOWNLOAD 1 @@ -37,11 +37,11 @@ set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${${EXTERNAL_NAME_UPPER}_INCLUDE_DIR} CA set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/lib CACHE FILEPATH "Location of QuaZip DLL") if (APPLE) - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5d.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") elseif (WIN32) - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/quazip.lib CACHE FILEPATH "Location of QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/quazip5d.lib CACHE FILEPATH "Location of QuaZip release library") else () - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip.so CACHE FILEPATH "Location of QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5d.so CACHE FILEPATH "Location of QuaZip release library") endif () include(SelectLibraryConfigurations) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index b484937ea5..c3a9a9f38a 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -8,4 +8,8 @@ find_package(QuaZip REQUIRED) target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${QUAZIP_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES}) +if (WIN32) + add_paths_to_fixup_libs(${QUAZIP_DLL_PATH}) +endif () + link_hifi_libraries(shared networking octree gpu ui procedural model model-networking recording avatars fbx entities controllers animation audio physics) diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 017c3f83e1..814a054e5e 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -1,6 +1,6 @@ // // FileScriptingInterface.cpp -// interface/src/scripting +// libraries/script-engine/src // // Created by Elisa Lupin-Jimenez on 6/28/16. // Copyright 2016 High Fidelity, Inc. @@ -10,15 +10,18 @@ // #include +#include #include #include #include +#include +#include #include #include #include #include -#include -#include +#include +#include #include "ResourceManager.h" #include "FileScriptingInterface.h" @@ -28,27 +31,43 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent // nothing for now } -void FileScriptingInterface::downloadZip() { - QUrl url(*parent); +void FileScriptingInterface::runUnzip(QString path, QString importURL) { + downloadZip(path, importURL); + +} + +QString FileScriptingInterface::getTempDir() { + QTemporaryDir dir; + dir.setAutoRemove(false); + return dir.path(); + // remember I must do something to delete this temp dir later +} + +void FileScriptingInterface::downloadZip(QString path, const QString link) { + QUrl url = QUrl(link); auto request = ResourceManager::createResourceRequest(nullptr, url); - connect(request, &ResourceRequest::finished, this, &FileScriptingInterface::unzipFile); + connect(request, &ResourceRequest::finished, this, [this, path]{ + unzipFile(path); + }); request->send(); } // clement's help :D -void FileScriptingInterface::unzipFile() { +void FileScriptingInterface::unzipFile(QString path) { ResourceRequest* request = qobject_cast(sender()); QUrl url = request->getUrl(); if (request->getResult() == ResourceRequest::Success) { qDebug() << "Zip file was downloaded"; - QTemporaryDir dir; + QDir dir(path); QByteArray compressedFileContent = request->getData(); // <-- Downloaded file is in here QBuffer buffer(&compressedFileContent); buffer.open(QIODevice::ReadOnly); + //QString zipFileName = QFile::decodeName(compressedFileContent); QString dirName = dir.path(); - JlCompress::extractDir(buffer, dirName); + qDebug() << "Zip directory is stored at: " + dirName; + JlCompress::extractDir(&buffer, dirName); QFileInfoList files = dir.entryInfoList(); foreach (QFileInfo file, files) { @@ -67,7 +86,7 @@ void FileScriptingInterface::unzipFile() { }*/ - //QString zipFileName = QFile::decodeName(&compressedFileContent); + //QFile file = //need to convert this byte array to a file @@ -100,10 +119,10 @@ void FileScriptingInterface::recursiveFileScan(QFileInfo file, QString* dirName) QFileInfoList files; if (file.fileName().contains(".zip")) { - JlCompress::extractDir(file); + JlCompress::extractDir(file.fileName()); qDebug() << "Extracting archive: " + file.fileName(); } - files = file.entryInfoList(); + files = file.dir().entryInfoList(); /*if (files.empty()) { files = JlCompress::getFileList(file.fileName()); @@ -111,7 +130,7 @@ void FileScriptingInterface::recursiveFileScan(QFileInfo file, QString* dirName) foreach (QFileInfo file, files) { qDebug() << "Looking into file: " + file.fileName(); - recursiveFileScan(file, &dirName); + recursiveFileScan(file, dirName); } return; } diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index cf965afa34..a9ccd3442f 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -1,6 +1,6 @@ // // FileScriptingInterface.h -// interface/src/scripting +// libraries/script-engine/src // // Created by Elisa Lupin-Jimenez on 6/28/16. // Copyright 2016 High Fidelity, Inc. @@ -14,23 +14,26 @@ #include #include +#include class FileScriptingInterface : public QObject { Q_OBJECT public: FileScriptingInterface(QObject* parent); + void runUnzip(QString path, QString importURL); + QString getTempDir(); public slots: - void unzipFile(); + void unzipFile(QString path); signals: - void downloadZip(); private: //void downloadZip(); //void unzipFile(); void recursiveFileScan(QFileInfo file, QString* dirName); + void downloadZip(QString path, const QString link); }; diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 18a76eb45f..f096deb0a7 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1208,10 +1208,13 @@ Window.svoImportRequested.connect(importSVO); // attempt to start ZIP download project function importZIP(importURL) { print("Import ZIP requested: " + importURL); - if (!Entities.canAdjustLocks()) { + /*if (!Entities.canAdjustLocks()) { Window.alert(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); return; - } + }*/ + var path = File.getTempDir(); + print("Temporary path to zip: " + path); + File.runUnzip(path, importURL); } From 76a847d34f1ca3df84d7606f796f5c02e97742f3 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Fri, 8 Jul 2016 10:54:11 -0700 Subject: [PATCH 040/332] testing for URL bug --- interface/src/Application.cpp | 7 +++++-- interface/src/scripting/WindowScriptingInterface.cpp | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4be9a3c5c1..3d0a4628b1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2010,13 +2010,13 @@ bool Application::importJSONFromURL(const QString& urlString) { } bool Application::importSVOFromURL(const QString& urlString) { - emit svoImportRequested(urlString); return true; } // attempt to start ZIP download project bool Application::importZIPFromURL(const QString& urlString) { + qDebug() << "zip import request has been emitted"; emit zipImportRequested(urlString); return true; } @@ -2141,13 +2141,16 @@ bool Application::event(QEvent* event) { // handle custom URL if (event->type() == QEvent::FileOpen) { - QFileOpenEvent* fileEvent = static_cast(event); + qDebug() << "we have received one url!: "; + QFileOpenEvent* fileEvent = static_cast(event); QUrl url = fileEvent->url(); if (!url.isEmpty()) { QString urlString = url.toString(); + qDebug() << "we got a url!: " + urlString; if (canAcceptURL(urlString)) { + qDebug() << "we got an ACCEPTED url!: " + urlString; return acceptURL(urlString); } } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 4a4f4b1025..2ea562143e 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -61,6 +61,7 @@ WindowScriptingInterface::WindowScriptingInterface() { }); // attempt to start ZIP download project connect(qApp, &Application::zipImportRequested, [this](const QString& urlString) { + qDebug() << "zip import has been requested"; static const QMetaMethod zipImportRequestedSignal = QMetaMethod::fromSignal(&WindowScriptingInterface::zipImportRequested); From eab7714d70ccda26395a8b05c444dc42fc68db68 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Wed, 13 Jul 2016 12:47:19 -0700 Subject: [PATCH 041/332] working within webview to access url --- interface/resources/qml/Browser.qml | 1 + .../resources/qml/controls-uit/WebView.qml | 16 +++- interface/src/Application.cpp | 18 ++++- libraries/ui/src/OffscreenUi.cpp | 3 + scripts/system/examples.js | 77 +++++++++++++++++++ 5 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 scripts/system/examples.js diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 47231957c2..33595c2038 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -223,6 +223,7 @@ ScrollingWindow { var newWindow = component.createObject(desktop); request.openIn(newWindow.webView) } + //profile: desktop.browserProfile } diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index b599e29fe0..99064cdcbd 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -49,6 +49,7 @@ WebEngineView { onLoadingChanged: { // Required to support clicking on "hifi://" links + console.log("loading change requested url"); if (WebEngineView.LoadStartedStatus == loadRequest.status) { var url = loadRequest.url.toString(); if (urlHandler.canHandleUrl(url)) { @@ -59,12 +60,19 @@ WebEngineView { } } - onNewViewRequested:{ - var component = Qt.createComponent("../Browser.qml"); - var newWindow = component.createObject(desktop); - request.openIn(newWindow.webView) + onNewViewRequested: { + console.log("new view requested url"); + console.log(request.url.toString()); + //if (toString(request.url) ) + var component = Qt.createComponent("../Browser.qml"); + var newWindow = component.createObject(desktop); + request.openIn(newWindow.webView); } + onLinkHovered: { + console.log(hoveredUrl); + } + // This breaks the webchannel used for passing messages. Fixed in Qt 5.6 // See https://bugreports.qt.io/browse/QTBUG-49521 //profile: desktop.browserProfile diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3d0a4628b1..2fee3488f9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -354,6 +354,7 @@ public: if (message->message == WM_COPYDATA) { COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)(message->lParam); + qDebug() << "os windows url for some reason"; QUrl url = QUrl((const char*)(pcds->lpData)); if (url.isValid() && url.scheme() == HIFI_URL_SCHEME) { DependencyManager::get()->handleLookupString(url.toString()); @@ -2157,6 +2158,10 @@ bool Application::event(QEvent* event) { return false; } + if (event->type() == QEvent::None) { + qDebug() << "this url just didn't click"; + } + if (HFActionEvent::types().contains(event->type())) { _controllerScriptingInterface->handleMetaEvent(static_cast(event)); } @@ -3180,6 +3185,7 @@ void Application::saveSettings() const { } bool Application::importEntities(const QString& urlOrFilename) { + qDebug() << "import entities url"; bool success = false; _entityClipboard->withWriteLock([&] { _entityClipboard->eraseAllOctreeElements(); @@ -4789,6 +4795,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri } bool Application::canAcceptURL(const QString& urlString) const { + qDebug() << "stepping through 'canAcceptURL'"; QUrl url(urlString); if (urlString.startsWith(HIFI_URL_SCHEME)) { return true; @@ -4805,6 +4812,7 @@ bool Application::canAcceptURL(const QString& urlString) const { } bool Application::acceptURL(const QString& urlString, bool defaultUpload) { + qDebug() << "stepping through 'acceptURL'"; if (urlString.startsWith(HIFI_URL_SCHEME)) { // this is a hifi URL - have the AddressManager handle it QMetaObject::invokeMethod(DependencyManager::get().data(), "handleLookupString", @@ -4834,6 +4842,7 @@ void Application::setSessionUUID(const QUuid& sessionUUID) const { } bool Application::askToSetAvatarUrl(const QString& url) { + qDebug() << "setting avatar url"; QUrl realUrl(url); if (realUrl.isLocalFile()) { OffscreenUi::warning("", "You can not use local files for avatar components."); @@ -4890,6 +4899,7 @@ bool Application::askToSetAvatarUrl(const QString& url) { bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { + qDebug() << "setting script url"; QMessageBox::StandardButton reply; QString shortName = scriptFilenameOrURL; @@ -4914,7 +4924,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { } bool Application::askToWearAvatarAttachmentUrl(const QString& url) { - + qDebug() << "setting avatar attachment url"; QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); @@ -4996,6 +5006,7 @@ bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) } void Application::toggleRunningScriptsWidget() const { + qDebug() << "toggle running scripts url"; static const QUrl url("hifi/dialogs/RunningScripts.qml"); DependencyManager::get()->show(url, "RunningScripts"); //if (_runningScriptsWidget->isVisible()) { @@ -5013,10 +5024,11 @@ void Application::toggleRunningScriptsWidget() const { } void Application::toggleAssetServerWidget(QString filePath) { + qDebug() << "toggle asset before if"; if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { return; } - + qDebug() << "toggle asset after if"; static const QUrl url { "AssetServer.qml" }; auto startUpload = [=](QQmlContext* context, QObject* newObject){ @@ -5033,6 +5045,7 @@ void Application::packageModel() { } void Application::openUrl(const QUrl& url) const { + qDebug() << "open url"; if (!url.isEmpty()) { if (url.scheme() == HIFI_URL_SCHEME) { DependencyManager::get()->handleLookupString(url.toString()); @@ -5063,6 +5076,7 @@ void Application::setPreviousScriptLocation(const QString& location) { } void Application::loadScriptURLDialog() const { + qDebug() << "load script url dialog"; auto newScript = OffscreenUi::getText(nullptr, "Open and Run Script", "Script URL"); if (!newScript.isEmpty()) { DependencyManager::get()->loadScript(newScript); diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 06ef456006..82a9186f92 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -78,17 +78,20 @@ class UrlHandler : public QObject { Q_OBJECT public: Q_INVOKABLE bool canHandleUrl(const QString& url) { + qDebug() << Q_FUNC_INFO; static auto handler = dynamic_cast(qApp); return handler->canAcceptURL(url); } Q_INVOKABLE bool handleUrl(const QString& url) { + qDebug() << Q_FUNC_INFO; static auto handler = dynamic_cast(qApp); return handler->acceptURL(url); } // FIXME hack for authentication, remove when we migrate to Qt 5.6 Q_INVOKABLE QString fixupUrl(const QString& originalUrl) { + qDebug() << Q_FUNC_INFO; return fixupHifiUrl(originalUrl); } }; diff --git a/scripts/system/examples.js b/scripts/system/examples.js new file mode 100644 index 0000000000..32d977834b --- /dev/null +++ b/scripts/system/examples.js @@ -0,0 +1,77 @@ +// +// examples.js +// examples +// +// Created by Eric Levin on 8 Jan 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var toolIconUrl = Script.resolvePath("assets/images/tools/"); + +var EXAMPLES_URL = "https://metaverse.highfidelity.com/examples"; +//var EXAMPLES_URL = "https://clara.io/view/c1c4d926-5648-4fd3-9673-6d9018ad4627"; +//var EXAMPLES_URL = "http://s3.amazonaws.com/DreamingContent/test.html"; + +var examplesWindow = new OverlayWebWindow({ + title: 'Examples', + source: "about:blank", + width: 900, + height: 700, + visible: false +}); + +var toolHeight = 50; +var toolWidth = 50; +var TOOLBAR_MARGIN_Y = 0; + + +function showExamples(marketplaceID) { + var url = EXAMPLES_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + print("setting examples URL to " + url); + examplesWindow.setURL(url); + examplesWindow.setVisible(true); + + UserActivityLogger.openedMarketplace(); +} + +function hideExamples() { + examplesWindow.setVisible(false); + examplesWindow.setURL("about:blank"); +} + +function toggleExamples() { + if (examplesWindow.visible) { + hideExamples(); + } else { + showExamples(); + } +} + +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + +var browseExamplesButton = toolBar.addButton({ + imageURL: toolIconUrl + "market.svg", + objectName: "examples", + buttonState: 1, + alpha: 0.9 +}); + +function onExamplesWindowVisibilityChanged() { + browseExamplesButton.writeProperty('buttonState', examplesWindow.visible ? 0 : 1); +} +function onClick() { + toggleExamples(); +} +browseExamplesButton.clicked.connect(onClick); +examplesWindow.visibleChanged.connect(onExamplesWindowVisibilityChanged); + +Script.scriptEnding.connect(function () { + browseExamplesButton.clicked.disconnect(onClick); + examplesWindow.visibleChanged.disconnect(onExamplesWindowVisibilityChanged); +}); From 6152fa28b7bc908a554609a01c14f7e6e72720e0 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Mon, 18 Jul 2016 15:29:15 -0700 Subject: [PATCH 042/332] Download through QML It kind of works, not crashing like before, but the weird ghost file/type error is still happening with multiple downloads --- interface/resources/qml/Browser.qml | 25 +++++++++++++++---- .../resources/qml/controls-uit/WebView.qml | 3 +-- interface/src/Application.cpp | 2 ++ .../src/FileScriptingInterface.cpp | 6 ++--- .../src/FileScriptingInterface.h | 7 +++--- scripts/system/edit.js | 2 +- scripts/system/examples.js | 6 +++-- 7 files changed, 35 insertions(+), 16 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 33595c2038..7436328dd8 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -218,12 +218,27 @@ ScrollingWindow { onIconChanged: { console.log("New icon: " + icon) } - onNewViewRequested:{ - var component = Qt.createComponent("Browser.qml"); - var newWindow = component.createObject(desktop); - request.openIn(newWindow.webView) - } + Component.onCompleted: { + webview.profile.downloadRequested.connect(function(download){ + console.log("Download start: " + download.state) + download.accept() + console.log("Download accept: " + download.state) + if (download.state === WebEngineDownloadItem.DownloadInterrupted) { + console.log("Download? " + download.state) + console.log("download failed to complete") + } + }) + + webview.profile.downloadFinished.connect(function(download){ + console.log("Download Finished: " + download.state) + if (download.state === WebEngineDownloadItem.DownloadCompleted) {console.log("getting download completed state")} + console.log("download success") + File.runUnzip(download.path) + }) + } + + //profile: desktop.browserProfile } diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index 99064cdcbd..3d3fc20888 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -62,8 +62,7 @@ WebEngineView { onNewViewRequested: { console.log("new view requested url"); - console.log(request.url.toString()); - //if (toString(request.url) ) + //console.log("new view requested url" + request.url.toString()); var component = Qt.createComponent("../Browser.qml"); var newWindow = component.createObject(desktop); request.openIn(newWindow.webView); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2fee3488f9..ccffceede0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -1542,6 +1543,7 @@ void Application::initializeUi() { rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); rootContext->setContextProperty("Controller", DependencyManager::get().data()); rootContext->setContextProperty("Entities", DependencyManager::get().data()); + rootContext->setContextProperty("File", new FileScriptingInterface(engine)); rootContext->setContextProperty("MyAvatar", getMyAvatar()); rootContext->setContextProperty("Messages", DependencyManager::get().data()); rootContext->setContextProperty("Recording", DependencyManager::get().data()); diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 814a054e5e..ea6f42a44a 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -31,9 +31,9 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent // nothing for now } -void FileScriptingInterface::runUnzip(QString path, QString importURL) { - downloadZip(path, importURL); - +void FileScriptingInterface::runUnzip(QString path) { + //downloadZip(path, importURL); + qDebug() << "Path where download is saved: " + path; } QString FileScriptingInterface::getTempDir() { diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index a9ccd3442f..0e12d3aa47 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -21,17 +21,18 @@ class FileScriptingInterface : public QObject { public: FileScriptingInterface(QObject* parent); - void runUnzip(QString path, QString importURL); + //void runUnzip(QString path, QString importURL); QString getTempDir(); public slots: - void unzipFile(QString path); + //void unzipFile(QString path); + void runUnzip(QString path); signals: private: //void downloadZip(); - //void unzipFile(); + void unzipFile(QString path); void recursiveFileScan(QFileInfo file, QString* dirName); void downloadZip(QString path, const QString link); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index f096deb0a7..1284202c6d 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1205,7 +1205,7 @@ function importSVO(importURL) { } Window.svoImportRequested.connect(importSVO); -// attempt to start ZIP download project +// attempt to start ZIP download project, GET RID OF THIS function importZIP(importURL) { print("Import ZIP requested: " + importURL); /*if (!Entities.canAdjustLocks()) { diff --git a/scripts/system/examples.js b/scripts/system/examples.js index 32d977834b..f67dd407bb 100644 --- a/scripts/system/examples.js +++ b/scripts/system/examples.js @@ -11,9 +11,11 @@ var toolIconUrl = Script.resolvePath("assets/images/tools/"); -var EXAMPLES_URL = "https://metaverse.highfidelity.com/examples"; +//var EXAMPLES_URL = "https://metaverse.highfidelity.com/examples"; //var EXAMPLES_URL = "https://clara.io/view/c1c4d926-5648-4fd3-9673-6d9018ad4627"; -//var EXAMPLES_URL = "http://s3.amazonaws.com/DreamingContent/test.html"; +var EXAMPLES_URL = "http://s3.amazonaws.com/DreamingContent/test.html"; +//var EXAMPLES_URL = "https://hifi-content.s3.amazonaws.com/elisalj/test.html"; +//var EXAMPLES_URL = "https://hifi-content.s3.amazonaws.com/elisalj/test_download.html"; var examplesWindow = new OverlayWebWindow({ title: 'Examples', From ab02d16eb7656ea0a37785a398716fad62dff045 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Mon, 18 Jul 2016 19:37:24 -0700 Subject: [PATCH 043/332] QML troubleshooting Trying to find out why it does multiple downloads on one click, when everything fails. Has something to do with a download finish request getting sent despite not being completed. --- interface/resources/qml/Browser.qml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 7436328dd8..e9421d5ad1 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -221,20 +221,25 @@ ScrollingWindow { Component.onCompleted: { webview.profile.downloadRequested.connect(function(download){ - console.log("Download start: " + download.state) - download.accept() - console.log("Download accept: " + download.state) - if (download.state === WebEngineDownloadItem.DownloadInterrupted) { - console.log("Download? " + download.state) - console.log("download failed to complete") + if (download.state === WebEngineDownloadItem.DownloadRequested) { + console.log("Download start: " + download.state) + download.accept() + console.log("Download accept: " + download.state) + if (download.state === WebEngineDownloadItem.DownloadInterrupted) { + console.log("Download? " + download.state) + console.log("download failed to complete") + } } }) webview.profile.downloadFinished.connect(function(download){ - console.log("Download Finished: " + download.state) - if (download.state === WebEngineDownloadItem.DownloadCompleted) {console.log("getting download completed state")} - console.log("download success") - File.runUnzip(download.path) + if (download.state === WebEngineDownloadItem.DownloadCompleted) { + console.log("Download Finished: " + download.state) + console.log("File object is: " + File) + File.runUnzip(download.path) + } else { + console.log("The download was corrupted") + } }) } From c36fc29363c9772a479cf01dd3eea3d2d5257146 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Tue, 19 Jul 2016 19:48:16 -0700 Subject: [PATCH 044/332] Zip download works --- interface/resources/qml/Browser.qml | 29 +++----------- .../resources/qml/controls-uit/WebView.qml | 7 +++- interface/resources/qml/hifi/Desktop.qml | 39 +++++++++++++++++++ .../src/FileScriptingInterface.cpp | 38 ++++++++++++------ .../src/FileScriptingInterface.h | 6 ++- 5 files changed, 82 insertions(+), 37 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index e9421d5ad1..79d045cf0f 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -140,6 +140,7 @@ ScrollingWindow { } } +<<<<<<< ab02d16eb7656ea0a37785a398716fad62dff045 Rectangle { id:permissionsContainer visible:false @@ -196,6 +197,8 @@ ScrollingWindow { } } } +======= +>>>>>>> Zip download works WebEngineView { id: webview @@ -220,35 +223,15 @@ ScrollingWindow { } Component.onCompleted: { - webview.profile.downloadRequested.connect(function(download){ - if (download.state === WebEngineDownloadItem.DownloadRequested) { - console.log("Download start: " + download.state) - download.accept() - console.log("Download accept: " + download.state) - if (download.state === WebEngineDownloadItem.DownloadInterrupted) { - console.log("Download? " + download.state) - console.log("download failed to complete") - } - } - }) - - webview.profile.downloadFinished.connect(function(download){ - if (download.state === WebEngineDownloadItem.DownloadCompleted) { - console.log("Download Finished: " + download.state) - console.log("File object is: " + File) - File.runUnzip(download.path) - } else { - console.log("The download was corrupted") - } - }) + desktop.initWebviewProfileHandlers(webview.profile) } - //profile: desktop.browserProfile } } // item - + + Keys.onPressed: { switch(event.key) { case Qt.Key_L: diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index 3d3fc20888..3ab1478750 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -69,9 +69,14 @@ WebEngineView { } onLinkHovered: { - console.log(hoveredUrl); + desktop.currentUrl = hoveredUrl + console.log("my url in WebView: " + desktop.currentUrl) } + //onLinkHovered: { + // console.log(hoveredUrl); + //} + // This breaks the webchannel used for passing messages. Fixed in Qt 5.6 // See https://bugreports.qt.io/browse/QTBUG-49521 //profile: desktop.browserProfile diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 561bd722f2..29f982887c 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -71,6 +71,45 @@ OriginalDesktop.Desktop { }); } + // Accept a download through the webview + property bool webViewProfileSetup: false + property string currentUrl: "" + property string adaptedPath: "" + function initWebviewProfileHandlers(profile) { + console.log("the webview url in desktop is: " + currentUrl) + if (webViewProfileSetup) return; + webViewProfileSetup = true; + + profile.downloadRequested.connect(function(download){ + console.log("Download start: " + download.state) + if (!File.testUrl(currentUrl)) { + console.log("This file type is not accepted. Look for a zip file") + download.cancel() + return + } + adaptedPath = File.convertUrlToPath(currentUrl) + download.path = "C:/Users/elisa/Downloads/" + adaptedPath + console.log("Path where it should download: " + download.path) + download.accept() + console.log("Download accept: " + download.state) + if (download.state === WebEngineDownloadItem.DownloadInterrupted) { + console.log("Download? " + download.state) + console.log("download failed to complete") + } + }) + + profile.downloadFinished.connect(function(download){ + if (download.state === WebEngineDownloadItem.DownloadCompleted) { + console.log("Download Finished: " + download.state) + console.log("File object is: " + File) + File.runUnzip(download.path, currentUrl) + //download.cancel() + } else { + console.log("The download was corrupted, state: " + download.state) + } + }) + } + // Create or fetch a toolbar with the given name function getToolbar(name) { var result = toolbars[name]; diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index ea6f42a44a..937618e9aa 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -31,9 +31,15 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent // nothing for now } -void FileScriptingInterface::runUnzip(QString path) { - //downloadZip(path, importURL); +void FileScriptingInterface::runUnzip(QString path, QUrl url) { + qDebug() << "Url that was downloaded: " + url.toString(); qDebug() << "Path where download is saved: " + path; + unzipFile(path); +} + +bool FileScriptingInterface::testUrl(QUrl url) { + if (url.toString().contains(".zip")) return true; + return false; } QString FileScriptingInterface::getTempDir() { @@ -43,6 +49,14 @@ QString FileScriptingInterface::getTempDir() { // remember I must do something to delete this temp dir later } +QString FileScriptingInterface::convertUrlToPath(QUrl url) { + QString newUrl; + QString oldUrl = url.toString(); + newUrl = oldUrl.section("filename=", 1, 1); + qDebug() << "Filename should be: " + newUrl; + return newUrl; +} + void FileScriptingInterface::downloadZip(QString path, const QString link) { QUrl url = QUrl(link); auto request = ResourceManager::createResourceRequest(nullptr, url); @@ -54,15 +68,15 @@ void FileScriptingInterface::downloadZip(QString path, const QString link) { // clement's help :D void FileScriptingInterface::unzipFile(QString path) { - ResourceRequest* request = qobject_cast(sender()); - QUrl url = request->getUrl(); + //ResourceRequest* request = qobject_cast(sender()); + //QUrl url = request->getUrl(); - if (request->getResult() == ResourceRequest::Success) { + //if (request->getResult() == ResourceRequest::Success) { qDebug() << "Zip file was downloaded"; QDir dir(path); - QByteArray compressedFileContent = request->getData(); // <-- Downloaded file is in here - QBuffer buffer(&compressedFileContent); - buffer.open(QIODevice::ReadOnly); + //QByteArray compressedFileContent = request->getData(); // <-- Downloaded file is in here + //QBuffer buffer(&compressedFileContent); + //buffer.open(QIODevice::ReadOnly); //QString zipFileName = QFile::decodeName(compressedFileContent); QString dirName = dir.path(); @@ -104,10 +118,10 @@ void FileScriptingInterface::unzipFile(QString path) { }*/ - buffer.close(); - } else { - qDebug() << "Could not download the zip file"; - } + //buffer.close(); + //} else { + // qDebug() << "Could not download the zip file"; + //} } diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index 0e12d3aa47..6ba6044060 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -21,12 +21,16 @@ class FileScriptingInterface : public QObject { public: FileScriptingInterface(QObject* parent); + //void runUnzip(QString path, QString importURL); QString getTempDir(); + public slots: //void unzipFile(QString path); - void runUnzip(QString path); + bool testUrl(QUrl url); + QString convertUrlToPath(QUrl url); + void runUnzip(QString path, QUrl url); signals: From 49712060ed6e9cf9390365b9c551ab0f4792fdf6 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Thu, 21 Jul 2016 17:43:19 -0700 Subject: [PATCH 045/332] switching to the main site progress --- interface/resources/qml/Browser.qml | 1 + .../resources/qml/controls-uit/WebView.qml | 4 ---- .../src/FileScriptingInterface.cpp | 20 ++++++++++++------- scripts/system/examples.js | 4 ++-- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 79d045cf0f..f3bdab74f4 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -225,6 +225,7 @@ ScrollingWindow { Component.onCompleted: { desktop.initWebviewProfileHandlers(webview.profile) } + //profile: desktop.browserProfile } diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index 3ab1478750..5fa0f5f584 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -73,10 +73,6 @@ WebEngineView { console.log("my url in WebView: " + desktop.currentUrl) } - //onLinkHovered: { - // console.log(hoveredUrl); - //} - // This breaks the webchannel used for passing messages. Fixed in Qt 5.6 // See https://bugreports.qt.io/browse/QTBUG-49521 //profile: desktop.browserProfile diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 937618e9aa..ad4ae8bdc2 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -80,12 +80,18 @@ void FileScriptingInterface::unzipFile(QString path) { //QString zipFileName = QFile::decodeName(compressedFileContent); QString dirName = dir.path(); + QDir testPath("C:/Users/elisa/Downloads/banana.zip"); qDebug() << "Zip directory is stored at: " + dirName; - JlCompress::extractDir(&buffer, dirName); + QStringList list = JlCompress::extractDir(testPath.path(), "C:/Users/elisa/Downloads/test"); - QFileInfoList files = dir.entryInfoList(); - foreach (QFileInfo file, files) { - recursiveFileScan(file, &dirName); + qDebug() << list; + + //QFileInfoList files = dir.entryInfoList(); + QFileInfoList files = testPath.entryInfoList(); + foreach(QFileInfo file, files) { + qDebug() << "My file: " + file.fileName(); + recursiveFileScan(file, &dirName); + } @@ -126,15 +132,15 @@ void FileScriptingInterface::unzipFile(QString path) { } void FileScriptingInterface::recursiveFileScan(QFileInfo file, QString* dirName) { - if (!file.isDir()) { + /*if (!file.isDir()) { qDebug() << "Regular file logged: " + file.fileName(); return; - } + }*/ QFileInfoList files; if (file.fileName().contains(".zip")) { + qDebug() << "Extracting archive: " + file.fileName(); JlCompress::extractDir(file.fileName()); - qDebug() << "Extracting archive: " + file.fileName(); } files = file.dir().entryInfoList(); diff --git a/scripts/system/examples.js b/scripts/system/examples.js index f67dd407bb..c445f218bc 100644 --- a/scripts/system/examples.js +++ b/scripts/system/examples.js @@ -12,8 +12,8 @@ var toolIconUrl = Script.resolvePath("assets/images/tools/"); //var EXAMPLES_URL = "https://metaverse.highfidelity.com/examples"; -//var EXAMPLES_URL = "https://clara.io/view/c1c4d926-5648-4fd3-9673-6d9018ad4627"; -var EXAMPLES_URL = "http://s3.amazonaws.com/DreamingContent/test.html"; +var EXAMPLES_URL = "https://clara.io/view/c1c4d926-5648-4fd3-9673-6d9018ad4627"; +//var EXAMPLES_URL = "http://s3.amazonaws.com/DreamingContent/test.html"; //var EXAMPLES_URL = "https://hifi-content.s3.amazonaws.com/elisalj/test.html"; //var EXAMPLES_URL = "https://hifi-content.s3.amazonaws.com/elisalj/test_download.html"; From 615627e98138a82089385841e20d223447d12f2e Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Fri, 22 Jul 2016 15:33:58 -0700 Subject: [PATCH 046/332] Working download and unpackage from Clara.io --- interface/resources/qml/Browser.qml | 3 +++ interface/resources/qml/controls-uit/WebView.qml | 10 ++++++++++ interface/resources/qml/hifi/Desktop.qml | 14 ++++++++------ .../script-engine/src/FileScriptingInterface.cpp | 9 +++++---- scripts/system/examples.js | 3 ++- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index f3bdab74f4..833816972a 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -225,6 +225,9 @@ ScrollingWindow { Component.onCompleted: { desktop.initWebviewProfileHandlers(webview.profile) } + + + //profile: desktop.browserProfile diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index 5fa0f5f584..50ba2d92c3 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -25,6 +25,8 @@ WebEngineView { }); } + + // FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6 Timer { id: urlReplacementTimer @@ -68,9 +70,17 @@ WebEngineView { request.openIn(newWindow.webView); } + property var myScript: 'var element = $("a.download-file"); + element.removeClass("download-file"); + element.removeAttr("download _target");' + onLinkHovered: { desktop.currentUrl = hoveredUrl console.log("my url in WebView: " + desktop.currentUrl) + if (File.testUrl(desktop.currentUrl)) { + runJavaScript(myScript, function() {console.log("ran the JS"); }); + } + } // This breaks the webchannel used for passing messages. Fixed in Qt 5.6 diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 29f982887c..db501be031 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -19,7 +19,9 @@ OriginalDesktop.Desktop { scrollGestureEnabled: false // we don't need/want these onEntered: ApplicationCompositor.reticleOverDesktop = true onExited: ApplicationCompositor.reticleOverDesktop = false - acceptedButtons: Qt.NoButton + acceptedButtons: Qt.NoButtonMouseArea + + } // The tool window, one instance @@ -82,11 +84,11 @@ OriginalDesktop.Desktop { profile.downloadRequested.connect(function(download){ console.log("Download start: " + download.state) - if (!File.testUrl(currentUrl)) { - console.log("This file type is not accepted. Look for a zip file") - download.cancel() - return - } + //if (!File.testUrl(currentUrl)) { + //console.log("This file type is not accepted. Look for a zip file") + //download.cancel() + //return + //} adaptedPath = File.convertUrlToPath(currentUrl) download.path = "C:/Users/elisa/Downloads/" + adaptedPath console.log("Path where it should download: " + download.path) diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index ad4ae8bdc2..3a9cca64c4 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -38,7 +38,8 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url) { } bool FileScriptingInterface::testUrl(QUrl url) { - if (url.toString().contains(".zip")) return true; + if (url.toString().contains(".zip") && url.toString().contains("fbx")) return true; + qDebug() << "This model is not a .fbx packaged in a .zip. Please try with another model."; return false; } @@ -80,14 +81,14 @@ void FileScriptingInterface::unzipFile(QString path) { //QString zipFileName = QFile::decodeName(compressedFileContent); QString dirName = dir.path(); - QDir testPath("C:/Users/elisa/Downloads/banana.zip"); + //QDir testPath("C:/Users/elisa/Downloads/banana.zip"); qDebug() << "Zip directory is stored at: " + dirName; - QStringList list = JlCompress::extractDir(testPath.path(), "C:/Users/elisa/Downloads/test"); + QStringList list = JlCompress::extractDir(dirName, "C:/Users/elisa/Downloads/test"); qDebug() << list; //QFileInfoList files = dir.entryInfoList(); - QFileInfoList files = testPath.entryInfoList(); + QFileInfoList files = dir.entryInfoList(); foreach(QFileInfo file, files) { qDebug() << "My file: " + file.fileName(); recursiveFileScan(file, &dirName); diff --git a/scripts/system/examples.js b/scripts/system/examples.js index c445f218bc..6c01fd58af 100644 --- a/scripts/system/examples.js +++ b/scripts/system/examples.js @@ -12,7 +12,8 @@ var toolIconUrl = Script.resolvePath("assets/images/tools/"); //var EXAMPLES_URL = "https://metaverse.highfidelity.com/examples"; -var EXAMPLES_URL = "https://clara.io/view/c1c4d926-5648-4fd3-9673-6d9018ad4627"; +//var EXAMPLES_URL = "https://clara.io/view/c1c4d926-5648-4fd3-9673-6d9018ad4627"; +var EXAMPLES_URL = "https://clara.io/library"; //var EXAMPLES_URL = "http://s3.amazonaws.com/DreamingContent/test.html"; //var EXAMPLES_URL = "https://hifi-content.s3.amazonaws.com/elisalj/test.html"; //var EXAMPLES_URL = "https://hifi-content.s3.amazonaws.com/elisalj/test_download.html"; From a94a3047007e54a1ee5f24cf01e851d6a952efdb Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Mon, 25 Jul 2016 16:15:43 -0700 Subject: [PATCH 047/332] Downloads from Clara.io and uploads to asset server --- interface/resources/qml/AssetServer.qml | 1 + interface/src/Application.cpp | 6 +- .../src/FileScriptingInterface.cpp | 82 ++++++------------- .../src/FileScriptingInterface.h | 3 +- 4 files changed, 31 insertions(+), 61 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 8d971e48d3..eca3d11721 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -320,6 +320,7 @@ ScrollingWindow { id: timer } function uploadClicked(fileUrl) { + console.log("Upload clicked url: " + fileUrl); if (uploadOpen) { return; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ccffceede0..7cf6410fd8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1543,7 +1543,10 @@ void Application::initializeUi() { rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); rootContext->setContextProperty("Controller", DependencyManager::get().data()); rootContext->setContextProperty("Entities", DependencyManager::get().data()); - rootContext->setContextProperty("File", new FileScriptingInterface(engine)); + //rootContext->setContextProperty("File", new FileScriptingInterface(engine)); + FileScriptingInterface* fileDownload = new FileScriptingInterface(engine); + rootContext->setContextProperty("File", fileDownload); + connect(fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::toggleAssetServerWidget); rootContext->setContextProperty("MyAvatar", getMyAvatar()); rootContext->setContextProperty("Messages", DependencyManager::get().data()); rootContext->setContextProperty("Recording", DependencyManager::get().data()); @@ -5035,6 +5038,7 @@ void Application::toggleAssetServerWidget(QString filePath) { auto startUpload = [=](QQmlContext* context, QObject* newObject){ if (!filePath.isEmpty()) { + qDebug() << "file in toggle: " + filePath; emit uploadRequest(filePath); } }; diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 3a9cca64c4..ac72b99c9f 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -34,7 +34,16 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent void FileScriptingInterface::runUnzip(QString path, QUrl url) { qDebug() << "Url that was downloaded: " + url.toString(); qDebug() << "Path where download is saved: " + path; - unzipFile(path); + QString file = unzipFile(path); + if (file != "") { + qDebug() << "file to upload: " + file; + QUrl url = QUrl::fromLocalFile(file); + qDebug() << "url from local file: " + url.toString(); + emit unzipSuccess(url.toString()); + //Application::toggleAssetServerWidget(file); + } else { + qDebug() << "unzip failed"; + } } bool FileScriptingInterface::testUrl(QUrl url) { @@ -68,67 +77,22 @@ void FileScriptingInterface::downloadZip(QString path, const QString link) { } // clement's help :D -void FileScriptingInterface::unzipFile(QString path) { - //ResourceRequest* request = qobject_cast(sender()); - //QUrl url = request->getUrl(); +QString FileScriptingInterface::unzipFile(QString path) { - //if (request->getResult() == ResourceRequest::Success) { - qDebug() << "Zip file was downloaded"; - QDir dir(path); - //QByteArray compressedFileContent = request->getData(); // <-- Downloaded file is in here - //QBuffer buffer(&compressedFileContent); - //buffer.open(QIODevice::ReadOnly); + qDebug() << "Zip file was downloaded"; + QDir dir(path); + QString dirName = dir.path(); + qDebug() << "Zip directory is stored at: " + dirName; + QStringList list = JlCompress::extractDir(dirName, "C:/Users/elisa/Downloads/test"); - //QString zipFileName = QFile::decodeName(compressedFileContent); - QString dirName = dir.path(); - //QDir testPath("C:/Users/elisa/Downloads/banana.zip"); - qDebug() << "Zip directory is stored at: " + dirName; - QStringList list = JlCompress::extractDir(dirName, "C:/Users/elisa/Downloads/test"); + qDebug() << list; - qDebug() << list; - - //QFileInfoList files = dir.entryInfoList(); - QFileInfoList files = dir.entryInfoList(); - foreach(QFileInfo file, files) { - qDebug() << "My file: " + file.fileName(); - recursiveFileScan(file, &dirName); - - } - - - /*foreach (QFileInfo file, files) { - if (file.isDir()) { - if (file.fileName().contains(".zip")) { - qDebug() << "Zip file expanded: " + file.fileName(); - } - - qDebug() << "Regular file logged: " + file.fileName(); - } - }*/ - - - - - //QFile file = - //need to convert this byte array to a file - /*QuaZip zip(zipFileName); - - if (zip.open(QuaZip::mdUnzip)) { - qDebug() << "Opened"; - - for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile()) { - // do something - qDebug() << zip.getCurrentFileName(); - } - if (zip.getZipError() == UNZ_OK) { - // ok, there was no error - }*/ - - - //buffer.close(); - //} else { - // qDebug() << "Could not download the zip file"; - //} + if (!list.isEmpty()) { + return list.front(); + } else { + qDebug() << "Extraction failed"; + return ""; + } } diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index 6ba6044060..d90bf1ed2b 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -33,10 +33,11 @@ public slots: void runUnzip(QString path, QUrl url); signals: + void unzipSuccess(QString url); private: //void downloadZip(); - void unzipFile(QString path); + QString unzipFile(QString path); void recursiveFileScan(QFileInfo file, QString* dirName); void downloadZip(QString path, const QString link); From b1b2ea48b245d26e38a7cda88340f8718c51fd1e Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Tue, 26 Jul 2016 16:33:06 -0700 Subject: [PATCH 048/332] Made code more QA; extra browser and download widget close upon download --- interface/resources/qml/AssetServer.qml | 2 +- interface/resources/qml/Browser.qml | 7 -- .../resources/qml/controls-uit/WebView.qml | 54 ++++++--- interface/resources/qml/hifi/Desktop.qml | 69 +++++------ interface/src/Application.cpp | 40 ++----- interface/src/Application.h | 2 - .../scripting/WindowScriptingInterface.cpp | 14 --- .../src/scripting/WindowScriptingInterface.h | 1 - .../src/FileScriptingInterface.cpp | 109 +++++++++--------- .../src/FileScriptingInterface.h | 22 ++-- libraries/script-engine/src/ScriptEngine.cpp | 1 - libraries/ui/src/OffscreenUi.cpp | 3 - scripts/system/edit.js | 16 --- 13 files changed, 144 insertions(+), 196 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index eca3d11721..0f55e77a66 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -320,7 +320,7 @@ ScrollingWindow { id: timer } function uploadClicked(fileUrl) { - console.log("Upload clicked url: " + fileUrl); + console.log("Upload clicked url: " + fileUrl); if (uploadOpen) { return; } diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 833816972a..2be0ca176d 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -140,7 +140,6 @@ ScrollingWindow { } } -<<<<<<< ab02d16eb7656ea0a37785a398716fad62dff045 Rectangle { id:permissionsContainer visible:false @@ -197,8 +196,6 @@ ScrollingWindow { } } } -======= ->>>>>>> Zip download works WebEngineView { id: webview @@ -221,15 +218,11 @@ ScrollingWindow { onIconChanged: { console.log("New icon: " + icon) } - Component.onCompleted: { desktop.initWebviewProfileHandlers(webview.profile) } - - - //profile: desktop.browserProfile } diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index 50ba2d92c3..fdc1452d9b 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -25,7 +25,7 @@ WebEngineView { }); } - + // FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6 Timer { @@ -51,7 +51,7 @@ WebEngineView { onLoadingChanged: { // Required to support clicking on "hifi://" links - console.log("loading change requested url"); + console.log("loading change requested url"); if (WebEngineView.LoadStartedStatus == loadRequest.status) { var url = loadRequest.url.toString(); if (urlHandler.canHandleUrl(url)) { @@ -62,26 +62,44 @@ WebEngineView { } } - onNewViewRequested: { - console.log("new view requested url"); - //console.log("new view requested url" + request.url.toString()); - var component = Qt.createComponent("../Browser.qml"); - var newWindow = component.createObject(desktop); - request.openIn(newWindow.webView); + Timer { + id: zipTimer + running: false + repeat: false + interval: 1000 + property var handler; + onTriggered: handler(); } - property var myScript: 'var element = $("a.download-file"); - element.removeClass("download-file"); - element.removeAttr("download _target");' + property var autoCancel: 'var element = $("a.btn.cancel"); + element.click();' - onLinkHovered: { - desktop.currentUrl = hoveredUrl - console.log("my url in WebView: " + desktop.currentUrl) - if (File.testUrl(desktop.currentUrl)) { - runJavaScript(myScript, function() {console.log("ran the JS"); }); - } + onNewViewRequested: { + console.log("new view requested url"); + var component = Qt.createComponent("../Browser.qml"); + var newWindow = component.createObject(desktop); + request.openIn(newWindow.webView); + if (File.testUrl(desktop.currentUrl)) { + zipTimer.handler = function() { + newWindow.destroy(); + runJavaScript(autoCancel); + } + zipTimer.start(); + } + } - } + property var simpleDownload: 'var element = $("a.download-file"); + element.removeClass("download-file"); + element.removeAttr("download");' + + onLinkHovered: { + desktop.currentUrl = hoveredUrl + console.log("my url in WebView: " + desktop.currentUrl) + if (File.testUrl(desktop.currentUrl)) { + runJavaScript(simpleDownload, function(){console.log("ran the JS");}); + } + + } // This breaks the webchannel used for passing messages. Fixed in Qt 5.6 // See https://bugreports.qt.io/browse/QTBUG-49521 diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index db501be031..9ab9324b74 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -19,7 +19,7 @@ OriginalDesktop.Desktop { scrollGestureEnabled: false // we don't need/want these onEntered: ApplicationCompositor.reticleOverDesktop = true onExited: ApplicationCompositor.reticleOverDesktop = false - acceptedButtons: Qt.NoButtonMouseArea + acceptedButtons: Qt.NoButton } @@ -73,44 +73,39 @@ OriginalDesktop.Desktop { }); } - // Accept a download through the webview - property bool webViewProfileSetup: false - property string currentUrl: "" - property string adaptedPath: "" - function initWebviewProfileHandlers(profile) { - console.log("the webview url in desktop is: " + currentUrl) - if (webViewProfileSetup) return; - webViewProfileSetup = true; + // Accept a download through the webview + property bool webViewProfileSetup: false + property string currentUrl: "" + property string adaptedPath: "" - profile.downloadRequested.connect(function(download){ - console.log("Download start: " + download.state) - //if (!File.testUrl(currentUrl)) { - //console.log("This file type is not accepted. Look for a zip file") - //download.cancel() - //return - //} - adaptedPath = File.convertUrlToPath(currentUrl) - download.path = "C:/Users/elisa/Downloads/" + adaptedPath - console.log("Path where it should download: " + download.path) - download.accept() - console.log("Download accept: " + download.state) - if (download.state === WebEngineDownloadItem.DownloadInterrupted) { - console.log("Download? " + download.state) - console.log("download failed to complete") - } - }) + function initWebviewProfileHandlers(profile) { + console.log("the webview url in desktop is: " + currentUrl); + if (webViewProfileSetup) return; + webViewProfileSetup = true; - profile.downloadFinished.connect(function(download){ - if (download.state === WebEngineDownloadItem.DownloadCompleted) { - console.log("Download Finished: " + download.state) - console.log("File object is: " + File) - File.runUnzip(download.path, currentUrl) - //download.cancel() - } else { - console.log("The download was corrupted, state: " + download.state) - } - }) - } + profile.downloadRequested.connect(function(download){ + console.log("Download start: " + download.state); + adaptedPath = File.convertUrlToPath(currentUrl); + download.path = "C:/Users/elisa/Downloads/" + adaptedPath; + console.log("Path where it should download: " + download.path); + download.accept(); + console.log("Download accept: " + download.state); + if (download.state === WebEngineDownloadItem.DownloadInterrupted) { + console.log("Download? " + download.state); + console.log("download failed to complete"); + } + }) + + profile.downloadFinished.connect(function(download){ + if (download.state === WebEngineDownloadItem.DownloadCompleted) { + console.log("Download Finished: " + download.state); + console.log("File object is: " + File); + File.runUnzip(download.path, currentUrl); + } else { + console.log("The download was corrupted, state: " + download.state); + } + }) + } // Create or fetch a toolbar with the given name function getToolbar(name) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7cf6410fd8..66fb6ed6b6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -355,7 +355,6 @@ public: if (message->message == WM_COPYDATA) { COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)(message->lParam); - qDebug() << "os windows url for some reason"; QUrl url = QUrl((const char*)(pcds->lpData)); if (url.isValid() && url.scheme() == HIFI_URL_SCHEME) { DependencyManager::get()->handleLookupString(url.toString()); @@ -1543,10 +1542,9 @@ void Application::initializeUi() { rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); rootContext->setContextProperty("Controller", DependencyManager::get().data()); rootContext->setContextProperty("Entities", DependencyManager::get().data()); - //rootContext->setContextProperty("File", new FileScriptingInterface(engine)); - FileScriptingInterface* fileDownload = new FileScriptingInterface(engine); - rootContext->setContextProperty("File", fileDownload); - connect(fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::toggleAssetServerWidget); + FileScriptingInterface* fileDownload = new FileScriptingInterface(engine); + rootContext->setContextProperty("File", fileDownload); + connect(fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::toggleAssetServerWidget); rootContext->setContextProperty("MyAvatar", getMyAvatar()); rootContext->setContextProperty("Messages", DependencyManager::get().data()); rootContext->setContextProperty("Recording", DependencyManager::get().data()); @@ -2020,14 +2018,6 @@ bool Application::importSVOFromURL(const QString& urlString) { return true; } -// attempt to start ZIP download project -bool Application::importZIPFromURL(const QString& urlString) { - qDebug() << "zip import request has been emitted"; - emit zipImportRequested(urlString); - return true; -} -// end attempt - bool Application::event(QEvent* event) { if (!Menu::getInstance()) { @@ -2147,26 +2137,21 @@ bool Application::event(QEvent* event) { // handle custom URL if (event->type() == QEvent::FileOpen) { - qDebug() << "we have received one url!: "; QFileOpenEvent* fileEvent = static_cast(event); QUrl url = fileEvent->url(); if (!url.isEmpty()) { QString urlString = url.toString(); - qDebug() << "we got a url!: " + urlString; + if (canAcceptURL(urlString)) { - qDebug() << "we got an ACCEPTED url!: " + urlString; + return acceptURL(urlString); } } return false; } - if (event->type() == QEvent::None) { - qDebug() << "this url just didn't click"; - } - if (HFActionEvent::types().contains(event->type())) { _controllerScriptingInterface->handleMetaEvent(static_cast(event)); } @@ -3190,7 +3175,6 @@ void Application::saveSettings() const { } bool Application::importEntities(const QString& urlOrFilename) { - qDebug() << "import entities url"; bool success = false; _entityClipboard->withWriteLock([&] { _entityClipboard->eraseAllOctreeElements(); @@ -4800,7 +4784,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri } bool Application::canAcceptURL(const QString& urlString) const { - qDebug() << "stepping through 'canAcceptURL'"; QUrl url(urlString); if (urlString.startsWith(HIFI_URL_SCHEME)) { return true; @@ -4817,7 +4800,6 @@ bool Application::canAcceptURL(const QString& urlString) const { } bool Application::acceptURL(const QString& urlString, bool defaultUpload) { - qDebug() << "stepping through 'acceptURL'"; if (urlString.startsWith(HIFI_URL_SCHEME)) { // this is a hifi URL - have the AddressManager handle it QMetaObject::invokeMethod(DependencyManager::get().data(), "handleLookupString", @@ -4847,7 +4829,6 @@ void Application::setSessionUUID(const QUuid& sessionUUID) const { } bool Application::askToSetAvatarUrl(const QString& url) { - qDebug() << "setting avatar url"; QUrl realUrl(url); if (realUrl.isLocalFile()) { OffscreenUi::warning("", "You can not use local files for avatar components."); @@ -4904,7 +4885,6 @@ bool Application::askToSetAvatarUrl(const QString& url) { bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { - qDebug() << "setting script url"; QMessageBox::StandardButton reply; QString shortName = scriptFilenameOrURL; @@ -4929,7 +4909,6 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { } bool Application::askToWearAvatarAttachmentUrl(const QString& url) { - qDebug() << "setting avatar attachment url"; QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); @@ -5011,7 +4990,6 @@ bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) } void Application::toggleRunningScriptsWidget() const { - qDebug() << "toggle running scripts url"; static const QUrl url("hifi/dialogs/RunningScripts.qml"); DependencyManager::get()->show(url, "RunningScripts"); //if (_runningScriptsWidget->isVisible()) { @@ -5029,16 +5007,16 @@ void Application::toggleRunningScriptsWidget() const { } void Application::toggleAssetServerWidget(QString filePath) { - qDebug() << "toggle asset before if"; + qDebug() << "toggle asset before if: " + filePath; if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { return; } - qDebug() << "toggle asset after if"; + qDebug() << "toggle asset after if"; static const QUrl url { "AssetServer.qml" }; auto startUpload = [=](QQmlContext* context, QObject* newObject){ if (!filePath.isEmpty()) { - qDebug() << "file in toggle: " + filePath; + qDebug() << "file in toggle: " + filePath; emit uploadRequest(filePath); } }; @@ -5051,7 +5029,6 @@ void Application::packageModel() { } void Application::openUrl(const QUrl& url) const { - qDebug() << "open url"; if (!url.isEmpty()) { if (url.scheme() == HIFI_URL_SCHEME) { DependencyManager::get()->handleLookupString(url.toString()); @@ -5082,7 +5059,6 @@ void Application::setPreviousScriptLocation(const QString& location) { } void Application::loadScriptURLDialog() const { - qDebug() << "load script url dialog"; auto newScript = OffscreenUi::getText(nullptr, "Open and Run Script", "Script URL"); if (!newScript.isEmpty()) { DependencyManager::get()->loadScript(newScript); diff --git a/interface/src/Application.h b/interface/src/Application.h index d8f02f41b7..0af65f665f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -250,7 +250,6 @@ public: signals: void svoImportRequested(const QString& url); - void zipImportRequested(const QString& url); void fullAvatarURLChanged(const QString& newValue, const QString& modelName); @@ -386,7 +385,6 @@ private: bool importJSONFromURL(const QString& urlString); bool importSVOFromURL(const QString& urlString); - bool importZIPFromURL(const QString& urlString); bool nearbyEntitiesAreReadyForPhysics(); int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 2ea562143e..b165cda135 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -59,20 +59,6 @@ WindowScriptingInterface::WindowScriptingInterface() { OffscreenUi::warning("Import SVO Error", "You need to be running edit.js to import entities."); } }); - // attempt to start ZIP download project - connect(qApp, &Application::zipImportRequested, [this](const QString& urlString) { - qDebug() << "zip import has been requested"; - static const QMetaMethod zipImportRequestedSignal = - QMetaMethod::fromSignal(&WindowScriptingInterface::zipImportRequested); - - if (isSignalConnected(zipImportRequestedSignal)) { - QUrl url(urlString); - emit zipImportRequested(url.url()); - } else { - OffscreenUi::warning("Import ZIP Error", "You need to be running edit.js to import entities."); - } - // end attempt - }); } WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height) { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index b33da4ef4d..9d73111333 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -58,7 +58,6 @@ public slots: signals: void domainChanged(const QString& domainHostname); void svoImportRequested(const QString& url); - void zipImportRequested(const QString& url); // zip project void domainConnectionRefused(const QString& reasonMessage, int reasonCode); void snapshotTaken(const QString& path); diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index ac72b99c9f..4126b5fdad 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -28,94 +28,97 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent) { - // nothing for now + // nothing for now } void FileScriptingInterface::runUnzip(QString path, QUrl url) { - qDebug() << "Url that was downloaded: " + url.toString(); - qDebug() << "Path where download is saved: " + path; - QString file = unzipFile(path); - if (file != "") { - qDebug() << "file to upload: " + file; - QUrl url = QUrl::fromLocalFile(file); - qDebug() << "url from local file: " + url.toString(); - emit unzipSuccess(url.toString()); - //Application::toggleAssetServerWidget(file); - } else { - qDebug() << "unzip failed"; - } + qDebug() << "Url that was downloaded: " + url.toString(); + qDebug() << "Path where download is saved: " + path; + QString file = unzipFile(path); + if (file != "") { + qDebug() << "file to upload: " + file; + QUrl url = QUrl::fromLocalFile(file); + qDebug() << "url from local file: " + url.toString(); + emit unzipSuccess(url.toString()); + } else { + qDebug() << "unzip failed"; + } } bool FileScriptingInterface::testUrl(QUrl url) { - if (url.toString().contains(".zip") && url.toString().contains("fbx")) return true; - qDebug() << "This model is not a .fbx packaged in a .zip. Please try with another model."; - return false; + if (url.toString().contains(".zip") && url.toString().contains("fbx")) return true; + qDebug() << "This model is not a .fbx packaged in a .zip. Please try with another model."; + return false; } +// this function is not in use QString FileScriptingInterface::getTempDir() { - QTemporaryDir dir; - dir.setAutoRemove(false); - return dir.path(); - // remember I must do something to delete this temp dir later + QTemporaryDir dir; + dir.setAutoRemove(false); + return dir.path(); + // do something to delete this temp dir later } QString FileScriptingInterface::convertUrlToPath(QUrl url) { - QString newUrl; - QString oldUrl = url.toString(); - newUrl = oldUrl.section("filename=", 1, 1); - qDebug() << "Filename should be: " + newUrl; - return newUrl; + QString newUrl; + QString oldUrl = url.toString(); + newUrl = oldUrl.section("filename=", 1, 1); + qDebug() << "Filename should be: " + newUrl; + return newUrl; } +// this function is not in use void FileScriptingInterface::downloadZip(QString path, const QString link) { - QUrl url = QUrl(link); - auto request = ResourceManager::createResourceRequest(nullptr, url); - connect(request, &ResourceRequest::finished, this, [this, path]{ - unzipFile(path); - }); + QUrl url = QUrl(link); + auto request = ResourceManager::createResourceRequest(nullptr, url); + connect(request, &ResourceRequest::finished, this, [this, path]{ + unzipFile(path); + }); request->send(); } -// clement's help :D + QString FileScriptingInterface::unzipFile(QString path) { - qDebug() << "Zip file was downloaded"; - QDir dir(path); + QDir dir(path); QString dirName = dir.path(); - qDebug() << "Zip directory is stored at: " + dirName; + qDebug() << "Zip directory is stored at: " + dirName; + QString target = path.section("/", -1) + "/model_repo"; + qDebug() << "Target path: " + target; QStringList list = JlCompress::extractDir(dirName, "C:/Users/elisa/Downloads/test"); - qDebug() << list; + qDebug() << list; - if (!list.isEmpty()) { - return list.front(); - } else { - qDebug() << "Extraction failed"; - return ""; - } + if (!list.isEmpty()) { + return list.front(); + } else { + qDebug() << "Extraction failed"; + return ""; + } } +// this function is not in use void FileScriptingInterface::recursiveFileScan(QFileInfo file, QString* dirName) { - /*if (!file.isDir()) { - qDebug() << "Regular file logged: " + file.fileName(); - return; - }*/ + /*if (!file.isDir()) { + qDebug() << "Regular file logged: " + file.fileName(); + return; + }*/ QFileInfoList files; - if (file.fileName().contains(".zip")) { - qDebug() << "Extracting archive: " + file.fileName(); - JlCompress::extractDir(file.fileName()); + if (file.fileName().contains(".zip")) { + qDebug() << "Extracting archive: " + file.fileName(); + JlCompress::extractDir(file.fileName()); } files = file.dir().entryInfoList(); - /*if (files.empty()) { - files = JlCompress::getFileList(file.fileName()); - }*/ + /*if (files.empty()) { + files = JlCompress::getFileList(file.fileName()); + }*/ - foreach (QFileInfo file, files) { + foreach (QFileInfo file, files) { qDebug() << "Looking into file: " + file.fileName(); recursiveFileScan(file, dirName); - } + } return; } diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index d90bf1ed2b..df6b91c678 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -22,24 +22,24 @@ class FileScriptingInterface : public QObject { public: FileScriptingInterface(QObject* parent); - //void runUnzip(QString path, QString importURL); - QString getTempDir(); + //void runUnzip(QString path, QString importURL); + QString getTempDir(); public slots: - //void unzipFile(QString path); - bool testUrl(QUrl url); - QString convertUrlToPath(QUrl url); - void runUnzip(QString path, QUrl url); + //void unzipFile(QString path); + bool testUrl(QUrl url); + QString convertUrlToPath(QUrl url); + void runUnzip(QString path, QUrl url); signals: - void unzipSuccess(QString url); + void unzipSuccess(QString url); private: - //void downloadZip(); - QString unzipFile(QString path); - void recursiveFileScan(QFileInfo file, QString* dirName); - void downloadZip(QString path, const QString link); + //void downloadZip(); + QString unzipFile(QString path); + void recursiveFileScan(QFileInfo file, QString* dirName); + void downloadZip(QString path, const QString link); }; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index fe7e1c4036..dd3fcad749 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -503,7 +503,6 @@ void ScriptEngine::init() { registerGlobalObject("Uuid", &_uuidLibrary); registerGlobalObject("Messages", DependencyManager::get().data()); - // unzip project registerGlobalObject("File", new FileScriptingInterface(this)); qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 82a9186f92..06ef456006 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -78,20 +78,17 @@ class UrlHandler : public QObject { Q_OBJECT public: Q_INVOKABLE bool canHandleUrl(const QString& url) { - qDebug() << Q_FUNC_INFO; static auto handler = dynamic_cast(qApp); return handler->canAcceptURL(url); } Q_INVOKABLE bool handleUrl(const QString& url) { - qDebug() << Q_FUNC_INFO; static auto handler = dynamic_cast(qApp); return handler->acceptURL(url); } // FIXME hack for authentication, remove when we migrate to Qt 5.6 Q_INVOKABLE QString fixupUrl(const QString& originalUrl) { - qDebug() << Q_FUNC_INFO; return fixupHifiUrl(originalUrl); } }; diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 1284202c6d..25c25a9a7e 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1205,22 +1205,6 @@ function importSVO(importURL) { } Window.svoImportRequested.connect(importSVO); -// attempt to start ZIP download project, GET RID OF THIS -function importZIP(importURL) { - print("Import ZIP requested: " + importURL); - /*if (!Entities.canAdjustLocks()) { - Window.alert(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); - return; - }*/ - var path = File.getTempDir(); - print("Temporary path to zip: " + path); - File.runUnzip(path, importURL); - - -} -Window.zipImportRequested.connect(importZIP); -//end attempt - Menu.menuItemEvent.connect(handeMenuEvent); Controller.keyPressEvent.connect(function (event) { From a9a8710689022e1e36f1d07bed282a885505828a Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Tue, 26 Jul 2016 17:47:54 -0700 Subject: [PATCH 049/332] Switched saving download/upload to temporary directory --- interface/resources/qml/controls-uit/WebView.qml | 2 +- interface/resources/qml/hifi/Desktop.qml | 7 ++++--- .../script-engine/src/FileScriptingInterface.cpp | 14 ++++++++------ .../script-engine/src/FileScriptingInterface.h | 10 +++------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index fdc1452d9b..bb08e5e389 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -66,7 +66,7 @@ WebEngineView { id: zipTimer running: false repeat: false - interval: 1000 + interval: 1500 property var handler; onTriggered: handler(); } diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 9ab9324b74..ab1fffdcf5 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -77,6 +77,7 @@ OriginalDesktop.Desktop { property bool webViewProfileSetup: false property string currentUrl: "" property string adaptedPath: "" + property string tempDir: "" function initWebviewProfileHandlers(profile) { console.log("the webview url in desktop is: " + currentUrl); @@ -86,12 +87,12 @@ OriginalDesktop.Desktop { profile.downloadRequested.connect(function(download){ console.log("Download start: " + download.state); adaptedPath = File.convertUrlToPath(currentUrl); - download.path = "C:/Users/elisa/Downloads/" + adaptedPath; + tempDir = File.getTempDir(); + download.path = tempDir + "/" + adaptedPath; console.log("Path where it should download: " + download.path); download.accept(); console.log("Download accept: " + download.state); if (download.state === WebEngineDownloadItem.DownloadInterrupted) { - console.log("Download? " + download.state); console.log("download failed to complete"); } }) @@ -100,7 +101,7 @@ OriginalDesktop.Desktop { if (download.state === WebEngineDownloadItem.DownloadCompleted) { console.log("Download Finished: " + download.state); console.log("File object is: " + File); - File.runUnzip(download.path, currentUrl); + File.runUnzip(download.path, tempDir, currentUrl); } else { console.log("The download was corrupted, state: " + download.state); } diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 4126b5fdad..94d8d1b424 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -31,10 +31,10 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent // nothing for now } -void FileScriptingInterface::runUnzip(QString path, QUrl url) { +void FileScriptingInterface::runUnzip(QString path, QString tempDir, QUrl url) { qDebug() << "Url that was downloaded: " + url.toString(); qDebug() << "Path where download is saved: " + path; - QString file = unzipFile(path); + QString file = unzipFile(path, tempDir); if (file != "") { qDebug() << "file to upload: " + file; QUrl url = QUrl::fromLocalFile(file); @@ -43,6 +43,8 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url) { } else { qDebug() << "unzip failed"; } + qDebug() << "Removing temporary directory at: " + tempDir; + QDir(tempDir).removeRecursively(); } bool FileScriptingInterface::testUrl(QUrl url) { @@ -72,20 +74,20 @@ void FileScriptingInterface::downloadZip(QString path, const QString link) { QUrl url = QUrl(link); auto request = ResourceManager::createResourceRequest(nullptr, url); connect(request, &ResourceRequest::finished, this, [this, path]{ - unzipFile(path); + unzipFile(path, ""); // so intellisense isn't mad }); request->send(); } -QString FileScriptingInterface::unzipFile(QString path) { +QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { QDir dir(path); QString dirName = dir.path(); qDebug() << "Zip directory is stored at: " + dirName; - QString target = path.section("/", -1) + "/model_repo"; + QString target = tempDir + "/model_repo"; qDebug() << "Target path: " + target; - QStringList list = JlCompress::extractDir(dirName, "C:/Users/elisa/Downloads/test"); + QStringList list = JlCompress::extractDir(dirName, target); qDebug() << list; diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index df6b91c678..2658622644 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -22,22 +22,18 @@ class FileScriptingInterface : public QObject { public: FileScriptingInterface(QObject* parent); - //void runUnzip(QString path, QString importURL); - QString getTempDir(); - public slots: - //void unzipFile(QString path); bool testUrl(QUrl url); QString convertUrlToPath(QUrl url); - void runUnzip(QString path, QUrl url); + void runUnzip(QString path, QString tempDir, QUrl url); + QString getTempDir(); signals: void unzipSuccess(QString url); private: - //void downloadZip(); - QString unzipFile(QString path); + QString unzipFile(QString path, QString tempDir); void recursiveFileScan(QFileInfo file, QString* dirName); void downloadZip(QString path, const QString link); From cd7706e80b2831ed0f4bd17155d74ed1439f994e Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Thu, 28 Jul 2016 13:56:38 -0700 Subject: [PATCH 050/332] Release now has quazip features --- cmake/externals/quazip/CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index bf51a52fc5..d8ab492be5 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -37,11 +37,14 @@ set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${${EXTERNAL_NAME_UPPER}_INCLUDE_DIR} CA set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/lib CACHE FILEPATH "Location of QuaZip DLL") if (APPLE) - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5d.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5d.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") elseif (WIN32) - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/quazip5d.lib CACHE FILEPATH "Location of QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/quazip5.lib CACHE FILEPATH "Location of QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/quazip5d.lib CACHE FILEPATH "Location of QuaZip release library") else () - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5d.so CACHE FILEPATH "Location of QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5d.so CACHE FILEPATH "Location of QuaZip release library") endif () include(SelectLibraryConfigurations) From d63d0ba870a6ffac8be96b6458b7457bfdbbdaef Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Wed, 3 Aug 2016 14:16:39 -0700 Subject: [PATCH 051/332] In process of creating combobox for marketplace with qml --- interface/resources/qml/QmlWindow.qml | 10 ++- .../resources/qml/controls-uit/ComboBox.qml | 1 + .../resources/qml/controls-uit/Label.qml | 1 + .../qml/controls-uit/MarketplaceComboBox.qml | 43 ++++++++++ scripts/system/marketplace - Elisa.js | 82 +++++++++++++++++++ scripts/system/marketplace.js | 1 + 6 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 interface/resources/qml/controls-uit/MarketplaceComboBox.qml create mode 100644 scripts/system/marketplace - Elisa.js diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml index 7be747a3ad..47857ac7c1 100644 --- a/interface/resources/qml/QmlWindow.qml +++ b/interface/resources/qml/QmlWindow.qml @@ -1,13 +1,16 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 +import QtQuick 2.5 +import QtQuick.Controls 1.4 import QtWebChannel 1.0 +import QtWebEngine 1.1 import QtWebSockets 1.0 import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel import "windows" as Windows import "controls" +import "controls-uit" as Controls import "styles" +import "styles-uit" Windows.Window { id: root @@ -23,6 +26,8 @@ Windows.Window { property var eventBridge; property var component; property var dynamicContent; + + onSourceChanged: { if (dynamicContent) { dynamicContent.destroy(); @@ -71,5 +76,6 @@ Windows.Window { Item { id: contentHolder anchors.fill: parent + } } diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index eb0c08624b..fa100a965f 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -18,6 +18,7 @@ import "." as VrControls FocusScope { id: root + HifiConstants { id: hifi } property alias model: comboBox.model; property alias comboBox: comboBox diff --git a/interface/resources/qml/controls-uit/Label.qml b/interface/resources/qml/controls-uit/Label.qml index 3b4d757154..330d74fa14 100644 --- a/interface/resources/qml/controls-uit/Label.qml +++ b/interface/resources/qml/controls-uit/Label.qml @@ -13,6 +13,7 @@ import QtQuick 2.5 import "../styles-uit" RalewaySemiBold { + HifiConstants { id: hifi } property int colorScheme: hifi.colorSchemes.light size: hifi.fontSizes.inputLabel diff --git a/interface/resources/qml/controls-uit/MarketplaceComboBox.qml b/interface/resources/qml/controls-uit/MarketplaceComboBox.qml new file mode 100644 index 0000000000..986131f3ec --- /dev/null +++ b/interface/resources/qml/controls-uit/MarketplaceComboBox.qml @@ -0,0 +1,43 @@ +// +// MarketplaceComboBox.qml +// +// Created by Elisa Lupin-Jimenez on 3 Aug 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtWebChannel 1.0 +import QtWebEngine 1.1 +import QtWebSockets 1.0 +import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel + +import "controls" +import "controls-uit" as Controls +import "styles" +import "styles-uit" + +// just temporary + Controls.WebView { + id: webview + anchors.fill: parent + url: "about:blank" + focus: true + } + + Controls.ComboBox { + id: switchMarketView + //anchors.top: parent.bottom + //anchors.bottomMargin: 4 + anchors.fill: parent + visible: true + model: ListModel { + id: cbItems + ListElement { text: "Banana" } + ListElement { text: "Apple" } + ListElement { text: "Coconut" } + } + } diff --git a/scripts/system/marketplace - Elisa.js b/scripts/system/marketplace - Elisa.js new file mode 100644 index 0000000000..7a4057d77b --- /dev/null +++ b/scripts/system/marketplace - Elisa.js @@ -0,0 +1,82 @@ +// +// marketplace.js +// +// Created by Eric Levin on 8 Jan 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var toolIconUrl = Script.resolvePath("assets/images/tools/"); + +var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; +var CLARA_URL = "https://clara.io/library"; +var marketplaceWindow = new OverlayWindow({ + title: "Marketplace", + source: "about:blank", + //source: MARKETPLACE_URL, + //source: "https://s3.amazonaws.com/DreamingContent/qml/content.qml", + //source: "C:\Users\elisa\Documents\GitHub\hifi\interface\resources\qml\controls-uit\WebView.qml", + width: 900, + height: 700, + toolWindow: false, + visible: false, +}); + +var toolHeight = 50; +var toolWidth = 50; +var TOOLBAR_MARGIN_Y = 0; + + +function showMarketplace(marketplaceID) { + var url = MARKETPLACE_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + marketplaceWindow.setVisible(true); + //marketplaceWindow.webview.url = url; + + UserActivityLogger.openedMarketplace(); +} + +function hideMarketplace() { + marketplaceWindow.setVisible(false); + marketplaceWindow.webview.url = "about:blank"; +} + +function toggleMarketplace() { + if (marketplaceWindow.visible) { + hideMarketplace(); + } else { + showMarketplace(); + } +} + +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + +var browseExamplesButton = toolBar.addButton({ + imageURL: toolIconUrl + "market.svg", + objectName: "marketplace", + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9 +}); + +function onExamplesWindowVisibilityChanged() { + browseExamplesButton.writeProperty('buttonState', marketplaceWindow.visible ? 0 : 1); + browseExamplesButton.writeProperty('defaultState', marketplaceWindow.visible ? 0 : 1); + browseExamplesButton.writeProperty('hoverState', marketplaceWindow.visible ? 2 : 3); +} +function onClick() { + toggleMarketplace(); +} +browseExamplesButton.clicked.connect(onClick); +marketplaceWindow.visibleChanged.connect(onExamplesWindowVisibilityChanged); + +Script.scriptEnding.connect(function () { + toolBar.removeButton("marketplace"); + browseExamplesButton.clicked.disconnect(onClick); + marketplaceWindow.visibleChanged.disconnect(onExamplesWindowVisibilityChanged); +}); diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js index 356ed8e8cf..e83e0c2ccb 100644 --- a/scripts/system/marketplace.js +++ b/scripts/system/marketplace.js @@ -11,6 +11,7 @@ var toolIconUrl = Script.resolvePath("assets/images/tools/"); var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; + var marketplaceWindow = new OverlayWebWindow({ title: "Marketplace", source: "about:blank", From b39c8c52abe0e722ac62cc1170a8abf706c05681 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Wed, 3 Aug 2016 19:08:14 -0700 Subject: [PATCH 052/332] Included combobox in marketplace view --- .../resources/qml/MarketplaceComboBox.qml | 52 +++++++++++++++++++ interface/resources/qml/QmlWindow.qml | 1 - .../qml/controls-uit/MarketplaceComboBox.qml | 43 --------------- scripts/system/marketplace - Elisa.js | 9 ++-- 4 files changed, 56 insertions(+), 49 deletions(-) create mode 100644 interface/resources/qml/MarketplaceComboBox.qml delete mode 100644 interface/resources/qml/controls-uit/MarketplaceComboBox.qml diff --git a/interface/resources/qml/MarketplaceComboBox.qml b/interface/resources/qml/MarketplaceComboBox.qml new file mode 100644 index 0000000000..21a7d48556 --- /dev/null +++ b/interface/resources/qml/MarketplaceComboBox.qml @@ -0,0 +1,52 @@ +// +// MarketplaceComboBox.qml +// +// Created by Elisa Lupin-Jimenez on 3 Aug 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtWebChannel 1.0 +import QtWebEngine 1.1 +import QtWebSockets 1.0 +import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel + +import "controls" +import "controls-uit" as Controls +import "styles" +import "styles-uit" + +Item { + id: marketplaceComboBox + anchors.fill: parent + property var currentUrl: "https://metaverse.highfidelity.com/marketplace" + Controls.WebView { + id: webview + url: currentUrl + anchors.top: parent.top + width: parent.width + height: parent.height - 50 + //anchors.bottom: switchMarketView.top + focus: true + } + + Controls.ComboBox { + id: switchMarketView + anchors.bottom: parent.bottom + width: parent.width + height: 50 + visible: true + label: "Market View: " + model: ["Marketplace", "Clara.io"] + onCurrentIndexChanged: { + if (currentIndex === 0) { webview.url = "https://metaverse.highfidelity.com/marketplace"; } + if (currentIndex === 1) { webview.url = "https://clara.io/library"; } + } + + } + +} \ No newline at end of file diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml index 47857ac7c1..908c1b1a77 100644 --- a/interface/resources/qml/QmlWindow.qml +++ b/interface/resources/qml/QmlWindow.qml @@ -76,6 +76,5 @@ Windows.Window { Item { id: contentHolder anchors.fill: parent - } } diff --git a/interface/resources/qml/controls-uit/MarketplaceComboBox.qml b/interface/resources/qml/controls-uit/MarketplaceComboBox.qml deleted file mode 100644 index 986131f3ec..0000000000 --- a/interface/resources/qml/controls-uit/MarketplaceComboBox.qml +++ /dev/null @@ -1,43 +0,0 @@ -// -// MarketplaceComboBox.qml -// -// Created by Elisa Lupin-Jimenez on 3 Aug 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtWebChannel 1.0 -import QtWebEngine 1.1 -import QtWebSockets 1.0 -import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel - -import "controls" -import "controls-uit" as Controls -import "styles" -import "styles-uit" - -// just temporary - Controls.WebView { - id: webview - anchors.fill: parent - url: "about:blank" - focus: true - } - - Controls.ComboBox { - id: switchMarketView - //anchors.top: parent.bottom - //anchors.bottomMargin: 4 - anchors.fill: parent - visible: true - model: ListModel { - id: cbItems - ListElement { text: "Banana" } - ListElement { text: "Apple" } - ListElement { text: "Coconut" } - } - } diff --git a/scripts/system/marketplace - Elisa.js b/scripts/system/marketplace - Elisa.js index 7a4057d77b..baec825c8d 100644 --- a/scripts/system/marketplace - Elisa.js +++ b/scripts/system/marketplace - Elisa.js @@ -9,15 +9,14 @@ // var toolIconUrl = Script.resolvePath("assets/images/tools/"); +var qml = Script.resolvePath("../../interface/resources/qml/MarketplaceComboBox.qml") var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; var CLARA_URL = "https://clara.io/library"; var marketplaceWindow = new OverlayWindow({ title: "Marketplace", - source: "about:blank", - //source: MARKETPLACE_URL, - //source: "https://s3.amazonaws.com/DreamingContent/qml/content.qml", - //source: "C:\Users\elisa\Documents\GitHub\hifi\interface\resources\qml\controls-uit\WebView.qml", + //source: "about:blank", + source: qml, width: 900, height: 700, toolWindow: false, @@ -42,7 +41,7 @@ function showMarketplace(marketplaceID) { function hideMarketplace() { marketplaceWindow.setVisible(false); - marketplaceWindow.webview.url = "about:blank"; + //marketplaceWindow.webview.url = "about:blank"; } function toggleMarketplace() { From 90a1325f65aadf0466fbd81d9d69cb47798accba Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Thu, 4 Aug 2016 15:45:16 -0700 Subject: [PATCH 053/332] Finalized combobox feature --- .../resources/qml/MarketplaceComboBox.qml | 25 ++++-- scripts/system/marketplace - Elisa.js | 81 ------------------- scripts/system/marketplace.js | 10 +-- 3 files changed, 22 insertions(+), 94 deletions(-) delete mode 100644 scripts/system/marketplace - Elisa.js diff --git a/interface/resources/qml/MarketplaceComboBox.qml b/interface/resources/qml/MarketplaceComboBox.qml index 21a7d48556..da9a994bcc 100644 --- a/interface/resources/qml/MarketplaceComboBox.qml +++ b/interface/resources/qml/MarketplaceComboBox.qml @@ -20,27 +20,28 @@ import "controls-uit" as Controls import "styles" import "styles-uit" -Item { +Rectangle { + HifiConstants { id: hifi } id: marketplaceComboBox anchors.fill: parent + color: hifi.colors.baseGrayShadow property var currentUrl: "https://metaverse.highfidelity.com/marketplace" Controls.WebView { id: webview url: currentUrl - anchors.top: parent.top + anchors.top: switchMarketView.bottom width: parent.width - height: parent.height - 50 - //anchors.bottom: switchMarketView.top + height: parent.height - 40 focus: true } Controls.ComboBox { id: switchMarketView - anchors.bottom: parent.bottom - width: parent.width - height: 50 + anchors.top: parent.top + anchors.right: parent.right + width: 200 + height: 40 visible: true - label: "Market View: " model: ["Marketplace", "Clara.io"] onCurrentIndexChanged: { if (currentIndex === 0) { webview.url = "https://metaverse.highfidelity.com/marketplace"; } @@ -49,4 +50,12 @@ Item { } + Controls.Label { + id: switchMarketLabel + anchors.verticalCenter: switchMarketView.verticalCenter + anchors.right: switchMarketView.left + color: hifi.colors.white + text: "Explore interesting content from: " + } + } \ No newline at end of file diff --git a/scripts/system/marketplace - Elisa.js b/scripts/system/marketplace - Elisa.js deleted file mode 100644 index baec825c8d..0000000000 --- a/scripts/system/marketplace - Elisa.js +++ /dev/null @@ -1,81 +0,0 @@ -// -// marketplace.js -// -// Created by Eric Levin on 8 Jan 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var toolIconUrl = Script.resolvePath("assets/images/tools/"); -var qml = Script.resolvePath("../../interface/resources/qml/MarketplaceComboBox.qml") - -var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var CLARA_URL = "https://clara.io/library"; -var marketplaceWindow = new OverlayWindow({ - title: "Marketplace", - //source: "about:blank", - source: qml, - width: 900, - height: 700, - toolWindow: false, - visible: false, -}); - -var toolHeight = 50; -var toolWidth = 50; -var TOOLBAR_MARGIN_Y = 0; - - -function showMarketplace(marketplaceID) { - var url = MARKETPLACE_URL; - if (marketplaceID) { - url = url + "/items/" + marketplaceID; - } - marketplaceWindow.setVisible(true); - //marketplaceWindow.webview.url = url; - - UserActivityLogger.openedMarketplace(); -} - -function hideMarketplace() { - marketplaceWindow.setVisible(false); - //marketplaceWindow.webview.url = "about:blank"; -} - -function toggleMarketplace() { - if (marketplaceWindow.visible) { - hideMarketplace(); - } else { - showMarketplace(); - } -} - -var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); - -var browseExamplesButton = toolBar.addButton({ - imageURL: toolIconUrl + "market.svg", - objectName: "marketplace", - buttonState: 1, - defaultState: 1, - hoverState: 3, - alpha: 0.9 -}); - -function onExamplesWindowVisibilityChanged() { - browseExamplesButton.writeProperty('buttonState', marketplaceWindow.visible ? 0 : 1); - browseExamplesButton.writeProperty('defaultState', marketplaceWindow.visible ? 0 : 1); - browseExamplesButton.writeProperty('hoverState', marketplaceWindow.visible ? 2 : 3); -} -function onClick() { - toggleMarketplace(); -} -browseExamplesButton.clicked.connect(onClick); -marketplaceWindow.visibleChanged.connect(onExamplesWindowVisibilityChanged); - -Script.scriptEnding.connect(function () { - toolBar.removeButton("marketplace"); - browseExamplesButton.clicked.disconnect(onClick); - marketplaceWindow.visibleChanged.disconnect(onExamplesWindowVisibilityChanged); -}); diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js index e83e0c2ccb..01aa4c76ce 100644 --- a/scripts/system/marketplace.js +++ b/scripts/system/marketplace.js @@ -9,15 +9,17 @@ // var toolIconUrl = Script.resolvePath("assets/images/tools/"); +var qml = Script.resolvePath("../../resources/qml/MarketplaceComboBox.qml") var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var marketplaceWindow = new OverlayWebWindow({ +var marketplaceWindow = new OverlayWindow({ title: "Marketplace", - source: "about:blank", + source: qml, width: 900, height: 700, - visible: false + toolWindow: false, + visible: false, }); var toolHeight = 50; @@ -30,7 +32,6 @@ function showMarketplace(marketplaceID) { if (marketplaceID) { url = url + "/items/" + marketplaceID; } - marketplaceWindow.setURL(url); marketplaceWindow.setVisible(true); UserActivityLogger.openedMarketplace(); @@ -38,7 +39,6 @@ function showMarketplace(marketplaceID) { function hideMarketplace() { marketplaceWindow.setVisible(false); - marketplaceWindow.setURL("about:blank"); } function toggleMarketplace() { From e8c5303f1b933508c3736717ff3182def37cbdb8 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Thu, 4 Aug 2016 17:23:36 -0700 Subject: [PATCH 054/332] Removed examples.js --- scripts/system/examples.js | 80 -------------------------------------- 1 file changed, 80 deletions(-) delete mode 100644 scripts/system/examples.js diff --git a/scripts/system/examples.js b/scripts/system/examples.js deleted file mode 100644 index 6c01fd58af..0000000000 --- a/scripts/system/examples.js +++ /dev/null @@ -1,80 +0,0 @@ -// -// examples.js -// examples -// -// Created by Eric Levin on 8 Jan 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var toolIconUrl = Script.resolvePath("assets/images/tools/"); - -//var EXAMPLES_URL = "https://metaverse.highfidelity.com/examples"; -//var EXAMPLES_URL = "https://clara.io/view/c1c4d926-5648-4fd3-9673-6d9018ad4627"; -var EXAMPLES_URL = "https://clara.io/library"; -//var EXAMPLES_URL = "http://s3.amazonaws.com/DreamingContent/test.html"; -//var EXAMPLES_URL = "https://hifi-content.s3.amazonaws.com/elisalj/test.html"; -//var EXAMPLES_URL = "https://hifi-content.s3.amazonaws.com/elisalj/test_download.html"; - -var examplesWindow = new OverlayWebWindow({ - title: 'Examples', - source: "about:blank", - width: 900, - height: 700, - visible: false -}); - -var toolHeight = 50; -var toolWidth = 50; -var TOOLBAR_MARGIN_Y = 0; - - -function showExamples(marketplaceID) { - var url = EXAMPLES_URL; - if (marketplaceID) { - url = url + "/items/" + marketplaceID; - } - print("setting examples URL to " + url); - examplesWindow.setURL(url); - examplesWindow.setVisible(true); - - UserActivityLogger.openedMarketplace(); -} - -function hideExamples() { - examplesWindow.setVisible(false); - examplesWindow.setURL("about:blank"); -} - -function toggleExamples() { - if (examplesWindow.visible) { - hideExamples(); - } else { - showExamples(); - } -} - -var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); - -var browseExamplesButton = toolBar.addButton({ - imageURL: toolIconUrl + "market.svg", - objectName: "examples", - buttonState: 1, - alpha: 0.9 -}); - -function onExamplesWindowVisibilityChanged() { - browseExamplesButton.writeProperty('buttonState', examplesWindow.visible ? 0 : 1); -} -function onClick() { - toggleExamples(); -} -browseExamplesButton.clicked.connect(onClick); -examplesWindow.visibleChanged.connect(onExamplesWindowVisibilityChanged); - -Script.scriptEnding.connect(function () { - browseExamplesButton.clicked.disconnect(onClick); - examplesWindow.visibleChanged.disconnect(onExamplesWindowVisibilityChanged); -}); From 92c0fa2e7aa7f4d88c00a0a01b0405c7df920c74 Mon Sep 17 00:00:00 2001 From: sam Date: Fri, 5 Aug 2016 09:26:48 -0700 Subject: [PATCH 055/332] Testiung more variations --- .../src/AmbientOcclusionEffect.cpp | 10 +++ .../render-utils/src/AmbientOcclusionEffect.h | 9 ++- .../render-utils/src/SurfaceGeometryPass.cpp | 2 +- libraries/render-utils/src/ssao.slh | 74 ++++++++++++++----- .../render-utils/src/ssao_debugOcclusion.slf | 26 +++++-- .../render-utils/src/ssao_makeOcclusion.slf | 34 +-------- .../utilities/render/ambientOcclusionPass.qml | 21 ++++++ .../render/debugAmbientOcclusionPass.js | 2 +- 8 files changed, 113 insertions(+), 65 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 97132a0fc7..9bdabbfb04 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -228,6 +228,11 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.y = 1.0f / config.numSamples; } + if (config.fetchMipsEnabled != _parametersBuffer->isFetchMipsEnabled()) { + auto& current = _parametersBuffer->sampleInfo; + current.w = (float)config.fetchMipsEnabled; + } + if (!_framebuffer) { _framebuffer = std::make_shared(); } @@ -510,6 +515,11 @@ void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const auto linearDepthFramebuffer = inputs.get2(); const auto ambientOcclusionUniforms = inputs.get3(); + // Skip if AO is not started yet + if (!ambientOcclusionUniforms._buffer) { + return; + } + auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); auto normalTexture = deferredFramebuffer->getDeferredNormalTexture(); auto sourceViewport = args->_viewport; diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 5a0e7da587..7c2c465ba3 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -62,8 +62,8 @@ class AmbientOcclusionEffectConfig : public render::Job::Config::Persistent { Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty) Q_PROPERTY(bool ditheringEnabled MEMBER ditheringEnabled NOTIFY dirty) Q_PROPERTY(bool borderingEnabled MEMBER borderingEnabled NOTIFY dirty) + Q_PROPERTY(bool fetchMipsEnabled MEMBER fetchMipsEnabled NOTIFY dirty) Q_PROPERTY(float radius MEMBER radius WRITE setRadius) - Q_PROPERTY(float perspectiveScale MEMBER perspectiveScale WRITE setPerspectiveScale) Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel) Q_PROPERTY(float falloffBias MEMBER falloffBias WRITE setFalloffBias) Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness WRITE setEdgeSharpness) @@ -80,7 +80,6 @@ public: const int MAX_BLUR_RADIUS = 6; void setRadius(float newRadius) { radius = std::max(0.01f, newRadius); emit dirty(); } - void setPerspectiveScale(float scale) { perspectiveScale = scale; emit dirty(); } void setObscuranceLevel(float level) { obscuranceLevel = std::max(0.01f, level); emit dirty(); } void setFalloffBias(float bias) { falloffBias = std::max(0.0f, std::min(bias, 0.2f)); emit dirty(); } void setEdgeSharpness(float sharpness) { edgeSharpness = std::max(0.0f, (float)sharpness); emit dirty(); } @@ -101,8 +100,9 @@ public: int numSamples{ 11 }; int resolutionLevel{ 0 }; int blurRadius{ 3 }; // 0 means no blurring - bool ditheringEnabled{ true }; // randomize the distribution of rays per pixel, should always be true + bool ditheringEnabled{ true }; // randomize the distribution of taps per pixel, should always be true bool borderingEnabled{ true }; // avoid evaluating information from non existing pixels out of the frame, should always be true + bool fetchMipsEnabled{ true }; // fetch taps in sub mips to otpimize cache, should always be true double gpuTime{ 0.0 }; signals: @@ -163,8 +163,11 @@ public: float getFalloffBias() const { return (float)ditheringInfo.z; } float getEdgeSharpness() const { return (float)blurInfo.x; } float getBlurDeviation() const { return blurInfo.z; } + float getNumSpiralTurns() const { return sampleInfo.z; } int getNumSamples() const { return (int)sampleInfo.x; } + bool isFetchMipsEnabled() const { return sampleInfo.w; } + int getBlurRadius() const { return (int)blurInfo.y; } bool isDitheringEnabled() const { return ditheringInfo.x; } bool isBorderingEnabled() const { return ditheringInfo.w; } diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 6dc1cb2f9b..683fe69ff0 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -71,7 +71,7 @@ void LinearDepthFramebuffer::allocate() { // For Linear Depth: _linearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); - // _linearDepthTexture->autoGenerateMips(1); + _linearDepthTexture->autoGenerateMips(1); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 3a3203e2e3..7efcd861c7 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -93,6 +93,10 @@ float getNumSpiralTurns() { return params._sampleInfo.z; } +int doFetchMips() { + return int(params._sampleInfo.w); +} + float getBlurEdgeSharpness() { return params._blurInfo.x; } @@ -128,6 +132,19 @@ float getBlurCoef(int c) { <@func declareSamplingDisk()@> +float getAngleDitheringWorldPos(in vec3 pixelWorldPos) { + vec3 worldPosFract = fract(pixelWorldPos * 0.2); + + ivec3 pixelPos = ivec3(worldPosFract * 256); + + return isDitheringEnabled() * (3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10 + getFrameDithering(); +} + +float getAngleDithering(in ivec2 pixelPos) { + // Hash function used in the AlchemyAO paper + return isDitheringEnabled() * (3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10 + getFrameDithering(); +} + float evalDiskRadius(float Zeye, vec2 imageSize) { // Choose the screen-space sample radius // proportional to the projected area of the sphere @@ -179,20 +196,28 @@ vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, tapPos.y -= (imageSize.y - tapPos.y); redoTap = true; } - +/* + if ((tapPos.x < 0.5)) { + tapPos.x = 0.5; + redoTap = true; + } else if ((tapPos.x > imageSize.x - 0.5)) { + tapPos.x = imageSize.x - 0.5; + redoTap = true; + } + + if ((tapPos.y < 0.5)) { + tapPos.y = 0.5; + redoTap = true; + } else if ((tapPos.y > imageSize.y - 0.5)) { + tapPos.y = imageSize.y - 0.5; + redoTap = true; + } +*/ + if (redoTap) { tap.xy = tapPos - pixelPos; tap.z = length(tap.xy); } -/* - if ((tapPos.x < 0.0) || (tapPos.x >= imageSize.x)) { - // tapPos.x = pixelPos.x - tapVec.x; - tap.x = -tap.x; - } - if ((tapPos.y < 0.0) || (tapPos.y >= imageSize.y)) { - // tapPos.y = pixelPos.y - tapVec.y; - tap.y = -tap.y; - }*/ return tap; } @@ -202,8 +227,6 @@ vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, <@func declareFetchDepthPyramidMap()@> -const int LOG_MAX_OFFSET = 3; -const int MAX_MIP_LEVEL = 5; // the depth pyramid texture uniform sampler2D pyramidMap; @@ -212,23 +235,34 @@ float getZEye(ivec2 pixel) { return -texelFetch(pyramidMap, pixel, getResolutionLevel()).x; } -vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec3 tap, vec2 imageSize) { - // Derivation: +const int LOG_MAX_OFFSET = 3; +const int MAX_MIP_LEVEL = 5; +int evalMipFromRadius(float radius) { // mipLevel = floor(log(ssR / MAX_OFFSET)); - int mipLevel = clamp(findMSB(int(tap.z)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); + return doFetchMips() * clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); +} + +vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec3 tap, vec2 imageSize) { + int mipLevel = evalMipFromRadius(tap.z); ivec2 ssP = ivec2(tap.xy) + ssC; ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); - vec3 P; - // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. // Manually clamp to the texture size because texelFetch bypasses the texture unit - ivec2 mipP = clamp(ssPFull >> mipLevel, ivec2(0), textureSize(pyramidMap, getResolutionLevel() + mipLevel) - ivec2(1)); - P.z = -texelFetch(pyramidMap, mipP, getResolutionLevel() + mipLevel).r; + // ivec2 mipSize = textureSize(pyramidMap, mipLevel); + // ivec2 mipSize = max(ivec2(imageSize) >> mipLevel, ivec2(1)); + + // ivec2 mipP = clamp(ssPFull >> mipLevel, ivec2(0), mipSize - ivec2(1)); + + vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize; + // vec2 tapUV = (vec2(mipP) + vec2(0.5)) / vec2(mipSize); + + vec3 P; + // P.z = -texelFetch(pyramidMap, mipP, mipLevel).r; + P.z = -textureLod(pyramidMap, tapUV, float(mipLevel)).r; // Offset to pixel center - vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize; P = evalEyePositionFromZeye(side.x, P.z, tapUV); return P; } diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index 7e6a792c3b..ecb1d112b9 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -16,6 +16,9 @@ <$declarePackOcclusionDepth()$> +<@include gpu/color.slh@> +<$declareColorWheel()$> + struct DebugParams{ vec4 pixelInfo; }; @@ -32,10 +35,6 @@ out vec4 outFragColor; uniform sampler2D normalMap; -float getAngleDithering(in ivec2 pixelPos) { - // Hash function used in the AlchemyAO paper - return isDitheringEnabled() * (3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10 + getFrameDithering(); -} float evalAO(in vec3 C, in vec3 n_C, vec3 Q) { vec3 v = Q - C; @@ -81,18 +80,23 @@ void main(void) { } // Let's make noise - float randomPatternRotationAngle = getAngleDithering(ssC); + // float randomPatternRotationAngle = getAngleDithering(ssC); + float randomPatternRotationAngle = getAngleDitheringWorldPos(Cp); + // Accumulate the Obscurance for each samples float sum = 0.0; - float keepTapRadius = 2.0; - bool keep = (dot(fragToCursor,fragToCursor) < keepTapRadius); + float keepTapRadius = 1.0; + int keepedMip = -1; + bool keep = false; + for (int i = 0; i < getNumSamples(); ++i) { vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize); // The occluding point in camera space vec2 fragToTap = vec2(ssC) + tap.xy - gl_FragCoord.xy; if (dot(fragToTap,fragToTap) < keepTapRadius) { keep = true; + keepedMip = evalMipFromRadius(tap.z); } vec3 Q = getOffsetPosition(side.xyz, ssC, tap, imageSize); @@ -130,8 +134,14 @@ void main(void) { // outFragColor = vec4((Cn + vec3(1.0))* 0.5, 1.0); //outFragColor = vec4(vec3(ssDiskRadius / 100.0), 1.0); - + if ((dot(fragToCursor,fragToCursor) < (4.0 * keepTapRadius * keepTapRadius) )) { + outFragColor = vec4(vec3(A), 1.0); + return; + } + if (!keep) { outFragColor = vec4(0.1); + } else { + outFragColor.rgb = colorWheel(float(keepedMip)/float(MAX_MIP_LEVEL)); } } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 739dc1ddcb..19a6244a07 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -21,37 +21,6 @@ out vec4 outFragColor; uniform sampler2D normalMap; -float getAngleDithering(in ivec2 pixelPos) { - // Hash function used in the AlchemyAO paper - return isDitheringEnabled() * (3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10 + getFrameDithering(); -} -/* -vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec2 unitOffset, float ssR) { - // Derivation: - // mipLevel = floor(log(ssR / MAX_OFFSET)); - int mipLevel = clamp(findMSB(int(ssR)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); - - ivec2 ssOffset = ivec2(ssR * unitOffset); - ivec2 ssP = ssOffset + ssC; - if (bool(isBorderingEnabled())) { - ssP.x = ((ssP.x < 0 || ssP.x >= side.z) ? ssC.x - ssOffset.x : ssP.x); - ssP.y = ((ssP.y < 0 || ssP.y >= int(getWidthHeight(getResolutionLevel()).y)) ? ssC.y - ssOffset.y : ssP.y); - } - - ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); - - vec3 P; - - // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. - // Manually clamp to the texture size because texelFetch bypasses the texture unit - ivec2 mipP = clamp(ssPFull >> mipLevel, ivec2(0), textureSize(pyramidMap, getResolutionLevel() + mipLevel) - ivec2(1)); - P.z = -texelFetch(pyramidMap, mipP, getResolutionLevel() + mipLevel).r; - - // Offset to pixel center - vec2 tapUV = (vec2(ssP) + vec2(0.5)) / float(side.z); - P = evalEyePositionFromZeye(side.x, P.z, tapUV); - return P; -}*/ float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) { vec3 v = Q - C; @@ -88,7 +57,8 @@ void main(void) { float ssDiskRadius = evalDiskRadius(Cp.z, imageSize); // Let's make noise - float randomPatternRotationAngle = getAngleDithering(ssC); + // float randomPatternRotationAngle = getAngleDithering(ssC); + float randomPatternRotationAngle = getAngleDitheringWorldPos(Cp); // Accumulate the Obscurance for each samples float sum = 0.0; diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 527c8d8cd0..c4d86e544e 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -10,6 +10,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import "configSlider" +import "../lib/plotperf" Column { spacing: 8 @@ -35,11 +36,15 @@ Column { min: 0.0 } } + } + + Column { Repeater { model: [ "resolutionLevel:resolutionLevel", "ditheringEnabled:ditheringEnabled", "borderingEnabled:borderingEnabled", + "fetchMipsEnabled:fetchMipsEnabled", ] CheckBox { text: qsTr(modelData.split(":")[0]) @@ -48,5 +53,21 @@ Column { } } } + + PlotPerf { + title: "Timing" + height: 50 + object: Render.getConfig("AmbientOcclusion") + valueUnit: "ms" + valueScale: 1 + valueNumDigits: "4" + plots: [ + { + prop: "gpuTime", + label: "gpu", + color: "#FFFFFF" + } + ] + } } } diff --git a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js index 680db81607..60fb8bf918 100644 --- a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js +++ b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js @@ -13,7 +13,7 @@ var qml = Script.resolvePath('ambientOcclusionPass.qml'); var window = new OverlayWindow({ title: 'Ambient Occlusion Pass', source: qml, - width: 400, height: 170, + width: 400, height: 200, }); window.setPosition(Window.innerWidth - 420, 50 + 550 + 50); window.closed.connect(function() { Script.stop(); }); From 1dd276c1a59b6b5b729dabc71f258d371349d9a4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 5 Aug 2016 11:32:50 -0700 Subject: [PATCH 056/332] Improved support for button rollover and hover Send a combination of mouse and touch events to the web browser entity. * MouseMove events for when the trigger is not depressed. This will trigger mouse over and hover events within the browser * Touch Begin, End, Update events when the trigger is squeezed. This will give us the iPad like scrolling behavior. --- scripts/system/controllers/handControllerGrab.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 40392f1d8b..7421dc03f7 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1314,7 +1314,13 @@ function MyController(hand) { Reticle.keyboardFocusEntity = focusedEntity; } - Reticle.sendEntityTouchUpdateEvent(focusedEntity, this.hand, intersectionPoint); + if (!this.triggerSmoothedGrab()) { + // send mouse events for button highlights and tooltips + Reticle.sendEntityMouseMoveEvent(focusedEntity, intersectionPoint); + } else { + // but send touch updates when grab is pressed. + Reticle.sendEntityTouchUpdateEvent(focusedEntity, this.hand, intersectionPoint); + } if (this.triggerSmoothedGrab() && !this.lastTriggerSmoothedGrab) { Reticle.sendEntityTouchBeginEvent(focusedEntity, this.hand, intersectionPoint); From 0ba34c5635c6ee0aad9a4cd23f8c93a153b28fd4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 5 Aug 2016 15:25:54 -0700 Subject: [PATCH 057/332] Remove input restrictions on grabbable web entities. You no longer have to be grabbing a grabbable web entity to interact with it. --- .../system/controllers/handControllerGrab.js | 316 ++++++++++-------- 1 file changed, 177 insertions(+), 139 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 7421dc03f7..84b4bc592d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -170,6 +170,7 @@ var STATE_NEAR_GRABBING = 3; var STATE_NEAR_TRIGGER = 4; var STATE_FAR_TRIGGER = 5; var STATE_HOLD = 6; +var STATE_ENTITY_TOUCHING = 7; // "collidesWith" is specified by comma-separated list of group names // the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar @@ -185,6 +186,8 @@ var delayedDeactivateEntityID; var CONTROLLER_STATE_MACHINE = {}; +var mostRecentSearchingHand = RIGHT_HAND; + CONTROLLER_STATE_MACHINE[STATE_OFF] = { name: "off", enterMethod: "offEnter", @@ -220,6 +223,40 @@ CONTROLLER_STATE_MACHINE[STATE_FAR_TRIGGER] = { enterMethod: "farTriggerEnter", updateMethod: "farTrigger" }; +CONTROLLER_STATE_MACHINE[STATE_ENTITY_TOUCHING] = { + name: "entityTouching", + enterMethod: "entityTouchingEnter", + exitMethod: "entityTouchingExit", + updateMethod: "entityTouching" +}; + +function handLaserIntersectWebEntity(entityID, hand) { + var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var pose = Controller.getPoseValue(standardControllerValue); + var worldHandPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position); + var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation); + + var props = entityPropertiesCache.getProps(entityID); + var planePosition = props.position; + var planeNormal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1.0}); + var rayStart = worldHandPosition; + var rayDirection = Quat.getUp(worldHandRotation); + var intersectionInfo = rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection); + + var intersectionPoint = planePosition; + if (intersectionInfo.hit && intersectionInfo.distance > 0) { + intersectionPoint = Vec3.sum(rayStart, Vec3.multiply(intersectionInfo.distance, rayDirection)); + } else { + intersectionPoint = planePosition; + } + intersectionInfo.point = intersectionPoint; + intersectionInfo.searchRay = { + origin: rayStart, + direction: rayDirection, + length: PICK_MAX_DISTANCE + }; + return intersectionInfo; +} function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) { var rayDirectionDotPlaneNormal = Vec3.dot(rayDirection, planeNormal); @@ -743,8 +780,6 @@ function MyController(hand) { } }; - - this.searchSphereOn = function(location, size, color) { var rotation = Quat.lookAt(location, Camera.getPosition(), Vec3.UP); @@ -1257,7 +1292,7 @@ function MyController(hand) { }; this.searchEnter = function() { - this.capturedWebEntity = null; + mostRecentSearchingHand = this.hand; }; this.search = function(deltaTime, timestamp) { @@ -1283,155 +1318,124 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - // route simulated touch events to a webEntity. - if (this.capturedWebEntity || - (rayPickInfo.entityID && entityPropertiesCache.getProps(rayPickInfo.entityID).type === "Web" && - (!this.entityIsGrabbable(rayPickInfo.entityID) || this.getOtherHandController().grabbedEntity == rayPickInfo.entityID))) { + var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); + entityPropertiesCache.addEntities(candidateEntities); - var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; - var pose = Controller.getPoseValue(standardControllerValue); - var worldHandPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position); - var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation); - - var focusedEntity = this.capturedWebEntity || rayPickInfo.entityID; - entityPropertiesCache.addEntity(focusedEntity); - - var props = entityPropertiesCache.getProps(focusedEntity); - var planePosition = props.position - var planeNormal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1.0}); - var rayStart = worldHandPosition; - var rayDirection = Quat.getUp(worldHandRotation); - var intersectionInfo = rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection); - - var intersectionPoint = planePosition; - if (intersectionInfo.hit && intersectionInfo.distance > 0) { - intersectionPoint = Vec3.sum(rayStart, Vec3.multiply(intersectionInfo.distance, rayDirection)); - } else { - intersectionPoint = planePosition; + var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); + if (potentialEquipHotspot) { + if (this.triggerSmoothedGrab()) { + this.grabbedHotspot = potentialEquipHotspot; + this.grabbedEntity = potentialEquipHotspot.entityID; + this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); + return; } + } - if (Reticle.keyboardFocusEntity != focusedEntity) { - Reticle.keyboardFocusEntity = focusedEntity; - } + var grabbableEntities = candidateEntities.filter(function(entity) { + return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE); + }); - if (!this.triggerSmoothedGrab()) { - // send mouse events for button highlights and tooltips - Reticle.sendEntityMouseMoveEvent(focusedEntity, intersectionPoint); - } else { - // but send touch updates when grab is pressed. - Reticle.sendEntityTouchUpdateEvent(focusedEntity, this.hand, intersectionPoint); + if (rayPickInfo.entityID) { + this.intersectionDistance = rayPickInfo.distance; + if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) { + grabbableEntities.push(rayPickInfo.entityID); } - - if (this.triggerSmoothedGrab() && !this.lastTriggerSmoothedGrab) { - Reticle.sendEntityTouchBeginEvent(focusedEntity, this.hand, intersectionPoint); - this.capturedWebEntity = focusedEntity; - } - if (!this.triggerSmoothedGrab() && this.lastTriggerSmoothedGrab) { - Reticle.sendEntityTouchEndEvent(focusedEntity, this.hand, intersectionPoint); - this.capturedWebEntity = null; - } - this.lastTriggerSmoothedGrab = this.triggerSmoothedGrab(); - - equipHotspotBuddy.updateHotspots([], timestamp); - this.intersectionDistance = intersectionInfo.distance; + } else if (rayPickInfo.overlayID) { + this.intersectionDistance = rayPickInfo.distance; } else { + this.intersectionDistance = 0; + } - var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); - entityPropertiesCache.addEntities(candidateEntities); - - var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); - if (potentialEquipHotspot) { - if (this.triggerSmoothedGrab()) { - this.grabbedHotspot = potentialEquipHotspot; - this.grabbedEntity = potentialEquipHotspot.entityID; - this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); - return; - } - } - - var grabbableEntities = candidateEntities.filter(function(entity) { - return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE); + var entity; + if (grabbableEntities.length > 0) { + // sort by distance + grabbableEntities.sort(function(a, b) { + var aDistance = Vec3.distance(entityPropertiesCache.getProps(a).position, handPosition); + var bDistance = Vec3.distance(entityPropertiesCache.getProps(b).position, handPosition); + return aDistance - bDistance; }); - - if (rayPickInfo.entityID) { - this.intersectionDistance = rayPickInfo.distance; - if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) { - grabbableEntities.push(rayPickInfo.entityID); - } - } else if (rayPickInfo.overlayID) { - this.intersectionDistance = rayPickInfo.distance; - } else { - this.intersectionDistance = 0; - } - - var entity; - if (grabbableEntities.length > 0) { - // sort by distance - grabbableEntities.sort(function(a, b) { - var aDistance = Vec3.distance(entityPropertiesCache.getProps(a).position, handPosition); - var bDistance = Vec3.distance(entityPropertiesCache.getProps(b).position, handPosition); - return aDistance - bDistance; - }); - entity = grabbableEntities[0]; - name = entityPropertiesCache.getProps(entity).name; - this.grabbedEntity = entity; - if (this.entityWantsTrigger(entity)) { - if (this.triggerSmoothedGrab()) { - this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'"); - return; - } else { - // potentialNearTriggerEntity = entity; - } + entity = grabbableEntities[0]; + name = entityPropertiesCache.getProps(entity).name; + this.grabbedEntity = entity; + if (this.entityWantsTrigger(entity)) { + if (this.triggerSmoothedGrab()) { + this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'"); + return; } else { - if (this.triggerSmoothedGrab()) { - var props = entityPropertiesCache.getProps(entity); - var grabProps = entityPropertiesCache.getGrabProps(entity); - var refCount = grabProps.refCount ? grabProps.refCount : 0; - if (refCount >= 1) { - // if another person is holding the object, remember to restore the - // parent info, when we are finished grabbing it. - this.shouldResetParentOnRelease = true; - this.previousParentID = props.parentID; - this.previousParentJointIndex = props.parentJointIndex; - } - - this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); - return; - } else { - // potentialNearGrabEntity = entity; + // potentialNearTriggerEntity = entity; + } + } else { + if (this.triggerSmoothedGrab()) { + var props = entityPropertiesCache.getProps(entity); + var grabProps = entityPropertiesCache.getGrabProps(entity); + var refCount = grabProps.refCount ? grabProps.refCount : 0; + if (refCount >= 1) { + // if another person is holding the object, remember to restore the + // parent info, when we are finished grabbing it. + this.shouldResetParentOnRelease = true; + this.previousParentID = props.parentID; + this.previousParentJointIndex = props.parentJointIndex; } + + this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); + return; + } else { + // potentialNearGrabEntity = entity; } } + } - if (rayPickInfo.entityID) { - entity = rayPickInfo.entityID; - name = entityPropertiesCache.getProps(entity).name; - if (this.entityWantsTrigger(entity)) { - if (this.triggerSmoothedGrab()) { - this.grabbedEntity = entity; - this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'"); - return; - } else { - // potentialFarTriggerEntity = entity; - } - } else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) { - if (this.triggerSmoothedGrab() && !isEditing()) { - this.grabbedEntity = entity; - this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'"); - return; - } else { - // potentialFarGrabEntity = entity; - } + if (rayPickInfo.entityID && entityPropertiesCache.getProps(rayPickInfo.entityID).type === "Web") { + entity = rayPickInfo.entityID; + name = entityPropertiesCache.getProps(entity).name; + + if (Reticle.keyboardFocusEntity != entity) { + Reticle.keyboardFocusEntity = entity; + } + + // send mouse events for button highlights and tooltips. + if (this.hand == mostRecentSearchingHand || (this.hand !== mostRecentSearchingHand && + this.getOtherHandController().state !== STATE_SEARCHING && + this.getOtherHandController().state !== STATE_ENTITY_TOUCHING)) { + // most recently searching hand has priority over other hand, for the purposes of button highlighting. + Reticle.sendEntityMouseMoveEvent(entity, rayPickInfo.intersection); + } + + if (this.triggerSmoothedGrab() && !isEditing()) { + this.grabbedEntity = entity; + this.setState(STATE_ENTITY_TOUCHING, "begin touching entity '" + name + "'"); + return; + } + } + + if (rayPickInfo.entityID) { + entity = rayPickInfo.entityID; + name = entityPropertiesCache.getProps(entity).name; + if (this.entityWantsTrigger(entity)) { + if (this.triggerSmoothedGrab()) { + this.grabbedEntity = entity; + this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'"); + return; + } else { + // potentialFarTriggerEntity = entity; + } + } else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) { + if (this.triggerSmoothedGrab() && !isEditing()) { + this.grabbedEntity = entity; + this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'"); + return; + } else { + // potentialFarGrabEntity = entity; } } + } - this.updateEquipHaptics(potentialEquipHotspot, handPosition); + this.updateEquipHaptics(potentialEquipHotspot, handPosition); - var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); - equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); - if (potentialEquipHotspot) { - equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); - } + var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); + equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); + if (potentialEquipHotspot) { + equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); } this.searchIndicatorOn(rayPickInfo.searchRay); @@ -1518,8 +1522,8 @@ function MyController(hand) { }; this.distanceHolding = function(deltaTime, timestamp) { - - if (!this.triggerClicked) { + + if (!this.triggerClicked) { this.callEntityMethodOnGrabbed("releaseGrab"); this.setState(STATE_OFF, "trigger released"); return; @@ -1609,9 +1613,9 @@ function MyController(hand) { // visualizations - var rayPickInfo = this.calcRayPickInfo(this.hand); + var rayPickInfo = this.calcRayPickInfo(this.hand); - this.overlayLineOn(rayPickInfo.searchRay.origin, grabbedProperties.position, COLORS_GRAB_DISTANCE_HOLD); + this.overlayLineOn(rayPickInfo.searchRay.origin, grabbedProperties.position, COLORS_GRAB_DISTANCE_HOLD); var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); var success = Entities.updateAction(this.grabbedEntity, this.actionID, { @@ -2044,6 +2048,40 @@ function MyController(hand) { this.release(); }; + this.entityTouchingEnter = function() { + // test for intersection between controller laser and web entity plane. + var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); + Reticle.sendEntityTouchBeginEvent(this.grabbedEntity, this.hand, intersectInfo.point); + }; + + this.entityTouchingExit = function() { + // test for intersection between controller laser and web entity plane. + var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); + Reticle.sendEntityTouchEndEvent(this.grabbedEntity, this.hand, intersectInfo.point); + }; + + this.entityTouching = function() { + entityPropertiesCache.addEntity(this.grabbedEntity); + + if (!this.triggerSmoothedGrab()) { + this.setState(STATE_OFF, "released trigger"); + return; + } + + // test for intersection between controller laser and web entity plane. + var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); + + if (Reticle.keyboardFocusEntity != this.grabbedEntity) { + Reticle.keyboardFocusEntity = this.grabbedEntity; + } + + Reticle.sendEntityTouchUpdateEvent(this.grabbedEntity, this.hand, intersectInfo.point); + + this.intersectionDistance = intersectInfo.distance; + this.searchIndicatorOn(intersectInfo.searchRay); + Reticle.setVisible(false); + }; + this.release = function() { Messages.sendLocalMessage('Hifi-Teleport-Disabler','none'); this.turnOffVisualizations(); From c00983468579621c551dd2e1d0fbd5d42d84878d Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Fri, 5 Aug 2016 17:36:27 -0700 Subject: [PATCH 058/332] Fixes for linux build of quazip --- cmake/externals/quazip/CMakeLists.txt | 2 +- interface/resources/qml/MarketplaceComboBox.qml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index d8ab492be5..f00403640a 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -17,7 +17,7 @@ ExternalProject_Add( URL https://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.7.2.zip URL_MD5 2955176048a31262c09259ca8d309d19 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=/lib -DZLIB_ROOT=${ZLIB_ROOT} + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=/lib -DZLIB_ROOT=${ZLIB_ROOT} -DCMAKE_POSITION_INDEPENDENT_CODE=ON LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 diff --git a/interface/resources/qml/MarketplaceComboBox.qml b/interface/resources/qml/MarketplaceComboBox.qml index da9a994bcc..d0c55c9fe7 100644 --- a/interface/resources/qml/MarketplaceComboBox.qml +++ b/interface/resources/qml/MarketplaceComboBox.qml @@ -39,6 +39,7 @@ Rectangle { id: switchMarketView anchors.top: parent.top anchors.right: parent.right + colorScheme: hifi.colorSchemes.dark width: 200 height: 40 visible: true From e92e69d963f4102073dc7b268eaa3755f536c78a Mon Sep 17 00:00:00 2001 From: sam Date: Sat, 6 Aug 2016 11:54:54 -0700 Subject: [PATCH 059/332] More changes --- .../src/AmbientOcclusionEffect.cpp | 55 +++++-------------- .../render-utils/src/AmbientOcclusionEffect.h | 8 +-- .../render-utils/src/SurfaceGeometryPass.cpp | 3 +- libraries/render-utils/src/ssao.slh | 27 +++++++-- .../render-utils/src/ssao_debugOcclusion.slf | 41 ++++++-------- .../render-utils/src/ssao_makeOcclusion.slf | 47 ++++------------ .../utilities/render/ambientOcclusionPass.qml | 1 + .../utilities/render/deferredLighting.qml | 1 + 8 files changed, 68 insertions(+), 115 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 9bdabbfb04..3899c1deb5 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -111,13 +111,6 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionBlurredTexture() { return _occlusionBlurredTexture; } -void AmbientOcclusionFramebuffer::setResolutionLevel(int resolutionLevel) { - if (resolutionLevel != getResolutionLevel()) { - clear(); - _resolutionLevel = resolutionLevel; - } -} - class GaussianDistribution { public: @@ -240,20 +233,11 @@ void AmbientOcclusionEffect::configure(const Config& config) { if (config.perspectiveScale != _parametersBuffer->getPerspectiveScale()) { _parametersBuffer->resolutionInfo.z = config.perspectiveScale; } - _framebuffer->setResolutionLevel(config.resolutionLevel); if (config.resolutionLevel != _parametersBuffer->getResolutionLevel()) { - _parametersBuffer->resolutionInfo.w = config.resolutionLevel; + auto& current = _parametersBuffer->resolutionInfo; + current.x = (float) config.resolutionLevel; } - const auto& resolutionLevel = config.resolutionLevel; - if (resolutionLevel != _parametersBuffer->getResolutionLevel()) { - auto& current = _parametersBuffer->resolutionInfo; - current.x = (float)resolutionLevel; - - // Communicate the change to the Framebuffer cache - // DependencyManager::get()->setAmbientOcclusionResolutionLevel(resolutionLevel); - } - if (config.blurRadius != _parametersBuffer->getBlurRadius()) { auto& current = _parametersBuffer->blurInfo; current.y = (float)config.blurRadius; @@ -355,17 +339,11 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext RenderArgs* args = renderContext->args; - // FIXME: Different render modes should have different tasks -// if (args->_renderMode != RenderArgs::DEFAULT_RENDER_MODE) { -// return; -// } - const auto frameTransform = inputs.get0(); const auto deferredFramebuffer = inputs.get1(); const auto linearDepthFramebuffer = inputs.get2(); auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); - auto normalTexture = deferredFramebuffer->getDeferredNormalTexture(); auto sourceViewport = args->_viewport; auto occlusionViewport = sourceViewport; @@ -373,10 +351,9 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext _framebuffer = std::make_shared(); } - if (_framebuffer->getResolutionLevel() > 0) { + if (_parametersBuffer->getResolutionLevel() > 0) { linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); - normalTexture = linearDepthFramebuffer->getHalfNormalTexture(); - occlusionViewport = occlusionViewport >> _framebuffer->getResolutionLevel(); + occlusionViewport = occlusionViewport >> _parametersBuffer->getResolutionLevel(); } _framebuffer->updateLinearDepth(linearDepthTexture); @@ -390,10 +367,10 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext auto framebufferSize = _framebuffer->getSourceFrameSize(); - float sMin = args->_viewport.x / (float)framebufferSize.x; - float sWidth = args->_viewport.z / (float)framebufferSize.x; - float tMin = args->_viewport.y / (float)framebufferSize.y; - float tHeight = args->_viewport.w / (float)framebufferSize.y; + float sMin = occlusionViewport.x / (float)framebufferSize.x; + float sWidth = occlusionViewport.z / (float)framebufferSize.x; + float tMin = occlusionViewport.y / (float)framebufferSize.y; + float tHeight = occlusionViewport.w / (float)framebufferSize.y; auto resolutionLevel = _parametersBuffer->getResolutionLevel(); @@ -536,10 +513,10 @@ void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, auto framebufferSize = glm::ivec2(linearDepthTexture->getDimensions()); - float sMin = args->_viewport.x / (float)framebufferSize.x; - float sWidth = args->_viewport.z / (float)framebufferSize.x; - float tMin = args->_viewport.y / (float)framebufferSize.y; - float tHeight = args->_viewport.w / (float)framebufferSize.y; + float sMin = occlusionViewport.x / (float)framebufferSize.x; + float sWidth = occlusionViewport.z / (float)framebufferSize.x; + float tMin = occlusionViewport.y / (float)framebufferSize.y; + float tHeight = occlusionViewport.w / (float)framebufferSize.y; // Running in stero ? @@ -562,19 +539,13 @@ void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, batch.setUniformBuffer(AmbientOcclusionEffect_FrameTransformSlot, frameTransform->getFrameTransformBuffer()); batch.setUniformBuffer(AmbientOcclusionEffect_ParamsSlot, ambientOcclusionUniforms); batch.setUniformBuffer(2, _parametersBuffer); - - - // We need this with the mips levels - batch.generateTextureMips(linearDepthTexture); batch.setPipeline(debugPipeline); batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, linearDepthTexture); batch.draw(gpu::TRIANGLE_STRIP, 4); - batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, nullptr); - batch.setResourceTexture(AmbientOcclusionEffect_OcclusionMapSlot, nullptr); - + batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, nullptr); }); } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 7c2c465ba3..dd0031bee7 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -34,10 +34,7 @@ public: void updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer); gpu::TexturePointer getLinearDepthTexture(); const glm::ivec2& getSourceFrameSize() const { return _frameSize; } - - void setResolutionLevel(int level); - int getResolutionLevel() const { return _resolutionLevel; } - + protected: void clear(); void allocate(); @@ -52,7 +49,6 @@ protected: glm::ivec2 _frameSize; - int _resolutionLevel{ 0 }; }; using AmbientOcclusionFramebufferPointer = std::shared_ptr; @@ -98,7 +94,7 @@ public: float blurDeviation{ 2.5f }; float numSpiralTurns{ 7.0f }; // defining an angle span to distribute the samples ray directions int numSamples{ 11 }; - int resolutionLevel{ 0 }; + int resolutionLevel{ 1 }; int blurRadius{ 3 }; // 0 means no blurring bool ditheringEnabled{ true }; // randomize the distribution of taps per pixel, should always be true bool borderingEnabled{ true }; // avoid evaluating information from non existing pixels out of the frame, should always be true diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 683fe69ff0..0f009990de 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -71,7 +71,7 @@ void LinearDepthFramebuffer::allocate() { // For Linear Depth: _linearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); - _linearDepthTexture->autoGenerateMips(1); + _linearDepthTexture->autoGenerateMips(5); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); @@ -79,6 +79,7 @@ void LinearDepthFramebuffer::allocate() { // For Downsampling: _halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _halfLinearDepthTexture->autoGenerateMips(-1); _halfNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 7efcd861c7..ae3b70095e 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -133,11 +133,11 @@ float getBlurCoef(int c) { <@func declareSamplingDisk()@> float getAngleDitheringWorldPos(in vec3 pixelWorldPos) { - vec3 worldPosFract = fract(pixelWorldPos * 0.2); + vec3 worldPosFract = fract(pixelWorldPos * 0.4); ivec3 pixelPos = ivec3(worldPosFract * 256); - return isDitheringEnabled() * (3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10 + getFrameDithering(); + return isDitheringEnabled() * ((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) + (3 * pixelPos.y ^ pixelPos.z + pixelPos.x * pixelPos.z)) * 10 + getFrameDithering(); } float getAngleDithering(in ivec2 pixelPos) { @@ -217,7 +217,8 @@ vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, if (redoTap) { tap.xy = tapPos - pixelPos; tap.z = length(tap.xy); - } + tap.z = 0; + } return tap; } @@ -231,8 +232,8 @@ vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, // the depth pyramid texture uniform sampler2D pyramidMap; -float getZEye(ivec2 pixel) { - return -texelFetch(pyramidMap, pixel, getResolutionLevel()).x; +float getZEye(ivec2 pixel, int level) { + return -texelFetch(pyramidMap, pixel, level).x; } const int LOG_MAX_OFFSET = 3; @@ -269,6 +270,22 @@ vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec3 tap, vec2 imageSize) { <@endfunc@> + +<@func declareEvalObscurance()@> + +float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) { + vec3 v = Q - C; + float vv = dot(v, v); + float vn = dot(v, n_C); + + // Fall off function as recommended in SAO paper + const float epsilon = 0.01; + float f = max(getRadius2() - vv, 0.0); + return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0); +} + +<@endfunc@> + <@func declareBlurPass(axis)@> <$declarePackOcclusionDepth()$> diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index ecb1d112b9..5db1626ab8 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -13,6 +13,7 @@ <$declareAmbientOcclusion()$> <$declareFetchDepthPyramidMap()$> <$declareSamplingDisk()$> +<$declareEvalObscurance()$> <$declarePackOcclusionDepth()$> @@ -33,32 +34,23 @@ vec2 getDebugCursorTexcoord(){ out vec4 outFragColor; -uniform sampler2D normalMap; - - -float evalAO(in vec3 C, in vec3 n_C, vec3 Q) { - vec3 v = Q - C; - float vv = dot(v, v); - float vn = dot(v, n_C); - - // Fall off function as recommended in SAO paper - const float epsilon = 0.01; - float f = max(getRadius2() - vv, 0.0); - return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0); -} - - void main(void) { + vec2 imageSize = getSideImageSize(getResolutionLevel()); + + // In debug adjust the correct frag pixel based on base resolution + vec2 fragCoord = gl_FragCoord.xy; + if (getResolutionLevel() > 0) { + fragCoord /= float (1 << getResolutionLevel()); + } + // Pixel Debugged vec2 cursorUV = getDebugCursorTexcoord(); - vec2 imageSize = getSideImageSize(getResolutionLevel()); vec2 cursorPixelPos = cursorUV * imageSize; - ivec2 ssC = ivec2(cursorPixelPos); - // Pixel being shaded - //ivec2 ssC = ivec2(gl_FragCoord.xy); + ivec2 ssC = ivec2(cursorPixelPos); + // Fetch the z under the pixel (stereo or not) - float Zeye = getZEye(ssC); + float Zeye = getZEye(ssC, 0); // Stereo side info ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel()); @@ -74,7 +66,7 @@ void main(void) { // Choose the screen-space sample radius float ssDiskRadius = evalDiskRadius(Cp.z, imageSize); - vec2 fragToCursor = cursorPixelPos - gl_FragCoord.xy; + vec2 fragToCursor = cursorPixelPos - fragCoord.xy; if (dot(fragToCursor,fragToCursor) > ssDiskRadius * ssDiskRadius) { discard; } @@ -93,7 +85,7 @@ void main(void) { for (int i = 0; i < getNumSamples(); ++i) { vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize); // The occluding point in camera space - vec2 fragToTap = vec2(ssC) + tap.xy - gl_FragCoord.xy; + vec2 fragToTap = vec2(ssC) + tap.xy - fragCoord.xy; if (dot(fragToTap,fragToTap) < keepTapRadius) { keep = true; keepedMip = evalMipFromRadius(tap.z); @@ -101,7 +93,7 @@ void main(void) { vec3 Q = getOffsetPosition(side.xyz, ssC, tap, imageSize); - sum += evalAO(Cp, Cn, Q); + sum += float(tap.z > 0.0) * evalAO(Cp, Cn, Q); } @@ -134,7 +126,8 @@ void main(void) { // outFragColor = vec4((Cn + vec3(1.0))* 0.5, 1.0); //outFragColor = vec4(vec3(ssDiskRadius / 100.0), 1.0); - if ((dot(fragToCursor,fragToCursor) < (4.0 * keepTapRadius * keepTapRadius) )) { + if ((dot(fragToCursor,fragToCursor) < (100.0 * keepTapRadius * keepTapRadius) )) { + // outFragColor = vec4(vec3(A), 1.0); outFragColor = vec4(vec3(A), 1.0); return; } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 19a6244a07..7af1feb477 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -13,41 +13,28 @@ <$declareAmbientOcclusion()$> <$declareFetchDepthPyramidMap()$> <$declareSamplingDisk()$> +<$declareEvalObscurance()$> <$declarePackOcclusionDepth()$> - out vec4 outFragColor; -uniform sampler2D normalMap; - - -float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) { - vec3 v = Q - C; - float vv = dot(v, v); - float vn = dot(v, n_C); - - // Fall off function as recommended in SAO paper - const float epsilon = 0.01; - float f = max(getRadius2() - vv, 0.0); - return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0); -} - void main(void) { - // Pixel being shaded - ivec2 ssC = ivec2(gl_FragCoord.xy); - vec2 imageSize = getSideImageSize(getResolutionLevel()); + // Pixel being shaded + vec2 fragCoord = gl_FragCoord.xy; + ivec2 ssC = ivec2(fragCoord.xy); + // Fetch the z under the pixel (stereo or not) - float Zeye = getZEye(ssC); + float Zeye = getZEye(ssC, 0); // Stereo side info ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel()); // From now on, ssC is the pixel pos in the side ssC.x -= side.y; - vec2 fragPos = (vec2(ssC) + vec2(0.5)) / vec2(getStereoSideWidth(getResolutionLevel()), getStereoSideHeight(getResolutionLevel())); + vec2 fragPos = (vec2(ssC) + vec2(0.5)) / imageSize; // The position and normal of the pixel fragment in Eye space vec3 Cp = evalEyePositionFromZeye(side.x, Zeye, fragPos); @@ -57,7 +44,7 @@ void main(void) { float ssDiskRadius = evalDiskRadius(Cp.z, imageSize); // Let's make noise - // float randomPatternRotationAngle = getAngleDithering(ssC); + //float randomPatternRotationAngle = getAngleDithering(ssC); float randomPatternRotationAngle = getAngleDitheringWorldPos(Cp); // Accumulate the Obscurance for each samples @@ -65,9 +52,9 @@ void main(void) { for (int i = 0; i < getNumSamples(); ++i) { vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, ssC, imageSize); - vec3 Q = getOffsetPosition(side.xyz, ssC, tap, imageSize); + vec3 Q = getOffsetPosition(side.xyz, ssC, tap, imageSize); - sum += evalAO(Cp, Cn, Q); + sum += float(tap.z > 0.0) * evalAO(Cp, Cn, Q); } float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); @@ -84,18 +71,4 @@ void main(void) { !> outFragColor = vec4(packOcclusionDepth(A, CSZToDephtKey(Cp.z)), 1.0); - - // KEEP IT for Debugging - // Debug Normal: outFragColor = vec4((Cn + vec3(1.0))* 0.5, 1.0); - // Debug Radius outFragColor = vec4(vec3(ssDiskRadius / 100.0), 1.0); - // Debug MaxMiplevel outFragColor = vec4(1.0 - vec3(float(clamp(findMSB(int(ssDiskRadius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL))/ float(MAX_MIP_LEVEL)), 1.0); - // Debug OffsetPosition - // float ssR; - // vec2 unitOffset = tapLocation(int(getNumSamples() - 1), 0, ssR); - // vec3 Q = getOffsetPosition(side, ssC, unitOffset, ssR * ssDiskRadius); - //outFragColor = vec4(vec3(Q.x / 10.0, Q.y / 2.0, -Q.z/ 3.0), 1.0); - // vec3 v = normalize(Q - Cp); - //outFragColor = vec4((v + vec3(1.0))* 0.5, 1.0); - // outFragColor = vec4((Cn + vec3(1.0))* 0.5, 1.0); - //outFragColor = vec4(vec3(ssDiskRadius / 100.0), 1.0); } diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index c4d86e544e..b6d54676e3 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -25,6 +25,7 @@ Column { "Level:obscuranceLevel:1.0:false", "Num Taps:numSamples:32:true", "Taps Spiral:numSpiralTurns:10.0:false", + "Falloff Bias:falloffBias:0.2:false", "Blur Radius:blurRadius:10.0:false", ] ConfigSlider { diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index 635d8b1471..4901be8e03 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -24,6 +24,7 @@ Column { "Emissive:LightingModel:enableEmissive", "Lightmap:LightingModel:enableLightmap", "Background:LightingModel:enableBackground", + "ssao:AmbientOcclusion:enabled", ] CheckBox { text: modelData.split(":")[0] From d1105b8102b48748eff21322ae647a8fc66c251a Mon Sep 17 00:00:00 2001 From: sam Date: Sun, 7 Aug 2016 18:10:03 -0700 Subject: [PATCH 060/332] Still trying --- .../src/AmbientOcclusionEffect.cpp | 24 +++++++++---------- .../render-utils/src/DebugDeferredBuffer.cpp | 18 ++++++++++++++ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 3899c1deb5..f11ec8f3f7 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -339,9 +339,9 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext RenderArgs* args = renderContext->args; - const auto frameTransform = inputs.get0(); - const auto deferredFramebuffer = inputs.get1(); - const auto linearDepthFramebuffer = inputs.get2(); + const auto& frameTransform = inputs.get0(); + const auto& deferredFramebuffer = inputs.get1(); + const auto& linearDepthFramebuffer = inputs.get2(); auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); auto sourceViewport = args->_viewport; @@ -403,13 +403,13 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext // We need this with the mips levels - batch.generateTextureMips(linearDepthTexture); + batch.generateTextureMips(_framebuffer->getLinearDepthTexture()); // Occlusion pass batch.setFramebuffer(occlusionFBO); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(1.0f)); batch.setPipeline(occlusionPipeline); - batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, linearDepthTexture); + batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, _framebuffer->getLinearDepthTexture()); batch.draw(gpu::TRIANGLE_STRIP, 4); @@ -487,10 +487,10 @@ void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, // return; // } - const auto frameTransform = inputs.get0(); - const auto deferredFramebuffer = inputs.get1(); - const auto linearDepthFramebuffer = inputs.get2(); - const auto ambientOcclusionUniforms = inputs.get3(); + const auto& frameTransform = inputs.get0(); + const auto& deferredFramebuffer = inputs.get1(); + const auto& linearDepthFramebuffer = inputs.get2(); + const auto& ambientOcclusionUniforms = inputs.get3(); // Skip if AO is not started yet if (!ambientOcclusionUniforms._buffer) { @@ -498,16 +498,14 @@ void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, } auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); - auto normalTexture = deferredFramebuffer->getDeferredNormalTexture(); auto sourceViewport = args->_viewport; auto occlusionViewport = sourceViewport; auto resolutionLevel = ambientOcclusionUniforms->getResolutionLevel(); if (resolutionLevel > 0) { - linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); - normalTexture = linearDepthFramebuffer->getHalfNormalTexture(); - occlusionViewport = occlusionViewport >> ambientOcclusionUniforms->getResolutionLevel(); + linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); + occlusionViewport = occlusionViewport >> ambientOcclusionUniforms->getResolutionLevel(); } diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index ed4eba972f..86f97d57a6 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -423,5 +423,23 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren const glm::vec2 bottomLeft(_size.x, _size.y); const glm::vec2 topRight(_size.z, _size.w); geometryBuffer->renderQuad(batch, bottomLeft, topRight, color); + + + batch.setResourceTexture(Albedo, nullptr); + batch.setResourceTexture(Normal, nullptr); + batch.setResourceTexture(Specular, nullptr); + batch.setResourceTexture(Depth, nullptr); + batch.setResourceTexture(Lighting, nullptr); + batch.setResourceTexture(Shadow, nullptr); + batch.setResourceTexture(LinearDepth, nullptr); + batch.setResourceTexture(HalfLinearDepth, nullptr); + batch.setResourceTexture(HalfNormal, nullptr); + + batch.setResourceTexture(Curvature, nullptr); + batch.setResourceTexture(DiffusedCurvature, nullptr); + + batch.setResourceTexture(AmbientOcclusion, nullptr); + batch.setResourceTexture(AmbientOcclusionBlurred, nullptr); + }); } From e6c74b29c7a3913c0ba0f59d738f0fa8b2811d35 Mon Sep 17 00:00:00 2001 From: sam Date: Sun, 7 Aug 2016 19:48:23 -0700 Subject: [PATCH 061/332] WTF... --- .../src/AmbientOcclusionEffect.cpp | 6 ++- .../render-utils/src/DebugDeferredBuffer.cpp | 38 +++++++++++------- .../render-utils/src/RenderDeferredTask.cpp | 6 +-- .../render-utils/src/SurfaceGeometryPass.cpp | 5 ++- .../utilities/render/ambientOcclusionPass.qml | 39 ++++++++++++------- 5 files changed, 61 insertions(+), 33 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index f11ec8f3f7..39a9d99458 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -403,7 +403,7 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext // We need this with the mips levels - batch.generateTextureMips(_framebuffer->getLinearDepthTexture()); + // batch.generateTextureMips(_framebuffer->getLinearDepthTexture()); // Occlusion pass batch.setFramebuffer(occlusionFBO); @@ -480,6 +480,10 @@ void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); + if (!_showCursorPixel) { + return; + } + RenderArgs* args = renderContext->args; // FIXME: Different render modes should have different tasks diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 86f97d57a6..c45ab8c0e2 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -403,22 +403,30 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren batch.setPipeline(getPipeline(_mode, first)); - batch.setResourceTexture(Albedo, deferredFramebuffer->getDeferredColorTexture()); - batch.setResourceTexture(Normal, deferredFramebuffer->getDeferredNormalTexture()); - batch.setResourceTexture(Specular, deferredFramebuffer->getDeferredSpecularTexture()); - batch.setResourceTexture(Depth, deferredFramebuffer->getPrimaryDepthTexture()); - batch.setResourceTexture(Lighting, deferredFramebuffer->getLightingTexture()); - batch.setResourceTexture(Shadow, lightStage.lights[0]->shadow.framebuffer->getDepthStencilBuffer()); - batch.setResourceTexture(LinearDepth, linearDepthTarget->getLinearDepthTexture()); - batch.setResourceTexture(HalfLinearDepth, linearDepthTarget->getHalfLinearDepthTexture()); - batch.setResourceTexture(HalfNormal, linearDepthTarget->getHalfNormalTexture()); - - batch.setResourceTexture(Curvature, surfaceGeometryFramebuffer->getCurvatureTexture()); - batch.setResourceTexture(DiffusedCurvature, surfaceGeometryFramebuffer->getLowCurvatureTexture()); - - batch.setResourceTexture(AmbientOcclusion, ambientOcclusionFramebuffer->getOcclusionTexture()); - batch.setResourceTexture(AmbientOcclusionBlurred, ambientOcclusionFramebuffer->getOcclusionBlurredTexture()); + if (deferredFramebuffer) { + batch.setResourceTexture(Albedo, deferredFramebuffer->getDeferredColorTexture()); + batch.setResourceTexture(Normal, deferredFramebuffer->getDeferredNormalTexture()); + batch.setResourceTexture(Specular, deferredFramebuffer->getDeferredSpecularTexture()); + batch.setResourceTexture(Depth, deferredFramebuffer->getPrimaryDepthTexture()); + batch.setResourceTexture(Lighting, deferredFramebuffer->getLightingTexture()); + } + if (!lightStage.lights.empty()) { + batch.setResourceTexture(Shadow, lightStage.lights[0]->shadow.framebuffer->getDepthStencilBuffer()); + } + if (linearDepthTarget) { + batch.setResourceTexture(LinearDepth, linearDepthTarget->getLinearDepthTexture()); + batch.setResourceTexture(HalfLinearDepth, linearDepthTarget->getHalfLinearDepthTexture()); + batch.setResourceTexture(HalfNormal, linearDepthTarget->getHalfNormalTexture()); + } + if (surfaceGeometryFramebuffer) { + batch.setResourceTexture(Curvature, surfaceGeometryFramebuffer->getCurvatureTexture()); + batch.setResourceTexture(DiffusedCurvature, surfaceGeometryFramebuffer->getLowCurvatureTexture()); + } + if (ambientOcclusionFramebuffer) { + batch.setResourceTexture(AmbientOcclusion, ambientOcclusionFramebuffer->getOcclusionTexture()); + batch.setResourceTexture(AmbientOcclusionBlurred, ambientOcclusionFramebuffer->getOcclusionBlurredTexture()); + } const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); const glm::vec2 bottomLeft(_size.x, _size.y); const glm::vec2 topRight(_size.z, _size.w); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 7ea81eb706..7be78890e9 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -124,9 +124,9 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { const auto linearDepthPassInputs = LinearDepthPass::Inputs(deferredFrameTransform, deferredFramebuffer).hasVarying(); const auto linearDepthPassOutputs = addJob("LinearDepth", linearDepthPassInputs); const auto linearDepthTarget = linearDepthPassOutputs.getN(0); - const auto linearDepthTexture = linearDepthPassOutputs.getN(2); - const auto halfLinearDepthTexture = linearDepthPassOutputs.getN(3); - const auto halfNormalTexture = linearDepthPassOutputs.getN(4); + // const auto linearDepthTexture = linearDepthPassOutputs.getN(2); + // const auto halfLinearDepthTexture = linearDepthPassOutputs.getN(3); + // const auto halfNormalTexture = linearDepthPassOutputs.getN(4); // Curvature pass diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 0f009990de..46158e30b3 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -61,6 +61,9 @@ void LinearDepthFramebuffer::updatePrimaryDepth(const gpu::TexturePointer& depth void LinearDepthFramebuffer::clear() { _linearDepthFramebuffer.reset(); _linearDepthTexture.reset(); + _downsampleFramebuffer.reset(); + _halfLinearDepthTexture.reset(); + _halfNormalTexture.reset(); } void LinearDepthFramebuffer::allocate() { @@ -79,7 +82,7 @@ void LinearDepthFramebuffer::allocate() { // For Downsampling: _halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); - _halfLinearDepthTexture->autoGenerateMips(-1); + _halfLinearDepthTexture->autoGenerateMips(5); _halfNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index b6d54676e3..89883ab399 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -38,21 +38,34 @@ Column { } } } - - Column { - Repeater { - model: [ - "resolutionLevel:resolutionLevel", - "ditheringEnabled:ditheringEnabled", - "borderingEnabled:borderingEnabled", - "fetchMipsEnabled:fetchMipsEnabled", - ] - CheckBox { - text: qsTr(modelData.split(":")[0]) - checked: Render.getConfig("AmbientOcclusion")[modelData.split(":")[1]] - onCheckedChanged: { Render.getConfig("AmbientOcclusion")[modelData.split(":")[1]] = checked } + Row{ + Column { + Repeater { + model: [ + "resolutionLevel:resolutionLevel", + "ditheringEnabled:ditheringEnabled", + "borderingEnabled:borderingEnabled", + "fetchMipsEnabled:fetchMipsEnabled" + ] + CheckBox { + text: qsTr(modelData.split(":")[0]) + checked: Render.getConfig("AmbientOcclusion")[modelData.split(":")[1]] + onCheckedChanged: { Render.getConfig("AmbientOcclusion")[modelData.split(":")[1]] = checked } + } } } + Column { + Repeater { + model: [ + "debugEnabled:showCursorPixel" + ] + CheckBox { + text: qsTr(modelData.split(":")[0]) + checked: Render.getConfig("DebugAmbientOcclusion")[modelData.split(":")[1]] + onCheckedChanged: { Render.getConfig("DebugAmbientOcclusion")[modelData.split(":")[1]] = checked } + } + } + } } PlotPerf { From db8bf78dd186fac8fe0077a274dab83cc10f42b9 Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 8 Aug 2016 02:28:25 -0700 Subject: [PATCH 062/332] Good state --- .../src/AmbientOcclusionEffect.cpp | 4 +- .../render-utils/src/AmbientOcclusionEffect.h | 4 +- .../render-utils/src/RenderDeferredTask.cpp | 17 +++----- .../render-utils/src/SurfaceGeometryPass.cpp | 2 +- libraries/render-utils/src/ssao.slh | 43 +++++++++++++------ .../render-utils/src/ssao_debugOcclusion.slf | 12 ++++-- .../render-utils/src/ssao_makeOcclusion.slf | 9 ++-- .../utilities/render/ambientOcclusionPass.qml | 4 +- .../render/debugAmbientOcclusionPass.js | 2 +- 9 files changed, 57 insertions(+), 40 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 39a9d99458..b17db5796d 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -375,7 +375,7 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext auto resolutionLevel = _parametersBuffer->getResolutionLevel(); - //_parametersBuffer.edit()._ditheringInfo.y += 0.25f; + _parametersBuffer->ditheringInfo.y += 0.25f; // Running in stero ? bool isStereo = args->_context->isStereo(); @@ -403,7 +403,7 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext // We need this with the mips levels - // batch.generateTextureMips(_framebuffer->getLinearDepthTexture()); + batch.generateTextureMips(_framebuffer->getLinearDepthTexture()); // Occlusion pass batch.setFramebuffer(occlusionFBO); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index dd0031bee7..5ec0060d84 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -94,8 +94,8 @@ public: float blurDeviation{ 2.5f }; float numSpiralTurns{ 7.0f }; // defining an angle span to distribute the samples ray directions int numSamples{ 11 }; - int resolutionLevel{ 1 }; - int blurRadius{ 3 }; // 0 means no blurring + int resolutionLevel{ 0 }; + int blurRadius{ 4 }; // 0 means no blurring bool ditheringEnabled{ true }; // randomize the distribution of taps per pixel, should always be true bool borderingEnabled{ true }; // avoid evaluating information from non existing pixels out of the frame, should always be true bool fetchMipsEnabled{ true }; // fetch taps in sub mips to otpimize cache, should always be true diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 7be78890e9..cedc2ef45e 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -124,10 +124,6 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { const auto linearDepthPassInputs = LinearDepthPass::Inputs(deferredFrameTransform, deferredFramebuffer).hasVarying(); const auto linearDepthPassOutputs = addJob("LinearDepth", linearDepthPassInputs); const auto linearDepthTarget = linearDepthPassOutputs.getN(0); - // const auto linearDepthTexture = linearDepthPassOutputs.getN(2); - // const auto halfLinearDepthTexture = linearDepthPassOutputs.getN(3); - // const auto halfNormalTexture = linearDepthPassOutputs.getN(4); - // Curvature pass const auto surfaceGeometryPassInputs = SurfaceGeometryPass::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget).hasVarying(); @@ -150,8 +146,8 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { addJob("DrawLight", lights); const auto deferredLightingInputs = RenderDeferred::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, - surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource).hasVarying(); - + surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource).hasVarying(); + // DeferredBuffer is complete, now let's shade it into the LightingBuffer addJob("RenderDeferred", deferredLightingInputs); @@ -178,17 +174,16 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { // Debugging stages { - // Debugging Deferred buffer job - const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); - addJob("DebugDeferredBuffer", debugFramebuffers); - + // Debugging Deferred buffer job + const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); + addJob("DebugDeferredBuffer", debugFramebuffers); + addJob("DebugScattering", deferredLightingInputs); const auto debugAmbientOcclusionInputs = DebugAmbientOcclusion::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget, ambientOcclusionUniforms).hasVarying(); addJob("DebugAmbientOcclusion", debugAmbientOcclusionInputs); - // Scene Octree Debuging job { addJob("DrawSceneOctree", spatialSelection); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 46158e30b3..0ee4e6af4a 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -74,7 +74,7 @@ void LinearDepthFramebuffer::allocate() { // For Linear Depth: _linearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); - _linearDepthTexture->autoGenerateMips(5); + // _linearDepthTexture->autoGenerateMips(5); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index ae3b70095e..fe4b77577f 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -133,7 +133,7 @@ float getBlurCoef(int c) { <@func declareSamplingDisk()@> float getAngleDitheringWorldPos(in vec3 pixelWorldPos) { - vec3 worldPosFract = fract(pixelWorldPos * 0.4); + vec3 worldPosFract = fract(pixelWorldPos * 1.0); ivec3 pixelPos = ivec3(worldPosFract * 256); @@ -240,34 +240,49 @@ const int LOG_MAX_OFFSET = 3; const int MAX_MIP_LEVEL = 5; int evalMipFromRadius(float radius) { // mipLevel = floor(log(ssR / MAX_OFFSET)); - return doFetchMips() * clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); + return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); } -vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec3 tap, vec2 imageSize) { - int mipLevel = evalMipFromRadius(tap.z); + +vec3 fetchTapUnfiltered(ivec3 side, ivec2 ssC, vec3 tap, vec2 imageSize) { + ivec2 ssP = ivec2(tap.xy) + ssC; + ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); + + vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize; + + vec3 P; + P.xy = tapUV; + P.z = -texture(pyramidMap, tapUV).x; + + return P; +} + +vec3 fetchTap(ivec3 side, ivec2 ssC, vec3 tap, vec2 imageSize) { + int mipLevel = evalMipFromRadius(tap.z * doFetchMips()); ivec2 ssP = ivec2(tap.xy) + ssC; ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. // Manually clamp to the texture size because texelFetch bypasses the texture unit - // ivec2 mipSize = textureSize(pyramidMap, mipLevel); - // ivec2 mipSize = max(ivec2(imageSize) >> mipLevel, ivec2(1)); + // ivec2 mipSize = textureSize(pyramidMap, mipLevel); + ivec2 mipSize = max(ivec2(imageSize) >> mipLevel, ivec2(1)); - // ivec2 mipP = clamp(ssPFull >> mipLevel, ivec2(0), mipSize - ivec2(1)); + ivec2 mipP = clamp(ssPFull >> mipLevel, ivec2(0), mipSize - ivec2(1)); - vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize; - // vec2 tapUV = (vec2(mipP) + vec2(0.5)) / vec2(mipSize); + // vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize; + vec2 tapUV = (vec2(mipP) + vec2(0.5)) / vec2(mipSize); vec3 P; - // P.z = -texelFetch(pyramidMap, mipP, mipLevel).r; - P.z = -textureLod(pyramidMap, tapUV, float(mipLevel)).r; + P.xy = tapUV; + P.z = -texelFetch(pyramidMap, mipP, mipLevel).x; + // P.z = -textureLod(pyramidMap, tapUV, float(mipLevel)).x; - // Offset to pixel center - P = evalEyePositionFromZeye(side.x, P.z, tapUV); - return P; + return P; } + + <@endfunc@> diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index 5db1626ab8..077b7e68b9 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -72,8 +72,9 @@ void main(void) { } // Let's make noise - // float randomPatternRotationAngle = getAngleDithering(ssC); - float randomPatternRotationAngle = getAngleDitheringWorldPos(Cp); + //float randomPatternRotationAngle = getAngleDithering(ssC); + vec3 wCp = (getViewInverse() * vec4(Cp, 1.0)).xyz; + float randomPatternRotationAngle = getAngleDitheringWorldPos(wCp); // Accumulate the Obscurance for each samples @@ -84,14 +85,17 @@ void main(void) { for (int i = 0; i < getNumSamples(); ++i) { vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize); + // The occluding point in camera space vec2 fragToTap = vec2(ssC) + tap.xy - fragCoord.xy; if (dot(fragToTap,fragToTap) < keepTapRadius) { keep = true; - keepedMip = evalMipFromRadius(tap.z); + keepedMip = evalMipFromRadius(tap.z * doFetchMips()); } - vec3 Q = getOffsetPosition(side.xyz, ssC, tap, imageSize); + vec3 tapUVZ = fetchTap(side.xyz, ssC, tap, imageSize); + + vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); sum += float(tap.z > 0.0) * evalAO(Cp, Cn, Q); } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 7af1feb477..4e90160d23 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -44,15 +44,18 @@ void main(void) { float ssDiskRadius = evalDiskRadius(Cp.z, imageSize); // Let's make noise - //float randomPatternRotationAngle = getAngleDithering(ssC); - float randomPatternRotationAngle = getAngleDitheringWorldPos(Cp); + // float randomPatternRotationAngle = getAngleDithering(ssC); + vec3 wCp = (getViewInverse() * vec4(Cp, 1.0)).xyz; + float randomPatternRotationAngle = getAngleDitheringWorldPos(wCp); // Accumulate the Obscurance for each samples float sum = 0.0; for (int i = 0; i < getNumSamples(); ++i) { vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, ssC, imageSize); - vec3 Q = getOffsetPosition(side.xyz, ssC, tap, imageSize); + vec3 tapUVZ = fetchTap(side.xyz, ssC, tap, imageSize); + + vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); sum += float(tap.z > 0.0) * evalAO(Cp, Cn, Q); } diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 89883ab399..402aba015b 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -44,8 +44,8 @@ Column { model: [ "resolutionLevel:resolutionLevel", "ditheringEnabled:ditheringEnabled", - "borderingEnabled:borderingEnabled", - "fetchMipsEnabled:fetchMipsEnabled" + "fetchMipsEnabled:fetchMipsEnabled", + "borderingEnabled:borderingEnabled" ] CheckBox { text: qsTr(modelData.split(":")[0]) diff --git a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js index 60fb8bf918..c57fdf0526 100644 --- a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js +++ b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js @@ -13,7 +13,7 @@ var qml = Script.resolvePath('ambientOcclusionPass.qml'); var window = new OverlayWindow({ title: 'Ambient Occlusion Pass', source: qml, - width: 400, height: 200, + width: 400, height: 250, }); window.setPosition(Window.innerWidth - 420, 50 + 550 + 50); window.closed.connect(function() { Script.stop(); }); From c8f3a898d25824291982c52261de3a4cddfaecfd Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 5 Aug 2016 10:51:51 -0700 Subject: [PATCH 063/332] First cut of a snapshot uploader Will need to test end-to-end shortly, etc... --- interface/src/Application.cpp | 5 ++ interface/src/Application.h | 1 + .../scripting/WindowScriptingInterface.cpp | 8 +- .../src/scripting/WindowScriptingInterface.h | 2 + interface/src/ui/Snapshot.cpp | 88 +++++++++++++++++++ interface/src/ui/Snapshot.h | 11 +++ scripts/system/snapshot.js | 13 ++- 7 files changed, 126 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index baddc26301..5be03a5e4f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5082,6 +5082,11 @@ void Application::takeSnapshot(bool notify) { emit DependencyManager::get()->snapshotTaken(path, notify); } +void Application::shareSnapshot(const QString& path) { + // not much to do here, everything is done in snapshot code... + Snapshot::uploadSnapshot(path); +} + float Application::getRenderResolutionScale() const { if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) { return 1.0f; diff --git a/interface/src/Application.h b/interface/src/Application.h index ea6a117cfa..aa3244c53a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -249,6 +249,7 @@ public: float getAverageSimsPerSecond() const { return _simCounter.rate(); } void takeSnapshot(bool notify); + void shareSnapshot(const QString& filename); signals: void svoImportRequested(const QString& url); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 026818ec82..9ce0b5bca5 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -202,7 +202,13 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) { void WindowScriptingInterface::takeSnapshot(bool notify) { // only evil-doers call takeSnapshot from a random thread - qApp->postLambdaEvent([&] { + qApp->postLambdaEvent([notify] { qApp->takeSnapshot(notify); }); } + +void WindowScriptingInterface::shareSnapshot(const QString& path) { + qApp->postLambdaEvent([path] { + qApp->shareSnapshot(path); + }); +} diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 1abcb9db35..24f2a619ce 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -55,12 +55,14 @@ public slots: QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); void copyToClipboard(const QString& text); void takeSnapshot(bool notify); + void shareSnapshot(const QString& path); signals: void domainChanged(const QString& domainHostname); void svoImportRequested(const QString& url); void domainConnectionRefused(const QString& reasonMessage, int reasonCode); void snapshotTaken(const QString& path, bool notify); + void snapshotShared(bool success); private slots: WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height); diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index aaf11d14a4..42ee4fa2f5 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -32,6 +32,7 @@ #include "Application.h" #include "Snapshot.h" +#include "scripting/WindowScriptingInterface.h" // filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg // %1 <= username, %2 <= date and time, %3 <= current location @@ -141,3 +142,90 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) { return imageTempFile; } } + +void Snapshot::uploadSnapshot(const QString& filename) { + + const QString SNAPSHOT_UPLOAD_URL = "/api/v1/snapshots"; + static SnapshotUploader uploader; + + qDebug() << "uploading snapshot " << filename; + + QFile* file = new QFile(filename); + Q_ASSERT(file->exists()); + file->open(QIODevice::ReadOnly); + + QHttpPart imagePart; + imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"image\"; filename=\"" + file->fileName() + "\"")); + imagePart.setBodyDevice(file); + + QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart + multiPart->append(imagePart); + + auto accountManager = DependencyManager::get(); + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = &uploader; + callbackParams.jsonCallbackMethod = "uploadSuccess"; + callbackParams.errorCallbackReceiver = &uploader; + callbackParams.errorCallbackMethod = "uploadFailure"; + + accountManager->sendRequest(SNAPSHOT_UPLOAD_URL, + AccountManagerAuth::Required, + QNetworkAccessManager::PostOperation, + callbackParams, + nullptr, + multiPart); +} + +void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { + const QString STORY_UPLOAD_URL = "/api/v1/user_stories"; + static SnapshotUploader uploader; + + // parse the reply for the thumbnail_url + QByteArray contents = reply.readAll(); + QJsonParseError jsonError; + auto doc = QJsonDocument::fromJson(contents, &jsonError); + if (jsonError.error == QJsonParseError::NoError) { + QString thumbnail_url = doc.object().value("thumbnail_url").toString(); + + // create json post data + QJsonObject rootObject; + QJsonObject userStoryObject; + userStoryObject.insert("thumbnail_url", thumbnail_url); + userStoryObject.insert("action", "snapshot"); + rootObject.insert("user_story", userStoryObject); + + auto accountManager = DependencyManager::get(); + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = &uploader; + callbackParams.jsonCallbackMethod = "createStorySuccess"; + callbackParams.errorCallbackReceiver = &uploader; + callbackParams.errorCallbackMethod = "createStoryFailure"; + + accountManager->sendRequest(STORY_UPLOAD_URL, + AccountManagerAuth::Required, + QNetworkAccessManager::PostOperation, + callbackParams, + QJsonDocument(rootObject).toJson()); + + } else { + qDebug() << "Error parsing upload response: " << jsonError.errorString(); + emit DependencyManager::get()->snapshotShared(false); + } +} + +void SnapshotUploader::uploadFailure(QNetworkReply& reply) { + // TODO: parse response, potentially helpful for logging (?) + emit DependencyManager::get()->snapshotShared(false); +} + +void SnapshotUploader::createStorySuccess(QNetworkReply& reply) { + emit DependencyManager::get()->snapshotShared(true); +} + +void SnapshotUploader::createStoryFailure(QNetworkReply& reply) { + // TODO: parse response, potentially helpful for logging (?) + emit DependencyManager::get()->snapshotShared(false); +} diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 2e7986a5c0..e780537b76 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -32,6 +32,16 @@ private: QUrl _URL; }; + +class SnapshotUploader: public QObject { + Q_OBJECT +public slots: + void uploadSuccess(QNetworkReply& reply); + void uploadFailure(QNetworkReply& reply); + void createStorySuccess(QNetworkReply& reply); + void createStoryFailure(QNetworkReply& reply); +}; + class Snapshot { public: static QString saveSnapshot(QImage image); @@ -40,6 +50,7 @@ public: static Setting::Handle snapshotsLocation; static Setting::Handle hasSetSnapshotsLocation; + static void uploadSnapshot(const QString& filename); private: static QFile* savedFileForSnapshot(QImage & image, bool isTemporary); }; diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 92ce36e935..8a6b811085 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -24,7 +24,18 @@ function confirmShare(data) { if (!Window.confirm("Share snapshot " + data.localPath + "?")) { // This dialog will be more elaborate... return; } - Window.alert("Pretending to upload. That code will go here."); + Window.snapshotShared.connect(snapshotShared); + Window.shareSnapshot(data.localPath); +} + +function snapshotShared(success) { + if(success) { + // for now just print status + print('snapshot uploaded and shared'); + } else { + // for now just print an error. + print('snapshot upload/share failed'); + } } function onClicked() { From 4d3dce449f7cb36a3f49f90af749a41aeed59a1d Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 8 Aug 2016 17:28:04 -0700 Subject: [PATCH 064/332] Better maybe --- .../src/AmbientOcclusionEffect.cpp | 48 +++++++++---------- .../render-utils/src/AmbientOcclusionEffect.h | 4 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index b17db5796d..21605292ca 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -234,8 +234,8 @@ void AmbientOcclusionEffect::configure(const Config& config) { _parametersBuffer->resolutionInfo.z = config.perspectiveScale; } if (config.resolutionLevel != _parametersBuffer->getResolutionLevel()) { - auto& current = _parametersBuffer->resolutionInfo; - current.x = (float) config.resolutionLevel; + auto& current = _parametersBuffer->resolutionInfo; + current.x = (float) config.resolutionLevel; } if (config.blurRadius != _parametersBuffer->getBlurRadius()) { @@ -351,9 +351,9 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext _framebuffer = std::make_shared(); } - if (_parametersBuffer->getResolutionLevel() > 0) { + if (_parametersBuffer->getResolutionLevel() > 0) { linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); - occlusionViewport = occlusionViewport >> _parametersBuffer->getResolutionLevel(); + occlusionViewport = occlusionViewport >> _parametersBuffer->getResolutionLevel(); } _framebuffer->updateLinearDepth(linearDepthTexture); @@ -367,15 +367,15 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext auto framebufferSize = _framebuffer->getSourceFrameSize(); - float sMin = occlusionViewport.x / (float)framebufferSize.x; - float sWidth = occlusionViewport.z / (float)framebufferSize.x; - float tMin = occlusionViewport.y / (float)framebufferSize.y; - float tHeight = occlusionViewport.w / (float)framebufferSize.y; + float sMin = occlusionViewport.x / (float)framebufferSize.x; + float sWidth = occlusionViewport.z / (float)framebufferSize.x; + float tMin = occlusionViewport.y / (float)framebufferSize.y; + float tHeight = occlusionViewport.w / (float)framebufferSize.y; auto resolutionLevel = _parametersBuffer->getResolutionLevel(); - _parametersBuffer->ditheringInfo.y += 0.25f; + //_parametersBuffer->ditheringInfo.y += 0.25f; // Running in stero ? bool isStereo = args->_context->isStereo(); @@ -403,13 +403,13 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext // We need this with the mips levels - batch.generateTextureMips(_framebuffer->getLinearDepthTexture()); + batch.generateTextureMips(_framebuffer->getLinearDepthTexture()); // Occlusion pass batch.setFramebuffer(occlusionFBO); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(1.0f)); batch.setPipeline(occlusionPipeline); - batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, _framebuffer->getLinearDepthTexture()); + batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, _framebuffer->getLinearDepthTexture()); batch.draw(gpu::TRIANGLE_STRIP, 4); @@ -480,9 +480,9 @@ void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); - if (!_showCursorPixel) { - return; - } + if (!_showCursorPixel) { + return; + } RenderArgs* args = renderContext->args; @@ -496,10 +496,10 @@ void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const auto& linearDepthFramebuffer = inputs.get2(); const auto& ambientOcclusionUniforms = inputs.get3(); - // Skip if AO is not started yet - if (!ambientOcclusionUniforms._buffer) { - return; - } + // Skip if AO is not started yet + if (!ambientOcclusionUniforms._buffer) { + return; + } auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); auto sourceViewport = args->_viewport; @@ -508,17 +508,17 @@ void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, auto resolutionLevel = ambientOcclusionUniforms->getResolutionLevel(); if (resolutionLevel > 0) { - linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); - occlusionViewport = occlusionViewport >> ambientOcclusionUniforms->getResolutionLevel(); + linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); + occlusionViewport = occlusionViewport >> ambientOcclusionUniforms->getResolutionLevel(); } auto framebufferSize = glm::ivec2(linearDepthTexture->getDimensions()); - float sMin = occlusionViewport.x / (float)framebufferSize.x; - float sWidth = occlusionViewport.z / (float)framebufferSize.x; - float tMin = occlusionViewport.y / (float)framebufferSize.y; - float tHeight = occlusionViewport.w / (float)framebufferSize.y; + float sMin = occlusionViewport.x / (float)framebufferSize.x; + float sWidth = occlusionViewport.z / (float)framebufferSize.x; + float tMin = occlusionViewport.y / (float)framebufferSize.y; + float tHeight = occlusionViewport.w / (float)framebufferSize.y; // Running in stero ? diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 5ec0060d84..5fdea81979 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -93,8 +93,8 @@ public: float edgeSharpness{ 1.0f }; float blurDeviation{ 2.5f }; float numSpiralTurns{ 7.0f }; // defining an angle span to distribute the samples ray directions - int numSamples{ 11 }; - int resolutionLevel{ 0 }; + int numSamples{ 9 }; + int resolutionLevel{ 1 }; int blurRadius{ 4 }; // 0 means no blurring bool ditheringEnabled{ true }; // randomize the distribution of taps per pixel, should always be true bool borderingEnabled{ true }; // avoid evaluating information from non existing pixels out of the frame, should always be true From f34030d276421e3e48be7afa6f837c04a09df994 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 9 Aug 2016 12:48:01 -0700 Subject: [PATCH 065/332] factor out metaverseBase to make it easier to repoint to a test server --- interface/resources/qml/AddressBarDialog.qml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 5883872c95..8590121fa0 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -55,6 +55,7 @@ Window { property var allStories: []; property int cardWidth: 200; property int cardHeight: 152; + property string metaverseBase: "https://metaverse.highfidelity.com/api/v1/"; function pastTime(timestamp) { // Answer a descriptive string timestamp = new Date(timestamp); var then = timestamp.getTime(), @@ -292,7 +293,7 @@ Window { } function getPlace(placeData, cb) { // cb(error, side-effected-placeData), after adding path, thumbnails, and description - var url = 'https://metaverse.highfidelity.com/api/v1/places/' + placeData.place_name; + var url = metaverseBase + 'places/' + placeData.place_name; getRequest(url, function (error, data) { if (handleError(url, error, data, cb)) { return; @@ -361,7 +362,7 @@ Window { 'sort_order=desc', 'page=' + pageNumber ]; - var url = 'https://metaverse.highfidelity.com/api/v1/domains/all?' + params.join('&'); + var url = metaverseBase + 'domains/all?' + params.join('&'); getRequest(url, function (error, data) { if (handleError(url, error, data, cb)) { return; @@ -389,7 +390,7 @@ Window { }); } function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model - var url = 'https://metaverse.highfidelity.com/api/v1/user_stories?page=' + pageNumber; + var url = metaverseBase + 'user_stories?page=' + pageNumber; getRequest(url, function (error, data) { if (handleError(url, error, data, cb)) { return; From 8457acd234c0ffd52ec7fbc781a9c33304de510c Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 9 Aug 2016 13:42:48 -0700 Subject: [PATCH 066/332] First guess on how to deal with place Fall back to host when there is no place. Probably better ideas, like specify the name and the type (a place, a domain if no place, etc...) will come soon. --- interface/src/ui/Snapshot.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 42ee4fa2f5..71e7d1a703 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -188,12 +188,17 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { QJsonParseError jsonError; auto doc = QJsonDocument::fromJson(contents, &jsonError); if (jsonError.error == QJsonParseError::NoError) { - QString thumbnail_url = doc.object().value("thumbnail_url").toString(); + QString thumbnailUrl = doc.object().value("thumbnail_url").toString(); + QString placeName = DependencyManager::get()->getPlaceName(); + if(placeName.isEmpty()) { + placeName = DependencyManager::get()->getHost(); + } // create json post data QJsonObject rootObject; QJsonObject userStoryObject; - userStoryObject.insert("thumbnail_url", thumbnail_url); + userStoryObject.insert("thumbnail_url", thumbnailUrl); + userStoryObject.insert("place_name", placeName); userStoryObject.insert("action", "snapshot"); rootObject.insert("user_story", userStoryObject); From 18990821df2f209bcc62441a1cf7424ad69ccac3 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 9 Aug 2016 14:15:49 -0700 Subject: [PATCH 067/332] Added 'path' to user stories, works now. yay --- interface/src/ui/Snapshot.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 71e7d1a703..e15182934b 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -189,16 +189,19 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { auto doc = QJsonDocument::fromJson(contents, &jsonError); if (jsonError.error == QJsonParseError::NoError) { QString thumbnailUrl = doc.object().value("thumbnail_url").toString(); - QString placeName = DependencyManager::get()->getPlaceName(); + auto addressManager = DependencyManager::get(); + QString placeName = addressManager->getPlaceName(); if(placeName.isEmpty()) { - placeName = DependencyManager::get()->getHost(); + placeName = addressManager->getHost(); } + QString currentPath = addressManager->currentPath(true); // create json post data QJsonObject rootObject; QJsonObject userStoryObject; userStoryObject.insert("thumbnail_url", thumbnailUrl); userStoryObject.insert("place_name", placeName); + userStoryObject.insert("path", currentPath); userStoryObject.insert("action", "snapshot"); rootObject.insert("user_story", userStoryObject); From d4f376879d38011e1cca5d4029404b19848e09d4 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 9 Aug 2016 15:05:23 -0700 Subject: [PATCH 068/332] PR feedback --- interface/src/ui/Snapshot.cpp | 17 +++-------------- scripts/system/snapshot.js | 2 +- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index e15182934b..f6bd8e2db9 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -148,8 +148,6 @@ void Snapshot::uploadSnapshot(const QString& filename) { const QString SNAPSHOT_UPLOAD_URL = "/api/v1/snapshots"; static SnapshotUploader uploader; - qDebug() << "uploading snapshot " << filename; - QFile* file = new QFile(filename); Q_ASSERT(file->exists()); file->open(QIODevice::ReadOnly); @@ -165,11 +163,7 @@ void Snapshot::uploadSnapshot(const QString& filename) { multiPart->append(imagePart); auto accountManager = DependencyManager::get(); - JSONCallbackParameters callbackParams; - callbackParams.jsonCallbackReceiver = &uploader; - callbackParams.jsonCallbackMethod = "uploadSuccess"; - callbackParams.errorCallbackReceiver = &uploader; - callbackParams.errorCallbackMethod = "uploadFailure"; + JSONCallbackParameters callbackParams(&uploader, "uploadSuccess", &uploader, "uploadFailure"); accountManager->sendRequest(SNAPSHOT_UPLOAD_URL, AccountManagerAuth::Required, @@ -191,7 +185,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { QString thumbnailUrl = doc.object().value("thumbnail_url").toString(); auto addressManager = DependencyManager::get(); QString placeName = addressManager->getPlaceName(); - if(placeName.isEmpty()) { + if (placeName.isEmpty()) { placeName = addressManager->getHost(); } QString currentPath = addressManager->currentPath(true); @@ -206,11 +200,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { rootObject.insert("user_story", userStoryObject); auto accountManager = DependencyManager::get(); - JSONCallbackParameters callbackParams; - callbackParams.jsonCallbackReceiver = &uploader; - callbackParams.jsonCallbackMethod = "createStorySuccess"; - callbackParams.errorCallbackReceiver = &uploader; - callbackParams.errorCallbackMethod = "createStoryFailure"; + JSONCallbackParameters callbackParams(&uploader, "createStorySuccess", &uploader, "createStoryFailure"); accountManager->sendRequest(STORY_UPLOAD_URL, AccountManagerAuth::Required, @@ -219,7 +209,6 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { QJsonDocument(rootObject).toJson()); } else { - qDebug() << "Error parsing upload response: " << jsonError.errorString(); emit DependencyManager::get()->snapshotShared(false); } } diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 8a6b811085..ac92a06749 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -29,7 +29,7 @@ function confirmShare(data) { } function snapshotShared(success) { - if(success) { + if (success) { // for now just print status print('snapshot uploaded and shared'); } else { From 5715fd00f312877395d0feeb2ddc898b8f4b8403 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 9 Aug 2016 16:42:41 -0700 Subject: [PATCH 069/332] proper share dialog --- scripts/system/html/ShareSnapshot.html | 78 ++++++++++++++++++++++++++ scripts/system/snapshot.js | 28 +++++++-- 2 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 scripts/system/html/ShareSnapshot.html diff --git a/scripts/system/html/ShareSnapshot.html b/scripts/system/html/ShareSnapshot.html new file mode 100644 index 0000000000..4691b7488c --- /dev/null +++ b/scripts/system/html/ShareSnapshot.html @@ -0,0 +1,78 @@ + + + Share + + + + + + + + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ + diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 61345254be..84019e68cc 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -21,11 +21,27 @@ var button = toolBar.addButton({ }); function confirmShare(data) { - if (!Window.confirm("Share snapshot " + data.localPath + "?")) { // This dialog will be more elaborate... - return; + var dialog = new OverlayWebWindow('Snapshot', Script.resolvePath("html/ShareSnapshot.html"), 800, 300); + function onMessage(message) { + if (message == 'ready') { // The window can now receive data from us. + dialog.emitScriptEvent(data); // Send it. + return; + } // Rest is confirmation processing + dialog.webEventReceived.disconnect(onMessage); // I'm not certain that this is necessary. If it is, what do we do on normal close? + dialog.close(); + dialog.deleteLater(); + message = message.filter(function (picture) { return picture.share; }); + if (message.length) { + print('sharing', message.map(function (picture) { return picture.localPath; }).join(', ')); + message.forEach(function (data) { + Window.shareSnapshot(data.localPath); + }); + } else { + print('declined to share', JSON.stringify(data)); + } } - Window.snapshotShared.connect(snapshotShared); - Window.shareSnapshot(data.localPath); + dialog.webEventReceived.connect(onMessage); + dialog.raise(); } function snapshotShared(success) { @@ -74,12 +90,14 @@ function resetButtons(path, notify) { button.writeProperty("defaultState", 1); button.writeProperty("hoverState", 3); Window.snapshotTaken.disconnect(resetButtons); - confirmShare({localPath: path}); + confirmShare([{localPath: path}]); } button.clicked.connect(onClicked); +Window.snapshotShared.connect(snapshotShared); Script.scriptEnding.connect(function () { toolBar.removeButton("snapshot"); button.clicked.disconnect(onClicked); + Window.snapshotShared.disconnect(snapshotShared); }); From b8f5a9d22f79239f9c643847fb8ba523417fd011 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Wed, 10 Aug 2016 10:26:58 -0700 Subject: [PATCH 070/332] CR changes --- interface/resources/qml/Browser.qml | 6 +-- .../resources/qml/MarketplaceComboBox.qml | 42 +++++++++++++++++++ interface/resources/qml/QmlWindow.qml | 2 +- .../resources/qml/controls-uit/WebView.qml | 38 ----------------- 4 files changed, 46 insertions(+), 42 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 2be0ca176d..3693b9dfea 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -218,9 +218,9 @@ ScrollingWindow { onIconChanged: { console.log("New icon: " + icon) } - Component.onCompleted: { - desktop.initWebviewProfileHandlers(webview.profile) - } + Component.onCompleted: { + desktop.initWebviewProfileHandlers(webview.profile) + } //profile: desktop.browserProfile diff --git a/interface/resources/qml/MarketplaceComboBox.qml b/interface/resources/qml/MarketplaceComboBox.qml index d0c55c9fe7..e7981cabec 100644 --- a/interface/resources/qml/MarketplaceComboBox.qml +++ b/interface/resources/qml/MarketplaceComboBox.qml @@ -20,12 +20,14 @@ import "controls-uit" as Controls import "styles" import "styles-uit" + Rectangle { HifiConstants { id: hifi } id: marketplaceComboBox anchors.fill: parent color: hifi.colors.baseGrayShadow property var currentUrl: "https://metaverse.highfidelity.com/marketplace" + Controls.WebView { id: webview url: currentUrl @@ -33,6 +35,46 @@ Rectangle { width: parent.width height: parent.height - 40 focus: true + + Timer { + id: zipTimer + running: false + repeat: false + interval: 1500 + property var handler; + onTriggered: handler(); + } + + property var autoCancel: 'var element = $("a.btn.cancel"); + element.click();' + + onNewViewRequested: { + console.log("new view requested url"); + var component = Qt.createComponent("Browser.qml"); + var newWindow = component.createObject(desktop); + request.openIn(newWindow.webView); + if (File.testUrl(desktop.currentUrl)) { + zipTimer.handler = function() { + newWindow.destroy(); + runJavaScript(autoCancel); + } + zipTimer.start(); + } + } + + property var simpleDownload: 'var element = $("a.download-file"); + element.removeClass("download-file"); + element.removeAttr("download");' + + onLinkHovered: { + desktop.currentUrl = hoveredUrl + console.log("my url in WebView: " + desktop.currentUrl) + if (File.testUrl(desktop.currentUrl)) { + runJavaScript(simpleDownload, function(){console.log("ran the JS");}); + } + + } + } Controls.ComboBox { diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml index 908c1b1a77..ac6ae31c7f 100644 --- a/interface/resources/qml/QmlWindow.qml +++ b/interface/resources/qml/QmlWindow.qml @@ -1,5 +1,5 @@ -import QtQuick 2.5 +import QtQuick 2.3 import QtQuick.Controls 1.4 import QtWebChannel 1.0 import QtWebEngine 1.1 diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index bb08e5e389..8069ee79fb 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -62,44 +62,6 @@ WebEngineView { } } - Timer { - id: zipTimer - running: false - repeat: false - interval: 1500 - property var handler; - onTriggered: handler(); - } - - property var autoCancel: 'var element = $("a.btn.cancel"); - element.click();' - - onNewViewRequested: { - console.log("new view requested url"); - var component = Qt.createComponent("../Browser.qml"); - var newWindow = component.createObject(desktop); - request.openIn(newWindow.webView); - if (File.testUrl(desktop.currentUrl)) { - zipTimer.handler = function() { - newWindow.destroy(); - runJavaScript(autoCancel); - } - zipTimer.start(); - } - } - - property var simpleDownload: 'var element = $("a.download-file"); - element.removeClass("download-file"); - element.removeAttr("download");' - - onLinkHovered: { - desktop.currentUrl = hoveredUrl - console.log("my url in WebView: " + desktop.currentUrl) - if (File.testUrl(desktop.currentUrl)) { - runJavaScript(simpleDownload, function(){console.log("ran the JS");}); - } - - } // This breaks the webchannel used for passing messages. Fixed in Qt 5.6 // See https://bugreports.qt.io/browse/QTBUG-49521 From f8f99721c3fdd606885db7285c592fbc6ca4e3b0 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 10 Aug 2016 14:25:00 -0700 Subject: [PATCH 071/332] Minor update to add details And, now reflects data-web's slightly updated json structure for returning info on an uploaded snapshot. --- interface/src/ui/Snapshot.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index f6bd8e2db9..beb86af1c4 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -182,7 +182,9 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { QJsonParseError jsonError; auto doc = QJsonDocument::fromJson(contents, &jsonError); if (jsonError.error == QJsonParseError::NoError) { - QString thumbnailUrl = doc.object().value("thumbnail_url").toString(); + auto dataObject = doc.object().value("data").toObject(); + QString thumbnailUrl = dataObject.value("thumbnail_url").toString(); + QString imageUrl = dataObject.value("image_url").toString(); auto addressManager = DependencyManager::get(); QString placeName = addressManager->getPlaceName(); if (placeName.isEmpty()) { @@ -193,6 +195,9 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { // create json post data QJsonObject rootObject; QJsonObject userStoryObject; + QJsonObject detailsObject; + detailsObject.insert("image_url", imageUrl); + userStoryObject.insert("details", detailsObject); userStoryObject.insert("thumbnail_url", thumbnailUrl); userStoryObject.insert("place_name", placeName); userStoryObject.insert("path", currentPath); From 5c15b520404ed1afb3e66d02bbd28803c5426719 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 10 Aug 2016 14:54:29 -0700 Subject: [PATCH 072/332] expose getPlaceName() as placename Nice for scripts to get the placename. Nice for us, anyways. --- libraries/networking/src/AddressManager.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 2e9f177137..0315d749c4 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -38,6 +38,7 @@ class AddressManager : public QObject, public Dependency { Q_PROPERTY(QString protocol READ getProtocol) Q_PROPERTY(QString hostname READ getHost) Q_PROPERTY(QString pathname READ currentPath) + Q_PROPERTY(QString placename READ getPlaceName) public: Q_INVOKABLE QString protocolVersion(); using PositionGetter = std::function; From 47dea0ea2b60ae1f06b940e5cbf30892e99da94f Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 10 Aug 2016 17:09:43 -0700 Subject: [PATCH 073/332] Hide sharing if not logged in, or in an accessible place The grand future will have option to login, and so on... --- scripts/system/html/ShareSnapshot.html | 25 ++++++++++++++++++++----- scripts/system/snapshot.js | 8 +++++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/scripts/system/html/ShareSnapshot.html b/scripts/system/html/ShareSnapshot.html index 4691b7488c..d838a01e99 100644 --- a/scripts/system/html/ShareSnapshot.html +++ b/scripts/system/html/ShareSnapshot.html @@ -28,16 +28,29 @@ paths.push(data); } + function handleShareButtons(canShare) { + if (!canShare) { + // hide the share/do not share buttons + document.getElementById("sharing").innerHTML = "

You can share if you are logged in and in a public place"; + } + } window.onload = function () { // Something like the following will allow testing in a browser. //addImage({localPath: 'c:/Users/howar/OneDrive/Pictures/hifi-snap-by--on-2016-07-27_12-58-43.jpg'}); openEventBridge(function () { // Set up a handler for receiving the data, and tell the .js we are ready to receive it. EventBridge.scriptEventReceived.connect(function (message) { + // last element of list contains a bool for whether or not we can share stuff + var canShare = message.pop().canShare; + print("canShare:"+canShare+", message:" + message.toString()) + handleShareButtons(canShare); + + // rest are image paths which we add message.forEach(addImage); }); EventBridge.emitWebEvent('ready'); }); + }; // beware of bug: Cannot send objects at top level. (Nested in arrays is fine.) shareSelected = function() { @@ -64,11 +77,13 @@

-
- -
-
- +
+
+ +
+
+ +
diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 84019e68cc..0acb5d5acf 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -77,6 +77,10 @@ function onClicked() { }, SNAPSHOT_DELAY); } +function canShare() { + return Account.isLoggedIn() && Boolean(Window.location.placename); +} + function resetButtons(path, notify) { // show overlays if they were on if (resetOverlays) { @@ -90,7 +94,9 @@ function resetButtons(path, notify) { button.writeProperty("defaultState", 1); button.writeProperty("hoverState", 3); Window.snapshotTaken.disconnect(resetButtons); - confirmShare([{localPath: path}]); + + // last element in data array tells dialog whether we can share or not + confirmShare([ { localPath: path }, { canShare: canShare() } ]); } button.clicked.connect(onClicked); From c1edd008a4f77eeb26fff9f7f95b7dc727d032cc Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 11 Aug 2016 11:47:55 -0700 Subject: [PATCH 074/332] PR feedback --- scripts/system/html/ShareSnapshot.html | 25 +++++++++++++++---------- scripts/system/snapshot.js | 9 ++++----- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/scripts/system/html/ShareSnapshot.html b/scripts/system/html/ShareSnapshot.html index d838a01e99..d41da0fb7a 100644 --- a/scripts/system/html/ShareSnapshot.html +++ b/scripts/system/html/ShareSnapshot.html @@ -28,10 +28,16 @@ paths.push(data); } - function handleShareButtons(canShare) { - if (!canShare) { - // hide the share/do not share buttons - document.getElementById("sharing").innerHTML = "

You can share if you are logged in and in a public place"; + function handleShareButtons(shareMsg) { + if (!shareMsg.canShare) { + // this means you may or may not be logged in, but can't share + // because you are not in a public place. + document.getElementById("sharing").innerHTML = "

You can share if you are in a public place"; + } else if (!shareMsg.isLoggedIn) { + // this means you are in a public place, but can't share because + // you need to login. Soon we will just bring up the share dialog + // in this case (and maybe set a boolean here to inform the html) + document.getElementById("sharing").innerHTML = "

You can share if you are logged in"; } } window.onload = function () { @@ -41,9 +47,8 @@ // Set up a handler for receiving the data, and tell the .js we are ready to receive it. EventBridge.scriptEventReceived.connect(function (message) { // last element of list contains a bool for whether or not we can share stuff - var canShare = message.pop().canShare; - print("canShare:"+canShare+", message:" + message.toString()) - handleShareButtons(canShare); + var shareMsg = message.pop(); + handleShareButtons(shareMsg); // rest are image paths which we add message.forEach(addImage); @@ -74,10 +79,10 @@

-
- -
+
+ +
diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 0acb5d5acf..4c0ba37ab3 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -77,10 +77,6 @@ function onClicked() { }, SNAPSHOT_DELAY); } -function canShare() { - return Account.isLoggedIn() && Boolean(Window.location.placename); -} - function resetButtons(path, notify) { // show overlays if they were on if (resetOverlays) { @@ -96,7 +92,10 @@ function resetButtons(path, notify) { Window.snapshotTaken.disconnect(resetButtons); // last element in data array tells dialog whether we can share or not - confirmShare([ { localPath: path }, { canShare: canShare() } ]); + confirmShare([ + { localPath: path }, + { canShare: Boolean(Window.location.placename), isLoggedIn: Account.isLoggedIn() } + ]); } button.clicked.connect(onClicked); From 1ddbd7022a0b3229dff32fb44b6e7766c93063ab Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 11 Aug 2016 11:52:04 -0700 Subject: [PATCH 075/332] Moved keyboardFocus, mouse/touch event methods from Reticle to Entities --- .../src/display-plugins/CompositorHelper.cpp | 34 ------------------- .../src/display-plugins/CompositorHelper.h | 11 ------ .../entities/src/EntityScriptingInterface.cpp | 34 +++++++++++++++++++ .../entities/src/EntityScriptingInterface.h | 16 ++++++++- .../system/controllers/handControllerGrab.js | 16 ++++----- 5 files changed, 57 insertions(+), 54 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index 3a154af462..56be8e1cf9 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -451,37 +451,3 @@ QVariant ReticleInterface::getPosition() const { void ReticleInterface::setPosition(QVariant position) { _compositor->setReticlePosition(vec2FromVariant(position)); } - -QUuid ReticleInterface::getKeyboardFocusEntity() const { - QUuid result; - QMetaObject::invokeMethod(qApp, "getKeyboardFocusEntity", Qt::DirectConnection, Q_RETURN_ARG(QUuid, result)); - return result; -} - -void ReticleInterface::setKeyboardFocusEntity(QUuid id) { - QMetaObject::invokeMethod(qApp, "setKeyboardFocusEntity", Qt::QueuedConnection, Q_ARG(QUuid, id)); -} - -void ReticleInterface::sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityMouseMoveEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); -} - -void ReticleInterface::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseDownEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); -} - -void ReticleInterface::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseUpEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); -} - -void ReticleInterface::sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityTouchUpdateEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); -} - -void ReticleInterface::sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityTouchBeginEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); -} - -void ReticleInterface::sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityTouchEndEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); -} diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h index 78d4a10bf0..b0b96d86be 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -181,7 +181,6 @@ class ReticleInterface : public QObject { Q_PROPERTY(bool mouseCaptured READ isMouseCaptured) Q_PROPERTY(bool allowMouseCapture READ getAllowMouseCapture WRITE setAllowMouseCapture) Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay) - Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity) public: ReticleInterface(CompositorHelper* outer) : QObject(outer), _compositor(outer) {} @@ -204,16 +203,6 @@ public: Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); } - Q_INVOKABLE QUuid getKeyboardFocusEntity() const; - Q_INVOKABLE void setKeyboardFocusEntity(QUuid id); - Q_INVOKABLE void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); - - Q_INVOKABLE void sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); - private: CompositorHelper* _compositor; }; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 653b37c3bb..35852e5acb 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1184,6 +1184,40 @@ QVector EntityScriptingInterface::getChildrenIDsOfJoint(const QUuid& pare return result; } +QUuid EntityScriptingInterface::getKeyboardFocusEntity() const { + QUuid result; + QMetaObject::invokeMethod(qApp, "getKeyboardFocusEntity", Qt::DirectConnection, Q_RETURN_ARG(QUuid, result)); + return result; +} + +void EntityScriptingInterface::setKeyboardFocusEntity(QUuid id) { + QMetaObject::invokeMethod(qApp, "setKeyboardFocusEntity", Qt::QueuedConnection, Q_ARG(QUuid, id)); +} + +void EntityScriptingInterface::sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityMouseMoveEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +} + +void EntityScriptingInterface::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseDownEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +} + +void EntityScriptingInterface::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseUpEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +} + +void EntityScriptingInterface::sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityTouchUpdateEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +} + +void EntityScriptingInterface::sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityTouchBeginEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +} + +void EntityScriptingInterface::sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityTouchEndEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +} + float EntityScriptingInterface::calculateCost(float mass, float oldVelocity, float newVelocity) { return std::abs(mass * (newVelocity - oldVelocity)); } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 5aa0f5907e..b98d872f74 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -59,9 +59,11 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra /// handles scripting of Entity commands from JS passed to assigned clients class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency { Q_OBJECT - + Q_PROPERTY(float currentAvatarEnergy READ getCurrentAvatarEnergy WRITE setCurrentAvatarEnergy) Q_PROPERTY(float costMultiplier READ getCostMultiplier WRITE setCostMultiplier) + Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity) + public: EntityScriptingInterface(bool bidOnSimulationOwnership); @@ -176,6 +178,18 @@ public slots: Q_INVOKABLE QVector getChildrenIDs(const QUuid& parentID); Q_INVOKABLE QVector getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex); + Q_INVOKABLE QUuid getKeyboardFocusEntity() const; + Q_INVOKABLE void setKeyboardFocusEntity(QUuid id); + + Q_INVOKABLE void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); + + Q_INVOKABLE void sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + + signals: void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 72c26c5ef1..96482c509d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1392,8 +1392,8 @@ function MyController(hand) { entity = rayPickInfo.entityID; name = entityPropertiesCache.getProps(entity).name; - if (Reticle.keyboardFocusEntity != entity) { - Reticle.keyboardFocusEntity = entity; + if (Entities.keyboardFocusEntity != entity) { + Entities.keyboardFocusEntity = entity; } // send mouse events for button highlights and tooltips. @@ -1401,7 +1401,7 @@ function MyController(hand) { this.getOtherHandController().state !== STATE_SEARCHING && this.getOtherHandController().state !== STATE_ENTITY_TOUCHING)) { // most recently searching hand has priority over other hand, for the purposes of button highlighting. - Reticle.sendEntityMouseMoveEvent(entity, rayPickInfo.intersection); + Entities.sendEntityMouseMoveEvent(entity, rayPickInfo.intersection); } if (this.triggerSmoothedGrab() && !isEditing()) { @@ -2054,13 +2054,13 @@ function MyController(hand) { this.entityTouchingEnter = function() { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); - Reticle.sendEntityTouchBeginEvent(this.grabbedEntity, this.hand, intersectInfo.point); + Entities.sendEntityTouchBeginEvent(this.grabbedEntity, this.hand, intersectInfo.point); }; this.entityTouchingExit = function() { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); - Reticle.sendEntityTouchEndEvent(this.grabbedEntity, this.hand, intersectInfo.point); + Entities.sendEntityTouchEndEvent(this.grabbedEntity, this.hand, intersectInfo.point); }; this.entityTouching = function() { @@ -2074,11 +2074,11 @@ function MyController(hand) { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); - if (Reticle.keyboardFocusEntity != this.grabbedEntity) { - Reticle.keyboardFocusEntity = this.grabbedEntity; + if (Entities.keyboardFocusEntity != this.grabbedEntity) { + Entities.keyboardFocusEntity = this.grabbedEntity; } - Reticle.sendEntityTouchUpdateEvent(this.grabbedEntity, this.hand, intersectInfo.point); + Entities.sendEntityTouchUpdateEvent(this.grabbedEntity, this.hand, intersectInfo.point); this.intersectionDistance = intersectInfo.distance; this.searchIndicatorOn(intersectInfo.searchRay); From 4c150efc1155960b702cf3e89806159ed6d3ae02 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 11 Aug 2016 11:55:08 -0700 Subject: [PATCH 076/332] Categorize some debug logging in FBX parsing --- libraries/fbx/src/FBXReader_Material.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp index eb25f1d8a2..8c0f4b34ac 100644 --- a/libraries/fbx/src/FBXReader_Material.cpp +++ b/libraries/fbx/src/FBXReader_Material.cpp @@ -9,7 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "FBXReader.h" + #include +#include + #include #include #include @@ -20,9 +24,8 @@ #include #include #include -#include "FBXReader.h" -#include +#include "ModelFormatLogging.h" bool FBXMaterial::needTangentSpace() const { return !normalTexture.isNull(); @@ -258,11 +261,11 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { } } } - qDebug() << " fbx material Name:" << material.name; + qCDebug(modelformat) << " fbx material Name:" << material.name; if (materialMap.contains(material.name)) { QJsonObject materialOptions = materialMap.value(material.name).toObject(); - qDebug() << "Mapping fbx material:" << material.name << " with HifiMaterial: " << materialOptions; + qCDebug(modelformat) << "Mapping fbx material:" << material.name << " with HifiMaterial: " << materialOptions; if (materialOptions.contains("scattering")) { float scattering = (float) materialOptions.value("scattering").toDouble(); From e3aae0e93f0e2a2d55bbbd6055efaf6c313e9a1b Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 11 Aug 2016 16:23:01 -0700 Subject: [PATCH 077/332] Fix up layout, and adapt to recent changes in file structure --- scripts/system/html/ShareSnapshot.html | 71 +++++++++++++++++++------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/scripts/system/html/ShareSnapshot.html b/scripts/system/html/ShareSnapshot.html index 4691b7488c..fd865cfb7a 100644 --- a/scripts/system/html/ShareSnapshot.html +++ b/scripts/system/html/ShareSnapshot.html @@ -1,9 +1,48 @@ Share - + + - + - + -
-
-
+
+
+
-
+
-
+
-
+
-
+
+
-
From 747c43d0656d0ec33050d3079ab2c55c490e18a0 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 11 Aug 2016 16:43:29 -0700 Subject: [PATCH 078/332] basic user story card --- interface/resources/qml/AddressBarDialog.qml | 63 ++++++++------ .../resources/qml/controls-uit/Button.qml | 2 + interface/resources/qml/hifi/Card.qml | 51 ++++++++++-- .../resources/qml/hifi/UserStoryCard.qml | 83 +++++++++++++++++++ 4 files changed, 167 insertions(+), 32 deletions(-) create mode 100644 interface/resources/qml/hifi/UserStoryCard.qml diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8590121fa0..a65a228cf8 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -47,7 +47,16 @@ Window { } function goCard(card) { - addressLine.text = card.path; + if (useFeed) { + storyCard.imageUrl = card.imageUrl; //"http://howard-stearns.github.io/models/images/dancing-avatars.jpg"; + storyCard.userName = card.userName; + storyCard.placeName = card.placeName; + storyCard.actionPhrase = card.actionPhrase; + storyCard.timePhrase = card.timePhrase; + storyCard.visible = true; + return; + } + addressLine.text = card.hifiUrl; toggleOrGo(true); } property bool useFeed: false; @@ -56,26 +65,6 @@ Window { property int cardWidth: 200; property int cardHeight: 152; property string metaverseBase: "https://metaverse.highfidelity.com/api/v1/"; - function pastTime(timestamp) { // Answer a descriptive string - timestamp = new Date(timestamp); - var then = timestamp.getTime(), - now = Date.now(), - since = now - then, - ONE_MINUTE = 1000 * 60, - ONE_HOUR = ONE_MINUTE * 60, - hours = since / ONE_HOUR, - minutes = (hours % 1) * 60; - if (hours > 24) { - return timestamp.toDateString(); - } - if (hours > 1) { - return Math.floor(hours).toString() + ' hr ' + Math.floor(minutes) + ' min ago'; - } - if (minutes >= 2) { - return Math.floor(minutes).toString() + ' min ago'; - } - return 'about a minute ago'; - } AddressBarDialog { id: addressBarDialog @@ -88,6 +77,7 @@ Window { ListModel { id: suggestions } ListView { + id: scroll width: (3 * cardWidth) + (2 * hifi.layout.spacing); height: cardHeight; spacing: hifi.layout.spacing; @@ -103,10 +93,14 @@ Window { width: cardWidth; height: cardHeight; goFunction: goCard; - path: model.place_name + model.path; + userName: model.username; + placeName: model.place_name; + hifiUrl: model.place_name + model.path; + imageUrl: model.thumbnail_url; // This is wrong, but it will have to wait. thumbnail: model.thumbnail_url; - placeText: model.created_at ? "" : model.place_name; - usersText: model.created_at ? pastTime(model.created_at) : (model.online_users + ((model.online_users === 1) ? ' person' : ' people')); + action: model.action; + timestamp: model.created_at; + onlineUsers: model.online_users; hoverThunk: function () { ListView.view.currentIndex = index; } unhoverThunk: function () { ListView.view.currentIndex = -1; } } @@ -209,8 +203,24 @@ Window { } } } + + UserStoryCard { + id: storyCard; + visible: false; + visitPlace: function (hifiUrl) { + storyCard.visible = false; + addressLine.text = hifiUrl; + toggleOrGo(true); + }; + anchors { + verticalCenter: scroll.verticalCenter; + horizontalCenter: scroll.horizontalCenter; + verticalCenterOffset: 50; + } + } } + function toggleFeed () { useFeed = !useFeed; placesButton.buttonState = useFeed ? 0 : 1; @@ -318,13 +328,15 @@ Window { description = data.description || ""; return { place_name: name, + username: data.username || "", path: data.path || "", created_at: data.created_at || "", + action: data.action || "", thumbnail_url: data.thumbnail_url || "", tags: tags, description: description, - online_users: data.online_users, + online_users: data.online_users || 0, searchText: [name].concat(tags, description).join(' ').toUpperCase() } @@ -402,7 +414,6 @@ Window { {created_at: "8/3/2016", action: "snapshot", path: "/10077.4,4003.6,9972.56/0,-0.410351,0,0.911928", place_name: "Ventura", thumbnail_url:"https://hifi-metaverse.s3-us-west-1.amazonaws.com/images/places/previews/1f5/e6b/00-/thumbnail/hifi-place-1f5e6b00-2bf0-4319-b9ae-a2344a72354c.png?1454321596"} ]; } - var stories = data.user_stories.map(function (story) { // explicit single-argument function return makeModelData(story); }); diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index 298943b551..59f8a63238 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -21,6 +21,8 @@ Original.Button { width: 120 height: hifi.dimensions.controlLineHeight + HifiConstants { id: hifi } + style: ButtonStyle { background: Rectangle { diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 5fae628283..278f9330d3 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -17,17 +17,54 @@ import QtGraphicalEffects 1.0 import "../styles-uit" Rectangle { + property string userName: ""; + property string placeName: ""; + property string action: ""; + property string timestamp: ""; + property string hifiUrl: ""; + property string thumbnail: defaultThumbnail; + property string imageUrl: ""; property var goFunction: null; - property alias image: lobby; - property alias placeText: place.text; - property alias usersText: users.text; + + property string timePhrase: pastTime(timestamp); + property string actionPhrase: makeActionPhrase(action); + property int onlineUsers: 0; + property bool isUserStory: userName && !onlineUsers; + property int textPadding: 20; property int textSize: 24; property int textSizeSmall: 18; property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif"); - property string thumbnail: defaultThumbnail; - property string path: ""; HifiConstants { id: hifi } + function pastTime(timestamp) { // Answer a descriptive string + timestamp = new Date(timestamp); + var then = timestamp.getTime(), + now = Date.now(), + since = now - then, + ONE_MINUTE = 1000 * 60, + ONE_HOUR = ONE_MINUTE * 60, + hours = since / ONE_HOUR, + minutes = (hours % 1) * 60; + if (hours > 24) { + return timestamp.toDateString(); + } + if (hours > 1) { + return Math.floor(hours).toString() + ' hr ' + Math.floor(minutes) + ' min ago'; + } + if (minutes >= 2) { + return Math.floor(minutes).toString() + ' min ago'; + } + return 'about a minute ago'; + } + function makeActionPhrase(actionLabel) { + switch (actionLabel) { + case "snapshot": + return "took a snapshot"; + default: + return "unknown" + } + } + Image { id: lobby; width: parent.width; @@ -39,7 +76,7 @@ Rectangle { anchors.left: parent.left; onStatusChanged: { if (status == Image.Error) { - console.log("source: " + source + ": failed to load " + path); + console.log("source: " + source + ": failed to load " + hfiUrl); source = defaultThumbnail; } } @@ -71,6 +108,7 @@ Rectangle { } RalewaySemiBold { id: place; + text: isUserStory ? "" : placeName; color: hifi.colors.white; size: textSize; anchors { @@ -81,6 +119,7 @@ Rectangle { } RalewayRegular { id: users; + text: isUserStory ? timePhrase : (onlineUsers + ((onlineUsers === 1) ? ' person' : ' people')); size: textSizeSmall; color: hifi.colors.white; anchors { diff --git a/interface/resources/qml/hifi/UserStoryCard.qml b/interface/resources/qml/hifi/UserStoryCard.qml new file mode 100644 index 0000000000..4b6c37dae8 --- /dev/null +++ b/interface/resources/qml/hifi/UserStoryCard.qml @@ -0,0 +1,83 @@ +// +// UserStoryCard.qml +// qml/hifi +// +// Displays a clickable card representing a user story or destination. +// +// Created by Howard Stearns on 8/11/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 +import QtQuick 2.5 +import "../styles-uit" as HifiStyles +import "../controls-uit" as HifiControls + +Rectangle { + id: storyCard; + width: 500; + height: 330; + property string userName: "User"; + property string placeName: "Home"; + property string actionPhrase: "did something"; + property string timePhrase: ""; + property string hifiUrl: storyCard.placeName; + property string imageUrl: Qt.resolvedUrl("../images/default-domain.gif"); + property var visitPlace: function (ignore) { }; + color: "white"; + HifiStyles.HifiConstants { id: otherHifi } + MouseArea { + anchors.fill: parent; + acceptedButtons: Qt.LeftButton; + onClicked: storyCard.visible = false; + hoverEnabled: true; + // The content of the storyCard has buttons. For these to work without being + // blanketed by the MouseArea, they need to be children of the MouseArea. + Image { + id: storyImage; + source: storyCard.imageUrl; + width: storyCard.width - 100; + height: storyImage.width / 1.91; + fillMode: Image.PreserveAspectCrop; + anchors { + horizontalCenter: parent.horizontalCenter; + top: parent.top; + topMargin: 20; + } + } + HifiStyles.RalewayRegular { + id: storyLabel; + text: storyCard.userName + " " + storyCard.actionPhrase + " in " + storyCard.placeName + size: 20; + color: "black" + anchors { + horizontalCenter: storyImage.horizontalCenter; + top: storyImage.bottom; + topMargin: hifi.layout.spacing + } + } + HifiStyles.RalewayRegular { + text: storyCard.timePhrase; + size: 15; + color: "slategrey" + anchors { + verticalCenter: visitButton.verticalCenter; + left: storyImage.left; + } + } + HifiControls.Button { + id: visitButton; + text: "visit " + storyCard.placeName; + color: otherHifi.buttons.blue; + onClicked: visitPlace(storyCard.hifiUrl); + anchors { + top: storyLabel.bottom; + topMargin: hifi.layout.spacing; + right: storyImage.right; + } + } + } +} From 211da3c428da40c66684adf74fcb58cfcacd08e6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 11 Aug 2016 16:49:29 -0700 Subject: [PATCH 079/332] Add more user activity tracking data --- interface/src/Application.cpp | 21 +++++++++++++++++++ .../entities/src/EntityScriptingInterface.cpp | 14 +++++++++++++ .../entities/src/EntityScriptingInterface.h | 12 +++++++++++ 3 files changed, 47 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3d519d1790..c4750c69df 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1145,10 +1145,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : static int SEND_STATS_INTERVAL_MS = 10000; static int NEARBY_AVATAR_RADIUS_METERS = 10; + static glm::vec3 lastAvatarPosition = getMyAvatar()->getPosition(); + static glm::mat4 lastHMDHeadPose = getHMDSensorPose(); + // Periodically send fps as a user activity event QTimer* sendStatsTimer = new QTimer(this); sendStatsTimer->setInterval(SEND_STATS_INTERVAL_MS); connect(sendStatsTimer, &QTimer::timeout, this, [this]() { + QJsonObject properties = {}; MemoryInfo memInfo; if (getMemoryInfo(memInfo)) { @@ -1190,6 +1194,23 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false; + glm::vec3 avatarPosition = getMyAvatar()->getPosition(); + properties["avatar_has_moved"] = lastAvatarPosition != avatarPosition; + lastAvatarPosition = avatarPosition; + + auto entityScriptingInterface = DependencyManager::get(); + auto entityActivityTracking = entityScriptingInterface->getActivityTracking(); + entityScriptingInterface->resetActivityTracking(); + properties["added_entity"] = entityActivityTracking.hasAddedEntity; + properties["deleted_entity"] = entityActivityTracking.hasDeletedEntity; + properties["edited_entity"] = entityActivityTracking.hasEditedEntity; + + auto hmdHeadPose = getHMDSensorPose(); + properties["hmd_head_pose_changed"] = isHMDMode() && (hmdHeadPose != lastHMDHeadPose); + lastHMDHeadPose = hmdHeadPose; + + properties["hand_pose_changed"] = false; + UserActivityLogger::getInstance().logAction("stats", properties); }); sendStatsTimer->start(); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 653b37c3bb..031847b3f3 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -40,6 +40,12 @@ void EntityScriptingInterface::queueEntityMessage(PacketType packetType, getEntityPacketSender()->queueEditEntityMessage(packetType, _entityTree, entityID, properties); } +void EntityScriptingInterface::resetActivityTracking() { + _activityTracking.hasAddedEntity = false; + _activityTracking.hasDeletedEntity = false; + _activityTracking.hasEditedEntity = false; +} + bool EntityScriptingInterface::canAdjustLocks() { auto nodeList = DependencyManager::get(); return nodeList->isAllowedEditor(); @@ -130,6 +136,8 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) { + _activityTracking.hasAddedEntity = true; + EntityItemProperties propertiesWithSimID = convertLocationFromScriptSemantics(properties); propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); @@ -200,6 +208,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& shapeType, bool dynamic, const glm::vec3& position, const glm::vec3& gravity) { + _activityTracking.hasAddedEntity = true; + EntityItemProperties properties; properties.setType(EntityTypes::Model); properties.setName(name); @@ -263,6 +273,8 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit } QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) { + _activityTracking.hasEditedEntity = true; + EntityItemProperties properties = scriptSideProperties; auto dimensions = properties.getDimensions(); @@ -406,6 +418,8 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } void EntityScriptingInterface::deleteEntity(QUuid id) { + _activityTracking.hasDeletedEntity = true; + EntityItemID entityID(id); bool shouldDelete = true; diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 5aa0f5907e..bde0974d7c 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -65,6 +65,13 @@ class EntityScriptingInterface : public OctreeScriptingInterface, public Depende public: EntityScriptingInterface(bool bidOnSimulationOwnership); + class ActivityTracking { + public: + bool hasAddedEntity { false }; + bool hasDeletedEntity { false }; + bool hasEditedEntity { false }; + }; + EntityEditPacketSender* getEntityPacketSender() const { return (EntityEditPacketSender*)getPacketSender(); } virtual NodeType_t getServerNodeType() const { return NodeType::EntityServer; } virtual OctreeEditPacketSender* createPacketSender() { return new EntityEditPacketSender(); } @@ -73,6 +80,9 @@ public: EntityTreePointer getEntityTree() { return _entityTree; } void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine); float calculateCost(float mass, float oldVelocity, float newVelocity); + + void resetActivityTracking(); + ActivityTracking getActivityTracking() const { return _activityTracking; } public slots: // returns true if the DomainServer will allow this Node/Avatar to make changes @@ -226,6 +236,8 @@ private: float _currentAvatarEnergy = { FLT_MAX }; float getCurrentAvatarEnergy() { return _currentAvatarEnergy; } void setCurrentAvatarEnergy(float energy); + + ActivityTracking _activityTracking; float costMultiplier = { 0.01f }; float getCostMultiplier(); From cd9bd8dddfed767b57d7042085fb2cad45d4b658 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 11 Aug 2016 16:58:06 -0700 Subject: [PATCH 080/332] we don't need the dummy data any more --- interface/resources/qml/AddressBarDialog.qml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index a65a228cf8..9e1eb37dea 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -407,13 +407,6 @@ Window { if (handleError(url, error, data, cb)) { return; } - // FIXME: For debugging until we have real data - if (!data.user_stories.length) { - data.user_stories = [ - {created_at: "8/3/2016", action: "snapshot", path: "/4257.1,126.084,1335.45/0,-0.857368,0,0.514705", place_name: "SpiritMoving", thumbnail_url:"https://hifi-metaverse.s3-us-west-1.amazonaws.com/images/places/previews/c28/a26/f0-/thumbnail/hifi-place-c28a26f0-6991-4654-9c2b-e64228c06954.jpg?1456878797"}, - {created_at: "8/3/2016", action: "snapshot", path: "/10077.4,4003.6,9972.56/0,-0.410351,0,0.911928", place_name: "Ventura", thumbnail_url:"https://hifi-metaverse.s3-us-west-1.amazonaws.com/images/places/previews/1f5/e6b/00-/thumbnail/hifi-place-1f5e6b00-2bf0-4319-b9ae-a2344a72354c.png?1454321596"} - ]; - } var stories = data.user_stories.map(function (story) { // explicit single-argument function return makeModelData(story); }); From b4a43db025f25ea58e634aeb6b7a52e458f66972 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 11 Aug 2016 17:04:48 -0700 Subject: [PATCH 081/332] include user name in search text for user stories --- interface/resources/qml/AddressBarDialog.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 9e1eb37dea..12fe44dcd8 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -324,7 +324,7 @@ Window { // ListModel elements will only ever have those properties that are defined by the first obj that is added. // So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story. var name = optionalPlaceName || data.place_name, - tags = data.tags || [data.action], + tags = data.tags || [data.action, data.username], description = data.description || ""; return { place_name: name, @@ -338,7 +338,7 @@ Window { description: description, online_users: data.online_users || 0, - searchText: [name].concat(tags, description).join(' ').toUpperCase() + searchText: [name].concat(tags, description || []).join(' ').toUpperCase() } } function mapDomainPlaces(domain, cb) { // cb(error, arrayOfDomainPlaceData) From 8708e2ac2bd45e28708c9668e68946402083c5be Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 12 Aug 2016 07:00:07 -0700 Subject: [PATCH 082/332] image url, in anticipation of having it in details --- interface/resources/qml/AddressBarDialog.qml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 12fe44dcd8..988f13a0a7 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -48,7 +48,7 @@ Window { function goCard(card) { if (useFeed) { - storyCard.imageUrl = card.imageUrl; //"http://howard-stearns.github.io/models/images/dancing-avatars.jpg"; + storyCard.imageUrl = card.imageUrl; storyCard.userName = card.userName; storyCard.placeName = card.placeName; storyCard.actionPhrase = card.actionPhrase; @@ -96,7 +96,7 @@ Window { userName: model.username; placeName: model.place_name; hifiUrl: model.place_name + model.path; - imageUrl: model.thumbnail_url; // This is wrong, but it will have to wait. + imageUrl: model.image_url; thumbnail: model.thumbnail_url; action: model.action; timestamp: model.created_at; @@ -325,14 +325,24 @@ Window { // So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story. var name = optionalPlaceName || data.place_name, tags = data.tags || [data.action, data.username], - description = data.description || ""; + description = data.description || "", + thumbnail_url = data.thumbnail_url || "", + image_url = thumbnail_url; + if (data.details) { + try { + image_url = JSON.parse(data.details).image_url || thumbnail_url; + } catch (e) { + console.log(name, "has bad details", data.details); + } + } return { place_name: name, username: data.username || "", path: data.path || "", created_at: data.created_at || "", action: data.action || "", - thumbnail_url: data.thumbnail_url || "", + thumbnail_url: thumbnail_url, + image_url: image_url, tags: tags, description: description, From 3408ad6f545e24ec35a289d8303d1bccc93121dc Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 12 Aug 2016 10:23:33 -0700 Subject: [PATCH 083/332] Wording change --- scripts/system/html/ShareSnapshot.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/html/ShareSnapshot.html b/scripts/system/html/ShareSnapshot.html index c054a3389a..00f805728c 100644 --- a/scripts/system/html/ShareSnapshot.html +++ b/scripts/system/html/ShareSnapshot.html @@ -71,12 +71,12 @@ if (!shareMsg.canShare) { // this means you may or may not be logged in, but can't share // because you are not in a public place. - document.getElementById("sharing").innerHTML = "

You can share if you are in a public place"; + document.getElementById("sharing").innerHTML = "

Snapshots can be shared when they're taken in shareable places."; } else if (!shareMsg.isLoggedIn) { // this means you are in a public place, but can't share because // you need to login. Soon we will just bring up the share dialog // in this case (and maybe set a boolean here to inform the html) - document.getElementById("sharing").innerHTML = "

You can share if you are logged in"; + document.getElementById("sharing").innerHTML = "

Snapshots can be shared when you are logged in."; } } window.onload = function () { From df0ceb17e83e5b5eec980fc30e830d0416f19b1f Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 12 Aug 2016 11:09:22 -0700 Subject: [PATCH 084/332] send (hi res image) details as a json string --- interface/src/ui/Snapshot.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index beb86af1c4..b7cdb1a126 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -197,7 +197,8 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { QJsonObject userStoryObject; QJsonObject detailsObject; detailsObject.insert("image_url", imageUrl); - userStoryObject.insert("details", detailsObject); + QString pickledDetails = QJsonDocument(detailsObject).toJson(); + userStoryObject.insert("details", pickledDetails); userStoryObject.insert("thumbnail_url", thumbnailUrl); userStoryObject.insert("place_name", placeName); userStoryObject.insert("path", currentPath); From ae9af88db287df65ad6d61cb24b2d29d84b996f8 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 12 Aug 2016 11:28:35 -0700 Subject: [PATCH 085/332] Spacing and better size for HMD pictures. --- scripts/system/html/ShareSnapshot.html | 8 +++++--- scripts/system/snapshot.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/system/html/ShareSnapshot.html b/scripts/system/html/ShareSnapshot.html index 00f805728c..a30532bbc9 100644 --- a/scripts/system/html/ShareSnapshot.html +++ b/scripts/system/html/ShareSnapshot.html @@ -35,11 +35,13 @@ } .snapsection{ - width:95% !important; - padding-right:0px; + width:95% !important; + padding-right:0px; + } + div#sharing { + margin-top:50px; } - div.clear { clear: both; } diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 4c0ba37ab3..39bcc7fe90 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -21,7 +21,7 @@ var button = toolBar.addButton({ }); function confirmShare(data) { - var dialog = new OverlayWebWindow('Snapshot', Script.resolvePath("html/ShareSnapshot.html"), 800, 300); + var dialog = new OverlayWebWindow('Snapshot', Script.resolvePath("html/ShareSnapshot.html"), 800, 470); function onMessage(message) { if (message == 'ready') { // The window can now receive data from us. dialog.emitScriptEvent(data); // Send it. From 1b62332058079cd0779657de3a12316b8b92688c Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 12 Aug 2016 11:50:11 -0700 Subject: [PATCH 086/332] don't show checkboxes on confirmation dialog when there's only one picture --- scripts/system/html/ShareSnapshot.html | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/scripts/system/html/ShareSnapshot.html b/scripts/system/html/ShareSnapshot.html index a30532bbc9..f9cc959ba9 100644 --- a/scripts/system/html/ShareSnapshot.html +++ b/scripts/system/html/ShareSnapshot.html @@ -46,7 +46,7 @@ @@ -120,16 +134,23 @@

-
-
- -
-
- -
-
- +
+
+
Would you like to share your pic in the Snapshots feed?
+
+
+
+
+ +
+
+
+ + + + +
diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 795e469c83..6c9132194e 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -20,24 +20,39 @@ var button = toolBar.addButton({ alpha: 0.9, }); +function showFeedWindow() { + DialogsManager.showFeed(); +} + +var openFeed, outstanding; function confirmShare(data) { - var dialog = new OverlayWebWindow('Snapshot', Script.resolvePath("html/ShareSnapshot.html"), 800, 470); + var dialog = new OverlayWebWindow('Snapshot Review', Script.resolvePath("html/ShareSnapshot.html"), 800, 470); function onMessage(message) { - if (message == 'ready') { // The window can now receive data from us. + switch (message) { + case 'ready': dialog.emitScriptEvent(data); // Send it. - return; - } // Rest is confirmation processing - dialog.webEventReceived.disconnect(onMessage); // I'm not certain that this is necessary. If it is, what do we do on normal close? - dialog.close(); - dialog.deleteLater(); - message = message.filter(function (picture) { return picture.share; }); - if (message.length) { - print('sharing', message.map(function (picture) { return picture.localPath; }).join(', ')); - message.forEach(function (data) { - Window.shareSnapshot(data.localPath); + openFeed = false; + outstanding = 0; + break; + case 'openSettings': + Desktop.show("hifi/dialogs/GeneralPreferencesDialog.qml", "GeneralPreferencesDialog"); + break; + default: + dialog.webEventReceived.disconnect(onMessage); // I'm not certain that this is necessary. If it is, what do we do on normal close? + dialog.close(); + dialog.deleteLater(); + message.forEach(function (submessage) { + if (submessage.share) { + print('sharing', submessage.localPath); + outstanding++; + Window.shareSnapshot(submessage.localPath); + } else if (submessage.openFeed) { + openFeed = true; + } }); - } else { - print('declined to share', JSON.stringify(data)); + if (openFeed && !outstanding) { + showFeedWindow(); + } } } dialog.webEventReceived.connect(onMessage); @@ -51,7 +66,10 @@ function snapshotShared(success) { } else { // for now just print an error. print('snapshot upload/share failed'); - } + } + if ((--outstanding <= 0) && openFeed) { + showFeedWindow(); + } } function onClicked() { From 5d19267d00e7237e6a5717d33b193a27acb60510 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Wed, 17 Aug 2016 11:10:38 -0700 Subject: [PATCH 125/332] Safeguard against file corruption This is what could be a temporary (or permanent) fix to users trying to delete important files on their computer through JS --- .../src/FileScriptingInterface.cpp | 19 +++++++++++++++++++ .../src/FileScriptingInterface.h | 1 + 2 files changed, 20 insertions(+) diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 63849c238d..2c8a68258f 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -39,6 +39,11 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url) { QString tempDir = path; tempDir.remove(fileName); qDebug() << "Temporary directory at: " + tempDir; + if (!isTempDir(tempDir)) { + qDebug() << "Temporary directory mismatch; risk of losing files"; + return; + } + QString file = unzipFile(path, tempDir); if (file != "") { qDebug() << "file to upload: " + file; @@ -52,6 +57,20 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url) { QDir(tempDir).removeRecursively(); } +// fix to check that we are only referring to a temporary directory +bool FileScriptingInterface::isTempDir(QString tempDir) { + QString folderName = "/" + tempDir.section("/", -1); + QString tempContainer = tempDir; + tempContainer.remove(folderName); + QTemporaryDir test; + QString testDir = test.path(); + folderName = "/" + testDir.section("/", -1); + QString testContainer = testDir; + testContainer.remove(folderName); + if (testContainer == tempContainer) return true; + return false; +} + bool FileScriptingInterface::isZippedFbx(QUrl url) { if (url.toString().contains(".zip") && url.toString().contains("fbx")) return true; qDebug() << "This model is not a .fbx packaged in a .zip. Please try with another model."; diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index 2e8de6b174..dd6ca3225b 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -33,6 +33,7 @@ signals: void unzipSuccess(QString url); private: + bool isTempDir(QString tempDir); QString unzipFile(QString path, QString tempDir); void recursiveFileScan(QFileInfo file, QString* dirName); void downloadZip(QString path, const QString link); From 9fa4a20e5501f8a44bb72179e62accfc447e28bf Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 18 Aug 2016 09:37:26 +1200 Subject: [PATCH 126/332] Fix Asset Browser dialog --- interface/resources/qml/LoginDialog/SignInBody.qml | 2 +- interface/resources/qml/LoginDialog/UsernameCollisionBody.qml | 2 +- interface/resources/qml/LoginDialog/WelcomeBody.qml | 2 +- .../resources/qml/styles-uit/{MenuItem.qml => InfoItem.qml} | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename interface/resources/qml/styles-uit/{MenuItem.qml => InfoItem.qml} (95%) diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml index 2da0ea856d..7bbba683c3 100644 --- a/interface/resources/qml/LoginDialog/SignInBody.qml +++ b/interface/resources/qml/LoginDialog/SignInBody.qml @@ -48,7 +48,7 @@ Item { } } - MenuItem { + InfoItem { id: mainTextContainer anchors { top: parent.top diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index 7e22b11f8b..b949c660d6 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -78,7 +78,7 @@ Item { placeholderText: "Choose your own" } - MenuItem { + InfoItem { id: termsContainer anchors { top: textField.bottom diff --git a/interface/resources/qml/LoginDialog/WelcomeBody.qml b/interface/resources/qml/LoginDialog/WelcomeBody.qml index ecc848cdc0..5fed9addf8 100644 --- a/interface/resources/qml/LoginDialog/WelcomeBody.qml +++ b/interface/resources/qml/LoginDialog/WelcomeBody.qml @@ -44,7 +44,7 @@ Item { } } - MenuItem { + InfoItem { id: mainTextContainer anchors { top: parent.top diff --git a/interface/resources/qml/styles-uit/MenuItem.qml b/interface/resources/qml/styles-uit/InfoItem.qml similarity index 95% rename from interface/resources/qml/styles-uit/MenuItem.qml rename to interface/resources/qml/styles-uit/InfoItem.qml index 4431c357bf..83781a4ef5 100644 --- a/interface/resources/qml/styles-uit/MenuItem.qml +++ b/interface/resources/qml/styles-uit/InfoItem.qml @@ -1,5 +1,5 @@ // -// MenuItem.qml +// InfoItem.qml // // Created by Clement on 7/18/16 // Copyright 2016 High Fidelity, Inc. From 8bc6ca2fa1db01238e1a4e8dc436bdfa9ed97703 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 17 Aug 2016 15:08:43 -0700 Subject: [PATCH 127/332] demo changes --- interface/resources/controllers/vive.json | 2 - scripts/defaultScripts.js | 1 + .../system/controllers/advancedMovement.js | 53 +++++++++++++++++++ scripts/system/controllers/teleport.js | 40 +++++++++++--- 4 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 scripts/system/controllers/advancedMovement.js diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 79114b8141..27230b3b7e 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -1,8 +1,6 @@ { "name": "Vive to Standard", "channels": [ - { "from": "Vive.LY", "when": "Vive.LSY", "filters": ["invert"], "to": "Standard.LY" }, - { "from": "Vive.LX", "when": "Vive.LSX", "to": "Standard.LX" }, { "from": "Vive.LT", "to": "Standard.LT", "filters": [ diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index cf707c4d19..e39c424521 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -26,5 +26,6 @@ Script.load("system/controllers/handControllerPointer.js"); Script.load("system/controllers/squeezeHands.js"); Script.load("system/controllers/grab.js"); Script.load("system/controllers/teleport.js"); +Script.load("system/controllers/advancedMovement.js") Script.load("system/dialTone.js"); Script.load("system/firstPersonHMD.js"); diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js new file mode 100644 index 0000000000..0968edc7f6 --- /dev/null +++ b/scripts/system/controllers/advancedMovement.js @@ -0,0 +1,53 @@ +var mappingName, advancedMapping; + +function addAdvancedMovementItemToSettingsMenu() { + Menu.addMenuItem({ + menuName: "Settings", + menuItemName: "Advanced Movement", + isCheckable: true, + isChecked: false + }); + +} + +function addTranslationToLeftStick() { + Controller.enableMapping(mappingName); +} + +function registerMappings() { + mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); + advancedMapping = Controller.newMapping(mappingName); + advancedMapping.from(Controller.Vive.LY).invert().to(Controller.Standard.LY); + advancedMapping.from(Controller.Vive.LX).to(Controller.Standard.LX); +} + +function removeTranslationFromLeftStick() { + Controller.disableMapping(mappingName); +} + +function scriptEnding() { + Menu.removeMenuItem("Settings", "Advanced Movement"); + removeTranslationFromLeftStick(); +} + +function menuItemEvent(menuItem) { + if (menuItem == "Advanced Movement") { + print(" checked=" + Menu.isOptionChecked("Advanced Movement")); + var isChecked = Menu.isOptionChecked("Advanced Movement"); + if (isChecked === true) { + addTranslationToLeftStick(); + } else if (isChecked === false) { + removeTranslationFromLeftStick(); + } + } + +} + +addAdvancedMovementItemToSettingsMenu(); + +// register our scriptEnding callback +Script.scriptEnding.connect(scriptEnding); + +Menu.menuItemEvent.connect(menuItemEvent); + +registerMappings(); \ No newline at end of file diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 5a1ae7e5ee..d1dd5b6348 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -38,6 +38,8 @@ var COLORS_TELEPORT_TOO_CLOSE = { }; var TELEPORT_CANCEL_RANGE = 1.5; +var USE_COOL_IN = true; +var COOL_IN_DURATION = 1750; function ThumbPad(hand) { this.hand = hand; @@ -81,6 +83,7 @@ function Teleporter() { this.smoothArrivalInterval = null; this.teleportHand = null; this.tooClose = false; + this.inCoolIn = false; this.initialize = function() { this.createMappings(); @@ -99,6 +102,7 @@ function Teleporter() { }; this.enterTeleportMode = function(hand) { + if (inTeleportMode === true) { return; } @@ -119,6 +123,10 @@ function Teleporter() { this.initialize(); Script.update.connect(this.update); this.updateConnected = true; + this.inCoolIn = true; + Script.setTimeout(function() { + _this.inCoolIn = false; + }, COOL_IN_DURATION) }; this.createTargetOverlay = function() { @@ -189,16 +197,16 @@ function Teleporter() { if (this.updateConnected === true) { Script.update.disconnect(this.update); } + this.disableMappings(); this.turnOffOverlayBeams(); - this.updateConnected = null; Script.setTimeout(function() { inTeleportMode = false; _this.enableGrab(); - }, 100); + }, 200); }; @@ -214,7 +222,13 @@ function Teleporter() { } teleporter.leftRay(); if ((leftPad.buttonValue === 0) && inTeleportMode === true) { - _this.teleport(); + if (_this.inCoolIn === true) { + _this.exitTeleportMode(); + _this.deleteTargetOverlay(); + _this.deleteCancelOverlay(); + } else { + _this.teleport(); + } return; } @@ -224,7 +238,13 @@ function Teleporter() { } teleporter.rightRay(); if ((rightPad.buttonValue === 0) && inTeleportMode === true) { - _this.teleport(); + if (_this.inCoolIn === true) { + _this.exitTeleportMode(); + _this.deleteTargetOverlay(); + _this.deleteCancelOverlay(); + } else { + _this.teleport(); + } return; } } @@ -235,7 +255,11 @@ function Teleporter() { var pose = Controller.getPoseValue(Controller.Standard.RightHand); var rightPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition(); var rightRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) : - Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, {x: 1, y: 0, z: 0})); + Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + })); var rightPickRay = { origin: rightPosition, @@ -283,7 +307,11 @@ function Teleporter() { var pose = Controller.getPoseValue(Controller.Standard.LeftHand); var leftPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition(); var leftRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) : - Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, {x: 1, y: 0, z: 0})); + Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + })); var leftPickRay = { origin: leftPosition, From a7ba4ce1f99fdc92b485117bb99549779ed6f1bc Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 17 Aug 2016 15:11:07 -0700 Subject: [PATCH 128/332] jumping --- interface/resources/controllers/vive.json | 1 - scripts/system/controllers/advancedMovement.js | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 27230b3b7e..9cf641d2b0 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -13,7 +13,6 @@ { "from": "Vive.LS", "to": "Standard.LS" }, { "from": "Vive.LSTouch", "to": "Standard.LSTouch" }, - { "from": "Vive.RY", "when": "Vive.RSY", "filters": ["invert"], "to": "Standard.RY" }, { "from": "Vive.RX", "when": "Vive.RSX", "to": "Standard.RX" }, { "from": "Vive.RT", "to": "Standard.RT", diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index 0968edc7f6..0d516664e3 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -19,6 +19,7 @@ function registerMappings() { advancedMapping = Controller.newMapping(mappingName); advancedMapping.from(Controller.Vive.LY).invert().to(Controller.Standard.LY); advancedMapping.from(Controller.Vive.LX).to(Controller.Standard.LX); + advancedMapping.from(Controller.Vive.RY).invert().to(Standard.RY); } function removeTranslationFromLeftStick() { From a32ab77b7dad3a9eaac11571584a7c3940835af2 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 17 Aug 2016 15:22:30 -0700 Subject: [PATCH 129/332] Address issues with random camera locations caused by bad HMD sensor poses --- interface/src/avatar/MyAvatar.cpp | 12 +++- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 74 +++++++++++++--------- plugins/openvr/src/OpenVrHelpers.h | 7 ++ 3 files changed, 62 insertions(+), 31 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 57e379a9ac..782ecbcc64 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -516,13 +516,23 @@ glm::mat4 MyAvatar::getSensorToWorldMatrix() const { return _sensorToWorldMatrixCache.get(); } +// As far as I know no HMD system supports a play area of a kilometer in radius. +static const float MAX_HMD_ORIGIN_DISTANCE = 1000.0f; // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD // as it moves through the world. void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { // update the sensorMatrices based on the new hmd pose _hmdSensorMatrix = hmdSensorMatrix; - _hmdSensorPosition = extractTranslation(hmdSensorMatrix); + auto newHmdSensorPosition = extractTranslation(hmdSensorMatrix); + + if (newHmdSensorPosition != _hmdSensorPosition && + glm::length(newHmdSensorPosition) > MAX_HMD_ORIGIN_DISTANCE) { + qWarning() << "Invalid HMD sensor position " << newHmdSensorPosition; + // Ignore unreasonable HMD sensor data + return; + } + _hmdSensorPosition = newHmdSensorPosition; _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); _hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation); } diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index b5f360ad8d..e751427ce2 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -108,19 +108,20 @@ public: } void updateSource() { - Lock lock(_plugin._presentMutex); - while (!_queue.empty()) { - auto& front = _queue.front(); - auto result = glClientWaitSync(front.fence, 0, 0); - if (GL_TIMEOUT_EXPIRED == result && GL_WAIT_FAILED == result) { - break; - } + _plugin.withNonPresentThreadLock([&] { + while (!_queue.empty()) { + auto& front = _queue.front(); + auto result = glClientWaitSync(front.fence, 0, 0); + if (GL_TIMEOUT_EXPIRED == result && GL_WAIT_FAILED == result) { + break; + } - glDeleteSync(front.fence); - front.fence = 0; - _current = front; - _queue.pop(); - } + glDeleteSync(front.fence); + front.fence = 0; + _current = front; + _queue.pop(); + } + }); } void run() override { @@ -170,15 +171,28 @@ public: PoseData nextRender, nextSim; nextRender.frameIndex = _plugin.presentCount(); vr::VRCompositor()->WaitGetPoses(nextRender.vrPoses, vr::k_unMaxTrackedDeviceCount, nextSim.vrPoses, vr::k_unMaxTrackedDeviceCount); - { - Lock lock(_plugin._presentMutex); - _presentCount++; - _presented.notify_one(); - _nextRender = nextRender; - _nextRender.update(_plugin._sensorResetMat); - _nextSim = nextSim; - _nextSim.update(_plugin._sensorResetMat); + + // Copy invalid poses in nextSim from nextRender + for (uint32_t i = 0; i < vr::k_unMaxTrackedDeviceCount; ++i) { + if (!nextSim.vrPoses[i].bPoseIsValid) { + nextSim.vrPoses[i] = nextRender.vrPoses[i]; + } } + + mat4 sensorResetMat; + _plugin.withNonPresentThreadLock([&] { + sensorResetMat = _plugin._sensorResetMat; + }); + + nextRender.update(sensorResetMat); + nextSim.update(sensorResetMat); + + _plugin.withNonPresentThreadLock([&] { + _nextRender = nextRender; + _nextSim = nextSim; + ++_presentCount; + _presented.notify_one(); + }); } _canvas.doneCurrent(); } @@ -366,19 +380,20 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { } _currentRenderFrameInfo = FrameInfo(); + PoseData nextSimPoseData; withNonPresentThreadLock([&] { - _currentRenderFrameInfo.renderPose = _nextSimPoseData.poses[vr::k_unTrackedDeviceIndex_Hmd]; + nextSimPoseData = _nextSimPoseData; }); // HACK: when interface is launched and steam vr is NOT running, openvr will return bad HMD poses for a few frames // To workaround this, filter out any hmd poses that are obviously bad, i.e. beneath the floor. - if (isBadPose(&_nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking)) { + if (isBadPose(&nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking)) { qDebug() << "WARNING: ignoring bad hmd pose from openvr"; // use the last known good HMD pose - _nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking = _lastGoodHMDPose; + nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking = _lastGoodHMDPose; } else { - _lastGoodHMDPose = _nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking; + _lastGoodHMDPose = nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking; } vr::TrackedDeviceIndex_t handIndices[2] { vr::k_unTrackedDeviceIndexInvalid, vr::k_unTrackedDeviceIndexInvalid }; @@ -387,7 +402,7 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { auto trackedCount = _system->GetSortedTrackedDeviceIndicesOfClass(vr::TrackedDeviceClass_Controller, controllerIndices, 2); // Find the left and right hand controllers, if they exist for (uint32_t i = 0; i < std::min(trackedCount, 2); ++i) { - if (_nextSimPoseData.vrPoses[i].bPoseIsValid) { + if (nextSimPoseData.vrPoses[i].bPoseIsValid) { auto role = _system->GetControllerRoleForTrackedDeviceIndex(controllerIndices[i]); if (vr::TrackedControllerRole_LeftHand == role) { handIndices[0] = controllerIndices[i]; @@ -398,8 +413,7 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { } } - _currentRenderFrameInfo.renderPose = _nextSimPoseData.poses[vr::k_unTrackedDeviceIndex_Hmd]; - + _currentRenderFrameInfo.renderPose = nextSimPoseData.poses[vr::k_unTrackedDeviceIndex_Hmd]; bool keyboardVisible = isOpenVrKeyboardShown(); std::array handPoses; @@ -409,9 +423,9 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { continue; } auto deviceIndex = handIndices[i]; - const mat4& mat = _nextSimPoseData.poses[deviceIndex]; - const vec3& linearVelocity = _nextSimPoseData.linearVelocities[deviceIndex]; - const vec3& angularVelocity = _nextSimPoseData.angularVelocities[deviceIndex]; + const mat4& mat = nextSimPoseData.poses[deviceIndex]; + const vec3& linearVelocity = nextSimPoseData.linearVelocities[deviceIndex]; + const vec3& angularVelocity = nextSimPoseData.angularVelocities[deviceIndex]; auto correctedPose = openVrControllerPoseToHandPose(i == 0, mat, linearVelocity, angularVelocity); static const glm::quat HAND_TO_LASER_ROTATION = glm::rotation(Vectors::UNIT_Z, Vectors::UNIT_NEG_Y); handPoses[i] = glm::translate(glm::mat4(), correctedPose.translation) * glm::mat4_cast(correctedPose.rotation * HAND_TO_LASER_ROTATION); diff --git a/plugins/openvr/src/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h index 368b14cb1a..4279e6a6ac 100644 --- a/plugins/openvr/src/OpenVrHelpers.h +++ b/plugins/openvr/src/OpenVrHelpers.h @@ -66,8 +66,15 @@ struct PoseData { vec3 linearVelocities[vr::k_unMaxTrackedDeviceCount]; vec3 angularVelocities[vr::k_unMaxTrackedDeviceCount]; + PoseData() { + memset(vrPoses, 0, sizeof(vr::TrackedDevicePose_t) * vr::k_unMaxTrackedDeviceCount); + } + void update(const glm::mat4& resetMat) { for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { + if (!vrPoses[i].bPoseIsValid) { + continue; + } poses[i] = resetMat * toGlm(vrPoses[i].mDeviceToAbsoluteTracking); linearVelocities[i] = transformVectorFast(resetMat, toGlm(vrPoses[i].vVelocity)); angularVelocities[i] = transformVectorFast(resetMat, toGlm(vrPoses[i].vAngularVelocity)); From 82a8887275b8bde3917702bccf98c709c5cf7b75 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 17 Aug 2016 15:50:19 -0700 Subject: [PATCH 130/332] add whens --- scripts/system/controllers/advancedMovement.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index 0d516664e3..9216b45072 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -17,9 +17,9 @@ function addTranslationToLeftStick() { function registerMappings() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); advancedMapping = Controller.newMapping(mappingName); - advancedMapping.from(Controller.Vive.LY).invert().to(Controller.Standard.LY); - advancedMapping.from(Controller.Vive.LX).to(Controller.Standard.LX); - advancedMapping.from(Controller.Vive.RY).invert().to(Standard.RY); + advancedMapping.from(Controller.Vive.LY).when(Controller.Vive.LSY).invert().to(Controller.Standard.LY); + advancedMapping.from(Controller.Vive.LX).when(Controller.Vive.LSX).to(Controller.Standard.LX); + advancedMapping.from(Controller.Vive.RY).when(Controller.Vive.RSY).invert().to(Controller.Standard.RY); } function removeTranslationFromLeftStick() { From a4a41faf247c9349aad99439784c534e6ee0fcc4 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 17 Aug 2016 16:12:07 -0700 Subject: [PATCH 131/332] demo and only do for vive --- scripts/defaultScripts.js | 6 ++++-- scripts/system/controllers/advancedMovement.js | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index e39c424521..57a5a2067a 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -26,6 +26,8 @@ Script.load("system/controllers/handControllerPointer.js"); Script.load("system/controllers/squeezeHands.js"); Script.load("system/controllers/grab.js"); Script.load("system/controllers/teleport.js"); -Script.load("system/controllers/advancedMovement.js") +if (Controller.Hardware.Vive !== undefined) { + Script.load("system/controllers/advancedMovement.js") +} Script.load("system/dialTone.js"); -Script.load("system/firstPersonHMD.js"); +Script.load("system/firstPersonHMD.js"); \ No newline at end of file diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index 9216b45072..e3085ee6bf 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -17,9 +17,10 @@ function addTranslationToLeftStick() { function registerMappings() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); advancedMapping = Controller.newMapping(mappingName); - advancedMapping.from(Controller.Vive.LY).when(Controller.Vive.LSY).invert().to(Controller.Standard.LY); - advancedMapping.from(Controller.Vive.LX).when(Controller.Vive.LSX).to(Controller.Standard.LX); - advancedMapping.from(Controller.Vive.RY).when(Controller.Vive.RSY).invert().to(Controller.Standard.RY); + var VIVE = Controller.Hardware.Vive; + advancedMapping.from(VIVE.LY).when(Controller.Vive.LSY).invert().to(Controller.Standard.LY); + advancedMapping.from(VIVE.LX).when(Controller.Vive.LSX).to(Controller.Standard.LX); + advancedMapping.from(VIVE.RY).when(Controller.Vive.RSY).invert().to(Controller.Standard.RY); } function removeTranslationFromLeftStick() { From fc65d246eecea7bddb5031af883c846f432e52a7 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 17 Aug 2016 16:13:23 -0700 Subject: [PATCH 132/332] update whens --- scripts/system/controllers/advancedMovement.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index e3085ee6bf..a4923df28c 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -18,9 +18,9 @@ function registerMappings() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); advancedMapping = Controller.newMapping(mappingName); var VIVE = Controller.Hardware.Vive; - advancedMapping.from(VIVE.LY).when(Controller.Vive.LSY).invert().to(Controller.Standard.LY); - advancedMapping.from(VIVE.LX).when(Controller.Vive.LSX).to(Controller.Standard.LX); - advancedMapping.from(VIVE.RY).when(Controller.Vive.RSY).invert().to(Controller.Standard.RY); + advancedMapping.from(VIVE.LY).when(VIVE.LSY).invert().to(Controller.Standard.LY); + advancedMapping.from(VIVE.LX).when(VIVE.LSX).to(Controller.Standard.LX); + advancedMapping.from(VIVE.RY).when(VIVE.RSY).invert().to(Controller.Standard.RY); } function removeTranslationFromLeftStick() { From 514d6ec1396be36b720beb37d1fa896057ed8bee Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 17 Aug 2016 16:22:44 -0700 Subject: [PATCH 133/332] get lsy lsx rsy values --- scripts/system/controllers/advancedMovement.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index a4923df28c..387ed0f6a3 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -18,9 +18,12 @@ function registerMappings() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); advancedMapping = Controller.newMapping(mappingName); var VIVE = Controller.Hardware.Vive; - advancedMapping.from(VIVE.LY).when(VIVE.LSY).invert().to(Controller.Standard.LY); - advancedMapping.from(VIVE.LX).when(VIVE.LSX).to(Controller.Standard.LX); - advancedMapping.from(VIVE.RY).when(VIVE.RSY).invert().to(Controller.Standard.RY); + var LSY = Controller.getValue(VIVE.LSY); + var LSX = Controller.getValue(VIVE.LSX); + var RSY = Controller.getValue(VIVE.RSY); + advancedMapping.from(VIVE.LY).when(LSY).invert().to(Controller.Standard.LY); + advancedMapping.from(VIVE.LX).when(LSX).to(Controller.Standard.LX); + advancedMapping.from(VIVE.RY).when(RSY).invert().to(Controller.Standard.RY); } function removeTranslationFromLeftStick() { From fdfcc235b661380635cd31d27f8b3404fd339b14 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 17 Aug 2016 16:24:59 -0700 Subject: [PATCH 134/332] as functions --- scripts/system/controllers/advancedMovement.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index 387ed0f6a3..e201f861b3 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -18,12 +18,9 @@ function registerMappings() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); advancedMapping = Controller.newMapping(mappingName); var VIVE = Controller.Hardware.Vive; - var LSY = Controller.getValue(VIVE.LSY); - var LSX = Controller.getValue(VIVE.LSX); - var RSY = Controller.getValue(VIVE.RSY); - advancedMapping.from(VIVE.LY).when(LSY).invert().to(Controller.Standard.LY); - advancedMapping.from(VIVE.LX).when(LSX).to(Controller.Standard.LX); - advancedMapping.from(VIVE.RY).when(RSY).invert().to(Controller.Standard.RY); + advancedMapping.from(VIVE.LY).when(Controller.getValue(VIVE.LSY)).invert().to(Controller.Standard.LY); + advancedMapping.from(VIVE.LX).when(Controller.getValue(VIVE.LSX)).to(Controller.Standard.LX); + advancedMapping.from(VIVE.RY).when(Controller.getValue(VIVE.RSY)).invert().to(Controller.Standard.RY); } function removeTranslationFromLeftStick() { From e75f950ac70587a93471ae1fb0402a7b7d54acca Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 17 Aug 2016 14:12:40 -0700 Subject: [PATCH 135/332] Signal receivedHifiSchemeURL --- interface/src/Application.cpp | 1 + interface/src/Application.h | 1 + interface/src/ui/AddressBarDialog.cpp | 1 + interface/src/ui/AddressBarDialog.h | 1 + 4 files changed, 4 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4077fc0d57..340597f18a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4853,6 +4853,7 @@ bool Application::canAcceptURL(const QString& urlString) const { bool Application::acceptURL(const QString& urlString, bool defaultUpload) { if (urlString.startsWith(HIFI_URL_SCHEME)) { // this is a hifi URL - have the AddressManager handle it + emit receivedHifiSchemeURL(urlString); QMetaObject::invokeMethod(DependencyManager::get().data(), "handleLookupString", Qt::AutoConnection, Q_ARG(const QString&, urlString)); return true; diff --git a/interface/src/Application.h b/interface/src/Application.h index 713febeb83..9fcce66f55 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -266,6 +266,7 @@ signals: void activeDisplayPluginChanged(); void uploadRequest(QString path); + void receivedHifiSchemeURL(QString path); public slots: QVector pasteEntities(float x, float y, float z); diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index e5b4262770..3e84c4c3c5 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -39,6 +39,7 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare _backEnabled = !(DependencyManager::get()->getBackStack().isEmpty()); _forwardEnabled = !(DependencyManager::get()->getForwardStack().isEmpty()); connect(DependencyManager::get().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed); + connect(qApp, &Application::receivedHifiSchemeURL, this, &AddressBarDialog::receivedHifiSchemeURL); } void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions) { diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index 10f0e0822a..68a12d4eb0 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -33,6 +33,7 @@ signals: void backEnabledChanged(); void forwardEnabledChanged(); void useFeedChanged(); + void receivedHifiSchemeURL(QString url); protected: void displayAddressOfflineMessage(); From d58cc8ddd99814557ed608c809c97c1c613c1335 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 17 Aug 2016 14:15:27 -0700 Subject: [PATCH 136/332] use html card from user_story id, and close things when receiveHifiSchemeUrl --- interface/resources/qml/AddressBarDialog.qml | 34 +- interface/resources/qml/AddressBarDialog.qml~ | 510 ++++++++++++++++++ interface/resources/qml/hifi/Card.qml | 1 + 3 files changed, 541 insertions(+), 4 deletions(-) create mode 100644 interface/resources/qml/AddressBarDialog.qml~ diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 832ba14013..7c2ef17590 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -15,6 +15,7 @@ import "styles" import "windows" import "hifi" import "hifi/toolbars" +import "controls-uit" as HifiControls Window { id: root @@ -46,15 +47,21 @@ Window { anchors.centerIn = parent; } + function resetAfterTeleport() { + console.log('hrs fixme got reset') + storyCardFrame.shown = root.shown = false; + } function goCard(card) { if (addressBarDialog.useFeed) { - storyCard.imageUrl = card.imageUrl; + /*storyCard.imageUrl = card.imageUrl; // hrs fixme storyCard.userName = card.userName; storyCard.placeName = card.placeName; storyCard.actionPhrase = card.actionPhrase; storyCard.timePhrase = card.timePhrase; storyCard.hifiUrl = card.hifiUrl; - storyCard.visible = true; + storyCard.visible = true;*/ + storyCard.url = metaverseBase + "user_stories/" + card.storyId + ".html"; + storyCardFrame.shown = true; return; } addressLine.text = card.hifiUrl; @@ -74,6 +81,7 @@ Window { onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0; onForwardEnabledChanged: forwardArrow.buttonState = addressBarDialog.forwardEnabled ? 1 : 0; onUseFeedChanged: { updateFeedState(); } + onReceivedHifiSchemeURL: { console.log('hrs status change'); console.log('hrs status got', url); resetAfterTeleport(); } ListModel { id: suggestions } @@ -102,6 +110,7 @@ Window { action: model.action; timestamp: model.created_at; onlineUsers: model.online_users; + storyId: model.metaverseId; hoverThunk: function () { ListView.view.currentIndex = index; } unhoverThunk: function () { ListView.view.currentIndex = -1; } } @@ -114,6 +123,7 @@ Window { Image { // Just a visual indicator that the user can swipe the cards over to see more. source: "../images/Swipe-Icon-single.svg" width: 50; + visible: suggestions.count > 3; anchors { right: scroll.right; verticalCenter: scroll.verticalCenter; @@ -214,14 +224,27 @@ Window { } } - UserStoryCard { + /*UserStoryCard { // hrs fixme remove id: storyCard; visible: false; visitPlace: function (hifiUrl) { storyCard.visible = false; addressLine.text = hifiUrl; toggleOrGo(true); - }; + };*/ + Window { + width: 750; + height: 360; + HifiControls.WebView { + anchors.fill: parent; + id: storyCard; + } + id: storyCardFrame; + + shown: false; + destroyOnCloseButton: false; + pinnable: false; + anchors { verticalCenter: scroll.verticalCenter; horizontalCenter: scroll.horizontalCenter; @@ -348,6 +371,7 @@ Window { console.log(name, "has bad details", data.details); } } + console.log('hrs fixme id', data.id, typeof data.id, JSON.stringify(data)); return { place_name: name, username: data.username || "", @@ -357,6 +381,8 @@ Window { thumbnail_url: thumbnail_url, image_url: image_url, + metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity. + tags: tags, description: description, online_users: data.online_users || 0, diff --git a/interface/resources/qml/AddressBarDialog.qml~ b/interface/resources/qml/AddressBarDialog.qml~ new file mode 100644 index 0000000000..5da49a1ee1 --- /dev/null +++ b/interface/resources/qml/AddressBarDialog.qml~ @@ -0,0 +1,510 @@ +// +// AddressBarDialog.qml +// +// Created by Austin Davis on 2015/04/14 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 +import QtQuick 2.4 +import "controls" +import "styles" +import "windows" +import "hifi" +import "hifi/toolbars" + +Window { + id: root + HifiConstants { id: hifi } + + objectName: "AddressBarDialog" + frame: HiddenFrame {} + hideBackground: true + + shown: false + destroyOnHidden: false + resizable: false + scale: 1.25 // Make this dialog a little larger than normal + + width: addressBarDialog.implicitWidth + height: addressBarDialog.implicitHeight + + onShownChanged: addressBarDialog.observeShownChanged(shown); + Component.onCompleted: { + root.parentChanged.connect(center); + center(); + } + Component.onDestruction: { + root.parentChanged.disconnect(center); + } + + function center() { + // Explicitly center in order to avoid warnings at shutdown + anchors.centerIn = parent; + } + + function goCard(card) { + if (addressBarDialog.useFeed) { + storyCard.imageUrl = card.imageUrl; + storyCard.userName = card.userName; + storyCard.placeName = card.placeName; + storyCard.actionPhrase = card.actionPhrase; + storyCard.timePhrase = card.timePhrase; + storyCard.hifiUrl = card.hifiUrl; + storyCard.visible = true; + return; + } + addressLine.text = card.hifiUrl; + toggleOrGo(true); + } + property var allPlaces: []; + property var allStories: []; + property int cardWidth: 200; + property int cardHeight: 152; + //property string metaverseBase: "https://metaverse.highfidelity.com/api/v1/"; + property string metaverseBase: "http://10.0.0.242:3000/api/v1/"; + + AddressBarDialog { + id: addressBarDialog + implicitWidth: backgroundImage.width + implicitHeight: backgroundImage.height + // The buttons have their button state changed on hover, so we have to manually fix them up here + onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0; + onForwardEnabledChanged: forwardArrow.buttonState = addressBarDialog.forwardEnabled ? 1 : 0; + onUseFeedChanged: { updateFeedState(); } + + ListModel { id: suggestions } + + ListView { + id: scroll + width: backgroundImage.width; + height: cardHeight; + spacing: hifi.layout.spacing; + clip: true; + anchors { + bottom: backgroundImage.top; + bottomMargin: 2 * hifi.layout.spacing; + horizontalCenter: backgroundImage.horizontalCenter + } + model: suggestions; + orientation: ListView.Horizontal; + delegate: Card { + width: cardWidth; + height: cardHeight; + goFunction: goCard; + userName: model.username; + placeName: model.place_name; + hifiUrl: model.place_name + model.path; + imageUrl: model.image_url; + thumbnail: model.thumbnail_url; + action: model.action; + timestamp: model.created_at; + onlineUsers: model.online_users; + hoverThunk: function () { ListView.view.currentIndex = index; } + unhoverThunk: function () { ListView.view.currentIndex = -1; } + } + highlightMoveDuration: -1; + highlightMoveVelocity: -1; + highlight: Rectangle { color: "transparent"; border.width: 4; border.color: "#1DB5ED"; z: 1; } + leftMargin: 50; // Start the first item over be about the same amount as the last item peeks through on the other side. + rightMargin: 50; + } + Image { // Just a visual indicator that the user can swipe the cards over to see more. + source: "../images/Swipe-Icon-single.svg" + width: 50; + anchors { + right: scroll.right; + verticalCenter: scroll.verticalCenter; + } + } + Image { + id: backgroundImage + source: "../images/address-bar.svg" + width: 576 * root.scale + height: 80 * root.scale + property int inputAreaHeight: 56.0 * root.scale // Height of the background's input area + property int inputAreaStep: (height - inputAreaHeight) / 2 + + ToolbarButton { + id: homeButton + imageURL: "../images/home.svg" + buttonState: 1 + defaultState: 1 + hoverState: 2 + onClicked: addressBarDialog.loadHome(); + anchors { + left: parent.left + leftMargin: homeButton.width / 2 + verticalCenter: parent.verticalCenter + } + } + + ToolbarButton { + id: backArrow; + imageURL: "../images/backward.svg"; + hoverState: addressBarDialog.backEnabled ? 2 : 0; + defaultState: addressBarDialog.backEnabled ? 1 : 0; + buttonState: addressBarDialog.backEnabled ? 1 : 0; + onClicked: addressBarDialog.loadBack(); + anchors { + left: homeButton.right + verticalCenter: parent.verticalCenter + } + } + ToolbarButton { + id: forwardArrow; + imageURL: "../images/forward.svg"; + hoverState: addressBarDialog.forwardEnabled ? 2 : 0; + defaultState: addressBarDialog.forwardEnabled ? 1 : 0; + buttonState: addressBarDialog.forwardEnabled ? 1 : 0; + onClicked: addressBarDialog.loadForward(); + anchors { + left: backArrow.right + verticalCenter: parent.verticalCenter + } + } + + // FIXME replace with TextField + TextInput { + id: addressLine + focus: true + anchors { + top: parent.top + bottom: parent.bottom + left: forwardArrow.right + right: placesButton.left + leftMargin: forwardArrow.width + rightMargin: placesButton.width + topMargin: parent.inputAreaStep + hifi.layout.spacing + bottomMargin: parent.inputAreaStep + hifi.layout.spacing + } + font.pixelSize: hifi.fonts.pixelSize * root.scale * 0.75 + helperText: "Go to: place, @user, /path, network address" + helperPixelSize: font.pixelSize * 0.75 + helperItalic: true + onTextChanged: filterChoicesByText() + } + // These two are radio buttons. + ToolbarButton { + id: placesButton + imageURL: "../images/places.svg" + buttonState: 1 + defaultState: addressBarDialog.useFeed ? 0 : 1; + hoverState: addressBarDialog.useFeed ? 2 : -1; + onClicked: addressBarDialog.useFeed ? toggleFeed() : identity() + anchors { + right: feedButton.left; + bottom: addressLine.bottom; + } + } + ToolbarButton { + id: feedButton; + imageURL: "../images/snap-feed.svg"; + buttonState: 0 + defaultState: addressBarDialog.useFeed ? 1 : 0; + hoverState: addressBarDialog.useFeed ? -1 : 2; + onClicked: addressBarDialog.useFeed ? identity() : toggleFeed(); + anchors { + right: parent.right; + bottom: addressLine.bottom; + rightMargin: feedButton.width / 2 + } + } + } + + UserStoryCard { + id: storyCard; + visible: false; + visitPlace: function (hifiUrl) { + storyCard.visible = false; + addressLine.text = hifiUrl; + toggleOrGo(true); + }; + anchors { + verticalCenter: scroll.verticalCenter; + horizontalCenter: scroll.horizontalCenter; + verticalCenterOffset: 50; + } + } + } + + + function toggleFeed() { + addressBarDialog.useFeed = !addressBarDialog.useFeed; + updateFeedState(); + } + function updateFeedState() { + placesButton.buttonState = addressBarDialog.useFeed ? 0 : 1; + feedButton.buttonState = addressBarDialog.useFeed ? 1 : 0; + filterChoicesByText(); + } + function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects. + // TODO: make available to other .qml. + var request = new XMLHttpRequest(); + // QT bug: apparently doesn't handle onload. Workaround using readyState. + request.onreadystatechange = function () { + var READY_STATE_DONE = 4; + var HTTP_OK = 200; + if (request.readyState >= READY_STATE_DONE) { + var error = (request.status !== HTTP_OK) && request.status.toString() + ':' + request.statusText, + response = !error && request.responseText, + contentType = !error && request.getResponseHeader('content-type'); + if (!error && contentType.indexOf('application/json') === 0) { + try { + response = JSON.parse(response); + } catch (e) { + error = e; + } + } + cb(error, response); + } + }; + request.open("GET", url, true); + request.send(); + } + function asyncMap(array, iterator, cb) { + // call iterator(element, icb) once for each element of array, and then cb(error, mappedResult) + // when icb(error, mappedElement) has been called by each iterator. + // Calls to iterator are overlapped and map call icb in any order, but the mappedResults are collected in the same + // order as the elements of the array. + // short-circuits if error. Note that iterator MUST be an asynchronous function. (Use setTimeout if necessary.) + var count = array.length, results = []; + if (!count) { + return cb(null, results); + } + array.forEach(function (element, index) { + if (count < 0) { // don't keep iterating after we short-circuit + return; + } + iterator(element, function (error, mapped) { + results[index] = mapped; + if (error || !--count) { + count = 1; // don't cb multiple times if error + cb(error, results); + } + }); + }); + } + // Example: + /*asyncMap([0, 1, 2, 3, 4, 5, 6], function (elt, icb) { + console.log('called', elt); + setTimeout(function () { + console.log('answering', elt); + icb(null, elt); + }, Math.random() * 1000); + }, console.log); */ + + function identity(x) { + return x; + } + + function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey + if (!error && (data.status === 'success')) { + return; + } + if (!error) { // Create a message from the data + error = data.status + ': ' + data.error; + } + if (typeof(error) === 'string') { // Make a proper Error object + error = new Error(error); + } + error.message += ' in ' + url; // Include the url. + cb(error); + return true; + } + + function getPlace(placeData, cb) { // cb(error, side-effected-placeData), after adding path, thumbnails, and description + var url = metaverseBase + 'places/' + placeData.place_name; + getRequest(url, function (error, data) { + if (handleError(url, error, data, cb)) { + return; + } + var place = data.data.place, previews = place.previews; + placeData.path = place.path; + if (previews && previews.thumbnail) { + placeData.thumbnail_url = previews.thumbnail; + } + if (place.description) { + placeData.description = place.description; + placeData.searchText += ' ' + place.description.toUpperCase(); + } + cb(error, placeData); + }); + } + function makeModelData(data, optionalPlaceName) { // create a new obj from data + // ListModel elements will only ever have those properties that are defined by the first obj that is added. + // So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story. + var name = optionalPlaceName || data.place_name, + tags = data.tags || [data.action, data.username], + description = data.description || "", + thumbnail_url = data.thumbnail_url || "", + image_url = thumbnail_url; + if (data.details) { + try { + image_url = JSON.parse(data.details).image_url || thumbnail_url; + } catch (e) { + console.log(name, "has bad details", data.details); + } + } + return { + place_name: name, + username: data.username || "", + path: data.path || "", + created_at: data.created_at || "", + action: data.action || "", + thumbnail_url: thumbnail_url, + image_url: image_url, + + tags: tags, + description: description, + online_users: data.online_users || 0, + + searchText: [name].concat(tags, description || []).join(' ').toUpperCase() + } + } + function mapDomainPlaces(domain, cb) { // cb(error, arrayOfDomainPlaceData) + function addPlace(name, icb) { + getPlace(makeModelData(domain, name), icb); + } + // IWBNI we could get these results in order with most-recent-entered first. + // In any case, we don't really need to preserve the domain.names order in the results. + asyncMap(domain.names || [], addPlace, cb); + } + + function suggestable(place) { + if (addressBarDialog.useFeed) { + return true; + } + return (place.place_name !== AddressManager.hostname) // Not our entry, but do show other entry points to current domain. + && place.thumbnail_url + && place.online_users // at least one present means it's actually online + && place.online_users <= 20; + } + function getDomainPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model + // Each page of results is processed completely before we start on the next page. + // For each page of domains, we process each domain in parallel, and for each domain, process each place name in parallel. + // This gives us minimum latency within the page, but we do preserve the order within the page by using asyncMap and + // only appending the collected results. + var params = [ + 'open', // published hours handle now + // FIXME: should determine if place is actually running + 'restriction=open', // Not by whitelist, etc. FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. + // FIXME add maturity + 'protocol=' + encodeURIComponent(AddressManager.protocolVersion()), + 'sort_by=users', + 'sort_order=desc', + 'page=' + pageNumber + ]; + var url = metaverseBase + 'domains/all?' + params.join('&'); + getRequest(url, function (error, data) { + if (handleError(url, error, data, cb)) { + return; + } + asyncMap(data.data.domains, mapDomainPlaces, function (error, pageResults) { + if (error) { + return cb(error); + } + // pageResults is now [ [ placeDataOneForDomainOne, placeDataTwoForDomainOne, ...], [ placeDataTwoForDomainTwo...] ] + pageResults.forEach(function (domainResults) { + allPlaces = allPlaces.concat(domainResults); + if (!addressLine.text && !addressBarDialog.useFeed) { // Don't add if the user is already filtering + domainResults.forEach(function (place) { + if (suggestable(place)) { + suggestions.append(place); + } + }); + } + }); + if (data.current_page < data.total_pages) { + return getDomainPage(pageNumber + 1, cb); + } + cb(); + }); + }); + } + function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model + var url = metaverseBase + 'user_stories?page=' + pageNumber; + getRequest(url, function (error, data) { + if (handleError(url, error, data, cb)) { + return; + } + var stories = data.user_stories.map(function (story) { // explicit single-argument function + return makeModelData(story); + }); + allStories = allStories.concat(stories); + if (!addressLine.text && addressBarDialog.useFeed) { // Don't add if the user is already filtering + stories.forEach(function (story) { + suggestions.append(story); + }); + } + if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now + return getUserStoryPage(pageNumber + 1, cb); + } + cb(); + }); + } + function filterChoicesByText() { + suggestions.clear(); + var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity), + data = addressBarDialog.useFeed ? allStories : allPlaces; + function matches(place) { + if (!words.length) { + return suggestable(place); + } + return words.every(function (word) { + return place.searchText.indexOf(word) >= 0; + }); + } + data.forEach(function (place) { + if (matches(place)) { + suggestions.append(place); + } + }); + } + + function fillDestinations() { + allPlaces = []; + allStories = []; + suggestions.clear(); + getDomainPage(1, function (error) { + console.log('domain query', error || 'ok', allPlaces.length); + }); + getUserStoryPage(1, function (error) { + console.log('user stories query', error || 'ok', allStories.length); + }); + } + + onVisibleChanged: { + if (visible) { + addressLine.forceActiveFocus() + fillDestinations(); + } else { + addressLine.text = "" + } + } + + function toggleOrGo(fromSuggestions) { + if (addressLine.text !== "") { + addressBarDialog.loadAddress(addressLine.text, fromSuggestions) + } + root.shown = false; + } + + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Escape: + case Qt.Key_Back: + root.shown = false + event.accepted = true + break + case Qt.Key_Enter: + case Qt.Key_Return: + toggleOrGo() + event.accepted = true + break + } + } +} diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 278f9330d3..fe162349c1 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -25,6 +25,7 @@ Rectangle { property string thumbnail: defaultThumbnail; property string imageUrl: ""; property var goFunction: null; + property string storyId: ""; property string timePhrase: pastTime(timestamp); property string actionPhrase: makeActionPhrase(action); From 25b9d756ca8965b1a0162f8edb92e41d0ac30955 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 17 Aug 2016 16:54:04 -0700 Subject: [PATCH 137/332] Update entity edit tracking to track count rather than bool --- interface/src/Application.cpp | 6 +++--- .../entities/src/EntityScriptingInterface.cpp | 14 +++++++------- libraries/entities/src/EntityScriptingInterface.h | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e352b841b7..0876869554 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1203,9 +1203,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : auto entityScriptingInterface = DependencyManager::get(); auto entityActivityTracking = entityScriptingInterface->getActivityTracking(); entityScriptingInterface->resetActivityTracking(); - properties["added_entity"] = entityActivityTracking.hasAddedEntity; - properties["deleted_entity"] = entityActivityTracking.hasDeletedEntity; - properties["edited_entity"] = entityActivityTracking.hasEditedEntity; + properties["added_entity_cnt"] = entityActivityTracking.addedEntityCount; + properties["deleted_entity_cnt"] = entityActivityTracking.deletedEntityCount; + properties["edited_entity_cnt"] = entityActivityTracking.editedEntityCount; auto hmdHeadPose = getHMDSensorPose(); properties["hmd_head_pose_changed"] = isHMDMode() && (hmdHeadPose != lastHMDHeadPose); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 031847b3f3..c3dbd47600 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -41,9 +41,9 @@ void EntityScriptingInterface::queueEntityMessage(PacketType packetType, } void EntityScriptingInterface::resetActivityTracking() { - _activityTracking.hasAddedEntity = false; - _activityTracking.hasDeletedEntity = false; - _activityTracking.hasEditedEntity = false; + _activityTracking.addedEntityCount = 0; + _activityTracking.deletedEntityCount = 0; + _activityTracking.editedEntityCount = 0; } bool EntityScriptingInterface::canAdjustLocks() { @@ -136,7 +136,7 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) { - _activityTracking.hasAddedEntity = true; + _activityTracking.addedEntityCount++; EntityItemProperties propertiesWithSimID = convertLocationFromScriptSemantics(properties); propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); @@ -208,7 +208,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& shapeType, bool dynamic, const glm::vec3& position, const glm::vec3& gravity) { - _activityTracking.hasAddedEntity = true; + _activityTracking.addedEntityCount++; EntityItemProperties properties; properties.setType(EntityTypes::Model); @@ -273,7 +273,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit } QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) { - _activityTracking.hasEditedEntity = true; + _activityTracking.editedEntityCount++; EntityItemProperties properties = scriptSideProperties; @@ -418,7 +418,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } void EntityScriptingInterface::deleteEntity(QUuid id) { - _activityTracking.hasDeletedEntity = true; + _activityTracking.deletedEntity++; EntityItemID entityID(id); bool shouldDelete = true; diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index bde0974d7c..ce5b332b2a 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -67,9 +67,9 @@ public: class ActivityTracking { public: - bool hasAddedEntity { false }; - bool hasDeletedEntity { false }; - bool hasEditedEntity { false }; + int addedEntityCount { 0 }; + int deletedEntityCount { 0 }; + int editedEntityCount { 0 }; }; EntityEditPacketSender* getEntityPacketSender() const { return (EntityEditPacketSender*)getPacketSender(); } From 0f415abd6b36fa279ce26137e3f901fec611787f Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 17 Aug 2016 16:56:52 -0700 Subject: [PATCH 138/332] dual purpose address bar - switchable between html and qml --- interface/resources/qml/AddressBarDialog.qml | 43 +- interface/resources/qml/AddressBarDialog.qml~ | 510 ------------------ 2 files changed, 26 insertions(+), 527 deletions(-) delete mode 100644 interface/resources/qml/AddressBarDialog.qml~ diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 7c2ef17590..089bec07f4 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -48,20 +48,22 @@ Window { } function resetAfterTeleport() { - console.log('hrs fixme got reset') storyCardFrame.shown = root.shown = false; } function goCard(card) { if (addressBarDialog.useFeed) { - /*storyCard.imageUrl = card.imageUrl; // hrs fixme - storyCard.userName = card.userName; - storyCard.placeName = card.placeName; - storyCard.actionPhrase = card.actionPhrase; - storyCard.timePhrase = card.timePhrase; - storyCard.hifiUrl = card.hifiUrl; - storyCard.visible = true;*/ - storyCard.url = metaverseBase + "user_stories/" + card.storyId + ".html"; - storyCardFrame.shown = true; + if (useHTML) { + storyCardHTML.url = metaverseBase + "user_stories/" + card.storyId + ".html"; + storyCardFrame.shown = true; + } else { + storyCardQML.imageUrl = card.imageUrl; + storyCardQML.userName = card.userName; + storyCardQML.placeName = card.placeName; + storyCardQML.actionPhrase = card.actionPhrase; + storyCardQML.timePhrase = card.timePhrase; + storyCardQML.hifiUrl = card.hifiUrl; + storyCardQML.visible = true; + } return; } addressLine.text = card.hifiUrl; @@ -72,6 +74,8 @@ Window { property int cardWidth: 200; property int cardHeight: 152; property string metaverseBase: "https://metaverse.highfidelity.com/api/v1/"; + //property string metaverseBase: "http://10.0.0.241:3000/api/v1/"; + property bool useHTML: false; // fixme: remove this and all false branches after the server is updated AddressBarDialog { id: addressBarDialog @@ -81,7 +85,7 @@ Window { onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0; onForwardEnabledChanged: forwardArrow.buttonState = addressBarDialog.forwardEnabled ? 1 : 0; onUseFeedChanged: { updateFeedState(); } - onReceivedHifiSchemeURL: { console.log('hrs status change'); console.log('hrs status got', url); resetAfterTeleport(); } + onReceivedHifiSchemeURL: resetAfterTeleport(); ListModel { id: suggestions } @@ -224,20 +228,26 @@ Window { } } - /*UserStoryCard { // hrs fixme remove - id: storyCard; + UserStoryCard { + id: storyCardQML; visible: false; visitPlace: function (hifiUrl) { - storyCard.visible = false; + storyCardQML.visible = false; addressLine.text = hifiUrl; toggleOrGo(true); - };*/ + }; + anchors { + verticalCenter: scroll.verticalCenter; + horizontalCenter: scroll.horizontalCenter; + verticalCenterOffset: 50; + } + } Window { width: 750; height: 360; HifiControls.WebView { anchors.fill: parent; - id: storyCard; + id: storyCardHTML; } id: storyCardFrame; @@ -371,7 +381,6 @@ Window { console.log(name, "has bad details", data.details); } } - console.log('hrs fixme id', data.id, typeof data.id, JSON.stringify(data)); return { place_name: name, username: data.username || "", diff --git a/interface/resources/qml/AddressBarDialog.qml~ b/interface/resources/qml/AddressBarDialog.qml~ deleted file mode 100644 index 5da49a1ee1..0000000000 --- a/interface/resources/qml/AddressBarDialog.qml~ +++ /dev/null @@ -1,510 +0,0 @@ -// -// AddressBarDialog.qml -// -// Created by Austin Davis on 2015/04/14 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import Hifi 1.0 -import QtQuick 2.4 -import "controls" -import "styles" -import "windows" -import "hifi" -import "hifi/toolbars" - -Window { - id: root - HifiConstants { id: hifi } - - objectName: "AddressBarDialog" - frame: HiddenFrame {} - hideBackground: true - - shown: false - destroyOnHidden: false - resizable: false - scale: 1.25 // Make this dialog a little larger than normal - - width: addressBarDialog.implicitWidth - height: addressBarDialog.implicitHeight - - onShownChanged: addressBarDialog.observeShownChanged(shown); - Component.onCompleted: { - root.parentChanged.connect(center); - center(); - } - Component.onDestruction: { - root.parentChanged.disconnect(center); - } - - function center() { - // Explicitly center in order to avoid warnings at shutdown - anchors.centerIn = parent; - } - - function goCard(card) { - if (addressBarDialog.useFeed) { - storyCard.imageUrl = card.imageUrl; - storyCard.userName = card.userName; - storyCard.placeName = card.placeName; - storyCard.actionPhrase = card.actionPhrase; - storyCard.timePhrase = card.timePhrase; - storyCard.hifiUrl = card.hifiUrl; - storyCard.visible = true; - return; - } - addressLine.text = card.hifiUrl; - toggleOrGo(true); - } - property var allPlaces: []; - property var allStories: []; - property int cardWidth: 200; - property int cardHeight: 152; - //property string metaverseBase: "https://metaverse.highfidelity.com/api/v1/"; - property string metaverseBase: "http://10.0.0.242:3000/api/v1/"; - - AddressBarDialog { - id: addressBarDialog - implicitWidth: backgroundImage.width - implicitHeight: backgroundImage.height - // The buttons have their button state changed on hover, so we have to manually fix them up here - onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0; - onForwardEnabledChanged: forwardArrow.buttonState = addressBarDialog.forwardEnabled ? 1 : 0; - onUseFeedChanged: { updateFeedState(); } - - ListModel { id: suggestions } - - ListView { - id: scroll - width: backgroundImage.width; - height: cardHeight; - spacing: hifi.layout.spacing; - clip: true; - anchors { - bottom: backgroundImage.top; - bottomMargin: 2 * hifi.layout.spacing; - horizontalCenter: backgroundImage.horizontalCenter - } - model: suggestions; - orientation: ListView.Horizontal; - delegate: Card { - width: cardWidth; - height: cardHeight; - goFunction: goCard; - userName: model.username; - placeName: model.place_name; - hifiUrl: model.place_name + model.path; - imageUrl: model.image_url; - thumbnail: model.thumbnail_url; - action: model.action; - timestamp: model.created_at; - onlineUsers: model.online_users; - hoverThunk: function () { ListView.view.currentIndex = index; } - unhoverThunk: function () { ListView.view.currentIndex = -1; } - } - highlightMoveDuration: -1; - highlightMoveVelocity: -1; - highlight: Rectangle { color: "transparent"; border.width: 4; border.color: "#1DB5ED"; z: 1; } - leftMargin: 50; // Start the first item over be about the same amount as the last item peeks through on the other side. - rightMargin: 50; - } - Image { // Just a visual indicator that the user can swipe the cards over to see more. - source: "../images/Swipe-Icon-single.svg" - width: 50; - anchors { - right: scroll.right; - verticalCenter: scroll.verticalCenter; - } - } - Image { - id: backgroundImage - source: "../images/address-bar.svg" - width: 576 * root.scale - height: 80 * root.scale - property int inputAreaHeight: 56.0 * root.scale // Height of the background's input area - property int inputAreaStep: (height - inputAreaHeight) / 2 - - ToolbarButton { - id: homeButton - imageURL: "../images/home.svg" - buttonState: 1 - defaultState: 1 - hoverState: 2 - onClicked: addressBarDialog.loadHome(); - anchors { - left: parent.left - leftMargin: homeButton.width / 2 - verticalCenter: parent.verticalCenter - } - } - - ToolbarButton { - id: backArrow; - imageURL: "../images/backward.svg"; - hoverState: addressBarDialog.backEnabled ? 2 : 0; - defaultState: addressBarDialog.backEnabled ? 1 : 0; - buttonState: addressBarDialog.backEnabled ? 1 : 0; - onClicked: addressBarDialog.loadBack(); - anchors { - left: homeButton.right - verticalCenter: parent.verticalCenter - } - } - ToolbarButton { - id: forwardArrow; - imageURL: "../images/forward.svg"; - hoverState: addressBarDialog.forwardEnabled ? 2 : 0; - defaultState: addressBarDialog.forwardEnabled ? 1 : 0; - buttonState: addressBarDialog.forwardEnabled ? 1 : 0; - onClicked: addressBarDialog.loadForward(); - anchors { - left: backArrow.right - verticalCenter: parent.verticalCenter - } - } - - // FIXME replace with TextField - TextInput { - id: addressLine - focus: true - anchors { - top: parent.top - bottom: parent.bottom - left: forwardArrow.right - right: placesButton.left - leftMargin: forwardArrow.width - rightMargin: placesButton.width - topMargin: parent.inputAreaStep + hifi.layout.spacing - bottomMargin: parent.inputAreaStep + hifi.layout.spacing - } - font.pixelSize: hifi.fonts.pixelSize * root.scale * 0.75 - helperText: "Go to: place, @user, /path, network address" - helperPixelSize: font.pixelSize * 0.75 - helperItalic: true - onTextChanged: filterChoicesByText() - } - // These two are radio buttons. - ToolbarButton { - id: placesButton - imageURL: "../images/places.svg" - buttonState: 1 - defaultState: addressBarDialog.useFeed ? 0 : 1; - hoverState: addressBarDialog.useFeed ? 2 : -1; - onClicked: addressBarDialog.useFeed ? toggleFeed() : identity() - anchors { - right: feedButton.left; - bottom: addressLine.bottom; - } - } - ToolbarButton { - id: feedButton; - imageURL: "../images/snap-feed.svg"; - buttonState: 0 - defaultState: addressBarDialog.useFeed ? 1 : 0; - hoverState: addressBarDialog.useFeed ? -1 : 2; - onClicked: addressBarDialog.useFeed ? identity() : toggleFeed(); - anchors { - right: parent.right; - bottom: addressLine.bottom; - rightMargin: feedButton.width / 2 - } - } - } - - UserStoryCard { - id: storyCard; - visible: false; - visitPlace: function (hifiUrl) { - storyCard.visible = false; - addressLine.text = hifiUrl; - toggleOrGo(true); - }; - anchors { - verticalCenter: scroll.verticalCenter; - horizontalCenter: scroll.horizontalCenter; - verticalCenterOffset: 50; - } - } - } - - - function toggleFeed() { - addressBarDialog.useFeed = !addressBarDialog.useFeed; - updateFeedState(); - } - function updateFeedState() { - placesButton.buttonState = addressBarDialog.useFeed ? 0 : 1; - feedButton.buttonState = addressBarDialog.useFeed ? 1 : 0; - filterChoicesByText(); - } - function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects. - // TODO: make available to other .qml. - var request = new XMLHttpRequest(); - // QT bug: apparently doesn't handle onload. Workaround using readyState. - request.onreadystatechange = function () { - var READY_STATE_DONE = 4; - var HTTP_OK = 200; - if (request.readyState >= READY_STATE_DONE) { - var error = (request.status !== HTTP_OK) && request.status.toString() + ':' + request.statusText, - response = !error && request.responseText, - contentType = !error && request.getResponseHeader('content-type'); - if (!error && contentType.indexOf('application/json') === 0) { - try { - response = JSON.parse(response); - } catch (e) { - error = e; - } - } - cb(error, response); - } - }; - request.open("GET", url, true); - request.send(); - } - function asyncMap(array, iterator, cb) { - // call iterator(element, icb) once for each element of array, and then cb(error, mappedResult) - // when icb(error, mappedElement) has been called by each iterator. - // Calls to iterator are overlapped and map call icb in any order, but the mappedResults are collected in the same - // order as the elements of the array. - // short-circuits if error. Note that iterator MUST be an asynchronous function. (Use setTimeout if necessary.) - var count = array.length, results = []; - if (!count) { - return cb(null, results); - } - array.forEach(function (element, index) { - if (count < 0) { // don't keep iterating after we short-circuit - return; - } - iterator(element, function (error, mapped) { - results[index] = mapped; - if (error || !--count) { - count = 1; // don't cb multiple times if error - cb(error, results); - } - }); - }); - } - // Example: - /*asyncMap([0, 1, 2, 3, 4, 5, 6], function (elt, icb) { - console.log('called', elt); - setTimeout(function () { - console.log('answering', elt); - icb(null, elt); - }, Math.random() * 1000); - }, console.log); */ - - function identity(x) { - return x; - } - - function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey - if (!error && (data.status === 'success')) { - return; - } - if (!error) { // Create a message from the data - error = data.status + ': ' + data.error; - } - if (typeof(error) === 'string') { // Make a proper Error object - error = new Error(error); - } - error.message += ' in ' + url; // Include the url. - cb(error); - return true; - } - - function getPlace(placeData, cb) { // cb(error, side-effected-placeData), after adding path, thumbnails, and description - var url = metaverseBase + 'places/' + placeData.place_name; - getRequest(url, function (error, data) { - if (handleError(url, error, data, cb)) { - return; - } - var place = data.data.place, previews = place.previews; - placeData.path = place.path; - if (previews && previews.thumbnail) { - placeData.thumbnail_url = previews.thumbnail; - } - if (place.description) { - placeData.description = place.description; - placeData.searchText += ' ' + place.description.toUpperCase(); - } - cb(error, placeData); - }); - } - function makeModelData(data, optionalPlaceName) { // create a new obj from data - // ListModel elements will only ever have those properties that are defined by the first obj that is added. - // So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story. - var name = optionalPlaceName || data.place_name, - tags = data.tags || [data.action, data.username], - description = data.description || "", - thumbnail_url = data.thumbnail_url || "", - image_url = thumbnail_url; - if (data.details) { - try { - image_url = JSON.parse(data.details).image_url || thumbnail_url; - } catch (e) { - console.log(name, "has bad details", data.details); - } - } - return { - place_name: name, - username: data.username || "", - path: data.path || "", - created_at: data.created_at || "", - action: data.action || "", - thumbnail_url: thumbnail_url, - image_url: image_url, - - tags: tags, - description: description, - online_users: data.online_users || 0, - - searchText: [name].concat(tags, description || []).join(' ').toUpperCase() - } - } - function mapDomainPlaces(domain, cb) { // cb(error, arrayOfDomainPlaceData) - function addPlace(name, icb) { - getPlace(makeModelData(domain, name), icb); - } - // IWBNI we could get these results in order with most-recent-entered first. - // In any case, we don't really need to preserve the domain.names order in the results. - asyncMap(domain.names || [], addPlace, cb); - } - - function suggestable(place) { - if (addressBarDialog.useFeed) { - return true; - } - return (place.place_name !== AddressManager.hostname) // Not our entry, but do show other entry points to current domain. - && place.thumbnail_url - && place.online_users // at least one present means it's actually online - && place.online_users <= 20; - } - function getDomainPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model - // Each page of results is processed completely before we start on the next page. - // For each page of domains, we process each domain in parallel, and for each domain, process each place name in parallel. - // This gives us minimum latency within the page, but we do preserve the order within the page by using asyncMap and - // only appending the collected results. - var params = [ - 'open', // published hours handle now - // FIXME: should determine if place is actually running - 'restriction=open', // Not by whitelist, etc. FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. - // FIXME add maturity - 'protocol=' + encodeURIComponent(AddressManager.protocolVersion()), - 'sort_by=users', - 'sort_order=desc', - 'page=' + pageNumber - ]; - var url = metaverseBase + 'domains/all?' + params.join('&'); - getRequest(url, function (error, data) { - if (handleError(url, error, data, cb)) { - return; - } - asyncMap(data.data.domains, mapDomainPlaces, function (error, pageResults) { - if (error) { - return cb(error); - } - // pageResults is now [ [ placeDataOneForDomainOne, placeDataTwoForDomainOne, ...], [ placeDataTwoForDomainTwo...] ] - pageResults.forEach(function (domainResults) { - allPlaces = allPlaces.concat(domainResults); - if (!addressLine.text && !addressBarDialog.useFeed) { // Don't add if the user is already filtering - domainResults.forEach(function (place) { - if (suggestable(place)) { - suggestions.append(place); - } - }); - } - }); - if (data.current_page < data.total_pages) { - return getDomainPage(pageNumber + 1, cb); - } - cb(); - }); - }); - } - function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model - var url = metaverseBase + 'user_stories?page=' + pageNumber; - getRequest(url, function (error, data) { - if (handleError(url, error, data, cb)) { - return; - } - var stories = data.user_stories.map(function (story) { // explicit single-argument function - return makeModelData(story); - }); - allStories = allStories.concat(stories); - if (!addressLine.text && addressBarDialog.useFeed) { // Don't add if the user is already filtering - stories.forEach(function (story) { - suggestions.append(story); - }); - } - if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now - return getUserStoryPage(pageNumber + 1, cb); - } - cb(); - }); - } - function filterChoicesByText() { - suggestions.clear(); - var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity), - data = addressBarDialog.useFeed ? allStories : allPlaces; - function matches(place) { - if (!words.length) { - return suggestable(place); - } - return words.every(function (word) { - return place.searchText.indexOf(word) >= 0; - }); - } - data.forEach(function (place) { - if (matches(place)) { - suggestions.append(place); - } - }); - } - - function fillDestinations() { - allPlaces = []; - allStories = []; - suggestions.clear(); - getDomainPage(1, function (error) { - console.log('domain query', error || 'ok', allPlaces.length); - }); - getUserStoryPage(1, function (error) { - console.log('user stories query', error || 'ok', allStories.length); - }); - } - - onVisibleChanged: { - if (visible) { - addressLine.forceActiveFocus() - fillDestinations(); - } else { - addressLine.text = "" - } - } - - function toggleOrGo(fromSuggestions) { - if (addressLine.text !== "") { - addressBarDialog.loadAddress(addressLine.text, fromSuggestions) - } - root.shown = false; - } - - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Escape: - case Qt.Key_Back: - root.shown = false - event.accepted = true - break - case Qt.Key_Enter: - case Qt.Key_Return: - toggleOrGo() - event.accepted = true - break - } - } -} From e69c89a93e67e809ea8de82fb3b14de98e756854 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 17 Aug 2016 17:00:17 -0700 Subject: [PATCH 139/332] y --- scripts/system/controllers/advancedMovement.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index e201f861b3..7b642b8bba 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -18,9 +18,9 @@ function registerMappings() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); advancedMapping = Controller.newMapping(mappingName); var VIVE = Controller.Hardware.Vive; - advancedMapping.from(VIVE.LY).when(Controller.getValue(VIVE.LSY)).invert().to(Controller.Standard.LY); - advancedMapping.from(VIVE.LX).when(Controller.getValue(VIVE.LSX)).to(Controller.Standard.LX); - advancedMapping.from(VIVE.RY).when(Controller.getValue(VIVE.RSY)).invert().to(Controller.Standard.RY); + advancedMapping.from(VIVE.LY).when(Controller.getValue(VIVE.LS)).invert().to(Controller.Standard.LY); + advancedMapping.from(VIVE.LX).when(Controller.getValue(VIVE.LS)).to(Controller.Standard.LX); + advancedMapping.from(VIVE.RY).when(Controller.getValue(VIVE.RS)).invert().to(Controller.Standard.RY); } function removeTranslationFromLeftStick() { From d46af500c195eb27b36e76174a989f10aa58f788 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 17 Aug 2016 17:00:56 -0700 Subject: [PATCH 140/332] Fix wrong property name in entity activity tracking --- libraries/entities/src/EntityScriptingInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index c3dbd47600..2bff2a99de 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -418,7 +418,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } void EntityScriptingInterface::deleteEntity(QUuid id) { - _activityTracking.deletedEntity++; + _activityTracking.deletedEntityCount++; EntityItemID entityID(id); bool shouldDelete = true; From 8ede4ddb66914a70934fc362cfb0d201389bc4be Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 17 Aug 2016 17:33:24 -0700 Subject: [PATCH 141/332] longform --- scripts/system/controllers/advancedMovement.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index 7b642b8bba..40e6320df9 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -17,10 +17,13 @@ function addTranslationToLeftStick() { function registerMappings() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); advancedMapping = Controller.newMapping(mappingName); - var VIVE = Controller.Hardware.Vive; - advancedMapping.from(VIVE.LY).when(Controller.getValue(VIVE.LS)).invert().to(Controller.Standard.LY); - advancedMapping.from(VIVE.LX).when(Controller.getValue(VIVE.LS)).to(Controller.Standard.LX); - advancedMapping.from(VIVE.RY).when(Controller.getValue(VIVE.RS)).invert().to(Controller.Standard.RY); + advancedMapping.from(Controller.Hardware.Vive.LY).when(Controller.getValue(Controller.Hardware.Vive.LSY)).invert().to(Controller.Standard.LY); + advancedMapping.from(Controller.Hardware.Vive.LX).when(Controller.getValue(Controller.Hardware.Vive.LSX)).to(Controller.Standard.LX); + advancedMapping.from(Controller.Hardware.Vive.RY).when(Controller.getValue(Controller.Hardware.Vive.RSY)).invert().to(Controller.Standard.RY); +} + +function testPrint(what){ + print('it was controller: ' + what) } function removeTranslationFromLeftStick() { From 2ff35768aa9ecfb9743ed35fd63939c8023befd1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 17 Aug 2016 18:45:09 -0700 Subject: [PATCH 142/332] make 3d-line overlays work again --- interface/src/ui/overlays/Line3DOverlay.cpp | 51 +++++++++++++++++++-- interface/src/ui/overlays/Line3DOverlay.h | 11 +++-- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index 6b46b5b884..a50681cdb2 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -23,6 +23,7 @@ Line3DOverlay::Line3DOverlay() : Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : Base3DOverlay(line3DOverlay), + _start(line3DOverlay->_start), _end(line3DOverlay->_end), _geometryCacheID(DependencyManager::get()->allocateID()) { @@ -31,6 +32,40 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : Line3DOverlay::~Line3DOverlay() { } +const glm::vec3& Line3DOverlay::getStart() const { + bool success; + glm::vec3 worldStart = localToWorld(_start, _parentID, _parentJointIndex, success); + if (!success) { + qDebug() << "Line3DOverlay::getStart failed"; + } + return worldStart; +} + +const glm::vec3& Line3DOverlay::getEnd() const { + bool success; + glm::vec3 worldEnd = localToWorld(_end, _parentID, _parentJointIndex, success); + if (!success) { + qDebug() << "Line3DOverlay::getEnd failed"; + } + return worldEnd; +} + +void Line3DOverlay::setStart(const glm::vec3& start) { + bool success; + _start = worldToLocal(start, _parentID, _parentJointIndex, success); + if (!success) { + qDebug() << "Line3DOverlay::setStart failed"; + } +} + +void Line3DOverlay::setEnd(const glm::vec3& end) { + bool success; + _end = worldToLocal(end, _parentID, _parentJointIndex, success); + if (!success) { + qDebug() << "Line3DOverlay::setEnd failed"; + } +} + AABox Line3DOverlay::getBounds() const { auto extents = Extents{}; extents.addPoint(_start); @@ -76,8 +111,8 @@ const render::ShapeKey Line3DOverlay::getShapeKey() { return builder.build(); } -void Line3DOverlay::setProperties(const QVariantMap& properties) { - Base3DOverlay::setProperties(properties); +void Line3DOverlay::setProperties(const QVariantMap& originalProperties) { + QVariantMap properties = originalProperties; auto start = properties["start"]; // if "start" property was not there, check to see if they included aliases: startPoint @@ -87,6 +122,7 @@ void Line3DOverlay::setProperties(const QVariantMap& properties) { if (start.isValid()) { setStart(vec3FromVariant(start)); } + properties.remove("start"); // so that Base3DOverlay doesn't respond to it auto end = properties["end"]; // if "end" property was not there, check to see if they included aliases: endPoint @@ -109,14 +145,16 @@ void Line3DOverlay::setProperties(const QVariantMap& properties) { if (glowWidth.isValid()) { setGlow(glowWidth.toFloat()); } + + Base3DOverlay::setProperties(properties); } QVariant Line3DOverlay::getProperty(const QString& property) { if (property == "start" || property == "startPoint" || property == "p1") { - return vec3toVariant(_start); + return vec3toVariant(getStart()); } if (property == "end" || property == "endPoint" || property == "p2") { - return vec3toVariant(_end); + return vec3toVariant(getEnd()); } return Base3DOverlay::getProperty(property); @@ -125,3 +163,8 @@ QVariant Line3DOverlay::getProperty(const QString& property) { Line3DOverlay* Line3DOverlay::createClone() const { return new Line3DOverlay(this); } + + +void Line3DOverlay::locationChanged(bool tellPhysics) { + // do nothing +} diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h index d066677c70..2138ddc5da 100644 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ b/interface/src/ui/overlays/Line3DOverlay.h @@ -28,14 +28,15 @@ public: virtual AABox getBounds() const override; // getters - const glm::vec3& getStart() const { return _start; } - const glm::vec3& getEnd() const { return _end; } + const glm::vec3& getStart() const; + const glm::vec3& getEnd() const; const float& getGlow() const { return _glow; } const float& getGlowWidth() const { return _glowWidth; } // setters - void setStart(const glm::vec3& start) { _start = start; } - void setEnd(const glm::vec3& end) { _end = end; } + void setStart(const glm::vec3& start); + void setEnd(const glm::vec3& end); + void setGlow(const float& glow) { _glow = glow; } void setGlowWidth(const float& glowWidth) { _glowWidth = glowWidth; } @@ -44,6 +45,8 @@ public: virtual Line3DOverlay* createClone() const override; + virtual void locationChanged(bool tellPhysics = true) override; + protected: glm::vec3 _start; glm::vec3 _end; From 8487f825a0ff1c924befa4f6a8cf0fd99ce3edff Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Aug 2016 08:01:42 -0700 Subject: [PATCH 143/332] don't return reference to local variables --- interface/src/ui/overlays/Line3DOverlay.cpp | 4 ++-- interface/src/ui/overlays/Line3DOverlay.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index a50681cdb2..c3a6c5920e 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -32,7 +32,7 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : Line3DOverlay::~Line3DOverlay() { } -const glm::vec3& Line3DOverlay::getStart() const { +glm::vec3 Line3DOverlay::getStart() const { bool success; glm::vec3 worldStart = localToWorld(_start, _parentID, _parentJointIndex, success); if (!success) { @@ -41,7 +41,7 @@ const glm::vec3& Line3DOverlay::getStart() const { return worldStart; } -const glm::vec3& Line3DOverlay::getEnd() const { +glm::vec3 Line3DOverlay::getEnd() const { bool success; glm::vec3 worldEnd = localToWorld(_end, _parentID, _parentJointIndex, success); if (!success) { diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h index 2138ddc5da..b4e2ba8168 100644 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ b/interface/src/ui/overlays/Line3DOverlay.h @@ -28,8 +28,8 @@ public: virtual AABox getBounds() const override; // getters - const glm::vec3& getStart() const; - const glm::vec3& getEnd() const; + glm::vec3 getStart() const; + glm::vec3 getEnd() const; const float& getGlow() const { return _glow; } const float& getGlowWidth() const { return _glowWidth; } From cfbc589829cba88182b35d3e6130295dd8ef1d96 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 08:43:26 -0700 Subject: [PATCH 144/332] debug prints --- .../system/controllers/advancedMovement.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index 40e6320df9..cca8dd0f52 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -17,13 +17,22 @@ function addTranslationToLeftStick() { function registerMappings() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); advancedMapping = Controller.newMapping(mappingName); - advancedMapping.from(Controller.Hardware.Vive.LY).when(Controller.getValue(Controller.Hardware.Vive.LSY)).invert().to(Controller.Standard.LY); - advancedMapping.from(Controller.Hardware.Vive.LX).when(Controller.getValue(Controller.Hardware.Vive.LSX)).to(Controller.Standard.LX); - advancedMapping.from(Controller.Hardware.Vive.RY).when(Controller.getValue(Controller.Hardware.Vive.RSY)).invert().to(Controller.Standard.RY); + advancedMapping.from(Controller.Hardware.Vive.LY).when(Controller.getValue(Controller.Hardware.Vive.LSY)).invert().to( + function(val) { + testPrint('LY:' + val); + }); + advancedMapping.from(Controller.Hardware.Vive.LX).when(Controller.getValue(Controller.Hardware.Vive.LSX)).to( + function(val) { + testPrint('LX' + val) + }); + advancedMapping.from(Controller.Hardware.Vive.RY).when(Controller.getValue(Controller.Hardware.Vive.RSY)).invert().to( + function(val) { + testPrint('RY' + val) + }); } -function testPrint(what){ - print('it was controller: ' + what) +function testPrint(what) { + print(what) } function removeTranslationFromLeftStick() { From 9e14191217c9796c7779dbe2bbe9a8d4a3b5c9b7 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 08:57:37 -0700 Subject: [PATCH 145/332] recondition --- .../system/controllers/advancedMovement.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index cca8dd0f52..620bd2723f 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -17,22 +17,13 @@ function addTranslationToLeftStick() { function registerMappings() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); advancedMapping = Controller.newMapping(mappingName); - advancedMapping.from(Controller.Hardware.Vive.LY).when(Controller.getValue(Controller.Hardware.Vive.LSY)).invert().to( - function(val) { - testPrint('LY:' + val); - }); - advancedMapping.from(Controller.Hardware.Vive.LX).when(Controller.getValue(Controller.Hardware.Vive.LSX)).to( - function(val) { - testPrint('LX' + val) - }); - advancedMapping.from(Controller.Hardware.Vive.RY).when(Controller.getValue(Controller.Hardware.Vive.RSY)).invert().to( - function(val) { - testPrint('RY' + val) - }); + advancedMapping.from(Controller.Hardware.Vive.LY).when(Controller.getValue(Controller.Hardware.Vive.LSY)===1).invert().to(Controller.Standard.LY); + advancedMapping.from(Controller.Hardware.Vive.LX).when(Controller.getValue(Controller.Hardware.Vive.LSX)===1).to(Controller.Standard.LX); + advancedMapping.from(Controller.Hardware.Vive.RY).when(Controller.getValue(Controller.Hardware.Vive.RSY)===1).invert().to(Controller.Standard.RY); } -function testPrint(what) { - print(what) +function testPrint(what){ + print('it was controller: ' + what) } function removeTranslationFromLeftStick() { From 7768e65e8bf0574df4766b2b0e66c7aef48efc19 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 09:22:43 -0700 Subject: [PATCH 146/332] update name --- scripts/defaultScripts.js | 4 +--- scripts/system/controllers/advancedMovement.js | 10 +++++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 57a5a2067a..feb9d8c2d6 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -26,8 +26,6 @@ Script.load("system/controllers/handControllerPointer.js"); Script.load("system/controllers/squeezeHands.js"); Script.load("system/controllers/grab.js"); Script.load("system/controllers/teleport.js"); -if (Controller.Hardware.Vive !== undefined) { - Script.load("system/controllers/advancedMovement.js") -} +Script.load("system/controllers/advancedMovement.js") Script.load("system/dialTone.js"); Script.load("system/firstPersonHMD.js"); \ No newline at end of file diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index 620bd2723f..73856cb045 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -3,7 +3,7 @@ var mappingName, advancedMapping; function addAdvancedMovementItemToSettingsMenu() { Menu.addMenuItem({ menuName: "Settings", - menuItemName: "Advanced Movement", + menuItemName: "Advanced Movement (Vive)", isCheckable: true, isChecked: false }); @@ -31,14 +31,14 @@ function removeTranslationFromLeftStick() { } function scriptEnding() { - Menu.removeMenuItem("Settings", "Advanced Movement"); + Menu.removeMenuItem("Settings", "Advanced Movement (Vive)"); removeTranslationFromLeftStick(); } function menuItemEvent(menuItem) { - if (menuItem == "Advanced Movement") { - print(" checked=" + Menu.isOptionChecked("Advanced Movement")); - var isChecked = Menu.isOptionChecked("Advanced Movement"); + if (menuItem == "Advanced Movement (Vive)") { + print(" checked=" + Menu.isOptionChecked("Advanced Movement (Vive)")); + var isChecked = Menu.isOptionChecked("Advanced Movement (Vive)"); if (isChecked === true) { addTranslationToLeftStick(); } else if (isChecked === false) { From 09cd0b8f4ada47ce56d29890ce00b4aefe3b3e6e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 09:24:01 -0700 Subject: [PATCH 147/332] debug prints 2 --- scripts/system/controllers/advancedMovement.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index 73856cb045..3c804e1e6d 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -17,12 +17,18 @@ function addTranslationToLeftStick() { function registerMappings() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); advancedMapping = Controller.newMapping(mappingName); - advancedMapping.from(Controller.Hardware.Vive.LY).when(Controller.getValue(Controller.Hardware.Vive.LSY)===1).invert().to(Controller.Standard.LY); - advancedMapping.from(Controller.Hardware.Vive.LX).when(Controller.getValue(Controller.Hardware.Vive.LSX)===1).to(Controller.Standard.LX); - advancedMapping.from(Controller.Hardware.Vive.RY).when(Controller.getValue(Controller.Hardware.Vive.RSY)===1).invert().to(Controller.Standard.RY); + advancedMapping.from(Controller.Hardware.Vive.LY).when(Controller.getValue(Controller.Hardware.Vive.LSY) === 1).invert().to(function(val) { + testPrint('ly:' + val) + }); + advancedMapping.from(Controller.Hardware.Vive.LX).when(Controller.getValue(Controller.Hardware.Vive.LSX) === 1).to(function(val) { + testPrint('lx:' + val) + }); + advancedMapping.from(Controller.Hardware.Vive.RY).when(Controller.getValue(Controller.Hardware.Vive.RSY) === 1).invert().to(function(val) { + testPrint('ry:' + val) + }); } -function testPrint(what){ +function testPrint(what) { print('it was controller: ' + what) } From 87dbfa7e4748cd142efa70eeed4282196fb93189 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Aug 2016 09:51:41 -0700 Subject: [PATCH 148/332] add -Wsuggest-override to compile flags and deal with fallout --- CMakeLists.txt | 2 +- assignment-client/src/Agent.h | 4 +- assignment-client/src/AssignmentAction.h | 30 +++---- .../src/AssignmentActionFactory.h | 4 +- assignment-client/src/assets/AssetServer.h | 8 +- assignment-client/src/assets/SendAssetTask.h | 2 +- .../src/assets/UploadAssetTask.h | 6 +- assignment-client/src/audio/AudioMixer.h | 4 +- .../src/audio/AudioMixerClientData.h | 8 +- .../src/audio/AvatarAudioStream.h | 2 +- assignment-client/src/avatars/AvatarMixer.h | 14 +-- .../src/entities/AssignmentParentFinder.h | 3 +- .../src/entities/EntityNodeData.h | 2 +- .../src/messages/MessagesMixer.h | 4 +- .../src/octree/OctreeInboundPacketProcessor.h | 10 +-- .../src/octree/OctreeSendThread.h | 2 +- assignment-client/src/octree/OctreeServer.h | 8 +- domain-server/src/DomainServer.h | 4 +- interface/src/FileLogger.h | 2 +- interface/src/InterfaceActionFactory.h | 4 +- interface/src/InterfaceParentFinder.h | 3 +- interface/src/ModelSelector.h | 14 +-- interface/src/ScriptHighlighting.h | 2 +- interface/src/avatar/Head.h | 12 +-- interface/src/devices/DdeFaceTracker.h | 34 ++++---- interface/src/devices/Faceshift.h | 36 ++++---- interface/src/devices/Leapmotion.h | 2 +- .../scripting/ControllerScriptingInterface.h | 14 +-- interface/src/scripting/WebWindowClass.h | 2 +- interface/src/ui/AudioStatsDialog.h | 47 +++++----- interface/src/ui/BandwidthDialog.h | 6 +- interface/src/ui/CachesSizeDialog.h | 14 +-- interface/src/ui/DataWebPage.h | 6 +- interface/src/ui/HMDToolsDialog.h | 16 ++-- interface/src/ui/JSConsole.h | 6 +- interface/src/ui/LodToolsDialog.h | 6 +- interface/src/ui/LogDialog.h | 6 +- interface/src/ui/OctreeStatsDialog.h | 6 +- interface/src/ui/ScriptEditBox.h | 2 +- interface/src/ui/ScriptEditorWindow.h | 4 +- interface/src/ui/ScriptLineNumberArea.h | 4 +- interface/src/ui/ScriptsTableWidget.h | 4 +- .../src/ui/overlays/LocalModelsOverlay.h | 10 +-- interface/src/ui/overlays/OverlayPanel.h | 2 +- interface/src/ui/overlays/RectangleOverlay.h | 5 +- libraries/animation/src/AnimationCache.h | 6 +- libraries/audio-client/src/AudioClient.h | 16 ++-- .../audio/src/AudioInjectorLocalBuffer.h | 22 ++--- libraries/audio/src/AudioLimiter.cpp | 2 +- .../audio/src/MixedProcessedAudioStream.h | 8 +- libraries/audio/src/PositionalAudioStream.h | 6 +- libraries/audio/src/SoundCache.h | 6 +- .../controllers/impl/endpoints/JSEndpoint.h | 4 +- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 2 +- .../embedded-webserver/src/HTTPManager.h | 4 +- .../src/EntityTreeRenderer.h | 32 +++---- libraries/entities/src/AddEntityOperator.h | 6 +- .../entities/src/AnimationPropertyGroup.h | 29 ++++--- libraries/entities/src/DeleteEntityOperator.h | 4 +- .../entities/src/EntityEditPacketSender.h | 4 +- libraries/entities/src/EntityItem.h | 2 +- .../entities/src/EntityScriptingInterface.h | 10 +-- libraries/entities/src/EntityTree.cpp | 12 +-- libraries/entities/src/EntityTreeElement.h | 48 ++++++----- .../entities/src/EntityTreeHeadlessViewer.h | 10 +-- .../entities/src/KeyLightPropertyGroup.h | 52 +++++------ libraries/entities/src/LightEntityItem.h | 22 ++--- libraries/entities/src/LineEntityItem.h | 43 +++++----- libraries/entities/src/ModelEntityItem.h | 22 ++--- .../entities/src/MovingEntitiesOperator.h | 6 +- .../entities/src/ParticleEffectEntityItem.h | 22 ++--- libraries/entities/src/PolyLineEntityItem.h | 45 +++++----- libraries/entities/src/PolyVoxEntityItem.h | 20 ++--- libraries/entities/src/PropertyGroup.h | 2 +- .../entities/src/RecurseOctreeToMapOperator.h | 4 +- libraries/entities/src/SkyboxPropertyGroup.h | 45 +++++----- libraries/entities/src/StagePropertyGroup.h | 49 ++++++----- libraries/entities/src/TextEntityItem.h | 34 ++++---- libraries/entities/src/UpdateEntityOperator.h | 6 +- libraries/entities/src/WebEntityItem.h | 30 +++---- libraries/entities/src/ZoneEntityItem.h | 36 ++++---- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 2 +- libraries/gl/src/gl/OglplusHelpers.h | 9 ++ libraries/gpu-gl/src/gpu/gl/GLBackend.h | 7 +- libraries/gpu-gl/src/gpu/gl/GLState.cpp | 12 +++ libraries/gpu/src/gpu/Buffer.h | 2 +- libraries/gpu/src/gpu/Texture.h | 2 +- .../src/model-networking/ModelCache.h | 2 +- .../src/model-networking/TextureCache.cpp | 2 +- .../src/model-networking/TextureCache.h | 8 +- libraries/networking/src/NLPacketList.h | 2 +- .../src/OAuthNetworkAccessManager.h | 4 +- libraries/networking/src/PacketSender.h | 4 +- .../networking/src/ReceivedPacketProcessor.h | 4 +- libraries/networking/src/udt/BasePacket.h | 10 +-- .../networking/src/udt/CongestionControl.h | 10 +-- libraries/networking/src/udt/PacketList.h | 8 +- .../octree/src/DirtyOctreeElementOperator.h | 4 +- libraries/octree/src/OctreeEditPacketSender.h | 2 +- libraries/octree/src/OctreePersistThread.h | 2 +- libraries/physics/src/CharacterController.cpp | 2 +- libraries/render-utils/src/Model.cpp | 2 +- libraries/render/src/render/Item.h | 46 +++++----- libraries/render/src/render/Task.h | 25 +++--- .../script-engine/src/ArrayBufferClass.h | 28 +++--- .../script-engine/src/ArrayBufferViewClass.h | 14 +-- libraries/script-engine/src/DataViewClass.h | 14 +-- libraries/script-engine/src/ScriptEngine.h | 7 +- libraries/script-engine/src/ScriptsModel.h | 10 +-- .../script-engine/src/ScriptsModelFilter.h | 4 +- libraries/script-engine/src/TypedArrays.h | 86 +++++++++---------- .../src/UndoStackScriptingInterface.h | 8 +- libraries/shared/src/CrashHelpers.h | 2 +- libraries/shared/src/GenericQueueThread.h | 2 +- libraries/shared/src/Preferences.h | 16 ++-- libraries/shared/src/SettingHandle.h | 62 ++++++------- libraries/shared/src/ShutdownEventListener.h | 2 +- .../src/steamworks-wrapper/SteamClient.cpp | 3 +- libraries/ui/src/CursorManager.cpp | 4 +- libraries/ui/src/ErrorDialog.h | 2 +- libraries/ui/src/MainWindow.h | 16 ++-- tools/vhacd-util/src/VHACDUtil.h | 2 +- 122 files changed, 773 insertions(+), 737 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d42be3d95..5ecd65ae22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,7 @@ if (WIN32) else () set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter") if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -Woverloaded-virtual -Wdouble-promotion") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -Woverloaded-virtual -Wdouble-promotion -Wsuggest-override") endif () endif(WIN32) diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 63d4cfa4d6..3f4e36374c 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -52,10 +52,10 @@ public: float getLastReceivedAudioLoudness() const { return _lastReceivedAudioLoudness; } QUuid getSessionUUID() const; - virtual void aboutToFinish(); + virtual void aboutToFinish() override; public slots: - void run(); + void run() override; void playAvatarSound(SharedSoundPointer avatarSound) { setAvatarSound(avatarSound); } private slots: diff --git a/assignment-client/src/AssignmentAction.h b/assignment-client/src/AssignmentAction.h index 6b55b280bb..98504b3545 100644 --- a/assignment-client/src/AssignmentAction.h +++ b/assignment-client/src/AssignmentAction.h @@ -24,27 +24,27 @@ public: AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity); virtual ~AssignmentAction(); - virtual void removeFromSimulation(EntitySimulationPointer simulation) const; - virtual EntityItemWeakPointer getOwnerEntity() const { return _ownerEntity; } - virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; } - virtual bool updateArguments(QVariantMap arguments); - virtual QVariantMap getArguments(); + virtual void removeFromSimulation(EntitySimulationPointer simulation) const override; + virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; } + virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; } + virtual bool updateArguments(QVariantMap arguments) override; + virtual QVariantMap getArguments() override; - virtual QByteArray serialize() const; - virtual void deserialize(QByteArray serializedArguments); + virtual QByteArray serialize() const override; + virtual void deserialize(QByteArray serializedArguments) override; private: QByteArray _data; protected: - virtual glm::vec3 getPosition(); - virtual void setPosition(glm::vec3 position); - virtual glm::quat getRotation(); - virtual void setRotation(glm::quat rotation); - virtual glm::vec3 getLinearVelocity(); - virtual void setLinearVelocity(glm::vec3 linearVelocity); - virtual glm::vec3 getAngularVelocity(); - virtual void setAngularVelocity(glm::vec3 angularVelocity); + virtual glm::vec3 getPosition() override; + virtual void setPosition(glm::vec3 position) override; + virtual glm::quat getRotation() override; + virtual void setRotation(glm::quat rotation) override; + virtual glm::vec3 getLinearVelocity() override; + virtual void setLinearVelocity(glm::vec3 linearVelocity) override; + virtual glm::vec3 getAngularVelocity() override; + virtual void setAngularVelocity(glm::vec3 angularVelocity) override; bool _active; EntityItemWeakPointer _ownerEntity; diff --git a/assignment-client/src/AssignmentActionFactory.h b/assignment-client/src/AssignmentActionFactory.h index e2d58f3e6a..87970c9431 100644 --- a/assignment-client/src/AssignmentActionFactory.h +++ b/assignment-client/src/AssignmentActionFactory.h @@ -22,8 +22,8 @@ public: virtual EntityActionPointer factory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity, - QVariantMap arguments); - virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data); + QVariantMap arguments) override; + virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override; }; #endif // hifi_AssignmentActionFactory_h diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 07ff0a92b3..132fb51433 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -26,7 +26,7 @@ public: AssetServer(ReceivedMessage& message); public slots: - void run(); + void run() override; private slots: void completeSetup(); @@ -35,9 +35,9 @@ private slots: void handleAssetGet(QSharedPointer packet, SharedNodePointer senderNode); void handleAssetUpload(QSharedPointer packetList, SharedNodePointer senderNode); void handleAssetMappingOperation(QSharedPointer message, SharedNodePointer senderNode); - - void sendStatsPacket(); - + + void sendStatsPacket() override; + private: using Mappings = QVariantHash; diff --git a/assignment-client/src/assets/SendAssetTask.h b/assignment-client/src/assets/SendAssetTask.h index 9fef8662d4..38da5e728b 100644 --- a/assignment-client/src/assets/SendAssetTask.h +++ b/assignment-client/src/assets/SendAssetTask.h @@ -27,7 +27,7 @@ class SendAssetTask : public QRunnable { public: SendAssetTask(QSharedPointer message, const SharedNodePointer& sendToNode, const QDir& resourcesDir); - void run(); + void run() override; private: QSharedPointer _message; diff --git a/assignment-client/src/assets/UploadAssetTask.h b/assignment-client/src/assets/UploadAssetTask.h index aa7beccd5c..700eecbf9a 100644 --- a/assignment-client/src/assets/UploadAssetTask.h +++ b/assignment-client/src/assets/UploadAssetTask.h @@ -27,9 +27,9 @@ class Node; class UploadAssetTask : public QRunnable { public: UploadAssetTask(QSharedPointer message, QSharedPointer senderNode, const QDir& resourcesDir); - - void run(); - + + void run() override; + private: QSharedPointer _receivedMessage; QSharedPointer _senderNode; diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 9b26989847..7e3ef8a69b 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -35,9 +35,9 @@ public: public slots: /// threaded run of assignment - void run(); + void run() override; - void sendStatsPacket(); + void sendStatsPacket() override; static const InboundAudioStream::Settings& getStreamSettings() { return _streamSettings; } diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 106841ee03..2f8ff4d049 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -49,17 +49,17 @@ public: // removes an AudioHRTF object for a given stream void removeHRTFForStream(const QUuid& nodeID, const QUuid& streamID = QUuid()); - - int parseData(ReceivedMessage& message); + + int parseData(ReceivedMessage& message) override; void checkBuffersBeforeFrameSend(); void removeDeadInjectedStreams(); QJsonObject getAudioStreamStats(); - + void sendAudioStreamStatsPackets(const SharedNodePointer& destinationNode); - + void incrementOutgoingMixedAudioSequenceNumber() { _outgoingMixedAudioSequenceNumber++; } quint16 getOutgoingSequenceNumber() const { return _outgoingMixedAudioSequenceNumber; } diff --git a/assignment-client/src/audio/AvatarAudioStream.h b/assignment-client/src/audio/AvatarAudioStream.h index cc2ff1aca7..d2e1137ae6 100644 --- a/assignment-client/src/audio/AvatarAudioStream.h +++ b/assignment-client/src/audio/AvatarAudioStream.h @@ -25,7 +25,7 @@ private: AvatarAudioStream(const AvatarAudioStream&); AvatarAudioStream& operator= (const AvatarAudioStream&); - int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples); + int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) override; }; #endif // hifi_AvatarAudioStream_h diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 00cf457d40..9286cd4691 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -27,11 +27,11 @@ public: ~AvatarMixer(); public slots: /// runs the avatar mixer - void run(); + void run() override; void nodeKilled(SharedNodePointer killedNode); - - void sendStatsPacket(); + + void sendStatsPacket() override; private slots: void handleAvatarDataPacket(QSharedPointer message, SharedNodePointer senderNode); @@ -45,14 +45,14 @@ private slots: private: void broadcastAvatarData(); void parseDomainServerSettings(const QJsonObject& domainSettings); - + QThread _broadcastThread; - + p_high_resolution_clock::time_point _lastFrameTimestamp; - + float _trailingSleepRatio { 1.0f }; float _performanceThrottlingRatio { 0.0f }; - + int _sumListeners { 0 }; int _numStatFrames { 0 }; int _sumIdentityPackets { 0 }; diff --git a/assignment-client/src/entities/AssignmentParentFinder.h b/assignment-client/src/entities/AssignmentParentFinder.h index 0c16143143..0ac915817c 100644 --- a/assignment-client/src/entities/AssignmentParentFinder.h +++ b/assignment-client/src/entities/AssignmentParentFinder.h @@ -25,7 +25,8 @@ class AssignmentParentFinder : public SpatialParentFinder { public: AssignmentParentFinder(EntityTreePointer tree) : _tree(tree) { } virtual ~AssignmentParentFinder() { } - virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, SpatialParentTree* entityTree = nullptr) const; + virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, + SpatialParentTree* entityTree = nullptr) const override; protected: EntityTreePointer _tree; diff --git a/assignment-client/src/entities/EntityNodeData.h b/assignment-client/src/entities/EntityNodeData.h index 0ca0834fef..e1f8a91030 100644 --- a/assignment-client/src/entities/EntityNodeData.h +++ b/assignment-client/src/entities/EntityNodeData.h @@ -18,7 +18,7 @@ class EntityNodeData : public OctreeQueryNode { public: - virtual PacketType getMyPacketType() const { return PacketType::EntityData; } + virtual PacketType getMyPacketType() const override { return PacketType::EntityData; } quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; } void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; } diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index 76ae2f7195..800d42199b 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -24,9 +24,9 @@ public: MessagesMixer(ReceivedMessage& message); public slots: - void run(); + void run() override; void nodeKilled(SharedNodePointer killedNode); - void sendStatsPacket(); + void sendStatsPacket() override; private slots: void handleMessages(QSharedPointer message, SharedNodePointer senderNode); diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.h b/assignment-client/src/octree/OctreeInboundPacketProcessor.h index 8d0245c2b1..4611fcada0 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.h +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.h @@ -74,15 +74,15 @@ public: NodeToSenderStatsMap getSingleSenderStats() { QReadLocker locker(&_senderStatsLock); return _singleSenderStats; } - virtual void terminating() { _shuttingDown = true; ReceivedPacketProcessor::terminating(); } + virtual void terminating() override { _shuttingDown = true; ReceivedPacketProcessor::terminating(); } protected: - virtual void processPacket(QSharedPointer message, SharedNodePointer sendingNode); + virtual void processPacket(QSharedPointer message, SharedNodePointer sendingNode) override; - virtual unsigned long getMaxWait() const; - virtual void preProcess(); - virtual void midProcess(); + virtual unsigned long getMaxWait() const override; + virtual void preProcess() override; + virtual void midProcess() override; private: int sendNackPackets(); diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 166171c7f9..7efe5b3a86 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -47,7 +47,7 @@ public: protected: /// Implements generic processing behavior for this thread. - virtual bool process(); + virtual bool process() override; private: int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent, bool dontSuppressDuplicate = false); diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index d153f31154..beea322e9b 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -120,16 +120,16 @@ public: static int howManyThreadsDidHandlePacketSend(quint64 since = 0); static int howManyThreadsDidCallWriteDatagram(quint64 since = 0); - bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler); + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) override; - virtual void aboutToFinish(); + virtual void aboutToFinish() override; public slots: /// runs the octree server assignment - void run(); + void run() override; virtual void nodeAdded(SharedNodePointer node); virtual void nodeKilled(SharedNodePointer node); - void sendStatsPacket(); + void sendStatsPacket() override; private slots: void domainSettingsRequestComplete(); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 06b3ed2c0a..e30f4515cc 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -50,8 +50,8 @@ public: static int const EXIT_CODE_REBOOT; - bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); - bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler = false); + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override; + bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler = false) override; public slots: /// Called by NodeList to inform us a node has been added diff --git a/interface/src/FileLogger.h b/interface/src/FileLogger.h index 28ac6fba40..950590e789 100644 --- a/interface/src/FileLogger.h +++ b/interface/src/FileLogger.h @@ -48,7 +48,7 @@ signals: protected: void rollFileIfNecessary(QFile& file, bool notifyListenersIfRolled = true); - virtual bool processQueueItems(const Queue& messages); + virtual bool processQueueItems(const Queue& messages) override; private: const FileLogger& _logger; diff --git a/interface/src/InterfaceActionFactory.h b/interface/src/InterfaceActionFactory.h index 2031f5c57a..3e8a17d871 100644 --- a/interface/src/InterfaceActionFactory.h +++ b/interface/src/InterfaceActionFactory.h @@ -21,9 +21,9 @@ public: virtual EntityActionPointer factory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity, - QVariantMap arguments); + QVariantMap arguments) override; virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, - QByteArray data); + QByteArray data) override; }; #endif // hifi_InterfaceActionFactory_h diff --git a/interface/src/InterfaceParentFinder.h b/interface/src/InterfaceParentFinder.h index a2e9fb50e4..19f7e30dca 100644 --- a/interface/src/InterfaceParentFinder.h +++ b/interface/src/InterfaceParentFinder.h @@ -21,7 +21,8 @@ class InterfaceParentFinder : public SpatialParentFinder { public: InterfaceParentFinder() { } virtual ~InterfaceParentFinder() { } - virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, SpatialParentTree* entityTree = nullptr) const; + virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, + SpatialParentTree* entityTree = nullptr) const override; }; #endif // hifi_InterfaceParentFinder_h diff --git a/interface/src/ModelSelector.h b/interface/src/ModelSelector.h index 0ac3df5963..ee9e75c17a 100644 --- a/interface/src/ModelSelector.h +++ b/interface/src/ModelSelector.h @@ -24,23 +24,23 @@ class QPushButton; class ModelSelector : public QDialog { Q_OBJECT - + public: ModelSelector(); - + QFileInfo getFileInfo() const; FSTReader::ModelType getModelType() const; - + public slots: - virtual void accept(); - + virtual void accept() override; + private slots: void browse(); - + private: QFileInfo _modelFile; QPushButton* _browseButton; QComboBox* _modelType; }; -#endif // hifi_ModelSelector_h \ No newline at end of file +#endif // hifi_ModelSelector_h diff --git a/interface/src/ScriptHighlighting.h b/interface/src/ScriptHighlighting.h index 2eb40796e3..8952b1bcbd 100644 --- a/interface/src/ScriptHighlighting.h +++ b/interface/src/ScriptHighlighting.h @@ -26,7 +26,7 @@ public: }; protected: - void highlightBlock(const QString& text); + void highlightBlock(const QString& text) override; void highlightKeywords(const QString& text); void formatComments(const QString& text); void formatQuotedText(const QString& text); diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 33ea180d33..29cd06865c 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -82,12 +82,12 @@ public: void setDeltaRoll(float roll) { _deltaRoll = roll; } float getDeltaRoll() const { return _deltaRoll; } - virtual void setFinalYaw(float finalYaw); - virtual void setFinalPitch(float finalPitch); - virtual void setFinalRoll(float finalRoll); - virtual float getFinalPitch() const; - virtual float getFinalYaw() const; - virtual float getFinalRoll() const; + virtual void setFinalYaw(float finalYaw) override; + virtual void setFinalPitch(float finalPitch) override; + virtual void setFinalRoll(float finalRoll) override; + virtual float getFinalPitch() const override; + virtual float getFinalYaw() const override; + virtual float getFinalRoll() const override; void relax(float deltaTime); diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index 9673f541d2..931ab099e9 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -27,26 +27,26 @@ class DdeFaceTracker : public FaceTracker, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY - -public: - virtual void init(); - virtual void reset(); - virtual void update(float deltaTime); - virtual bool isActive() const; - virtual bool isTracking() const; - +public: + virtual void init() override; + virtual void reset() override; + virtual void update(float deltaTime) override; + + virtual bool isActive() const override; + virtual bool isTracking() const override; + float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); } float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); } float getLeftEyeOpen() const { return getBlendshapeCoefficient(_leftEyeOpenIndex); } float getRightEyeOpen() const { return getBlendshapeCoefficient(_rightEyeOpenIndex); } - + float getBrowDownLeft() const { return getBlendshapeCoefficient(_browDownLeftIndex); } float getBrowDownRight() const { return getBlendshapeCoefficient(_browDownRightIndex); } float getBrowUpCenter() const { return getBlendshapeCoefficient(_browUpCenterIndex); } float getBrowUpLeft() const { return getBlendshapeCoefficient(_browUpLeftIndex); } float getBrowUpRight() const { return getBlendshapeCoefficient(_browUpRightIndex); } - + float getMouthSize() const { return getBlendshapeCoefficient(_jawOpenIndex); } float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); } float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); } @@ -55,7 +55,7 @@ public: void setEyeClosingThreshold(float eyeClosingThreshold); public slots: - void setEnabled(bool enabled); + void setEnabled(bool enabled) override; void calibrate(); private slots: @@ -77,18 +77,18 @@ private: QHostAddress _host; quint16 _serverPort; quint16 _controlPort; - + float getBlendshapeCoefficient(int index) const; void decodePacket(const QByteArray& buffer); - + // sockets QUdpSocket _udpSocket; quint64 _lastReceiveTimestamp; - + bool _reset; glm::vec3 _referenceTranslation; glm::quat _referenceRotation; - + int _leftBlinkIndex; int _rightBlinkIndex; int _leftEyeDownIndex; @@ -103,10 +103,10 @@ private: int _browUpCenterIndex; int _browUpLeftIndex; int _browUpRightIndex; - + int _mouthSmileLeftIndex; int _mouthSmileRightIndex; - + int _jawOpenIndex; QVector _coefficients; diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h index 4cb7557410..48a4cd682a 100644 --- a/interface/src/devices/Faceshift.h +++ b/interface/src/devices/Faceshift.h @@ -49,7 +49,7 @@ public: // these pitch/yaw angles are in degrees float getEyeGazeLeftPitch() const { return _eyeGazeLeftPitch; } float getEyeGazeLeftYaw() const { return _eyeGazeLeftYaw; } - + float getEyeGazeRightPitch() const { return _eyeGazeRightPitch; } float getEyeGazeRightYaw() const { return _eyeGazeRightYaw; } @@ -67,10 +67,10 @@ public: float getMouthSize() const { return getBlendshapeCoefficient(_jawOpenIndex); } float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); } float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); } - + QString getHostname() { return _hostname.get(); } void setHostname(const QString& hostname); - + void updateFakeCoefficients(float leftBlink, float rightBlink, float browUp, @@ -79,76 +79,76 @@ public: float mouth3, float mouth4, QVector& coefficients) const; - + signals: void connectionStateChanged(); public slots: - void setEnabled(bool enabled); - + void setEnabled(bool enabled) override; + private slots: void connectSocket(); void noteConnected(); void noteError(QAbstractSocket::SocketError error); void readPendingDatagrams(); - void readFromSocket(); + void readFromSocket(); void noteDisconnected(); private: Faceshift(); virtual ~Faceshift() {} - + void send(const std::string& message); void receive(const QByteArray& buffer); - + QTcpSocket _tcpSocket; QUdpSocket _udpSocket; #ifdef HAVE_FACESHIFT fs::fsBinaryStream _stream; #endif - + bool _tcpEnabled = true; int _tcpRetryCount = 0; bool _tracking = false; quint64 _lastReceiveTimestamp = 0; quint64 _lastMessageReceived = 0; float _averageFrameTime = STARTING_FACESHIFT_FRAME_TIME; - + glm::vec3 _headAngularVelocity = glm::vec3(0.0f); glm::vec3 _headLinearVelocity = glm::vec3(0.0f); glm::vec3 _lastHeadTranslation = glm::vec3(0.0f); glm::vec3 _filteredHeadTranslation = glm::vec3(0.0f); - + // degrees float _eyeGazeLeftPitch = 0.0f; float _eyeGazeLeftYaw = 0.0f; float _eyeGazeRightPitch = 0.0f; float _eyeGazeRightYaw = 0.0f; - + // degrees float _longTermAverageEyePitch = 0.0f; float _longTermAverageEyeYaw = 0.0f; bool _longTermAverageInitialized = false; - + Setting::Handle _hostname; - + // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes int _leftBlinkIndex = 0; int _rightBlinkIndex = 1; int _leftEyeOpenIndex = 8; int _rightEyeOpenIndex = 9; - + // Brows int _browDownLeftIndex = 14; int _browDownRightIndex = 15; int _browUpCenterIndex = 16; int _browUpLeftIndex = 17; int _browUpRightIndex = 18; - + int _mouthSmileLeftIndex = 28; int _mouthSmileRightIndex = 29; - + int _jawOpenIndex = 21; }; diff --git a/interface/src/devices/Leapmotion.h b/interface/src/devices/Leapmotion.h index 266b9beb87..d7981a65e8 100644 --- a/interface/src/devices/Leapmotion.h +++ b/interface/src/devices/Leapmotion.h @@ -33,7 +33,7 @@ public: bool isActive() const { return _active; } - virtual void update(); + virtual void update() override; protected: Leapmotion(); diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index fc8d125839..50539e7a05 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -33,16 +33,16 @@ class InputController : public controller::InputController { public: InputController(int deviceTrackerId, int subTrackerId, QObject* parent = NULL); - virtual void update(); - virtual Key getKey() const; + virtual void update() override; + virtual Key getKey() const override; public slots: - virtual bool isActive() const { return _isActive; } - virtual glm::vec3 getAbsTranslation() const { return _eventCache.absTranslation; } - virtual glm::quat getAbsRotation() const { return _eventCache.absRotation; } - virtual glm::vec3 getLocTranslation() const { return _eventCache.locTranslation; } - virtual glm::quat getLocRotation() const { return _eventCache.locRotation; } + virtual bool isActive() const override { return _isActive; } + virtual glm::vec3 getAbsTranslation() const override { return _eventCache.absTranslation; } + virtual glm::quat getAbsRotation() const override { return _eventCache.absRotation; } + virtual glm::vec3 getLocTranslation() const override { return _eventCache.locTranslation; } + virtual glm::quat getLocRotation() const override { return _eventCache.locRotation; } private: diff --git a/interface/src/scripting/WebWindowClass.h b/interface/src/scripting/WebWindowClass.h index 8859eb5b37..d7a610dd39 100644 --- a/interface/src/scripting/WebWindowClass.h +++ b/interface/src/scripting/WebWindowClass.h @@ -66,7 +66,7 @@ signals: void closed(); protected: - virtual bool eventFilter(QObject* sender, QEvent* event); + virtual bool eventFilter(QObject* sender, QEvent* event) override; private slots: void hasClosed(); diff --git a/interface/src/ui/AudioStatsDialog.h b/interface/src/ui/AudioStatsDialog.h index 7f14cd6018..3abab258c4 100644 --- a/interface/src/ui/AudioStatsDialog.h +++ b/interface/src/ui/AudioStatsDialog.h @@ -36,13 +36,13 @@ public: AudioStatsDisplay(QFormLayout* form, QString text, unsigned colorRGBA); void updatedDisplay(QString str); void paint(); - + private: QString _strBuf; QLabel* _label; QString _text; unsigned _colorRGBA; - + }; //dialog @@ -51,9 +51,9 @@ class AudioStatsDialog : public QDialog { public: AudioStatsDialog(QWidget* parent); ~AudioStatsDialog(); - - void paintEvent(QPaintEvent*); - + + void paintEvent(QPaintEvent*) override; + private: // audio stats methods for rendering QVector _audioMixerStats; @@ -61,48 +61,47 @@ private: QVector _upstreamMixerStats; QVector _downstreamStats; QVector _upstreamInjectedStats; - + int _audioMixerID; int _upstreamClientID; int _upstreamMixerID; int _downstreamID; int _upstreamInjectedID; - + QVector> _audioDisplayChannels; - + int addChannel(QFormLayout* form, QVector& stats, const unsigned color); void updateStats(QVector& stats, const int channelID); void renderStats(); void clearAllChannels(); void renderAudioStreamStats(const AudioStreamStats* streamStats, QVector* audioStreamstats, bool isDownstreamStats); - - + + const AudioIOStats* _stats; QFormLayout* _form; - + bool _isEnabled; bool _shouldShowInjectedStreams; - - + + signals: - - + + void closed(); - + public slots: - - - void reject(); + + + void reject() override; void updateTimerTimeout(); - + protected: - + // Emits a 'closed' signal when this dialog is closed. - void closeEvent(QCloseEvent*); - + void closeEvent(QCloseEvent*) override; + private: QTimer* averageUpdateTimer = new QTimer(this); - }; diff --git a/interface/src/ui/BandwidthDialog.h b/interface/src/ui/BandwidthDialog.h index 1fc8627191..a53cc21030 100644 --- a/interface/src/ui/BandwidthDialog.h +++ b/interface/src/ui/BandwidthDialog.h @@ -57,7 +57,7 @@ public: BandwidthDialog(QWidget* parent); ~BandwidthDialog(); - void paintEvent(QPaintEvent*); + void paintEvent(QPaintEvent*) override; private: BandwidthChannelDisplay* _audioChannelDisplay; @@ -77,14 +77,14 @@ signals: public slots: - void reject(); + void reject() override; void updateTimerTimeout(); protected: // Emits a 'closed' signal when this dialog is closed. - void closeEvent(QCloseEvent*); + void closeEvent(QCloseEvent*) override; private: QTimer* averageUpdateTimer = new QTimer(this); diff --git a/interface/src/ui/CachesSizeDialog.h b/interface/src/ui/CachesSizeDialog.h index fa01ce4534..025d0f2bac 100644 --- a/interface/src/ui/CachesSizeDialog.h +++ b/interface/src/ui/CachesSizeDialog.h @@ -21,19 +21,19 @@ class CachesSizeDialog : public QDialog { public: // Sets up the UI CachesSizeDialog(QWidget* parent); - + signals: void closed(); - + public slots: - void reject(); + void reject() override; void confirmClicked(bool checked); void resetClicked(bool checked); - + protected: // Emits a 'closed' signal when this dialog is closed. - void closeEvent(QCloseEvent* event); - + void closeEvent(QCloseEvent* event) override; + private: QDoubleSpinBox* _animations = nullptr; QDoubleSpinBox* _geometries = nullptr; @@ -42,4 +42,4 @@ private: QDoubleSpinBox* _textures = nullptr; }; -#endif // hifi_CachesSizeDialog_h \ No newline at end of file +#endif // hifi_CachesSizeDialog_h diff --git a/interface/src/ui/DataWebPage.h b/interface/src/ui/DataWebPage.h index c1c343a216..f9aa5be8a8 100644 --- a/interface/src/ui/DataWebPage.h +++ b/interface/src/ui/DataWebPage.h @@ -18,9 +18,9 @@ class DataWebPage : public QWebPage { public: DataWebPage(QObject* parent = 0); protected: - void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID); - bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type); - virtual QString userAgentForUrl(const QUrl& url) const; + void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID) override; + bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type) override; + virtual QString userAgentForUrl(const QUrl& url) const override; }; #endif // hifi_DataWebPage_h diff --git a/interface/src/ui/HMDToolsDialog.h b/interface/src/ui/HMDToolsDialog.h index 7d30fc17f6..16ea090d95 100644 --- a/interface/src/ui/HMDToolsDialog.h +++ b/interface/src/ui/HMDToolsDialog.h @@ -29,18 +29,18 @@ public: QScreen* getLastApplicationScreen() const { return _previousScreen; } bool hasHMDScreen() const { return _hmdScreenNumber >= -1; } void watchWindow(QWindow* window); - + signals: void closed(); public slots: - void reject(); + void reject() override; void screenCountChanged(int newCount); - + protected: - virtual void closeEvent(QCloseEvent*); // Emits a 'closed' signal when this dialog is closed. - virtual void showEvent(QShowEvent* event); - virtual void hideEvent(QHideEvent* event); + virtual void closeEvent(QCloseEvent*) override; // Emits a 'closed' signal when this dialog is closed. + virtual void showEvent(QShowEvent* event) override; + virtual void hideEvent(QHideEvent* event) override; private: void centerCursorOnWidget(QWidget* widget); @@ -59,7 +59,7 @@ private: QScreen* _previousDialogScreen{ nullptr }; QString _hmdPluginName; QString _defaultPluginName; - + QHash _windowWatchers; friend class HMDWindowWatcher; }; @@ -75,7 +75,7 @@ public: public slots: void windowScreenChanged(QScreen* screen); void windowGeometryChanged(int arg); - + private: QWindow* _window; HMDToolsDialog* _hmdTools; diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index cb58beab35..47878fea99 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -40,9 +40,9 @@ public slots: protected: void setAndSelectCommand(const QString& command); - virtual bool eventFilter(QObject* sender, QEvent* event); - virtual void mouseReleaseEvent(QMouseEvent* event); - virtual void showEvent(QShowEvent* event); + virtual bool eventFilter(QObject* sender, QEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void showEvent(QShowEvent* event) override; protected slots: void scrollToBottom(); diff --git a/interface/src/ui/LodToolsDialog.h b/interface/src/ui/LodToolsDialog.h index e5a2dae836..b2390a1cd7 100644 --- a/interface/src/ui/LodToolsDialog.h +++ b/interface/src/ui/LodToolsDialog.h @@ -24,12 +24,12 @@ class LodToolsDialog : public QDialog { public: // Sets up the UI LodToolsDialog(QWidget* parent); - + signals: void closed(); public slots: - void reject(); + void reject() override; void sizeScaleValueChanged(int value); void resetClicked(bool checked); void reloadSliders(); @@ -38,7 +38,7 @@ public slots: protected: // Emits a 'closed' signal when this dialog is closed. - void closeEvent(QCloseEvent* event); + void closeEvent(QCloseEvent* event) override; private: QSlider* _lodSize; diff --git a/interface/src/ui/LogDialog.h b/interface/src/ui/LogDialog.h index 999a8394cc..c38cf84f00 100644 --- a/interface/src/ui/LogDialog.h +++ b/interface/src/ui/LogDialog.h @@ -30,7 +30,7 @@ public: QString keyword; protected: - void highlightBlock(const QString &text); + void highlightBlock(const QString &text) override; private: QTextCharFormat keywordFormat; @@ -54,8 +54,8 @@ private slots: void handleSearchTextChanged(const QString); protected: - void resizeEvent(QResizeEvent*); - void showEvent(QShowEvent*); + void resizeEvent(QResizeEvent*) override; + void showEvent(QShowEvent*) override; private: QPushButton* _searchButton; diff --git a/interface/src/ui/OctreeStatsDialog.h b/interface/src/ui/OctreeStatsDialog.h index 97f87849ec..67f5c01f65 100644 --- a/interface/src/ui/OctreeStatsDialog.h +++ b/interface/src/ui/OctreeStatsDialog.h @@ -33,15 +33,15 @@ signals: void closed(); public slots: - void reject(); + void reject() override; void moreless(const QString& link); protected: // State <- data model held by BandwidthMeter - void paintEvent(QPaintEvent*); + void paintEvent(QPaintEvent*) override; // Emits a 'closed' signal when this dialog is closed. - void closeEvent(QCloseEvent*); + void closeEvent(QCloseEvent*) override; int AddStatItem(const char* caption, unsigned colorRGBA = DEFAULT_COLOR); void RemoveStatItem(int item); diff --git a/interface/src/ui/ScriptEditBox.h b/interface/src/ui/ScriptEditBox.h index 41d881b904..0b037db16a 100644 --- a/interface/src/ui/ScriptEditBox.h +++ b/interface/src/ui/ScriptEditBox.h @@ -24,7 +24,7 @@ public: int lineNumberAreaWidth(); protected: - void resizeEvent(QResizeEvent* event); + void resizeEvent(QResizeEvent* event) override; private slots: void updateLineNumberAreaWidth(int blockCount); diff --git a/interface/src/ui/ScriptEditorWindow.h b/interface/src/ui/ScriptEditorWindow.h index 185a0f40bd..af9863d136 100644 --- a/interface/src/ui/ScriptEditorWindow.h +++ b/interface/src/ui/ScriptEditorWindow.h @@ -35,8 +35,8 @@ signals: void windowActivated(); protected: - void closeEvent(QCloseEvent* event); - virtual bool event(QEvent* event); + void closeEvent(QCloseEvent* event) override; + virtual bool event(QEvent* event) override; private: Ui::ScriptEditorWindow* _ScriptEditorWindowUI; diff --git a/interface/src/ui/ScriptLineNumberArea.h b/interface/src/ui/ScriptLineNumberArea.h index 00bd078170..77de8244ce 100644 --- a/interface/src/ui/ScriptLineNumberArea.h +++ b/interface/src/ui/ScriptLineNumberArea.h @@ -20,10 +20,10 @@ class ScriptLineNumberArea : public QWidget { public: ScriptLineNumberArea(ScriptEditBox* scriptEditBox); - QSize sizeHint() const; + QSize sizeHint() const override; protected: - void paintEvent(QPaintEvent* event); + void paintEvent(QPaintEvent* event) override; private: ScriptEditBox* _scriptEditBox; diff --git a/interface/src/ui/ScriptsTableWidget.h b/interface/src/ui/ScriptsTableWidget.h index 4b54130e82..f5e3407e97 100644 --- a/interface/src/ui/ScriptsTableWidget.h +++ b/interface/src/ui/ScriptsTableWidget.h @@ -21,8 +21,8 @@ public: explicit ScriptsTableWidget(QWidget* parent); protected: - virtual void paintEvent(QPaintEvent* event); - virtual void keyPressEvent(QKeyEvent* event); + virtual void paintEvent(QPaintEvent* event) override; + virtual void keyPressEvent(QKeyEvent* event) override; }; #endif // hifi__ScriptsTableWidget_h diff --git a/interface/src/ui/overlays/LocalModelsOverlay.h b/interface/src/ui/overlays/LocalModelsOverlay.h index 011f3dfb11..6bab7f5f86 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.h +++ b/interface/src/ui/overlays/LocalModelsOverlay.h @@ -20,15 +20,15 @@ class LocalModelsOverlay : public Volume3DOverlay { Q_OBJECT public: static QString const TYPE; - virtual QString getType() const { return TYPE; } + virtual QString getType() const override { return TYPE; } LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer); LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay); - - virtual void update(float deltatime); - virtual void render(RenderArgs* args); - virtual LocalModelsOverlay* createClone() const; + virtual void update(float deltatime) override; + virtual void render(RenderArgs* args) override; + + virtual LocalModelsOverlay* createClone() const override; private: EntityTreeRenderer* _entityTreeRenderer; diff --git a/interface/src/ui/overlays/OverlayPanel.h b/interface/src/ui/overlays/OverlayPanel.h index df553883f1..b0b8cdb989 100644 --- a/interface/src/ui/overlays/OverlayPanel.h +++ b/interface/src/ui/overlays/OverlayPanel.h @@ -62,7 +62,7 @@ public: void setProperties(const QVariantMap& properties); QVariant getProperty(const QString& property); - virtual void applyTransformTo(Transform& transform, bool force = false); + virtual void applyTransformTo(Transform& transform, bool force = false) override; private: Transform _anchorTransform; diff --git a/interface/src/ui/overlays/RectangleOverlay.h b/interface/src/ui/overlays/RectangleOverlay.h index 06e2fb228c..c429210c42 100644 --- a/interface/src/ui/overlays/RectangleOverlay.h +++ b/interface/src/ui/overlays/RectangleOverlay.h @@ -14,14 +14,13 @@ class RectangleOverlay : public QmlOverlay { public: static QString const TYPE; - virtual QString getType() const { return TYPE; } + virtual QString getType() const override { return TYPE; } static QUrl const URL; RectangleOverlay(); RectangleOverlay(const RectangleOverlay* RectangleOverlay); - virtual RectangleOverlay* createClone() const; + virtual RectangleOverlay* createClone() const override; }; - #endif // hifi_RectangleOverlay_h diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index 9da649e66e..a7d8700fed 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -34,9 +34,9 @@ public: Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url); protected: - + virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - const void* extra); + const void* extra) override; private: explicit AnimationCache(QObject* parent = NULL); virtual ~AnimationCache() { } @@ -82,7 +82,7 @@ class AnimationReader : public QObject, public QRunnable { public: AnimationReader(const QUrl& url, const QByteArray& data); - virtual void run(); + virtual void run() override; signals: void onSuccess(FBXGeometry::Pointer geometry); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 171014edda..b83547ce00 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -86,7 +86,7 @@ public: using Mutex = std::mutex; using Lock = std::unique_lock; - + class AudioOutputIODevice : public QIODevice { public: AudioOutputIODevice(MixedProcessedAudioStream& receivedAudioStream, AudioClient* audio) : @@ -94,8 +94,8 @@ public: void start() { open(QIODevice::ReadOnly); } void stop() { close(); } - qint64 readData(char * data, qint64 maxSize); - qint64 writeData(const char * data, qint64 maxSize) { return 0; } + qint64 readData(char * data, qint64 maxSize) override; + qint64 writeData(const char * data, qint64 maxSize) override { return 0; } int getRecentUnfulfilledReads() { int unfulfilledReads = _unfulfilledReads; _unfulfilledReads = 0; return unfulfilledReads; } private: MixedProcessedAudioStream& _receivedAudioStream; @@ -136,7 +136,7 @@ public: void setPositionGetter(AudioPositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(AudioOrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } - + QVector& getActiveLocalAudioInjectors() { return _activeLocalAudioInjectors; } static const float CALLBACK_ACCELERATOR_RATIO; @@ -163,7 +163,7 @@ public slots: void audioMixerKilled(); void toggleMute(); - virtual void setIsStereoInput(bool stereo); + virtual void setIsStereoInput(bool stereo) override; void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; } @@ -175,7 +175,7 @@ public slots: int setOutputBufferSize(int numFrames, bool persist = true); - virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector); + virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector) override; bool switchInputToAudioDevice(const QString& inputDeviceName); bool switchOutputToAudioDevice(const QString& outputDeviceName); @@ -215,7 +215,7 @@ protected: AudioClient(); ~AudioClient(); - virtual void customDeleter() { + virtual void customDeleter() override { deleteLater(); } @@ -316,7 +316,7 @@ private: void checkDevices(); bool _hasReceivedFirstPacket = false; - + QVector _activeLocalAudioInjectors; CodecPluginPointer _codec; diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.h b/libraries/audio/src/AudioInjectorLocalBuffer.h index 32a1221b78..988b622ce7 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.h +++ b/libraries/audio/src/AudioInjectorLocalBuffer.h @@ -20,27 +20,27 @@ class AudioInjectorLocalBuffer : public QIODevice { Q_OBJECT public: AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent); - + void stop(); - - bool seek(qint64 pos); - - qint64 readData(char* data, qint64 maxSize); - qint64 writeData(const char* data, qint64 maxSize) { return 0; } - + + bool seek(qint64 pos) override; + + qint64 readData(char* data, qint64 maxSize) override; + qint64 writeData(const char* data, qint64 maxSize) override { return 0; } + void setShouldLoop(bool shouldLoop) { _shouldLoop = shouldLoop; } void setCurrentOffset(int currentOffset) { _currentOffset = currentOffset; } void setVolume(float volume) { _volume = glm::clamp(volume, 0.0f, 1.0f); } - + private: qint64 recursiveReadFromFront(char* data, qint64 maxSize); - + QByteArray _rawAudioArray; bool _shouldLoop; bool _isStopped; - + int _currentOffset; float _volume; }; -#endif // hifi_AudioInjectorLocalBuffer_h \ No newline at end of file +#endif // hifi_AudioInjectorLocalBuffer_h diff --git a/libraries/audio/src/AudioLimiter.cpp b/libraries/audio/src/AudioLimiter.cpp index d9257b7df5..b5550fba0c 100644 --- a/libraries/audio/src/AudioLimiter.cpp +++ b/libraries/audio/src/AudioLimiter.cpp @@ -567,7 +567,7 @@ class LimiterMono : public LimiterImpl { public: LimiterMono(int sampleRate) : LimiterImpl(sampleRate) {} - void process(float* input, int16_t* output, int numFrames); + void process(float* input, int16_t* output, int numFrames) override; }; template diff --git a/libraries/audio/src/MixedProcessedAudioStream.h b/libraries/audio/src/MixedProcessedAudioStream.h index 2f9a691278..b2127abcf7 100644 --- a/libraries/audio/src/MixedProcessedAudioStream.h +++ b/libraries/audio/src/MixedProcessedAudioStream.h @@ -22,7 +22,7 @@ public: MixedProcessedAudioStream(int numFrameSamples, int numFramesCapacity, const InboundAudioStream::Settings& settings); signals: - + void addedSilence(int silentSamplesPerChannel); void addedLastFrameRepeatedWithFade(int samplesPerChannel); void addedStereoSamples(const QByteArray& samples); @@ -33,9 +33,9 @@ public: void outputFormatChanged(int outputFormatChannelCountTimesSampleRate); protected: - int writeDroppableSilentSamples(int silentSamples); - int writeLastFrameRepeatedWithFade(int samples); - int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties); + int writeDroppableSilentSamples(int silentSamples) override; + int writeLastFrameRepeatedWithFade(int samples) override; + int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) override; private: int networkToDeviceSamples(int networkSamples); diff --git a/libraries/audio/src/PositionalAudioStream.h b/libraries/audio/src/PositionalAudioStream.h index 5b50cfa60e..a330fbdd3f 100644 --- a/libraries/audio/src/PositionalAudioStream.h +++ b/libraries/audio/src/PositionalAudioStream.h @@ -32,9 +32,9 @@ public: const QUuid DEFAULT_STREAM_IDENTIFIER = QUuid(); virtual const QUuid& getStreamIdentifier() const { return DEFAULT_STREAM_IDENTIFIER; } - virtual void resetStats(); + virtual void resetStats() override; - virtual AudioStreamStats getAudioStreamStats() const; + virtual AudioStreamStats getAudioStreamStats() const override; void updateLastPopOutputLoudnessAndTrailingLoudness(); float getLastPopOutputTrailingLoudness() const { return _lastPopOutputTrailingLoudness; } @@ -46,7 +46,7 @@ public: PositionalAudioStream::Type getType() const { return _type; } const glm::vec3& getPosition() const { return _position; } const glm::quat& getOrientation() const { return _orientation; } - + protected: // disallow copying of PositionalAudioStream objects diff --git a/libraries/audio/src/SoundCache.h b/libraries/audio/src/SoundCache.h index 59a25dd847..97d5f659d8 100644 --- a/libraries/audio/src/SoundCache.h +++ b/libraries/audio/src/SoundCache.h @@ -23,12 +23,12 @@ class SoundCache : public ResourceCache, public Dependency { public: Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url); - + protected: virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - const void* extra); + const void* extra) override; private: SoundCache(QObject* parent = NULL); }; -#endif // hifi_SoundCache_h \ No newline at end of file +#endif // hifi_SoundCache_h diff --git a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h index 958914264e..24d5ec93e9 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h @@ -24,11 +24,11 @@ public: : Endpoint(Input::INVALID_INPUT), _callable(callable) { } - virtual float peek() const { + virtual float peek() const override { return (float)const_cast(this)->_callable.call().toNumber(); } - virtual void apply(float newValue, const Pointer& source) { + virtual void apply(float newValue, const Pointer& source) override { _callable.call(QJSValueList({ QJSValue(newValue) })); } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index c916fcafe2..6904700be5 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -305,7 +305,7 @@ void HmdDisplayPlugin::updateFrameData() { { vec2 xdir = glm::normalize(vec2(intersectionPosition.x, -intersectionPosition.z)); yawPitch.x = glm::atan(xdir.x, xdir.y); - yawPitch.y = (acosf(intersectionPosition.y) * -1.0f) + M_PI_2; + yawPitch.y = (acosf(intersectionPosition.y) * -1.0f) + (float)M_PI_2; } vec2 halfFov = CompositorHelper::VIRTUAL_UI_TARGET_FOV / 2.0f; diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index c560c43a5e..cb76eed9f2 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -35,7 +35,7 @@ public: /// Initializes the manager. HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = NULL, QObject* parent = 0); - bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override; private slots: void isTcpServerListening(); @@ -46,7 +46,7 @@ private: protected: /// Accepts all pending connections - virtual void incomingConnection(qintptr socketDescriptor); + virtual void incomingConnection(qintptr socketDescriptor) override; virtual bool requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url); QHostAddress _listenAddress; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 99c62ab5f6..5d8db371b8 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -44,10 +44,10 @@ public: AbstractScriptingServicesInterface* scriptingServices); virtual ~EntityTreeRenderer(); - virtual char getMyNodeType() const { return NodeType::EntityServer; } - virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; } - virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; } - virtual void setTree(OctreePointer newTree); + virtual char getMyNodeType() const override { return NodeType::EntityServer; } + virtual PacketType getMyQueryMessageType() const override { return PacketType::EntityQuery; } + virtual PacketType getExpectedPacketType() const override { return PacketType::EntityData; } + virtual void setTree(OctreePointer newTree) override; // Returns the priority at which an entity should be loaded. Higher values indicate higher priority. float getEntityLoadingPriority(const EntityItem& item) const { return _calculateEntityLoadingPriorityFunc(item); } @@ -60,29 +60,29 @@ public: void processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode); - virtual void init(); + virtual void init() override; + + virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) override; + virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) override; + virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem) override; - virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem); - virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem); - virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem); - /// clears the tree - virtual void clear(); + virtual void clear() override; /// reloads the entity scripts, calling unload and preload void reloadEntityScripts(); /// if a renderable entity item needs a model, we will allocate it for them Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority = 0.0f); - + /// if a renderable entity item needs to update the URL of a model, we will handle that for the entity Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl, const QString& collisionUrl); /// if a renderable entity item is done with a model, it should return it to us void releaseModel(ModelPointer model); - + void deleteReleasedModels(); - + // event handles which may generate entity related events void mouseReleaseEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event); @@ -128,7 +128,7 @@ public slots: void setDontDoPrecisionPicking(bool value) { _dontDoPrecisionPicking = value; } protected: - virtual OctreePointer createTree() { + virtual OctreePointer createTree() override { EntityTreePointer newTree = EntityTreePointer(new EntityTree(true)); newTree->createRootElement(); return newTree; @@ -180,7 +180,7 @@ private: AbstractScriptingServicesInterface* _scriptingServices; bool _displayModelBounds; bool _dontDoPrecisionPicking; - + bool _shuttingDown { false }; QMultiMap _waitingOnPreload; @@ -203,7 +203,7 @@ private: float _previousStageAltitude; float _previousStageHour; int _previousStageDay; - + QHash _entitiesInScene; // For Scene.shouldRenderEntities QList _entityIDsLastInScene; diff --git a/libraries/entities/src/AddEntityOperator.h b/libraries/entities/src/AddEntityOperator.h index 6aa2d5f727..0f33cacae3 100644 --- a/libraries/entities/src/AddEntityOperator.h +++ b/libraries/entities/src/AddEntityOperator.h @@ -16,9 +16,9 @@ class AddEntityOperator : public RecurseOctreeOperator { public: AddEntityOperator(EntityTreePointer tree, EntityItemPointer newEntity); - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element); - virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex); + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override; + virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override; private: EntityTreePointer _tree; EntityItemPointer _newEntity; diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h index 3ee452cc5f..4284be1eda 100644 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ b/libraries/entities/src/AnimationPropertyGroup.h @@ -34,30 +34,33 @@ public: void associateWithAnimationLoop(AnimationLoop* animationLoop) { _animationLoop = animationLoop; } // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); - virtual void debugDump() const; - virtual void listChangedProperties(QList& out); + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, - OctreeElement::AppendState& appendState) const; + OctreeElement::AppendState& appendState) const override; - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes); - virtual void markAllChanged(); - virtual EntityPropertyFlags getChangedProperties() const; + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; // EntityItem related helpers // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const; + virtual void getProperties(EntityItemProperties& propertiesOut) const override; /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties); + virtual bool setProperties(const EntityItemProperties& properties) override; - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, @@ -65,12 +68,12 @@ public: EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, - OctreeElement::AppendState& appendState) const; + OctreeElement::AppendState& appendState) const override; virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, URL, url, QString, ""); DEFINE_PROPERTY(PROP_ANIMATION_FPS, FPS, fps, float, 30.0f); diff --git a/libraries/entities/src/DeleteEntityOperator.h b/libraries/entities/src/DeleteEntityOperator.h index ae2ec3621e..245d331743 100644 --- a/libraries/entities/src/DeleteEntityOperator.h +++ b/libraries/entities/src/DeleteEntityOperator.h @@ -36,8 +36,8 @@ public: ~DeleteEntityOperator(); void addEntityIDToDeleteList(const EntityItemID& searchEntityID); - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element); + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override; const RemovedEntities& getEntities() const { return _entitiesToDelete; } private: diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 1991142f3f..9150748a68 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -42,8 +42,8 @@ public: void queueEraseEntityMessage(const EntityItemID& entityItemID); // My server type is the model server - virtual char getMyNodeType() const { return NodeType::EntityServer; } - virtual void adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, qint64 clockSkew); + virtual char getMyNodeType() const override { return NodeType::EntityServer; } + virtual void adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, qint64 clockSkew) override; public slots: void processEntityEditNackPacket(QSharedPointer message, SharedNodePointer sendingNode); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 3677a0bbc8..fd2cf41b77 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -53,7 +53,7 @@ namespace render { } #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0; -#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { }; +#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() override { }; #define debugTime(T, N) qPrintable(QString("%1 [ %2 ago]").arg(T, 16, 10).arg(formatUsecTime(N - T), 15)) #define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10)) diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 5aa0f5907e..afc529bc53 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -59,15 +59,15 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra /// handles scripting of Entity commands from JS passed to assigned clients class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency { Q_OBJECT - + Q_PROPERTY(float currentAvatarEnergy READ getCurrentAvatarEnergy WRITE setCurrentAvatarEnergy) Q_PROPERTY(float costMultiplier READ getCostMultiplier WRITE setCostMultiplier) public: EntityScriptingInterface(bool bidOnSimulationOwnership); EntityEditPacketSender* getEntityPacketSender() const { return (EntityEditPacketSender*)getPacketSender(); } - virtual NodeType_t getServerNodeType() const { return NodeType::EntityServer; } - virtual OctreeEditPacketSender* createPacketSender() { return new EntityEditPacketSender(); } + virtual NodeType_t getServerNodeType() const override { return NodeType::EntityServer; } + virtual OctreeEditPacketSender* createPacketSender() override { return new EntityEditPacketSender(); } void setEntityTree(EntityTreePointer modelTree); EntityTreePointer getEntityTree() { return _entityTree; } @@ -221,12 +221,12 @@ private: std::recursive_mutex _entitiesScriptEngineLock; EntitiesScriptEngineProvider* _entitiesScriptEngine { nullptr }; - + bool _bidOnSimulationOwnership { false }; float _currentAvatarEnergy = { FLT_MAX }; float getCurrentAvatarEnergy() { return _currentAvatarEnergy; } void setCurrentAvatarEnergy(float energy); - + float costMultiplier = { 0.01f }; float getCostMultiplier(); void setCostMultiplier(float value); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 848d473321..4cdebc364c 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1304,8 +1304,8 @@ void EntityTree::debugDumpMap() { class ContentsDimensionOperator : public RecurseOctreeOperator { public: - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element) { return true; } + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override { return true; } glm::vec3 getDimensions() const { return _contentExtents.size(); } float getLargestDimension() const { return _contentExtents.largestDimension(); } private: @@ -1332,8 +1332,8 @@ float EntityTree::getContentsLargestDimension() { class DebugOperator : public RecurseOctreeOperator { public: - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element) { return true; } + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override { return true; } }; bool DebugOperator::preRecursion(OctreeElementPointer element) { @@ -1350,8 +1350,8 @@ void EntityTree::dumpTree() { class PruneOperator : public RecurseOctreeOperator { public: - virtual bool preRecursion(OctreeElementPointer element) { return true; } - virtual bool postRecursion(OctreeElementPointer element); + virtual bool preRecursion(OctreeElementPointer element) override { return true; } + virtual bool postRecursion(OctreeElementPointer element) override; }; bool PruneOperator::postRecursion(OctreeElementPointer element) { diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index aa05438bde..4875e258da 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -80,7 +80,7 @@ class EntityTreeElement : public OctreeElement, ReadWriteLockable { EntityTreeElement(unsigned char* octalCode = NULL); - virtual OctreeElementPointer createNewElement(unsigned char* octalCode = NULL); + virtual OctreeElementPointer createNewElement(unsigned char* octalCode = NULL) override; public: virtual ~EntityTreeElement(); @@ -93,67 +93,69 @@ public: // methods you can and should override to implement your tree functionality /// Adds a child to the current element. Override this if there is additional child initialization your class needs. - virtual OctreeElementPointer addChildAtIndex(int index); + virtual OctreeElementPointer addChildAtIndex(int index) override; /// Override this to implement LOD averaging on changes to the tree. - virtual void calculateAverageFromChildren(); + virtual void calculateAverageFromChildren() override; /// Override this to implement LOD collapsing and identical child pruning on changes to the tree. - virtual bool collapseChildren(); + virtual bool collapseChildren() override; /// Should this element be considered to have content in it. This will be used in collision and ray casting methods. /// By default we assume that only leaves are actual content, but some octrees may have different semantics. - virtual bool hasContent() const { return hasEntities(); } + virtual bool hasContent() const override { return hasEntities(); } /// Should this element be considered to have detailed content in it. Specifically should it be rendered. /// By default we assume that only leaves have detailed content, but some octrees may have different semantics. - virtual bool hasDetailedContent() const { return hasEntities(); } + virtual bool hasDetailedContent() const override { return hasEntities(); } /// Override this to break up large octree elements when an edit operation is performed on a smaller octree element. /// For example, if the octrees represent solid cubes and a delete of a smaller octree element is done then the /// meaningful split would be to break the larger cube into smaller cubes of the same color/texture. - virtual void splitChildren() { } + virtual void splitChildren() override { } /// Override to indicate that this element requires a split before editing lower elements in the octree - virtual bool requiresSplit() const { return false; } + virtual bool requiresSplit() const override { return false; } - virtual void debugExtraEncodeData(EncodeBitstreamParams& params) const; - virtual void initializeExtraEncodeData(EncodeBitstreamParams& params); - virtual bool shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const; - virtual bool shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const; - virtual void updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const; - virtual void elementEncodeComplete(EncodeBitstreamParams& params) const; + virtual void debugExtraEncodeData(EncodeBitstreamParams& params) const override; + virtual void initializeExtraEncodeData(EncodeBitstreamParams& params) override; + virtual bool shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const override; + virtual bool shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const override; + virtual void updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const override; + virtual void elementEncodeComplete(EncodeBitstreamParams& params) const override; bool alreadyFullyEncoded(EncodeBitstreamParams& params) const; /// Override to serialize the state of this element. This is used for persistance and for transmission across the network. - virtual OctreeElement::AppendState appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const; + virtual OctreeElement::AppendState appendElementData(OctreePacketData* packetData, + EncodeBitstreamParams& params) const override; /// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading /// from the network. - virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); + virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args) override; /// Override to indicate that the item is currently rendered in the rendering engine. By default we assume that if /// the element should be rendered, then your rendering engine is rendering. But some rendering engines my have cases /// where an element is not actually rendering all should render elements. If the isRendered() state doesn't match the /// shouldRender() state, the tree will remark elements as changed even in cases there the elements have not changed. - virtual bool isRendered() const { return getShouldRender(); } - virtual bool deleteApproved() const { return !hasEntities(); } + virtual bool isRendered() const override { return getShouldRender(); } + virtual bool deleteApproved() const override { return !hasEntities(); } - virtual bool canRayIntersect() const { return hasEntities(); } + virtual bool canRayIntersect() const override { return hasEntities(); } virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& node, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, void** intersectedObject = NULL, bool precisionPicking = false); virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, + bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, void** intersectedObject, bool precisionPicking, float distanceToElementCube); virtual bool findSpherePenetration(const glm::vec3& center, float radius, - glm::vec3& penetration, void** penetratedObject) const; + glm::vec3& penetration, void** penetratedObject) const override; template @@ -232,7 +234,7 @@ public: } protected: - virtual void init(unsigned char * octalCode); + virtual void init(unsigned char * octalCode) override; EntityTreePointer _myTree; EntityItems _entityItems; }; diff --git a/libraries/entities/src/EntityTreeHeadlessViewer.h b/libraries/entities/src/EntityTreeHeadlessViewer.h index 0e0d4f9726..fe05323a0b 100644 --- a/libraries/entities/src/EntityTreeHeadlessViewer.h +++ b/libraries/entities/src/EntityTreeHeadlessViewer.h @@ -30,9 +30,9 @@ public: EntityTreeHeadlessViewer(); virtual ~EntityTreeHeadlessViewer(); - virtual char getMyNodeType() const { return NodeType::EntityServer; } - virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; } - virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; } + virtual char getMyNodeType() const override { return NodeType::EntityServer; } + virtual PacketType getMyQueryMessageType() const override { return PacketType::EntityQuery; } + virtual PacketType getExpectedPacketType() const override { return PacketType::EntityData; } void update(); @@ -40,10 +40,10 @@ public: void processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode); - virtual void init(); + virtual void init() override; protected: - virtual OctreePointer createTree() { + virtual OctreePointer createTree() override { EntityTreePointer newTree = EntityTreePointer(new EntityTree(true)); newTree->createRootElement(); return newTree; diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h index b905a5a997..029a257bf1 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -30,57 +30,57 @@ class ReadBitstreamToTreeParams; class KeyLightPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); - virtual void debugDump() const; - virtual void listChangedProperties(QList& out); + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; - virtual bool appendToEditPacket(OctreePacketData* packetData, + virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes); - virtual void markAllChanged(); - virtual EntityPropertyFlags getChangedProperties() const; + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; // EntityItem related helpers // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties); + virtual void getProperties(EntityItemProperties& propertiesOut) const override; - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); - + bool& somethingChanged) override; + static const xColor DEFAULT_KEYLIGHT_COLOR; static const float DEFAULT_KEYLIGHT_INTENSITY; static const float DEFAULT_KEYLIGHT_AMBIENT_INTENSITY; static const glm::vec3 DEFAULT_KEYLIGHT_DIRECTION; - + DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, Color, color, xColor, DEFAULT_KEYLIGHT_COLOR); DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, Intensity, intensity, float, DEFAULT_KEYLIGHT_INTENSITY); DEFINE_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, AmbientIntensity, ambientIntensity, float, DEFAULT_KEYLIGHT_AMBIENT_INTENSITY); DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, Direction, direction, glm::vec3, DEFAULT_KEYLIGHT_DIRECTION); DEFINE_PROPERTY_REF(PROP_KEYLIGHT_AMBIENT_URL, AmbientURL, ambientURL, QString, ""); - -protected: - }; #endif // hifi_KeyLightPropertyGroup_h diff --git a/libraries/entities/src/LightEntityItem.h b/libraries/entities/src/LightEntityItem.h index 4c84d3204c..31ef012f6a 100644 --- a/libraries/entities/src/LightEntityItem.h +++ b/libraries/entities/src/LightEntityItem.h @@ -25,30 +25,30 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); LightEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately - virtual void setDimensions(const glm::vec3& value); - + virtual void setDimensions(const glm::vec3& value) override; + // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; const rgbColor& getColor() const { return _color; } xColor getXColor() const { diff --git a/libraries/entities/src/LineEntityItem.h b/libraries/entities/src/LineEntityItem.h index 6a5ef20bac..575d987605 100644 --- a/libraries/entities/src/LineEntityItem.h +++ b/libraries/entities/src/LineEntityItem.h @@ -12,35 +12,35 @@ #ifndef hifi_LineEntityItem_h #define hifi_LineEntityItem_h -#include "EntityItem.h" +#include "EntityItem.h" class LineEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); LineEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated - + // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; const rgbColor& getColor() const { return _color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } @@ -51,25 +51,26 @@ class LineEntityItem : public EntityItem { _color[GREEN_INDEX] = value.green; _color[BLUE_INDEX] = value.blue; } - + void setLineWidth(float lineWidth){ _lineWidth = lineWidth; } float getLineWidth() const{ return _lineWidth; } - + bool setLinePoints(const QVector& points); bool appendPoint(const glm::vec3& point); - + const QVector& getLinePoints() const{ return _points; } - - virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; } + + virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; } // never have a ray intersection pick a LineEntityItem. - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const { return false; } + bool& keepSearching, OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, + void** intersectedObject, + bool precisionPicking) const override { return false; } - virtual void debugDump() const; + virtual void debugDump() const override; static const float DEFAULT_LINE_WIDTH; static const int MAX_POINTS_PER_LINE; diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 7b7edaf945..6f2a0e1b31 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -26,11 +26,11 @@ public: ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, @@ -38,20 +38,20 @@ public: EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, - OctreeElement::AppendState& appendState) const; + OctreeElement::AppendState& appendState) const override; virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; - virtual void update(const quint64& now); - virtual bool needsToCallUpdate() const; - virtual void debugDump() const; + virtual void update(const quint64& now) override; + virtual bool needsToCallUpdate() const override; + virtual void debugDump() const override; - void setShapeType(ShapeType type); - virtual ShapeType getShapeType() const; + void setShapeType(ShapeType type) override; + virtual ShapeType getShapeType() const override; // TODO: Move these to subclasses, or other appropriate abstraction @@ -115,7 +115,7 @@ public: const QString getTextures() const; void setTextures(const QString& textures); - virtual bool shouldBePhysical() const; + virtual bool shouldBePhysical() const override; virtual glm::vec3 getJointPosition(int jointIndex) const { return glm::vec3(); } virtual glm::quat getJointRotation(int jointIndex) const { return glm::quat(); } diff --git a/libraries/entities/src/MovingEntitiesOperator.h b/libraries/entities/src/MovingEntitiesOperator.h index 5d16c41543..27ecb340a8 100644 --- a/libraries/entities/src/MovingEntitiesOperator.h +++ b/libraries/entities/src/MovingEntitiesOperator.h @@ -38,9 +38,9 @@ public: ~MovingEntitiesOperator(); void addEntityToMoveList(EntityItemPointer entity, const AACube& newCube); - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element); - virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex); + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override; + virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override; bool hasMovingEntities() const { return _entitiesToMove.size() > 0; } private: EntityTreePointer _tree; diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 9ddda62c8b..36bc5e6247 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -26,10 +26,10 @@ public: ParticleEffectEntityItem(const EntityItemID& entityItemID); // methods for getting/setting all properties of this entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, @@ -37,15 +37,15 @@ public: EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, - OctreeElement::AppendState& appendState) const; + OctreeElement::AppendState& appendState) const override; virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; - virtual void update(const quint64& now); - virtual bool needsToCallUpdate() const; + virtual void update(const quint64& now) override; + virtual bool needsToCallUpdate() const override; const rgbColor& getColor() const { return _color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } @@ -95,10 +95,10 @@ public: void setAlphaSpread(float alphaSpread); float getAlphaSpread() const { return _alphaSpread; } - void setShapeType(ShapeType type); - virtual ShapeType getShapeType() const { return _shapeType; } + void setShapeType(ShapeType type) override; + virtual ShapeType getShapeType() const override { return _shapeType; } - virtual void debugDump() const; + virtual void debugDump() const override; bool isEmittingParticles() const; /// emitting enabled, and there are particles alive bool getIsEmitting() const { return _isEmitting; } @@ -219,7 +219,7 @@ public: _emitterShouldTrail = emitterShouldTrail; } - virtual bool supportsDetailedRayIntersection() const { return false; } + virtual bool supportsDetailedRayIntersection() const override { return false; } protected: struct Particle; diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index 3231e7c5e1..95522fe57f 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -12,35 +12,35 @@ #ifndef hifi_PolyLineEntityItem_h #define hifi_PolyLineEntityItem_h -#include "EntityItem.h" +#include "EntityItem.h" class PolyLineEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); PolyLineEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated - + // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; const rgbColor& getColor() const { return _color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } @@ -49,22 +49,21 @@ class PolyLineEntityItem : public EntityItem { memcpy(_color, value, sizeof(_color)); } void setColor(const xColor& value) { - _color[RED_INDEX] = value.red; _color[GREEN_INDEX] = value.green; _color[BLUE_INDEX] = value.blue; } - + void setLineWidth(float lineWidth){ _lineWidth = lineWidth; } float getLineWidth() const{ return _lineWidth; } - + bool setLinePoints(const QVector& points); bool appendPoint(const glm::vec3& point); const QVector& getLinePoints() const{ return _points; } - + bool setNormals(const QVector& normals); const QVector& getNormals() const{ return _normals; } - + bool setStrokeWidths(const QVector& strokeWidths); const QVector& getStrokeWidths() const{ return _strokeWidths; } @@ -76,18 +75,18 @@ class PolyLineEntityItem : public EntityItem { } } - virtual bool needsToCallUpdate() const { return true; } + virtual bool needsToCallUpdate() const override { return true; } - virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; } + virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; } // never have a ray intersection pick a PolyLineEntityItem. - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const { return false;} + bool& keepSearching, OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, + void** intersectedObject, bool precisionPicking) const override { return false; } - virtual void debugDump() const; + virtual void debugDump() const override; static const float DEFAULT_LINE_WIDTH; static const int MAX_POINTS_PER_LINE; diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 7441b34c9c..b2d64b60b1 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -23,11 +23,11 @@ class PolyVoxEntityItem : public EntityItem { ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, @@ -35,21 +35,21 @@ class PolyVoxEntityItem : public EntityItem { EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, - OctreeElement::AppendState& appendState) const; + OctreeElement::AppendState& appendState) const override; virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; // never have a ray intersection pick a PolyVoxEntityItem. - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const { return false; } + bool& keepSearching, OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, + void** intersectedObject, bool precisionPicking) const override { return false; } - virtual void debugDump() const; + virtual void debugDump() const override; virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); virtual glm::vec3 getVoxelVolumeSize() const; diff --git a/libraries/entities/src/PropertyGroup.h b/libraries/entities/src/PropertyGroup.h index 138ee020c1..d2d48c4cd0 100644 --- a/libraries/entities/src/PropertyGroup.h +++ b/libraries/entities/src/PropertyGroup.h @@ -63,7 +63,7 @@ public: EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, + int& propertyCount, OctreeElement::AppendState& appendState) const = 0; virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) = 0; diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.h b/libraries/entities/src/RecurseOctreeToMapOperator.h index c64cf91b61..dbf8dbd15b 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.h +++ b/libraries/entities/src/RecurseOctreeToMapOperator.h @@ -15,8 +15,8 @@ class RecurseOctreeToMapOperator : public RecurseOctreeOperator { public: RecurseOctreeToMapOperator(QVariantMap& map, OctreeElementPointer top, QScriptEngine* engine, bool skipDefaultValues, bool skipThoseWithBadParents); - bool preRecursion(OctreeElementPointer element); - bool postRecursion(OctreeElementPointer element); + bool preRecursion(OctreeElementPointer element) override; + bool postRecursion(OctreeElementPointer element) override; private: QVariantMap& _map; OctreeElementPointer _top; diff --git a/libraries/entities/src/SkyboxPropertyGroup.h b/libraries/entities/src/SkyboxPropertyGroup.h index 745f762821..8b485353b8 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.h +++ b/libraries/entities/src/SkyboxPropertyGroup.h @@ -30,44 +30,47 @@ class ReadBitstreamToTreeParams; class SkyboxPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); - virtual void debugDump() const; - virtual void listChangedProperties(QList& out); + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes); - virtual void markAllChanged(); - virtual EntityPropertyFlags getChangedProperties() const; + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; // EntityItem related helpers // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties); + virtual void getProperties(EntityItemProperties& propertiesOut) const override; - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); - + bool& somethingChanged) override; + glm::vec3 getColorVec3() const { const quint8 MAX_COLOR = 255; glm::vec3 color = { (float)_color.red / (float)MAX_COLOR, diff --git a/libraries/entities/src/StagePropertyGroup.h b/libraries/entities/src/StagePropertyGroup.h index c25a1e629e..80f02f851a 100644 --- a/libraries/entities/src/StagePropertyGroup.h +++ b/libraries/entities/src/StagePropertyGroup.h @@ -30,54 +30,57 @@ class ReadBitstreamToTreeParams; class StagePropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); - virtual void debugDump() const; - virtual void listChangedProperties(QList& out); + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes); - virtual void markAllChanged(); - virtual EntityPropertyFlags getChangedProperties() const; + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; // EntityItem related helpers // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties); + virtual void getProperties(EntityItemProperties& propertiesOut) const override; - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); - + bool& somethingChanged) override; + static const bool DEFAULT_STAGE_SUN_MODEL_ENABLED; static const float DEFAULT_STAGE_LATITUDE; static const float DEFAULT_STAGE_LONGITUDE; static const float DEFAULT_STAGE_ALTITUDE; static const quint16 DEFAULT_STAGE_DAY; static const float DEFAULT_STAGE_HOUR; - + float calculateHour() const; uint16_t calculateDay() const; - + DEFINE_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, SunModelEnabled, sunModelEnabled, bool, DEFAULT_STAGE_SUN_MODEL_ENABLED); DEFINE_PROPERTY(PROP_STAGE_LATITUDE, Latitude, latitude, float, DEFAULT_STAGE_LATITUDE); DEFINE_PROPERTY(PROP_STAGE_LONGITUDE, Longitude, longitude, float, DEFAULT_STAGE_LONGITUDE); diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index 1caceee085..65d3d8bb21 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -12,45 +12,45 @@ #ifndef hifi_TextEntityItem_h #define hifi_TextEntityItem_h -#include "EntityItem.h" +#include "EntityItem.h" class TextEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); TextEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately - virtual void setDimensions(const glm::vec3& value); - virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; } - + virtual void setDimensions(const glm::vec3& value) override; + virtual ShapeType getShapeType() const override { return SHAPE_TYPE_BOX; } + // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, + bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const; + void** intersectedObject, bool precisionPicking) const override; static const QString DEFAULT_TEXT; void setText(const QString& value) { _text = value; } @@ -81,7 +81,7 @@ public: _backgroundColor[GREEN_INDEX] = value.green; _backgroundColor[BLUE_INDEX] = value.blue; } - + static const bool DEFAULT_FACE_CAMERA; bool getFaceCamera() const { return _faceCamera; } void setFaceCamera(bool value) { _faceCamera = value; } diff --git a/libraries/entities/src/UpdateEntityOperator.h b/libraries/entities/src/UpdateEntityOperator.h index 8b5bbdf135..b33d6c6c3b 100644 --- a/libraries/entities/src/UpdateEntityOperator.h +++ b/libraries/entities/src/UpdateEntityOperator.h @@ -25,9 +25,9 @@ public: ~UpdateEntityOperator(); - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element); - virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex); + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override; + virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override; private: EntityTreePointer _tree; EntityItemPointer _existingEntity; diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index 8e9d924cde..7c4e9f801a 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -9,7 +9,7 @@ #ifndef hifi_WebEntityItem_h #define hifi_WebEntityItem_h -#include "EntityItem.h" +#include "EntityItem.h" class WebEntityItem : public EntityItem { public: @@ -18,38 +18,38 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); WebEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately - virtual void setDimensions(const glm::vec3& value); - virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; } - + virtual void setDimensions(const glm::vec3& value) override; + virtual ShapeType getShapeType() const override { return SHAPE_TYPE_BOX; } + // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const; + void** intersectedObject, bool precisionPicking) const override; virtual void setSourceUrl(const QString& value); const QString& getSourceUrl() const; diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index f0f2a91d63..cad8a3baac 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -23,28 +23,28 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); ZoneEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated - + // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; @@ -53,11 +53,11 @@ public: static bool getDrawZoneBoundaries() { return _drawZoneBoundaries; } static void setDrawZoneBoundaries(bool value) { _drawZoneBoundaries = value; } - - virtual bool isReadyToComputeShape() { return false; } - void setShapeType(ShapeType type) { _shapeType = type; } - virtual ShapeType getShapeType() const; - + + virtual bool isReadyToComputeShape() override { return false; } + void setShapeType(ShapeType type) override { _shapeType = type; } + virtual ShapeType getShapeType() const override; + virtual bool hasCompoundShapeURL() const { return !_compoundShapeURL.isEmpty(); } const QString getCompoundShapeURL() const { return _compoundShapeURL; } virtual void setCompoundShapeURL(const QString& url); @@ -75,13 +75,13 @@ public: bool getGhostingAllowed() const { return _ghostingAllowed; } void setGhostingAllowed(bool value) { _ghostingAllowed = value; } - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const; + void** intersectedObject, bool precisionPicking) const override; - virtual void debugDump() const; + virtual void debugDump() const override; static const ShapeType DEFAULT_SHAPE_TYPE; static const QString DEFAULT_COMPOUND_SHAPE_URL; diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index d813e002c7..0470766eb9 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -65,7 +65,7 @@ protected: class QmlNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory { public: - QNetworkAccessManager* create(QObject* parent); + QNetworkAccessManager* create(QObject* parent) override; }; QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) { diff --git a/libraries/gl/src/gl/OglplusHelpers.h b/libraries/gl/src/gl/OglplusHelpers.h index c453fbad28..2ed5f7b14b 100644 --- a/libraries/gl/src/gl/OglplusHelpers.h +++ b/libraries/gl/src/gl/OglplusHelpers.h @@ -33,6 +33,11 @@ #pragma clang diagnostic ignored "-Wpessimizing-move" #endif +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif + #include #include @@ -42,6 +47,10 @@ #include #include +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + #ifdef _WIN32 #pragma warning(pop) #elif defined(Q_OS_MAC) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 080c05829f..bc348e3c3f 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -46,17 +46,18 @@ public: ~GLBackend(); void setCameraCorrection(const Mat4& correction); - void render(const Batch& batch) final; + void render(const Batch& batch) final override; // This call synchronize the Full Backend cache with the current GLState // THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync // the gpu::Backend state with the true gl state which has probably been messed up by these ugly naked gl calls // Let's try to avoid to do that as much as possible! - void syncCache() final; + void syncCache() final override; // This is the ugly "download the pixels to sysmem for taking a snapshot" // Just avoid using it, it's ugly and will break performances - virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) final; + virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, + const Vec4i& region, QImage& destImage) final override; static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; diff --git a/libraries/gpu-gl/src/gpu/gl/GLState.cpp b/libraries/gpu-gl/src/gpu/gl/GLState.cpp index 8cb2efa7b4..6d81eb7371 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLState.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLState.cpp @@ -5,7 +5,19 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif + #include "GLState.h" + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + #include "GLBackend.h" using namespace gpu; diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index da1a987bee..d2f52180c5 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -58,7 +58,7 @@ public: ~Buffer(); // The size in bytes of data stored in the buffer - Size getSize() const; + Size getSize() const override; template Size getTypedSize() const { return getSize() / sizeof(T); }; diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 8f075d906b..a7ac472922 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -294,7 +294,7 @@ public: Stamp getDataStamp() const { return _storage->getStamp(); } // The theoretical size in bytes of data stored in the texture - Size getSize() const { return _size; } + Size getSize() const override { return _size; } // The actual size in bytes of data stored in the texture Size getStoredSize() const; diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 962a919d6c..5c1aafdd45 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -140,7 +140,7 @@ protected: friend class GeometryMappingResource; virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - const void* extra); + const void* extra) override; private: ModelCache(); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index e10be30f54..1cc00bb850 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -282,7 +282,7 @@ public: ImageReader(const QWeakPointer& resource, const QByteArray& data, const QUrl& url = QUrl()); - virtual void run(); + virtual void run() override; private: static void listSupportedImageFormats(); diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 9c78e7e378..e1fa119e0d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -127,19 +127,19 @@ public: /// Loads a texture from the specified URL. NetworkTexturePointer getTexture(const QUrl& url, Type type = Type::DEFAULT_TEXTURE, const QByteArray& content = QByteArray()); - + protected: // Overload ResourceCache::prefetch to allow specifying texture type for loads Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type); virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - const void* extra); - + const void* extra) override; + private: TextureCache(); virtual ~TextureCache(); friend class DilatableNetworkTexture; - + gpu::TexturePointer _permutationNormalTexture; gpu::TexturePointer _whiteTexture; gpu::TexturePointer _grayTexture; diff --git a/libraries/networking/src/NLPacketList.h b/libraries/networking/src/NLPacketList.h index 01a017f371..48ce5ef81a 100644 --- a/libraries/networking/src/NLPacketList.h +++ b/libraries/networking/src/NLPacketList.h @@ -31,7 +31,7 @@ private: NLPacketList(const NLPacketList& other) = delete; NLPacketList& operator=(const NLPacketList& other) = delete; - virtual std::unique_ptr createPacket(); + virtual std::unique_ptr createPacket() override; PacketVersion _packetVersion; diff --git a/libraries/networking/src/OAuthNetworkAccessManager.h b/libraries/networking/src/OAuthNetworkAccessManager.h index 434d9b7c75..f4139e5cfd 100644 --- a/libraries/networking/src/OAuthNetworkAccessManager.h +++ b/libraries/networking/src/OAuthNetworkAccessManager.h @@ -19,7 +19,7 @@ public: static OAuthNetworkAccessManager* getInstance(); protected: OAuthNetworkAccessManager(QObject* parent = Q_NULLPTR) : NetworkAccessManager(parent) { } - virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& req, QIODevice* outgoingData = 0); + virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& req, QIODevice* outgoingData = 0) override; }; -#endif // hifi_OAuthNetworkAccessManager_h \ No newline at end of file +#endif // hifi_OAuthNetworkAccessManager_h diff --git a/libraries/networking/src/PacketSender.h b/libraries/networking/src/PacketSender.h index e177172788..68faeaca47 100644 --- a/libraries/networking/src/PacketSender.h +++ b/libraries/networking/src/PacketSender.h @@ -43,8 +43,8 @@ public: void setPacketsPerSecond(int packetsPerSecond); int getPacketsPerSecond() const { return _packetsPerSecond; } - virtual bool process(); - virtual void terminating(); + virtual bool process() override; + virtual void terminating() override; /// are there packets waiting in the send queue to be sent bool hasPacketsToSend() const { return _packets.size() > 0; } diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index cbb27fa5d6..dd790a9b3d 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -49,7 +49,7 @@ public: float getIncomingPPS() const { return _incomingPPS.getAverage(); } float getProcessedPPS() const { return _processedPPS.getAverage(); } - virtual void terminating(); + virtual void terminating() override; public slots: void nodeKilled(SharedNodePointer node); @@ -61,7 +61,7 @@ protected: virtual void processPacket(QSharedPointer message, SharedNodePointer sendingNode) = 0; /// Implements generic processing behavior for this thread. - virtual bool process(); + virtual bool process() override; /// Determines the timeout of the wait when there are no packets to process. Default value means no timeout virtual unsigned long getMaxWait() const { return ULONG_MAX; } diff --git a/libraries/networking/src/udt/BasePacket.h b/libraries/networking/src/udt/BasePacket.h index 846c05ecbf..33b8020d3c 100644 --- a/libraries/networking/src/udt/BasePacket.h +++ b/libraries/networking/src/udt/BasePacket.h @@ -70,9 +70,9 @@ public: // QIODevice virtual functions // WARNING: Those methods all refer to the payload ONLY and NOT the entire packet - virtual bool isSequential() const { return false; } - virtual bool reset(); - virtual qint64 size() const { return _payloadCapacity; } + virtual bool isSequential() const override { return false; } + virtual bool reset() override; + virtual qint64 size() const override { return _payloadCapacity; } using QIODevice::read; // Bring QIODevice::read methods to scope, otherwise they are hidden by folling method QByteArray read(qint64 maxSize); @@ -94,8 +94,8 @@ protected: BasePacket& operator=(BasePacket&& other); // QIODevice virtual functions - virtual qint64 writeData(const char* data, qint64 maxSize); - virtual qint64 readData(char* data, qint64 maxSize); + virtual qint64 writeData(const char* data, qint64 maxSize) override; + virtual qint64 readData(char* data, qint64 maxSize) override; void adjustPayloadStartAndCapacity(qint64 headerSize, bool shouldDecreasePayloadSize = false); diff --git a/libraries/networking/src/udt/CongestionControl.h b/libraries/networking/src/udt/CongestionControl.h index cb99e6c8d4..3ab69efe52 100644 --- a/libraries/networking/src/udt/CongestionControl.h +++ b/libraries/networking/src/udt/CongestionControl.h @@ -94,7 +94,7 @@ public: template class CongestionControlFactory: public CongestionControlVirtualFactory { public: virtual ~CongestionControlFactory() {} - virtual std::unique_ptr create() { return std::unique_ptr(new T()); } + virtual std::unique_ptr create() override { return std::unique_ptr(new T()); } }; class DefaultCC: public CongestionControl { @@ -102,12 +102,12 @@ public: DefaultCC(); public: - virtual void onACK(SequenceNumber ackNum); - virtual void onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd); - virtual void onTimeout(); + virtual void onACK(SequenceNumber ackNum) override; + virtual void onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) override; + virtual void onTimeout() override; protected: - virtual void setInitialSendSequenceNumber(SequenceNumber seqNum); + virtual void setInitialSendSequenceNumber(SequenceNumber seqNum) override; private: void stopSlowStart(); // stops the slow start on loss or timeout diff --git a/libraries/networking/src/udt/PacketList.h b/libraries/networking/src/udt/PacketList.h index 875eb70e45..5083d77ec9 100644 --- a/libraries/networking/src/udt/PacketList.h +++ b/libraries/networking/src/udt/PacketList.h @@ -54,8 +54,8 @@ public: void closeCurrentPacket(bool shouldSendEmpty = false); // QIODevice virtual functions - virtual bool isSequential() const { return false; } - virtual qint64 size() const { return getDataSize(); } + virtual bool isSequential() const override { return false; } + virtual qint64 size() const override { return getDataSize(); } template qint64 readPrimitive(T* data); template qint64 writePrimitive(const T& data); @@ -68,9 +68,9 @@ protected: void preparePackets(MessageNumber messageNumber); - virtual qint64 writeData(const char* data, qint64 maxSize); + virtual qint64 writeData(const char* data, qint64 maxSize) override; // Not implemented, added an assert so that it doesn't get used by accident - virtual qint64 readData(char* data, qint64 maxSize) { Q_ASSERT(false); return 0; } + virtual qint64 readData(char* data, qint64 maxSize) override { Q_ASSERT(false); return 0; } PacketType _packetType; std::list> _packets; diff --git a/libraries/octree/src/DirtyOctreeElementOperator.h b/libraries/octree/src/DirtyOctreeElementOperator.h index a5eec780a0..a8d00a13f0 100644 --- a/libraries/octree/src/DirtyOctreeElementOperator.h +++ b/libraries/octree/src/DirtyOctreeElementOperator.h @@ -20,8 +20,8 @@ public: ~DirtyOctreeElementOperator() {} - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element); + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override; private: glm::vec3 _point; OctreeElementPointer _element; diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h index 4a390814f4..fd8cc85f91 100644 --- a/libraries/octree/src/OctreeEditPacketSender.h +++ b/libraries/octree/src/OctreeEditPacketSender.h @@ -58,7 +58,7 @@ public: } /// if you're running in non-threaded mode, you must call this method regularly - virtual bool process(); + virtual bool process() override; /// Set the desired number of pending messages that the OctreeEditPacketSender should attempt to queue even if /// servers are not present. This only applies to how the OctreeEditPacketSender will manage messages when no diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index 061a7a0e15..d0f0f03f98 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -51,7 +51,7 @@ signals: protected: /// Implements generic processing behavior for this thread. - virtual bool process(); + virtual bool process() override; void persist(); void backup(); diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index b5ef03be0f..19207f13ed 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -36,7 +36,7 @@ public: m_collisionFilterGroup = BULLET_COLLISION_GROUP_MY_AVATAR; m_collisionFilterMask = BULLET_COLLISION_MASK_MY_AVATAR; } - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) { + virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) override { if (rayResult.m_collisionObject == _me) { return 1.0f; } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 2c7e9485fb..a6e1bb53f4 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -916,7 +916,7 @@ public: Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, const QVector& meshes, const QVector& blendshapeCoefficients); - virtual void run(); + virtual void run() override; private: diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index fb5534e3ab..9a65b48021 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -31,7 +31,7 @@ namespace render { class Context; - + // Key is the KEY to filter Items and create specialized lists class ItemKey { public: @@ -51,8 +51,8 @@ public: NUM_FLAGS, // Not a valid flag }; - typedef std::bitset Flags; - + typedef std::bitset Flags; + // The key is the Flags Flags _flags; @@ -103,7 +103,7 @@ public: bool isRigid() const { return !_flags[DEFORMED]; } bool isDeformed() const { return _flags[DEFORMED]; } - + bool isVisible() const { return !_flags[INVISIBLE]; } bool isInvisible() const { return _flags[INVISIBLE]; } @@ -125,12 +125,12 @@ using ItemKeys = std::vector; inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) { debug << "[ItemKey: isOpaque:" << itemKey.isOpaque() - << ", isStatic:" << itemKey.isStatic() - << ", isWorldSpace:" << itemKey.isWorldSpace() + << ", isStatic:" << itemKey.isStatic() + << ", isWorldSpace:" << itemKey.isWorldSpace() << "]"; return debug; } - + class ItemFilter { public: ItemKey::Flags _value{ 0 }; @@ -150,10 +150,10 @@ public: Builder& withTypeShape() { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); } Builder& withTypeLight() { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); } - + Builder& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } - + Builder& withWorldSpace() { _value.reset(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); } Builder& withViewSpace() { _value.set(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); } @@ -224,9 +224,9 @@ public: // Bound is the AABBox fully containing this item typedef AABox Bound; - + // Status records the life history and performances of this item while performing at rendering and updating. - // This is Used for monitoring and dynamically adjust the quality + // This is Used for monitoring and dynamically adjust the quality class Status { public: @@ -243,7 +243,7 @@ public: Value() {} Value(float scale, float hue, unsigned char icon = 0xFF) { setScale(scale); setColor(hue); setIcon(icon); } - // It can be scaled in the range [0, 1] + // It can be scaled in the range [0, 1] void setScale(float scale); // the color hue in the range [0, 360] representing the color wheel hue void setColor(float hue); @@ -378,7 +378,7 @@ template const ItemKey payloadGetKey(const std::shared_ptr& payload template const Item::Bound payloadGetBound(const std::shared_ptr& payloadData) { return Item::Bound(); } template int payloadGetLayer(const std::shared_ptr& payloadData) { return 0; } template void payloadRender(const std::shared_ptr& payloadData, RenderArgs* args) { } - + // Shape type interface // This allows shapes to characterize their pipeline via a ShapeKey, to be picked with a subclass of Shape. // When creating a new shape payload you need to create a specialized version, or the ShapeKey will be ownPipeline, @@ -393,21 +393,23 @@ public: Payload(const DataPointer& data) : _data(data) {} // Payload general interface - virtual const ItemKey getKey() const { return payloadGetKey(_data); } - virtual const Item::Bound getBound() const { return payloadGetBound(_data); } - virtual int getLayer() const { return payloadGetLayer(_data); } + virtual const ItemKey getKey() const override { return payloadGetKey(_data); } + virtual const Item::Bound getBound() const override { return payloadGetBound(_data); } + virtual int getLayer() const override { return payloadGetLayer(_data); } - virtual void render(RenderArgs* args) { payloadRender(_data, args); } + virtual void render(RenderArgs* args) override { payloadRender(_data, args); } // Shape Type interface - virtual const ShapeKey getShapeKey() const { return shapeGetShapeKey(_data); } + virtual const ShapeKey getShapeKey() const override { return shapeGetShapeKey(_data); } protected: DataPointer _data; // Update mechanics - virtual void update(const UpdateFunctorPointer& functor) { std::static_pointer_cast(functor)->_func((*_data)); } + virtual void update(const UpdateFunctorPointer& functor) override { + std::static_pointer_cast(functor)->_func((*_data)); + } friend class Item; }; @@ -440,7 +442,7 @@ template <> const Item::Bound payloadGetBound(const FooPointer& foo) { return foo->evaluateMyBound(); } -// In this example, do not specialize the payloadRender call which means the compiler will use the default version which does nothing +// In this example, do not specialize the payloadRender call which means the compiler will use the default version which does nothing */ // End of the example @@ -448,7 +450,7 @@ template <> const Item::Bound payloadGetBound(const FooPointer& foo) { typedef Item::PayloadPointer PayloadPointer; typedef std::vector< PayloadPointer > Payloads; -// A few typedefs for standard containers of ItemIDs +// A few typedefs for standard containers of ItemIDs using ItemIDs = std::vector; using ItemIDSet = std::set; @@ -457,7 +459,7 @@ class ItemBound { public: ItemBound(ItemID id) : id(id) { } ItemBound(ItemID id, const AABox& bound) : id(id), bound(bound) { } - + ItemID id; AABox bound; }; diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index 8268090997..53e5319fc1 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -47,19 +47,19 @@ public: template T& edit() { return std::static_pointer_cast>(_concept)->_data; } template const T& get() const { return std::static_pointer_cast>(_concept)->_data; } - + // access potential sub varyings contained in this one. Varying operator[] (uint8_t index) const { return (*_concept)[index]; } uint8_t length() const { return (*_concept).length(); } template Varying getN (uint8_t index) const { return get()[index]; } template Varying editN (uint8_t index) { return edit()[index]; } - + protected: class Concept { public: virtual ~Concept() = default; - + virtual Varying operator[] (uint8_t index) const = 0; virtual uint8_t length() const = 0; }; @@ -69,12 +69,12 @@ protected: Model(const Data& data) : _data(data) {} virtual ~Model() = default; - - virtual Varying operator[] (uint8_t index) const { + + virtual Varying operator[] (uint8_t index) const override { Varying var; return var; } - virtual uint8_t length() const { return 0; } + virtual uint8_t length() const override { return 0; } Data _data; }; @@ -362,7 +362,7 @@ public: Q_INVOKABLE QString toJSON() { return QJsonDocument(toJsonValue(*this).toObject()).toJson(QJsonDocument::Compact); } Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); emit loaded(); } - // Running Time measurement + // Running Time measurement // The new stats signal is emitted once per run time of a job when stats (cpu runtime) are updated void setCPURunTime(quint64 ustime) { _CPURunTime = ustime; emit newStats(); } quint64 getCPUTRunTime() const { return _CPURunTime; } @@ -457,8 +457,8 @@ public: Varying _input; Varying _output; - const Varying getInput() const { return _input; } - const Varying getOutput() const { return _output; } + const Varying getInput() const override { return _input; } + const Varying getOutput() const override { return _output; } template Model(const Varying& input, A&&... args) : @@ -466,11 +466,11 @@ public: applyConfiguration(); } - void applyConfiguration() { + void applyConfiguration() override { jobConfigure(_data, *std::static_pointer_cast(_config)); } - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) override { renderContext->jobConfig = std::static_pointer_cast(_config); if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->isEnabled()) { jobRun(_data, sceneContext, renderContext, _input.get(), _output.edit()); @@ -609,7 +609,7 @@ public: void configure(const QObject& configuration) { for (auto& job : _jobs) { job.applyConfiguration(); - + } } @@ -622,5 +622,4 @@ protected: } - #endif // hifi_render_Task_h diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index f7ad8ad4bd..69c2cc0799 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -28,34 +28,34 @@ public: ArrayBufferClass(ScriptEngine* scriptEngine); QScriptValue newInstance(qint32 size); QScriptValue newInstance(const QByteArray& ba); - + QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, - QueryFlags flags, uint* id); + QueryFlags flags, uint* id) override; QScriptValue property(const QScriptValue& object, - const QScriptString& name, uint id); + const QScriptString& name, uint id) override; QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id); - - QString name() const; - QScriptValue prototype() const; - + const QScriptString& name, uint id) override; + + QString name() const override; + QScriptValue prototype() const override; + ScriptEngine* getEngine() { return _scriptEngine; } - + private: static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); - + static QScriptValue toScriptValue(QScriptEngine* eng, const QByteArray& ba); static void fromScriptValue(const QScriptValue& obj, QByteArray& ba); - + QScriptValue _proto; QScriptValue _ctor; - + // JS Object attributes QScriptString _name; QScriptString _byteLength; - + ScriptEngine* _scriptEngine; }; -#endif // hifi_ArrayBufferClass_h \ No newline at end of file +#endif // hifi_ArrayBufferClass_h diff --git a/libraries/script-engine/src/ArrayBufferViewClass.h b/libraries/script-engine/src/ArrayBufferViewClass.h index b673ebf280..67af4a3fc3 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.h +++ b/libraries/script-engine/src/ArrayBufferViewClass.h @@ -30,23 +30,23 @@ class ArrayBufferViewClass : public QObject, public QScriptClass { Q_OBJECT public: ArrayBufferViewClass(ScriptEngine* scriptEngine); - + ScriptEngine* getScriptEngine() { return _scriptEngine; } - + virtual QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, - QueryFlags flags, uint* id); + QueryFlags flags, uint* id) override; virtual QScriptValue property(const QScriptValue& object, - const QScriptString& name, uint id); + const QScriptString& name, uint id) override; virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id); + const QScriptString& name, uint id) override; protected: // JS Object attributes QScriptString _bufferName; QScriptString _byteOffsetName; QScriptString _byteLengthName; - + ScriptEngine* _scriptEngine; }; -#endif // hifi_ArrayBufferViewClass_h \ No newline at end of file +#endif // hifi_ArrayBufferViewClass_h diff --git a/libraries/script-engine/src/DataViewClass.h b/libraries/script-engine/src/DataViewClass.h index b87803f4b4..72c920a727 100644 --- a/libraries/script-engine/src/DataViewClass.h +++ b/libraries/script-engine/src/DataViewClass.h @@ -19,18 +19,18 @@ class DataViewClass : public ArrayBufferViewClass { public: DataViewClass(ScriptEngine* scriptEngine); QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLength); - - QString name() const; - QScriptValue prototype() const; - + + QString name() const override; + QScriptValue prototype() const override; + private: static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); - + QScriptValue _proto; QScriptValue _ctor; - + QScriptString _name; }; -#endif // hifi_DataViewClass_h \ No newline at end of file +#endif // hifi_DataViewClass_h diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index e093f0393b..38455e8fd3 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -138,7 +138,8 @@ public: static void loadEntityScript(QWeakPointer theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload); Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method Q_INVOKABLE void unloadAllEntityScripts(); - Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList()); + Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, + const QStringList& params = QStringList()) override; Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision); @@ -157,8 +158,8 @@ public: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // NOTE - These are the callback implementations for ScriptUser the get called by ScriptCache when the contents // of a script are available. - virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents); - virtual void errorInLoadingScript(const QUrl& url); + virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents) override; + virtual void errorInLoadingScript(const QUrl& url) override; // These are currently used by Application to track if a script is user loaded or not. Consider finding a solution // inside of Application so that the ScriptEngine class is not polluted by this notion diff --git a/libraries/script-engine/src/ScriptsModel.h b/libraries/script-engine/src/ScriptsModel.h index e1902f4b23..a3ca554e51 100644 --- a/libraries/script-engine/src/ScriptsModel.h +++ b/libraries/script-engine/src/ScriptsModel.h @@ -68,11 +68,11 @@ class ScriptsModel : public QAbstractItemModel { public: ScriptsModel(QObject* parent = NULL); ~ScriptsModel(); - QModelIndex index(int row, int column, const QModelIndex& parent) const; - QModelIndex parent(const QModelIndex& child) const; - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; - int rowCount(const QModelIndex& parent = QModelIndex()) const; - int columnCount(const QModelIndex& parent = QModelIndex()) const; + QModelIndex index(int row, int column, const QModelIndex& parent) const override; + QModelIndex parent(const QModelIndex& child) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; TreeNodeBase* getTreeNodeFromIndex(const QModelIndex& index) const; QList getFolderNodes(TreeNodeFolder* parent) const; diff --git a/libraries/script-engine/src/ScriptsModelFilter.h b/libraries/script-engine/src/ScriptsModelFilter.h index 7b7cdd974e..4854665d12 100644 --- a/libraries/script-engine/src/ScriptsModelFilter.h +++ b/libraries/script-engine/src/ScriptsModelFilter.h @@ -20,8 +20,8 @@ class ScriptsModelFilter : public QSortFilterProxyModel { public: ScriptsModelFilter(QObject *parent = NULL); protected: - bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const; - bool lessThan(const QModelIndex& left, const QModelIndex& right) const; + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; + bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; }; #endif // hifi_ScriptsModelFilter_h diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index a1a288c3c9..141e7870d9 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -34,33 +34,33 @@ public: virtual QScriptValue newInstance(quint32 length); virtual QScriptValue newInstance(QScriptValue array); virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); - + virtual QueryFlags queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id); + const QScriptString& name, + QueryFlags flags, uint* id) override; virtual QScriptValue property(const QScriptValue& object, - const QScriptString& name, uint id); - virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) = 0; + const QScriptString& name, uint id) override; + virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override = 0; virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id); - - QString name() const; - QScriptValue prototype() const; - + const QScriptString& name, uint id) override; + + QString name() const override; + QScriptValue prototype() const override; + protected: static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); - + void setBytesPerElement(quint32 bytesPerElement); - + QScriptValue _proto; QScriptValue _ctor; - + QScriptString _name; QScriptString _bytesPerElementName; QScriptString _lengthName; - + quint32 _bytesPerElement; - + friend class TypedArrayPrototype; }; @@ -68,81 +68,81 @@ class Int8ArrayClass : public TypedArray { Q_OBJECT public: Int8ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Uint8ArrayClass : public TypedArray { Q_OBJECT public: Uint8ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Uint8ClampedArrayClass : public TypedArray { Q_OBJECT public: Uint8ClampedArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Int16ArrayClass : public TypedArray { Q_OBJECT public: Int16ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Uint16ArrayClass : public TypedArray { Q_OBJECT public: Uint16ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Int32ArrayClass : public TypedArray { Q_OBJECT public: Int32ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Uint32ArrayClass : public TypedArray { Q_OBJECT public: Uint32ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Float32ArrayClass : public TypedArray { Q_OBJECT public: Float32ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Float64ArrayClass : public TypedArray { Q_OBJECT public: Float64ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; -#endif // hifi_TypedArrays_h \ No newline at end of file +#endif // hifi_TypedArrays_h diff --git a/libraries/script-engine/src/UndoStackScriptingInterface.h b/libraries/script-engine/src/UndoStackScriptingInterface.h index 479648fc92..420a282c38 100644 --- a/libraries/script-engine/src/UndoStackScriptingInterface.h +++ b/libraries/script-engine/src/UndoStackScriptingInterface.h @@ -33,10 +33,10 @@ class ScriptUndoCommand : public QObject, public QUndoCommand { public: ScriptUndoCommand(QScriptValue undoFunction, QScriptValue undoData, QScriptValue redoFunction, QScriptValue redoData); - virtual void undo(); - virtual void redo(); - virtual bool mergeWith(const QUndoCommand* command) { return false; } - virtual int id() const { return -1; } + virtual void undo() override; + virtual void redo() override; + virtual bool mergeWith(const QUndoCommand* command) override { return false; } + virtual int id() const override { return -1; } public slots: void doUndo(); diff --git a/libraries/shared/src/CrashHelpers.h b/libraries/shared/src/CrashHelpers.h index dae39d4a99..6233ab9177 100644 --- a/libraries/shared/src/CrashHelpers.h +++ b/libraries/shared/src/CrashHelpers.h @@ -30,7 +30,7 @@ private: class B : public A { public: B() : A(this) { } - virtual void virtualFunction() { } + virtual void virtualFunction() override { } }; A::~A() { diff --git a/libraries/shared/src/GenericQueueThread.h b/libraries/shared/src/GenericQueueThread.h index 91b8e5b6d8..5c16693d53 100644 --- a/libraries/shared/src/GenericQueueThread.h +++ b/libraries/shared/src/GenericQueueThread.h @@ -55,7 +55,7 @@ public: } } - virtual bool process() { + virtual bool process() override { lock(); if (!_items.size()) { unlock(); diff --git a/libraries/shared/src/Preferences.h b/libraries/shared/src/Preferences.h index 0b8140af1b..abb3f5afbd 100644 --- a/libraries/shared/src/Preferences.h +++ b/libraries/shared/src/Preferences.h @@ -105,7 +105,7 @@ public: using Lambda = std::function; ButtonPreference(const QString& category, const QString& name, Lambda triggerHandler) : Preference(category, name), _triggerHandler(triggerHandler) { } - Type getType() { return Button; } + Type getType() override { return Button; } Q_INVOKABLE void trigger() { _triggerHandler(); } protected: @@ -210,7 +210,7 @@ public: SliderPreference(const QString& category, const QString& name, Getter getter, Setter setter) : FloatPreference(category, name, getter, setter) { } - Type getType() { return Slider; } + Type getType() override { return Slider; } }; class SpinnerPreference : public FloatPreference { @@ -219,7 +219,7 @@ public: SpinnerPreference(const QString& category, const QString& name, Getter getter, Setter setter) : FloatPreference(category, name, getter, setter) { } - Type getType() { return Spinner; } + Type getType() override { return Spinner; } }; class EditPreference : public StringPreference { @@ -229,7 +229,7 @@ class EditPreference : public StringPreference { public: EditPreference(const QString& category, const QString& name, Getter getter, Setter setter) : StringPreference(category, name, getter, setter) { } - Type getType() { return Editable; } + Type getType() override { return Editable; } const QString& getPlaceholderText() const { return _placeholderText; } void setPlaceholderText(const QString& placeholderText) { _placeholderText = placeholderText; } @@ -244,7 +244,7 @@ class ComboBoxPreference : public EditPreference { public: ComboBoxPreference(const QString& category, const QString& name, Getter getter, Setter setter) : EditPreference(category, name, getter, setter) { } - Type getType() { return ComboBox; } + Type getType() override { return ComboBox; } const QStringList& getItems() { return _items; } void setItems(const QStringList& items) { _items = items; } @@ -260,7 +260,7 @@ class BrowsePreference : public EditPreference { public: BrowsePreference(const QString& category, const QString& name, Getter getter, Setter setter) : EditPreference(category, name, getter, setter) { } - Type getType() { return Browsable; } + Type getType() override { return Browsable; } const QString& getBrowseLabel() { return _browseLabel; } void setBrowseLabel(const QString& browseLabel) { _browseLabel = browseLabel; } @@ -276,7 +276,7 @@ public: : BrowsePreference(category, name, getter, setter) { _browseLabel = "Change"; } - Type getType() { return Avatar; } + Type getType() override { return Avatar; } }; @@ -285,7 +285,7 @@ class CheckPreference : public BoolPreference { public: CheckPreference(const QString& category, const QString& name, Getter getter, Setter setter) : BoolPreference(category, name, getter, setter) { } - Type getType() { return Checkbox; } + Type getType() override { return Checkbox; } }; #endif diff --git a/libraries/shared/src/SettingHandle.h b/libraries/shared/src/SettingHandle.h index 8e07d28dad..36d4f0b249 100644 --- a/libraries/shared/src/SettingHandle.h +++ b/libraries/shared/src/SettingHandle.h @@ -70,57 +70,57 @@ namespace Setting { public: Handle(const QString& key) : Interface(key) {} Handle(const QStringList& path) : Interface(path.join("/")) {} - + Handle(const QString& key, const T& defaultValue) : Interface(key), _defaultValue(defaultValue) {} Handle(const QStringList& path, const T& defaultValue) : Handle(path.join("/"), defaultValue) {} - + virtual ~Handle() { deinit(); } - + // Returns setting value, returns its default value if not found - T get() const { - return get(_defaultValue); + T get() const { + return get(_defaultValue); } // Returns setting value, returns other if not found - T get(const T& other) const { - maybeInit(); - return (_isSet) ? _value : other; + T get(const T& other) const { + maybeInit(); + return (_isSet) ? _value : other; } - const T& getDefault() const { - return _defaultValue; - } - - void reset() { - set(_defaultValue); + const T& getDefault() const { + return _defaultValue; } - void set(const T& value) { - maybeInit(); + void reset() { + set(_defaultValue); + } + + void set(const T& value) { + maybeInit(); if ((!_isSet && (value != _defaultValue)) || _value != value) { - _value = value; - _isSet = true; - save(); - } + _value = value; + _isSet = true; + save(); + } } - void remove() { - maybeInit(); - if (_isSet) { - _isSet = false; - save(); - } + void remove() { + maybeInit(); + if (_isSet) { + _isSet = false; + save(); + } } - + protected: - virtual void setVariant(const QVariant& variant); - virtual QVariant getVariant() { return QVariant::fromValue(get()); } - + virtual void setVariant(const QVariant& variant) override; + virtual QVariant getVariant() override { return QVariant::fromValue(get()); } + private: T _value; const T _defaultValue; }; - + template void Handle::setVariant(const QVariant& variant) { if (variant.canConvert() || std::is_same::value) { diff --git a/libraries/shared/src/ShutdownEventListener.h b/libraries/shared/src/ShutdownEventListener.h index f6d50401b2..8a9363e2c8 100644 --- a/libraries/shared/src/ShutdownEventListener.h +++ b/libraries/shared/src/ShutdownEventListener.h @@ -20,7 +20,7 @@ class ShutdownEventListener : public QObject, public QAbstractNativeEventFilter public: static ShutdownEventListener& getInstance(); - virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result); + virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; private: ShutdownEventListener(QObject* parent = 0); }; diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index 2e8f6bd7b3..549bf69ef4 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -23,6 +23,7 @@ #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Woverloaded-virtual" +#pragma GCC diagnostic ignored "-Wsuggest-override" #endif #include @@ -310,4 +311,4 @@ void SteamClient::joinLobby(QString lobbyIdStr) { qDebug() << "Trying to join Steam lobby:" << lobbyIdStr; CSteamID lobbyId(lobbyIdStr.toULongLong()); SteamMatchmaking()->JoinLobby(lobbyId); -} \ No newline at end of file +} diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp index a4c7cf9fb2..f768b5f227 100644 --- a/libraries/ui/src/CursorManager.cpp +++ b/libraries/ui/src/CursorManager.cpp @@ -26,7 +26,7 @@ namespace Cursor { class MouseInstance : public Instance { - Source getType() const { + Source getType() const override { return Source::MOUSE; } }; @@ -79,4 +79,4 @@ namespace Cursor { _scale = scale; } -} \ No newline at end of file +} diff --git a/libraries/ui/src/ErrorDialog.h b/libraries/ui/src/ErrorDialog.h index 38954714a7..4312e82036 100644 --- a/libraries/ui/src/ErrorDialog.h +++ b/libraries/ui/src/ErrorDialog.h @@ -36,7 +36,7 @@ signals: void textChanged(); protected slots: - virtual void accept(); + virtual void accept() override; private: QString _text; diff --git a/libraries/ui/src/MainWindow.h b/libraries/ui/src/MainWindow.h index 3fee62692d..db02bfa1b6 100644 --- a/libraries/ui/src/MainWindow.h +++ b/libraries/ui/src/MainWindow.h @@ -31,14 +31,14 @@ signals: void windowShown(bool shown); protected: - virtual void closeEvent(QCloseEvent* event); - virtual void moveEvent(QMoveEvent* event); - virtual void resizeEvent(QResizeEvent* event); - virtual void showEvent(QShowEvent* event); - virtual void hideEvent(QHideEvent* event); - virtual void changeEvent(QEvent* event); - virtual void dragEnterEvent(QDragEnterEvent *e); - virtual void dropEvent(QDropEvent *e); + virtual void closeEvent(QCloseEvent* event) override; + virtual void moveEvent(QMoveEvent* event) override; + virtual void resizeEvent(QResizeEvent* event) override; + virtual void showEvent(QShowEvent* event) override; + virtual void hideEvent(QHideEvent* event) override; + virtual void changeEvent(QEvent* event) override; + virtual void dragEnterEvent(QDragEnterEvent *e) override; + virtual void dropEvent(QDropEvent *e) override; private: Setting::Handle _windowGeometry; diff --git a/tools/vhacd-util/src/VHACDUtil.h b/tools/vhacd-util/src/VHACDUtil.h index 8f82c4e4e4..35ec3ef56b 100644 --- a/tools/vhacd-util/src/VHACDUtil.h +++ b/tools/vhacd-util/src/VHACDUtil.h @@ -51,7 +51,7 @@ namespace vhacd { // Couldn't follow coding guideline here due to virtual function declared in IUserCallback void Update(const double overallProgress, const double stageProgress, const double operationProgress, - const char * const stage, const char * const operation); + const char * const stage, const char * const operation) override; }; } From 722e88253df8ef7d3da75f555a4a93126cd5f1e1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Aug 2016 09:59:40 -0700 Subject: [PATCH 149/332] missed one --- libraries/audio/src/AudioLimiter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioLimiter.cpp b/libraries/audio/src/AudioLimiter.cpp index b5550fba0c..7bbaca62ca 100644 --- a/libraries/audio/src/AudioLimiter.cpp +++ b/libraries/audio/src/AudioLimiter.cpp @@ -619,7 +619,7 @@ public: LimiterStereo(int sampleRate) : LimiterImpl(sampleRate) {} // interleaved stereo input/output - void process(float* input, int16_t* output, int numFrames); + void process(float* input, int16_t* output, int numFrames) override; }; template From 09b7a5188c8d312ea97e5b3049329f162154c141 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Aug 2016 10:15:35 -0700 Subject: [PATCH 150/332] a few more --- libraries/render/src/render/Task.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index 53e5319fc1..6ba1e3c625 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -528,8 +528,8 @@ public: Varying _input; Varying _output; - const Varying getInput() const { return _input; } - const Varying getOutput() const { return _output; } + const Varying getInput() const override { return _input; } + const Varying getOutput() const override { return _output; } template Model(const Varying& input, A&&... args) : @@ -540,11 +540,11 @@ public: applyConfiguration(); } - void applyConfiguration() { + void applyConfiguration() override { jobConfigure(_data, *std::static_pointer_cast(_config)); } - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) override { renderContext->jobConfig = std::static_pointer_cast(_config); if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { jobRun(_data, sceneContext, renderContext, _input.get(), _output.edit()); From ccaf7379fc001e7f629a52da898bdc31e4ac457e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Aug 2016 10:31:53 -0700 Subject: [PATCH 151/332] missed some more due to conditional compilation --- interface/src/devices/Faceshift.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h index 48a4cd682a..2c5889857c 100644 --- a/interface/src/devices/Faceshift.h +++ b/interface/src/devices/Faceshift.h @@ -34,12 +34,12 @@ class Faceshift : public FaceTracker, public Dependency { public: #ifdef HAVE_FACESHIFT // If we don't have faceshift, use the base class' methods - virtual void init(); - virtual void update(float deltaTime); - virtual void reset(); + virtual void init() override; + virtual void update(float deltaTime) override; + virtual void reset() override; - virtual bool isActive() const; - virtual bool isTracking() const; + virtual bool isActive() const override; + virtual bool isTracking() const override; #endif bool isConnectedOrConnecting() const; From 1eaac8605fdfc032c34e8663a321d1b0d4035f52 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 10:43:37 -0700 Subject: [PATCH 152/332] invert the structure and make it work with oculus --- interface/resources/controllers/standard.json | 2 +- interface/resources/controllers/vive.json | 13 +-- .../system/controllers/advancedMovement.js | 85 +++++++++++++------ 3 files changed, 70 insertions(+), 30 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 222357ac9d..c9e91c8666 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -11,7 +11,7 @@ [ { "type": "deadZone", "min": 0.15 }, "constrainToInteger", - { "type": "pulse", "interval": 0.5 }, + { "type": "pulse", "interval": 0.25 }, { "type": "scale", "scale": 22.5 } ] }, diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 9cf641d2b0..dce3e9660c 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -1,9 +1,11 @@ { "name": "Vive to Standard", "channels": [ + { "from": "Vive.LY", "when": "Vive.LSY", "filters": ["invert"], "to": "Standard.LY" }, + { "from": "Vive.LX", "when": "Vive.LSX", "to": "Standard.LX" }, { - "from": "Vive.LT", "to": "Standard.LT", - "filters": [ + "from": "Vive.LT", "to": "Standard.LT", + "filters": [ { "type": "deadZone", "min": 0.05 } ] }, @@ -13,10 +15,11 @@ { "from": "Vive.LS", "to": "Standard.LS" }, { "from": "Vive.LSTouch", "to": "Standard.LSTouch" }, + { "from": "Vive.RY", "when": "Vive.RSY", "filters": ["invert"], "to": "Standard.RY" }, { "from": "Vive.RX", "when": "Vive.RSX", "to": "Standard.RX" }, { - "from": "Vive.RT", "to": "Standard.RT", - "filters": [ + "from": "Vive.RT", "to": "Standard.RT", + "filters": [ { "type": "deadZone", "min": 0.05 } ] }, @@ -34,4 +37,4 @@ { "from": "Vive.LeftHand", "to": "Standard.LeftHand" }, { "from": "Vive.RightHand", "to": "Standard.RightHand" } ] -} +} \ No newline at end of file diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index 3c804e1e6d..0eb50434b2 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -1,54 +1,78 @@ -var mappingName, advancedMapping; +//advanced movements settings are in individual controller json files +//what we do is check the status of the 'advance movement' checkbox when you enter HMD mode +//if 'advanced movement' is checked...we give you the defaults that are in the json. +//if 'advanced movement' is not checked... we override the advanced controls with basic ones. +//when the script stops, + +//todo: store in prefs +// + + +var mappingName, basicMapping; function addAdvancedMovementItemToSettingsMenu() { Menu.addMenuItem({ menuName: "Settings", - menuItemName: "Advanced Movement (Vive)", + menuItemName: "Advanced Movement For Hand Controllers", isCheckable: true, isChecked: false }); } -function addTranslationToLeftStick() { - Controller.enableMapping(mappingName); +function rotate180() { + MyAvatar.orientation = Quat.inverse(MyAvatar.orientation); } -function registerMappings() { +function registerBasicMapping() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); - advancedMapping = Controller.newMapping(mappingName); - advancedMapping.from(Controller.Hardware.Vive.LY).when(Controller.getValue(Controller.Hardware.Vive.LSY) === 1).invert().to(function(val) { - testPrint('ly:' + val) - }); - advancedMapping.from(Controller.Hardware.Vive.LX).when(Controller.getValue(Controller.Hardware.Vive.LSX) === 1).to(function(val) { - testPrint('lx:' + val) - }); - advancedMapping.from(Controller.Hardware.Vive.RY).when(Controller.getValue(Controller.Hardware.Vive.RSY) === 1).invert().to(function(val) { - testPrint('ry:' + val) + basicMapping = Controller.newMapping(mappingName); + basicMapping.from(Controller.Standard.LY).to(function(value) { + var stick = Controller.getValue(Controller.Standard.LS); + if (value === 1) { + rotate180(); + } + print('should do LY stuff' + value + ":stick:" + stick); + return; }); + basicMapping.from(Controller.Standard.LX).to(Controller.Standard.RX); + basicMapping.from(Controller.Standard.RY).to(function(value) { + var stick = Controller.getValue(Controller.Standard.RS); + if (value === 1) { + rotate180(); + } + print('should do RY stuff' + value + ":stick:" + stick); + return; + }) } function testPrint(what) { print('it was controller: ' + what) } -function removeTranslationFromLeftStick() { +function enableMappings() { + Controller.enableMapping(mappingName); +} + +function disableMappings() { Controller.disableMapping(mappingName); } function scriptEnding() { - Menu.removeMenuItem("Settings", "Advanced Movement (Vive)"); - removeTranslationFromLeftStick(); + Menu.removeMenuItem("Settings", "Advanced Movement For Hand Controllers"); + disableMappings(); } +var isChecked = false; + function menuItemEvent(menuItem) { - if (menuItem == "Advanced Movement (Vive)") { - print(" checked=" + Menu.isOptionChecked("Advanced Movement (Vive)")); - var isChecked = Menu.isOptionChecked("Advanced Movement (Vive)"); + if (menuItem == "Advanced Movement For Hand Controllers") { + print(" checked=" + Menu.isOptionChecked("Advanced Movement For Hand Controllers")); + isChecked = Menu.isOptionChecked("Advanced Movement For Hand Controllers"); if (isChecked === true) { - addTranslationToLeftStick(); + disableMappings(); } else if (isChecked === false) { - removeTranslationFromLeftStick(); + enableMappings(); } } @@ -56,9 +80,22 @@ function menuItemEvent(menuItem) { addAdvancedMovementItemToSettingsMenu(); -// register our scriptEnding callback Script.scriptEnding.connect(scriptEnding); Menu.menuItemEvent.connect(menuItemEvent); -registerMappings(); \ No newline at end of file +registerBasicMapping(); +enableMappings(); + +HMD.displayModeChanged.connect(function(isHMDMode) { + if (isHMDMode) { + if (Controller.Hardware.Vive !== undefined || Controller.Hardware.OculusTouch !== undefined) { + if (isChecked === true) { + disableMappings(); + } else if (isChecked === false) { + enableMappings(); + } + + } + } +}); \ No newline at end of file From 43c5735baa31c0a1978a1dc0fa7b895d4298f381 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Aug 2016 11:05:31 -0700 Subject: [PATCH 153/332] don't use -Wsuggest-override on older gcc --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ecd65ae22..a167207683 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,10 @@ if (WIN32) else () set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter") if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -Woverloaded-virtual -Wdouble-promotion -Wsuggest-override") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -Woverloaded-virtual -Wdouble-promotion") + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "5.1") # gcc 5.1 and on have suggest-override + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override") + endif () endif () endif(WIN32) From 37846d9f0ca6ad88d177e5046dd30eaebd1242f4 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 11:07:50 -0700 Subject: [PATCH 154/332] store settings --- .../system/controllers/advancedMovement.js | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/advancedMovement.js index 0eb50434b2..d53fa3a2f9 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/advancedMovement.js @@ -8,14 +8,27 @@ // -var mappingName, basicMapping; +var mappingName, basicMapping,isChecked; + +var previousSetting = Settings.getValue('advancedMovementForHandControllersIsChecked'); +if (previousSetting === '') { + previousSetting = false; + isChecked=false; +} + +if(previousSetting===true){ + isChecked=true; +} +if(previousSetting===false){ + isChecked=false; +} function addAdvancedMovementItemToSettingsMenu() { Menu.addMenuItem({ menuName: "Settings", menuItemName: "Advanced Movement For Hand Controllers", isCheckable: true, - isChecked: false + isChecked: previousSetting }); } @@ -63,15 +76,16 @@ function scriptEnding() { disableMappings(); } -var isChecked = false; function menuItemEvent(menuItem) { if (menuItem == "Advanced Movement For Hand Controllers") { print(" checked=" + Menu.isOptionChecked("Advanced Movement For Hand Controllers")); isChecked = Menu.isOptionChecked("Advanced Movement For Hand Controllers"); if (isChecked === true) { + Settings.setValue('advancedMovementForHandControllersIsChecked', true); disableMappings(); } else if (isChecked === false) { + Settings.setValue('advancedMovementForHandControllersIsChecked', false); enableMappings(); } } @@ -85,7 +99,14 @@ Script.scriptEnding.connect(scriptEnding); Menu.menuItemEvent.connect(menuItemEvent); registerBasicMapping(); -enableMappings(); +if (previousSetting === true) { + print('JBP WAS SET TO TRUE') + disableMappings(); +} else if (previousSetting === false) { + print('JBP WAS SET TO FALSE') + enableMappings(); +} + HMD.displayModeChanged.connect(function(isHMDMode) { if (isHMDMode) { From cb9a5466d551e6cac5a2cec9f496f0a4ed76a3a9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 18 Aug 2016 11:09:48 -0700 Subject: [PATCH 155/332] workaround bad FBXMesh data rather than assert --- .../src/RenderableModelEntityItem.cpp | 57 ++++++++++++------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 2e96cdb4ac..b713a882cb 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -595,6 +595,9 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { } void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { + const uint32_t TRIANGLE_STRIDE = 3; + const uint32_t QUAD_STRIDE = 4; + ShapeType type = getShapeType(); glm::vec3 dimensions = getDimensions(); if (type == SHAPE_TYPE_COMPOUND) { @@ -611,8 +614,6 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case. - const uint32_t TRIANGLE_STRIDE = 3; - const uint32_t QUAD_STRIDE = 4; foreach (const FBXMesh& mesh, collisionGeometry.meshes) { // each meshPart is a convex hull foreach (const FBXMeshPart &meshPart, mesh.parts) { @@ -621,7 +622,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // run through all the triangles and (uniquely) add each point to the hull uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size(); - assert(numIndices % TRIANGLE_STRIDE == 0); + // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + //assert(numIndices % TRIANGLE_STRIDE == 0); + numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader + for (uint32_t j = 0; j < numIndices; j += TRIANGLE_STRIDE) { glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[j]]; glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[j + 1]]; @@ -639,7 +643,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // run through all the quads and (uniquely) add each point to the hull numIndices = (uint32_t)meshPart.quadIndices.size(); - assert(numIndices % QUAD_STRIDE == 0); + // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + //assert(numIndices % QUAD_STRIDE == 0); + numIndices -= numIndices % QUAD_STRIDE; // WORKAROUND lack of sanity checking in FBXReader + for (uint32_t j = 0; j < numIndices; j += QUAD_STRIDE) { glm::vec3 p0 = mesh.vertices[meshPart.quadIndices[j]]; glm::vec3 p1 = mesh.vertices[meshPart.quadIndices[j + 1]]; @@ -768,24 +775,30 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements())); gpu::BufferView::Iterator partItr = parts.cbegin(); while (partItr != parts.cend()) { + auto numIndices = partItr->_numIndices; if (partItr->_topology == model::Mesh::TRIANGLES) { - assert(partItr->_numIndices % 3 == 0); + // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + //assert(numIndices % TRIANGLE_STRIDE == 0); + numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader + auto indexItr = indices.cbegin() + partItr->_startIndex; - auto indexEnd = indexItr + partItr->_numIndices; - while (indexItr != indexEnd) { + auto indexEnd = indexItr + numIndices; + while (indexItr < indexEnd) { triangleIndices.push_back(*indexItr + meshIndexOffset); ++indexItr; } } else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) { - assert(partItr->_numIndices > 2); - uint32_t approxNumIndices = 3 * partItr->_numIndices; + // TODO: resurrect assert after we start sanitizing FBXMesh higher up + //assert(numIndices > 2); + + uint32_t approxNumIndices = TRIANGLE_STRIDE * numIndices; if (approxNumIndices > (uint32_t)(triangleIndices.capacity() - triangleIndices.size())) { // we underestimated the final size of triangleIndices so we pre-emptively expand it triangleIndices.reserve(triangleIndices.size() + approxNumIndices); } auto indexItr = indices.cbegin() + partItr->_startIndex; - auto indexEnd = indexItr + (partItr->_numIndices - 2); + auto indexEnd = indexItr + (numIndices - 2); // first triangle uses the first three indices triangleIndices.push_back(*(indexItr++) + meshIndexOffset); @@ -794,7 +807,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // the rest use previous and next index uint32_t triangleCount = 1; - while (indexItr != indexEnd) { + while (indexItr < indexEnd) { if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) { if (triangleCount % 2 == 0) { // even triangles use first two indices in order @@ -819,18 +832,24 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { while (partItr != parts.cend()) { // collect unique list of indices for this part std::set uniqueIndices; + auto numIndices = partItr->_numIndices; if (partItr->_topology == model::Mesh::TRIANGLES) { - assert(partItr->_numIndices % 3 == 0); + // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + //assert(numIndices% TRIANGLE_STRIDE == 0); + numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader + auto indexItr = indices.cbegin() + partItr->_startIndex; - auto indexEnd = indexItr + partItr->_numIndices; - while (indexItr != indexEnd) { + auto indexEnd = indexItr + numIndices; + while (indexItr < indexEnd) { uniqueIndices.insert(*indexItr); ++indexItr; } } else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) { - assert(partItr->_numIndices > 2); + // TODO: resurrect assert after we start sanitizing FBXMesh higher up + //assert(numIndices > TRIANGLE_STRIDE - 1); + auto indexItr = indices.cbegin() + partItr->_startIndex; - auto indexEnd = indexItr + (partItr->_numIndices - 2); + auto indexEnd = indexItr + (numIndices - 2); // first triangle uses the first three indices uniqueIndices.insert(*(indexItr++)); @@ -839,14 +858,14 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // the rest use previous and next index uint32_t triangleCount = 1; - while (indexItr != indexEnd) { + while (indexItr < indexEnd) { if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) { if (triangleCount % 2 == 0) { - // even triangles use first two indices in order + // EVEN triangles use first two indices in order uniqueIndices.insert(*(indexItr - 2)); uniqueIndices.insert(*(indexItr - 1)); } else { - // odd triangles swap order of first two indices + // ODD triangles swap order of first two indices uniqueIndices.insert(*(indexItr - 1)); uniqueIndices.insert(*(indexItr - 2)); } From 813fefd59901ff07b0e010c98111357a71efaba8 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 18 Aug 2016 11:26:25 -0700 Subject: [PATCH 156/332] reimplement buffer get for (c)end --- libraries/gpu/src/gpu/Buffer.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index da1a987bee..8160f648fc 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -293,10 +293,18 @@ public: template Iterator end() { return Iterator(&edit(getNum()), _stride); } #else template Iterator begin() const { return Iterator(&get(), _stride); } - template Iterator end() const { return Iterator(&get(getNum()), _stride); } + template Iterator end() const { + // reimplement get without bounds checking + Resource::Size elementOffset = getNum() * _stride + _offset; + return Iterator((reinterpret_cast (_buffer->getData() + elementOffset)), _stride); + } #endif template Iterator cbegin() const { return Iterator(&get(), _stride); } - template Iterator cend() const { return Iterator(&get(getNum()), _stride); } + template Iterator cend() const { + // reimplement get without bounds checking + Resource::Size elementOffset = getNum() * _stride + _offset; + return Iterator((reinterpret_cast (_buffer->getData() + elementOffset)), _stride); + } // the number of elements of the specified type fitting in the view size template Index getNum() const { From 6b46b3082cde3a0b707d2925ca9233bba5cd5e91 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 18 Aug 2016 11:45:03 -0700 Subject: [PATCH 157/332] try, try again --- libraries/gl/src/gl/OglplusHelpers.h | 2 ++ libraries/gpu-gl/src/gpu/gl/GLState.cpp | 3 +++ .../steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp | 2 ++ 3 files changed, 7 insertions(+) diff --git a/libraries/gl/src/gl/OglplusHelpers.h b/libraries/gl/src/gl/OglplusHelpers.h index 2ed5f7b14b..fe5822c4be 100644 --- a/libraries/gl/src/gl/OglplusHelpers.h +++ b/libraries/gl/src/gl/OglplusHelpers.h @@ -35,8 +35,10 @@ #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push +#if __GNUC__ >= 5 && __GNUC_MINOR__ >= 1 #pragma GCC diagnostic ignored "-Wsuggest-override" #endif +#endif #include diff --git a/libraries/gpu-gl/src/gpu/gl/GLState.cpp b/libraries/gpu-gl/src/gpu/gl/GLState.cpp index 6d81eb7371..b6d917b928 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLState.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLState.cpp @@ -8,8 +8,11 @@ #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push +#if __GNUC__ >= 5 && __GNUC_MINOR__ >= 1 #pragma GCC diagnostic ignored "-Wsuggest-override" #endif +#endif + #include "GLState.h" diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index 549bf69ef4..235d258d21 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -23,8 +23,10 @@ #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Woverloaded-virtual" +#if __GNUC__ >= 5 && __GNUC_MINOR__ >= 1 #pragma GCC diagnostic ignored "-Wsuggest-override" #endif +#endif #include From 2ba35e6398c7aafbee12bfdd1f728f6b606d9dee Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 18 Aug 2016 11:48:56 -0700 Subject: [PATCH 158/332] skip non-existant meshes --- libraries/entities-renderer/src/RenderableModelEntityItem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index b713a882cb..e135107323 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -742,6 +742,9 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { int32_t meshCount = 0; int32_t pointListIndex = 0; for (auto& mesh : meshes) { + if (!mesh) { + continue; + } const gpu::BufferView& vertices = mesh->getVertexBuffer(); const gpu::BufferView& indices = mesh->getIndexBuffer(); const gpu::BufferView& parts = mesh->getPartBuffer(); From 6446c7c302007ae9d95740097022526157b4380f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 18 Aug 2016 11:49:15 -0700 Subject: [PATCH 159/332] make createShapeFromInfo() fail gently on bad data --- libraries/physics/src/ShapeFactory.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 137038e133..5182038c27 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -164,13 +164,24 @@ btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) { assert(info.getType() == SHAPE_TYPE_STATIC_MESH); // should only get here for mesh shapes const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); - assert(pointCollection.size() == 1); // should only have one mesh + if (pointCollection.size() < 1) { + // no lists of points to work with + return nullptr; + } + + // we only use the first point collection const ShapeInfo::PointList& pointList = pointCollection[0]; - assert(pointList.size() > 2); // should have at least one triangle's worth of points + if (pointList.size() < 3) { + // not enough distinct points to make a non-degenerate triangle + return nullptr; + } const ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); - assert(triangleIndices.size() > 2); // should have at least one triangle's worth of indices + if (triangleIndices.size() < 3) { + // not enough indices to make a single triangle + return nullptr; + } // allocate mesh buffers btIndexedMesh mesh; @@ -328,7 +339,9 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { break; case SHAPE_TYPE_STATIC_MESH: { btTriangleIndexVertexArray* dataArray = createStaticMeshArray(info); - shape = new StaticMeshShape(dataArray); + if (dataArray) { + shape = new StaticMeshShape(dataArray); + } } break; } From b8213aed7bcc39349ef11d9524675104d1bc2ee0 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 11:59:58 -0700 Subject: [PATCH 160/332] naming and some logging for teleport stuff --- scripts/defaultScripts.js | 2 +- scripts/system/controllers/teleport.js | 9 ++- ...ggleAdvancedMovementForHandControllers.js} | 61 +++++++++++++------ 3 files changed, 50 insertions(+), 22 deletions(-) rename scripts/system/controllers/{advancedMovement.js => toggleAdvancedMovementForHandControllers.js} (65%) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index feb9d8c2d6..aba1e1ba51 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -21,11 +21,11 @@ Script.load("system/edit.js"); Script.load("system/mod.js"); Script.load("system/selectAudioDevice.js"); Script.load("system/notifications.js"); +Script.load("system/controllers/toggleAdvancedMovementForHandControllers.js") Script.load("system/controllers/handControllerGrab.js"); Script.load("system/controllers/handControllerPointer.js"); Script.load("system/controllers/squeezeHands.js"); Script.load("system/controllers/grab.js"); Script.load("system/controllers/teleport.js"); -Script.load("system/controllers/advancedMovement.js") Script.load("system/dialTone.js"); Script.load("system/firstPersonHMD.js"); \ No newline at end of file diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index d1dd5b6348..d223b59bca 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -202,6 +202,7 @@ function Teleporter() { this.turnOffOverlayBeams(); this.updateConnected = null; + _this.inCoolIn = false; Script.setTimeout(function() { inTeleportMode = false; @@ -209,8 +210,6 @@ function Teleporter() { }, 200); }; - - this.update = function() { if (isDisabled === 'both') { return; @@ -223,12 +222,15 @@ function Teleporter() { teleporter.leftRay(); if ((leftPad.buttonValue === 0) && inTeleportMode === true) { if (_this.inCoolIn === true) { + print('released during cool in period. exit.') _this.exitTeleportMode(); _this.deleteTargetOverlay(); _this.deleteCancelOverlay(); } else { + print('release while not in cool in and in teleport mode. should teleport') _this.teleport(); } + print('some other state::' + leftPad.buttonValue + "///" + inTeleportMode) return; } @@ -239,12 +241,15 @@ function Teleporter() { teleporter.rightRay(); if ((rightPad.buttonValue === 0) && inTeleportMode === true) { if (_this.inCoolIn === true) { + print('released during cool in period. exit.') _this.exitTeleportMode(); _this.deleteTargetOverlay(); _this.deleteCancelOverlay(); } else { + print('release while not in cool in and in teleport mode. should teleport') _this.teleport(); } + print('some other state::' + rightPad.buttonValue + "///" + inTeleportMode) return; } } diff --git a/scripts/system/controllers/advancedMovement.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js similarity index 65% rename from scripts/system/controllers/advancedMovement.js rename to scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index d53fa3a2f9..57aa4cd2c1 100644 --- a/scripts/system/controllers/advancedMovement.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -1,26 +1,26 @@ +// Created by james b. pollack @imgntn on 7/2/2016 +// Copyright 2016 High Fidelity, Inc. //advanced movements settings are in individual controller json files //what we do is check the status of the 'advance movement' checkbox when you enter HMD mode //if 'advanced movement' is checked...we give you the defaults that are in the json. //if 'advanced movement' is not checked... we override the advanced controls with basic ones. -//when the script stops, - -//todo: store in prefs // +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - -var mappingName, basicMapping,isChecked; +var mappingName, basicMapping, isChecked; var previousSetting = Settings.getValue('advancedMovementForHandControllersIsChecked'); if (previousSetting === '') { previousSetting = false; - isChecked=false; + isChecked = false; } -if(previousSetting===true){ - isChecked=true; +if (previousSetting === true) { + isChecked = true; } -if(previousSetting===false){ - isChecked=false; +if (previousSetting === false) { + isChecked = false; } function addAdvancedMovementItemToSettingsMenu() { @@ -37,31 +37,57 @@ function rotate180() { MyAvatar.orientation = Quat.inverse(MyAvatar.orientation); } +var inFlipTurn = false; + function registerBasicMapping() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); basicMapping = Controller.newMapping(mappingName); basicMapping.from(Controller.Standard.LY).to(function(value) { var stick = Controller.getValue(Controller.Standard.LS); - if (value === 1) { + if (value === 1 && Controller.Hardware.OculusTouch !== undefined) { rotate180(); } - print('should do LY stuff' + value + ":stick:" + stick); + if (Controller.Hardware.Vive !== undefined) { + if (value > 0.75 && inFlipTurn === false) { + print('vive should flip turn') + inFlipTurn = true; + rotate180(); + Script.setTimeout(function() { + print('vive should be able to flip turn again') + inFlipTurn = false; + }, 250) + } else { + print('vive should not flip turn') + + } + } return; }); basicMapping.from(Controller.Standard.LX).to(Controller.Standard.RX); basicMapping.from(Controller.Standard.RY).to(function(value) { var stick = Controller.getValue(Controller.Standard.RS); - if (value === 1) { + if (value === 1 && Controller.Hardware.OculusTouch !== undefined) { rotate180(); } + if (Controller.Hardware.Vive !== undefined) { + if (value > 0.75 && inFlipTurn === false) { + print('vive should flip turn') + inFlipTurn = true; + rotate180(); + Script.setTimeout(function() { + print('vive should be able to flip turn again') + inFlipTurn = false; + }, 250) + } else { + print('vive should not flip turn') + + } + } print('should do RY stuff' + value + ":stick:" + stick); return; }) } -function testPrint(what) { - print('it was controller: ' + what) -} function enableMappings() { Controller.enableMapping(mappingName); @@ -79,7 +105,6 @@ function scriptEnding() { function menuItemEvent(menuItem) { if (menuItem == "Advanced Movement For Hand Controllers") { - print(" checked=" + Menu.isOptionChecked("Advanced Movement For Hand Controllers")); isChecked = Menu.isOptionChecked("Advanced Movement For Hand Controllers"); if (isChecked === true) { Settings.setValue('advancedMovementForHandControllersIsChecked', true); @@ -100,10 +125,8 @@ Menu.menuItemEvent.connect(menuItemEvent); registerBasicMapping(); if (previousSetting === true) { - print('JBP WAS SET TO TRUE') disableMappings(); } else if (previousSetting === false) { - print('JBP WAS SET TO FALSE') enableMappings(); } From 5e8722ccf549a1f2b48c93d3dfc8d2c06787e773 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 18 Aug 2016 12:58:00 -0700 Subject: [PATCH 161/332] persist "open feed after" checkbox setting --- scripts/system/html/ShareSnapshot.html | 7 +++++-- scripts/system/snapshot.js | 27 ++++++++++++++++++-------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/scripts/system/html/ShareSnapshot.html b/scripts/system/html/ShareSnapshot.html index 9098b30121..9b9403cf78 100644 --- a/scripts/system/html/ShareSnapshot.html +++ b/scripts/system/html/ShareSnapshot.html @@ -84,6 +84,9 @@ } function handleShareButtons(shareMsg) { + var openFeed = document.getElementById('openFeed'); + openFeed.checked = shareMsg.openFeedAfterShare; + openFeed.onchange = function () { EventBridge.emitWebEvent(openFeed.checked ? 'setOpenFeedTrue' : 'setOpenFeedFalse'); }; if (!shareMsg.canShare) { // this means you may or may not be logged in, but can't share // because you are not in a public place. @@ -116,10 +119,10 @@ }; // beware of bug: Cannot send objects at top level. (Nested in arrays is fine.) shareSelected = function () { - EventBridge.emitWebEvent(paths.concat({openFeed: document.getElementById("openFeed").checked})); + EventBridge.emitWebEvent(paths); }; doNotShare = function () { - EventBridge.emitWebEvent([{openFeed: document.getElementById("openFeed").checked}]); + EventBridge.emitWebEvent([]); }; snapshotSettings = function () { EventBridge.emitWebEvent("openSettings"); diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 6c9132194e..8e99783ff6 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -20,23 +20,32 @@ var button = toolBar.addButton({ alpha: 0.9, }); +function shouldOpenFeedAfterShare() { + var persisted = Settings.getValue('openFeedAfterShare', true); // might answer true, false, "true", or "false" + return persisted && (persisted !== 'false'); +} function showFeedWindow() { DialogsManager.showFeed(); } -var openFeed, outstanding; +var outstanding; function confirmShare(data) { var dialog = new OverlayWebWindow('Snapshot Review', Script.resolvePath("html/ShareSnapshot.html"), 800, 470); function onMessage(message) { switch (message) { case 'ready': dialog.emitScriptEvent(data); // Send it. - openFeed = false; outstanding = 0; break; case 'openSettings': Desktop.show("hifi/dialogs/GeneralPreferencesDialog.qml", "GeneralPreferencesDialog"); break; + case 'setOpenFeedFalse': + Settings.setValue('openFeedAfterShare', false) + break; + case 'setOpenFeedTrue': + Settings.setValue('openFeedAfterShare', true) + break; default: dialog.webEventReceived.disconnect(onMessage); // I'm not certain that this is necessary. If it is, what do we do on normal close? dialog.close(); @@ -46,11 +55,9 @@ function confirmShare(data) { print('sharing', submessage.localPath); outstanding++; Window.shareSnapshot(submessage.localPath); - } else if (submessage.openFeed) { - openFeed = true; } }); - if (openFeed && !outstanding) { + if (!outstanding && shouldOpenFeedAfterShare()) { showFeedWindow(); } } @@ -67,7 +74,7 @@ function snapshotShared(success) { // for now just print an error. print('snapshot upload/share failed'); } - if ((--outstanding <= 0) && openFeed) { + if ((--outstanding <= 0) && shouldOpenFeedAfterShare()) { showFeedWindow(); } } @@ -114,8 +121,12 @@ function resetButtons(path, notify) { // last element in data array tells dialog whether we can share or not confirmShare([ - { localPath: path }, - { canShare: Boolean(Window.location.placename), isLoggedIn: Account.isLoggedIn() } + { localPath: path }, + { + canShare: Boolean(Window.location.placename), + isLoggedIn: Account.isLoggedIn(), + openFeedAfterShare: shouldOpenFeedAfterShare() + } ]); } From 992504e1794f1ebbf6489b7fa162ecb7d0844ca3 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 13:35:51 -0700 Subject: [PATCH 162/332] adjust coolin --- scripts/system/controllers/teleport.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index d223b59bca..71096d2ed7 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -39,7 +39,7 @@ var COLORS_TELEPORT_TOO_CLOSE = { var TELEPORT_CANCEL_RANGE = 1.5; var USE_COOL_IN = true; -var COOL_IN_DURATION = 1750; +var COOL_IN_DURATION = 500; function ThumbPad(hand) { this.hand = hand; @@ -111,6 +111,12 @@ function Teleporter() { } inTeleportMode = true; + this.inCoolIn = true; + print('setting cool in timeout') + this.coolInTimeout = Script.setTimeout(function() { + print('should exit cool in mode now' + COOL_IN_DURATION) + _this.inCoolIn = false; + }, COOL_IN_DURATION) if (this.smoothArrivalInterval !== null) { Script.clearInterval(this.smoothArrivalInterval); @@ -123,10 +129,9 @@ function Teleporter() { this.initialize(); Script.update.connect(this.update); this.updateConnected = true; - this.inCoolIn = true; - Script.setTimeout(function() { - _this.inCoolIn = false; - }, COOL_IN_DURATION) + + + }; this.createTargetOverlay = function() { @@ -202,7 +207,7 @@ function Teleporter() { this.turnOffOverlayBeams(); this.updateConnected = null; - _this.inCoolIn = false; + this.inCoolIn = false; Script.setTimeout(function() { inTeleportMode = false; From 2e22bc5e46849e0c994b5fd3b78899b66fc2bde3 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 14:08:31 -0700 Subject: [PATCH 163/332] new coolin --- scripts/system/controllers/teleport.js | 46 +++++++++++++++++++------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 71096d2ed7..1511d1c2fc 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -294,15 +294,26 @@ function Teleporter() { this.createCancelOverlay(); } } else { - this.deleteCancelOverlay(); - - this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); - if (this.targetOverlay !== null) { - this.updateTargetOverlay(rightIntersection); + if (this.inCoolIn === true) { + this.deleteTargetOverlay(); + this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE); + if (this.cancelOverlay !== null) { + this.updateCancelOverlay(rightIntersection); + } else { + this.createCancelOverlay(); + } } else { - this.createTargetOverlay(); + this.deleteCancelOverlay(); + + this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); + if (this.targetOverlay !== null) { + this.updateTargetOverlay(rightIntersection); + } else { + this.createTargetOverlay(); + } } + } } else { @@ -346,15 +357,26 @@ function Teleporter() { this.createCancelOverlay(); } } else { - this.deleteCancelOverlay(); - this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); - - if (this.targetOverlay !== null) { - this.updateTargetOverlay(leftIntersection); + if (this.inCoolIn === true) { + this.deleteTargetOverlay(); + this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE); + if (this.cancelOverlay !== null) { + this.updateCancelOverlay(leftIntersection); + } else { + this.createCancelOverlay(); + } } else { - this.createTargetOverlay(); + this.deleteCancelOverlay(); + this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); + + if (this.targetOverlay !== null) { + this.updateTargetOverlay(leftIntersection); + } else { + this.createTargetOverlay(); + } } + } From 4492c4e64ecc83134c2cdbcac6a4e6bd8ed57eb3 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 18 Aug 2016 14:19:36 -0700 Subject: [PATCH 164/332] Allow javascript to safely open the correct login window (or answer that it is unneded). --- interface/src/scripting/AccountScriptingInterface.cpp | 5 +++++ interface/src/scripting/AccountScriptingInterface.h | 1 + 2 files changed, 6 insertions(+) diff --git a/interface/src/scripting/AccountScriptingInterface.cpp b/interface/src/scripting/AccountScriptingInterface.cpp index 4090c99ac8..d8533bb769 100644 --- a/interface/src/scripting/AccountScriptingInterface.cpp +++ b/interface/src/scripting/AccountScriptingInterface.cpp @@ -26,6 +26,11 @@ bool AccountScriptingInterface::isLoggedIn() { return accountManager->isLoggedIn(); } +bool AccountScriptingInterface::checkAndSignalForAccessToken() { + auto accountManager = DependencyManager::get(); + return accountManager->checkAndSignalForAccessToken(); +} + QString AccountScriptingInterface::getUsername() { auto accountManager = DependencyManager::get(); if (accountManager->isLoggedIn()) { diff --git a/interface/src/scripting/AccountScriptingInterface.h b/interface/src/scripting/AccountScriptingInterface.h index 49648781ce..0f958f2286 100644 --- a/interface/src/scripting/AccountScriptingInterface.h +++ b/interface/src/scripting/AccountScriptingInterface.h @@ -26,6 +26,7 @@ public slots: static AccountScriptingInterface* getInstance(); QString getUsername(); bool isLoggedIn(); + bool checkAndSignalForAccessToken(); }; #endif // hifi_AccountScriptingInterface_h From aab3b83ad9a138e3356a4851fa9758ec12d74562 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 18 Aug 2016 14:20:53 -0700 Subject: [PATCH 165/332] open login if user tries to share and is not logged in, rather than messaging user that they cannot share --- scripts/system/snapshot.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 8e99783ff6..92b16d31bc 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -32,6 +32,12 @@ var outstanding; function confirmShare(data) { var dialog = new OverlayWebWindow('Snapshot Review', Script.resolvePath("html/ShareSnapshot.html"), 800, 470); function onMessage(message) { + // Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following: + // 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.) + // 2. Although we currently use a single image, we would like to take snapshot, a selfie, a 360 etc. all at the + // same time, show the user all of them, and have the user deselect any that they do not want to share. + // So we'll ultimately be receiving a set of objects, perhaps with different post processing for each. + var isLoggedIn, needsLogin = false; switch (message) { case 'ready': dialog.emitScriptEvent(data); // Send it. @@ -50,16 +56,26 @@ function confirmShare(data) { dialog.webEventReceived.disconnect(onMessage); // I'm not certain that this is necessary. If it is, what do we do on normal close? dialog.close(); dialog.deleteLater(); + isLoggedIn = Account.isLoggedIn(); message.forEach(function (submessage) { + if (submessage.share && !isLoggedIn) { + needsLogin = true; + submessage.share = false; + } if (submessage.share) { print('sharing', submessage.localPath); outstanding++; Window.shareSnapshot(submessage.localPath); + } else { + print('not sharing', submessage.localPath); } }); if (!outstanding && shouldOpenFeedAfterShare()) { showFeedWindow(); } + if (needsLogin) { // after the possible feed, so that the login is on top + Account.checkAndSignalForAccessToken(); + } } } dialog.webEventReceived.connect(onMessage); @@ -124,7 +140,7 @@ function resetButtons(path, notify) { { localPath: path }, { canShare: Boolean(Window.location.placename), - isLoggedIn: Account.isLoggedIn(), + isLoggedIn: true, // Just have the dialog act as though we are. To be removed at both ends later. openFeedAfterShare: shouldOpenFeedAfterShare() } ]); From 859d3ccadc8cd4d45085d73f6989be7d155c1c3b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 14:36:52 -0700 Subject: [PATCH 166/332] tweaks --- scripts/system/controllers/teleport.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 1511d1c2fc..4e5f6e8fef 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -72,6 +72,8 @@ function Trigger(hand) { }; } +var coolInTimeout = null; + function Teleporter() { var _this = this; this.intersection = null; @@ -85,6 +87,7 @@ function Teleporter() { this.tooClose = false; this.inCoolIn = false; + this.initialize = function() { this.createMappings(); this.disableGrab(); @@ -113,7 +116,11 @@ function Teleporter() { inTeleportMode = true; this.inCoolIn = true; print('setting cool in timeout') - this.coolInTimeout = Script.setTimeout(function() { + if (coolInTimeout !== null) { + Script.clearTimeout(coolInTimeout); + + } + coolInTimeout = Script.setTimeout(function() { print('should exit cool in mode now' + COOL_IN_DURATION) _this.inCoolIn = false; }, COOL_IN_DURATION) @@ -208,9 +215,9 @@ function Teleporter() { this.updateConnected = null; this.inCoolIn = false; + inTeleportMode = false; Script.setTimeout(function() { - inTeleportMode = false; _this.enableGrab(); }, 200); }; From c89103481bbcf934858e2ba9579dc95a10348c7d Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 14:40:08 -0700 Subject: [PATCH 167/332] change load order --- scripts/defaultScripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index aba1e1ba51..a194b64435 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -21,11 +21,11 @@ Script.load("system/edit.js"); Script.load("system/mod.js"); Script.load("system/selectAudioDevice.js"); Script.load("system/notifications.js"); -Script.load("system/controllers/toggleAdvancedMovementForHandControllers.js") Script.load("system/controllers/handControllerGrab.js"); Script.load("system/controllers/handControllerPointer.js"); Script.load("system/controllers/squeezeHands.js"); Script.load("system/controllers/grab.js"); Script.load("system/controllers/teleport.js"); +Script.load("system/controllers/toggleAdvancedMovementForHandControllers.js") Script.load("system/dialTone.js"); Script.load("system/firstPersonHMD.js"); \ No newline at end of file From bae51bcfd4b1c7da20404a6050561533a24b0d7b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 15:29:49 -0700 Subject: [PATCH 168/332] remove prints --- scripts/system/controllers/teleport.js | 8 -------- .../toggleAdvancedMovementForHandControllers.js | 11 ++--------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 4e5f6e8fef..98af1eb19b 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -115,13 +115,11 @@ function Teleporter() { inTeleportMode = true; this.inCoolIn = true; - print('setting cool in timeout') if (coolInTimeout !== null) { Script.clearTimeout(coolInTimeout); } coolInTimeout = Script.setTimeout(function() { - print('should exit cool in mode now' + COOL_IN_DURATION) _this.inCoolIn = false; }, COOL_IN_DURATION) @@ -234,15 +232,12 @@ function Teleporter() { teleporter.leftRay(); if ((leftPad.buttonValue === 0) && inTeleportMode === true) { if (_this.inCoolIn === true) { - print('released during cool in period. exit.') _this.exitTeleportMode(); _this.deleteTargetOverlay(); _this.deleteCancelOverlay(); } else { - print('release while not in cool in and in teleport mode. should teleport') _this.teleport(); } - print('some other state::' + leftPad.buttonValue + "///" + inTeleportMode) return; } @@ -253,15 +248,12 @@ function Teleporter() { teleporter.rightRay(); if ((rightPad.buttonValue === 0) && inTeleportMode === true) { if (_this.inCoolIn === true) { - print('released during cool in period. exit.') _this.exitTeleportMode(); _this.deleteTargetOverlay(); _this.deleteCancelOverlay(); } else { - print('release while not in cool in and in teleport mode. should teleport') _this.teleport(); } - print('some other state::' + rightPad.buttonValue + "///" + inTeleportMode) return; } } diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 57aa4cd2c1..696cac714a 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -1,5 +1,6 @@ -// Created by james b. pollack @imgntn on 7/2/2016 +// Created by james b. pollack @imgntn on 8/18/2016 // Copyright 2016 High Fidelity, Inc. +// //advanced movements settings are in individual controller json files //what we do is check the status of the 'advance movement' checkbox when you enter HMD mode //if 'advanced movement' is checked...we give you the defaults that are in the json. @@ -49,15 +50,12 @@ function registerBasicMapping() { } if (Controller.Hardware.Vive !== undefined) { if (value > 0.75 && inFlipTurn === false) { - print('vive should flip turn') inFlipTurn = true; rotate180(); Script.setTimeout(function() { - print('vive should be able to flip turn again') inFlipTurn = false; }, 250) } else { - print('vive should not flip turn') } } @@ -71,19 +69,15 @@ function registerBasicMapping() { } if (Controller.Hardware.Vive !== undefined) { if (value > 0.75 && inFlipTurn === false) { - print('vive should flip turn') inFlipTurn = true; rotate180(); Script.setTimeout(function() { - print('vive should be able to flip turn again') inFlipTurn = false; }, 250) } else { - print('vive should not flip turn') } } - print('should do RY stuff' + value + ":stick:" + stick); return; }) } @@ -114,7 +108,6 @@ function menuItemEvent(menuItem) { enableMappings(); } } - } addAdvancedMovementItemToSettingsMenu(); From 3a8de5ab0d15acb1eb9859df39c0c67a5ed8d9f4 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 15:58:12 -0700 Subject: [PATCH 169/332] fix rotation function --- .../toggleAdvancedMovementForHandControllers.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 696cac714a..f5fdc6301a 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -35,7 +35,12 @@ function addAdvancedMovementItemToSettingsMenu() { } function rotate180() { - MyAvatar.orientation = Quat.inverse(MyAvatar.orientation); + var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.angleAxis(180, { + x: 0, + y: 1, + z: 0 + })) + MyAvatar.orientation = newOrientation } var inFlipTurn = false; From f6670a63746fcec41e9227b819a7519b4a2db16f Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 18 Aug 2016 16:29:42 -0700 Subject: [PATCH 170/332] new optional aspect-ratio argument to snapshot chain (javascript through c++ display plugin). When non-zero, it pulls out the largest piece from the center that maintains that ratio. Snapshot button uses 1.91. --- interface/src/Application.cpp | 4 ++-- interface/src/Application.h | 2 +- .../src/scripting/WindowScriptingInterface.cpp | 6 +++--- .../src/scripting/WindowScriptingInterface.h | 2 +- .../src/display-plugins/NullDisplayPlugin.cpp | 2 +- .../src/display-plugins/NullDisplayPlugin.h | 2 +- .../src/display-plugins/OpenGLDisplayPlugin.cpp | 17 ++++++++++++++--- .../src/display-plugins/OpenGLDisplayPlugin.h | 2 +- libraries/plugins/src/plugins/DisplayPlugin.h | 2 +- scripts/system/snapshot.js | 2 +- 10 files changed, 26 insertions(+), 15 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 340597f18a..59ec1c0641 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5130,13 +5130,13 @@ void Application::toggleLogDialog() { } } -void Application::takeSnapshot(bool notify) { +void Application::takeSnapshot(bool notify, float aspectRatio) { QMediaPlayer* player = new QMediaPlayer(); QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav"); player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath())); player->play(); - QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot()); + QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio)); emit DependencyManager::get()->snapshotTaken(path, notify); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 9fcce66f55..667969abf1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -250,7 +250,7 @@ public: float getAvatarSimrate() const { return _avatarSimCounter.rate(); } float getAverageSimsPerSecond() const { return _simCounter.rate(); } - void takeSnapshot(bool notify); + void takeSnapshot(bool notify, float aspectRatio = 0.0f); void shareSnapshot(const QString& filename); model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index f843673c07..f68f6d212a 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -204,10 +204,10 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) { QApplication::clipboard()->setText(text); } -void WindowScriptingInterface::takeSnapshot(bool notify) { +void WindowScriptingInterface::takeSnapshot(bool notify, float aspectRatio) { // only evil-doers call takeSnapshot from a random thread - qApp->postLambdaEvent([notify] { - qApp->takeSnapshot(notify); + qApp->postLambdaEvent([notify, aspectRatio] { + qApp->takeSnapshot(notify, aspectRatio); }); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 15dc1a8004..ca82753598 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -55,7 +55,7 @@ public slots: QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); void showAssetServer(const QString& upload = ""); void copyToClipboard(const QString& text); - void takeSnapshot(bool notify); + void takeSnapshot(bool notify = true, float aspectRatio = 0.0f); void shareSnapshot(const QString& path); signals: diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index 5ee05fa2e3..a4777b087b 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -31,6 +31,6 @@ void NullDisplayPlugin::submitFrame(const gpu::FramePointer& frame) { } } -QImage NullDisplayPlugin::getScreenshot() const { +QImage NullDisplayPlugin::getScreenshot(float aspectRatio) const { return QImage(); } diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index 198c89ae78..1852ed53ee 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -18,7 +18,7 @@ public: glm::uvec2 getRecommendedRenderSize() const override; bool hasFocus() const override; void submitFrame(const gpu::FramePointer& newFrame) override; - QImage getScreenshot() const override; + QImage getScreenshot(float aspectRatio = 0.0f) const override; private: static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 905042cb79..7334191b75 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -659,15 +659,26 @@ void OpenGLDisplayPlugin::withMainThreadContext(std::function f) const { _container->makeRenderingContextCurrent(); } -QImage OpenGLDisplayPlugin::getScreenshot() const { +QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { auto size = _compositeFramebuffer->getSize(); if (isHmd()) { size.x /= 2; } + auto bestSize = size; + uvec2 corner(0); + if (aspectRatio != 0.0f) { // Pick out the largest piece of the center that produces the requested width/height aspectRatio + if (((size.y * aspectRatio) + 0.5f) < size.x) { + bestSize.x = round(size.y * aspectRatio); + } else { + bestSize.y = round(size.x / aspectRatio); + } + 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(size.x, size.y, QImage::Format_ARGB32); + QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32); withMainThreadContext([&] { - glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(uvec2(0), size), screenshot); + glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), 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 48f9a78eda..51b33c9bcd 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -54,7 +54,7 @@ public: return getSurfaceSize(); } - QImage getScreenshot() const override; + QImage getScreenshot(float aspectRatio = 0.0f) const override; float presentRate() const override; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 49c341cdcb..288cee3223 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -172,7 +172,7 @@ public: } // Fetch the most recently displayed image as a QImage - virtual QImage getScreenshot() const = 0; + virtual QImage getScreenshot(float aspectRatio = 0.0f) const = 0; // will query the underlying hmd api to compute the most recent head pose virtual bool beginFrameRender(uint32_t frameIndex) { return true; } diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 92b16d31bc..5c893fad4a 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -116,7 +116,7 @@ function onClicked() { // take snapshot (with no notification) Script.setTimeout(function () { - Window.takeSnapshot(false); + Window.takeSnapshot(false, 1.91); }, SNAPSHOT_DELAY); } From 214a7051997a9ffd849974e482643bdc7862972a Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 16:50:50 -0700 Subject: [PATCH 171/332] slow 180 rate --- ...toggleAdvancedMovementForHandControllers.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index f5fdc6301a..383e4f78f2 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -11,6 +11,8 @@ var mappingName, basicMapping, isChecked; +var TURN_RATE = 1000; + var previousSetting = Settings.getValue('advancedMovementForHandControllersIsChecked'); if (previousSetting === '') { previousSetting = false; @@ -59,7 +61,7 @@ function registerBasicMapping() { rotate180(); Script.setTimeout(function() { inFlipTurn = false; - }, 250) + }, TURN_RATE) } else { } @@ -122,11 +124,15 @@ Script.scriptEnding.connect(scriptEnding); Menu.menuItemEvent.connect(menuItemEvent); registerBasicMapping(); -if (previousSetting === true) { - disableMappings(); -} else if (previousSetting === false) { - enableMappings(); -} + +Script.setTimeout(function() { + if (previousSetting === true) { + disableMappings(); + } else(previousSetting === false) { + enableMappings(); + } + +}, 0) HMD.displayModeChanged.connect(function(isHMDMode) { From a0d4fd4c0e999e7f5b25cb7b5b18db0994fc5eb6 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 16:51:04 -0700 Subject: [PATCH 172/332] fix bug --- .../controllers/toggleAdvancedMovementForHandControllers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 383e4f78f2..44a18e5a5a 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -128,7 +128,7 @@ registerBasicMapping(); Script.setTimeout(function() { if (previousSetting === true) { disableMappings(); - } else(previousSetting === false) { + } else { enableMappings(); } From 855561c0146fcdc9497eb5e287be84aec80cf0e2 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 16:52:32 -0700 Subject: [PATCH 173/332] moar --- .../controllers/toggleAdvancedMovementForHandControllers.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 44a18e5a5a..71b18aaac6 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -23,6 +23,7 @@ if (previousSetting === true) { isChecked = true; } if (previousSetting === false) { + previousSetting = false; isChecked = false; } @@ -132,7 +133,7 @@ Script.setTimeout(function() { enableMappings(); } -}, 0) +}, 100) HMD.displayModeChanged.connect(function(isHMDMode) { From b694b8e54146ed5663a49c008ccc2f65585fda4c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 18 Aug 2016 17:24:18 -0700 Subject: [PATCH 174/332] update turn rate for left stick --- scripts/system/controllers/teleport.js | 2 +- .../controllers/toggleAdvancedMovementForHandControllers.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 98af1eb19b..77d0109b9f 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -37,7 +37,7 @@ var COLORS_TELEPORT_TOO_CLOSE = { blue: 73 }; -var TELEPORT_CANCEL_RANGE = 1.5; +var TELEPORT_CANCEL_RANGE = 1; var USE_COOL_IN = true; var COOL_IN_DURATION = 500; diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 71b18aaac6..91b635a919 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -23,7 +23,6 @@ if (previousSetting === true) { isChecked = true; } if (previousSetting === false) { - previousSetting = false; isChecked = false; } @@ -81,7 +80,7 @@ function registerBasicMapping() { rotate180(); Script.setTimeout(function() { inFlipTurn = false; - }, 250) + }, TURN_RATE) } else { } From 4db1687746801a9e427376a92b036f80282e9b95 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 18 Aug 2016 18:37:52 -0700 Subject: [PATCH 175/332] bug fix for when web-entity is deleted while being scrolled --- .../system/controllers/handControllerGrab.js | 155 ++++++++++-------- 1 file changed, 84 insertions(+), 71 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index bb94285af9..cef2c4fdda 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -253,26 +253,32 @@ function handLaserIntersectEntity(entityID, hand) { var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation); var props = entityPropertiesCache.getProps(entityID); - var planePosition = props.position; - var planeNormal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1.0}); - var rayStart = worldHandPosition; - var rayDirection = Quat.getUp(worldHandRotation); - var intersectionInfo = rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection); - var intersectionPoint = planePosition; - if (intersectionInfo.hit && intersectionInfo.distance > 0) { - intersectionPoint = Vec3.sum(rayStart, Vec3.multiply(intersectionInfo.distance, rayDirection)); + if (props.position) { + var planePosition = props.position; + var planeNormal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1.0}); + var rayStart = worldHandPosition; + var rayDirection = Quat.getUp(worldHandRotation); + var intersectionInfo = rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection); + + var intersectionPoint = planePosition; + if (intersectionInfo.hit && intersectionInfo.distance > 0) { + intersectionPoint = Vec3.sum(rayStart, Vec3.multiply(intersectionInfo.distance, rayDirection)); + } else { + intersectionPoint = planePosition; + } + intersectionInfo.point = intersectionPoint; + intersectionInfo.normal = planeNormal; + intersectionInfo.searchRay = { + origin: rayStart, + direction: rayDirection, + length: PICK_MAX_DISTANCE + }; + return intersectionInfo; } else { - intersectionPoint = planePosition; + // entity has been destroyed? or is no longer in cache + return null; } - intersectionInfo.point = intersectionPoint; - intersectionInfo.normal = planeNormal; - intersectionInfo.searchRay = { - origin: rayStart, - direction: rayDirection, - length: PICK_MAX_DISTANCE - }; - return intersectionInfo; } function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) { @@ -2109,45 +2115,47 @@ function MyController(hand) { this.entityTouchingEnter = function() { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand); + if (intersectInfo) { + var pointerEvent = { + type: "Press", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "Primary", + isPrimaryButton: true, + isSecondaryButton: false, + isTertiaryButton: false + }; - var pointerEvent = { - type: "Press", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), - pos3D: intersectInfo.point, - normal: intersectInfo.normal, - direction: intersectInfo.searchRay.direction, - button: "Primary", - isPrimaryButton: true, - isSecondaryButton: false, - isTertiaryButton: false - }; - - Entities.sendMousePressOnEntity(this.grabbedEntity, pointerEvent); - Entities.sendClickDownOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendMousePressOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendClickDownOnEntity(this.grabbedEntity, pointerEvent); + } }; this.entityTouchingExit = function() { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand); + if (intersectInfo) { + var pointerEvent = { + type: "Release", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "Primary", + isPrimaryButton: true, + isSecondaryButton: false, + isTertiaryButton: false + }; - var pointerEvent = { - type: "Release", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), - pos3D: intersectInfo.point, - normal: intersectInfo.normal, - direction: intersectInfo.searchRay.direction, - button: "Primary", - isPrimaryButton: true, - isSecondaryButton: false, - isTertiaryButton: false - }; - - Entities.sendMouseReleaseOnEntity(this.grabbedEntity, pointerEvent); - Entities.sendClickReleaseOnEntity(this.grabbedEntity, pointerEvent); - Entities.sendHoverLeaveEntity(this.grabbedEntity, pointerEvent); - this.focusedEntity = null; + Entities.sendMouseReleaseOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendClickReleaseOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendHoverLeaveEntity(this.grabbedEntity, pointerEvent); + this.focusedEntity = null; + } }; this.entityTouching = function() { @@ -2160,30 +2168,35 @@ function MyController(hand) { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand); + if (intersectInfo) { - if (Entities.keyboardFocusEntity != this.grabbedEntity) { - Entities.keyboardFocusEntity = this.grabbedEntity; + if (Entities.keyboardFocusEntity != this.grabbedEntity) { + Entities.keyboardFocusEntity = this.grabbedEntity; + } + + var pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "NoButtons", + isPrimaryButton: true, + isSecondaryButton: false, + isTertiaryButton: false + }; + + Entities.sendMouseMoveOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendHoldingClickOnEntity(this.grabbedEntity, pointerEvent); + + this.intersectionDistance = intersectInfo.distance; + this.searchIndicatorOn(intersectInfo.searchRay); + Reticle.setVisible(false); + } else { + this.setState(STATE_OFF, "grabbed entity was destroyed"); + return; } - - var pointerEvent = { - type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), - pos3D: intersectInfo.point, - normal: intersectInfo.normal, - direction: intersectInfo.searchRay.direction, - button: "NoButtons", - isPrimaryButton: true, - isSecondaryButton: false, - isTertiaryButton: false - }; - - Entities.sendMouseMoveOnEntity(this.grabbedEntity, pointerEvent); - Entities.sendHoldingClickOnEntity(this.grabbedEntity, pointerEvent); - - this.intersectionDistance = intersectInfo.distance; - this.searchIndicatorOn(intersectInfo.searchRay); - Reticle.setVisible(false); }; this.release = function() { From ff95ef2492cef4661e254edf5813c3437e8e7a0b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 19 Aug 2016 08:53:33 -0700 Subject: [PATCH 176/332] ignoreIK flag on action-grabbable objects works again --- scripts/system/controllers/handControllerGrab.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 1264502aa7..470c227f7b 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1674,12 +1674,12 @@ function MyController(hand) { var handRotation = (this.hand === RIGHT_HAND) ? MyAvatar.getRightPalmRotation() : MyAvatar.getLeftPalmRotation(); var handPosition = this.getHandPosition(); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false; + var hasPresetPosition = false; if (this.state == STATE_HOLD && this.grabbedHotspot) { - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); // if an object is "equipped" and has a predefined offset, use it. - this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false; - var offsets = USE_ATTACH_POINT_SETTINGS && getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand); if (offsets) { this.offsetPosition = offsets[0]; @@ -1694,8 +1694,6 @@ function MyController(hand) { } } } else { - this.ignoreIK = false; - var objectRotation = grabbedProperties.rotation; this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); From 4d7d483c0eac43fca4232c6b4d44ff1601d9c372 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 19 Aug 2016 09:51:58 -0700 Subject: [PATCH 177/332] use ceil --- .../display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 7334191b75..b304b3802e 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -667,7 +667,7 @@ QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { auto bestSize = size; uvec2 corner(0); if (aspectRatio != 0.0f) { // Pick out the largest piece of the center that produces the requested width/height aspectRatio - if (((size.y * aspectRatio) + 0.5f) < size.x) { + if (ceil(size.y * aspectRatio) < size.x) { bestSize.x = round(size.y * aspectRatio); } else { bestSize.y = round(size.x / aspectRatio); From 304c313db71939a51e36dde69619e90d8f3ad37c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 19 Aug 2016 10:15:30 -0700 Subject: [PATCH 178/332] minor cleanup --- libraries/physics/src/ShapeFactory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 5182038c27..67072d46d4 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -178,14 +178,14 @@ btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) { } const ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); - if (triangleIndices.size() < 3) { + int32_t numIndices = triangleIndices.size(); + if (numIndices < 3) { // not enough indices to make a single triangle return nullptr; } // allocate mesh buffers btIndexedMesh mesh; - int32_t numIndices = triangleIndices.size(); const int32_t VERTICES_PER_TRIANGLE = 3; mesh.m_numTriangles = numIndices / VERTICES_PER_TRIANGLE; if (numIndices < INT16_MAX) { From c3f165d9dafc82d4449ea19b9b54139f40a863dc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 19 Aug 2016 10:15:37 -0700 Subject: [PATCH 179/332] BufferView::Iterator doesn't support operator< --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index e135107323..ac447417aa 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -786,7 +786,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { auto indexItr = indices.cbegin() + partItr->_startIndex; auto indexEnd = indexItr + numIndices; - while (indexItr < indexEnd) { + while (indexItr != indexEnd) { triangleIndices.push_back(*indexItr + meshIndexOffset); ++indexItr; } @@ -810,7 +810,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // the rest use previous and next index uint32_t triangleCount = 1; - while (indexItr < indexEnd) { + while (indexItr != indexEnd) { if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) { if (triangleCount % 2 == 0) { // even triangles use first two indices in order @@ -843,7 +843,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { auto indexItr = indices.cbegin() + partItr->_startIndex; auto indexEnd = indexItr + numIndices; - while (indexItr < indexEnd) { + while (indexItr != indexEnd) { uniqueIndices.insert(*indexItr); ++indexItr; } @@ -861,7 +861,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // the rest use previous and next index uint32_t triangleCount = 1; - while (indexItr < indexEnd) { + while (indexItr != indexEnd) { if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) { if (triangleCount % 2 == 0) { // EVEN triangles use first two indices in order From 5b5ed220bc2b81432e3da965a753661701c1f994 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 19 Aug 2016 10:26:47 -0700 Subject: [PATCH 180/332] add BufferView::Iterator operator<() and friend --- libraries/gpu/src/gpu/Buffer.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index da1a987bee..d6b8f943dd 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -224,6 +224,8 @@ public: bool operator==(const Iterator& iterator) const { return (_ptr == iterator.getConstPtr()); } bool operator!=(const Iterator& iterator) const { return (_ptr != iterator.getConstPtr()); } + bool operator<(const Iterator& iterator) const { return (_ptr < iterator.getConstPtr()); } + bool operator>(const Iterator& iterator) const { return (_ptr > iterator.getConstPtr()); } void movePtr(const Index& movement) { auto byteptr = ((Byte*)_ptr); From 8bb6bb7c6f260d8fc6ae3d4be22cbee28df29810 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 19 Aug 2016 10:56:52 -0700 Subject: [PATCH 181/332] generalized keyboard focus interface Removed dynamic casts to RenderableWebEntityItem, instead virtual methods were added to EntityItem to facilitate this. * bool wantsKeyboardFocus() * void setProxyWindow() * QObject* getEventHandler() --- interface/src/Application.cpp | 23 ++++++++----------- .../src/RenderableWebEntityItem.h | 6 +++-- libraries/entities/src/EntityItem.h | 5 ++++ libraries/entities/src/WebEntityItem.h | 2 -- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 070be759f3..ebae24b3b8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2048,10 +2048,9 @@ bool Application::event(QEvent* event) { case QEvent::KeyRelease: { auto entityScriptingInterface = DependencyManager::get(); auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem.get()); - RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); - if (webEntity && webEntity->getEventHandler()) { + if (entity && entity->getEventHandler()) { event->setAccepted(false); - QCoreApplication::sendEvent(webEntity->getEventHandler(), event); + QCoreApplication::sendEvent(entity->getEventHandler(), event); if (event->isAccepted()) { _lastAcceptedKeyPress = usecTimestampNow(); return true; @@ -2931,10 +2930,9 @@ void Application::idle(float nsecsElapsed) { // update position of highlight overlay auto entityScriptingInterface = DependencyManager::get(); auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem.get()); - RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); - if (webEntity && _keyboardFocusHighlight) { - _keyboardFocusHighlight->setRotation(webEntity->getRotation()); - _keyboardFocusHighlight->setPosition(webEntity->getPosition()); + if (entity && _keyboardFocusHighlight) { + _keyboardFocusHighlight->setRotation(entity->getRotation()); + _keyboardFocusHighlight->setPosition(entity->getPosition()); } } } @@ -3540,9 +3538,8 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { auto properties = entityScriptingInterface->getEntityProperties(entityItemID); if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); - RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); - if (webEntity) { - webEntity->setProxyWindow(_window->windowHandle()); + if (entity && entity->wantsKeyboardFocus()) { + entity->setProxyWindow(_window->windowHandle()); if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->pluginFocusOutEvent(); } @@ -3560,9 +3557,9 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { _keyboardFocusHighlight->setIgnoreRayIntersection(true); _keyboardFocusHighlight->setDrawInFront(false); } - _keyboardFocusHighlight->setRotation(webEntity->getRotation()); - _keyboardFocusHighlight->setPosition(webEntity->getPosition()); - _keyboardFocusHighlight->setDimensions(webEntity->getDimensions() * 1.05f); + _keyboardFocusHighlight->setRotation(entity->getRotation()); + _keyboardFocusHighlight->setPosition(entity->getPosition()); + _keyboardFocusHighlight->setDimensions(entity->getDimensions() * 1.05f); _keyboardFocusHighlight->setVisible(true); _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight); } diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 578bd2f3b1..ee8a484109 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -32,8 +32,10 @@ public: virtual void render(RenderArgs* args) override; virtual void setSourceUrl(const QString& value) override; - void setProxyWindow(QWindow* proxyWindow); - QObject* getEventHandler(); + virtual bool wantsHandControllerPointerEvents() const override { return true; } + virtual bool wantsKeyboardFocus() const override { return true; } + virtual void setProxyWindow(QWindow* proxyWindow) override; + virtual QObject* getEventHandler() override; void handlePointerEvent(const PointerEvent& event); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index e2bc9b568b..5427514573 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -17,6 +17,8 @@ #include +#include + #include // for Animation, AnimationCache, and AnimationPointer classes #include // for EncodeBitstreamParams class #include // for OctreeElement::AppendState @@ -435,6 +437,9 @@ public: virtual bool isTransparent() { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; } virtual bool wantsHandControllerPointerEvents() const { return false; } + virtual bool wantsKeyboardFocus() const { return false; } + virtual void setProxyWindow(QWindow* proxyWindow) {} + virtual QObject* getEventHandler() { return nullptr; } protected: diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index b63edc99ea..538d0c2f45 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -54,8 +54,6 @@ public: virtual void setSourceUrl(const QString& value); const QString& getSourceUrl() const; - virtual bool wantsHandControllerPointerEvents() const { return true; } - protected: QString _sourceUrl; }; From 6a076bcee4316d40bb7bec8c652c2abc4cff1145 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 19 Aug 2016 11:09:12 -0700 Subject: [PATCH 182/332] PRoviding a fix for the macOS build --- libraries/gpu/src/gpu/Buffer.h | 8 ++++---- .../render-utils/src/AmbientOcclusionEffect.cpp | 17 +---------------- .../render-utils/src/AmbientOcclusionEffect.h | 9 ++++++--- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index 8160f648fc..9fc0108e35 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -180,10 +180,10 @@ public: using Index = int; BufferPointer _buffer; - Size _offset; - Size _size; - Element _element; - uint16 _stride; + Size _offset { 0 }; + Size _size { 0 }; + Element _element { DEFAULT_ELEMENT }; + uint16 _stride { 0 }; BufferView(const BufferView& view) = default; BufferView& operator=(const BufferView& view) = default; diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 3470e4b8ff..5f3c3ca76a 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -340,7 +340,6 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext RenderArgs* args = renderContext->args; const auto& frameTransform = inputs.get0(); - const auto& deferredFramebuffer = inputs.get1(); const auto& linearDepthFramebuffer = inputs.get2(); auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); @@ -372,13 +371,9 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext float tMin = occlusionViewport.y / (float)framebufferSize.y; float tHeight = occlusionViewport.w / (float)framebufferSize.y; - auto resolutionLevel = _parametersBuffer->getResolutionLevel(); - //_parametersBuffer->ditheringInfo.y += 0.25f; - // Running in stero ? - bool isStereo = args->_context->isStereo(); auto occlusionPipeline = getOcclusionPipeline(); auto firstHBlurPipeline = getHBlurPipeline(); @@ -486,13 +481,7 @@ void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, RenderArgs* args = renderContext->args; - // FIXME: Different render modes should have different tasks -// if (args->_renderMode != RenderArgs::DEFAULT_RENDER_MODE) { -// return; -// } - const auto& frameTransform = inputs.get0(); - const auto& deferredFramebuffer = inputs.get1(); const auto& linearDepthFramebuffer = inputs.get2(); const auto& ambientOcclusionUniforms = inputs.get3(); @@ -519,11 +508,7 @@ void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, float sWidth = occlusionViewport.z / (float)framebufferSize.x; float tMin = occlusionViewport.y / (float)framebufferSize.y; float tHeight = occlusionViewport.w / (float)framebufferSize.y; - - - // Running in stero ? - bool isStereo = args->_context->isStereo(); - + auto debugPipeline = getDebugPipeline(); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 5fdea81979..0c393709d5 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -109,15 +109,18 @@ signals: namespace gpu { template class UniformBuffer : public gpu::BufferView { public: + + static BufferPointer makeBuffer() { + T t; + return std::make_shared(sizeof(T), (const gpu::Byte*) &t); + } ~UniformBuffer() {}; - UniformBuffer() : gpu::BufferView(std::make_shared(sizeof(T), (const gpu::Byte*) &T())) {} + UniformBuffer() : gpu::BufferView(makeBuffer()) {} const T* operator ->() const { return &get(); } T* operator ->() { return &edit(); } }; - - } class AmbientOcclusionEffect { From 8bc9a92429396763b36761b69bc5848e2c54053f Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Fri, 19 Aug 2016 11:32:13 -0700 Subject: [PATCH 183/332] removed extraneous debug prints --- interface/resources/qml/AssetServer.qml | 1 - interface/resources/qml/MarketplaceComboBox.qml | 2 -- interface/resources/qml/controls-uit/WebView.qml | 1 - interface/resources/qml/hifi/Desktop.qml | 7 ++----- libraries/script-engine/src/FileScriptingInterface.cpp | 7 +------ 5 files changed, 3 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 87dcf9368c..050bc8e99e 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -328,7 +328,6 @@ ScrollingWindow { id: timer } function uploadClicked(fileUrl) { - console.log("Upload clicked url: " + fileUrl); if (uploadOpen) { return; } diff --git a/interface/resources/qml/MarketplaceComboBox.qml b/interface/resources/qml/MarketplaceComboBox.qml index 559f4b9d65..7a600d7bbe 100644 --- a/interface/resources/qml/MarketplaceComboBox.qml +++ b/interface/resources/qml/MarketplaceComboBox.qml @@ -49,7 +49,6 @@ Rectangle { element.click();' onNewViewRequested: { - console.log("new view requested url"); var component = Qt.createComponent("Browser.qml"); var newWindow = component.createObject(desktop); request.openIn(newWindow.webView); @@ -68,7 +67,6 @@ Rectangle { onLinkHovered: { desktop.currentUrl = hoveredUrl - console.log("my url in WebView: " + desktop.currentUrl) if (File.isZippedFbx(desktop.currentUrl)) { runJavaScript(simpleDownload, function(){console.log("ran the JS");}); } diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index 8069ee79fb..faf7f746a2 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -51,7 +51,6 @@ WebEngineView { onLoadingChanged: { // Required to support clicking on "hifi://" links - console.log("loading change requested url"); if (WebEngineView.LoadStartedStatus == loadRequest.status) { var url = loadRequest.url.toString(); if (urlHandler.canHandleUrl(url)) { diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index ebc776617f..62f31f07fc 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -80,7 +80,7 @@ OriginalDesktop.Desktop { property string tempDir: "" function initWebviewProfileHandlers(profile) { - console.log("the webview url in desktop is: " + currentUrl); + console.log("The webview url in desktop is: " + currentUrl); if (webViewProfileSetup) return; webViewProfileSetup = true; @@ -90,9 +90,8 @@ OriginalDesktop.Desktop { tempDir = File.getTempDir(); console.log("Temp dir created: " + tempDir); download.path = tempDir + "/" + adaptedPath; - console.log("Path where it should download: " + download.path); + console.log("Path where object should download: " + download.path); download.accept(); - console.log("Download accept: " + download.state); if (download.state === WebEngineDownloadItem.DownloadInterrupted) { console.log("download failed to complete"); } @@ -100,8 +99,6 @@ OriginalDesktop.Desktop { profile.downloadFinished.connect(function(download){ if (download.state === WebEngineDownloadItem.DownloadCompleted) { - console.log("Download Finished: " + download.state); - console.log("File object is: " + File); File.runUnzip(download.path, currentUrl); } else { console.log("The download was corrupted, state: " + download.state); diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 2c8a68258f..fa38e46d31 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -35,7 +35,6 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url) { qDebug() << "Url that was downloaded: " + url.toString(); qDebug() << "Path where download is saved: " + path; QString fileName = "/" + path.section("/", -1); - qDebug() << "Filename to remove from temp path: " + fileName; QString tempDir = path; tempDir.remove(fileName); qDebug() << "Temporary directory at: " + tempDir; @@ -46,9 +45,8 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url) { QString file = unzipFile(path, tempDir); if (file != "") { - qDebug() << "file to upload: " + file; + qDebug() << "Object file to upload: " + file; QUrl url = QUrl::fromLocalFile(file); - qDebug() << "url from local file: " + url.toString(); emit unzipSuccess(url.toString()); } else { qDebug() << "unzip failed"; @@ -73,7 +71,6 @@ bool FileScriptingInterface::isTempDir(QString tempDir) { bool FileScriptingInterface::isZippedFbx(QUrl url) { if (url.toString().contains(".zip") && url.toString().contains("fbx")) return true; - qDebug() << "This model is not a .fbx packaged in a .zip. Please try with another model."; return false; } @@ -108,9 +105,7 @@ QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { QDir dir(path); QString dirName = dir.path(); - qDebug() << "Zip directory is stored at: " + dirName; QString target = tempDir + "/model_repo"; - qDebug() << "Target path: " + target; QStringList list = JlCompress::extractDir(dirName, target); qDebug() << list; From ac4ecc9907299e4639b9aee651e3400088535198 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 19 Aug 2016 14:10:27 -0700 Subject: [PATCH 184/332] don't send a login-denied packet when we're still waiting for user's public key. --- domain-server/src/DomainGatekeeper.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index c827e79223..23a53c3eb0 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -509,9 +509,7 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username, } } else { if (!senderSockAddr.isNull()) { - qDebug() << "Insufficient data to decrypt username signature - denying connection."; - sendConnectionDeniedPacket("Insufficient data", senderSockAddr, - DomainHandler::ConnectionRefusedReason::LoginError); + qDebug() << "Insufficient data to decrypt username signature - delaying connection."; } } From a3d64dbd9c360d15fe53e706fde540db1ca83097 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 19 Aug 2016 14:14:31 -0700 Subject: [PATCH 185/332] code cleanup --- interface/resources/qml/AddressBarDialog.qml | 3 +-- interface/src/Application.h | 2 +- interface/src/scripting/WindowScriptingInterface.h | 2 +- interface/src/ui/AddressBarDialog.h | 5 ++++- interface/src/ui/Snapshot.cpp | 10 ++++------ .../html/{ShareSnapshot.html => SnapshotReview.html} | 0 scripts/system/snapshot.js | 10 ++++------ 7 files changed, 15 insertions(+), 17 deletions(-) rename scripts/system/html/{ShareSnapshot.html => SnapshotReview.html} (100%) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 089bec07f4..b61b4111d6 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -73,8 +73,7 @@ Window { property var allStories: []; property int cardWidth: 200; property int cardHeight: 152; - property string metaverseBase: "https://metaverse.highfidelity.com/api/v1/"; - //property string metaverseBase: "http://10.0.0.241:3000/api/v1/"; + property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/"; property bool useHTML: false; // fixme: remove this and all false branches after the server is updated AddressBarDialog { diff --git a/interface/src/Application.h b/interface/src/Application.h index 667969abf1..0ce1aac566 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -266,7 +266,7 @@ signals: void activeDisplayPluginChanged(); void uploadRequest(QString path); - void receivedHifiSchemeURL(QString path); + void receivedHifiSchemeURL(const QString& url); public slots: QVector pasteEntities(float x, float y, float z); diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index ca82753598..7a01be7fac 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -63,7 +63,7 @@ signals: void svoImportRequested(const QString& url); void domainConnectionRefused(const QString& reasonMessage, int reasonCode); void snapshotTaken(const QString& path, bool notify); - void snapshotShared(bool success); + void snapshotShared(const QString& error); private slots: WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height); diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index 68a12d4eb0..3197770433 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -14,6 +14,7 @@ #define hifi_AddressBarDialog_h #include +#include class AddressBarDialog : public OffscreenQmlDialog { Q_OBJECT @@ -21,6 +22,7 @@ class AddressBarDialog : public OffscreenQmlDialog { Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged) Q_PROPERTY(bool forwardEnabled READ forwardEnabled NOTIFY forwardEnabledChanged) Q_PROPERTY(bool useFeed READ useFeed WRITE setUseFeed NOTIFY useFeedChanged) + Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl) public: AddressBarDialog(QQuickItem* parent = nullptr); @@ -28,12 +30,13 @@ public: bool forwardEnabled() { return _forwardEnabled; } bool useFeed() { return _useFeed; } void setUseFeed(bool useFeed) { if (_useFeed != useFeed) { _useFeed = useFeed; emit useFeedChanged(); } } + QString metaverseServerUrl() { return NetworkingConstants::METAVERSE_SERVER_URL.toString(); } signals: void backEnabledChanged(); void forwardEnabledChanged(); void useFeedChanged(); - void receivedHifiSchemeURL(QString url); + void receivedHifiSchemeURL(const QString& url); protected: void displayAddressOfflineMessage(); diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index b7cdb1a126..4513894f5a 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -215,20 +215,18 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { QJsonDocument(rootObject).toJson()); } else { - emit DependencyManager::get()->snapshotShared(false); + emit DependencyManager::get()->snapshotShared(contents); } } void SnapshotUploader::uploadFailure(QNetworkReply& reply) { - // TODO: parse response, potentially helpful for logging (?) - emit DependencyManager::get()->snapshotShared(false); + emit DependencyManager::get()->snapshotShared(reply.readAll()); } void SnapshotUploader::createStorySuccess(QNetworkReply& reply) { - emit DependencyManager::get()->snapshotShared(true); + emit DependencyManager::get()->snapshotShared(""); } void SnapshotUploader::createStoryFailure(QNetworkReply& reply) { - // TODO: parse response, potentially helpful for logging (?) - emit DependencyManager::get()->snapshotShared(false); + emit DependencyManager::get()->snapshotShared(reply.readAll()); } diff --git a/scripts/system/html/ShareSnapshot.html b/scripts/system/html/SnapshotReview.html similarity index 100% rename from scripts/system/html/ShareSnapshot.html rename to scripts/system/html/SnapshotReview.html diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 5c893fad4a..fab26a70e6 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -30,7 +30,7 @@ function showFeedWindow() { var outstanding; function confirmShare(data) { - var dialog = new OverlayWebWindow('Snapshot Review', Script.resolvePath("html/ShareSnapshot.html"), 800, 470); + var dialog = new OverlayWebWindow('Snapshot Review', Script.resolvePath("html/SnapshotReview.html"), 800, 470); function onMessage(message) { // Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following: // 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.) @@ -82,13 +82,11 @@ function confirmShare(data) { dialog.raise(); } -function snapshotShared(success) { - if (success) { - // for now just print status +function snapshotShared(errorMessage) { + if (!errorMessage) { print('snapshot uploaded and shared'); } else { - // for now just print an error. - print('snapshot upload/share failed'); + print(errorMessage); } if ((--outstanding <= 0) && shouldOpenFeedAfterShare()) { showFeedWindow(); From 653b1acc4ab5793ac16a4ce1f785966579089896 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 19 Aug 2016 14:29:14 -0700 Subject: [PATCH 186/332] cleanup --- ...oggleAdvancedMovementForHandControllers.js | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 91b635a919..19f28e51e7 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -12,8 +12,9 @@ var mappingName, basicMapping, isChecked; var TURN_RATE = 1000; - -var previousSetting = Settings.getValue('advancedMovementForHandControllersIsChecked'); +var MENU_ITEM_NAME = "Advanced Movement For Hand Controllers"; +var SETTINGS_KEY = 'advancedMovementForHandControllersIsChecked'; +var previousSetting = Settings.getValue(SETTINGS_KEY); if (previousSetting === '') { previousSetting = false; isChecked = false; @@ -29,7 +30,7 @@ if (previousSetting === false) { function addAdvancedMovementItemToSettingsMenu() { Menu.addMenuItem({ menuName: "Settings", - menuItemName: "Advanced Movement For Hand Controllers", + menuItemName: MENU_ITEM_NAME, isCheckable: true, isChecked: previousSetting }); @@ -54,16 +55,13 @@ function registerBasicMapping() { var stick = Controller.getValue(Controller.Standard.LS); if (value === 1 && Controller.Hardware.OculusTouch !== undefined) { rotate180(); - } - if (Controller.Hardware.Vive !== undefined) { + } else if (Controller.Hardware.Vive !== undefined) { if (value > 0.75 && inFlipTurn === false) { inFlipTurn = true; rotate180(); Script.setTimeout(function() { inFlipTurn = false; }, TURN_RATE) - } else { - } } return; @@ -73,16 +71,13 @@ function registerBasicMapping() { var stick = Controller.getValue(Controller.Standard.RS); if (value === 1 && Controller.Hardware.OculusTouch !== undefined) { rotate180(); - } - if (Controller.Hardware.Vive !== undefined) { + } else if (Controller.Hardware.Vive !== undefined) { if (value > 0.75 && inFlipTurn === false) { inFlipTurn = true; rotate180(); Script.setTimeout(function() { inFlipTurn = false; }, TURN_RATE) - } else { - } } return; @@ -99,19 +94,19 @@ function disableMappings() { } function scriptEnding() { - Menu.removeMenuItem("Settings", "Advanced Movement For Hand Controllers"); + Menu.removeMenuItem("Settings", MENU_ITEM_NAME); disableMappings(); } function menuItemEvent(menuItem) { - if (menuItem == "Advanced Movement For Hand Controllers") { - isChecked = Menu.isOptionChecked("Advanced Movement For Hand Controllers"); + if (menuItem == MENU_ITEM_NAME) { + isChecked = Menu.isOptionChecked(MENU_ITEM_NAME); if (isChecked === true) { - Settings.setValue('advancedMovementForHandControllersIsChecked', true); + Settings.setValue(SETTINGS_KEY, true); disableMappings(); } else if (isChecked === false) { - Settings.setValue('advancedMovementForHandControllersIsChecked', false); + Settings.setValue(SETTINGS_KEY, false); enableMappings(); } } @@ -131,7 +126,6 @@ Script.setTimeout(function() { } else { enableMappings(); } - }, 100) From 3922192cfc89597a0d2055ff9c980a69a0cce413 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 19 Aug 2016 14:31:47 -0700 Subject: [PATCH 187/332] update --- .../controllers/toggleAdvancedMovementForHandControllers.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 19f28e51e7..6f1f284be2 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -15,7 +15,7 @@ var TURN_RATE = 1000; var MENU_ITEM_NAME = "Advanced Movement For Hand Controllers"; var SETTINGS_KEY = 'advancedMovementForHandControllersIsChecked'; var previousSetting = Settings.getValue(SETTINGS_KEY); -if (previousSetting === '') { +if (previousSetting === '' || previousSetting === false) { previousSetting = false; isChecked = false; } @@ -23,9 +23,6 @@ if (previousSetting === '') { if (previousSetting === true) { isChecked = true; } -if (previousSetting === false) { - isChecked = false; -} function addAdvancedMovementItemToSettingsMenu() { Menu.addMenuItem({ From 1e08914f8d608e790dabcddabccc520bb705d143 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 19 Aug 2016 14:32:15 -0700 Subject: [PATCH 188/332] simplify --- scripts/system/snapshot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index fab26a70e6..9ce3cb366e 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -137,7 +137,7 @@ function resetButtons(path, notify) { confirmShare([ { localPath: path }, { - canShare: Boolean(Window.location.placename), + canShare: Boolean(location.placename), isLoggedIn: true, // Just have the dialog act as though we are. To be removed at both ends later. openFeedAfterShare: shouldOpenFeedAfterShare() } From 2b1e629ccae2f4b875ea0dcbac0be0e47167f42e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 19 Aug 2016 14:51:15 -0700 Subject: [PATCH 189/332] some extra checks on settings values --- .../controllers/toggleAdvancedMovementForHandControllers.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 6f1f284be2..3a75482770 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -15,12 +15,13 @@ var TURN_RATE = 1000; var MENU_ITEM_NAME = "Advanced Movement For Hand Controllers"; var SETTINGS_KEY = 'advancedMovementForHandControllersIsChecked'; var previousSetting = Settings.getValue(SETTINGS_KEY); -if (previousSetting === '' || previousSetting === false) { +if (previousSetting === '' || previousSetting === false || previousSetting === 'false') { previousSetting = false; isChecked = false; } -if (previousSetting === true) { +if (previousSetting === true || previousSetting === 'true') { + previousSetting = true; isChecked = true; } From ff4c194756874829c10c19900f122ce5bebca869 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 19 Aug 2016 15:31:57 -0700 Subject: [PATCH 190/332] eslint fix --- scripts/system/controllers/handControllerGrab.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index cef2c4fdda..9cbdebb93d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1409,12 +1409,11 @@ function MyController(hand) { } } + var pointerEvent; if (rayPickInfo.entityID && Entities.wantsHandControllerPointerEvents(rayPickInfo.entityID)) { entity = rayPickInfo.entityID; name = entityPropertiesCache.getProps(entity).name; - var pointerEvent; - if (Entities.keyboardFocusEntity != entity) { Entities.keyboardFocusEntity = entity; @@ -1464,7 +1463,7 @@ function MyController(hand) { return; } } else if (this.hoverEntity) { - var pinterEvent = { + pointerEvent = { type: "Move", id: this.hand + 1 }; From 3d2afaac77df4addd18d559cdd503b036ec21c0a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 20 Aug 2016 11:19:30 +1200 Subject: [PATCH 191/332] Size and position snapshot dialog content blocks --- scripts/system/html/SnapshotReview.html | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/scripts/system/html/SnapshotReview.html b/scripts/system/html/SnapshotReview.html index 9b9403cf78..8d73e5295f 100644 --- a/scripts/system/html/SnapshotReview.html +++ b/scripts/system/html/SnapshotReview.html @@ -3,6 +3,41 @@ Share + - - + diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css new file mode 100644 index 0000000000..4faa17e7a3 --- /dev/null +++ b/scripts/system/html/css/SnapshotReview.css @@ -0,0 +1,131 @@ +/* +// edit-style.css +// +// Created by Howard Stearns for David Rowe 8/22/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +*/ + + +.snapshot-container { + width: 100%; + padding-top: 3px; +} + +.snapshot-column-left { + width: 320px; + position: absolute; + padding-top: 8px; +} + +.snapshot-column-right { + margin-left: 342px; +} + +.snapshot-column-right > div > img { + width: 100%; +} + +@media (max-width: 768px) { + .snapshot-column-left { + position: initial; + width: 100%; + } + .snapshot-column-right { + margin-left: 0; + width: 100%; + } + .snapshot-column-right > div > img { + margin-top: 18px !important; + } +} + +.snapshot-column-right > div { + position: relative; + padding: 2px; +} + +.snapshot-column-right > div > img { + border: 2px solid #575757; + margin: -2px; +} + +hr { + padding-left: 0; + padding-right: 0; + margin: 21px 0; +} + +.snapsection { + text-align: center; +} + +.title { + text-transform: uppercase; + font-size: 12px; +} + +.prompt { + font-family: Raleway-SemiBold; + font-size: 14px; +} + +div.button { + padding-top: 21px; +} + +.compound-button { + position: relative; + height: auto; +} + +.compound-button input { + padding-left: 40px; +} + +.compound-button .glyph { + display: inline-block; + position: absolute; + left: 12px; + top: 16px; + width: 23px; + height: 23px; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgaGVpZ2h0PSI0MCIKICAgd2lkdGg9IjQwIgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgdmlld0JveD0iMCAwIDQwIDQwIgogICB5PSIwcHgiCiAgIHg9IjBweCIKICAgdmVyc2lvbj0iMS4xIj48bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGEzNCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48ZGM6dGl0bGU+PC9kYzp0aXRsZT48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczMyIiAvPjxzdHlsZQogICAgIGlkPSJzdHlsZTQiCiAgICAgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiM0MTQwNDI7fQoJLnN0MXtmaWxsOiNDQ0NDQ0M7fQoJLnN0MntmaWxsOiMxMzk4QkI7fQoJLnN0M3tmaWxsOiMzMUQ4RkY7fQo8L3N0eWxlPjxnCiAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwtMTEwKSIKICAgICBpZD0iTGF5ZXJfMSI+PGNpcmNsZQogICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MSIKICAgICAgIGlkPSJjaXJjbGUxMyIKICAgICAgIHI9IjQuNDQwMDAwMSIKICAgICAgIGN5PSIxMjYuMTciCiAgICAgICBjeD0iMjAuNTQwMDAxIgogICAgICAgY2xhc3M9InN0MSIgLz48cGF0aAogICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MSIKICAgICAgIGlkPSJwYXRoMTUiCiAgICAgICBkPSJtIDI4Ljg3LDEzOS4yNiBjIDAuMDEsLTAuMDEgMC4wMiwtMC4wMiAwLjAzLC0wLjAzIGwgMCwtMS44NiBjIDAsLTIuNjggLTIuMzMsLTQuNzcgLTUsLTQuNzcgbCAtNi40MiwwIGMgLTIuNjgsMCAtNC44NSwyLjA5IC00Ljg1LDQuNzcgbCAwLDEuODggMTYuMjQsMCB6IgogICAgICAgY2xhc3M9InN0MSIgLz48cGF0aAogICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MSIKICAgICAgIGlkPSJwYXRoMTciCiAgICAgICBkPSJtIDM4LjE3LDEyMy40MiBjIDAsLTMuOTcgLTMuMjIsLTcuMTkgLTcuMTksLTcuMTkgbCAtMjAuMzEsMCBjIC0zLjk3LDAgLTcuMTksMy4yMiAtNy4xOSw3LjE5IGwgMCwxNC4xOCBjIDAsMy45NyAzLjIyLDcuMTkgNy4xOSw3LjE5IGwgMjAuMzEsMCBjIDMuOTcsMCA3LjE5LC0zLjIyIDcuMTksLTcuMTkgbCAwLC0xNC4xOCB6IG0gLTEuNzgsMTQuMjcgYyAwLDMuMDMgLTIuNDYsNS40OSAtNS40OSw1LjQ5IGwgLTIwLjMyLDAgYyAtMy4wMywwIC01LjQ5LC0yLjQ2IC01LjQ5LC01LjQ5IGwgMCwtMTQuMTkgYyAwLC0zLjAzIDIuNDYsLTUuNDkgNS40OSwtNS40OSBsIDIwLjMzLDAgYyAzLjAzLDAgNS40OSwyLjQ2IDUuNDksNS40OSBsIDAsMTQuMTkgeiIKICAgICAgIGNsYXNzPSJzdDEiIC8+PC9nPjxnCiAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwtMTEwKSIKICAgICBpZD0iTGF5ZXJfMiIgLz48L3N2Zz4=); + background-repeat: no-repeat; + background-size: 23px 23px; +} + +.setting { + display: inline-table; + height: 28px; +} + +.setting label { + display: table-cell; + vertical-align: middle; + font-family: Raleway-SemiBold; + font-size: 14px; +} + +.setting + .setting { + margin-left: 18px; +} + +input[type=button].naked { + font-size: 40px; + line-height: 40px; + width: 30px; + padding: 0; + margin: 0 0 -6px 0; + position: relative; + top: -6px; + left: -8px; + background: none; +} + +input[type=button].naked:hover { + color: #00b4ef; + background: none; +} diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js new file mode 100644 index 0000000000..a6515df825 --- /dev/null +++ b/scripts/system/html/js/SnapshotReview.js @@ -0,0 +1,77 @@ +"use strict"; +// +// SnapshotReview.js +// scripts/system/html/js/ +// +// Created by Howard Stearns 8/22/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var paths = [], idCounter = 0, useCheckboxes; +function addImage(data) { + var div = document.createElement("DIV"), + input = document.createElement("INPUT"), + label = document.createElement("LABEL"), + img = document.createElement("IMG"), + id = "p" + idCounter++; + function toggle() { data.share = input.checked; } + img.src = data.localPath; + div.appendChild(img); + data.share = true; + if (useCheckboxes) { // I'd rather use css, but the included stylesheet is quite particular. + // Our stylesheet(?) requires input.id to match label.for. Otherwise input doesn't display the check state. + label.setAttribute('for', id); // cannot do label.for = + input.id = id; + input.type = "checkbox"; + input.checked = true; + input.addEventListener('change', toggle); + div.class = "property checkbox"; + div.appendChild(input); + div.appendChild(label); + } + document.getElementById("snapshot-images").appendChild(div); + paths.push(data); + +} +function handleShareButtons(shareMsg) { + var openFeed = document.getElementById('openFeed'); + openFeed.checked = shareMsg.openFeedAfterShare; + openFeed.onchange = function () { EventBridge.emitWebEvent(openFeed.checked ? 'setOpenFeedTrue' : 'setOpenFeedFalse'); }; + if (!shareMsg.canShare) { + // this means you may or may not be logged in, but can't share + // because you are not in a public place. + document.getElementById("sharing").innerHTML = "

Snapshots can be shared when they're taken in shareable places."; + } +} +window.onload = function () { + // Something like the following will allow testing in a browser. + //addImage({localPath: 'c:/Users/howar/OneDrive/Pictures/hifi-snap-by--on-2016-07-27_12-58-43.jpg'}); + //addImage({localPath: 'http://lorempixel.com/1512/1680'}); + openEventBridge(function () { + // Set up a handler for receiving the data, and tell the .js we are ready to receive it. + EventBridge.scriptEventReceived.connect(function (message) { + // last element of list contains a bool for whether or not we can share stuff + var shareMsg = message.pop(); + handleShareButtons(shareMsg); + + // rest are image paths which we add + useCheckboxes = message.length > 1; + message.forEach(addImage); + }); + EventBridge.emitWebEvent('ready'); + }); + +}; +// beware of bug: Cannot send objects at top level. (Nested in arrays is fine.) +function shareSelected() { + EventBridge.emitWebEvent(paths); +}; +function doNotShare() { + EventBridge.emitWebEvent([]); +}; +function snapshotSettings() { + EventBridge.emitWebEvent("openSettings"); +}; From 8bf87128d98e6cd5c7a0a27e798bbebff34fb8a8 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Mon, 22 Aug 2016 16:04:55 -0700 Subject: [PATCH 218/332] Fixed browser.qml 'onNewViewRequested' --- interface/resources/qml/Browser.qml | 5 +++++ interface/resources/qml/MarketplaceComboBox.qml | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 3693b9dfea..050e10eead 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -217,6 +217,11 @@ ScrollingWindow { } onIconChanged: { console.log("New icon: " + icon) + } + onNewViewRequested: { + var component = Qt.createComponent("Browser.qml"); + var newWindow = component.createObject(desktop); + request.openIn(newWindow.webView) } Component.onCompleted: { desktop.initWebviewProfileHandlers(webview.profile) diff --git a/interface/resources/qml/MarketplaceComboBox.qml b/interface/resources/qml/MarketplaceComboBox.qml index 7a600d7bbe..f7f224485b 100644 --- a/interface/resources/qml/MarketplaceComboBox.qml +++ b/interface/resources/qml/MarketplaceComboBox.qml @@ -66,7 +66,8 @@ Rectangle { element.removeAttr("download");' onLinkHovered: { - desktop.currentUrl = hoveredUrl + desktop.currentUrl = hoveredUrl; + // add an error message for non-fbx files if (File.isZippedFbx(desktop.currentUrl)) { runJavaScript(simpleDownload, function(){console.log("ran the JS");}); } From bc90b0bc43fc37be9d26cc755130fd429859dab7 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 22 Aug 2016 16:09:09 -0700 Subject: [PATCH 219/332] pr review --- interface/resources/qml/AddressBarDialog.qml | 12 ++++++------ interface/resources/qml/hifi/Card.qml | 6 +++++- scripts/system/html/css/SnapshotReview.css | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 7be7c2c83b..f2bdc5ba6d 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -273,9 +273,9 @@ Window { function asyncMap(array, iterator, cb) { // call iterator(element, icb) once for each element of array, and then cb(error, mappedResult) // when icb(error, mappedElement) has been called by each iterator. - // Calls to iterator are overlapped and map call icb in any order, but the mappedResults are collected in the same + // Calls to iterator are overlapped and may call icb in any order, but the mappedResults are collected in the same // order as the elements of the array. - // short-circuits if error. Note that iterator MUST be an asynchronous function. (Use setTimeout if necessary.) + // Short-circuits if error. Note that iterator MUST be an asynchronous function. (Use setTimeout if necessary.) var count = array.length, results = []; if (!count) { return cb(null, results); @@ -287,7 +287,7 @@ Window { iterator(element, function (error, mapped) { results[index] = mapped; if (error || !--count) { - count = 1; // don't cb multiple times if error + count = 0; // don't cb multiple times if error cb(error, results); } }); @@ -397,9 +397,9 @@ Window { // only appending the collected results. var params = [ 'open', // published hours handle now - // FIXME: should determine if place is actually running - 'restriction=open', // Not by whitelist, etc. FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. - // FIXME add maturity + // TBD: should determine if place is actually running? + 'restriction=open', // Not by whitelist, etc. TBD: If logged in, add hifi to the restriction options, in order to include places that require login? + // TBD: add maturity? 'protocol=' + encodeURIComponent(AddressManager.protocolVersion()), 'sort_by=users', 'sort_order=desc', diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index fe162349c1..53829eed9e 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -37,6 +37,7 @@ Rectangle { property int textSizeSmall: 18; property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif"); HifiConstants { id: hifi } + function pastTime(timestamp) { // Answer a descriptive string timestamp = new Date(timestamp); var then = timestamp.getTime(), @@ -77,7 +78,7 @@ Rectangle { anchors.left: parent.left; onStatusChanged: { if (status == Image.Error) { - console.log("source: " + source + ": failed to load " + hfiUrl); + console.log("source: " + source + ": failed to load " + hifiUrl); source = defaultThumbnail; } } @@ -129,6 +130,9 @@ Rectangle { margins: textPadding; } } + // These two can be supplied to provide hover behavior. + // For example, AddressBarDialog provides functions that set the current list view item + // to that which is being hovered over. property var hoverThunk: function () { }; property var unhoverThunk: function () { }; MouseArea { diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index 4faa17e7a3..c2965f92e1 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -1,5 +1,5 @@ /* -// edit-style.css +// SnapshotReview.css // // Created by Howard Stearns for David Rowe 8/22/2016. // Copyright 2016 High Fidelity, Inc. From cbb115bbcc0aa286ed2ba93e7b50235bf1cb937f Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 22 Aug 2016 16:34:23 -0700 Subject: [PATCH 220/332] fix typo argee->agree --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 836b48b3fb..34c3494371 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4941,7 +4941,7 @@ bool Application::askToSetAvatarUrl(const QString& url) { modelLicense = simpleWordWrap(modelLicense, MAX_CHARACTERS_PER_LINE); agreeToLicence = QMessageBox::Yes == OffscreenUi::question("Avatar Usage License", - modelLicense + "\nDo you argee to these terms?", + modelLicense + "\nDo you agree to these terms?", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); } From 4b61cc9f5fabb375cab2ff950fc6a87532a02471 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 22 Aug 2016 17:30:08 -0700 Subject: [PATCH 221/332] new user data editor --- scripts/system/html/css/edit-style.css | 18 +- .../system/html/css/img/jsoneditor-icons.svg | 893 +++++++++++++++++ scripts/system/html/css/jsoneditor.css | 934 ++++++++++++++++++ scripts/system/html/entityProperties.html | 15 +- scripts/system/html/js/entityProperties.js | 473 ++++++--- scripts/system/html/js/jsoneditor.min.js | 49 + 6 files changed, 2241 insertions(+), 141 deletions(-) create mode 100644 scripts/system/html/css/img/jsoneditor-icons.svg create mode 100644 scripts/system/html/css/jsoneditor.css create mode 100644 scripts/system/html/js/jsoneditor.min.js diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 0b58cf22ac..58f8833db3 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -798,7 +798,7 @@ tuple, .blue:focus, .tuple .z:focus, .tuple .roll:focus { } ::-webkit-scrollbar { - width: 10px; + width: 20px; height: 10px; } ::-webkit-scrollbar-track { @@ -1081,3 +1081,19 @@ input#reset-to-natural-dimensions { #animation-fps { margin-top: 48px; } + +#userdata-clear{ + margin-bottom: 10px; +} + + +#static-userdata{ + display: none; + z-index: 99; + position: absolute; + width: 96%; + padding-left:1%; + margin-top:5px; + margin-bottom:10px; + background-color: #2e2e2e; +} \ No newline at end of file diff --git a/scripts/system/html/css/img/jsoneditor-icons.svg b/scripts/system/html/css/img/jsoneditor-icons.svg new file mode 100644 index 0000000000..1b40068aad --- /dev/null +++ b/scripts/system/html/css/img/jsoneditor-icons.svg @@ -0,0 +1,893 @@ + + + JSON Editor Icons + + + + image/svg+xml + + JSON Editor Icons + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/html/css/jsoneditor.css b/scripts/system/html/css/jsoneditor.css new file mode 100644 index 0000000000..aea0696609 --- /dev/null +++ b/scripts/system/html/css/jsoneditor.css @@ -0,0 +1,934 @@ +/* reset styling (prevent conflicts with bootstrap, materialize.css, etc.) */ + +div.jsoneditor input { + height: auto; + border: inherit; +} + +div.jsoneditor input:focus { + border: none !important; + box-shadow: none !important; +} + +div.jsoneditor table { + border-collapse: collapse; + width: auto; +} + +div.jsoneditor td, +div.jsoneditor th { + padding: 0; + display: table-cell; + text-align: left; + vertical-align: inherit; + border-radius: inherit; +} + + +div.jsoneditor-field, +div.jsoneditor-value, +div.jsoneditor-readonly { + border: 1px solid transparent; + min-height: 16px; + min-width: 32px; + padding: 2px; + margin: 1px; + word-wrap: break-word; + float: left; +} + +/* adjust margin of p elements inside editable divs, needed for Opera, IE */ + +div.jsoneditor-field p, +div.jsoneditor-value p { + margin: 0; +} + +div.jsoneditor-value { + word-break: break-word; +} + +div.jsoneditor-readonly { + min-width: 16px; + color: red; +} + +div.jsoneditor-empty { + border-color: lightgray; + border-style: dashed; + border-radius: 2px; +} + +div.jsoneditor-field.jsoneditor-empty::after, +div.jsoneditor-value.jsoneditor-empty::after { + pointer-events: none; + color: lightgray; + font-size: 8pt; +} + +div.jsoneditor-field.jsoneditor-empty::after { + content: "field"; +} + +div.jsoneditor-value.jsoneditor-empty::after { + content: "value"; +} + +div.jsoneditor-value.jsoneditor-url, +a.jsoneditor-value.jsoneditor-url { + color: green; + text-decoration: underline; +} + +a.jsoneditor-value.jsoneditor-url { + display: inline-block; + padding: 2px; + margin: 2px; +} + +a.jsoneditor-value.jsoneditor-url:hover, +a.jsoneditor-value.jsoneditor-url:focus { + color: #ee422e; +} + +div.jsoneditor td.jsoneditor-separator { + padding: 3px 0; + vertical-align: top; + color: gray; +} + +div.jsoneditor-field[contenteditable=true]:focus, +div.jsoneditor-field[contenteditable=true]:hover, +div.jsoneditor-value[contenteditable=true]:focus, +div.jsoneditor-value[contenteditable=true]:hover, +div.jsoneditor-field.jsoneditor-highlight, +div.jsoneditor-value.jsoneditor-highlight { + background-color: #FFFFAB; + border: 1px solid yellow; + border-radius: 2px; +} + +div.jsoneditor-field.jsoneditor-highlight-active, +div.jsoneditor-field.jsoneditor-highlight-active:focus, +div.jsoneditor-field.jsoneditor-highlight-active:hover, +div.jsoneditor-value.jsoneditor-highlight-active, +div.jsoneditor-value.jsoneditor-highlight-active:focus, +div.jsoneditor-value.jsoneditor-highlight-active:hover { + background-color: #ffee00; + border: 1px solid #ffc700; + border-radius: 2px; +} + +div.jsoneditor-value.jsoneditor-string { + color: #008000; +} + +div.jsoneditor-value.jsoneditor-object, +div.jsoneditor-value.jsoneditor-array { + min-width: 16px; + color: #808080; +} + +div.jsoneditor-value.jsoneditor-number { + color: #ee422e; +} + +div.jsoneditor-value.jsoneditor-boolean { + color: #ff8c00; +} + +div.jsoneditor-value.jsoneditor-null { + color: #004ED0; +} + +div.jsoneditor-value.jsoneditor-invalid { + color: #000000; +} + +div.jsoneditor-tree button { + width: 24px; + height: 24px; + padding: 0; + margin: 0; + border: none; + cursor: pointer; + background: transparent url("img/jsoneditor-icons.svg"); +} + +div.jsoneditor-mode-view tr.jsoneditor-expandable td.jsoneditor-tree, +div.jsoneditor-mode-form tr.jsoneditor-expandable td.jsoneditor-tree { + cursor: pointer; +} + +div.jsoneditor-tree button.jsoneditor-collapsed { + background-position: 0 -48px; +} + +div.jsoneditor-tree button.jsoneditor-expanded { + background-position: 0 -72px; +} + +div.jsoneditor-tree button.jsoneditor-contextmenu { + background-position: -48px -72px; +} + +div.jsoneditor-tree button.jsoneditor-contextmenu:hover, +div.jsoneditor-tree button.jsoneditor-contextmenu:focus, +div.jsoneditor-tree button.jsoneditor-contextmenu.jsoneditor-selected, +tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu { + background-position: -48px -48px; +} + +div.jsoneditor-tree *:focus { + outline: none; +} + +div.jsoneditor-tree button:focus { + /* TODO: nice outline for buttons with focus + outline: #97B0F8 solid 2px; + box-shadow: 0 0 8px #97B0F8; + */ + background-color: #f5f5f5; + outline: #e5e5e5 solid 1px; +} + +div.jsoneditor-tree button.jsoneditor-invisible { + visibility: hidden; + background: none; +} + +#userdata-editor{ + height:100%; +} + +div.jsoneditor { + color: #1A1A1A; + border: 1px solid #2e2e2e; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + overflow: hidden; + position: relative; + padding: 0; + line-height: 100%; + min-height: 200px; +} + +div.jsoneditor-tree table.jsoneditor-tree { + border-collapse: collapse; + border-spacing: 0; + width: 100%; + margin: 0; +} + +div.jsoneditor-outer { + width: 100%; + height: 100%; + margin: -35px 0 0 0; + padding: 0 0 0 0; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow-y: auto; +} + +textarea.jsoneditor-text, +.ace-jsoneditor { + min-height: 150px; +} + +div.jsoneditor-tree { + width: 100%; + height: 100%; + position: relative; +} + +textarea.jsoneditor-text { + width: 100%; + height: 100%; + margin: 0; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + outline-width: 0; + border: none; + background-color: white; + resize: none; +} + +tr.jsoneditor-highlight, +tr.jsoneditor-selected { + background-color: #e6e6e6; +} + +tr.jsoneditor-selected button.jsoneditor-dragarea, +tr.jsoneditor-selected button.jsoneditor-contextmenu { + visibility: hidden; +} + +tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea, +tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu { + visibility: visible; +} + +div.jsoneditor-tree button.jsoneditor-dragarea { + background: url("img/jsoneditor-icons.svg") -72px -72px; + cursor: move; +} + +div.jsoneditor-tree button.jsoneditor-dragarea:hover, +div.jsoneditor-tree button.jsoneditor-dragarea:focus, +tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea { + background-position: -72px -48px; +} + +div.jsoneditor tr, +div.jsoneditor th, +div.jsoneditor td { + padding: 0; + margin: 0; + overflow: visible; +} + +div.jsoneditor td { + vertical-align: top; +} + +div.jsoneditor td.jsoneditor-tree { + vertical-align: top; +} + +div.jsoneditor-field, +div.jsoneditor-value, +div.jsoneditor td, +div.jsoneditor th, +div.jsoneditor textarea, +.jsoneditor-schema-error { + font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif; + font-size: 10pt; + color: grey; +} + +/* popover */ + +.jsoneditor-schema-error { + cursor: default; + display: inline-block; + /*font-family: arial, sans-serif;*/ + height: 24px; + line-height: 24px; + position: relative; + text-align: center; + width: 24px; +} + +div.jsoneditor-tree .jsoneditor-schema-error { + width: 24px; + height: 24px; + padding: 0; + margin: 0 4px 0 0; + background: url("img/jsoneditor-icons.svg") -168px -48px; +} + +.jsoneditor-schema-error .jsoneditor-popover { + background-color: #4c4c4c; + border-radius: 3px; + box-shadow: 0 0 5px rgba(0,0,0,0.4); + color: #fff; + display: none; + padding: 7px 10px; + position: absolute; + width: 200px; + z-index: 4; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above { + bottom: 32px; + left: -98px; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below { + top: 32px; + left: -98px; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left { + top: -7px; + right: 32px; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right { + top: -7px; + left: 32px; +} + +.jsoneditor-schema-error .jsoneditor-popover:before { + border-right: 7px solid transparent; + border-left: 7px solid transparent; + content: ''; + display: block; + left: 50%; + margin-left: -7px; + position: absolute; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above:before { + border-top: 7px solid #4c4c4c; + bottom: -7px; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below:before { + border-bottom: 7px solid #4c4c4c; + top: -7px; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left:before { + border-left: 7px solid #4c4c4c; + border-top: 7px solid transparent; + border-bottom: 7px solid transparent; + content: ''; + top: 19px; + right: -14px; + left: inherit; + margin-left: inherit; + margin-top: -7px; + position: absolute; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right:before { + border-right: 7px solid #4c4c4c; + border-top: 7px solid transparent; + border-bottom: 7px solid transparent; + content: ''; + top: 19px; + left: -14px; + margin-left: inherit; + margin-top: -7px; + position: absolute; +} + +.jsoneditor-schema-error:hover .jsoneditor-popover, +.jsoneditor-schema-error:focus .jsoneditor-popover { + display: block; + -webkit-animation: fade-in .3s linear 1, move-up .3s linear 1; + -moz-animation: fade-in .3s linear 1, move-up .3s linear 1; + -ms-animation: fade-in .3s linear 1, move-up .3s linear 1; +} + +@-webkit-keyframes fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@-moz-keyframes fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@-ms-keyframes fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +/*@-webkit-keyframes move-up {*/ + +/*from { bottom: 24px; }*/ + +/*to { bottom: 32px; }*/ + +/*}*/ + +/*@-moz-keyframes move-up {*/ + +/*from { bottom: 24px; }*/ + +/*to { bottom: 32px; }*/ + +/*}*/ + +/*@-ms-keyframes move-up {*/ + +/*from { bottom: 24px; }*/ + +/*to { bottom: 32px; }*/ + +/*}*/ + +/* JSON schema errors displayed at the bottom of the editor in mode text and code */ + +.jsoneditor .jsoneditor-text-errors { + width: 100%; + border-collapse: collapse; + background-color: #ffef8b; + border-top: 1px solid #ffd700; +} + +.jsoneditor .jsoneditor-text-errors td { + padding: 3px 6px; + vertical-align: middle; +} + +.jsoneditor-text-errors .jsoneditor-schema-error { + border: none; + width: 24px; + height: 24px; + padding: 0; + margin: 0 4px 0 0; + background: url("img/jsoneditor-icons.svg") -168px -48px; +} +/* ContextMenu - main menu */ + +div.jsoneditor-contextmenu-root { + position: relative; + width: 0; + height: 0; +} + +div.jsoneditor-contextmenu { + position: absolute; + box-sizing: content-box; + z-index: 99999; +} + +div.jsoneditor-contextmenu ul, +div.jsoneditor-contextmenu li { + box-sizing: content-box; +} + +div.jsoneditor-contextmenu ul { + position: relative; + left: 0; + top: 0; + width: 124px; + background: white; + border: 1px solid #d3d3d3; + box-shadow: 2px 2px 12px rgba(128, 128, 128, 0.3); + list-style: none; + margin: 0; + padding: 0; +} + +div.jsoneditor-contextmenu ul li button { + padding: 0; + margin: 0; + width: 124px; + height: 24px; + border: none; + cursor: pointer; + color: #4d4d4d; + background: transparent; + font-size: 10pt; + font-family: arial, sans-serif; + box-sizing: border-box; + line-height: 26px; + text-align: left; +} + +/* Fix button padding in firefox */ + +div.jsoneditor-contextmenu ul li button::-moz-focus-inner { + padding: 0; + border: 0; +} + +div.jsoneditor-contextmenu ul li button:hover, +div.jsoneditor-contextmenu ul li button:focus { + color: #1a1a1a; + background-color: #f5f5f5; + outline: none; +} + +div.jsoneditor-contextmenu ul li button.jsoneditor-default { + width: 92px; +} + +div.jsoneditor-contextmenu ul li button.jsoneditor-expand { + float: right; + width: 32px; + height: 24px; + border-left: 1px solid #e5e5e5; +} + +div.jsoneditor-contextmenu div.jsoneditor-icon { + float: left; + width: 24px; + height: 24px; + border: none; + padding: 0; + margin: 0; + background-image: url("img/jsoneditor-icons.svg"); +} + +div.jsoneditor-contextmenu ul li button div.jsoneditor-expand { + float: right; + width: 24px; + height: 24px; + padding: 0; + margin: 0 4px 0 0; + background: url("img/jsoneditor-icons.svg") 0 -72px; + opacity: 0.4; +} + +div.jsoneditor-contextmenu ul li button:hover div.jsoneditor-expand, +div.jsoneditor-contextmenu ul li button:focus div.jsoneditor-expand, +div.jsoneditor-contextmenu ul li.jsoneditor-selected div.jsoneditor-expand, +div.jsoneditor-contextmenu ul li button.jsoneditor-expand:hover div.jsoneditor-expand, +div.jsoneditor-contextmenu ul li button.jsoneditor-expand:focus div.jsoneditor-expand { + opacity: 1; +} + +div.jsoneditor-contextmenu div.jsoneditor-separator { + height: 0; + border-top: 1px solid #e5e5e5; + padding-top: 5px; + margin-top: 5px; +} + +div.jsoneditor-contextmenu button.jsoneditor-remove > div.jsoneditor-icon { + background-position: -24px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-remove:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-remove:focus > div.jsoneditor-icon { + background-position: -24px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-append > div.jsoneditor-icon { + background-position: 0 -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-append:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-append:focus > div.jsoneditor-icon { + background-position: 0 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-insert > div.jsoneditor-icon { + background-position: 0 -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-insert:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-insert:focus > div.jsoneditor-icon { + background-position: 0 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-duplicate > div.jsoneditor-icon { + background-position: -48px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-duplicate:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-duplicate:focus > div.jsoneditor-icon { + background-position: -48px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-sort-asc > div.jsoneditor-icon { + background-position: -168px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-sort-asc:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-sort-asc:focus > div.jsoneditor-icon { + background-position: -168px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-sort-desc > div.jsoneditor-icon { + background-position: -192px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-sort-desc:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-sort-desc:focus > div.jsoneditor-icon { + background-position: -192px 0; +} + +/* ContextMenu - sub menu */ + +div.jsoneditor-contextmenu ul li button.jsoneditor-selected, +div.jsoneditor-contextmenu ul li button.jsoneditor-selected:hover, +div.jsoneditor-contextmenu ul li button.jsoneditor-selected:focus { + color: white; + background-color: #ee422e; +} + +div.jsoneditor-contextmenu ul li { + overflow: hidden; +} + +div.jsoneditor-contextmenu ul li ul { + display: none; + position: relative; + left: -10px; + top: 0; + border: none; + box-shadow: inset 0 0 10px rgba(128, 128, 128, 0.5); + padding: 0 10px; + /* TODO: transition is not supported on IE8-9 */ + -webkit-transition: all 0.3s ease-out; + -moz-transition: all 0.3s ease-out; + -o-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + + + +div.jsoneditor-contextmenu ul li ul li button { + padding-left: 24px; + animation: all ease-in-out 1s; +} + +div.jsoneditor-contextmenu ul li ul li button:hover, +div.jsoneditor-contextmenu ul li ul li button:focus { + background-color: #f5f5f5; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-string > div.jsoneditor-icon { + background-position: -144px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-string:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-string:focus > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-string.jsoneditor-selected > div.jsoneditor-icon { + background-position: -144px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-auto > div.jsoneditor-icon { + background-position: -120px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-auto:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-auto:focus > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-auto.jsoneditor-selected > div.jsoneditor-icon { + background-position: -120px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-object > div.jsoneditor-icon { + background-position: -72px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-object:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-object:focus > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-object.jsoneditor-selected > div.jsoneditor-icon { + background-position: -72px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-array > div.jsoneditor-icon { + background-position: -96px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-array:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-array:focus > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-array.jsoneditor-selected > div.jsoneditor-icon { + background-position: -96px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-modes > div.jsoneditor-icon { + background-image: none; + width: 6px; +} +div.jsoneditor-menu { + width: 100%; + height: 35px; + padding: 2px; + margin: 0; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: white; + background-color: #2e2e2e; + border-bottom: 1px solid #2e2e2e; +} + +div.jsoneditor-menu > button, +div.jsoneditor-menu > div.jsoneditor-modes > button { + width: 26px; + height: 26px; + margin: 2px; + padding: 0; + border-radius: 2px; + border: 1px solid transparent; + background: transparent url("img/jsoneditor-icons.svg"); + color: white; + opacity: 0.8; + font-family: arial, sans-serif; + font-size: 10pt; + float: left; +} + +div.jsoneditor-menu > button:hover, +div.jsoneditor-menu > div.jsoneditor-modes > button:hover { + background-color: rgba(255,255,255,0.2); + border: 1px solid rgba(255,255,255,0.4); +} + +div.jsoneditor-menu > button:focus, +div.jsoneditor-menu > button:active, +div.jsoneditor-menu > div.jsoneditor-modes > button:focus, +div.jsoneditor-menu > div.jsoneditor-modes > button:active { + background-color: rgba(255,255,255,0.3); +} + +div.jsoneditor-menu > button:disabled, +div.jsoneditor-menu > div.jsoneditor-modes > button:disabled { + opacity: 0.5; +} + +div.jsoneditor-menu > button.jsoneditor-collapse-all { + background-position: 0 -96px; +} + +div.jsoneditor-menu > button.jsoneditor-expand-all { + background-position: 0 -120px; +} + +div.jsoneditor-menu > button.jsoneditor-undo { + background-position: -24px -96px; +} + +div.jsoneditor-menu > button.jsoneditor-undo:disabled { + background-position: -24px -120px; +} + +div.jsoneditor-menu > button.jsoneditor-redo { + background-position: -48px -96px; +} + +div.jsoneditor-menu > button.jsoneditor-redo:disabled { + background-position: -48px -120px; +} + +div.jsoneditor-menu > button.jsoneditor-compact { + background-position: -72px -96px; +} + +div.jsoneditor-menu > button.jsoneditor-format { + background-position: -72px -120px; +} + +div.jsoneditor-menu > div.jsoneditor-modes { + display: inline-block; + float: left; +} + +div.jsoneditor-menu > div.jsoneditor-modes > button { + background-image: none; + width: auto; + padding-left: 6px; + padding-right: 6px; +} + +div.jsoneditor-menu > button.jsoneditor-separator, +div.jsoneditor-menu > div.jsoneditor-modes > button.jsoneditor-separator { + margin-left: 10px; +} + +div.jsoneditor-menu a { + font-family: arial, sans-serif; + font-size: 10pt; + color: white; + opacity: 0.8; + vertical-align: middle; +} + +div.jsoneditor-menu a:hover { + opacity: 1; +} + +div.jsoneditor-menu a.jsoneditor-poweredBy { + font-size: 8pt; + position: absolute; + right: 0; + top: 0; + padding: 10px; +} +table.jsoneditor-search input, +table.jsoneditor-search div.jsoneditor-results { + font-family: arial, sans-serif; + font-size: 10pt; + color: #1A1A1A; + background: transparent; + /* For Firefox */ +} + +table.jsoneditor-search div.jsoneditor-results { + color: white; + padding-right: 5px; + line-height: 24px; +} + +table.jsoneditor-search { + position: absolute; + right: 4px; + top: 4px; + border-collapse: collapse; + border-spacing: 0; +} + +table.jsoneditor-search div.jsoneditor-frame { + border: 1px solid transparent; + background-color: white; + padding: 0 2px; + margin: 0; +} + +table.jsoneditor-search div.jsoneditor-frame table { + border-collapse: collapse; +} + +table.jsoneditor-search input { + width: 120px; + border: none; + outline: none; + margin: 1px; + line-height: 20px; +} + +table.jsoneditor-search button { + width: 16px; + height: 24px; + padding: 0; + margin: 0; + border: none; + background: url("img/jsoneditor-icons.svg"); + vertical-align: top; +} + +table.jsoneditor-search button:hover { + background-color: transparent; +} + +table.jsoneditor-search button.jsoneditor-refresh { + width: 18px; + background-position: -99px -73px; +} + +table.jsoneditor-search button.jsoneditor-next { + cursor: pointer; + background-position: -124px -73px; +} + +table.jsoneditor-search button.jsoneditor-next:hover { + background-position: -124px -49px; +} + +table.jsoneditor-search button.jsoneditor-previous { + cursor: pointer; + background-position: -148px -73px; + margin-right: 2px; +} + +table.jsoneditor-search button.jsoneditor-previous:hover { + background-position: -148px -49px; +} \ No newline at end of file diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 01826b8e10..f5e7ffea63 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -11,14 +11,18 @@ Properties + + + +

@@ -58,9 +62,18 @@
+
- +
+
+ + + +
+
+
+
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 5898f85e90..bf477a3ed5 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -28,7 +28,7 @@ var colorPickers = []; debugPrint = function(message) { EventBridge.emitWebEvent( JSON.stringify({ - type:"print", + type: "print", message: message }) ); @@ -40,6 +40,7 @@ function enableChildren(el, selector) { els[i].removeAttribute('disabled'); } } + function disableChildren(el, selector) { els = el.querySelectorAll(selector); for (var i = 0; i < els.length; i++) { @@ -50,14 +51,26 @@ function disableChildren(el, selector) { function enableProperties() { enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); enableChildren(document, ".colpick"); + var elLocked = document.getElementById("property-locked"); + + if (elLocked.checked === false) { + removeStaticUserData(); + } } + function disableProperties() { disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); disableChildren(document, ".colpick"); for (var i = 0; i < colorPickers.length; i++) { colorPickers[i].colpickHide(); } + var elLocked = document.getElementById("property-locked"); + + if ($('#userdata-editor').css('display') === "block" && elLocked.checked === true) { + showStaticUserData(); + } + } function showElements(els, show) { @@ -85,7 +98,7 @@ function createEmitCheckedToStringPropertyUpdateFunction(checkboxElement, name, } function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) { - return function () { + return function() { var properties = {}; properties[group] = {}; properties[group][propertyName] = this.checked; @@ -108,6 +121,7 @@ function createEmitNumberPropertyUpdateFunction(propertyName, decimals) { ); }; } + function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) { return function() { var properties = {}; @@ -127,6 +141,7 @@ function createEmitTextPropertyUpdateFunction(propertyName) { return function() { var properties = {}; properties[propertyName] = this.value; + console.log('properties at set thingo yo') EventBridge.emitWebEvent( JSON.stringify({ type: "update", @@ -136,7 +151,7 @@ function createEmitTextPropertyUpdateFunction(propertyName) { }; } -function createEmitGroupTextPropertyUpdateFunction(group,propertyName) { +function createEmitGroupTextPropertyUpdateFunction(group, propertyName) { return function() { var properties = {}; properties[group] = {}; @@ -153,14 +168,13 @@ function createEmitGroupTextPropertyUpdateFunction(group,propertyName) { function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) { return function() { var data = { - type: "update", - properties: { - } + type: "update", + properties: {} }; data.properties[property] = { - x: elX.value, - y: elY.value, - z: elZ.value, + x: elX.value, + y: elY.value, + z: elZ.value, }; EventBridge.emitWebEvent(JSON.stringify(data)); } @@ -169,15 +183,14 @@ function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) { function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) { return function() { var data = { - type: "update", - properties: { - } + type: "update", + properties: {} }; - data.properties[group] = { }; + data.properties[group] = {}; data.properties[group][property] = { - x: elX.value, - y: elY.value, - z: elZ ? elZ.value : 0, + x: elX.value, + y: elY.value, + z: elZ ? elZ.value : 0, }; EventBridge.emitWebEvent(JSON.stringify(data)); } @@ -186,14 +199,13 @@ function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, el function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) { return function() { var data = { - type: "update", - properties: { - } + type: "update", + properties: {} }; data.properties[property] = { - x: elX.value * multiplier, - y: elY.value * multiplier, - z: elZ.value * multiplier, + x: elX.value * multiplier, + y: elY.value * multiplier, + z: elZ.value * multiplier, }; EventBridge.emitWebEvent(JSON.stringify(data)); } @@ -206,42 +218,40 @@ function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) }; function emitColorPropertyUpdate(property, red, green, blue, group) { - var data = { - type: "update", - properties: { - } + var data = { + type: "update", + properties: {} + }; + if (group) { + data.properties[group] = {}; + data.properties[group][property] = { + red: red, + green: green, + blue: blue, }; - if (group) { - data.properties[group] = { }; - data.properties[group][property] = { - red: red, - green: green, - blue: blue, - }; - } else { - data.properties[property] = { - red: red, - green: green, - blue: blue, - }; - } - EventBridge.emitWebEvent(JSON.stringify(data)); + } else { + data.properties[property] = { + red: red, + green: green, + blue: blue, + }; + } + EventBridge.emitWebEvent(JSON.stringify(data)); }; function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) { return function() { var data = { - type: "update", - properties: { - } + type: "update", + properties: {} }; - data.properties[group] = { }; + data.properties[group] = {}; data.properties[group][property] = { - red: elRed.value, - green: elGreen.value, - blue: elBlue.value, + red: elRed.value, + green: elGreen.value, + blue: elBlue.value, }; EventBridge.emitWebEvent(JSON.stringify(data)); } @@ -251,30 +261,65 @@ function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElemen if (subPropertyElement.checked) { if (propertyValue.indexOf(subPropertyString)) { propertyValue += subPropertyString + ','; - } + } } else { // We've unchecked, so remove propertyValue = propertyValue.replace(subPropertyString + ",", ""); } - var _properties ={} + var _properties = {} _properties[propertyName] = propertyValue; - + EventBridge.emitWebEvent( - JSON.stringify({ - type: "update", - properties: _properties - }) + JSON.stringify({ + type: "update", + properties: _properties + }) ); } +function setUserDataFromEditor() { + var json = null; + try { + json = editor.get(); + console.log('its good json') + } catch (e) { + console.log('failed to parse json', e) + alert('Invalid JSON code - look for red X in your code ', +e) + } + if (json === null) { + return; + } else { + var text = editor.getText() + console.log('editor text at send:', text) + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: { + userData: text + }, + }) + ); + } + + +} + + function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, defaultValue) { var properties = {}; var parsedData = {}; try { - parsedData = JSON.parse(userDataElement.value); - } catch(e) {} + if ($('#userdata-editor').css('height') === "0px") { + console.log('GET JSON FROM EDITOR') + parsedData = editor.getJSON(); + } else { + console.log('GET JSON FROM TEXT AREA'); + parsedData = JSON.parse(userDataElement.value); + + } + } catch (e) {} if (!(groupName in parsedData)) { parsedData[groupName] = {} @@ -306,9 +351,95 @@ function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, d function setTextareaScrolling(element) { var isScrolling = element.scrollHeight > element.offsetHeight; element.setAttribute("scrolling", isScrolling ? "true" : "false"); +}; + +var editor = null; + +function createJSONEditor() { + var container = document.getElementById("userdata-editor"); + var options = { + search: false, + mode: 'tree', + modes: ['code', 'tree'], + name: 'userData', + onModeChange: function() { + $('.jsoneditor-poweredBy').remove(); + }, + onError: function(e) { + alert('JSON editor:' + e) + }, + onChange: function() { + console.log('editor did change') + } + }; + editor = new JSONEditor(container, options); +}; + +function hideNewJSONEditorButton() { + $('#userdata-new-editor').hide(); + +}; + +function hideClearUserDataButton() { + $('#userdata-clear').hide(); +}; + +function showSaveUserDataButton() { + $('#userdata-save').show(); } -function loaded() { +function hideSaveUserDataButton() { + $('#userdata-save').hide(); + +} + +function showNewJSONEditorButton() { + $('#userdata-new-editor').show(); + +}; + +function showClearUserDataButton() { + $('#userdata-clear').show(); + +}; + +function showUserDataTextArea() { + $('#property-user-data').show(); +}; + +function hideUserDataTextArea() { + $('#property-user-data').hide(); +}; + +function showStaticUserData() { + console.log('showing static userdata') + $('#static-userdata').show(); + $('#static-userdata').css('height', $('#userdata-editor').height()) + $('#static-userdata').text(editor.getText()); +}; + +function removeStaticUserData() { + console.log('hiding static userdata') + $('#static-userdata').hide(); +}; + +function setEditorJSON(json) { + return editor.set(json) +}; + +function getEditorJSON() { + return editor.get(); +}; + +function deleteJSONEditor() { + console.log('should delete editor') + if (editor !== null) { + console.log('has one so do it') + editor.destroy(); + } +}; + +function loaded() { openEventBridge(function() { var allSections = []; var elID = document.getElementById("property-id"); @@ -364,7 +495,7 @@ function loaded() { var elDensity = document.getElementById("property-density"); var elCollisionless = document.getElementById("property-collisionless"); - var elDynamic = document.getElementById("property-dynamic" ); + var elDynamic = document.getElementById("property-dynamic"); var elCollideStatic = document.getElementById("property-collide-static"); var elCollideDynamic = document.getElementById("property-collide-dynamic"); var elCollideKinematic = document.getElementById("property-collide-kinematic"); @@ -384,7 +515,10 @@ function loaded() { */ var elReloadScriptButton = document.getElementById("reload-script-button"); var elUserData = document.getElementById("property-user-data"); - + var elClearUserData = document.getElementById("userdata-clear"); + var elSaveUserData = document.getElementById("userdata-save"); + var elJSONEditor = document.getElementById("userdata-editor"); + var elNewJSONEditor = document.getElementById('userdata-new-editor'); var elColorSections = document.querySelectorAll(".color-section"); var elColor = document.getElementById("property-color"); var elColorRed = document.getElementById("property-color-red"); @@ -394,7 +528,7 @@ function loaded() { var elShapeSections = document.querySelectorAll(".shape-section"); allSections.push(elShapeSections); var elShape = document.getElementById("property-shape"); - + var elLightSections = document.querySelectorAll(".light-section"); allSections.push(elLightSections); var elLightSpotLight = document.getElementById("property-light-spot-light"); @@ -431,9 +565,9 @@ function loaded() { var elDescription = document.getElementById("property-description"); var elHyperlinkHref = document.getElementById("property-hyperlink-href"); - + var elHyperlinkSections = document.querySelectorAll(".hyperlink-section"); - + var elTextSections = document.querySelectorAll(".text-section"); allSections.push(elTextSections); @@ -533,7 +667,7 @@ function loaded() { disableProperties(); } else { - + properties = data.selections[0].properties; @@ -545,12 +679,6 @@ function loaded() { elLocked.checked = properties.locked; - if (properties.locked) { - disableProperties(); - elLocked.removeAttribute('disabled'); - } else { - enableProperties(); - } elName.value = properties.name; @@ -612,7 +740,7 @@ function loaded() { var parsedUserData = {} try { parsedUserData = JSON.parse(properties.userData); - + if ("grabbableKey" in parsedUserData) { if ("grabbable" in parsedUserData["grabbableKey"]) { elGrabbable.checked = parsedUserData["grabbableKey"].grabbable; @@ -624,7 +752,7 @@ function loaded() { elIgnoreIK.checked = parsedUserData["grabbableKey"].ignoreIK; } } - } catch(e) {} + } catch (e) {} elCollisionSoundURL.value = properties.collisionSoundURL; elLifetime.value = properties.lifetime; @@ -633,8 +761,29 @@ function loaded() { FIXME: See FIXME for property-script-url. elScriptTimestamp.value = properties.scriptTimestamp; */ - elUserData.value = properties.userData; - setTextareaScrolling(elUserData); + deleteJSONEditor(); + hideUserDataTextArea(); + var json = null; + try { + json = JSON.parse(properties.userData) + //its json + if (Object.keys(json).length === 0 && json.constructor === Object) { + //it's an empty object + console.log('empty object') + } + createJSONEditor(); + setEditorJSON(json) + showSaveUserDataButton(); + hideNewJSONEditorButton(); + console.log('did parse json successfully') + + } catch (e) { + console.log('error parsing json') + elUserData.value = properties.userData; + showUserDataTextArea(); + hideSaveUserDataButton(); + //normal text + } elHyperlinkHref.value = properties.href; elDescription.value = properties.description; @@ -643,7 +792,7 @@ function loaded() { for (var j = 0; j < allSections[i].length; j++) { allSections[i][j].style.display = 'none'; } - } + } for (var i = 0; i < elHyperlinkSections.length; i++) { elHyperlinkSections[i].style.display = 'table'; @@ -661,7 +810,7 @@ function loaded() { elShapeSections[i].style.display = 'none'; } } - + if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") { for (var i = 0; i < elColorSections.length; i++) { elColorSections[i].style.display = 'table'; @@ -669,7 +818,7 @@ function loaded() { elColorRed.value = properties.color.red; elColorGreen.value = properties.color.green; elColorBlue.value = properties.color.blue; - elColor.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; + elColor.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; } else { for (var i = 0; i < elColorSections.length; i++) { elColorSections[i].style.display = 'none'; @@ -714,7 +863,7 @@ function loaded() { elTextText.value = properties.text; elTextLineHeight.value = properties.lineHeight.toFixed(4); elTextFaceCamera = properties.faceCamera; - elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red + "," + properties.textColor.green + "," + properties.textColor.blue + ")"; + elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red + "," + properties.textColor.green + "," + properties.textColor.blue + ")"; elTextTextColorRed.value = properties.textColor.red; elTextTextColorGreen.value = properties.textColor.green; elTextTextColorBlue.value = properties.textColor.blue; @@ -728,7 +877,7 @@ function loaded() { elLightSpotLight.checked = properties.isSpotlight; - elLightColor.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; + elLightColor.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; elLightColorRed.value = properties.color.red; elLightColorGreen.value = properties.color.green; elLightColorBlue.value = properties.color.blue; @@ -743,7 +892,7 @@ function loaded() { } elZoneStageSunModelEnabled.checked = properties.stage.sunModelEnabled; - elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")"; + elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")"; elZoneKeyLightColorRed.value = properties.keyLight.color.red; elZoneKeyLightColorGreen.value = properties.keyLight.color.green; elZoneKeyLightColorBlue.value = properties.keyLight.color.blue; @@ -766,7 +915,7 @@ function loaded() { elZoneBackgroundMode.value = properties.backgroundMode; setDropdownText(elZoneBackgroundMode); - elZoneSkyboxColor.style.backgroundColor = "rgb(" + properties.skybox.color.red + "," + properties.skybox.color.green + "," + properties.skybox.color.blue + ")"; + elZoneSkyboxColor.style.backgroundColor = "rgb(" + properties.skybox.color.red + "," + properties.skybox.color.green + "," + properties.skybox.color.blue + ")"; elZoneSkyboxColorRed.value = properties.skybox.color.red; elZoneSkyboxColorGreen.value = properties.skybox.color.green; elZoneSkyboxColorBlue.value = properties.skybox.color.blue; @@ -790,10 +939,17 @@ function loaded() { elYTextureURL.value = properties.yTextureURL; elZTextureURL.value = properties.zTextureURL; } - + + if (properties.locked) { + disableProperties(); + elLocked.removeAttribute('disabled'); + } else { + enableProperties(); + } + var activeElement = document.activeElement; - - if(typeof activeElement.select!=="undefined"){ + + if (typeof activeElement.select !== "undefined") { activeElement.select(); } } @@ -903,8 +1059,48 @@ function loaded() { FIXME: See FIXME for property-script-url. elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp')); */ + + + elClearUserData.addEventListener("click", function() { + deleteJSONEditor(); + console.log('CLEAR USER DATA WAS CLICKED w') + elUserData.value = ""; + showUserDataTextArea(); + console.log('CLEAR USER DATA WAS CLICKED x') + showNewJSONEditorButton(); + console.log('CLEAR USER DATA WAS CLICKED y') + hideSaveUserDataButton(); + console.log('CLEAR USER DATA WAS CLICKED z') + var properties = {}; + properties['userData'] = elUserData.value; + console.log('properties at set thingo yo', elUserData.value) + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: properties, + }) + ); + + + }); + + elSaveUserData.addEventListener("click", function() { + console.log('click on save json button') + setUserDataFromEditor(); + }); + elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); + elNewJSONEditor.addEventListener('click', function() { + deleteJSONEditor(); + createJSONEditor(); + var data = {}; + setEditorJSON(data); + hideUserDataTextArea(); + hideNewJSONEditorButton(); + showSaveUserDataButton(); + }); + var colorChangeFunction = createEmitColorPropertyUpdateFunction( 'color', elColorRed, elColorGreen, elColorBlue); elColorRed.addEventListener('change', colorChangeFunction); @@ -914,13 +1110,13 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', - onShow: function (colpick) { + onShow: function(colpick) { $('#property-color').attr('active', 'true'); }, - onHide: function (colpick) { + onHide: function(colpick) { $('#property-color').attr('active', 'false'); }, - onSubmit: function (hsb, hex, rgb, el) { + onSubmit: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); $(el).colpickHide(); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); @@ -938,13 +1134,13 @@ function loaded() { colorScheme: 'dark', layout: 'hex', color: '000000', - onShow: function (colpick) { + onShow: function(colpick) { $('#property-light-color').attr('active', 'true'); }, - onHide: function (colpick) { + onHide: function(colpick) { $('#property-light-color').attr('active', 'false'); }, - onSubmit: function (hsb, hex, rgb, el) { + onSubmit: function(hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); $(el).colpickHide(); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); @@ -965,8 +1161,8 @@ function loaded() { elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL')); elModelAnimationURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('animation', 'url')); - elModelAnimationPlaying.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation','running')); - elModelAnimationFPS.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation','fps')); + elModelAnimationPlaying.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'running')); + elModelAnimationFPS.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'fps')); elModelAnimationFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'currentFrame')); elModelAnimationFirstFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'firstFrame')); elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame')); @@ -984,17 +1180,17 @@ function loaded() { elTextTextColorGreen.addEventListener('change', textTextColorChangeFunction); elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction); colorPickers.push($('#property-text-text-color').colpick({ - colorScheme:'dark', - layout:'hex', + colorScheme: 'dark', + layout: 'hex', color: '000000', - onShow: function (colpick) { + onShow: function(colpick) { $('#property-text-text-color').attr('active', 'true'); }, - onHide: function (colpick) { + onHide: function(colpick) { $('#property-text-text-color').attr('active', 'false'); }, - onSubmit: function (hsb, hex, rgb, el) { - $(el).css('background-color', '#'+hex); + onSubmit: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); $(el).colpickHide(); $(el).attr('active', 'false'); emitColorPropertyUpdate('textColor', rgb.r, rgb.g, rgb.b); @@ -1007,82 +1203,82 @@ function loaded() { elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction); elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction); colorPickers.push($('#property-text-background-color').colpick({ - colorScheme:'dark', - layout:'hex', - color:'000000', - onShow: function (colpick) { + colorScheme: 'dark', + layout: 'hex', + color: '000000', + onShow: function(colpick) { $('#property-text-background-color').attr('active', 'true'); }, - onHide: function (colpick) { + onHide: function(colpick) { $('#property-text-background-color').attr('active', 'false'); }, - onSubmit: function (hsb, hex, rgb, el) { - $(el).css('background-color', '#'+hex); + onSubmit: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); $(el).colpickHide(); emitColorPropertyUpdate('backgroundColor', rgb.r, rgb.g, rgb.b); } })); - elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','sunModelEnabled')); + elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage', 'sunModelEnabled')); colorPickers.push($('#property-zone-key-light-color').colpick({ - colorScheme:'dark', - layout:'hex', - color:'000000', - onShow: function (colpick) { + colorScheme: 'dark', + layout: 'hex', + color: '000000', + onShow: function(colpick) { $('#property-zone-key-light-color').attr('active', 'true'); }, - onHide: function (colpick) { + onHide: function(colpick) { $('#property-zone-key-light-color').attr('active', 'false'); }, - onSubmit: function (hsb, hex, rgb, el) { - $(el).css('background-color', '#'+hex); + onSubmit: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); $(el).colpickHide(); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight'); } })); - var zoneKeyLightColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('keyLight','color', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue); + var zoneKeyLightColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('keyLight', 'color', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue); elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction); elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction); elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction); - elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','intensity')); - elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity')); - elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight','ambientURL')); - var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY); + elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight', 'intensity')); + elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight', 'ambientIntensity')); + elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight', 'ambientURL')); + var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight', 'direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY); elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction); elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction); - elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','latitude')); - elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','longitude')); - elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','altitude')); - elZoneStageAutomaticHourDay.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','automaticHourDay')); - elZoneStageDay.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','day')); - elZoneStageHour.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','hour')); + elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'latitude')); + elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'longitude')); + elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'altitude')); + elZoneStageAutomaticHourDay.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage', 'automaticHourDay')); + elZoneStageDay.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'day')); + elZoneStageHour.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'hour')); elZoneBackgroundMode.addEventListener('change', createEmitTextPropertyUpdateFunction('backgroundMode')); - var zoneSkyboxColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('skybox','color', + var zoneSkyboxColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('skybox', 'color', elZoneSkyboxColorRed, elZoneSkyboxColorGreen, elZoneSkyboxColorBlue); elZoneSkyboxColorRed.addEventListener('change', zoneSkyboxColorChangeFunction); elZoneSkyboxColorGreen.addEventListener('change', zoneSkyboxColorChangeFunction); elZoneSkyboxColorBlue.addEventListener('change', zoneSkyboxColorChangeFunction); colorPickers.push($('#property-zone-skybox-color').colpick({ - colorScheme:'dark', - layout:'hex', - color:'000000', - onShow: function (colpick) { + colorScheme: 'dark', + layout: 'hex', + color: '000000', + onShow: function(colpick) { $('#property-zone-skybox-color').attr('active', 'true'); }, - onHide: function (colpick) { + onHide: function(colpick) { $('#property-zone-skybox-color').attr('active', 'false'); }, - onSubmit: function (hsb, hex, rgb, el) { - $(el).css('background-color', '#'+hex); + onSubmit: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); $(el).colpickHide(); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'skybox'); } })); - elZoneSkyboxURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('skybox','url')); + elZoneSkyboxURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('skybox', 'url')); elZoneFlyingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('flyingAllowed')); elZoneGhostingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('ghostingAllowed')); @@ -1169,7 +1365,7 @@ function loaded() { // Collapsible sections var elCollapsible = document.getElementsByClassName("section-header"); - var toggleCollapsedEvent = function (event) { + var toggleCollapsedEvent = function(event) { var element = event.target; if (element.nodeName !== "DIV") { element = element.parentNode; @@ -1188,7 +1384,7 @@ function loaded() { // Textarea scrollbars var elTextareas = document.getElementsByTagName("TEXTAREA"); - var textareaOnChangeEvent = function (event) { + var textareaOnChangeEvent = function(event) { setTextareaScrolling(event.target); } @@ -1274,7 +1470,7 @@ function loaded() { dt.appendChild(span); var span = document.createElement("span"); - span.textContent = "5"; // caratDn + span.textContent = "5"; // caratDn dt.appendChild(span); var dd = document.createElement("dd"); @@ -1302,8 +1498,7 @@ function loaded() { augmentSpinButtons(); // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked - document.addEventListener("contextmenu", function (event) { + document.addEventListener("contextmenu", function(event) { event.preventDefault(); }, false); -} - +} \ No newline at end of file diff --git a/scripts/system/html/js/jsoneditor.min.js b/scripts/system/html/js/jsoneditor.min.js new file mode 100644 index 0000000000..12e8a07378 --- /dev/null +++ b/scripts/system/html/js/jsoneditor.min.js @@ -0,0 +1,49 @@ +/*! + * jsoneditor.js + * + * @brief + * JSONEditor is a web-based tool to view, edit, format, and validate JSON. + * It has various modes such as a tree editor, a code editor, and a plain text + * editor. + * + * Supported browsers: Chrome, Firefox, Safari, Opera, Internet Explorer 8+ + * + * @license + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * Copyright (c) 2011-2016 Jos de Jong, http://jsoneditoronline.org + * + * @author Jos de Jong, + * @version 5.5.7 + * @date 2016-08-17 + */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.JSONEditor=t():e.JSONEditor=t()}(this,function(){return function(e){function t(n){if(i[n])return i[n].exports;var r=i[n]={exports:{},id:n,loaded:!1};return e[n].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var i={};return t.m=e,t.c=i,t.p="",t(0)}([function(e,t,i){"use strict";function n(e,t,i){if(!(this instanceof n))throw new Error('JSONEditor constructor called without "new".');var r=l.getInternetExplorerVersion();if(-1!=r&&9>r)throw new Error("Unsupported browser, IE9 or newer required. Please install the newest version of your browser.");if(t&&(t.error&&(console.warn('Option "error" has been renamed to "onError"'),t.onError=t.error,delete t.error),t.change&&(console.warn('Option "change" has been renamed to "onChange"'),t.onChange=t.change,delete t.change),t.editable&&(console.warn('Option "editable" has been renamed to "onEditable"'),t.onEditable=t.editable,delete t.editable),t)){var o=["ace","theme","ajv","schema","onChange","onEditable","onError","onModeChange","escapeUnicode","history","search","mode","modes","name","indentation","sortObjectKeys"];Object.keys(t).forEach(function(e){-1===o.indexOf(e)&&console.warn('Unknown option "'+e+'". This option will be ignored')})}arguments.length&&this._create(e,t,i)}var r;try{r=i(1)}catch(o){}var s=i(51),a=i(62),l=i(54);n.modes={},n.prototype.DEBOUNCE_INTERVAL=150,n.prototype._create=function(e,t,i){this.container=e,this.options=t||{},this.json=i||{};var n=this.options.mode||"tree";this.setMode(n)},n.prototype.destroy=function(){},n.prototype.set=function(e){this.json=e},n.prototype.get=function(){return this.json},n.prototype.setText=function(e){this.json=l.parse(e)},n.prototype.getText=function(){return JSON.stringify(this.json)},n.prototype.setName=function(e){this.options||(this.options={}),this.options.name=e},n.prototype.getName=function(){return this.options&&this.options.name},n.prototype.setMode=function(e){var t,i,r=this.container,o=l.extend({},this.options),s=o.mode;o.mode=e;var a=n.modes[e];if(!a)throw new Error('Unknown mode "'+o.mode+'"');try{var c="text"==a.data;if(i=this.getName(),t=this[c?"getText":"get"](),this.destroy(),l.clear(this),l.extend(this,a.mixin),this.create(r,o),this.setName(i),this[c?"setText":"set"](t),"function"==typeof a.load)try{a.load.call(this)}catch(h){console.error(h)}if("function"==typeof o.onModeChange&&e!==s)try{o.onModeChange(e,s)}catch(h){console.error(h)}}catch(h){this._onError(h)}},n.prototype.getMode=function(){return this.options.mode},n.prototype._onError=function(e){if(!this.options||"function"!=typeof this.options.onError)throw e;this.options.onError(e)},n.prototype.setSchema=function(e){if(e){var t;try{t=this.options.ajv||r({allErrors:!0,verbose:!0})}catch(i){console.warn("Failed to create an instance of Ajv, JSON Schema validation is not available. Please use a JSONEditor bundle including Ajv, or pass an instance of Ajv as via the configuration option `ajv`.")}t&&(this.validateSchema=t.compile(e),this.options.schema=e,this.validate()),this.refresh()}else this.validateSchema=null,this.options.schema=null,this.validate(),this.refresh()},n.prototype.validate=function(){},n.prototype.refresh=function(){},n.registerMode=function(e){var t,i;if(l.isArray(e))for(t=0;t",'"',"`"," ","\r","\n"," "],g=["{","}","|","\\","^","`"].concat(m),v=["'"].concat(g),w=["%","/","?",";","#"].concat(v),y=["/","?","#"],A=255,C=/^[a-z0-9A-Z_-]{0,63}$/,E=/^([a-z0-9A-Z_-]{0,63})(.*)$/,b={javascript:!0,"javascript:":!0},F={javascript:!0,"javascript:":!0},x={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},S=i(7);n.prototype.parse=function(e,t,i){if(!l(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var n=e;n=n.trim();var r=f.exec(n);if(r){r=r[0];var o=r.toLowerCase();this.protocol=o,n=n.substr(r.length)}if(i||r||n.match(/^\/\/[^@\/]+@[^@\/]+/)){var s="//"===n.substr(0,2);!s||r&&F[r]||(n=n.substr(2),this.slashes=!0)}if(!F[r]&&(s||r&&!x[r])){for(var a=-1,c=0;ch)&&(a=h)}var d,p;p=-1===a?n.lastIndexOf("@"):n.lastIndexOf("@",a),-1!==p&&(d=n.slice(0,p),n=n.slice(p+1),this.auth=decodeURIComponent(d)),a=-1;for(var c=0;ch)&&(a=h)}-1===a&&(a=n.length),this.host=n.slice(0,a),n=n.slice(a),this.parseHost(),this.hostname=this.hostname||"";var m="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!m)for(var g=this.hostname.split(/\./),c=0,$=g.length;$>c;c++){var D=g[c];if(D&&!D.match(C)){for(var k="",B=0,_=D.length;_>B;B++)k+=D.charCodeAt(B)>127?"x":D[B];if(!k.match(C)){var L=g.slice(0,c),R=g.slice(c+1),T=D.match(E);T&&(L.push(T[1]),R.unshift(T[2])),R.length&&(n="/"+R.join(".")+n),this.hostname=L.join(".");break}}}if(this.hostname.length>A?this.hostname="":this.hostname=this.hostname.toLowerCase(),!m){for(var P=this.hostname.split("."),M=[],c=0;cc;c++){var j=v[c],W=encodeURIComponent(j);W===j&&(W=escape(j)),n=n.split(j).join(W)}var H=n.indexOf("#");-1!==H&&(this.hash=n.substr(H),n=n.slice(0,H));var V=n.indexOf("?");if(-1!==V?(this.search=n.substr(V),this.query=n.substr(V+1),t&&(this.query=S.parse(this.query)),n=n.slice(0,V)):t&&(this.search="",this.query={}),n&&(this.pathname=n),x[o]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var N=this.pathname||"",O=this.search||"";this.path=N+O}return this.href=this.format(),this},n.prototype.format=function(){var e=this.auth||"";e&&(e=encodeURIComponent(e),e=e.replace(/%3A/i,":"),e+="@");var t=this.protocol||"",i=this.pathname||"",n=this.hash||"",r=!1,o="";this.host?r=e+this.host:this.hostname&&(r=e+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(r+=":"+this.port)),this.query&&c(this.query)&&Object.keys(this.query).length&&(o=S.stringify(this.query));var s=this.search||o&&"?"+o||"";return t&&":"!==t.substr(-1)&&(t+=":"),this.slashes||(!t||x[t])&&r!==!1?(r="//"+(r||""),i&&"/"!==i.charAt(0)&&(i="/"+i)):r||(r=""),n&&"#"!==n.charAt(0)&&(n="#"+n),s&&"?"!==s.charAt(0)&&(s="?"+s),i=i.replace(/[?#]/g,function(e){return encodeURIComponent(e)}),s=s.replace("#","%23"),t+r+i+s+n},n.prototype.resolve=function(e){return this.resolveObject(r(e,!1,!0)).format()},n.prototype.resolveObject=function(e){if(l(e)){var t=new n;t.parse(e,!1,!0),e=t}var i=new n;if(Object.keys(this).forEach(function(e){i[e]=this[e]},this),i.hash=e.hash,""===e.href)return i.href=i.format(),i;if(e.slashes&&!e.protocol)return Object.keys(e).forEach(function(t){"protocol"!==t&&(i[t]=e[t])}),x[i.protocol]&&i.hostname&&!i.pathname&&(i.path=i.pathname="/"),i.href=i.format(),i;if(e.protocol&&e.protocol!==i.protocol){if(!x[e.protocol])return Object.keys(e).forEach(function(t){i[t]=e[t]}),i.href=i.format(),i;if(i.protocol=e.protocol,e.host||F[e.protocol])i.pathname=e.pathname;else{for(var r=(e.pathname||"").split("/");r.length&&!(e.host=r.shift()););e.host||(e.host=""),e.hostname||(e.hostname=""),""!==r[0]&&r.unshift(""),r.length<2&&r.unshift(""),i.pathname=r.join("/")}if(i.search=e.search,i.query=e.query,i.host=e.host||"",i.auth=e.auth,i.hostname=e.hostname||e.host,i.port=e.port,i.pathname||i.search){var o=i.pathname||"",s=i.search||"";i.path=o+s}return i.slashes=i.slashes||e.slashes,i.href=i.format(),i}var a=i.pathname&&"/"===i.pathname.charAt(0),c=e.host||e.pathname&&"/"===e.pathname.charAt(0),u=c||a||i.host&&e.pathname,f=u,p=i.pathname&&i.pathname.split("/")||[],r=e.pathname&&e.pathname.split("/")||[],m=i.protocol&&!x[i.protocol];if(m&&(i.hostname="",i.port=null,i.host&&(""===p[0]?p[0]=i.host:p.unshift(i.host)),i.host="",e.protocol&&(e.hostname=null,e.port=null,e.host&&(""===r[0]?r[0]=e.host:r.unshift(e.host)),e.host=null),u=u&&(""===r[0]||""===p[0])),c)i.host=e.host||""===e.host?e.host:i.host,i.hostname=e.hostname||""===e.hostname?e.hostname:i.hostname,i.search=e.search,i.query=e.query,p=r;else if(r.length)p||(p=[]),p.pop(),p=p.concat(r),i.search=e.search,i.query=e.query;else if(!d(e.search)){if(m){i.hostname=i.host=p.shift();var g=i.host&&i.host.indexOf("@")>0?i.host.split("@"):!1;g&&(i.auth=g.shift(),i.host=i.hostname=g.shift())}return i.search=e.search,i.query=e.query,h(i.pathname)&&h(i.search)||(i.path=(i.pathname?i.pathname:"")+(i.search?i.search:"")),i.href=i.format(),i}if(!p.length)return i.pathname=null,i.search?i.path="/"+i.search:i.path=null,i.href=i.format(),i;for(var v=p.slice(-1)[0],w=(i.host||e.host)&&("."===v||".."===v)||""===v,y=0,A=p.length;A>=0;A--)v=p[A],"."==v?p.splice(A,1):".."===v?(p.splice(A,1),y++):y&&(p.splice(A,1),y--);if(!u&&!f)for(;y--;y)p.unshift("..");!u||""===p[0]||p[0]&&"/"===p[0].charAt(0)||p.unshift(""),w&&"/"!==p.join("/").substr(-1)&&p.push("");var C=""===p[0]||p[0]&&"/"===p[0].charAt(0);if(m){i.hostname=i.host=C?"":p.length?p.shift():"";var g=i.host&&i.host.indexOf("@")>0?i.host.split("@"):!1;g&&(i.auth=g.shift(),i.host=i.hostname=g.shift())}return u=u||i.host&&p.length,u&&!C&&p.unshift(""),p.length?i.pathname=p.join("/"):(i.pathname=null,i.path=null),h(i.pathname)&&h(i.search)||(i.path=(i.pathname?i.pathname:"")+(i.search?i.search:"")),i.auth=e.auth||i.auth,i.slashes=i.slashes||e.slashes,i.href=i.format(),i},n.prototype.parseHost=function(){var e=this.host,t=p.exec(e);t&&(t=t[0],":"!==t&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},function(e,t,i){var n;(function(e,r){!function(o){function s(e){throw RangeError(L[e])}function a(e,t){for(var i=e.length,n=[];i--;)n[i]=t(e[i]);return n}function l(e,t){var i=e.split("@"),n="";i.length>1&&(n=i[0]+"@",e=i[1]),e=e.replace(_,".");var r=e.split("."),o=a(r,t).join(".");return n+o}function c(e){for(var t,i,n=[],r=0,o=e.length;o>r;)t=e.charCodeAt(r++),t>=55296&&56319>=t&&o>r?(i=e.charCodeAt(r++),56320==(64512&i)?n.push(((1023&t)<<10)+(1023&i)+65536):(n.push(t),r--)):n.push(t);return n}function h(e){return a(e,function(e){var t="";return e>65535&&(e-=65536,t+=P(e>>>10&1023|55296),e=56320|1023&e),t+=P(e)}).join("")}function d(e){return 10>e-48?e-22:26>e-65?e-65:26>e-97?e-97:C}function u(e,t){return e+22+75*(26>e)-((0!=t)<<5)}function f(e,t,i){var n=0;for(e=i?T(e/x):e>>1,e+=T(e/t);e>R*b>>1;n+=C)e=T(e/R);return T(n+(R+1)*e/(e+F))}function p(e){var t,i,n,r,o,a,l,c,u,p,m=[],g=e.length,v=0,w=$,y=S;for(i=e.lastIndexOf(D),0>i&&(i=0),n=0;i>n;++n)e.charCodeAt(n)>=128&&s("not-basic"),m.push(e.charCodeAt(n));for(r=i>0?i+1:0;g>r;){for(o=v,a=1,l=C;r>=g&&s("invalid-input"),c=d(e.charCodeAt(r++)),(c>=C||c>T((A-v)/a))&&s("overflow"),v+=c*a,u=y>=l?E:l>=y+b?b:l-y,!(u>c);l+=C)p=C-u,a>T(A/p)&&s("overflow"),a*=p;t=m.length+1,y=f(v-o,t,0==o),T(v/t)>A-w&&s("overflow"),w+=T(v/t),v%=t,m.splice(v++,0,w)}return h(m)}function m(e){var t,i,n,r,o,a,l,h,d,p,m,g,v,w,y,F=[];for(e=c(e),g=e.length,t=$,i=0,o=S,a=0;g>a;++a)m=e[a],128>m&&F.push(P(m));for(n=r=F.length,r&&F.push(D);g>n;){for(l=A,a=0;g>a;++a)m=e[a],m>=t&&l>m&&(l=m);for(v=n+1,l-t>T((A-i)/v)&&s("overflow"),i+=(l-t)*v,t=l,a=0;g>a;++a)if(m=e[a],t>m&&++i>A&&s("overflow"),m==t){for(h=i,d=C;p=o>=d?E:d>=o+b?b:d-o,!(p>h);d+=C)y=h-p,w=C-p,F.push(P(u(p+y%w,0))),h=T(y/w);F.push(P(u(h,0))),o=f(i,v,n==r),i=0,++n}++i,++t}return F.join("")}function g(e){return l(e,function(e){return k.test(e)?p(e.slice(4).toLowerCase()):e})}function v(e){return l(e,function(e){return B.test(e)?"xn--"+m(e):e})}var w=("object"==typeof t&&t&&!t.nodeType&&t,"object"==typeof e&&e&&!e.nodeType&&e,"object"==typeof r&&r);w.global!==w&&w.window!==w&&w.self!==w||(o=w);var y,A=2147483647,C=36,E=1,b=26,F=38,x=700,S=72,$=128,D="-",k=/^xn--/,B=/[^\x20-\x7E]/,_=/[\x2E\u3002\uFF0E\uFF61]/g,L={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},R=C-E,T=Math.floor,P=String.fromCharCode;y={version:"1.3.2",ucs2:{decode:c,encode:h},decode:p,encode:m,toASCII:v,toUnicode:g},n=function(){return y}.call(t,i,t,e),!(void 0!==n&&(e.exports=n))}(this)}).call(t,i(6)(e),function(){return this}())},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children=[],e.webpackPolyfill=1),e}},function(e,t,i){"use strict";t.decode=t.parse=i(8),t.encode=t.stringify=i(9)},function(e,t){"use strict";function i(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,n,r){t=t||"&",n=n||"=";var o={};if("string"!=typeof e||0===e.length)return o;var s=/\+/g;e=e.split(t);var a=1e3;r&&"number"==typeof r.maxKeys&&(a=r.maxKeys);var l=e.length;a>0&&l>a&&(l=a);for(var c=0;l>c;++c){var h,d,u,f,p=e[c].replace(s,"%20"),m=p.indexOf(n);m>=0?(h=p.substr(0,m),d=p.substr(m+1)):(h=p,d=""),u=decodeURIComponent(h),f=decodeURIComponent(d),i(o,u)?Array.isArray(o[u])?o[u].push(f):o[u]=[o[u],f]:o[u]=f}return o}},function(e,t){"use strict";var i=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,n,r){return t=t||"&",n=n||"=",null===e&&(e=void 0),"object"==typeof e?Object.keys(e).map(function(r){var o=encodeURIComponent(i(r))+n;return Array.isArray(e[r])?e[r].map(function(e){return o+encodeURIComponent(i(e))}).join(t):o+encodeURIComponent(i(e[r]))}).join(t):r?encodeURIComponent(i(r))+n+encodeURIComponent(i(e)):""}},function(e,t){"use strict";e.exports=function i(e,t){if(e===t)return!0;var n,r=Array.isArray(e),o=Array.isArray(t);if(r&&o){if(e.length!=t.length)return!1;for(n=0;nr;)i++,t=e.charCodeAt(r++),t>=55296&&56319>=t&&n>r&&(t=e.charCodeAt(r),56320==(64512&t)&&r++);return i}function d(e,t){t+="[^0-9]";var i=e.match(new RegExp(t,"g"));return i?i.length:0}function u(e,t,i){return t+="([^0-9])",i=i.replace(/\$/g,"$$$$"),e.replace(new RegExp(t,"g"),i+"$1")}function f(e){return e.replace(D,"").replace(k,"").replace(B,"if (!($1))")}function p(e,t){var i=e.match(_);return i&&2===i.length?t?e.replace(R,"").replace(M,O):e.replace(L,"").replace(T,P):e}function m(e,t){for(var i in e)if(t[i])return!0}function g(e){return"'"+c(e)+"'"}function v(e,t,i,n){var r=i?"'/' + "+t+(n?"":".replace(/~/g, '~0').replace(/\\//g, '~1')"):n?"'[' + "+t+" + ']'":"'[\\'' + "+t+" + '\\']'";return A(e,r)}function w(e,t,i){var n=g(i?"/"+b(t):l(t));return A(e,n)}function y(e,t,i){var n=e.match(N);if(!n)throw new Error("Invalid relative JSON-pointer: "+e);var r=+n[1],o=n[2];if("#"==o){if(r>=t)throw new Error("Cannot access property/index "+r+" levels up, current level is "+t);return i[t-r]}if(r>t)throw new Error("Cannot access data "+r+" levels up, current level is "+t);var s="data"+(t-r||"");if(!o)return s;for(var a=s,c=o.split("/"),h=0;h="0"&&"9">=n;)t+=n,l();if("."===n)for(t+=".";l()&&n>="0"&&"9">=n;)t+=n;if("e"===n||"E"===n)for(t+=n,l(),"-"!==n&&"+"!==n||(t+=n,l());n>="0"&&"9">=n;)t+=n,l();return e=+t,isFinite(e)?e:void a("Bad number")},h=function(){var e,t,i,r="";if('"'===n)for(;l();){if('"'===n)return l(),r;if("\\"===n)if(l(),"u"===n){for(i=0,t=0;4>t&&(e=parseInt(l(),16),isFinite(e));t+=1)i=16*i+e;r+=String.fromCharCode(i)}else{if("string"!=typeof s[n])break;r+=s[n]}else r+=n}a("Bad string")},d=function(){for(;n&&" ">=n;)l()},u=function(){switch(n){case"t":return l("t"),l("r"),l("u"),l("e"),!0;case"f":return l("f"),l("a"),l("l"),l("s"),l("e"),!1;case"n":return l("n"),l("u"),l("l"),l("l"),null}a("Unexpected '"+n+"'")},f=function(){var e=[];if("["===n){if(l("["),d(),"]"===n)return l("]"),e;for(;n;){if(e.push(o()),d(),"]"===n)return l("]"),e;l(","),d()}}a("Bad array")},p=function(){var e,t={};if("{"===n){if(l("{"),d(),"}"===n)return l("}"),t;for(;n;){if(e=h(),d(),l(":"),Object.hasOwnProperty.call(t,e)&&a('Duplicate key "'+e+'"'),t[e]=o(),d(),"}"===n)return l("}"),t;l(","),d()}}a("Bad object")};o=function(){switch(d(),n){case"{":return p();case"[":return f();case'"':return h();case"-":return c();default:return n>="0"&&"9">=n?c():u()}},e.exports=function(e,t){var s;return r=e,i=0,n=" ",s=o(),d(),n&&a("Syntax error"),"function"==typeof t?function l(e,i){var n,r,o=e[i];if(o&&"object"==typeof o)for(n in o)Object.prototype.hasOwnProperty.call(o,n)&&(r=l(o,n),void 0!==r?o[n]=r:delete o[n]);return t.call(e,i,o)}({"":s},""):s}},function(e,t){function i(e){return a.lastIndex=0,a.test(e)?'"'+e.replace(a,function(e){var t=l[e];return"string"==typeof t?t:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}function n(e,t){var a,l,c,h,d,u=r,f=t[e];switch(f&&"object"==typeof f&&"function"==typeof f.toJSON&&(f=f.toJSON(e)),"function"==typeof s&&(f=s.call(t,e,f)),typeof f){case"string":return i(f);case"number":return isFinite(f)?String(f):"null";case"boolean":case"null":return String(f);case"object":if(!f)return"null";if(r+=o,d=[],"[object Array]"===Object.prototype.toString.apply(f)){for(h=f.length,a=0;h>a;a+=1)d[a]=n(a,f)||"null";return c=0===d.length?"[]":r?"[\n"+r+d.join(",\n"+r)+"\n"+u+"]":"["+d.join(",")+"]",r=u,c}if(s&&"object"==typeof s)for(h=s.length,a=0;h>a;a+=1)l=s[a],"string"==typeof l&&(c=n(l,f),c&&d.push(i(l)+(r?": ":":")+c));else for(l in f)Object.prototype.hasOwnProperty.call(f,l)&&(c=n(l,f),c&&d.push(i(l)+(r?": ":":")+c));return c=0===d.length?"{}":r?"{\n"+r+d.join(",\n"+r)+"\n"+u+"}":"{"+d.join(",")+"}",r=u,c}}var r,o,s,a=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,l={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};e.exports=function(e,t,i){var a;if(r="",o="","number"==typeof i)for(a=0;i>a;a+=1)o+=" ";else"string"==typeof i&&(o=i);if(s=t,t&&"function"!=typeof t&&("object"!=typeof t||"number"!=typeof t.length))throw new Error("JSON.stringify");return n("",{"":e})}},function(e,t,i){"use strict";function n(e){r.copy(e,this)}var r=i(11);e.exports=n},function(module,exports,__webpack_require__){"use strict";function setupAsync(e,t){t!==!1&&(t=!0);var i,n=e.async,r=e.transpile;switch(typeof r){case"string":var o=TRANSPILE[r];if(!o)throw new Error("bad transpiler: "+r);return e._transpileFunc=o(e,t);case"undefined":case"boolean":if("string"==typeof n){if(i=ASYNC[n],!i)throw new Error("bad async mode: "+n);return e.transpile=i(e,t)}for(var s=0;s$;)S=x[$+=1],$&&(r+=" if ("+b+" === undefined) { ",F+="}"),"string"==S?r+=" if ("+E+" == 'number' || "+E+" == 'boolean') "+b+" = '' + "+c+"; else if ("+c+" === null) "+b+" = ''; ":"number"==S||"integer"==S?(r+=" if ("+E+" == 'boolean' || "+c+" === null || ("+E+" == 'string' && "+c+" && "+c+" == +"+c+" ","integer"==S&&(r+=" && !("+c+" % 1)"),r+=")) "+b+" = +"+c+"; "):"boolean"==S?r+=" if ("+c+" === 'false' || "+c+" === 0 || "+c+" === null) "+b+" = false; else if ("+c+" === 'true' || "+c+" === 1) "+b+" = true; ":"null"==S&&(r+=" if ("+c+" === '' || "+c+" === 0 || "+c+" === false) "+b+" = null; ");r+=" "+F+" if ("+b+" === undefined) { ";var k=k||[];k.push(r),r="",e.createErrors!==!1?(r+=" { keyword: '"+(d||"type")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+A+"\" , params: { type: '",r+=v?""+g.join(","):""+g,r+="' } ",e.opts.messages!==!1&&(r+=" , message: 'should be ",r+=v?""+g.join(","):""+g,r+="' "),e.opts.verbose&&(r+=" , schema: validate.schema"+y+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+c+" "),r+=" } "):r+=" {} ";var B=r;if(r=k.pop(),r+=!e.compositeRule&&f?e.async?" throw new ValidationError(["+B+"]); ":" validate.errors = ["+B+"]; return false; ":" var err = "+B+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",r+=" } else { ",l){var _="data"+(l-1||""),L=e.dataPathArr[l];r+=" "+c+" = "+_+"["+L+"] = "+b+"; "}else r+=" data = "+b+"; if (parentData !== undefined) parentData[parentDataProperty] = "+b+"; ";r+=" } } "}}var R=e.RULES;if(R)for(var T,P=-1,M=R.length-1;M>P;)if(T=R[P+=1],i(T)){if(T.type&&(r+=" if ("+e.util.checkDataType(T.type,c)+") { "),e.opts.useDefaults&&!e.compositeRule)if("object"==T.type&&e.schema.properties){var O=e.schema.properties,N=Object.keys(O),I=N;if(I)for(var j,W=-1,H=I.length-1;H>W;){j=I[W+=1];var V=O[j];if(void 0!==V["default"]){var z=c+e.util.getProperty(j);r+=" if ("+z+" === undefined) "+z+" = ",r+="clone"==e.opts.useDefaults?" "+JSON.stringify(V["default"])+" ":" "+e.useDefault(V["default"])+" ",r+="; "}}}else if("array"==T.type&&Array.isArray(e.schema.items)){var U=e.schema.items;if(U)for(var V,$=-1,K=U.length-1;K>$;)if(V=U[$+=1],void 0!==V["default"]){var z=c+"["+$+"]";r+=" if ("+z+" === undefined) "+z+" = ",r+="clone"==e.opts.useDefaults?" "+JSON.stringify(V["default"])+" ":" "+e.useDefault(V["default"])+" ",r+="; "}}var q=T.rules;if(q)for(var G,Y=-1,Q=q.length-1;Q>Y;)if(G=q[Y+=1],n(G)){if(G.custom){var O=e.schema[G.keyword],J=e.useCustomRule(G,O,e.schema,e),X=J.code+".errors",y=e.schemaPath+"."+G.keyword,A=e.errSchemaPath+"/"+G.keyword,Z="errs"+a,$="i"+a,ee="ruleErr"+a,te=G.definition,ie=te.async,ne=te.inline,re=te.macro;if(ie&&!e.async)throw new Error("async keyword in sync schema");if(ne||re||(r+=""+X+" = null;"),r+="var "+Z+" = errors;var valid"+a+";",ne&&te.statements)r+=" "+J.validate;else if(re){var oe=e.util.copy(e);oe.level++,oe.schema=J.validate,oe.schemaPath="";var se=e.compositeRule;e.compositeRule=oe.compositeRule=!0;var ae=e.validate(oe).replace(/validate\.schema/g,J.code);e.compositeRule=oe.compositeRule=se,r+=" "+ae}else if(te.compile||te.validate){var k=k||[];k.push(r),r="",r+=" "+J.code+".call( ",r+=e.opts.passContext?"this":"self";J.validate.length;r+=te.compile||te.schema===!1?" , "+c+" ":" , validate.schema"+y+" , "+c+" , validate.schema"+e.schemaPath+" ",r+=" , (dataPath || '')",'""'!=e.errorPath&&(r+=" + "+e.errorPath),r+=l?" , data"+(l-1||"")+" , "+e.dataPathArr[l]+" ":" , parentData , parentDataProperty ",r+=" ) ";var le=r;r=k.pop(),te.errors!==!1&&(ie?(X="customErrors"+a,r+=" var "+X+" = null; try { valid"+a+" = "+e.yieldAwait+le+"; } catch (e) { valid"+a+" = false; if (e instanceof ValidationError) "+X+" = e.errors; else throw e; } "):r+=" "+J.code+".errors = null; ")}r+="if (! ",r+=ne?te.statements?" valid"+a+" ":" ("+J.validate+") ":re?" valid"+oe.level+" ":ie?te.errors===!1?" ("+e.yieldAwait+le+") ":" valid"+a+" ":" "+le+" ",r+=") { ",d=G.keyword;var k=k||[];k.push(r),r="";var k=k||[];k.push(r),r="",e.createErrors!==!1?(r+=" { keyword: '"+(d||"custom")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+A+"\" , params: { keyword: '"+G.keyword+"' } ",e.opts.messages!==!1&&(r+=" , message: 'should pass \""+G.keyword+"\" keyword validation' "),e.opts.verbose&&(r+=" , schema: validate.schema"+y+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+c+" "),r+=" } "):r+=" {} ";var B=r;r=k.pop(),r+=!e.compositeRule&&f?e.async?" throw new ValidationError(["+B+"]); ":" validate.errors = ["+B+"]; return false; ":" var err = "+B+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ";var ce=r;r=k.pop(),ne?te.errors?"full"!=te.errors&&(r+=" for (var "+$+"="+Z+"; "+$+"2&&(t=d.call(arguments,1)),void i(t))})})}function o(e){return Promise.all(e.map(n,this))}function s(e){function t(e,t){i[t]=void 0,o.push(e.then(function(e){i[t]=e}))}for(var i=new e.constructor,r=Object.keys(e),o=[],s=0;s=1&&12>=i&&n>=1&&n<=m[i]}function o(e,t){var i=e.match(g);if(!i)return!1;var n=i[1],r=i[2],o=i[3],s=i[5];return 23>=n&&59>=r&&59>=o&&(!t||s)}function s(e){var t=e.split(E);return r(t[0])&&o(t[1],!0)}function a(e){return e.length<=255&&v.test(e)}function l(e){return b.test(e)&&w.test(e)}function c(e){try{return new RegExp(e),!0}catch(t){return!1}}function h(e,t){return e&&t?e>t?1:t>e?-1:e===t?0:void 0:void 0}function d(e,t){return e&&t&&(e=e.match(g),t=t.match(g),e&&t)?(e=e[1]+e[2]+e[3]+(e[4]||""),t=t[1]+t[2]+t[3]+(t[4]||""),e>t?1:t>e?-1:e===t?0:void 0):void 0}function u(e,t){if(e&&t){e=e.split(E),t=t.split(E);var i=h(e[0],t[0]);if(void 0!==i)return i||d(e[1],t[1])}}var f=i(11),p=/^\d\d\d\d-(\d\d)-(\d\d)$/,m=[0,31,29,31,30,31,30,31,31,30,31,30,31],g=/^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d:\d\d)?$/i,v=/^[a-z](?:(?:[-0-9a-z]{0,61})?[0-9a-z])?(\.[a-z](?:(?:[-0-9a-z]{0,61})?[0-9a-z])?)*$/i,w=/^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9a-f]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9a-f]{2})*)?$/i,y=/^(?:urn\:uuid\:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i,A=/^(?:\/(?:[^~\/]|~0|~1)+)*(?:\/)?$|^\#(?:\/(?:[a-z0-9_\-\.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)+)*(?:\/)?$/i,C=/^(?:0|[1-9][0-9]*)(?:\#|(?:\/(?:[^~\/]|~0|~1)+)*(?:\/)?)$/;e.exports=n,n.fast={date:/^\d\d\d\d-[0-1]\d-[0-3]\d$/,time:/^[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:z|[+-]\d\d:\d\d)?$/i,"date-time":/^\d\d\d\d-[0-1]\d-[0-3]\d[t\s][0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:z|[+-]\d\d:\d\d)$/i,uri:/^(?:[a-z][a-z0-9+-.]*)?(?:\:|\/)\/?[^\s]*$/i,email:/^[a-z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i,hostname:v,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,ipv6:/^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,regex:c,uuid:y,"json-pointer":A,"relative-json-pointer":C},n.full={date:r,time:o,"date-time":s,uri:l,email:/^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&''*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,hostname:a,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,ipv6:/^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,regex:c,uuid:y,"json-pointer":A,"relative-json-pointer":C},n.compare={date:h,time:d,"date-time":u};var E=/t|\s/i,b=/\/|\:/},function(e,t,i){"use strict";var n=i(24),r=i(11);e.exports=function(){var e=[{type:"number",rules:["maximum","minimum","multipleOf"]},{type:"string",rules:["maxLength","minLength","pattern","format"]},{type:"array",rules:["maxItems","minItems","uniqueItems","items"]},{type:"object",rules:["maxProperties","minProperties","required","dependencies","properties"]},{rules:["$ref","enum","not","anyOf","oneOf","allOf"]}];return e.all=["type","additionalProperties","patternProperties"],e.keywords=["additionalItems","$schema","id","title","description","default"],e.types=["number","integer","string","array","object","boolean","null"],e.forEach(function(t){t.rules=t.rules.map(function(t){return e.all.push(t),{keyword:t,code:n[t]}})}),e.keywords=r.toHash(e.all.concat(e.keywords)),e.all=r.toHash(e.all),e.types=r.toHash(e.types),e}},function(e,t,i){"use strict";e.exports={$ref:i(25),allOf:i(26),anyOf:i(27),dependencies:i(28),"enum":i(29),format:i(30),items:i(31),maximum:i(32),minimum:i(32),maxItems:i(33),minItems:i(33),maxLength:i(34),minLength:i(34),maxProperties:i(35),minProperties:i(35),multipleOf:i(36),not:i(37),oneOf:i(38),pattern:i(39),properties:i(40),required:i(41),uniqueItems:i(42),validate:i(18)}},function(e,t){"use strict";e.exports=function(e,t){var i,n,r,o=" ",s=e.level,a=e.dataLevel,l=e.schema[t],c=e.errSchemaPath+"/"+t,h=!e.opts.allErrors,d="data"+(a||""),u="valid"+s;if("#"==l||"#/"==l)e.isRoot?(n=e.async,r="validate"):(n=e.root.schema.$async===!0,r="root.refVal[0]");else{var f=e.resolveRef(e.baseId,l,e.isRoot);if(void 0===f){var p="can't resolve reference "+l+" from id "+e.baseId;if("fail"==e.opts.missingRefs){console.log(p);var m=m||[];m.push(o),o="",e.createErrors!==!1?(o+=" { keyword: '"+(i||"$ref")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+c+"\" , params: { ref: '"+e.util.escapeQuotes(l)+"' } ",e.opts.messages!==!1&&(o+=" , message: 'can\\'t resolve reference "+e.util.escapeQuotes(l)+"' "),e.opts.verbose&&(o+=" , schema: "+e.util.toQuotedString(l)+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+d+" "),o+=" } "):o+=" {} ";var g=o;o=m.pop(),o+=!e.compositeRule&&h?e.async?" throw new ValidationError(["+g+"]); ":" validate.errors = ["+g+"]; return false; ":" var err = "+g+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",h&&(o+=" if (false) { ")}else{if("ignore"!=e.opts.missingRefs){var v=new Error(p);throw v.missingRef=e.resolve.url(e.baseId,l),v.missingSchema=e.resolve.normalizeId(e.resolve.fullPath(v.missingRef)),v}console.log(p),h&&(o+=" if (true) { ")}}else if(f.inline){var w=e.util.copy(e);w.level++,w.schema=f.schema,w.schemaPath="",w.errSchemaPath=l;var y=e.validate(w).replace(/validate\.schema/g,f.code);o+=" "+y+" ",h&&(o+=" if (valid"+w.level+") { ")}else n=f.async,r=f.code}if(r){var m=m||[];m.push(o),o="",o+=e.opts.passContext?" "+r+".call(this, ":" "+r+"( ",o+=" "+d+", (dataPath || '')",'""'!=e.errorPath&&(o+=" + "+e.errorPath),o+=a?" , data"+(a-1||"")+" , "+e.dataPathArr[a]+" ":" , parentData , parentDataProperty ",o+=") ";var A=o;if(o=m.pop(),n){if(!e.async)throw new Error("async schema referenced by sync schema");o+=" try { ",h&&(o+="var "+u+" ="),o+=" "+e.yieldAwait+" "+A+"; } catch (e) { if (!(e instanceof ValidationError)) throw e; if (vErrors === null) vErrors = e.errors; else vErrors = vErrors.concat(e.errors); errors = vErrors.length; } ",h&&(o+=" if ("+u+") { ")}else o+=" if (!"+A+") { if (vErrors === null) vErrors = "+r+".errors; else vErrors = vErrors.concat("+r+".errors); errors = vErrors.length; } ",h&&(o+=" else { ")}return o}},function(e,t){"use strict";e.exports=function(e,t){var i=" ",n=e.schema[t],r=e.schemaPath+"."+t,o=e.errSchemaPath+"/"+t,s=!e.opts.allErrors,a=e.util.copy(e),l="";a.level++;var c=n;if(c)for(var h,d=-1,u=c.length-1;u>d;)h=c[d+=1],e.util.schemaHasRules(h,e.RULES.all)&&(a.schema=h,a.schemaPath=r+"["+d+"]",a.errSchemaPath=o+"/"+d,i+=" "+e.validate(a)+" ",s&&(i+=" if (valid"+a.level+") { ",l+="}"));return s&&(i+=" "+l.slice(0,-1)),i=e.util.cleanUpCode(i)}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="valid"+r,u="errs__"+r,f=e.util.copy(e),p="";f.level++;var m=s.every(function(t){return e.util.schemaHasRules(t,e.RULES.all)});if(m){n+=" var "+u+" = errors; var "+d+" = false; ";var g=e.compositeRule;e.compositeRule=f.compositeRule=!0;var v=s;if(v)for(var w,y=-1,A=v.length-1;A>y;)w=v[y+=1],f.schema=w,f.schemaPath=a+"["+y+"]",f.errSchemaPath=l+"/"+y,n+=" "+e.validate(f)+" "+d+" = "+d+" || valid"+f.level+"; if (!"+d+") { ",p+="}";e.compositeRule=f.compositeRule=g,n+=" "+p+" if (!"+d+") { var err = ",e.createErrors!==!1?(n+=" { keyword: '"+(i||"anyOf")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: {} ',e.opts.messages!==!1&&(n+=" , message: 'should match some schema in anyOf' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ",n+="; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } else { errors = "+u+"; if (vErrors !== null) { if ("+u+") vErrors.length = "+u+"; else vErrors = null; } ",e.opts.allErrors&&(n+=" } "),n=e.util.cleanUpCode(n)}else c&&(n+=" if (true) { ");return n}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="errs__"+r,u=e.util.copy(e),f="";u.level++;var p={},m={};for(y in s){var g=s[y],v=Array.isArray(g)?m:p;v[y]=g}n+="var "+d+" = errors;";var w=e.errorPath;n+="var missing"+r+";";for(var y in m){v=m[y],n+=" if ("+h+e.util.getProperty(y)+" !== undefined && ( ";var A=v;if(A)for(var C,E=-1,b=A.length-1;b>E;){C=A[E+=1],E&&(n+=" || ");var F=e.util.getProperty(C);n+=" ( "+h+F+" === undefined && (missing"+r+" = "+e.util.toQuotedString(e.opts.jsonPointers?C:F)+") ) "}n+=")) { ";var x="missing"+r,S="' + "+x+" + '";e.opts._errorDataPathProperty&&(e.errorPath=e.opts.jsonPointers?e.util.getPathExpr(w,x,!0):w+" + "+x);var $=$||[];$.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"dependencies")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+"\" , params: { property: '"+e.util.escapeQuotes(y)+"', missingProperty: '"+S+"', depsCount: "+v.length+", deps: '"+e.util.escapeQuotes(1==v.length?v[0]:v.join(", "))+"' } ",e.opts.messages!==!1&&(n+=" , message: 'should have ",n+=1==v.length?"property "+e.util.escapeQuotes(v[0]):"properties "+e.util.escapeQuotes(v.join(", ")),n+=" when property "+e.util.escapeQuotes(y)+" is present' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var D=n;n=$.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+D+"]); ":" validate.errors = ["+D+"]; return false; ":" var err = "+D+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } ",c&&(f+="}",n+=" else { ")}e.errorPath=w;for(var y in p){var g=p[y];e.util.schemaHasRules(g,e.RULES.all)&&(n+=" valid"+u.level+" = true; if ("+h+"['"+y+"'] !== undefined) { ",u.schema=g,u.schemaPath=a+e.util.getProperty(y),u.errSchemaPath=l+"/"+e.util.escapeFragment(y),n+=" "+e.validate(u)+" } ",c&&(n+=" if (valid"+u.level+") { ",f+="}"))}return c&&(n+=" "+f+" if ("+d+" == errors) {"),n=e.util.cleanUpCode(n)}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="valid"+r,u=e.opts.v5&&s.$data,f=u?e.util.getData(s.$data,o,e.dataPathArr):s; +u&&(n+=" var schema"+r+" = "+f+"; ",f="schema"+r);var p="i"+r;u||(n+=" var schema"+r+" = validate.schema"+a+";"),n+="var "+d+";",u&&(n+=" if (schema"+r+" === undefined) "+d+" = true; else if (!Array.isArray(schema"+r+")) "+d+" = false; else {"),n+=""+d+" = false;for (var "+p+"=0; "+p+"b;)if(E=C[b+=1],e.util.schemaHasRules(E,e.RULES.all)){n+=" valid"+f.level+" = true; if ("+h+".length > "+b+") { ";var x=h+"["+b+"]";f.schema=E,f.schemaPath=a+"["+b+"]",f.errSchemaPath=l+"/"+b,f.errorPath=e.util.getPathExpr(e.errorPath,b,e.opts.jsonPointers,!0),f.dataPathArr[m]=b;var S=e.validate(f);n+=e.util.varOccurences(S,g)<2?" "+e.util.varReplace(S,g,x)+" ":" var "+g+" = "+x+"; "+S+" ",n+=" } ",c&&(n+=" if (valid"+f.level+") { ",p+="}")}if("object"==typeof v&&e.util.schemaHasRules(v,e.RULES.all)){f.schema=v,f.schemaPath=e.schemaPath+".additionalItems",f.errSchemaPath=e.errSchemaPath+"/additionalItems",n+=" valid"+f.level+" = true; if ("+h+".length > "+s.length+") { for (var i"+r+" = "+s.length+"; i"+r+" < "+h+".length; i"+r+"++) { ",f.errorPath=e.util.getPathExpr(e.errorPath,"i"+r,e.opts.jsonPointers,!0);var x=h+"[i"+r+"]";f.dataPathArr[m]="i"+r;var S=e.validate(f);n+=e.util.varOccurences(S,g)<2?" "+e.util.varReplace(S,g,x)+" ":" var "+g+" = "+x+"; "+S+" ",c&&(n+=" if (!valid"+f.level+") break; "),n+=" } } ",c&&(n+=" if (valid"+f.level+") { ",p+="}")}}else if(e.util.schemaHasRules(s,e.RULES.all)){f.schema=s,f.schemaPath=a,f.errSchemaPath=l,n+=" for (var i"+r+" = 0; i"+r+" < "+h+".length; i"+r+"++) { ",f.errorPath=e.util.getPathExpr(e.errorPath,"i"+r,e.opts.jsonPointers,!0);var x=h+"[i"+r+"]";f.dataPathArr[m]="i"+r;var S=e.validate(f);n+=e.util.varOccurences(S,g)<2?" "+e.util.varReplace(S,g,x)+" ":" var "+g+" = "+x+"; "+S+" ",c&&(n+=" if (!valid"+f.level+") break; "),n+=" } ",c&&(n+=" if (valid"+f.level+") { ",p+="}")}return c&&(n+=" "+p+" if ("+u+" == errors) {"),n=e.util.cleanUpCode(n)}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d=e.opts.v5&&s.$data,u=d?e.util.getData(s.$data,o,e.dataPathArr):s;d&&(n+=" var schema"+r+" = "+u+"; ",u="schema"+r);var f="maximum"==t,p=f?"exclusiveMaximum":"exclusiveMinimum",m=e.schema[p],g=e.opts.v5&&m&&m.$data,v=f?"<":">",w=f?">":"<";if(g){var y=e.util.getData(m.$data,o,e.dataPathArr),A="exclusive"+r,C="op"+r,E="' + "+C+" + '";n+=" var schemaExcl"+r+" = "+y+"; ",y="schemaExcl"+r,n+=" var exclusive"+r+"; if (typeof "+y+" != 'boolean' && typeof "+y+" != 'undefined') { ";var i=p,b=b||[];b.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"_exclusiveLimit")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: {} ',e.opts.messages!==!1&&(n+=" , message: '"+p+" should be boolean' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var F=n;n=b.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+F+"]); ":" validate.errors = ["+F+"]; return false; ":" var err = "+F+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } else if( ",d&&(n+=" ("+u+" !== undefined && typeof "+u+" != 'number') || "),n+=" ((exclusive"+r+" = "+y+" === true) ? "+h+" "+w+"= "+u+" : "+h+" "+w+" "+u+")) { var op"+r+" = exclusive"+r+" ? '"+v+"' : '"+v+"=';"}else{var A=m===!0,E=v;A||(E+="=");var C="'"+E+"'";n+=" if ( ",d&&(n+=" ("+u+" !== undefined && typeof "+u+" != 'number') || "),n+=" "+h+" "+w,A&&(n+="="),n+=" "+u+") {"}var i=t,b=b||[];b.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"_limit")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: { comparison: '+C+", limit: "+u+", exclusive: "+A+" } ",e.opts.messages!==!1&&(n+=" , message: 'should be "+E+" ",n+=d?"' + "+u:""+s+"'"),e.opts.verbose&&(n+=" , schema: ",n+=d?"validate.schema"+a:""+s,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var F=n;return n=b.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+F+"]); ":" validate.errors = ["+F+"]; return false; ":" var err = "+F+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } ",c&&(n+=" else { "),n}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d=e.opts.v5&&s.$data,u=d?e.util.getData(s.$data,o,e.dataPathArr):s;d&&(n+=" var schema"+r+" = "+u+"; ",u="schema"+r);var f="maxItems"==t?">":"<";n+="if ( ",d&&(n+=" ("+u+" !== undefined && typeof "+u+" != 'number') || "),n+=" "+h+".length "+f+" "+u+") { ";var i=t,p=p||[];p.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"_limitItems")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: { limit: '+u+" } ",e.opts.messages!==!1&&(n+=" , message: 'should NOT have ",n+="maxItems"==t?"more":"less",n+=" than ",n+=d?"' + "+u+" + '":""+s,n+=" items' "),e.opts.verbose&&(n+=" , schema: ",n+=d?"validate.schema"+a:""+s,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var m=n;return n=p.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+m+"]); ":" validate.errors = ["+m+"]; return false; ":" var err = "+m+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+="} ",c&&(n+=" else { "),n}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d=e.opts.v5&&s.$data,u=d?e.util.getData(s.$data,o,e.dataPathArr):s;d&&(n+=" var schema"+r+" = "+u+"; ",u="schema"+r);var f="maxLength"==t?">":"<";n+="if ( ",d&&(n+=" ("+u+" !== undefined && typeof "+u+" != 'number') || "),n+=e.opts.unicode===!1?" "+h+".length ":" ucs2length("+h+") ",n+=" "+f+" "+u+") { ";var i=t,p=p||[];p.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"_limitLength")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: { limit: '+u+" } ",e.opts.messages!==!1&&(n+=" , message: 'should NOT be ",n+="maxLength"==t?"longer":"shorter",n+=" than ",n+=d?"' + "+u+" + '":""+s,n+=" characters' "),e.opts.verbose&&(n+=" , schema: ",n+=d?"validate.schema"+a:""+s,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var m=n;return n=p.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+m+"]); ":" validate.errors = ["+m+"]; return false; ":" var err = "+m+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+="} ",c&&(n+=" else { "),n}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d=e.opts.v5&&s.$data,u=d?e.util.getData(s.$data,o,e.dataPathArr):s;d&&(n+=" var schema"+r+" = "+u+"; ",u="schema"+r);var f="maxProperties"==t?">":"<";n+="if ( ",d&&(n+=" ("+u+" !== undefined && typeof "+u+" != 'number') || "),n+=" Object.keys("+h+").length "+f+" "+u+") { ";var i=t,p=p||[];p.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"_limitProperties")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: { limit: '+u+" } ",e.opts.messages!==!1&&(n+=" , message: 'should NOT have ",n+="maxProperties"==t?"more":"less",n+=" than ",n+=d?"' + "+u+" + '":""+s,n+=" properties' "),e.opts.verbose&&(n+=" , schema: ",n+=d?"validate.schema"+a:""+s,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var m=n;return n=p.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+m+"]); ":" validate.errors = ["+m+"]; return false; ":" var err = "+m+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+="} ",c&&(n+=" else { "),n}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d=e.opts.v5&&s.$data,u=d?e.util.getData(s.$data,o,e.dataPathArr):s;d&&(n+=" var schema"+r+" = "+u+"; ",u="schema"+r),n+="var division"+r+";if (",d&&(n+=" "+u+" !== undefined && ( typeof "+u+" != 'number' || "),n+=" (division"+r+" = "+h+" / "+u+", ",n+=e.opts.multipleOfPrecision?" Math.abs(Math.round(division"+r+") - division"+r+") > 1e-"+e.opts.multipleOfPrecision+" ":" division"+r+" !== parseInt(division"+r+") ",n+=" ) ",d&&(n+=" ) "),n+=" ) { ";var f=f||[];f.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"multipleOf")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: { multipleOf: '+u+" } ",e.opts.messages!==!1&&(n+=" , message: 'should be multiple of ",n+=d?"' + "+u:""+s+"'"),e.opts.verbose&&(n+=" , schema: ",n+=d?"validate.schema"+a:""+s,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var p=n;return n=f.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+p+"]); ":" validate.errors = ["+p+"]; return false; ":" var err = "+p+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+="} ",c&&(n+=" else { "),n}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="errs__"+r,u=e.util.copy(e);if(u.level++,e.util.schemaHasRules(s,e.RULES.all)){u.schema=s,u.schemaPath=a,u.errSchemaPath=l,n+=" var "+d+" = errors; ";var f=e.compositeRule;e.compositeRule=u.compositeRule=!0,u.createErrors=!1;var p;u.opts.allErrors&&(p=u.opts.allErrors,u.opts.allErrors=!1),n+=" "+e.validate(u)+" ",u.createErrors=!0,p&&(u.opts.allErrors=p),e.compositeRule=u.compositeRule=f,n+=" if (valid"+u.level+") { ";var m=m||[];m.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"not")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: {} ',e.opts.messages!==!1&&(n+=" , message: 'should NOT be valid' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var g=n;n=m.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+g+"]); ":" validate.errors = ["+g+"]; return false; ":" var err = "+g+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } else { errors = "+d+"; if (vErrors !== null) { if ("+d+") vErrors.length = "+d+"; else vErrors = null; } ",e.opts.allErrors&&(n+=" } ")}else n+=" var err = ",e.createErrors!==!1?(n+=" { keyword: '"+(i||"not")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: {} ',e.opts.messages!==!1&&(n+=" , message: 'should NOT be valid' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ",n+="; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",c&&(n+=" if (false) { ");return n}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="valid"+r,u="errs__"+r,f=e.util.copy(e),p="";f.level++,n+="var "+u+" = errors;var prevValid"+r+" = false;var "+d+" = false; ";var m=e.compositeRule;e.compositeRule=f.compositeRule=!0;var g=s;if(g)for(var v,w=-1,y=g.length-1;y>w;)v=g[w+=1],e.util.schemaHasRules(v,e.RULES.all)?(f.schema=v,f.schemaPath=a+"["+w+"]",f.errSchemaPath=l+"/"+w,n+=" "+e.validate(f)+" "):n+=" var valid"+f.level+" = true; ",w&&(n+=" if (valid"+f.level+" && prevValid"+r+") "+d+" = false; else { ",p+="}"),n+=" if (valid"+f.level+") "+d+" = prevValid"+r+" = true;";e.compositeRule=f.compositeRule=m,n+=""+p+"if (!"+d+") { ";var A=A||[];A.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"oneOf")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: {} ',e.opts.messages!==!1&&(n+=" , message: 'should match exactly one schema in oneOf' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var C=n;return n=A.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+C+"]); ":" validate.errors = ["+C+"]; return false; ":" var err = "+C+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+="} else { errors = "+u+"; if (vErrors !== null) { if ("+u+") vErrors.length = "+u+"; else vErrors = null; }",e.opts.allErrors&&(n+=" } "),n}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d=e.opts.v5&&s.$data,u=d?e.util.getData(s.$data,o,e.dataPathArr):s;d&&(n+=" var schema"+r+" = "+u+"; ",u="schema"+r);var f=d?"(new RegExp("+u+"))":e.usePattern(s);n+="if ( ",d&&(n+=" ("+u+" !== undefined && typeof "+u+" != 'string') || "),n+=" !"+f+".test("+h+") ) { ";var p=p||[];p.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"pattern")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: { pattern: ',n+=d?""+u:""+e.util.toQuotedString(s),n+=" } ",e.opts.messages!==!1&&(n+=" , message: 'should match pattern \"",n+=d?"' + "+u+" + '":""+e.util.escapeQuotes(s),n+="\"' "),e.opts.verbose&&(n+=" , schema: ",n+=d?"validate.schema"+a:""+e.util.toQuotedString(s),n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var m=n;return n=p.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+m+"]); ":" validate.errors = ["+m+"]; return false; ":" var err = "+m+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+="} ",c&&(n+=" else { "),n}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="valid"+r,u="errs__"+r,f=e.util.copy(e),p="";f.level++;var m=f.dataLevel=e.dataLevel+1,g="data"+m,v=Object.keys(s||{}),w=e.schema.patternProperties||{},y=Object.keys(w),A=e.schema.additionalProperties,C=v.length||y.length,E=A===!1,b="object"==typeof A&&Object.keys(A).length,F=e.opts.removeAdditional,x=E||b||F,S=e.schema.required;if(S&&(!e.opts.v5||!S.$data)&&S.length5)n+=" || validate.schema"+a+"[key"+r+"] ";else{var B=v;if(B)for(var _,L=-1,R=B.length-1;R>L;)_=B[L+=1],n+=" || key"+r+" == "+e.util.toQuotedString(_)+" "}if(y.length){var T=y;if(T)for(var P,M=-1,O=T.length-1;O>M;)P=T[M+=1],n+=" || "+e.usePattern(P)+".test(key"+r+") "}if(e.opts.v5&&k&&k.length){var N=k;if(N)for(var I,M=-1,j=N.length-1;j>M;)I=N[M+=1],n+=" || "+e.usePattern(I)+".test(key"+r+") "}n+=" ); if (isAdditional"+r+") { "}if("all"==F)n+=" delete "+h+"[key"+r+"]; ";else{var W=e.errorPath,H="' + key"+r+" + '";if(e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPathExpr(e.errorPath,"key"+r,e.opts.jsonPointers)),E)if(F)n+=" delete "+h+"[key"+r+"]; ";else{n+=" valid"+f.level+" = false; ";var V=l;l=e.errSchemaPath+"/additionalProperties";var z=z||[];z.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"additionalProperties")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+"\" , params: { additionalProperty: '"+H+"' } ",e.opts.messages!==!1&&(n+=" , message: 'should NOT have additional properties' "),e.opts.verbose&&(n+=" , schema: false , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var U=n;n=z.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+U+"]); ":" validate.errors = ["+U+"]; return false; ":" var err = "+U+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",l=V,c&&(n+=" break; ")}else if(b)if("failing"==F){n+=" var "+u+" = errors; ";var K=e.compositeRule;e.compositeRule=f.compositeRule=!0,f.schema=A,f.schemaPath=e.schemaPath+".additionalProperties",f.errSchemaPath=e.errSchemaPath+"/additionalProperties",f.errorPath=e.opts._errorDataPathProperty?e.errorPath:e.util.getPathExpr(e.errorPath,"key"+r,e.opts.jsonPointers);var q=h+"[key"+r+"]";f.dataPathArr[m]="key"+r;var G=e.validate(f);n+=e.util.varOccurences(G,g)<2?" "+e.util.varReplace(G,g,q)+" ":" var "+g+" = "+q+"; "+G+" ",n+=" if (!valid"+f.level+") { errors = "+u+"; if (validate.errors !== null) { if (errors) validate.errors.length = errors; else validate.errors = null; } delete "+h+"[key"+r+"]; } ",e.compositeRule=f.compositeRule=K}else{f.schema=A,f.schemaPath=e.schemaPath+".additionalProperties",f.errSchemaPath=e.errSchemaPath+"/additionalProperties",f.errorPath=e.opts._errorDataPathProperty?e.errorPath:e.util.getPathExpr(e.errorPath,"key"+r,e.opts.jsonPointers);var q=h+"[key"+r+"]";f.dataPathArr[m]="key"+r;var G=e.validate(f);n+=e.util.varOccurences(G,g)<2?" "+e.util.varReplace(G,g,q)+" ":" var "+g+" = "+q+"; "+G+" ",c&&(n+=" if (!valid"+f.level+") break; ")}e.errorPath=W}C&&(n+=" } "),n+=" } ",c&&(n+=" if (valid"+f.level+") { ",p+="}")}var Y=e.opts.useDefaults&&!e.compositeRule;if(v.length){var Q=v;if(Q)for(var _,J=-1,X=Q.length-1;X>J;){_=Q[J+=1];var Z=s[_];if(e.util.schemaHasRules(Z,e.RULES.all)){var ee=e.util.getProperty(_),q=h+ee,te=Y&&void 0!==Z["default"];f.schema=Z,f.schemaPath=a+ee,f.errSchemaPath=l+"/"+e.util.escapeFragment(_),f.errorPath=e.util.getPath(e.errorPath,_,e.opts.jsonPointers),f.dataPathArr[m]=e.util.toQuotedString(_);var G=e.validate(f);if(e.util.varOccurences(G,g)<2){G=e.util.varReplace(G,g,q);var ie=q}else{var ie=g;n+=" var "+g+" = "+q+"; "}if(te)n+=" "+G+" ";else{if($&&$[_]){n+=" if ("+ie+" === undefined) { valid"+f.level+" = false; ";var W=e.errorPath,V=l,ne=e.util.escapeQuotes(_);e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPath(W,_,e.opts.jsonPointers)),l=e.errSchemaPath+"/required";var z=z||[];z.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"required")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+"\" , params: { missingProperty: '"+ne+"' } ",e.opts.messages!==!1&&(n+=" , message: '",n+=e.opts._errorDataPathProperty?"is a required property":"should have required property \\'"+ne+"\\'",n+="' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var U=n;n=z.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+U+"]); ":" validate.errors = ["+U+"]; return false; ":" var err = "+U+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",l=V,e.errorPath=W,n+=" } else { "}else n+=c?" if ("+ie+" === undefined) { valid"+f.level+" = true; } else { ":" if ("+ie+" !== undefined) { ";n+=" "+G+" } "}}c&&(n+=" if (valid"+f.level+") { ",p+="}")}}var re=y;if(re)for(var P,oe=-1,se=re.length-1;se>oe;){P=re[oe+=1];var Z=w[P];if(e.util.schemaHasRules(Z,e.RULES.all)){f.schema=Z,f.schemaPath=e.schemaPath+".patternProperties"+e.util.getProperty(P),f.errSchemaPath=e.errSchemaPath+"/patternProperties/"+e.util.escapeFragment(P),n+=" for (var key"+r+" in "+h+") { if ("+e.usePattern(P)+".test(key"+r+")) { ",f.errorPath=e.util.getPathExpr(e.errorPath,"key"+r,e.opts.jsonPointers);var q=h+"[key"+r+"]";f.dataPathArr[m]="key"+r;var G=e.validate(f);n+=e.util.varOccurences(G,g)<2?" "+e.util.varReplace(G,g,q)+" ":" var "+g+" = "+q+"; "+G+" ",c&&(n+=" if (!valid"+f.level+") break; "),n+=" } ",c&&(n+=" else valid"+f.level+" = true; "),n+=" } ",c&&(n+=" if (valid"+f.level+") { ",p+="}")}}if(e.opts.v5){var ae=k;if(ae)for(var I,le=-1,ce=ae.length-1;ce>le;){I=ae[le+=1];var he=D[I],Z=he.schema;if(e.util.schemaHasRules(Z,e.RULES.all)){f.schema=Z,f.schemaPath=e.schemaPath+".patternGroups"+e.util.getProperty(I)+".schema",f.errSchemaPath=e.errSchemaPath+"/patternGroups/"+e.util.escapeFragment(I)+"/schema",n+=" var pgPropCount"+r+" = 0; for (var key"+r+" in "+h+") { if ("+e.usePattern(I)+".test(key"+r+")) { pgPropCount"+r+"++; ",f.errorPath=e.util.getPathExpr(e.errorPath,"key"+r,e.opts.jsonPointers);var q=h+"[key"+r+"]";f.dataPathArr[m]="key"+r;var G=e.validate(f);n+=e.util.varOccurences(G,g)<2?" "+e.util.varReplace(G,g,q)+" ":" var "+g+" = "+q+"; "+G+" ",c&&(n+=" if (!valid"+f.level+") break; "),n+=" } ",c&&(n+=" else valid"+f.level+" = true; "),n+=" } ",c&&(n+=" if (valid"+f.level+") { ",p+="}");var de=he.minimum,ue=he.maximum;if(void 0!==de||void 0!==ue){n+=" var "+d+" = true; ";var V=l;if(void 0!==de){var fe=de,pe="minimum",me="less";n+=" "+d+" = pgPropCount"+r+" >= "+de+"; ",l=e.errSchemaPath+"/patternGroups/minimum",n+=" if (!"+d+") { ";var z=z||[];z.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"patternGroups")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+"\" , params: { reason: '"+pe+"', limit: "+fe+", pattern: '"+e.util.escapeQuotes(I)+"' } ",e.opts.messages!==!1&&(n+=" , message: 'should NOT have "+me+" than "+fe+' properties matching pattern "'+e.util.escapeQuotes(I)+"\"' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var U=n;n=z.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+U+"]); ":" validate.errors = ["+U+"]; return false; ":" var err = "+U+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } ",void 0!==ue&&(n+=" else ")}if(void 0!==ue){var fe=ue,pe="maximum",me="more";n+=" "+d+" = pgPropCount"+r+" <= "+ue+"; ",l=e.errSchemaPath+"/patternGroups/maximum",n+=" if (!"+d+") { ";var z=z||[];z.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"patternGroups")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+"\" , params: { reason: '"+pe+"', limit: "+fe+", pattern: '"+e.util.escapeQuotes(I)+"' } ",e.opts.messages!==!1&&(n+=" , message: 'should NOT have "+me+" than "+fe+' properties matching pattern "'+e.util.escapeQuotes(I)+"\"' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var U=n;n=z.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+U+"]); ":" validate.errors = ["+U+"]; return false; ":" var err = "+U+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } "}l=V,c&&(n+=" if ("+d+") { ",p+="}")}}}}return c&&(n+=" "+p+" if ("+u+" == errors) {"),n=e.util.cleanUpCode(n)}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="valid"+r,u=e.opts.v5&&s.$data,f=u?e.util.getData(s.$data,o,e.dataPathArr):s;if(u&&(n+=" var schema"+r+" = "+f+"; ",f="schema"+r),!u)if(s.lengthv;){g=m[v+=1];var y=e.schema.properties[g];y&&e.util.schemaHasRules(y,e.RULES.all)||(p[p.length]=g)}}else var p=s;if(u||p.length){var A=e.errorPath,C=u||p.length>=e.opts.loopRequired;if(c)if(n+=" var missing"+r+"; ",C){u||(n+=" var schema"+r+" = validate.schema"+a+"; ");var E="i"+r,b="schema"+r+"["+E+"]",F="' + "+b+" + '";e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPathExpr(A,b,e.opts.jsonPointers)),n+=" var "+d+" = true; ",u&&(n+=" if (schema"+r+" === undefined) "+d+" = true; else if (!Array.isArray(schema"+r+")) "+d+" = false; else {"),n+=" for (var "+E+" = 0; "+E+" < schema"+r+".length; "+E+"++) { "+d+" = "+h+"[schema"+r+"["+E+"]] !== undefined; if (!"+d+") break; } ",u&&(n+=" } "),n+=" if (!"+d+") { ";var x=x||[];x.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"required")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+"\" , params: { missingProperty: '"+F+"' } ",e.opts.messages!==!1&&(n+=" , message: '",n+=e.opts._errorDataPathProperty?"is a required property":"should have required property \\'"+F+"\\'",n+="' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var S=n;n=x.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+S+"]); ":" validate.errors = ["+S+"]; return false; ":" var err = "+S+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } else { "}else{n+=" if ( ";var $=p;if($)for(var D,E=-1,k=$.length-1;k>E;){D=$[E+=1],E&&(n+=" || ");var B=e.util.getProperty(D);n+=" ( "+h+B+" === undefined && (missing"+r+" = "+e.util.toQuotedString(e.opts.jsonPointers?D:B)+") ) "}n+=") { ";var b="missing"+r,F="' + "+b+" + '";e.opts._errorDataPathProperty&&(e.errorPath=e.opts.jsonPointers?e.util.getPathExpr(A,b,!0):A+" + "+b);var x=x||[];x.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"required")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+"\" , params: { missingProperty: '"+F+"' } ",e.opts.messages!==!1&&(n+=" , message: '",n+=e.opts._errorDataPathProperty?"is a required property":"should have required property \\'"+F+"\\'",n+="' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var S=n;n=x.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+S+"]); ":" validate.errors = ["+S+"]; return false; ":" var err = "+S+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } else { "}else if(C){u||(n+=" var schema"+r+" = validate.schema"+a+"; ");var E="i"+r,b="schema"+r+"["+E+"]",F="' + "+b+" + '";e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPathExpr(A,b,e.opts.jsonPointers)),u&&(n+=" if (schema"+r+" && !Array.isArray(schema"+r+")) { var err = ",e.createErrors!==!1?(n+=" { keyword: '"+(i||"required")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+"\" , params: { missingProperty: '"+F+"' } ",e.opts.messages!==!1&&(n+=" , message: '",n+=e.opts._errorDataPathProperty?"is a required property":"should have required property \\'"+F+"\\'",n+="' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ",n+="; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } else if (schema"+r+" !== undefined) { "),n+=" for (var "+E+" = 0; "+E+" < schema"+r+".length; "+E+"++) { if ("+h+"[schema"+r+"["+E+"]] === undefined) { var err = ",e.createErrors!==!1?(n+=" { keyword: '"+(i||"required")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+"\" , params: { missingProperty: '"+F+"' } ",e.opts.messages!==!1&&(n+=" , message: '",n+=e.opts._errorDataPathProperty?"is a required property":"should have required property \\'"+F+"\\'",n+="' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ",n+="; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } } ",u&&(n+=" } ")}else{var _=p;if(_)for(var g,E=-1,L=_.length-1;L>E;){g=_[E+=1];var B=e.util.getProperty(g),F=e.util.escapeQuotes(g);e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPath(A,g,e.opts.jsonPointers)),n+=" if ("+h+B+" === undefined) { var err = ",e.createErrors!==!1?(n+=" { keyword: '"+(i||"required")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+"\" , params: { missingProperty: '"+F+"' } ",e.opts.messages!==!1&&(n+=" , message: '",n+=e.opts._errorDataPathProperty?"is a required property":"should have required property \\'"+F+"\\'", +n+="' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ",n+="; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } "}}e.errorPath=A}else c&&(n+=" if (true) {");return n}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="valid"+r,u=e.opts.v5&&s.$data,f=u?e.util.getData(s.$data,o,e.dataPathArr):s;if(u&&(n+=" var schema"+r+" = "+f+"; ",f="schema"+r),(s||u)&&e.opts.uniqueItems!==!1){u&&(n+=" var "+d+"; if ("+f+" === false || "+f+" === undefined) "+d+" = true; else if (typeof "+f+" != 'boolean') "+d+" = false; else { "),n+=" var "+d+" = true; if ("+h+".length > 1) { var i = "+h+".length, j; outer: for (;i--;) { for (j = i; j--;) { if (equal("+h+"[i], "+h+"[j])) { "+d+" = false; break outer; } } } } ",u&&(n+=" } "),n+=" if (!"+d+") { ";var p=p||[];p.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"uniqueItems")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: { i: i, j: j } ',e.opts.messages!==!1&&(n+=" , message: 'should NOT have duplicate items (items ## ' + j + ' and ' + i + ' are identical)' "),e.opts.verbose&&(n+=" , schema: ",n+=u?"validate.schema"+a:""+s,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var m=n;n=p.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+m+"]); ":" validate.errors = ["+m+"]; return false; ":" var err = "+m+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } ",c&&(n+=" else { ")}else c&&(n+=" if (true) { ");return n}},function(e,t,i){"use strict";function n(e){function t(t,i,r){var o={inline:r||n[t],statements:!0,errors:"full"};i&&(o.type=i),e.addKeyword(t,o)}var n={"switch":i(44),constant:i(45),_formatLimit:i(46),patternRequired:i(47)};if(e._opts.meta!==!1){var s=i(48);e.addMetaSchema(s,o)}t("constant"),e.addKeyword("contains",{type:"array",macro:r}),t("formatMaximum","string",n._formatLimit),t("formatMinimum","string",n._formatLimit),e.addKeyword("exclusiveFormatMaximum"),e.addKeyword("exclusiveFormatMinimum"),e.addKeyword("patternGroups"),t("patternRequired","object"),t("switch")}function r(e){return{not:{items:{not:e}}}}var o="https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-v5.json";e.exports={enable:n,META_SCHEMA_ID:o}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="valid"+r,u="errs__"+r,f=e.util.copy(e),p="";f.level++;var m,g="ifPassed"+e.level;n+="var "+g+";";var v=s;if(v)for(var w,y=-1,A=v.length-1;A>y;){if(w=v[y+=1],y&&!m&&(n+=" if (!"+g+") { ",p+="}"),w["if"]&&e.util.schemaHasRules(w["if"],e.RULES.all)){n+=" var "+u+" = errors; ";var C=e.compositeRule;if(e.compositeRule=f.compositeRule=!0,f.createErrors=!1,f.schema=w["if"],f.schemaPath=a+"["+y+"].if",f.errSchemaPath=l+"/"+y+"/if",n+=" "+e.validate(f)+" ",f.createErrors=!0,e.compositeRule=f.compositeRule=C,n+=" "+g+" = valid"+f.level+"; if ("+g+") { ","boolean"==typeof w.then){if(w.then===!1){var E=E||[];E.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"switch")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: { caseIndex: '+y+" } ",e.opts.messages!==!1&&(n+=" , message: 'should pass \"switch\" keyword validation' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var b=n;n=E.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+b+"]); ":" validate.errors = ["+b+"]; return false; ":" var err = "+b+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; "}n+=" var valid"+f.level+" = "+w.then+"; "}else f.schema=w.then,f.schemaPath=a+"["+y+"].then",f.errSchemaPath=l+"/"+y+"/then",n+=" "+e.validate(f)+" ";n+=" } else { errors = "+u+"; if (vErrors !== null) { if ("+u+") vErrors.length = "+u+"; else vErrors = null; } } "}else if(n+=" "+g+" = true; ","boolean"==typeof w.then){if(w.then===!1){var E=E||[];E.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"switch")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: { caseIndex: '+y+" } ",e.opts.messages!==!1&&(n+=" , message: 'should pass \"switch\" keyword validation' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var b=n;n=E.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+b+"]); ":" validate.errors = ["+b+"]; return false; ":" var err = "+b+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; "}n+=" var valid"+f.level+" = "+w.then+"; "}else f.schema=w.then,f.schemaPath=a+"["+y+"].then",f.errSchemaPath=l+"/"+y+"/then",n+=" "+e.validate(f)+" ";m=w["continue"]}return n+=""+p+"var "+d+" = valid"+f.level+"; ",n=e.util.cleanUpCode(n)}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="valid"+r,u=e.opts.v5&&s.$data,f=u?e.util.getData(s.$data,o,e.dataPathArr):s;u&&(n+=" var schema"+r+" = "+f+"; ",f="schema"+r),u||(n+=" var schema"+r+" = validate.schema"+a+";"),n+="var "+d+" = equal("+h+", schema"+r+"); if (!"+d+") { ";var p=p||[];p.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"constant")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: {} ',e.opts.messages!==!1&&(n+=" , message: 'should be equal to constant' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var m=n;return n=p.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+m+"]); ":" validate.errors = ["+m+"]; return false; ":" var err = "+m+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" }"}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="valid"+r;if(n+="var "+d+" = undefined;",e.opts.format===!1)return n+=" "+d+" = true; ";var u=e.schema.format,f=e.opts.v5&&u.$data,p="";if(f){var m=e.util.getData(u.$data,o,e.dataPathArr),g="format"+r,v="compare"+r;n+=" var "+g+" = formats["+m+"] , "+v+" = "+g+" && "+g+".compare;"}else{var g=e.formats[u];if(!g||!g.compare)return n+=" "+d+" = true; ";var v="formats"+e.util.getProperty(u)+".compare"}var w="formatMaximum"==t,y="exclusiveFormat"+(w?"Maximum":"Minimum"),A=e.schema[y],C=e.opts.v5&&A&&A.$data,E=w?"<":">",b="result"+r,F=e.opts.v5&&s.$data,x=F?e.util.getData(s.$data,o,e.dataPathArr):s;if(F&&(n+=" var schema"+r+" = "+x+"; ",x="schema"+r),C){var S=e.util.getData(A.$data,o,e.dataPathArr),$="exclusive"+r,D="op"+r,k="' + "+D+" + '";n+=" var schemaExcl"+r+" = "+S+"; ",S="schemaExcl"+r,n+=" if (typeof "+S+" != 'boolean' && "+S+" !== undefined) { "+d+" = false; ";var i=y,B=B||[];B.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"_exclusiveFormatLimit")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: {} ',e.opts.messages!==!1&&(n+=" , message: '"+y+" should be boolean' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var _=n;n=B.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+_+"]); ":" validate.errors = ["+_+"]; return false; ":" var err = "+_+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } ",c&&(p+="}",n+=" else { "),F&&(n+=" if ("+x+" === undefined) "+d+" = true; else if (typeof "+x+" != 'string') "+d+" = false; else { ",p+="}"),f&&(n+=" if (!"+v+") "+d+" = true; else { ",p+="}"),n+=" var "+b+" = "+v+"("+h+", ",n+=F?""+x:""+e.util.toQuotedString(s),n+=" ); if ("+b+" === undefined) "+d+" = false; var exclusive"+r+" = "+S+" === true; if ("+d+" === undefined) { "+d+" = exclusive"+r+" ? "+b+" "+E+" 0 : "+b+" "+E+"= 0; } if (!"+d+") var op"+r+" = exclusive"+r+" ? '"+E+"' : '"+E+"=';"}else{var $=A===!0,k=E;$||(k+="=");var D="'"+k+"'";F&&(n+=" if ("+x+" === undefined) "+d+" = true; else if (typeof "+x+" != 'string') "+d+" = false; else { ",p+="}"),f&&(n+=" if (!"+v+") "+d+" = true; else { ",p+="}"),n+=" var "+b+" = "+v+"("+h+", ",n+=F?""+x:""+e.util.toQuotedString(s),n+=" ); if ("+b+" === undefined) "+d+" = false; if ("+d+" === undefined) "+d+" = "+b+" "+E,$||(n+="="),n+=" 0;"}n+=""+p+"if (!"+d+") { ";var i=t,B=B||[];B.push(n),n="",e.createErrors!==!1?(n+=" { keyword: '"+(i||"_formatLimit")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+'" , params: { limit: ',n+=F?""+x:""+e.util.toQuotedString(s),n+=" } ",e.opts.messages!==!1&&(n+=" , message: 'should be "+k+' "',n+=F?"' + "+x+" + '":""+e.util.escapeQuotes(s),n+="\"' "),e.opts.verbose&&(n+=" , schema: ",n+=F?"validate.schema"+a:""+e.util.toQuotedString(s),n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var _=n;return n=B.pop(),n+=!e.compositeRule&&c?e.async?" throw new ValidationError(["+_+"]); ":" validate.errors = ["+_+"]; return false; ":" var err = "+_+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+="}"}},function(e,t){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+"."+t,l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="valid"+r,u="key"+r,f="patternMatched"+r,p="";n+="var "+d+" = true;";var m=s;if(m)for(var g,v=-1,w=m.length-1;w>v;){g=m[v+=1],n+=" var "+f+" = false; for (var "+u+" in "+h+") { "+f+" = "+e.usePattern(g)+".test("+u+"); if ("+f+") break; } ";var y=e.util.escapeQuotes(g);n+=" if (!"+f+") { "+d+" = false; var err = ",e.createErrors!==!1?(n+=" { keyword: '"+(i||"patternRequired")+"' , dataPath: (dataPath || '') + "+e.errorPath+' , schemaPath: "'+l+"\" , params: { missingPattern: '"+y+"' } ",e.opts.messages!==!1&&(n+=" , message: 'should have property matching pattern \\'"+y+"\\'' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ",n+="; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } ",c&&(p+="}",n+=" else { ")}return n+=""+p}},function(e,t){e.exports={id:"https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-v5.json#",$schema:"http://json-schema.org/draft-04/schema#",description:"Core schema meta-schema (v5 proposals)",definitions:{schemaArray:{type:"array",minItems:1,items:{$ref:"#"}},positiveInteger:{type:"integer",minimum:0},positiveIntegerDefault0:{allOf:[{$ref:"#/definitions/positiveInteger"},{"default":0}]},simpleTypes:{"enum":["array","boolean","integer","null","number","object","string"]},stringArray:{type:"array",items:{type:"string"},minItems:1,uniqueItems:!0},$data:{type:"object",required:["$data"],properties:{$data:{type:"string",format:"relative-json-pointer"}},additionalProperties:!1}},type:"object",properties:{id:{type:"string",format:"uri"},$schema:{type:"string",format:"uri"},title:{type:"string"},description:{type:"string"},"default":{},multipleOf:{anyOf:[{type:"number",minimum:0,exclusiveMinimum:!0},{$ref:"#/definitions/$data"}]},maximum:{anyOf:[{type:"number"},{$ref:"#/definitions/$data"}]},exclusiveMaximum:{anyOf:[{type:"boolean","default":!1},{$ref:"#/definitions/$data"}]},minimum:{anyOf:[{type:"number"},{$ref:"#/definitions/$data"}]},exclusiveMinimum:{anyOf:[{type:"boolean","default":!1},{$ref:"#/definitions/$data"}]},maxLength:{anyOf:[{$ref:"#/definitions/positiveInteger"},{$ref:"#/definitions/$data"}]},minLength:{anyOf:[{$ref:"#/definitions/positiveIntegerDefault0"},{$ref:"#/definitions/$data"}]},pattern:{anyOf:[{type:"string",format:"regex"},{$ref:"#/definitions/$data"}]},additionalItems:{anyOf:[{type:"boolean"},{$ref:"#"},{$ref:"#/definitions/$data"}],"default":{}},items:{anyOf:[{$ref:"#"},{$ref:"#/definitions/schemaArray"}],"default":{}},maxItems:{anyOf:[{$ref:"#/definitions/positiveInteger"},{$ref:"#/definitions/$data"}]},minItems:{anyOf:[{$ref:"#/definitions/positiveIntegerDefault0"},{$ref:"#/definitions/$data"}]},uniqueItems:{anyOf:[{type:"boolean","default":!1},{$ref:"#/definitions/$data"}]},maxProperties:{anyOf:[{$ref:"#/definitions/positiveInteger"},{$ref:"#/definitions/$data"}]},minProperties:{anyOf:[{$ref:"#/definitions/positiveIntegerDefault0"},{$ref:"#/definitions/$data"}]},required:{anyOf:[{$ref:"#/definitions/stringArray"},{$ref:"#/definitions/$data"}]},additionalProperties:{anyOf:[{type:"boolean"},{$ref:"#"},{$ref:"#/definitions/$data"}],"default":{}},definitions:{type:"object",additionalProperties:{$ref:"#"},"default":{}},properties:{type:"object",additionalProperties:{$ref:"#"},"default":{}},patternProperties:{type:"object",additionalProperties:{$ref:"#"},"default":{}},dependencies:{type:"object",additionalProperties:{anyOf:[{$ref:"#"},{$ref:"#/definitions/stringArray"}]}},"enum":{anyOf:[{type:"array",minItems:1,uniqueItems:!0},{$ref:"#/definitions/$data"}]},type:{anyOf:[{$ref:"#/definitions/simpleTypes"},{type:"array",items:{$ref:"#/definitions/simpleTypes"},minItems:1,uniqueItems:!0}]},allOf:{$ref:"#/definitions/schemaArray"},anyOf:{$ref:"#/definitions/schemaArray"},oneOf:{$ref:"#/definitions/schemaArray"},not:{$ref:"#"},format:{anyOf:[{type:"string"},{$ref:"#/definitions/$data"}]},formatMaximum:{anyOf:[{type:"string"},{$ref:"#/definitions/$data"}]},formatMinimum:{anyOf:[{type:"string"},{$ref:"#/definitions/$data"}]},exclusiveFormatMaximum:{anyOf:[{type:"boolean","default":!1},{$ref:"#/definitions/$data"}]},exclusiveFormatMinimum:{anyOf:[{type:"boolean","default":!1},{$ref:"#/definitions/$data"}]},constant:{anyOf:[{},{$ref:"#/definitions/$data"}]},contains:{$ref:"#"},patternGroups:{type:"object",additionalProperties:{type:"object",required:["schema"],properties:{maximum:{anyOf:[{$ref:"#/definitions/positiveInteger"},{$ref:"#/definitions/$data"}]},minimum:{anyOf:[{$ref:"#/definitions/positiveIntegerDefault0"},{$ref:"#/definitions/$data"}]},schema:{$ref:"#"}},additionalProperties:!1},"default":{}},"switch":{type:"array",items:{required:["then"],properties:{"if":{$ref:"#"},then:{anyOf:[{type:"boolean"},{$ref:"#"}]},"continue":{type:"boolean"}},additionalProperties:!1,dependencies:{"continue":["if"]}}}},dependencies:{exclusiveMaximum:["maximum"],exclusiveMinimum:["minimum"],formatMaximum:["format"],formatMinimum:["format"],exclusiveFormatMaximum:["formatMaximum"],exclusiveFormatMinimum:["formatMinimum"]},"default":{}}},function(e,t){"use strict";var i=/^[a-z_$][a-z0-9_$]*$/i;e.exports=function(e,t){function n(e,t,i){for(var n,r=0;ra;a++)r(s[a]);for(a=0;l>a;a++)n(e,s[a],t)}else s&&r(s),n(e,s,t)}this.RULES.keywords[e]=!0,this.RULES.all[e]=!0}},function(e,t){e.exports={id:"http://json-schema.org/draft-04/schema#",$schema:"http://json-schema.org/draft-04/schema#",description:"Core schema meta-schema",definitions:{schemaArray:{type:"array",minItems:1,items:{$ref:"#"}},positiveInteger:{type:"integer",minimum:0},positiveIntegerDefault0:{allOf:[{$ref:"#/definitions/positiveInteger"},{"default":0}]},simpleTypes:{"enum":["array","boolean","integer","null","number","object","string"]},stringArray:{type:"array",items:{type:"string"},minItems:1,uniqueItems:!0}},type:"object",properties:{id:{type:"string",format:"uri"},$schema:{type:"string",format:"uri"},title:{type:"string"},description:{type:"string"},"default":{},multipleOf:{type:"number",minimum:0,exclusiveMinimum:!0},maximum:{type:"number"},exclusiveMaximum:{type:"boolean","default":!1},minimum:{type:"number"},exclusiveMinimum:{type:"boolean","default":!1},maxLength:{$ref:"#/definitions/positiveInteger"},minLength:{$ref:"#/definitions/positiveIntegerDefault0"},pattern:{type:"string",format:"regex"},additionalItems:{anyOf:[{type:"boolean"},{$ref:"#"}],"default":{}},items:{anyOf:[{$ref:"#"},{$ref:"#/definitions/schemaArray"}],"default":{}},maxItems:{$ref:"#/definitions/positiveInteger"},minItems:{$ref:"#/definitions/positiveIntegerDefault0"},uniqueItems:{type:"boolean","default":!1},maxProperties:{$ref:"#/definitions/positiveInteger"},minProperties:{$ref:"#/definitions/positiveIntegerDefault0"},required:{$ref:"#/definitions/stringArray"},additionalProperties:{anyOf:[{type:"boolean"},{$ref:"#"}],"default":{}},definitions:{type:"object",additionalProperties:{$ref:"#"},"default":{}},properties:{type:"object",additionalProperties:{$ref:"#"},"default":{}},patternProperties:{type:"object",additionalProperties:{$ref:"#"},"default":{}},dependencies:{type:"object",additionalProperties:{anyOf:[{$ref:"#"},{$ref:"#/definitions/stringArray"}]}},"enum":{type:"array",minItems:1,uniqueItems:!0},type:{anyOf:[{$ref:"#/definitions/simpleTypes"},{type:"array",items:{$ref:"#/definitions/simpleTypes"},minItems:1,uniqueItems:!0}]},allOf:{$ref:"#/definitions/schemaArray"},anyOf:{$ref:"#/definitions/schemaArray"},oneOf:{$ref:"#/definitions/schemaArray"},not:{$ref:"#"}},dependencies:{exclusiveMaximum:["maximum"],exclusiveMinimum:["minimum"]},"default":{}}},function(e,t,i){"use strict";var n=i(52),r=i(53),o=i(56),s=i(57),a=i(58),l=i(61),c=i(54),h={};h.create=function(e,t){if(!e)throw new Error("No container element provided.");this.container=e,this.dom={},this.highlighter=new n,this.selection=void 0,this.multiselection={nodes:[]},this.validateSchema=null,this.errorNodes=[],this.node=null,this.focusTarget=null,this._setOptions(t),this.options.history&&"view"!==this.options.mode&&(this.history=new r(this)),this._createFrame(),this._createTable()},h.destroy=function(){this.frame&&this.container&&this.frame.parentNode==this.container&&(this.container.removeChild(this.frame),this.frame=null),this.container=null,this.dom=null,this.clear(),this.node=null,this.focusTarget=null,this.selection=null,this.multiselection=null,this.errorNodes=null,this.validateSchema=null,this._debouncedValidate=null,this.history&&(this.history.destroy(),this.history=null),this.searchBox&&(this.searchBox.destroy(),this.searchBox=null),this.modeSwitcher&&(this.modeSwitcher.destroy(),this.modeSwitcher=null)},h._setOptions=function(e){if(this.options={search:!0,history:!0,mode:"tree",name:void 0,schema:null},e)for(var t in e)e.hasOwnProperty(t)&&(this.options[t]=e[t]);this.setSchema(this.options.schema),this._debouncedValidate=c.debounce(this.validate.bind(this),this.DEBOUNCE_INTERVAL)},h.set=function(e,t){if(t&&(console.warn('Second parameter "name" is deprecated. Use setName(name) instead.'),this.options.name=t),e instanceof Function||void 0===e)this.clear();else{this.content.removeChild(this.table);var i={field:this.options.name,value:e},n=new a(this,i);this._setRoot(n),this.validate();var r=!1;this.node.expand(r),this.content.appendChild(this.table)}this.history&&this.history.clear(),this.searchBox&&this.searchBox.clear()},h.get=function(){if(this.focusTarget){var e=a.getNodeFromTarget(this.focusTarget);e&&e.blur()}return this.node?this.node.getValue():void 0},h.getText=function(){return JSON.stringify(this.get())},h.setText=function(e){this.set(c.parse(e))},h.setName=function(e){this.options.name=e,this.node&&this.node.updateField(this.options.name)},h.getName=function(){return this.options.name},h.focus=function(){var e=this.content.querySelector("[contenteditable=true]");e?e.focus():this.node.dom.expand?this.node.dom.expand.focus():this.node.dom.menu?this.node.dom.menu.focus():(e=this.frame.querySelector("button"),e&&e.focus())},h.clear=function(){this.node&&(this.node.collapse(),this.tbody.removeChild(this.node.getDom()),delete this.node)},h._setRoot=function(e){this.clear(),this.node=e,this.tbody.appendChild(e.getDom())},h.search=function(e){var t;return this.node?(this.content.removeChild(this.table),t=this.node.search(e),this.content.appendChild(this.table)):t=[],t},h.expandAll=function(){this.node&&(this.content.removeChild(this.table),this.node.expand(),this.content.appendChild(this.table))},h.collapseAll=function(){this.node&&(this.content.removeChild(this.table),this.node.collapse(),this.content.appendChild(this.table))},h._onAction=function(e,t){this.history&&this.history.add(e,t),this._onChange()},h._onChange=function(){if(this._debouncedValidate(),this.options.onChange)try{this.options.onChange()}catch(e){console.error("Error in onChange callback: ",e)}},h.validate=function(){this.errorNodes&&this.errorNodes.forEach(function(e){e.setError(null)});var e=this.node;if(e){var t=e.validate(),i=[];if(this.validateSchema){var n=this.validateSchema(e.getValue());n||(i=this.validateSchema.errors.map(function(e){return c.improveSchemaError(e)}).map(function(t){return{node:e.findNode(t.dataPath),error:t}}).filter(function(e){return null!=e.node}))}this.errorNodes=t.concat(i).reduce(function(e,t){return t.node.findParents().map(function(e){return{node:e,child:t.node,error:{message:"object"===e.type?"Contains invalid properties":"Contains invalid items"}}}).concat(e,[t])},[]).map(function(e){return e.node.setError(e.error,e.child),e.node})}},h.refresh=function(){this.node&&this.node.updateDom({recurse:!0})},h.startAutoScroll=function(e){var t=this,i=this.content,n=c.getAbsoluteTop(i),r=i.clientHeight,o=n+r,s=24,a=50;n+s>e&&i.scrollTop>0?this.autoScrollStep=(n+s-e)/3:e>o-s&&r+i.scrollTop3?(i.scrollTop+=r/3,n.animateCallback=t,n.animateTimeout=setTimeout(a,50)):(t&&t(!0),i.scrollTop=s,delete n.animateTimeout,delete n.animateCallback)};a()}else t&&t(!1)},h._createFrame=function(){function e(e){t._onEvent&&t._onEvent(e)}this.frame=document.createElement("div"),this.frame.className="jsoneditor jsoneditor-mode-"+this.options.mode,this.container.appendChild(this.frame);var t=this;this.frame.onclick=function(t){var i=t.target;e(t),"BUTTON"==i.nodeName&&t.preventDefault()},this.frame.oninput=e,this.frame.onchange=e,this.frame.onkeydown=e,this.frame.onkeyup=e,this.frame.oncut=e,this.frame.onpaste=e,this.frame.onmousedown=e,this.frame.onmouseup=e,this.frame.onmouseover=e,this.frame.onmouseout=e,c.addEventListener(this.frame,"focus",e,!0),c.addEventListener(this.frame,"blur",e,!0),this.frame.onfocusin=e,this.frame.onfocusout=e,this.menu=document.createElement("div"),this.menu.className="jsoneditor-menu",this.frame.appendChild(this.menu);var i=document.createElement("button");i.className="jsoneditor-expand-all",i.title="Expand all fields",i.onclick=function(){t.expandAll()},this.menu.appendChild(i);var n=document.createElement("button");if(n.title="Collapse all fields",n.className="jsoneditor-collapse-all",n.onclick=function(){t.collapseAll()},this.menu.appendChild(n),this.history){var r=document.createElement("button");r.className="jsoneditor-undo jsoneditor-separator",r.title="Undo last action (Ctrl+Z)",r.onclick=function(){t._onUndo()},this.menu.appendChild(r),this.dom.undo=r;var s=document.createElement("button");s.className="jsoneditor-redo",s.title="Redo (Ctrl+Shift+Z)",s.onclick=function(){t._onRedo()},this.menu.appendChild(s),this.dom.redo=s,this.history.onChange=function(){r.disabled=!t.history.canUndo(),s.disabled=!t.history.canRedo()},this.history.onChange()}if(this.options&&this.options.modes&&this.options.modes.length){var a=this;this.modeSwitcher=new l(this.menu,this.options.modes,this.options.mode,function(e){a.modeSwitcher.destroy(),a.setMode(e),a.modeSwitcher.focus()})}this.options.search&&(this.searchBox=new o(this,this.menu))},h._onUndo=function(){this.history&&(this.history.undo(),this._onChange())},h._onRedo=function(){this.history&&(this.history.redo(),this._onChange())},h._onEvent=function(e){"keydown"==e.type&&this._onKeyDown(e),"focus"==e.type&&(this.focusTarget=e.target),"mousedown"==e.type&&this._startDragDistance(e),"mousemove"!=e.type&&"mouseup"!=e.type&&"click"!=e.type||this._updateDragDistance(e);var t=a.getNodeFromTarget(e.target);if(t&&t.selected){if("click"==e.type){if(e.target==t.dom.menu)return void this.showContextMenu(e.target);e.hasMoved||this.deselect()}"mousedown"==e.type&&a.onDragStart(this.multiselection.nodes,e)}else"mousedown"==e.type&&(this.deselect(),t&&e.target==t.dom.drag?a.onDragStart(t,e):(!t||e.target!=t.dom.field&&e.target!=t.dom.value&&e.target!=t.dom.select)&&this._onMultiSelectStart(e));t&&t.onEvent(e)},h._startDragDistance=function(e){this.dragDistanceEvent={initialTarget:e.target,initialPageX:e.pageX,initialPageY:e.pageY,dragDistance:0,hasMoved:!1}},h._updateDragDistance=function(e){this.dragDistanceEvent||this._startDragDistance(e);var t=e.pageX-this.dragDistanceEvent.initialPageX,i=e.pageY-this.dragDistanceEvent.initialPageY;return this.dragDistanceEvent.dragDistance=Math.sqrt(t*t+i*i),this.dragDistanceEvent.hasMoved=this.dragDistanceEvent.hasMoved||this.dragDistanceEvent.dragDistance>10,e.dragDistance=this.dragDistanceEvent.dragDistance,e.hasMoved=this.dragDistanceEvent.hasMoved,e.dragDistance},h._onMultiSelectStart=function(e){var t=a.getNodeFromTarget(e.target);if("tree"===this.options.mode&&void 0===this.options.onEditable){this.multiselection={start:t||null,end:null,nodes:[]},this._startDragDistance(e);var i=this;this.mousemove||(this.mousemove=c.addEventListener(window,"mousemove",function(e){i._onMultiSelect(e)})),this.mouseup||(this.mouseup=c.addEventListener(window,"mouseup",function(e){i._onMultiSelectEnd(e)}))}},h._onMultiSelect=function(e){if(e.preventDefault(),this._updateDragDistance(e),e.hasMoved){var t=a.getNodeFromTarget(e.target);t&&(null==this.multiselection.start&&(this.multiselection.start=t),this.multiselection.end=t),this.deselect();var i=this.multiselection.start,n=this.multiselection.end||this.multiselection.start;i&&n&&(this.multiselection.nodes=this._findTopLevelNodes(i,n),this.select(this.multiselection.nodes))}},h._onMultiSelectEnd=function(e){this.multiselection.nodes[0]&&this.multiselection.nodes[0].dom.menu.focus(),this.multiselection.start=null,this.multiselection.end=null,this.mousemove&&(c.removeEventListener(window,"mousemove",this.mousemove),delete this.mousemove),this.mouseup&&(c.removeEventListener(window,"mouseup",this.mouseup),delete this.mouseup)},h.deselect=function(e){this.multiselection.nodes.forEach(function(e){e.setSelected(!1)}),this.multiselection.nodes=[],e&&(this.multiselection.start=null,this.multiselection.end=null)},h.select=function(e){if(!Array.isArray(e))return this.select([e]);if(e){this.deselect(),this.multiselection.nodes=e.slice(0);var t=e[0];e.forEach(function(e){e.setSelected(!0,e===t)})}},h._findTopLevelNodes=function(e,t){for(var i=e.getNodePath(),n=t.getNodePath(),r=0;r=0},n.prototype.canRedo=function(){return this.index=0;){var t=c[e];if(" "!==t&&"\n"!==t&&"\r"!==t&&" "!==t)return t;e--}return""}function o(){for(h+=2;hn;n++){var o=i[n];o.style&&o.removeAttribute("style");var s=o.attributes;if(s)for(var a=s.length-1;a>=0;a--){var l=s[a];l.specified===!0&&o.removeAttribute(l.name)}t.stripFormatting(o)}},t.setEndOfContentEditable=function(e){var t,i;document.createRange&&(t=document.createRange(),t.selectNodeContents(e),t.collapse(!1),i=window.getSelection(),i.removeAllRanges(),i.addRange(t))},t.selectContentEditable=function(e){if(e&&"DIV"==e.nodeName){var t,i;window.getSelection&&document.createRange&&(i=document.createRange(),i.selectNodeContents(e),t=window.getSelection(),t.removeAllRanges(),t.addRange(i))}},t.getSelection=function(){if(window.getSelection){var e=window.getSelection();if(e.getRangeAt&&e.rangeCount)return e.getRangeAt(0)}return null},t.setSelection=function(e){if(e&&window.getSelection){var t=window.getSelection();t.removeAllRanges(),t.addRange(e)}},t.getSelectionOffset=function(){var e=t.getSelection();return e&&"startOffset"in e&&"endOffset"in e&&e.startContainer&&e.startContainer==e.endContainer?{startOffset:e.startOffset,endOffset:e.endOffset,container:e.startContainer.parentNode}:null},t.setSelectionOffset=function(e){if(document.createRange&&window.getSelection){var i=window.getSelection();if(i){var n=document.createRange();e.container.firstChild||e.container.appendChild(document.createTextNode("")),n.setStart(e.container.firstChild,e.startOffset),n.setEnd(e.container.firstChild,e.endOffset),t.setSelection(n)}}},t.getInnerText=function(e,i){var n=void 0==i;if(n&&(i={text:"",flush:function(){var e=this.text;return this.text="",e},set:function(e){this.text=e}}),e.nodeValue)return i.flush()+e.nodeValue;if(e.hasChildNodes()){for(var r=e.childNodes,o="",s=0,a=r.length;a>s;s++){var l=r[s];if("DIV"==l.nodeName||"P"==l.nodeName){var c=r[s-1],h=c?c.nodeName:void 0;h&&"DIV"!=h&&"P"!=h&&"BR"!=h&&(o+="\n",i.flush()),o+=t.getInnerText(l,i),i.set("\n")}else"BR"==l.nodeName?(o+=i.flush(),i.set("\n")):o+=t.getInnerText(l,i)}return o}return"P"==e.nodeName&&-1!=t.getInternetExplorerVersion()?i.flush():""},t.getInternetExplorerVersion=function(){if(-1==o){var e=-1;if("Microsoft Internet Explorer"==navigator.appName){var t=navigator.userAgent,i=new RegExp("MSIE ([0-9]{1,}[.0-9]{0,})");null!=i.exec(t)&&(e=parseFloat(RegExp.$1))}o=e}return o},t.isFirefox=function(){return-1!=navigator.userAgent.indexOf("Firefox")};var o=-1;t.addEventListener=function(e,i,n,r){if(e.addEventListener)return void 0===r&&(r=!1),"mousewheel"===i&&t.isFirefox()&&(i="DOMMouseScroll"),e.addEventListener(i,n,r),n;if(e.attachEvent){var o=function(){return n.call(e,window.event)};return e.attachEvent("on"+i,o),o}},t.removeEventListener=function(e,i,n,r){e.removeEventListener?(void 0===r&&(r=!1),"mousewheel"===i&&t.isFirefox()&&(i="DOMMouseScroll"),e.removeEventListener(i,n,r)):e.detachEvent&&e.detachEvent("on"+i,n)},t.parsePath=function s(e){var t,i;if(0===e.length)return[];var n=e.match(/^\.(\w+)/);if(n)t=n[1],i=e.substr(t.length+1);else{if("["!==e[0])throw new SyntaxError("Failed to parse path");var r=e.indexOf("]");if(-1===r)throw new SyntaxError("Character ] expected in path");if(1===r)throw new SyntaxError("Index expected after [");var o=e.substring(1,r);t="*"===o?o:JSON.parse(o),i=e.substr(r+1)}return[t].concat(s(i))},t.improveSchemaError=function(e){if("enum"===e.keyword&&Array.isArray(e.schema)){var t=e.schema;if(t){if(t=t.map(function(e){return JSON.stringify(e)}),t.length>5){var i=["("+(t.length-5)+" more...)"];t=t.slice(0,5),t.push(i)}e.message="should be equal to one of: "+t.join(", ")}}return"additionalProperties"===e.keyword&&(e.message="should NOT have additional property: "+e.params.additionalProperty),e},t.insideRect=function(e,t,i){var n=void 0!==i?i:0;return t.left-n>=e.left&&t.right+n<=e.right&&t.top-n>=e.top&&t.bottom+n<=e.bottom},t.debounce=function(e,t,i){var n;return function(){var r=this,o=arguments,s=function(){n=null,i||e.apply(r,o)},a=i&&!n;clearTimeout(n),n=setTimeout(s,t),a&&e.apply(r,o)}},t.textDiff=function(e,t){for(var i=t.length,n=0,r=e.length,o=t.length;t.charAt(n)===e.charAt(n)&&i>n;)n++;for(;t.charAt(o-1)===e.charAt(r-1)&&o>n&&r>0;)o--,r--;return{start:n,end:o}}},function(e,t,i){var n=function(){var e={trace:function(){},yy:{},symbols_:{error:2,JSONString:3,STRING:4,JSONNumber:5,NUMBER:6,JSONNullLiteral:7,NULL:8,JSONBooleanLiteral:9,TRUE:10,FALSE:11,JSONText:12,JSONValue:13,EOF:14,JSONObject:15,JSONArray:16,"{":17,"}":18,JSONMemberList:19,JSONMember:20,":":21,",":22,"[":23,"]":24,JSONElementList:25,$accept:0,$end:1},terminals_:{2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"},productions_:[0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]],performAction:function(e,t,i,n,r,o,s){var a=o.length-1;switch(r){case 1:this.$=e.replace(/\\(\\|")/g,"$1").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\v/g,"\x0B").replace(/\\f/g,"\f").replace(/\\b/g,"\b");break;case 2:this.$=Number(e);break;case 3:this.$=null;break;case 4:this.$=!0;break;case 5:this.$=!1;break;case 6:return this.$=o[a-1];case 13:this.$={};break;case 14:this.$=o[a-1];break;case 15:this.$=[o[a-2],o[a]];break;case 16:this.$={},this.$[o[a][0]]=o[a][1];break;case 17:this.$=o[a-2],o[a-2][o[a][0]]=o[a][1];break;case 18:this.$=[];break;case 19:this.$=o[a-1];break;case 20:this.$=[o[a]];break;case 21:this.$=o[a-2],o[a-2].push(o[a])}},table:[{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}],defaultActions:{16:[2,6]},parseError:function(e,t){throw new Error(e)},parse:function(e){function t(e){r.length=r.length-2*e,o.length=o.length-e,s.length=s.length-e}function i(){var e;return e=n.lexer.lex()||1,"number"!=typeof e&&(e=n.symbols_[e]||e),e}var n=this,r=[0],o=[null],s=[],a=this.table,l="",c=0,h=0,d=0,u=2,f=1;this.lexer.setInput(e),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,"undefined"==typeof this.lexer.yylloc&&(this.lexer.yylloc={});var p=this.lexer.yylloc;s.push(p),"function"==typeof this.yy.parseError&&(this.parseError=this.yy.parseError);for(var m,g,v,w,y,A,C,E,b,F={};;){if(v=r[r.length-1],this.defaultActions[v]?w=this.defaultActions[v]:(null==m&&(m=i()),w=a[v]&&a[v][m]),"undefined"==typeof w||!w.length||!w[0]){if(!d){b=[];for(A in a[v])this.terminals_[A]&&A>2&&b.push("'"+this.terminals_[A]+"'");var x="";x=this.lexer.showPosition?"Parse error on line "+(c+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+b.join(", ")+", got '"+this.terminals_[m]+"'":"Parse error on line "+(c+1)+": Unexpected "+(1==m?"end of input":"'"+(this.terminals_[m]||m)+"'"),this.parseError(x,{text:this.lexer.match,token:this.terminals_[m]||m,line:this.lexer.yylineno,loc:p,expected:b})}if(3==d){if(m==f)throw new Error(x||"Parsing halted.");h=this.lexer.yyleng,l=this.lexer.yytext,c=this.lexer.yylineno,p=this.lexer.yylloc,m=i()}for(;;){if(u.toString()in a[v])break;if(0==v)throw new Error(x||"Parsing halted.");t(1),v=r[r.length-1]}g=m,m=u,v=r[r.length-1],w=a[v]&&a[v][u],d=3}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+v+", token: "+m);switch(w[0]){case 1:r.push(m),o.push(this.lexer.yytext),s.push(this.lexer.yylloc),r.push(w[1]),m=null,g?(m=g,g=null):(h=this.lexer.yyleng,l=this.lexer.yytext,c=this.lexer.yylineno,p=this.lexer.yylloc,d>0&&d--);break;case 2:if(C=this.productions_[w[1]][1],F.$=o[o.length-C],F._$={first_line:s[s.length-(C||1)].first_line,last_line:s[s.length-1].last_line,first_column:s[s.length-(C||1)].first_column,last_column:s[s.length-1].last_column},y=this.performAction.call(F,l,h,c,this.yy,w[1],o,s),"undefined"!=typeof y)return y;C&&(r=r.slice(0,-1*C*2),o=o.slice(0,-1*C),s=s.slice(0,-1*C)),r.push(this.productions_[w[1]][0]),o.push(F.$),s.push(F._$),E=a[r[r.length-2]][r[r.length-1]],r.push(E);break;case 3:return!0}}return!0}},t=function(){var e={EOF:1,parseError:function(e,t){if(!this.yy.parseError)throw new Error(e);this.yy.parseError(e,t)},setInput:function(e){return this._input=e,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this},input:function(){var e=this._input[0];this.yytext+=e,this.yyleng++,this.match+=e,this.matched+=e;var t=e.match(/\n/);return t&&this.yylineno++,this._input=this._input.slice(1),e},unput:function(e){return this._input=e+this._input,this},more:function(){return this._more=!0,this},less:function(e){this._input=this.match.slice(e)+this._input},pastInput:function(){var e=this.matched.substr(0,this.matched.length-this.match.length);return(e.length>20?"...":"")+e.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var e=this.match;return e.length<20&&(e+=this._input.substr(0,20-e.length)),(e.substr(0,20)+(e.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var e=this.pastInput(),t=new Array(e.length+1).join("-");return e+this.upcomingInput()+"\n"+t+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var e,t,i,n,r;this._more||(this.yytext="",this.match="");for(var o=this._currentRules(),s=0;st[0].length)||(t=i,n=s,this.options.flex));s++);return t?(r=t[0].match(/\n.*/g),r&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-1:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.yyleng=this.yytext.length,this._more=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],e=this.performAction.call(this,this.yy,this,o[n],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),e?e:void 0):""===this._input?this.EOF:void this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var e=this.next();return"undefined"!=typeof e?e:this.lex()},begin:function(e){this.conditionStack.push(e)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(e){this.begin(e)}};return e.options={},e.performAction=function(e,t,i,n){switch(i){case 0:break;case 1:return 6;case 2:return t.yytext=t.yytext.substr(1,t.yyleng-2),4;case 3:return 17;case 4:return 18;case 5:return 23;case 6:return 24;case 7:return 22;case 8:return 21;case 9:return 10;case 10:return 11;case 11:return 8;case 12:return 14;case 13:return"INVALID"}},e.rules=[/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt\/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/],e.conditions={INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13],inclusive:!0}},e}();return e.lexer=t,e}();t.parser=n,t.parse=n.parse.bind(n)},function(e,t){"use strict";function i(e,t){var i=this;this.editor=e,this.timeout=void 0,this.delay=200,this.lastText=void 0,this.dom={},this.dom.container=t;var n=document.createElement("table");this.dom.table=n,n.className="jsoneditor-search",t.appendChild(n);var r=document.createElement("tbody");this.dom.tbody=r,n.appendChild(r);var o=document.createElement("tr");r.appendChild(o);var s=document.createElement("td");o.appendChild(s);var a=document.createElement("div");this.dom.results=a,a.className="jsoneditor-results",s.appendChild(a),s=document.createElement("td"),o.appendChild(s);var l=document.createElement("div");this.dom.input=l,l.className="jsoneditor-frame",l.title="Search fields and values",s.appendChild(l);var c=document.createElement("table");l.appendChild(c);var h=document.createElement("tbody");c.appendChild(h),o=document.createElement("tr"),h.appendChild(o);var d=document.createElement("button");d.className="jsoneditor-refresh",s=document.createElement("td"),s.appendChild(d),o.appendChild(s);var u=document.createElement("input");this.dom.search=u,u.oninput=function(e){i._onDelayedSearch(e)},u.onchange=function(e){i._onSearch()},u.onkeydown=function(e){i._onKeyDown(e)},u.onkeyup=function(e){i._onKeyUp(e)},d.onclick=function(e){u.select()},s=document.createElement("td"),s.appendChild(u),o.appendChild(s);var f=document.createElement("button");f.title="Next result (Enter)",f.className="jsoneditor-next",f.onclick=function(){i.next()},s=document.createElement("td"),s.appendChild(f),o.appendChild(s);var p=document.createElement("button");p.title="Previous result (Shift+Enter)",p.className="jsoneditor-previous",p.onclick=function(){i.previous()},s=document.createElement("td"),s.appendChild(p),o.appendChild(s)}i.prototype.next=function(e){if(void 0!=this.results){var t=void 0!=this.resultIndex?this.resultIndex+1:0;t>this.results.length-1&&(t=0),this._setActiveResult(t,e)}},i.prototype.previous=function(e){if(void 0!=this.results){var t=this.results.length-1,i=void 0!=this.resultIndex?this.resultIndex-1:t;0>i&&(i=t),this._setActiveResult(i,e)}},i.prototype._setActiveResult=function(e,t){if(this.activeResult){var i=this.activeResult.node,n=this.activeResult.elem;"field"==n?delete i.searchFieldActive:delete i.searchValueActive,i.updateDom()}if(!this.results||!this.results[e])return this.resultIndex=void 0,void(this.activeResult=void 0);this.resultIndex=e;var r=this.results[this.resultIndex].node,o=this.results[this.resultIndex].elem;"field"==o?r.searchFieldActive=!0:r.searchValueActive=!0,this.activeResult=this.results[this.resultIndex],r.updateDom(),r.scrollTo(function(){t&&r.focus(o)})},i.prototype._clearDelay=function(){void 0!=this.timeout&&(clearTimeout(this.timeout),delete this.timeout)},i.prototype._onDelayedSearch=function(e){this._clearDelay();var t=this;this.timeout=setTimeout(function(e){t._onSearch()},this.delay)},i.prototype._onSearch=function(e){this._clearDelay();var t=this.dom.search.value,i=t.length>0?t:void 0;if(i!=this.lastText||e)if(this.lastText=i,this.results=this.editor.search(i),this._setActiveResult(void 0),void 0!=i){var n=this.results.length;switch(n){case 0:this.dom.results.innerHTML="no results";break;case 1:this.dom.results.innerHTML="1 result";break;default:this.dom.results.innerHTML=n+" results"}}else this.dom.results.innerHTML=""},i.prototype._onKeyDown=function(e){var t=e.which;27==t?(this.dom.search.value="",this._onSearch(),e.preventDefault(),e.stopPropagation()):13==t&&(e.ctrlKey?this._onSearch(!0):e.shiftKey?this.previous():this.next(),e.preventDefault(),e.stopPropagation())},i.prototype._onKeyUp=function(e){var t=e.keyCode;27!=t&&13!=t&&this._onDelayedSearch(e)},i.prototype.clear=function(){this.dom.search.value="",this._onSearch()},i.prototype.destroy=function(){this.editor=null,this.dom.container.removeChild(this.dom.table),this.dom=null,this.results=null,this.activeResult=null,this._clearDelay()},e.exports=i},function(e,t,i){"use strict";function n(e,t){function i(e,t,r){r.forEach(function(r){if("separator"==r.type){var o=document.createElement("div");o.className="jsoneditor-separator",a=document.createElement("li"),a.appendChild(o),e.appendChild(a)}else{var s={},a=document.createElement("li");e.appendChild(a);var l=document.createElement("button");if(l.className=r.className,s.button=l,r.title&&(l.title=r.title),r.click&&(l.onclick=function(e){e.preventDefault(),n.hide(),r.click()}),a.appendChild(l),r.submenu){var c=document.createElement("div");c.className="jsoneditor-icon",l.appendChild(c),l.appendChild(document.createTextNode(r.text));var h;if(r.click){l.className+=" jsoneditor-default";var d=document.createElement("button");s.buttonExpand=d,d.className="jsoneditor-expand",d.innerHTML='
',a.appendChild(d),r.submenuTitle&&(d.title=r.submenuTitle),h=d}else{var u=document.createElement("div");u.className="jsoneditor-expand",l.appendChild(u),h=l}h.onclick=function(e){e.preventDefault(),n._onExpandItem(s),h.focus()};var f=[];s.subItems=f;var p=document.createElement("ul");s.ul=p,p.className="jsoneditor-menu",p.style.height="0",a.appendChild(p),i(p,f,r.submenu)}else l.innerHTML='
'+r.text;t.push(s)}})}this.dom={};var n=this,r=this.dom;this.anchor=void 0,this.items=e,this.eventListeners={},this.selection=void 0,this.onClose=t?t.close:void 0;var o=document.createElement("div");o.className="jsoneditor-contextmenu-root",r.root=o;var s=document.createElement("div");s.className="jsoneditor-contextmenu",r.menu=s,o.appendChild(s);var a=document.createElement("ul");a.className="jsoneditor-menu",s.appendChild(a),r.list=a,r.items=[];var l=document.createElement("button");r.focusButton=l;var c=document.createElement("li");c.style.overflow="hidden",c.style.height="0",c.appendChild(l),a.appendChild(c),i(a,this.dom.items,e),this.maxHeight=0,e.forEach(function(t){var i=24*(e.length+(t.submenu?t.submenu.length:0));n.maxHeight=Math.max(n.maxHeight,i)})}var r=i(54);n.prototype._getVisibleButtons=function(){var e=[],t=this;return this.dom.items.forEach(function(i){e.push(i.button),i.buttonExpand&&e.push(i.buttonExpand),i.subItems&&i==t.expandedItem&&i.subItems.forEach(function(t){e.push(t.button),t.buttonExpand&&e.push(t.buttonExpand)})}),e},n.visibleMenu=void 0,n.prototype.show=function(e,t){this.hide();var i=!0;if(t){var o=e.getBoundingClientRect(),s=t.getBoundingClientRect();o.bottom+this.maxHeights.top&&(i=!1)}if(i){var a=e.offsetHeight;this.dom.menu.style.left="0px",this.dom.menu.style.top=a+"px",this.dom.menu.style.bottom=""}else this.dom.menu.style.left="0px",this.dom.menu.style.top="",this.dom.menu.style.bottom="0px";var l=e.parentNode;l.insertBefore(this.dom.root,l.firstChild);var c=this,h=this.dom.list;this.eventListeners.mousedown=r.addEventListener(window,"mousedown",function(e){var t=e.target;t==h||c._isChildOf(t,h)||(c.hide(),e.stopPropagation(),e.preventDefault())}),this.eventListeners.keydown=r.addEventListener(window,"keydown",function(e){c._onKeyDown(e)}),this.selection=r.getSelection(),this.anchor=e,setTimeout(function(){c.dom.focusButton.focus()},0),n.visibleMenu&&n.visibleMenu.hide(),n.visibleMenu=this},n.prototype.hide=function(){this.dom.root.parentNode&&(this.dom.root.parentNode.removeChild(this.dom.root),this.onClose&&this.onClose());for(var e in this.eventListeners)if(this.eventListeners.hasOwnProperty(e)){var t=this.eventListeners[e];t&&r.removeEventListener(window,e,t),delete this.eventListeners[e]}n.visibleMenu==this&&(n.visibleMenu=void 0)},n.prototype._onExpandItem=function(e){var t=this,i=e==this.expandedItem,n=this.expandedItem;if(n&&(n.ul.style.height="0",n.ul.style.padding="",setTimeout(function(){t.expandedItem!=n&&(n.ul.style.display="",r.removeClassName(n.ul.parentNode,"jsoneditor-selected"))},300),this.expandedItem=void 0),!i){var o=e.ul;o.style.display="block";o.clientHeight;setTimeout(function(){t.expandedItem==e&&(o.style.height=24*o.childNodes.length+"px",o.style.padding="5px 10px")},0),r.addClassName(o.parentNode,"jsoneditor-selected"),this.expandedItem=e}},n.prototype._onKeyDown=function(e){var t,i,n,o,s=e.target,a=e.which,l=!1;27==a?(this.selection&&r.setSelection(this.selection),this.anchor&&this.anchor.focus(),this.hide(),l=!0):9==a?e.shiftKey?(t=this._getVisibleButtons(),i=t.indexOf(s),0==i&&(t[t.length-1].focus(),l=!0)):(t=this._getVisibleButtons(),i=t.indexOf(s),i==t.length-1&&(t[0].focus(),l=!0)):37==a?("jsoneditor-expand"==s.className&&(t=this._getVisibleButtons(),i=t.indexOf(s),n=t[i-1],n&&n.focus()),l=!0):38==a?(t=this._getVisibleButtons(),i=t.indexOf(s),n=t[i-1],n&&"jsoneditor-expand"==n.className&&(n=t[i-2]),n||(n=t[t.length-1]),n&&n.focus(),l=!0):39==a?(t=this._getVisibleButtons(),i=t.indexOf(s),o=t[i+1],o&&"jsoneditor-expand"==o.className&&o.focus(),l=!0):40==a&&(t=this._getVisibleButtons(),i=t.indexOf(s),o=t[i+1],o&&"jsoneditor-expand"==o.className&&(o=t[i+2]),o||(o=t[0]),o&&(o.focus(),l=!0),l=!0),l&&(e.stopPropagation(),e.preventDefault())},n.prototype._isChildOf=function(e,t){for(var i=e.parentNode;i;){if(i==t)return!0;i=i.parentNode}return!1},e.exports=n},function(e,t,i){"use strict";function n(e,t){this.editor=e,this.dom={},this.expanded=!1,t&&t instanceof Object?(this.setField(t.field,t.fieldEditable),this.setValue(t.value,t.type)):(this.setField(""),this.setValue(null)),this._debouncedOnChangeValue=a.debounce(this._onChangeValue.bind(this),n.prototype.DEBOUNCE_INTERVAL),this._debouncedOnChangeField=a.debounce(this._onChangeField.bind(this),n.prototype.DEBOUNCE_INTERVAL)}var r=i(59),o=i(57),s=i(60),a=i(54);n.prototype.DEBOUNCE_INTERVAL=150,n.prototype._updateEditability=function(){if(this.editable={field:!0,value:!0},this.editor&&(this.editable.field="tree"===this.editor.options.mode,this.editable.value="view"!==this.editor.options.mode,("tree"===this.editor.options.mode||"form"===this.editor.options.mode)&&"function"==typeof this.editor.options.onEditable)){var e=this.editor.options.onEditable({field:this.field,value:this.value,path:this.getPath()});"boolean"==typeof e?(this.editable.field=e,this.editable.value=e):("boolean"==typeof e.field&&(this.editable.field=e.field),"boolean"==typeof e.value&&(this.editable.value=e.value))}},n.prototype.getPath=function(){for(var e=this,t=[];e;){var i=e.parent?"array"!=e.parent.type?e.field:e.index:void 0;void 0!==i&&t.unshift(i),e=e.parent}return t},n.prototype.findNode=function(e){for(var t=a.parsePath(e),i=this;i&&t.length>0;){var n=t.shift();if("number"==typeof n){if("array"!==i.type)throw new Error("Cannot get child node at index "+n+": node is no array");i=i.childs[n]}else{if("object"!==i.type)throw new Error("Cannot get child node "+n+": node is no object");i=i.childs.filter(function(e){return e.field===n})[0]}}return i},n.prototype.findParents=function(){for(var e=[],t=this.parent;t;)e.unshift(t),t=t.parent;return e},n.prototype.setError=function(e,t){this.getDom(),this.error=e;var i=this.dom.tdError;if(e){i||(i=document.createElement("td"),this.dom.tdError=i,this.dom.tdValue.parentNode.appendChild(i));var n=document.createElement("div");n.className="jsoneditor-popover jsoneditor-right",n.appendChild(document.createTextNode(e.message));var r=document.createElement("button");for(r.className="jsoneditor-schema-error",r.appendChild(n),r.onmouseover=r.onfocus=function(){for(var e=["right","above","below","left"],t=0;ts;s++)i=e[s],void 0===i||i instanceof Function||(r=new n(this.editor,{value:i}),this.appendChild(r));this.value=""}else if("object"==this.type){this.childs=[];for(var l in e)e.hasOwnProperty(l)&&(i=e[l],void 0===i||i instanceof Function||(r=new n(this.editor,{field:l,value:i}),this.appendChild(r)));this.value="",this.editor.options.sortObjectKeys===!0&&this.sort("asc")}else this.childs=void 0,this.value=e;this.previousValue=this.value},n.prototype.getValue=function(){if("array"==this.type){var e=[];return this.childs.forEach(function(t){e.push(t.getValue())}),e}if("object"==this.type){var t={};return this.childs.forEach(function(e){t[e.getField()]=e.getValue()}),t}return void 0===this.value&&this._getDomValue(),this.value},n.prototype.getLevel=function(){return this.parent?this.parent.getLevel()+1:0},n.prototype.getNodePath=function(){var e=this.parent?this.parent.getNodePath():[];return e.push(this),e},n.prototype.clone=function(){var e=new n(this.editor);if(e.type=this.type,e.field=this.field,e.fieldInnerText=this.fieldInnerText,e.fieldEditable=this.fieldEditable,e.value=this.value,e.valueInnerText=this.valueInnerText,e.expanded=this.expanded,this.childs){var t=[];this.childs.forEach(function(i){var n=i.clone();n.setParent(e),t.push(n)}),e.childs=t}else e.childs=void 0;return e},n.prototype.expand=function(e){this.childs&&(this.expanded=!0,this.dom.expand&&(this.dom.expand.className="jsoneditor-expanded"),this.showChilds(),e!==!1&&this.childs.forEach(function(t){t.expand(e)}))},n.prototype.collapse=function(e){this.childs&&(this.hideChilds(),e!==!1&&this.childs.forEach(function(t){t.collapse(e)}),this.dom.expand&&(this.dom.expand.className="jsoneditor-collapsed"),this.expanded=!1)},n.prototype.showChilds=function(){var e=this.childs;if(e&&this.expanded){var t=this.dom.tr,i=t?t.parentNode:void 0;if(i){var n=this.getAppend(),r=t.nextSibling;r?i.insertBefore(n,r):i.appendChild(n),this.childs.forEach(function(e){i.insertBefore(e.getDom(),n),e.showChilds()})}}},n.prototype.hide=function(){var e=this.dom.tr,t=e?e.parentNode:void 0;t&&t.removeChild(e),this.hideChilds()},n.prototype.hideChilds=function(){var e=this.childs;if(e&&this.expanded){var t=this.getAppend();t.parentNode&&t.parentNode.removeChild(t),this.childs.forEach(function(e){e.hide()})}},n.prototype.appendChild=function(e){if(this._hasChilds()){if(e.setParent(this),e.fieldEditable="object"==this.type,"array"==this.type&&(e.index=this.childs.length),this.childs.push(e),this.expanded){var t=e.getDom(),i=this.getAppend(),n=i?i.parentNode:void 0; +i&&n&&n.insertBefore(t,i),e.showChilds()}this.updateDom({updateIndexes:!0}),e.updateDom({recurse:!0})}},n.prototype.moveBefore=function(e,t){if(this._hasChilds()){var i=this.dom.tr?this.dom.tr.parentNode:void 0;if(i){var n=document.createElement("tr");n.style.height=i.clientHeight+"px",i.appendChild(n)}e.parent&&e.parent.removeChild(e),t instanceof l?this.appendChild(e):this.insertBefore(e,t),i&&i.removeChild(n)}},n.prototype.moveTo=function(e,t){if(e.parent==this){var i=this.childs.indexOf(e);t>i&&t++}var n=this.childs[t]||this.append;this.moveBefore(e,n)},n.prototype.insertBefore=function(e,t){if(this._hasChilds()){if(t==this.append)e.setParent(this),e.fieldEditable="object"==this.type,this.childs.push(e);else{var i=this.childs.indexOf(t);if(-1==i)throw new Error("Node not found");e.setParent(this),e.fieldEditable="object"==this.type,this.childs.splice(i,0,e)}if(this.expanded){var n=e.getDom(),r=t.getDom(),o=r?r.parentNode:void 0;r&&o&&o.insertBefore(n,r),e.showChilds()}this.updateDom({updateIndexes:!0}),e.updateDom({recurse:!0})}},n.prototype.insertAfter=function(e,t){if(this._hasChilds()){var i=this.childs.indexOf(t),n=this.childs[i+1];n?this.insertBefore(e,n):this.appendChild(e)}},n.prototype.search=function(e){var t,i=[],n=e?e.toLowerCase():void 0;if(delete this.searchField,delete this.searchValue,void 0!=this.field){var r=String(this.field).toLowerCase();t=r.indexOf(n),-1!=t&&(this.searchField=!0,i.push({node:this,elem:"field"})),this._updateDomField()}if(this._hasChilds()){if(this.childs){var o=[];this.childs.forEach(function(t){o=o.concat(t.search(e))}),i=i.concat(o)}if(void 0!=n){var s=!1;0==o.length?this.collapse(s):this.expand(s)}}else{if(void 0!=this.value){var a=String(this.value).toLowerCase();t=a.indexOf(n),-1!=t&&(this.searchValue=!0,i.push({node:this,elem:"value"}))}this._updateDomValue()}return i},n.prototype.scrollTo=function(e){if(!this.dom.tr||!this.dom.tr.parentNode)for(var t=this.parent,i=!1;t;)t.expand(i),t=t.parent;this.dom.tr&&this.dom.tr.parentNode&&this.editor.scrollTo(this.dom.tr.offsetTop,e)},n.focusElement=void 0,n.prototype.focus=function(e){if(n.focusElement=e,this.dom.tr&&this.dom.tr.parentNode){var t=this.dom;switch(e){case"drag":t.drag?t.drag.focus():t.menu.focus();break;case"menu":t.menu.focus();break;case"expand":this._hasChilds()?t.expand.focus():t.field&&this.fieldEditable?(t.field.focus(),a.selectContentEditable(t.field)):t.value&&!this._hasChilds()?(t.value.focus(),a.selectContentEditable(t.value)):t.menu.focus();break;case"field":t.field&&this.fieldEditable?(t.field.focus(),a.selectContentEditable(t.field)):t.value&&!this._hasChilds()?(t.value.focus(),a.selectContentEditable(t.value)):this._hasChilds()?t.expand.focus():t.menu.focus();break;case"value":default:t.value&&!this._hasChilds()?(t.value.focus(),a.selectContentEditable(t.value)):t.field&&this.fieldEditable?(t.field.focus(),a.selectContentEditable(t.field)):this._hasChilds()?t.expand.focus():t.menu.focus()}}},n.select=function(e){setTimeout(function(){a.selectContentEditable(e)},0)},n.prototype.blur=function(){this._getDomValue(!1),this._getDomField(!1)},n.prototype.containsNode=function(e){if(this==e)return!0;var t=this.childs;if(t)for(var i=0,n=t.length;n>i;i++)if(t[i].containsNode(e))return!0;return!1},n.prototype._move=function(e,t){if(e!=t){if(e.containsNode(this))throw new Error("Cannot move a field into a child of itself");e.parent&&e.parent.removeChild(e);var i=e.clone();e.clearDom(),t?this.insertBefore(i,t):this.appendChild(i)}},n.prototype.removeChild=function(e){if(this.childs){var t=this.childs.indexOf(e);if(-1!=t){e.hide(),delete e.searchField,delete e.searchValue;var i=this.childs.splice(t,1)[0];return i.parent=null,this.updateDom({updateIndexes:!0}),i}}},n.prototype._remove=function(e){this.removeChild(e)},n.prototype.changeType=function(e){var t=this.type;if(t!=e){if("string"!=e&&"auto"!=e||"string"!=t&&"auto"!=t){var i,n=this.dom.tr?this.dom.tr.parentNode:void 0;i=this.expanded?this.getAppend():this.getDom();var r=i&&i.parentNode?i.nextSibling:void 0;this.hide(),this.clearDom(),this.type=e,"object"==e?(this.childs||(this.childs=[]),this.childs.forEach(function(e,t){e.clearDom(),delete e.index,e.fieldEditable=!0,void 0==e.field&&(e.field="")}),"string"!=t&&"auto"!=t||(this.expanded=!0)):"array"==e?(this.childs||(this.childs=[]),this.childs.forEach(function(e,t){e.clearDom(),e.fieldEditable=!1,e.index=t}),"string"!=t&&"auto"!=t||(this.expanded=!0)):this.expanded=!1,n&&(r?n.insertBefore(this.getDom(),r):n.appendChild(this.getDom())),this.showChilds()}else this.type=e;"auto"!=e&&"string"!=e||("string"==e?this.value=String(this.value):this.value=this._stringCast(String(this.value)),this.focus()),this.updateDom({updateIndexes:!0})}},n.prototype._getDomValue=function(e){if(this.dom.value&&"array"!=this.type&&"object"!=this.type&&(this.valueInnerText=a.getInnerText(this.dom.value)),void 0!=this.valueInnerText)try{var t;if("string"==this.type)t=this._unescapeHTML(this.valueInnerText);else{var i=this._unescapeHTML(this.valueInnerText);t=this._stringCast(i)}t!==this.value&&(this.value=t,this._debouncedOnChangeValue())}catch(n){if(this.value=void 0,e!==!0)throw n}},n.prototype._onChangeValue=function(){var e=this.editor.getSelection();if(e.range){var t=a.textDiff(String(this.value),String(this.previousValue));e.range.startOffset=t.start,e.range.endOffset=t.end}var i=this.editor.getSelection();if(i.range){var n=a.textDiff(String(this.previousValue),String(this.value));i.range.startOffset=n.start,i.range.endOffset=n.end}this.editor._onAction("editValue",{node:this,oldValue:this.previousValue,newValue:this.value,oldSelection:e,newSelection:i}),this.previousValue=this.value},n.prototype._onChangeField=function(){var e=this.editor.getSelection();if(e.range){var t=a.textDiff(this.field,this.previousField);e.range.startOffset=t.start,e.range.endOffset=t.end}var i=this.editor.getSelection();if(i.range){var n=a.textDiff(this.previousField,this.field);i.range.startOffset=n.start,i.range.endOffset=n.end}this.editor._onAction("editField",{node:this,oldValue:this.previousField,newValue:this.field,oldSelection:e,newSelection:i}),this.previousField=this.field},n.prototype._updateDomValue=function(){var e=this.dom.value;if(e){var t=["jsoneditor-value"],i=this.value,n="auto"==this.type?a.type(i):this.type,r="string"==n&&a.isUrl(i);t.push("jsoneditor-"+n),r&&t.push("jsoneditor-url");var o=""==String(this.value)&&"array"!=this.type&&"object"!=this.type;if(o&&t.push("jsoneditor-empty"),this.searchValueActive&&t.push("jsoneditor-highlight-active"),this.searchValue&&t.push("jsoneditor-highlight"),e.className=t.join(" "),"array"==n||"object"==n){var s=this.childs?this.childs.length:0;e.title=this.type+" containing "+s+" items"}else r&&this.editable.value?e.title="Ctrl+Click or Ctrl+Enter to open url in new window":e.title="";if("boolean"===n&&this.editable.value?(this.dom.checkbox||(this.dom.checkbox=document.createElement("input"),this.dom.checkbox.type="checkbox",this.dom.tdCheckbox=document.createElement("td"),this.dom.tdCheckbox.className="jsoneditor-tree",this.dom.tdCheckbox.appendChild(this.dom.checkbox),this.dom.tdValue.parentNode.insertBefore(this.dom.tdCheckbox,this.dom.tdValue)),this.dom.checkbox.checked=this.value):this.dom.tdCheckbox&&(this.dom.tdCheckbox.parentNode.removeChild(this.dom.tdCheckbox),delete this.dom.tdCheckbox,delete this.dom.checkbox),this["enum"]&&this.editable.value){if(!this.dom.select){this.dom.select=document.createElement("select"),this.id=this.field+"_"+(new Date).getUTCMilliseconds(),this.dom.select.id=this.id,this.dom.select.name=this.dom.select.id,this.dom.select.option=document.createElement("option"),this.dom.select.option.value="",this.dom.select.option.innerHTML="--",this.dom.select.appendChild(this.dom.select.option);for(var l=0;l0&&(e=this.childs.filter(function(e){return-1!==i.indexOf(e.field)}).map(function(e){return{node:e,error:{message:'duplicate key "'+e.field+'"'}}}))}if(this.childs)for(var n=0;n0&&(e=e.concat(o))}return e},n.prototype.clearDom=function(){this.dom={}},n.prototype.getDom=function(){var e=this.dom;if(e.tr)return e.tr;if(this._updateEditability(),e.tr=document.createElement("tr"),e.tr.node=this,"tree"===this.editor.options.mode){var t=document.createElement("td");if(this.editable.field&&this.parent){var i=document.createElement("button");e.drag=i,i.className="jsoneditor-dragarea",i.title="Drag to move this field (Alt+Shift+Arrows)",t.appendChild(i)}e.tr.appendChild(t);var n=document.createElement("td"),r=document.createElement("button");e.menu=r,r.className="jsoneditor-contextmenu",r.title="Click to open the actions menu (Ctrl+M)",n.appendChild(e.menu),e.tr.appendChild(n)}var o=document.createElement("td");return e.tr.appendChild(o),e.tree=this._createDomTree(),o.appendChild(e.tree),this.updateDom({updateIndexes:!0}),e.tr},n.onDragStart=function(e,t){if(!Array.isArray(e))return n.onDragStart([e],t);if(0!==e.length){var i=e[0],r=e[e.length-1],o=n.getNodeFromTarget(t.target),s=r._nextSibling(),l=i.editor,c=a.getAbsoluteTop(o.dom.tr)-a.getAbsoluteTop(i.dom.tr);l.mousemove||(l.mousemove=a.addEventListener(window,"mousemove",function(t){n.onDrag(e,t)})),l.mouseup||(l.mouseup=a.addEventListener(window,"mouseup",function(t){n.onDragEnd(e,t)})),l.highlighter.lock(),l.drag={oldCursor:document.body.style.cursor,oldSelection:l.getSelection(),oldBeforeNode:s,mouseX:t.pageX,offsetY:c,level:i.getLevel()},document.body.style.cursor="move",t.preventDefault()}},n.onDrag=function(e,t){if(!Array.isArray(e))return n.onDrag([e],t);if(0!==e.length){var i,r,o,s,c,h,d,u,f,p,m,g,v,w,y=e[0].editor,A=t.pageY-y.drag.offsetY,C=t.pageX,E=!1,b=e[0];if(i=b.dom.tr,f=a.getAbsoluteTop(i),g=i.offsetHeight,f>A){r=i;do r=r.previousSibling,d=n.getNodeFromTarget(r),p=r?a.getAbsoluteTop(r):0;while(r&&p>A);d&&!d.parent&&(d=void 0),d||(h=i.parentNode.firstChild,r=h?h.nextSibling:void 0,d=n.getNodeFromTarget(r),d==b&&(d=void 0)),d&&(r=d.dom.tr,p=r?a.getAbsoluteTop(r):0,A>p+g&&(d=void 0)),d&&(e.forEach(function(e){d.parent.moveBefore(e,d)}),E=!0)}else{var F=e[e.length-1];if(c=F.expanded&&F.append?F.append.getDom():F.dom.tr,s=c?c.nextSibling:void 0){m=a.getAbsoluteTop(s),o=s;do u=n.getNodeFromTarget(o),o&&(v=o.nextSibling?a.getAbsoluteTop(o.nextSibling):0,w=o?v-m:0,u.parent.childs.length==e.length&&u.parent.childs[e.length-1]==F&&(f+=27)),o=o.nextSibling;while(o&&A>f+w);if(u&&u.parent){var x=C-y.drag.mouseX,S=Math.round(x/24/2),$=y.drag.level+S,D=u.getLevel();for(r=u.dom.tr.previousSibling;$>D&&r;){d=n.getNodeFromTarget(r);var k=e.some(function(e){return e===d||d._isChildOf(e)});if(k);else{if(!(d instanceof l))break;var B=d.parent.childs;if(B.length==e.length&&B[e.length-1]==F)break;u=n.getNodeFromTarget(r),D=u.getLevel()}r=r.previousSibling}c.nextSibling!=u.dom.tr&&(e.forEach(function(e){u.parent.moveBefore(e,u)}),E=!0)}}}E&&(y.drag.mouseX=C,y.drag.level=b.getLevel()),y.startAutoScroll(A),t.preventDefault()}},n.onDragEnd=function(e,t){if(!Array.isArray(e))return n.onDrag([e],t);if(0!==e.length){var i=e[0],r=i.editor,o=i.parent,s=o.childs.indexOf(i),l=o.childs[s+e.length]||o.append;e[0]&&e[0].dom.menu.focus();var c={nodes:e,oldSelection:r.drag.oldSelection,newSelection:r.getSelection(),oldBeforeNode:r.drag.oldBeforeNode,newBeforeNode:l};c.oldBeforeNode!=c.newBeforeNode&&r._onAction("moveNodes",c),document.body.style.cursor=r.drag.oldCursor,r.highlighter.unlock(),e.forEach(function(e){t.target!==e.dom.drag&&t.target!==e.dom.menu&&r.highlighter.unhighlight()}),delete r.drag,r.mousemove&&(a.removeEventListener(window,"mousemove",r.mousemove),delete r.mousemove),r.mouseup&&(a.removeEventListener(window,"mouseup",r.mouseup),delete r.mouseup),r.stopAutoScroll(),t.preventDefault()}},n.prototype._isChildOf=function(e){for(var t=this.parent;t;){if(t==e)return!0;t=t.parent}return!1},n.prototype._createDomField=function(){return document.createElement("div")},n.prototype.setHighlight=function(e){this.dom.tr&&(e?a.addClassName(this.dom.tr,"jsoneditor-highlight"):a.removeClassName(this.dom.tr,"jsoneditor-highlight"),this.append&&this.append.setHighlight(e),this.childs&&this.childs.forEach(function(t){t.setHighlight(e)}))},n.prototype.setSelected=function(e,t){this.selected=e,this.dom.tr&&(e?a.addClassName(this.dom.tr,"jsoneditor-selected"):a.removeClassName(this.dom.tr,"jsoneditor-selected"),t?a.addClassName(this.dom.tr,"jsoneditor-first"):a.removeClassName(this.dom.tr,"jsoneditor-first"),this.append&&this.append.setSelected(e),this.childs&&this.childs.forEach(function(t){t.setSelected(e)}))},n.prototype.updateValue=function(e){this.value=e,this.updateDom()},n.prototype.updateField=function(e){this.field=e,this.updateDom()},n.prototype.updateDom=function(e){var t=this.dom.tree;t&&(t.style.marginLeft=24*this.getLevel()+"px");var i=this.dom.field;if(i){this.fieldEditable?(i.contentEditable=this.editable.field,i.spellcheck=!1,i.className="jsoneditor-field"):i.className="jsoneditor-readonly";var n;n=void 0!=this.index?this.index:void 0!=this.field?this.field:this._hasChilds()?this.type:"",i.innerHTML=this._escapeHTML(n),this._updateSchema()}var r=this.dom.value;if(r){var o=this.childs?this.childs.length:0;"array"==this.type?(r.innerHTML="["+o+"]",a.addClassName(this.dom.tr,"jsoneditor-expandable")):"object"==this.type?(r.innerHTML="{"+o+"}",a.addClassName(this.dom.tr,"jsoneditor-expandable")):(r.innerHTML=this._escapeHTML(this.value),a.removeClassName(this.dom.tr,"jsoneditor-expandable"))}this._updateDomField(),this._updateDomValue(),e&&e.updateIndexes===!0&&this._updateDomIndexes(),e&&e.recurse===!0&&this.childs&&this.childs.forEach(function(t){t.updateDom(e)}),this.append&&this.append.updateDom()},n.prototype._updateSchema=function(){this.editor&&this.editor.options&&(this.schema=n._findSchema(this.editor.options.schema,this.getPath()),this.schema?this["enum"]=n._findEnum(this.schema):delete this["enum"])},n._findEnum=function(e){if(e["enum"])return e["enum"];var t=e.oneOf||e.anyOf||e.allOf;if(t){var i=t.filter(function(e){return e["enum"]});if(i.length>0)return i[0]["enum"]}return null},n._findSchema=function(e,t){for(var i=e,n=0;n0?this.editor.multiselection.nodes:[this],A=y[0],C=y[y.length-1];if(13==u){if(f==this.dom.value)this.editable.value&&!e.ctrlKey||a.isUrl(this.value)&&(window.open(this.value,"_blank"),v=!0);else if(f==this.dom.expand){var E=this._hasChilds();if(E){var b=e.ctrlKey;this._onExpand(b),f.focus(),v=!0}}}else if(68==u)p&&w&&(n.onDuplicate(y),v=!0);else if(69==u)p&&(this._onExpand(m),f.focus(),v=!0);else if(77==u&&w)p&&(this.showContextMenu(f),v=!0);else if(46==u&&w)p&&(n.onRemove(y),v=!0);else if(45==u&&w)p&&!m?(this._onInsertBefore(),v=!0):p&&m&&(this._onInsertAfter(),v=!0);else if(35==u){if(g){var F=this._lastNode();F&&F.focus(n.focusElement||this._getElementName(f)),v=!0}}else if(36==u){if(g){var x=this._firstNode();x&&x.focus(n.focusElement||this._getElementName(f)),v=!0}}else if(37==u){if(g&&!m){var S=this._previousElement(f);S&&this.focus(this._getElementName(S)),v=!0}else if(g&&m&&w){if(C.expanded){var $=C.getAppend();r=$?$.nextSibling:void 0}else{var D=C.getDom();r=D.nextSibling}r&&(i=n.getNodeFromTarget(r),o=r.nextSibling,_=n.getNodeFromTarget(o),i&&i instanceof l&&1!=C.parent.childs.length&&_&&_.parent&&(s=this.editor.getSelection(),c=C._nextSibling(),y.forEach(function(e){_.parent.moveBefore(e,_)}),this.focus(n.focusElement||this._getElementName(f)),this.editor._onAction("moveNodes",{nodes:y,oldBeforeNode:c,newBeforeNode:_,oldSelection:s,newSelection:this.editor.getSelection()})))}}else if(38==u)g&&!m?(t=this._previousNode(),t&&(this.editor.deselect(!0),t.focus(n.focusElement||this._getElementName(f))),v=!0):!g&&p&&m&&w?(t=this._previousNode(),t&&(d=this.editor.multiselection,d.start=d.start||this,d.end=t,h=this.editor._findTopLevelNodes(d.start,d.end),this.editor.select(h),t.focus("field")),v=!0):g&&m&&w&&(t=A._previousNode(),t&&t.parent&&(s=this.editor.getSelection(),c=C._nextSibling(),y.forEach(function(e){t.parent.moveBefore(e,t)}),this.focus(n.focusElement||this._getElementName(f)),this.editor._onAction("moveNodes",{nodes:y,oldBeforeNode:c,newBeforeNode:t,oldSelection:s,newSelection:this.editor.getSelection()})),v=!0);else if(39==u){if(g&&!m){var k=this._nextElement(f);k&&this.focus(this._getElementName(k)),v=!0}else if(g&&m&&w){D=A.getDom();var B=D.previousSibling;B&&(t=n.getNodeFromTarget(B),t&&t.parent&&t instanceof l&&!t.isVisible()&&(s=this.editor.getSelection(),c=C._nextSibling(),y.forEach(function(e){t.parent.moveBefore(e,t)}),this.focus(n.focusElement||this._getElementName(f)),this.editor._onAction("moveNodes",{nodes:y,oldBeforeNode:c,newBeforeNode:t,oldSelection:s,newSelection:this.editor.getSelection()})))}}else if(40==u)if(g&&!m)i=this._nextNode(),i&&(this.editor.deselect(!0),i.focus(n.focusElement||this._getElementName(f))),v=!0;else if(!g&&p&&m&&w)i=this._nextNode(),i&&(d=this.editor.multiselection,d.start=d.start||this,d.end=i,h=this.editor._findTopLevelNodes(d.start,d.end),this.editor.select(h),i.focus("field")),v=!0;else if(g&&m&&w){i=C.expanded?C.append?C.append._nextNode():void 0:C._nextNode();var _=i&&(i._nextNode()||i.parent.append);_&&_.parent&&(s=this.editor.getSelection(),c=C._nextSibling(),y.forEach(function(e){_.parent.moveBefore(e,_)}),this.focus(n.focusElement||this._getElementName(f)),this.editor._onAction("moveNodes",{nodes:y,oldBeforeNode:c,newBeforeNode:_,oldSelection:s,newSelection:this.editor.getSelection()})),v=!0}v&&(e.preventDefault(),e.stopPropagation())},n.prototype._onExpand=function(e){if(e){var t=this.dom.tr.parentNode,i=t.parentNode,n=i.scrollTop;i.removeChild(t)}this.expanded?this.collapse(e):this.expand(e),e&&(i.appendChild(t),i.scrollTop=n)},n.onRemove=function(e){if(!Array.isArray(e))return n.onRemove([e]);if(e&&e.length>0){var t=e[0],i=t.parent,r=t.editor,o=t.getIndex();r.highlighter.unhighlight();var s=r.getSelection();n.blurNodes(e);var a=r.getSelection();e.forEach(function(e){e.parent._remove(e)}),r._onAction("removeNodes",{nodes:e.slice(0),parent:i,index:o,oldSelection:s,newSelection:a})}},n.onDuplicate=function(e){if(!Array.isArray(e))return n.onDuplicate([e]);if(e&&e.length>0){var t=e[e.length-1],i=t.parent,r=t.editor;r.deselect(r.multiselection.nodes);var o=r.getSelection(),s=t,a=e.map(function(e){var t=e.clone();return i.insertAfter(t,s),s=t,t});1===e.length?a[0].focus():r.select(a);var l=r.getSelection();r._onAction("duplicateNodes",{afterNode:t,nodes:a,parent:i,oldSelection:o,newSelection:l})}},n.prototype._onInsertBefore=function(e,t,i){var r=this.editor.getSelection(),o=new n(this.editor,{field:void 0!=e?e:"",value:void 0!=t?t:"",type:i});o.expand(!0),this.parent.insertBefore(o,this),this.editor.highlighter.unhighlight(),o.focus("field");var s=this.editor.getSelection();this.editor._onAction("insertBeforeNodes",{nodes:[o],beforeNode:this,parent:this.parent,oldSelection:r,newSelection:s})},n.prototype._onInsertAfter=function(e,t,i){var r=this.editor.getSelection(),o=new n(this.editor,{field:void 0!=e?e:"",value:void 0!=t?t:"",type:i});o.expand(!0),this.parent.insertAfter(o,this),this.editor.highlighter.unhighlight(),o.focus("field");var s=this.editor.getSelection();this.editor._onAction("insertAfterNodes",{nodes:[o],afterNode:this,parent:this.parent,oldSelection:r,newSelection:s})},n.prototype._onAppend=function(e,t,i){var r=this.editor.getSelection(),o=new n(this.editor,{field:void 0!=e?e:"",value:void 0!=t?t:"",type:i});o.expand(!0),this.parent.appendChild(o),this.editor.highlighter.unhighlight(),o.focus("field");var s=this.editor.getSelection();this.editor._onAction("appendNodes",{nodes:[o],parent:this.parent,oldSelection:r,newSelection:s})},n.prototype._onChangeType=function(e){var t=this.type;if(e!=t){var i=this.editor.getSelection();this.changeType(e);var n=this.editor.getSelection();this.editor._onAction("changeType",{node:this,oldType:t,newType:e,oldSelection:i,newSelection:n})}},n.prototype.sort=function(e){if(this._hasChilds()){var t="desc"==e?-1:1,i="array"==this.type?"value":"field";this.hideChilds();var n=this.childs,o=this.sortOrder;this.childs=this.childs.concat(),this.childs.sort(function(e,n){return t*r(e[i],n[i])}),this.sortOrder=1==t?"asc":"desc",this.editor._onAction("sort",{node:this,oldChilds:n,oldSort:o,newChilds:this.childs,newSort:this.sortOrder}),this.showChilds()}},n.prototype.getAppend=function(){return this.append||(this.append=new l(this.editor),this.append.setParent(this)),this.append.getDom()},n.getNodeFromTarget=function(e){for(;e;){if(e.node)return e.node;e=e.parentNode}},n.blurNodes=function(e){if(!Array.isArray(e))return void n.blurNodes([e]);var t=e[0],i=t.parent,r=t.getIndex();i.childs[r+e.length]?i.childs[r+e.length].focus():i.childs[r-1]?i.childs[r-1].focus():i.focus()},n.prototype._nextSibling=function(){var e=this.parent.childs.indexOf(this);return this.parent.childs[e+1]||this.parent.append},n.prototype._previousNode=function(){var e=null,t=this.getDom();if(t&&t.parentNode){var i=t;do i=i.previousSibling,e=n.getNodeFromTarget(i);while(i&&e instanceof l&&!e.isVisible())}return e},n.prototype._nextNode=function(){var e=null,t=this.getDom();if(t&&t.parentNode){var i=t;do i=i.nextSibling,e=n.getNodeFromTarget(i);while(i&&e instanceof l&&!e.isVisible())}return e},n.prototype._firstNode=function(){var e=null,t=this.getDom();if(t&&t.parentNode){var i=t.parentNode.firstChild;e=n.getNodeFromTarget(i)}return e},n.prototype._lastNode=function(){var e=null,t=this.getDom();if(t&&t.parentNode){var i=t.parentNode.lastChild;for(e=n.getNodeFromTarget(i);i&&e instanceof l&&!e.isVisible();)i=i.previousSibling,e=n.getNodeFromTarget(i)}return e},n.prototype._previousElement=function(e){var t=this.dom;switch(e){case t.value:if(this.fieldEditable)return t.field;case t.field:if(this._hasChilds())return t.expand;case t.expand:return t.menu;case t.menu:if(t.drag)return t.drag;default:return null}},n.prototype._nextElement=function(e){var t=this.dom;switch(e){case t.drag:return t.menu;case t.menu:if(this._hasChilds())return t.expand;case t.expand:if(this.fieldEditable)return t.field;case t.field:if(!this._hasChilds())return t.value;default:return null}},n.prototype._getElementName=function(e){var t=this.dom;for(var i in t)if(t.hasOwnProperty(i)&&t[i]==e)return i;return null},n.prototype._hasChilds=function(){return"array"==this.type||"object"==this.type},n.TYPE_TITLES={auto:'Field type "auto". The field type is automatically determined from the value and can be a string, number, boolean, or null.',object:'Field type "object". An object contains an unordered set of key/value pairs.',array:'Field type "array". An array contains an ordered collection of values.',string:'Field type "string". Field type is not determined from the value, but always returned as string.'},n.prototype.showContextMenu=function(e,t){var i=this,r=n.TYPE_TITLES,s=[];if(this.editable.value&&s.push({text:"Type",title:"Change the type of this field",className:"jsoneditor-type-"+this.type,submenu:[{text:"Auto",className:"jsoneditor-type-auto"+("auto"==this.type?" jsoneditor-selected":""),title:r.auto,click:function(){i._onChangeType("auto")}},{text:"Array",className:"jsoneditor-type-array"+("array"==this.type?" jsoneditor-selected":""),title:r.array,click:function(){i._onChangeType("array")}},{text:"Object",className:"jsoneditor-type-object"+("object"==this.type?" jsoneditor-selected":""),title:r.object,click:function(){i._onChangeType("object")}},{text:"String",className:"jsoneditor-type-string"+("string"==this.type?" jsoneditor-selected":""),title:r.string,click:function(){i._onChangeType("string")}}]}),this._hasChilds()){var a="asc"==this.sortOrder?"desc":"asc";s.push({text:"Sort",title:"Sort the childs of this "+this.type,className:"jsoneditor-sort-"+a,click:function(){i.sort(a)},submenu:[{text:"Ascending",className:"jsoneditor-sort-asc",title:"Sort the childs of this "+this.type+" in ascending order",click:function(){i.sort("asc")}},{text:"Descending",className:"jsoneditor-sort-desc",title:"Sort the childs of this "+this.type+" in descending order",click:function(){i.sort("desc")}}]})}if(this.parent&&this.parent._hasChilds()){s.length&&s.push({type:"separator"});var l=i.parent.childs;i==l[l.length-1]&&s.push({text:"Append",title:"Append a new field with type 'auto' after this field (Ctrl+Shift+Ins)",submenuTitle:"Select the type of the field to be appended",className:"jsoneditor-append",click:function(){i._onAppend("","","auto")},submenu:[{text:"Auto",className:"jsoneditor-type-auto",title:r.auto,click:function(){i._onAppend("","","auto")}},{text:"Array",className:"jsoneditor-type-array",title:r.array,click:function(){i._onAppend("",[])}},{text:"Object",className:"jsoneditor-type-object",title:r.object,click:function(){i._onAppend("",{})}},{text:"String",className:"jsoneditor-type-string",title:r.string,click:function(){i._onAppend("","","string")}}]}),s.push({text:"Insert",title:"Insert a new field with type 'auto' before this field (Ctrl+Ins)",submenuTitle:"Select the type of the field to be inserted",className:"jsoneditor-insert",click:function(){i._onInsertBefore("","","auto")},submenu:[{text:"Auto",className:"jsoneditor-type-auto",title:r.auto,click:function(){i._onInsertBefore("","","auto")}},{text:"Array",className:"jsoneditor-type-array", +title:r.array,click:function(){i._onInsertBefore("",[])}},{text:"Object",className:"jsoneditor-type-object",title:r.object,click:function(){i._onInsertBefore("",{})}},{text:"String",className:"jsoneditor-type-string",title:r.string,click:function(){i._onInsertBefore("","","string")}}]}),this.editable.field&&(s.push({text:"Duplicate",title:"Duplicate this field (Ctrl+D)",className:"jsoneditor-duplicate",click:function(){n.onDuplicate(i)}}),s.push({text:"Remove",title:"Remove this field (Ctrl+Del)",className:"jsoneditor-remove",click:function(){n.onRemove(i)}}))}var c=new o(s,{close:t});c.show(e,this.editor.content)},n.prototype._getType=function(e){return e instanceof Array?"array":e instanceof Object?"object":"string"==typeof e&&"string"!=typeof this._stringCast(e)?"string":"auto"},n.prototype._stringCast=function(e){var t=e.toLowerCase(),i=Number(e),n=parseFloat(e);return""==e?"":"null"==t?null:"true"==t?!0:"false"==t?!1:isNaN(i)||isNaN(n)?e:i},n.prototype._escapeHTML=function(e){if("string"!=typeof e)return String(e);var t=String(e).replace(/&/g,"&").replace(//g,">").replace(/ /g,"  ").replace(/^ /," ").replace(/ $/," "),i=JSON.stringify(t),n=i.substring(1,i.length-1);return this.editor.options.escapeUnicode===!0&&(n=a.escapeUnicodeChars(n)),n},n.prototype._unescapeHTML=function(e){var t='"'+this._escapeJSON(e)+'"',i=a.parse(t);return i.replace(/</g,"<").replace(/>/g,">").replace(/ |\u00A0/g," ").replace(/&/g,"&")},n.prototype._escapeJSON=function(e){for(var t="",i=0;im)return-1;if(m>g)return 1}for(var v=0,w=Math.max(f.length,p.length);w>v;v++){if(n=!(f[v]||"").match(c)&&parseFloat(f[v])||f[v]||0,r=!(p[v]||"").match(c)&&parseFloat(p[v])||p[v]||0,isNaN(n)!==isNaN(r))return isNaN(n)?1:-1;if(typeof n!=typeof r&&(n+="",r+=""),r>n)return-1;if(n>r)return 1}return 0}},function(e,t,i){"use strict";function n(e){function t(e){this.editor=e,this.dom={}}return t.prototype=new e,t.prototype.getDom=function(){var e=this.dom;if(e.tr)return e.tr;this._updateEditability();var t=document.createElement("tr");if(t.node=this,e.tr=t,"tree"===this.editor.options.mode){e.tdDrag=document.createElement("td");var i=document.createElement("td");e.tdMenu=i;var n=document.createElement("button");n.className="jsoneditor-contextmenu",n.title="Click to open the actions menu (Ctrl+M)",e.menu=n,i.appendChild(e.menu)}var r=document.createElement("td"),o=document.createElement("div");return o.innerHTML="(empty)",o.className="jsoneditor-readonly",r.appendChild(o),e.td=r,e.text=o,this.updateDom(),t},t.prototype.updateDom=function(){var e=this.dom,t=e.td;t&&(t.style.paddingLeft=24*this.getLevel()+26+"px");var i=e.text;i&&(i.innerHTML="(empty "+this.parent.type+")");var n=e.tr;this.isVisible()?e.tr.firstChild||(e.tdDrag&&n.appendChild(e.tdDrag),e.tdMenu&&n.appendChild(e.tdMenu),n.appendChild(t)):e.tr.firstChild&&(e.tdDrag&&n.removeChild(e.tdDrag),e.tdMenu&&n.removeChild(e.tdMenu),n.removeChild(t))},t.prototype.isVisible=function(){return 0==this.parent.childs.length},t.prototype.showContextMenu=function(t,i){var n=this,r=e.TYPE_TITLES,s=[{text:"Append",title:"Append a new field with type 'auto' (Ctrl+Shift+Ins)",submenuTitle:"Select the type of the field to be appended",className:"jsoneditor-insert",click:function(){n._onAppend("","","auto")},submenu:[{text:"Auto",className:"jsoneditor-type-auto",title:r.auto,click:function(){n._onAppend("","","auto")}},{text:"Array",className:"jsoneditor-type-array",title:r.array,click:function(){n._onAppend("",[])}},{text:"Object",className:"jsoneditor-type-object",title:r.object,click:function(){n._onAppend("",{})}},{text:"String",className:"jsoneditor-type-string",title:r.string,click:function(){n._onAppend("","","string")}}]}],a=new o(s,{close:i});a.show(t,this.editor.content)},t.prototype.onEvent=function(e){var t=e.type,i=e.target||e.srcElement,n=this.dom,o=n.menu;if(i==o&&("mouseover"==t?this.editor.highlighter.highlight(this.parent):"mouseout"==t&&this.editor.highlighter.unhighlight()),"click"==t&&i==n.menu){var s=this.editor.highlighter;s.highlight(this.parent),s.lock(),r.addClassName(n.menu,"jsoneditor-selected"),this.showContextMenu(n.menu,function(){r.removeClassName(n.menu,"jsoneditor-selected"),s.unlock(),s.unhighlight()})}"keydown"==t&&this.onKeyDown(e)},t}var r=i(54),o=i(57);e.exports=n},function(e,t,i){"use strict";function n(e,t,i,n){for(var o={code:{text:"Code",title:"Switch to code highlighter",click:function(){n("code")}},form:{text:"Form",title:"Switch to form editor",click:function(){n("form")}},text:{text:"Text",title:"Switch to plain text editor",click:function(){n("text")}},tree:{text:"Tree",title:"Switch to tree editor",click:function(){n("tree")}},view:{text:"View",title:"Switch to tree view",click:function(){n("view")}}},s=[],a=0;a0){var o=i.length>l;if(o){i=i.slice(0,l);var a=this.validateSchema.errors.length-l;i.push("("+a+" more errors...)")}var c=document.createElement("div");c.innerHTML=''+i.map(function(e){var t;return t="string"==typeof e?'":"",''+t+""}).join("")+"
'+e+"
"+e.dataPath+""+e.message+"
",this.dom.validationErrors=c,this.frame.appendChild(c);var h=c.clientHeight;this.content.style.marginBottom=-h+"px",this.content.style.paddingBottom=h+"px"}if(this.aceEditor){var d=!1;this.aceEditor.resize(d)}},e.exports=[{mode:"text",mixin:a,data:"text",load:a.format},{mode:"code",mixin:a,data:"text",load:a.format}]},function(e,t,i){var n=i(64);i(67),i(69),i(70),e.exports=n},function(e,t,i){!function(){function e(e){var t=i;e&&(i[e]||(i[e]={}),t=i[e]),t.define&&t.define.packaged||(n.original=t.define,t.define=n,t.define.packaged=!0),t.acequire&&t.acequire.packaged||(o.original=t.acequire,t.acequire=o,t.acequire.packaged=!0)}var t="ace",i=function(){return this}();if(i||"undefined"==typeof window||(i=window),t||"undefined"==typeof acequirejs){var n=function(e,t,i){return"string"!=typeof e?void(n.original?n.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace())):(2==arguments.length&&(i=t),void(n.modules[e]||(n.payloads[e]=i,n.modules[e]=null)))};n.modules={},n.payloads={};var r=function(e,t,i){if("string"==typeof t){var n=a(e,t);if(void 0!=n)return i&&i(),n}else if("[object Array]"===Object.prototype.toString.call(t)){for(var r=[],s=0,l=t.length;l>s;++s){var c=a(e,t[s]);if(void 0==c&&o.original)return;r.push(c)}return i&&i.apply(null,r)||!0}},o=function(e,t){var i=r("",e,t);return void 0==i&&o.original?o.original.apply(this,arguments):i},s=function(e,t){if(-1!==t.indexOf("!")){var i=t.split("!");return s(e,i[0])+"!"+s(e,i[1])}if("."==t.charAt(0)){var n=e.split("/").slice(0,-1).join("/");for(t=n+"/"+t;-1!==t.indexOf(".")&&r!=t;){var r=t;t=t.replace(/\/\.\//,"/").replace(/[^\/]+\/\.\.\//,"")}}return t},a=function(e,t){t=s(e,t);var i=n.modules[t];if(!i){if(i=n.payloads[t],"function"==typeof i){var o={},a={id:t,uri:"",exports:o,packaged:!0},l=function(e,i){return r(t,e,i)},c=i(l,o,a);o=c||a.exports,n.modules[t]=o,delete n.payloads[t]}i=n.modules[t]=o||i}return i};e(t)}}(),ace.define("ace/lib/regexp",["require","exports","module"],function(e,t,i){"use strict";function n(e){return(e.global?"g":"")+(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.extended?"x":"")+(e.sticky?"y":"")}function r(e,t,i){if(Array.prototype.indexOf)return e.indexOf(t,i);for(var n=i||0;n1&&r(l,"")>-1&&(i=RegExp(this.source,o.replace.call(n(this),"g","")),o.replace.call(e.slice(l.index),i,function(){for(var e=1;el.index&&this.lastIndex--}return l},a||(RegExp.prototype.test=function(e){var t=o.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t}))}),ace.define("ace/lib/es5-shim",["require","exports","module"],function(e,t,i){function n(){}function r(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}function o(e){return e=+e,e!==e?e=0:0!==e&&e!==1/0&&e!==-(1/0)&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}Function.prototype.bind||(Function.prototype.bind=function(e){var t=this;if("function"!=typeof t)throw new TypeError("Function.prototype.bind called on incompatible "+t);var i=p.call(arguments,1),r=function(){if(this instanceof r){var n=t.apply(this,i.concat(p.call(arguments)));return Object(n)===n?n:this}return t.apply(e,i.concat(p.call(arguments)))};return t.prototype&&(n.prototype=t.prototype,r.prototype=new n,n.prototype=null),r});var s,a,l,c,h,d=Function.prototype.call,u=Array.prototype,f=Object.prototype,p=u.slice,m=d.bind(f.toString),g=d.bind(f.hasOwnProperty);if((h=g(f,"__defineGetter__"))&&(s=d.bind(f.__defineGetter__),a=d.bind(f.__defineSetter__),l=d.bind(f.__lookupGetter__),c=d.bind(f.__lookupSetter__)),2!=[1,2].splice(0).length)if(function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t,i=[];return i.splice.apply(i,e(20)),i.splice.apply(i,e(26)),t=i.length,i.splice(5,0,"XXX"),t+1==i.length,t+1==i.length?!0:void 0}()){var v=Array.prototype.splice;Array.prototype.splice=function(e,t){return arguments.length?v.apply(this,[void 0===e?0:e,void 0===t?this.length-e:t].concat(p.call(arguments,2))):[]}}else Array.prototype.splice=function(e,t){var i=this.length;e>0?e>i&&(e=i):void 0==e?e=0:0>e&&(e=Math.max(i+e,0)),i>e+t||(t=i-e);var n=this.slice(e,e+t),r=p.call(arguments,2),o=r.length;if(e===i)o&&this.push.apply(this,r);else{var s=Math.min(t,i-e),a=e+s,l=a+o-s,c=i-a,h=i-s;if(a>l)for(var d=0;c>d;++d)this[l+d]=this[a+d];else if(l>a)for(d=c;d--;)this[l+d]=this[a+d];if(o&&e===h)this.length=h,this.push.apply(this,r);else for(this.length=h+o,d=0;o>d;++d)this[e+d]=r[d]}return n};Array.isArray||(Array.isArray=function(e){return"[object Array]"==m(e)});var w=Object("a"),y="a"!=w[0]||!(0 in w);if(Array.prototype.forEach||(Array.prototype.forEach=function(e){var t=M(this),i=y&&"[object String]"==m(this)?this.split(""):t,n=arguments[1],r=-1,o=i.length>>>0;if("[object Function]"!=m(e))throw new TypeError;for(;++r>>0,r=Array(n),o=arguments[1];if("[object Function]"!=m(e))throw new TypeError(e+" is not a function");for(var s=0;n>s;s++)s in i&&(r[s]=e.call(o,i[s],s,t));return r}),Array.prototype.filter||(Array.prototype.filter=function(e){var t,i=M(this),n=y&&"[object String]"==m(this)?this.split(""):i,r=n.length>>>0,o=[],s=arguments[1];if("[object Function]"!=m(e))throw new TypeError(e+" is not a function");for(var a=0;r>a;a++)a in n&&(t=n[a],e.call(s,t,a,i)&&o.push(t));return o}),Array.prototype.every||(Array.prototype.every=function(e){var t=M(this),i=y&&"[object String]"==m(this)?this.split(""):t,n=i.length>>>0,r=arguments[1];if("[object Function]"!=m(e))throw new TypeError(e+" is not a function");for(var o=0;n>o;o++)if(o in i&&!e.call(r,i[o],o,t))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(e){var t=M(this),i=y&&"[object String]"==m(this)?this.split(""):t,n=i.length>>>0,r=arguments[1];if("[object Function]"!=m(e))throw new TypeError(e+" is not a function");for(var o=0;n>o;o++)if(o in i&&e.call(r,i[o],o,t))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(e){var t=M(this),i=y&&"[object String]"==m(this)?this.split(""):t,n=i.length>>>0;if("[object Function]"!=m(e))throw new TypeError(e+" is not a function");if(!n&&1==arguments.length)throw new TypeError("reduce of empty array with no initial value");var r,o=0;if(arguments.length>=2)r=arguments[1];else for(;;){if(o in i){r=i[o++];break}if(++o>=n)throw new TypeError("reduce of empty array with no initial value")}for(;n>o;o++)o in i&&(r=e.call(void 0,r,i[o],o,t));return r}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(e){var t=M(this),i=y&&"[object String]"==m(this)?this.split(""):t,n=i.length>>>0;if("[object Function]"!=m(e))throw new TypeError(e+" is not a function");if(!n&&1==arguments.length)throw new TypeError("reduceRight of empty array with no initial value");var r,o=n-1;if(arguments.length>=2)r=arguments[1];else for(;;){if(o in i){r=i[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}do o in this&&(r=e.call(void 0,r,i[o],o,t));while(o--);return r}),Array.prototype.indexOf&&-1==[0,1].indexOf(1,2)||(Array.prototype.indexOf=function(e){var t=y&&"[object String]"==m(this)?this.split(""):M(this),i=t.length>>>0;if(!i)return-1;var n=0;for(arguments.length>1&&(n=o(arguments[1])),n=n>=0?n:Math.max(0,i+n);i>n;n++)if(n in t&&t[n]===e)return n;return-1}),Array.prototype.lastIndexOf&&-1==[0,1].lastIndexOf(0,-3)||(Array.prototype.lastIndexOf=function(e){var t=y&&"[object String]"==m(this)?this.split(""):M(this),i=t.length>>>0;if(!i)return-1;var n=i-1;for(arguments.length>1&&(n=Math.min(n,o(arguments[1]))),n=n>=0?n:i-Math.abs(n);n>=0;n--)if(n in t&&e===t[n])return n;return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(e){return e.__proto__||(e.constructor?e.constructor.prototype:f)}),!Object.getOwnPropertyDescriptor){var A="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(e,t){if("object"!=typeof e&&"function"!=typeof e||null===e)throw new TypeError(A+e);if(g(e,t)){var i,n,r;if(i={enumerable:!0,configurable:!0},h){var o=e.__proto__;e.__proto__=f;var n=l(e,t),r=c(e,t);if(e.__proto__=o,n||r)return n&&(i.get=n),r&&(i.set=r),i}return i.value=e[t],i}}}if(Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(e){return Object.keys(e)}),!Object.create){var C;C=null===Object.prototype.__proto__?function(){return{__proto__:null}}:function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(e,t){var i;if(null===e)i=C();else{if("object"!=typeof e)throw new TypeError("typeof prototype["+typeof e+"] != 'object'");var n=function(){};n.prototype=e,i=new n,i.__proto__=e}return void 0!==t&&Object.defineProperties(i,t),i}}if(Object.defineProperty){var E=r({}),b="undefined"==typeof document||r(document.createElement("div"));if(!E||!b)var F=Object.defineProperty}if(!Object.defineProperty||F){var x="Property description must be an object: ",S="Object.defineProperty called on non-object: ",$="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(e,t,i){if("object"!=typeof e&&"function"!=typeof e||null===e)throw new TypeError(S+e);if("object"!=typeof i&&"function"!=typeof i||null===i)throw new TypeError(x+i);if(F)try{return F.call(Object,e,t,i)}catch(n){}if(g(i,"value"))if(h&&(l(e,t)||c(e,t))){var r=e.__proto__;e.__proto__=f,delete e[t],e[t]=i.value,e.__proto__=r}else e[t]=i.value;else{if(!h)throw new TypeError($);g(i,"get")&&s(e,t,i.get),g(i,"set")&&a(e,t,i.set)}return e}}Object.defineProperties||(Object.defineProperties=function(e,t){for(var i in t)g(t,i)&&Object.defineProperty(e,i,t[i]);return e}),Object.seal||(Object.seal=function(e){return e}),Object.freeze||(Object.freeze=function(e){return e});try{Object.freeze(function(){})}catch(D){Object.freeze=function(e){return function(t){return"function"==typeof t?t:e(t)}}(Object.freeze)}if(Object.preventExtensions||(Object.preventExtensions=function(e){return e}),Object.isSealed||(Object.isSealed=function(e){return!1}),Object.isFrozen||(Object.isFrozen=function(e){return!1}),Object.isExtensible||(Object.isExtensible=function(e){if(Object(e)===e)throw new TypeError;for(var t="";g(e,t);)t+="?";e[t]=!0;var i=g(e,t);return delete e[t],i}),!Object.keys){var k=!0,B=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],_=B.length;for(var L in{toString:null})k=!1;Object.keys=function O(e){if("object"!=typeof e&&"function"!=typeof e||null===e)throw new TypeError("Object.keys called on a non-object");var O=[];for(var t in e)g(e,t)&&O.push(t);if(k)for(var i=0,n=_;n>i;i++){var r=B[i];g(e,r)&&O.push(r)}return O}}Date.now||(Date.now=function(){return(new Date).getTime()});var R=" \n\x0B\f\r   ᠎              \u2028\u2029\ufeff";if(!String.prototype.trim||R.trim()){R="["+R+"]";var T=new RegExp("^"+R+R+"*"),P=new RegExp(R+R+"*$");String.prototype.trim=function(){return String(this).replace(T,"").replace(P,"")}}var M=function(e){if(null==e)throw new TypeError("can't convert "+e+" to object");return Object(e)}}),ace.define("ace/lib/fixoldbrowsers",["require","exports","module","ace/lib/regexp","ace/lib/es5-shim"],function(e,t,i){"use strict";e("./regexp"),e("./es5-shim")}),ace.define("ace/lib/dom",["require","exports","module"],function(e,t,i){"use strict";var n="http://www.w3.org/1999/xhtml";return t.getDocumentHead=function(e){return e||(e=document),e.head||e.getElementsByTagName("head")[0]||e.documentElement},t.createElement=function(e,t){return document.createElementNS?document.createElementNS(t||n,e):document.createElement(e)},t.hasCssClass=function(e,t){var i=(e.className||"").split(/\s+/g);return-1!==i.indexOf(t)},t.addCssClass=function(e,i){t.hasCssClass(e,i)||(e.className+=" "+i)},t.removeCssClass=function(e,t){for(var i=e.className.split(/\s+/g);;){var n=i.indexOf(t);if(-1==n)break;i.splice(n,1)}e.className=i.join(" ")},t.toggleCssClass=function(e,t){for(var i=e.className.split(/\s+/g),n=!0;;){var r=i.indexOf(t);if(-1==r)break;n=!1,i.splice(r,1)}return n&&i.push(t),e.className=i.join(" "),n},t.setCssClass=function(e,i,n){n?t.addCssClass(e,i):t.removeCssClass(e,i)},t.hasCssString=function(e,t){var i,n=0;if(t=t||document,t.createStyleSheet&&(i=t.styleSheets)){for(;n=0?parseFloat((r.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((r.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=(window.Controllers||window.controllers)&&"Gecko"===window.navigator.product,t.isOldGecko=t.isGecko&&parseInt((r.match(/rv\:(\d+)/)||[])[1],10)<4,t.isOpera=window.opera&&"[object Opera]"==Object.prototype.toString.call(window.opera),t.isWebKit=parseFloat(r.split("WebKit/")[1])||void 0,t.isChrome=parseFloat(r.split(" Chrome/")[1])||void 0,t.isAIR=r.indexOf("AdobeAIR")>=0,t.isIPad=r.indexOf("iPad")>=0,t.isTouchPad=r.indexOf("TouchPad")>=0,t.isChromeOS=r.indexOf(" CrOS ")>=0}}),ace.define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,i){"use strict";function n(e,t,i){var n=c(t);if(!s.isMac&&a){if(a.OSKey&&(n|=8),a.altGr){if(3==(3&n))return;a.altGr=0}if(18===i||17===i){var r="location"in t?t.location:t.keyLocation;if(17===i&&1===r)1==a[i]&&(l=t.timeStamp);else if(18===i&&3===n&&2===r){var h=t.timeStamp-l;50>h&&(a.altGr=!0)}}}if(i in o.MODIFIER_KEYS&&(i=-1),8&n&&i>=91&&93>=i&&(i=-1),!n&&13===i){var r="location"in t?t.location:t.keyLocation;if(3===r&&(e(t,n,-i),t.defaultPrevented))return}if(s.isChromeOS&&8&n){if(e(t,n,i),t.defaultPrevented)return;n&=-9}return n||i in o.FUNCTION_KEYS||i in o.PRINTABLE_KEYS?e(t,n,i):!1}function r(){a=Object.create(null),a.count=0,a.lastT=0}var o=e("./keys"),s=e("./useragent"),a=null,l=0; +t.addListener=function(e,t,i){if(e.addEventListener)return e.addEventListener(t,i,!1);if(e.attachEvent){var n=function(){i.call(e,window.event)};i._wrapper=n,e.attachEvent("on"+t,n)}},t.removeListener=function(e,t,i){return e.removeEventListener?e.removeEventListener(t,i,!1):void(e.detachEvent&&e.detachEvent("on"+t,i._wrapper||i))},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return"dblclick"==e.type?0:"contextmenu"==e.type||s.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},t.capture=function(e,i,n){function r(e){i&&i(e),n&&n(e),t.removeListener(document,"mousemove",i,!0),t.removeListener(document,"mouseup",r,!0),t.removeListener(document,"dragstart",r,!0)}return t.addListener(document,"mousemove",i,!0),t.addListener(document,"mouseup",r,!0),t.addListener(document,"dragstart",r,!0),r},t.addTouchMoveListener=function(e,i){if("ontouchmove"in e){var n,r;t.addListener(e,"touchstart",function(e){var t=e.changedTouches[0];n=t.clientX,r=t.clientY}),t.addListener(e,"touchmove",function(e){var t=1,o=e.changedTouches[0];e.wheelX=-(o.clientX-n)/t,e.wheelY=-(o.clientY-r)/t,n=o.clientX,r=o.clientY,i(e)})}},t.addMouseWheelListener=function(e,i){"onmousewheel"in e?t.addListener(e,"mousewheel",function(e){var t=8;void 0!==e.wheelDeltaX?(e.wheelX=-e.wheelDeltaX/t,e.wheelY=-e.wheelDeltaY/t):(e.wheelX=0,e.wheelY=-e.wheelDelta/t),i(e)}):"onwheel"in e?t.addListener(e,"wheel",function(e){var t=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*t||0,e.wheelY=e.deltaY*t||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=5*(e.deltaX||0),e.wheelY=5*(e.deltaY||0)}i(e)}):t.addListener(e,"DOMMouseScroll",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=5*(e.detail||0),e.wheelY=0):(e.wheelX=0,e.wheelY=5*(e.detail||0)),i(e)})},t.addMultiMouseDownListener=function(e,i,n,r){function o(e){if(0!==t.getButton(e)?d=0:e.detail>1?(d++,d>4&&(d=1)):d=1,s.isIE){var o=Math.abs(e.clientX-l)>5||Math.abs(e.clientY-c)>5;h&&!o||(d=1),h&&clearTimeout(h),h=setTimeout(function(){h=null},i[d-1]||600),1==d&&(l=e.clientX,c=e.clientY)}if(e._clicks=d,n[r]("mousedown",e),d>4)d=0;else if(d>1)return n[r](u[d],e)}function a(e){d=2,h&&clearTimeout(h),h=setTimeout(function(){h=null},i[d-1]||600),n[r]("mousedown",e),n[r](u[d],e)}var l,c,h,d=0,u={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){t.addListener(e,"mousedown",o),s.isOldIE&&t.addListener(e,"dblclick",a)})};var c=!s.isMac||!s.isOpera||"KeyboardEvent"in window?function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)}:function(e){return 0|(e.metaKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.ctrlKey?8:0)};if(t.getModifierString=function(e){return o.KEY_MODS[c(e)]},t.addCommandKeyListener=function(e,i){var o=t.addListener;if(s.isOldGecko||s.isOpera&&!("KeyboardEvent"in window)){var l=null;o(e,"keydown",function(e){l=e.keyCode}),o(e,"keypress",function(e){return n(i,e,l)})}else{var c=null;o(e,"keydown",function(e){var t=e.keyCode;a[t]=(a[t]||0)+1,91==t||92==t?a.OSKey=!0:a.OSKey&&e.timeStamp-a.lastT>200&&1==a.count&&r(),1==a[t]&&a.count++,a.lastT=e.timeStamp;var o=n(i,e,t);return c=e.defaultPrevented,o}),o(e,"keypress",function(e){c&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),c=null)}),o(e,"keyup",function(e){var t=e.keyCode;a[t]?a.count=Math.max(a.count-1,0):r(),91!=t&&92!=t||(a.OSKey=!1),a[t]=null}),a||(r(),o(window,"focus",r))}},"object"==typeof window&&window.postMessage&&!s.isOldIE){var h=1;t.nextTick=function(e,i){i=i||window;var n="zero-timeout-message-"+h;t.addListener(i,"message",function r(o){o.data==n&&(t.stopPropagation(o),t.removeListener(i,"message",r),e())}),i.postMessage(n,"*")}}t.nextFrame="object"==typeof window&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),ace.define("ace/lib/lang",["require","exports","module"],function(e,t,i){"use strict";t.last=function(e){return e[e.length-1]},t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){for(var i="";t>0;)1&t&&(i+=e),(t>>=1)&&(e+=e);return i};var n=/^\s\s*/,r=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(n,"")},t.stringTrimRight=function(e){return e.replace(r,"")},t.copyObject=function(e){var t={};for(var i in e)t[i]=e[i];return t},t.copyArray=function(e){for(var t=[],i=0,n=e.length;n>i;i++)e[i]&&"object"==typeof e[i]?t[i]=this.copyObject(e[i]):t[i]=e[i];return t},t.deepCopy=function o(e){if("object"!=typeof e||!e)return e;var t;if(Array.isArray(e)){t=[];for(var i=0;ii?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var s=(e("../lib/dom"),e("../lib/event"),e("../lib/useragent"),0);(function(){this.onMouseDown=function(e){var t=e.inSelection(),i=e.getDocumentPosition();this.mousedownEvent=e;var n=this.editor,r=e.getButton();if(0!==r){var o=n.getSelectionRange(),s=o.isEmpty();return n.$blockScrolling++,(s||1==r)&&n.selection.moveToPosition(i),n.$blockScrolling--,void(2==r&&n.textInput.onContextMenu(e.domEvent))}return this.mousedownEvent.time=Date.now(),!t||n.isFocused()||(n.focus(),!this.$focusTimout||this.$clickSelection||n.inMultiSelectMode)?(this.captureMouse(e),this.startSelect(i,e.domEvent._clicks>1),e.preventDefault()):(this.setState("focusWait"),void this.captureMouse(e))},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var i=this.editor;i.$blockScrolling++,this.mousedownEvent.getShiftKey()?i.selection.selectToPosition(e):t||i.selection.moveToPosition(e),t||this.select(),i.renderer.scroller.setCapture&&i.renderer.scroller.setCapture(),i.setStyle("ace_selecting"),this.setState("select"),i.$blockScrolling--},this.select=function(){var e,t=this.editor,i=t.renderer.screenToTextCoordinates(this.x,this.y);if(t.$blockScrolling++,this.$clickSelection){var n=this.$clickSelection.comparePoint(i);if(-1==n)e=this.$clickSelection.end;else if(1==n)e=this.$clickSelection.start;else{var r=o(this.$clickSelection,i);i=r.cursor,e=r.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(i),t.$blockScrolling--,t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,i=this.editor,n=i.renderer.screenToTextCoordinates(this.x,this.y),r=i.selection[e](n.row,n.column);if(i.$blockScrolling++,this.$clickSelection){var s=this.$clickSelection.comparePoint(r.start),a=this.$clickSelection.comparePoint(r.end);if(-1==s&&0>=a)t=this.$clickSelection.end,r.end.row==n.row&&r.end.column==n.column||(n=r.start);else if(1==a&&s>=0)t=this.$clickSelection.start,r.start.row==n.row&&r.start.column==n.column||(n=r.end);else if(-1==s&&1==a)n=r.end,t=r.start;else{var l=o(this.$clickSelection,n);n=l.cursor,t=l.anchor}i.selection.setSelectionAnchor(t.row,t.column)}i.selection.selectToPosition(n),i.$blockScrolling--,i.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=r(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>s||t-this.mousedownEvent.time>this.$focusTimout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),i=this.editor,n=i.session,r=n.getBracketRange(t);r?(r.isEmpty()&&(r.start.column--,r.end.column++),this.setState("select")):(r=i.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=r,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),i=this.editor;this.setState("selectByLines");var n=i.getSelectionRange();n.isMultiLine()&&n.contains(t.row,t.column)?(this.$clickSelection=i.selection.getLineRange(n.start.row),this.$clickSelection.end=i.selection.getLineRange(n.end.row).end):this.$clickSelection=i.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(!e.getAccelKey()){e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=e.domEvent.timeStamp,i=t-(this.$lastScrollTime||0),n=this.editor,r=n.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);return r||200>i?(this.$lastScrollTime=t,n.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()):void 0}},this.onTouchMove=function(e){var t=e.domEvent.timeStamp,i=t-(this.$lastScrollTime||0),n=this.editor,r=n.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);return r||200>i?(this.$lastScrollTime=t,n.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()):void 0}}).call(n.prototype),t.DefaultHandlers=n}),ace.define("ace/tooltip",["require","exports","module","ace/lib/oop","ace/lib/dom"],function(e,t,i){"use strict";function n(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}var r=(e("./lib/oop"),e("./lib/dom"));(function(){this.$init=function(){return this.$element=r.createElement("div"),this.$element.className="ace_tooltip",this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},this.getElement=function(){return this.$element||this.$init()},this.setText=function(e){r.setInnerText(this.getElement(),e)},this.setHtml=function(e){this.getElement().innerHTML=e},this.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},this.setClassName=function(e){r.addCssClass(this.getElement(),e)},this.show=function(e,t,i){null!=e&&this.setText(e),null!=t&&null!=i&&this.setPosition(t,i),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},this.hide=function(){this.isOpen&&(this.getElement().style.display="none",this.isOpen=!1)},this.getHeight=function(){return this.getElement().offsetHeight},this.getWidth=function(){return this.getElement().offsetWidth}}).call(n.prototype),t.Tooltip=n}),ace.define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event","ace/tooltip"],function(e,t,i){"use strict";function n(e){function t(){var t=d.getDocumentPosition().row,r=l.$annotations[t];if(!r)return i();var o=s.session.getLength();if(t==o){var a=s.renderer.pixelToScreenCoordinates(0,d.y).row,h=d.$pos;if(a>s.session.documentToScreenRow(h.row,h.column))return i()}if(u!=r)if(u=r.text.join("
"),c.setHtml(u),c.show(),s.on("mousewheel",i),e.$tooltipFollowsMouse)n(d);else{var f=d.domEvent.target,p=f.getBoundingClientRect(),m=c.getElement().style;m.left=p.right+"px",m.top=p.bottom+"px"}}function i(){h&&(h=clearTimeout(h)),u&&(c.hide(),u=null,s.removeEventListener("mousewheel",i))}function n(e){c.setPosition(e.x,e.y)}var s=e.editor,l=s.renderer.$gutterLayer,c=new r(s.container);e.editor.setDefaultHandler("guttermousedown",function(t){if(s.isFocused()&&0==t.getButton()){var i=l.getRegion(t);if("foldWidgets"!=i){var n=t.getDocumentPosition().row,r=s.session.selection;if(t.getShiftKey())r.selectTo(n,0);else{if(2==t.domEvent.detail)return s.selectAll(),t.preventDefault();e.$clickSelection=s.selection.getLineRange(n)}return e.setState("selectByLines"),e.captureMouse(t),t.preventDefault()}}});var h,d,u;e.editor.setDefaultHandler("guttermousemove",function(r){var s=r.domEvent.target||r.domEvent.srcElement;return o.hasCssClass(s,"ace_fold-widget")?i():(u&&e.$tooltipFollowsMouse&&n(r),d=r,void(h||(h=setTimeout(function(){h=null,d&&!e.isMousePressed?t():i()},50))))}),a.addListener(s.renderer.$gutter,"mouseout",function(e){d=null,u&&!h&&(h=setTimeout(function(){h=null,i()},50))}),s.on("changeSession",i)}function r(e){l.call(this,e)}var o=e("../lib/dom"),s=e("../lib/oop"),a=e("../lib/event"),l=e("../tooltip").Tooltip;s.inherits(r,l),function(){this.setPosition=function(e,t){var i=window.innerWidth||document.documentElement.clientWidth,n=window.innerHeight||document.documentElement.clientHeight,r=this.getWidth(),o=this.getHeight();e+=15,t+=15,e+r>i&&(e-=e+r-i),t+o>n&&(t-=20+o),l.prototype.setPosition.call(this,e,t)}}.call(r.prototype),t.GutterHandler=n}),ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,i){"use strict";var n=e("../lib/event"),r=e("../lib/useragent"),o=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){n.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){n.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(null!==this.$inSelection)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var i=this.getDocumentPosition();this.$inSelection=t.contains(i.row,i.column)}return this.$inSelection},this.getButton=function(){return n.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=r.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(o.prototype)}),ace.define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,i){"use strict";function n(e){function t(e,t){var i=Date.now(),n=!t||e.row!=t.row,o=!t||e.column!=t.column;if(!D||n||o)g.$blockScrolling+=1,g.moveCursorToPosition(e),g.$blockScrolling-=1,D=i,k={x:A,y:C};else{var s=r(k.x,k.y,A,C);s>h?D=null:i-D>=c&&(g.renderer.scrollCursorIntoView(),D=null)}}function i(e,t){var i=Date.now(),n=g.renderer.layerConfig.lineHeight,r=g.renderer.layerConfig.characterWidth,o=g.renderer.scroller.getBoundingClientRect(),s={x:{left:A-o.left,right:o.right-A},y:{top:C-o.top,bottom:o.bottom-C}},a=Math.min(s.x.left,s.x.right),c=Math.min(s.y.top,s.y.bottom),h={row:e.row,column:e.column};2>=a/r&&(h.column+=s.x.left=c/n&&(h.row+=s.y.top=l&&g.renderer.scrollCursorIntoView(h):$=i:$=null}function n(){var e=F;F=g.renderer.screenToTextCoordinates(A,C),t(F,e),i(F,e)}function d(){b=g.selection.toOrientedRange(),y=g.session.addMarker(b,"ace_selection",g.getSelectionStyle()),g.clearSelection(),g.isFocused()&&g.renderer.$cursorLayer.setBlinking(!1),clearInterval(E),n(),E=setInterval(n,20),_=0,s.addListener(document,"mousemove",f)}function u(){clearInterval(E),g.session.removeMarker(y),y=null,g.$blockScrolling+=1,g.selection.fromOrientedRange(b),g.$blockScrolling-=1,g.isFocused()&&!S&&g.renderer.$cursorLayer.setBlinking(!g.getReadOnly()),b=null,F=null,_=0,$=null,D=null,s.removeListener(document,"mousemove",f)}function f(){null==L&&(L=setTimeout(function(){null!=L&&y&&u()},20))}function p(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return"text/plain"==e||"Text"==e})}function m(e){var t=["copy","copymove","all","uninitialized"],i=["move","copymove","linkmove","all","uninitialized"],n=a.isMac?e.altKey:e.ctrlKey,r="uninitialized";try{r=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return n&&t.indexOf(r)>=0?o="copy":i.indexOf(r)>=0?o="move":t.indexOf(r)>=0&&(o="copy"),o}var g=e.editor,v=o.createElement("img");v.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",a.isOpera&&(v.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var w=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];w.forEach(function(t){e[t]=this[t]},this),g.addEventListener("mousedown",this.onMouseDown.bind(e));var y,A,C,E,b,F,x,S,$,D,k,B=g.container,_=0;this.onDragStart=function(e){if(this.cancelDrag||!B.draggable){var t=this;return setTimeout(function(){t.startSelect(),t.captureMouse(e)},0),e.preventDefault()}b=g.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=g.getReadOnly()?"copy":"copyMove",a.isOpera&&(g.container.appendChild(v),v.scrollTop=0),i.setDragImage&&i.setDragImage(v,0,0),a.isOpera&&g.container.removeChild(v),i.clearData(),i.setData("Text",g.session.getTextRange()),S=!0,this.setState("drag")},this.onDragEnd=function(e){if(B.draggable=!1,S=!1,this.setState(null),!g.getReadOnly()){var t=e.dataTransfer.dropEffect;x||"move"!=t||g.session.remove(g.getSelectionRange()),g.renderer.$cursorLayer.setBlinking(!0)}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){return!g.getReadOnly()&&p(e.dataTransfer)?(A=e.clientX,C=e.clientY,y||d(),_++,e.dataTransfer.dropEffect=x=m(e),s.preventDefault(e)):void 0},this.onDragOver=function(e){return!g.getReadOnly()&&p(e.dataTransfer)?(A=e.clientX,C=e.clientY,y||(d(),_++),null!==L&&(L=null),e.dataTransfer.dropEffect=x=m(e),s.preventDefault(e)):void 0},this.onDragLeave=function(e){return _--,0>=_&&y?(u(),x=null,s.preventDefault(e)):void 0},this.onDrop=function(e){if(F){var t=e.dataTransfer;if(S)switch(x){case"move":b=b.contains(F.row,F.column)?{start:F,end:F}:g.moveText(b,F);break;case"copy":b=g.moveText(b,F,!0)}else{var i=t.getData("Text");b={start:F,end:g.session.insert(F,i)},g.focus(),x=null}return u(),s.preventDefault(e)}},s.addListener(B,"dragstart",this.onDragStart.bind(e)),s.addListener(B,"dragend",this.onDragEnd.bind(e)),s.addListener(B,"dragenter",this.onDragEnter.bind(e)),s.addListener(B,"dragover",this.onDragOver.bind(e)),s.addListener(B,"dragleave",this.onDragLeave.bind(e)),s.addListener(B,"drop",this.onDrop.bind(e));var L=null}function r(e,t,i,n){return Math.sqrt(Math.pow(i-e,2)+Math.pow(n-t,2))}var o=e("../lib/dom"),s=e("../lib/event"),a=e("../lib/useragent"),l=200,c=200,h=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var i=a.isWin?"default":"move";e.renderer.setCursorStyle(i),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(a.isIE&&"dragReady"==this.state){var i=r(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);i>3&&t.dragDrop()}if("dragWait"===this.state){var i=r(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);i>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(this.$dragEnabled){this.mousedownEvent=e;var t=this.editor,i=e.inSelection(),n=e.getButton(),r=e.domEvent.detail||1;if(1===r&&0===n&&i){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;if("unselectable"in o&&(o.unselectable="on"),t.getDragDelay()){if(a.isWebKit){this.cancelDrag=!0;var s=t.container;s.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}}).call(n.prototype),t.DragdropHandler=n}),ace.define("ace/lib/net",["require","exports","module","ace/lib/dom"],function(e,t,i){"use strict";var n=e("./dom");t.get=function(e,t){var i=new XMLHttpRequest;i.open("GET",e,!0),i.onreadystatechange=function(){4===i.readyState&&t(i.responseText)},i.send(null)},t.loadScript=function(e,t){var i=n.getDocumentHead(),r=document.createElement("script");r.src=e,i.appendChild(r),r.onload=r.onreadystatechange=function(e,i){!i&&r.readyState&&"loaded"!=r.readyState&&"complete"!=r.readyState||(r=r.onload=r.onreadystatechange=null,i||t())}},t.qualifyURL=function(e){var t=document.createElement("a");return t.href=e,t.href}}),ace.define("ace/lib/event_emitter",["require","exports","module"],function(e,t,i){"use strict";var n={},r=function(){this.propagationStopped=!0},o=function(){this.defaultPrevented=!0};n._emit=n._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var i=this._eventRegistry[e]||[],n=this._defaultHandlers[e];if(i.length||n){"object"==typeof t&&t||(t={}),t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=r),t.preventDefault||(t.preventDefault=o),i=i.slice();for(var s=0;sv;v++){var y=g[v];0===y.name.indexOf("data-ace-")&&(s[o(y.name.replace(/^data-ace-/,""))]=y.value)}var A=m.match(/^(.*)\/ace(\-\w+)?\.js(\?|$)/);A&&(a=A[1])}}a&&(s.base=s.base||a,s.packaged=!0),s.basePath=s.base,s.workerPath=s.workerPath||s.base,s.modePath=s.modePath||s.base,s.themePath=s.themePath||s.base,delete s.base;for(var C in s)"undefined"!=typeof s[C]&&t.set(C,s[C])}}function o(e){return e.replace(/-(.)/g,function(e,t){return t.toUpperCase()})}var s=e("./lib/lang"),a=(e("./lib/oop"),e("./lib/net")),l=e("./lib/app_config").AppConfig;n.exports=t=new l;var c=function(){return this||"undefined"!=typeof window&&window}(),h={packaged:!1,workerPath:null,modePath:null,themePath:null,basePath:"",suffix:".js",$moduleUrls:{}};t.get=function(e){if(!h.hasOwnProperty(e))throw new Error("Unknown config key: "+e);return h[e]},t.set=function(e,t){if(!h.hasOwnProperty(e))throw new Error("Unknown config key: "+e);h[e]=t},t.all=function(){return s.copyObject(h)},t.moduleUrl=function(e,t){if(h.$moduleUrls[e])return h.$moduleUrls[e];var i=e.split("/");t=t||i[i.length-2]||"";var n="snippets"==t?"/":"-",r=i[i.length-1];if("worker"==t&&"-"==n){var o=new RegExp("^"+t+"[\\-_]|[\\-_]"+t+"$","g");r=r.replace(o,"")}(!r||r==t)&&i.length>1&&(r=i[i.length-2]);var s=h[t+"Path"];return null==s?s=h.basePath:"/"==n&&(t=n=""),s&&"/"!=s.slice(-1)&&(s+="/"),s+t+n+r+this.get("suffix")},t.setModuleUrl=function(e,t){return h.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(i,n){var r,o;Array.isArray(i)&&(o=i[0],i=i[1]);try{r=e(i)}catch(s){}if(r&&!t.$loading[i])return n&&n(r);if(t.$loading[i]||(t.$loading[i]=[]),t.$loading[i].push(n),!(t.$loading[i].length>1)){var l=function(){e([i],function(e){t._emit("load.module",{name:i,module:e});var n=t.$loading[i];t.$loading[i]=null,n.forEach(function(t){t&&t(e)})})};return t.get("packaged")?void a.loadScript(t.moduleUrl(i,o),l):l()}},r(!0),t.init=r}),ace.define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop_handler","ace/config"],function(e,t,i){"use strict";var n=e("../lib/event"),r=e("../lib/useragent"),o=e("./default_handlers").DefaultHandlers,s=e("./default_gutter_handler").GutterHandler,a=e("./mouse_event").MouseEvent,l=e("./dragdrop_handler").DragdropHandler,c=e("../config"),h=function(e){var t=this;this.editor=e,new o(this),new s(this),new l(this);var i=function(t){var i=!document.hasFocus||!document.hasFocus()||!e.isFocused()&&document.activeElement==(e.textInput&&e.textInput.getElement());i&&window.focus(),e.focus()},a=e.renderer.getMouseEventTarget();n.addListener(a,"click",this.onMouseEvent.bind(this,"click")),n.addListener(a,"mousemove",this.onMouseMove.bind(this,"mousemove")),n.addMultiMouseDownListener([a,e.renderer.scrollBarV&&e.renderer.scrollBarV.inner,e.renderer.scrollBarH&&e.renderer.scrollBarH.inner,e.textInput&&e.textInput.getElement()].filter(Boolean),[400,300,250],this,"onMouseEvent"),n.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel")),n.addTouchMoveListener(e.container,this.onTouchMove.bind(this,"touchmove"));var c=e.renderer.$gutter;n.addListener(c,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),n.addListener(c,"click",this.onMouseEvent.bind(this,"gutterclick")),n.addListener(c,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),n.addListener(c,"mousemove",this.onMouseEvent.bind(this,"guttermousemove")),n.addListener(a,"mousedown",i),n.addListener(c,"mousedown",i),r.isIE&&e.renderer.scrollBarV&&(n.addListener(e.renderer.scrollBarV.element,"mousedown",i),n.addListener(e.renderer.scrollBarH.element,"mousedown",i)),e.on("mousemove",function(i){if(!t.state&&!t.$dragDelay&&t.$dragEnabled){var n=e.renderer.screenToTextCoordinates(i.x,i.y),r=e.session.selection.getRange(),o=e.renderer;!r.isEmpty()&&r.insideStart(n.row,n.column)?o.setCursorStyle("default"):o.setCursorStyle("")}})};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new a(t,this.editor))},this.onMouseMove=function(e,t){var i=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;i&&i.length&&this.editor._emit(e,new a(t,this.editor))},this.onMouseWheel=function(e,t){var i=new a(t,this.editor);i.speed=2*this.$scrollSpeed,i.wheelX=t.wheelX,i.wheelY=t.wheelY,this.editor._emit(e,i)},this.onTouchMove=function(e,t){var i=new a(t,this.editor);i.speed=1,i.wheelX=t.wheelX,i.wheelY=t.wheelY,this.editor._emit(e,i)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var i=this.editor.renderer;i.$keepTextAreaAtCursor&&(i.$keepTextAreaAtCursor=null);var o=this,s=function(e){if(e){if(r.isWebKit&&!e.which&&o.releaseMouse)return o.releaseMouse();o.x=e.clientX,o.y=e.clientY,t&&t(e),o.mouseEvent=new a(e,o.editor),o.$mouseMoved=!0}},l=function(e){clearInterval(h),c(),o[o.state+"End"]&&o[o.state+"End"](e),o.state="",null==i.$keepTextAreaAtCursor&&(i.$keepTextAreaAtCursor=!0,i.$moveTextAreaToCursor()),o.isMousePressed=!1,o.$onCaptureMouseMove=o.releaseMouse=null,e&&o.onMouseEvent("mouseup",e)},c=function(){o[o.state]&&o[o.state](),o.$mouseMoved=!1};if(r.isOldIE&&"dblclick"==e.domEvent.type)return setTimeout(function(){l(e)});o.$onCaptureMouseMove=s,o.releaseMouse=n.capture(this.editor.container,s,l);var h=setInterval(c,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){t&&t.domEvent&&"contextmenu"!=t.domEvent.type||(this.editor.off("nativecontextmenu",e),t&&t.domEvent&&n.stopEvent(t.domEvent))}.bind(this);setTimeout(e,10),this.editor.on("nativecontextmenu",e)}}).call(h.prototype),c.defineOptions(h.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:r.isMac?150:0},dragEnabled:{initialValue:!0},focusTimout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=h}),ace.define("ace/mouse/fold_handler",["require","exports","module"],function(e,t,i){"use strict";function n(e){e.on("click",function(t){var i=t.getDocumentPosition(),n=e.session,r=n.getFoldAt(i.row,i.column,1);r&&(t.getAccelKey()?n.removeFold(r):n.expandFold(r),t.stop())}),e.on("gutterclick",function(t){var i=e.renderer.$gutterLayer.getRegion(t);if("foldWidgets"==i){var n=t.getDocumentPosition().row,r=e.session;r.foldWidgets&&r.foldWidgets[n]&&e.session.onFoldWidgetClick(n,t),e.isFocused()||e.focus(),t.stop()}}),e.on("gutterdblclick",function(t){var i=e.renderer.$gutterLayer.getRegion(t);if("foldWidgets"==i){var n=t.getDocumentPosition().row,r=e.session,o=r.getParentFoldRangeData(n,!0),s=o.range||o.firstRange;if(s){n=s.start.row;var a=r.getFoldAt(n,r.getLine(n).length,1);a?r.removeFold(a):(r.addFold("...",s),e.renderer.scrollCursorIntoView({row:s.start.row,column:0}))}t.stop()}})}t.FoldHandler=n}),ace.define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,i){"use strict";var n=e("../lib/keys"),r=e("../lib/event"),o=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]!=e){for(;t[t.length-1]&&t[t.length-1]!=this.$defaultHandler;)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)}},this.addKeyboardHandler=function(e,t){if(e){"function"!=typeof e||e.handleKeyboard||(e.handleKeyboard=e);var i=this.$handlers.indexOf(e);-1!=i&&this.$handlers.splice(i,1),void 0==t?this.$handlers.push(e):this.$handlers.splice(t,0,e),-1==i&&e.attach&&e.attach(this.$editor)}},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return-1==t?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(i){return i.getStatusText&&i.getStatusText(t,e)||""}).filter(Boolean).join(" ")},this.$callKeyboardHandlers=function(e,t,i,n){for(var o,s=!1,a=this.$editor.commands,l=this.$handlers.length;l--&&(o=this.$handlers[l].handleKeyboard(this.$data,e,t,i,n),!(o&&o.command&&(s="null"==o.command?!0:a.exec(o.command,this.$editor,o.args,n),s&&n&&-1!=e&&1!=o.passEvent&&1!=o.command.passEvent&&r.stopEvent(n),s))););return s||-1!=e||(o={command:"insertstring"},s=a.exec("insertstring",this.$editor,t)),s&&this.$editor._signal("keyboardActivity",o),s},this.onCommandKey=function(e,t,i){var r=n.keyCodeToString(i);this.$callKeyboardHandlers(t,r,i,e)},this.onTextInput=function(e){this.$callKeyboardHandlers(-1,e)}}).call(o.prototype),t.KeyBinding=o}),ace.define("ace/range",["require","exports","module"],function(e,t,i){"use strict";var n=function(e,t){return e.row-t.row||e.column-t.column},r=function(e,t,i,n){this.start={row:e,column:t},this.end={row:i,column:n}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return 0==this.compare(e,t)},this.compareRange=function(e){var t,i=e.end,n=e.start;return t=this.compare(i.row,i.column),1==t?(t=this.compare(n.row,n.column),1==t?2:0==t?1:0):-1==t?-2:(t=this.compare(n.row,n.column),-1==t?-1:1==t?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return 0==this.comparePoint(e.start)&&0==this.comparePoint(e.end)},this.intersects=function(e){var t=this.compareRange(e);return-1==t||0==t||1==t},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){"object"==typeof e?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){"object"==typeof e?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return 0==this.compare(e,t)?!this.isEnd(e,t)&&!this.isStart(e,t):!1},this.insideStart=function(e,t){return 0==this.compare(e,t)?!this.isEnd(e,t):!1},this.insideEnd=function(e,t){return 0==this.compare(e,t)?!this.isStart(e,t):!1},this.compare=function(e,t){return this.isMultiLine()||e!==this.start.row?ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0:tthis.end.column?1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var i={row:t+1,column:0};else if(this.end.rowt)var n={row:t+1,column:0};else if(this.start.rowt.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.isEmpty()?s.fromPoints(t,t):this.isBackwards()?s.fromPoints(t,e):s.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){var e=this.doc.getLength()-1;this.setSelectionAnchor(0,0),this.moveCursorTo(e,this.doc.getLine(e).length)},this.setRange=this.setSelectionRange=function(e,t){t?(this.setSelectionAnchor(e.end.row,e.end.column),this.selectTo(e.start.row,e.start.column)):(this.setSelectionAnchor(e.start.row,e.start.column),this.selectTo(e.end.row,e.end.column)),this.getRange().isEmpty()&&(this.$isEmpty=!0),this.$desiredColumn=null},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if("undefined"==typeof t){var i=e||this.lead;e=i.row,t=i.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var i,n="number"==typeof e?e:this.lead.row,r=this.session.getFoldLine(n);return r?(n=r.start.row,i=r.end.row):i=n,t===!0?new s(n,0,i,this.session.getLine(i).length):new s(n,0,i+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var e,t=this.lead.getPosition();if(e=this.session.getFoldAt(t.row,t.column,-1))this.moveCursorTo(e.start.row,e.start.column);else if(0===t.column)t.row>0&&this.moveCursorTo(t.row-1,this.doc.getLine(t.row-1).length);else{var i=this.session.getTabSize();this.session.isTabStop(t)&&this.doc.getLine(t.row).slice(t.column-i,t.column).split(" ").length-1==i?this.moveCursorBy(0,-i):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e,t=this.lead.getPosition();if(e=this.session.getFoldAt(t.row,t.column,1))this.moveCursorTo(e.end.row,e.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=n)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e,t=this.lead.row,i=this.lead.column,n=this.doc.getLine(t),r=n.substring(i);this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var o=this.session.getFoldAt(t,i,1);return o?void this.moveCursorTo(o.end.row,o.end.column):((e=this.session.nonTokenRe.exec(r))&&(i+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(i)),i>=n.length?(this.moveCursorTo(t,n.length),this.moveCursorRight(),void(t=i?(this.moveCursorTo(t,0),this.moveCursorLeft(),void(t>0&&this.moveCursorWordLeft())):((o=this.session.tokenRe.exec(s))&&(i-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0),void this.moveCursorTo(t,i))},this.$shortWordEndIndex=function(e){var t,i,n=0,r=/\s/,o=this.session.tokenRe;if(o.lastIndex=0,t=this.session.tokenRe.exec(e))n=this.session.tokenRe.lastIndex;else{for(;(i=e[n])&&r.test(i);)n++;if(1>n)for(o.lastIndex=0;(i=e[n])&&!o.test(i);)if(o.lastIndex=0,n++,r.test(i)){if(n>2){n--;break}for(;(i=e[n])&&r.test(i);)n++;if(n>2)break}}return o.lastIndex=0,n},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,i=this.doc.getLine(e),n=i.substring(t),r=this.session.getFoldAt(e,t,1);if(r)return this.moveCursorTo(r.end.row,r.end.column);if(t==i.length){var o=this.doc.getLength();do e++,n=this.doc.getLine(e);while(o>e&&/^\s*$/.test(n));/^\s+/.test(n)||(n=""),t=0}var s=this.$shortWordEndIndex(n);this.moveCursorTo(e,t+s)},this.moveCursorShortWordLeft=function(){var e,t=this.lead.row,i=this.lead.column;if(e=this.session.getFoldAt(t,i,-1))return this.moveCursorTo(e.start.row,e.start.column);var n=this.session.getLine(t).substring(0,i);if(0===i){do t--,n=this.doc.getLine(t);while(t>0&&/^\s*$/.test(n));i=n.length,/\s+$/.test(n)||(n="")}var o=r.stringReverse(n),s=this.$shortWordEndIndex(o);return this.moveCursorTo(t,i-s)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var i=this.session.documentToScreenPosition(this.lead.row,this.lead.column);0===t&&(this.$desiredColumn?i.column=this.$desiredColumn:this.$desiredColumn=i.column);var n=this.session.screenToDocumentPosition(i.row+e,i.column);0!==e&&0===t&&n.row===this.lead.row&&n.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[n.row]&&(n.row>0||e>0)&&n.row++,this.moveCursorTo(n.row,n.column+t,0===t)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,i){var n=this.session.getFoldAt(e,t,1);n&&(e=n.start.row,t=n.start.column),this.$keepDesiredColumnOnChange=!0,this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,i||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,i){var n=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(n.row,n.column,i)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var i=this.getCursor();return s.fromPoints(t,i)}catch(n){return s.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(void 0==e.start){if(this.rangeList){this.toSingleRange(e[0]);for(var t=e.length;t--;){var i=s.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(i.cursor=i.start),this.addRange(i,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(a.prototype),t.Selection=a}),ace.define("ace/tokenizer",["require","exports","module","ace/config"],function(e,t,i){"use strict";var n=e("./config"),r=2e3,o=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){for(var i=this.states[t],n=[],r=0,o=this.matchMappings[t]={defaultToken:"text"},s="g",a=[],l=0;l1?c.onMatch=this.$applyToken:c.onMatch=c.token),d>1&&(/\\\d/.test(c.regex)?h=c.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+r+1)}):(d=1,h=this.removeCapturingGroups(c.regex)),c.splitRegex||"string"==typeof c.token||a.push(c)),o[r]=l,r+=d,n.push(h),c.onMatch||(c.onMatch=null)}}n.length||(o[0]=0,n.push("$")),a.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,s)},this),this.regExps[t]=new RegExp("("+n.join(")|(")+")|($)",s)}};(function(){this.$setMaxTokenCount=function(e){r=0|e},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),i=this.token.apply(this,t);if("string"==typeof i)return[{type:i,value:e}];for(var n=[],r=0,o=i.length;o>r;r++)t[r]&&(n[n.length]={type:i[r],value:t[r]});return n},this.$arrayTokens=function(e){if(!e)return[];var t=this.splitRegex.exec(e);if(!t)return"text";for(var i=[],n=this.tokenArray,r=0,o=n.length;o>r;r++)t[r+1]&&(i[i.length]={type:n[r],value:t[r+1]});return i},this.removeCapturingGroups=function(e){var t=e.replace(/\[(?:\\.|[^\]])*?\]|\\.|\(\?[:=!]|(\()/g,function(e,t){return t?"(?:":e});return t},this.createSplitterRegexp=function(e,t){if(-1!=e.indexOf("(?=")){var i=0,n=!1,r={};e.replace(/(\\.)|(\((?:\?[=!])?)|(\))|([\[\]])/g,function(e,t,o,s,a,l){return n?n="]"!=a:a?n=!0:s?(i==r.stack&&(r.end=l+1,r.stack=-1),i--):o&&(i++,1!=o.length&&(r.stack=i,r.start=l)),e}),null!=r.end&&/^\)*$/.test(e.substr(r.end))&&(e=e.substring(0,r.start)+e.substr(r.end))}return"^"!=e.charAt(0)&&(e="^"+e),"$"!=e.charAt(e.length-1)&&(e+="$"),new RegExp(e,(t||"").replace("g",""))},this.getLineTokens=function(e,t){if(t&&"string"!=typeof t){var i=t.slice(0);t=i[0],"#tmp"===t&&(i.shift(),t=i.shift())}else var i=[];var n=t||"start",o=this.states[n];o||(n="start",o=this.states[n]);var s=this.matchMappings[n],a=this.regExps[n];a.lastIndex=0;for(var l,c=[],h=0,d=0,u={type:null,value:""};l=a.exec(e);){var f=s.defaultToken,p=null,m=l[0],g=a.lastIndex;if(g-m.length>h){var v=e.substring(h,g-m.length);u.type==f?u.value+=v:(u.type&&c.push(u),u={type:f,value:v})}for(var w=0;wr){for(d>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});h1&&i[0]!==n&&i.unshift("#tmp",n),{tokens:c,state:i.length?i:n}},this.reportError=n.reportError}).call(o.prototype),t.Tokenizer=o}),ace.define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,i){"use strict";var n=e("../lib/lang"),r=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(t)for(var i in e){for(var n=e[i],r=0;r=this.$rowTokens.length;){if(this.$row+=1,e||(e=this.$session.getLength()),this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,i=e[t].start;if(void 0!==i)return i;for(i=0;t>0;)t-=1,i+=e[t].value.length;return i},this.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}}}).call(n.prototype),t.TokenIterator=n}),ace.define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour","ace/unicode","ace/lib/lang","ace/token_iterator","ace/range"],function(e,t,i){"use strict";var n=e("../tokenizer").Tokenizer,r=e("./text_highlight_rules").TextHighlightRules,o=e("./behaviour").Behaviour,s=e("../unicode"),a=e("../lib/lang"),l=e("../token_iterator").TokenIterator,c=e("../range").Range,h=function(){this.HighlightRules=r,this.$behaviour=new o};(function(){this.tokenRe=new RegExp("^["+s.packages.L+s.packages.Mn+s.packages.Mc+s.packages.Nd+s.packages.Pc+"\\$_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+s.packages.L+s.packages.Mn+s.packages.Mc+s.packages.Nd+s.packages.Pc+"\\$_]|\\s])+","g"),this.getTokenizer=function(){return this.$tokenizer||(this.$highlightRules=this.$highlightRules||new this.HighlightRules,this.$tokenizer=new n(this.$highlightRules.getRules())),this.$tokenizer},this.lineCommentStart="",this.blockComment="",this.toggleCommentLines=function(e,t,i,n){function r(e){for(var t=i;n>=t;t++)e(o.getLine(t),t)}var o=t.doc,s=!0,l=!0,c=1/0,h=t.getTabSize(),d=!1;if(this.lineCommentStart){if(Array.isArray(this.lineCommentStart))var u=this.lineCommentStart.map(a.escapeRegExp).join("|"),f=this.lineCommentStart[0];else var u=a.escapeRegExp(this.lineCommentStart),f=this.lineCommentStart;u=new RegExp("^(\\s*)(?:"+u+") ?"),d=t.getUseSoftTabs();var p=function(e,t){var i=e.match(u);if(i){var n=i[1].length,r=i[0].length;w(e,n,r)||" "!=i[0][r-1]||r--,o.removeInLine(t,n,r)}},m=f+" ",g=function(e,t){s&&!/\S/.test(e)||(w(e,c,c)?o.insertInLine({row:t,column:c},m):o.insertInLine({row:t,column:c},f))},v=function(e,t){return u.test(e)},w=function(e,t,i){for(var n=0;t--&&" "==e.charAt(t);)n++;if(n%h!=0)return!1;for(var n=0;" "==e.charAt(i++);)n++;return h>2?n%h!=h-1:n%h==0}}else{if(!this.blockComment)return!1;var f=this.blockComment.start,y=this.blockComment.end,u=new RegExp("^(\\s*)(?:"+a.escapeRegExp(f)+")"),A=new RegExp("(?:"+a.escapeRegExp(y)+")\\s*$"),g=function(e,t){v(e,t)||s&&!/\S/.test(e)||(o.insertInLine({row:t,column:e.length},y),o.insertInLine({row:t,column:c},f))},p=function(e,t){var i;(i=e.match(A))&&o.removeInLine(t,e.length-i[0].length,e.length),(i=e.match(u))&&o.removeInLine(t,i[1].length,i[0].length)},v=function(e,i){if(u.test(e))return!0;for(var n=t.getTokens(i),r=0;ri&&(c=i),l&&!v(e,t)&&(l=!1)):C>e.length&&(C=e.length)}),c==1/0&&(c=C,s=!1,l=!1),d&&c%h!=0&&(c=Math.floor(c/h)*h),r(l?p:g)},this.toggleBlockComment=function(e,t,i,n){var r=this.blockComment;if(r){!r.start&&r[0]&&(r=r[0]);var o,s,a=new l(t,n.row,n.column),h=a.getCurrentToken(),d=(t.selection,t.selection.toOrientedRange());if(h&&/comment/.test(h.type)){for(var u,f;h&&/comment/.test(h.type);){var p=h.value.indexOf(r.start);if(-1!=p){var m=a.getCurrentTokenRow(),g=a.getCurrentTokenColumn()+p;u=new c(m,g,m,g+r.start.length);break}h=a.stepBackward()}for(var a=new l(t,n.row,n.column),h=a.getCurrentToken();h&&/comment/.test(h.type);){var p=h.value.indexOf(r.end);if(-1!=p){var m=a.getCurrentTokenRow(),g=a.getCurrentTokenColumn()+p;f=new c(m,g,m,g+r.end.length);break}h=a.stepForward()}f&&t.remove(f),u&&(t.remove(u),o=u.start.row,s=-r.start.length)}else s=r.start.length,o=i.start.row,t.insert(i.end,r.end),t.insert(i.start,r.start);d.start.row==o&&(d.start.column+=s),d.end.row==o&&(d.end.column+=s),t.selection.fromOrientedRange(d)}},this.getNextLineIndent=function(e,t,i){return this.$getIndent(t)},this.checkOutdent=function(e,t,i){return!1},this.autoOutdent=function(e,t,i){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)e[t]&&(this.$embeds.push(t),this.$modes[t]=new e[t]);for(var i=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"],t=0;to;o++)if("string"==typeof r[o].token)/keyword|support|storage/.test(r[o].token)&&i.push(r[o].regex);else if("object"==typeof r[o].token)for(var a=0,l=r[o].token.length;l>a;a++)if(/keyword|support|storage/.test(r[o].token[a])){var n=r[o].regex.match(/\(.+?\)/g)[a];i.push(n.substr(1,n.length-2))}this.completionKeywords=i}return e?i.concat(this.$keywordList||[]):this.$keywordList},this.$createKeywordList=function(){return this.$highlightRules||this.getTokenizer(),this.$keywordList=this.$highlightRules.$keywordList||[]},this.getCompletions=function(e,t,i,n){var r=this.$keywordList||this.$createKeywordList();return r.map(function(e){return{name:e,value:e,score:0,meta:"keyword"}})},this.$id="ace/mode/text"}).call(h.prototype),t.Mode=h}),ace.define("ace/apply_delta",["require","exports","module"],function(e,t,i){"use strict";t.applyDelta=function(e,t,i){var n=t.start.row,r=t.start.column,o=e[n]||"";switch(t.action){case"insert":var s=t.lines;if(1===s.length)e[n]=o.substring(0,r)+t.lines[0]+o.substring(r);else{var a=[n,1].concat(t.lines);e.splice.apply(e,a),e[n]=o.substring(0,r)+e[n],e[n+t.lines.length-1]+=o.substring(r)}break;case"remove":var l=t.end.column,c=t.end.row;n===c?e[n]=o.substring(0,r)+o.substring(l):e.splice(n,c-n+1,o.substring(0,r)+e[c].substring(l))}}}),ace.define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,i){"use strict";var n=e("./lib/oop"),r=e("./lib/event_emitter").EventEmitter,o=t.Anchor=function(e,t,i){this.$onChange=this.onChange.bind(this),this.attach(e),"undefined"==typeof i?this.setPosition(t.row,t.column):this.setPosition(t,i)};(function(){function e(e,t,i){var n=i?e.column<=t.column:e.columnthis.row)){var i=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(i.row,i.column,!0)}},this.setPosition=function(e,t,i){var n;if(n=i?{row:e,column:t}:this.$clipPositionToDocument(e,t),this.row!=n.row||this.column!=n.column){var r={row:this.row,column:this.column};this.row=n.row,this.column=n.column,this._signal("change",{old:r,value:n})}},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var i={};return e>=this.document.getLength()?(i.row=Math.max(0,this.document.getLength()-1),i.column=this.document.getLine(i.row).length):0>e?(i.row=0,i.column=0):(i.row=e,i.column=Math.min(this.document.getLine(i.row).length,Math.max(0,t))),0>t&&(i.column=0),i}}).call(o.prototype)}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,i){"use strict";var n=e("./lib/oop"),r=e("./apply_delta").applyDelta,o=e("./lib/event_emitter").EventEmitter,s=e("./range").Range,a=e("./anchor").Anchor,l=function(e){this.$lines=[""],0===e.length?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){n.implement(this,o),this.setValue=function(e){var t=this.getLength()-1;this.remove(new s(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new a(this,e,t)},0==="aaa".split(/a/).length?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){this.$newLineMode!==e&&(this.$newLineMode=e,this._signal("changeNewLineMode"))},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return"\r\n"==e||"\r"==e||"\n"==e},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var i=t.length-1;e.end.row-e.start.row==i&&(t[i]=t[i].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var i=this.clippedPos(e.row,e.column),n=this.pos(e.row,e.column+t.length);return this.applyDelta({start:i,end:n,action:"insert",lines:[t]},!0),this.clonePos(n)},this.clippedPos=function(e,t){var i=this.getLength();void 0===e?e=i:0>e?e=0:e>=i&&(e=i-1,t=void 0);var n=this.getLine(e);return void 0==t&&(t=n.length),t=Math.min(Math.max(t,0),n.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var i=0;e0,n=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){if(e instanceof s||(e=s.fromPoints(e.start,e.end)),0===t.length&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var i;return i=t?this.insert(e.start,t):e.start},this.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var i="insert"==e.action;(i?e.lines.length<=1&&!e.lines[0]:!s.comparePoints(e.start,e.end))||(i&&e.lines.length>2e4&&this.$splitAndapplyLargeDelta(e,2e4),r(this.$lines,e,t),this._signal("change",e))},this.$splitAndapplyLargeDelta=function(e,t){for(var i=e.lines,n=i.length,r=e.start.row,o=e.start.column,s=0,a=0;;){s=a,a+=t-1;var l=i.slice(s,a);if(a>n){e.lines=l,e.start.row=r+s,e.start.column=o;break}l.push(""),this.applyDelta({start:this.pos(r+s,o),end:this.pos(r+a,o=0),action:e.action,lines:l},!0)}},this.revertDelta=function(e){this.applyDelta({start:this.clonePos(e.start),end:this.clonePos(e.end),action:"insert"==e.action?"remove":"insert",lines:e.lines.slice()})},this.indexToPosition=function(e,t){for(var i=this.$lines||this.getAllLines(),n=this.getNewLineCharacter().length,r=t||0,o=i.length;o>r;r++)if(e-=i[r].length+n,0>e)return{row:r,column:e+i[r].length+n};return{row:o-1,column:i[o-1].length}},this.positionToIndex=function(e,t){for(var i=this.$lines||this.getAllLines(),n=this.getNewLineCharacter().length,r=0,o=Math.min(e.row,i.length),s=t||0;o>s;++s)r+=i[s].length+n;return r+e.column}}).call(l.prototype),t.Document=l}),ace.define("ace/background_tokenizer",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,i){"use strict";var n=e("./lib/oop"),r=e("./lib/event_emitter").EventEmitter,o=function(e,t){this.running=!1,this.lines=[],this.states=[],this.currentLine=0,this.tokenizer=e;var i=this;this.$worker=function(){if(i.running){for(var e=new Date,t=i.currentLine,n=-1,r=i.doc,o=t;i.lines[t];)t++;var s=r.getLength(),a=0;for(i.running=!1;s>t;){i.$tokenizeRow(t),n=t;do t++;while(i.lines[t]);if(a++,a%5===0&&new Date-e>20){i.running=setTimeout(i.$worker,20);break}}i.currentLine=t,n>=o&&i.fireUpdateEvent(o,n)}}};(function(){n.implement(this,r),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var i={first:e,last:t};this._signal("update",{data:i})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.start.row,i=e.end.row-t;if(0===i)this.lines[t]=null;else if("remove"==e.action)this.lines.splice(t,i+1,null),this.states.splice(t,i+1,null);else{var n=Array(i+1);n.unshift(t,1),this.lines.splice.apply(this.lines,n),this.states.splice.apply(this.states,n)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),i=this.states[e-1],n=this.tokenizer.getLineTokens(t,i,e);return this.states[e]+""!=n.state+""?(this.states[e]=n.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=n.tokens}}).call(o.prototype),t.BackgroundTokenizer=o}),ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,i){"use strict";var n=e("./lib/lang"),r=(e("./lib/oop"),e("./range").Range),o=function(e,t,i){this.setRegexp(e),this.clazz=t,this.type=i||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){this.regExp+""!=e+""&&(this.regExp=e,this.cache=[])},this.update=function(e,t,i,o){if(this.regExp)for(var s=o.firstRow,a=o.lastRow,l=s;a>=l;l++){var c=this.cache[l];null==c&&(c=n.getMatchOffsets(i.getLine(l),this.regExp),c.length>this.MAX_RANGES&&(c=c.slice(0,this.MAX_RANGES)),c=c.map(function(e){return new r(l,e.offset,l,e.offset+e.length)}),this.cache[l]=c.length?c:"");for(var h=c.length;h--;)t.drawSingleLineMarker(e,c[h].toScreenRange(i),this.clazz,o)}}}).call(o.prototype),t.SearchHighlight=o}),ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,i){"use strict";function n(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var i=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,i.end.row,i.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,i){var n,r,o,s=0,a=this.folds,l=!0;null==t&&(t=this.end.row,i=this.end.column);for(var c=0;ce)return{row:n.start.row,column:n.start.column+e};if(e-=n.placeholder.length,0>e)return n.start;t=n.end.column}return{row:this.end.row,column:this.end.column+e}}}).call(n.prototype),t.FoldLine=n}),ace.define("ace/range_list",["require","exports","module","ace/range"],function(e,t,i){"use strict";var n=e("./range").Range,r=n.comparePoints,o=function(){this.ranges=[]};(function(){this.comparePoints=r,this.pointIndex=function(e,t,i){for(var n=this.ranges,o=i||0;o0)){var l=r(e,s.start);return 0===a?t&&0!==l?-o-2:o:l>0||0===l&&!t?o:-o-1}}return-o-1},this.add=function(e){var t=!e.isEmpty(),i=this.pointIndex(e.start,t);0>i&&(i=-i-1);var n=this.pointIndex(e.end,t,i);return 0>n?n=-n-1:n++,this.ranges.splice(i,n-i,e)},this.addList=function(e){for(var t=[],i=e.length;i--;)t.push.apply(t,this.add(e[i]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);return t>=0?this.ranges.splice(t,1):void 0},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return r(e.start,t.start)});for(var i,n=t[0],o=1;os||(0!=s||i.isEmpty()||n.isEmpty())&&(r(i.end,n.end)<0&&(i.end.row=n.end.row,i.end.column=n.end.column),t.splice(o,1),e.push(n),n=i,o--)}return this.ranges=t,e},this.contains=function(e,t){return this.pointIndex({row:e,column:t})>=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);return t>=0?this.ranges[t]:void 0},this.clipRows=function(e,t){var i=this.ranges;if(i[0].start.row>t||i[i.length-1].start.rown&&(n=-n-1);var r=this.pointIndex({row:t,column:0},n);0>r&&(r=-r-1);for(var o=[],s=n;r>s;s++)o.push(i[s]);return o},this.removeAll=function(){return this.ranges.splice(0,this.ranges.length)},this.attach=function(e){this.session&&this.detach(),this.session=e,this.onChange=this.$onChange.bind(this),this.session.on("change",this.onChange)},this.detach=function(){this.session&&(this.session.removeListener("change",this.onChange),this.session=null)},this.$onChange=function(e){if("insert"==e.action)var t=e.start,i=e.end;else var i=e.start,t=e.end;for(var n=t.row,r=i.row,o=r-n,s=-t.column+i.column,a=this.ranges,l=0,c=a.length;c>l;l++){var h=a[l];if(!(h.end.rown)break;if(h.start.row==n&&h.start.column>=t.column&&(h.start.column==t.column&&this.$insertRight||(h.start.column+=s,h.start.row+=o)),h.end.row==n&&h.end.column>=t.column){if(h.end.column==t.column&&this.$insertRight)continue;h.end.column==t.column&&s>0&&c-1>l&&h.end.column>h.start.column&&h.end.column==a[l+1].start.column&&(h.end.column-=s),h.end.column+=s,h.end.row+=o}}}if(0!=o&&c>l)for(;c>l;l++){var h=a[l];h.start.row+=o,h.end.row+=o}}}).call(o.prototype),t.RangeList=o}),ace.define("ace/edit_session/fold",["require","exports","module","ace/range","ace/range_list","ace/lib/oop"],function(e,t,i){"use strict";function n(e,t){e.row-=t.row,0==e.row&&(e.column-=t.column)}function r(e,t){n(e.start,t),n(e.end,t)}function o(e,t){0==e.row&&(e.column+=t.column),e.row+=t.row}function s(e,t){o(e.start,t),o(e.end,t)}var a=(e("../range").Range,e("../range_list").RangeList),l=e("../lib/oop"),c=t.Fold=function(e,t){this.foldLine=null,this.placeholder=t,this.range=e,this.start=e.start,this.end=e.end,this.sameRow=e.start.row==e.end.row,this.subFolds=this.ranges=[]};l.inherits(c,a),function(){this.toString=function(){return'"'+this.placeholder+'" '+this.range.toString()},this.setFoldLine=function(e){this.foldLine=e,this.subFolds.forEach(function(t){t.setFoldLine(e)})},this.clone=function(){var e=this.range.clone(),t=new c(e,this.placeholder);return this.subFolds.forEach(function(e){t.subFolds.push(e.clone())}),t.collapseChildren=this.collapseChildren,t},this.addSubFold=function(e){if(!this.range.isEqual(e)){if(!this.range.containsRange(e))throw new Error("A fold can't intersect already existing fold"+e.range+this.range);r(e,this.start);for(var t=e.start.row,i=e.start.column,n=0,o=-1;n=e)return r;if(r.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var i=this.$foldData,n=0;for(t&&(n=i.indexOf(t)),-1==n&&(n=0),n;n=e)return r}return null},this.getFoldedRowCount=function(e,t){for(var i=this.$foldData,n=t-e+1,r=0;r=t){t>a&&(a>=e?n-=t-a:n=0);break}s>=e&&(n-=a>=e?s-a:s-e+1)}return n},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var i,n=this.$foldData,r=!1;e instanceof s?i=e:(i=new s(t,e),i.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(i.range);var a=i.start.row,l=i.start.column,c=i.end.row,h=i.end.column;if(!(c>a||a==c&&h-2>=l))throw new Error("The range has to be at least 2 characters width");var d=this.getFoldAt(a,l,1),u=this.getFoldAt(c,h,-1);if(d&&u==d)return d.addSubFold(i);d&&!d.range.isStart(a,l)&&this.removeFold(d),u&&!u.range.isEnd(c,h)&&this.removeFold(u);var f=this.getFoldsInRange(i.range);f.length>0&&(this.removeFolds(f),f.forEach(function(e){i.addSubFold(e)}));for(var p=0;p0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var i,n;if(null==e?(i=new r(0,0,this.getLength(),0),t=!0):i="number"==typeof e?new r(e,0,e,this.getLine(e).length):"row"in e?r.fromPoints(e,e):e,n=this.getFoldsInRangeList(i),t)this.removeFolds(n);else for(var o=n;o.length;)this.expandFolds(o),o=this.getFoldsInRangeList(i);return n.length?n:void 0},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var i=this.getFoldLine(e,t);return i?i.end.row:e},this.getRowFoldStart=function(e,t){var i=this.getFoldLine(e,t);return i?i.start.row:e},this.getFoldDisplayLine=function(e,t,i,n,r){null==n&&(n=e.start.row),null==r&&(r=0),null==t&&(t=e.end.row),null==i&&(i=this.getLine(t).length);var o=this.doc,s="";return e.walk(function(e,t,i,a){if(!(n>t)){if(t==n){if(r>i)return;a=Math.max(r,a)}s+=null!=e?e:o.getLine(t).substring(a,i)}},t,i),s},this.getDisplayLine=function(e,t,i,n){var r=this.getFoldLine(e);if(r)return this.getFoldDisplayLine(r,e,t,i,n);var o;return o=this.doc.getLine(e),o.substring(n||0,t||o.length)},this.$cloneFoldData=function(){var e=[];return e=this.$foldData.map(function(t){var i=t.folds.map(function(e){return e.clone()});return new o(e,i)})},this.toggleFold=function(e){var t,i,n=this.selection,r=n.getRange();if(r.isEmpty()){var o=r.start;if(t=this.getFoldAt(o.row,o.column))return void this.expandFold(t);(i=this.findMatchingBracket(o))?1==r.comparePoint(i)?r.end=i:(r.start=i,r.start.column++,r.end.column--):(i=this.findMatchingBracket({row:o.row,column:o.column+1}))?(1==r.comparePoint(i)?r.end=i:r.start=i,r.start.column++):r=this.getCommentFoldRange(o.row,o.column)||r}else{var s=this.getFoldsInRange(r);if(e&&s.length)return void this.expandFolds(s);1==s.length&&(t=s[0])}if(t||(t=this.getFoldAt(r.start.row,r.start.column)),t&&t.range.toString()==r.toString())return void this.expandFold(t);var a="...";if(!r.isMultiLine()){if(a=this.getTextRange(r),a.length<4)return;a=a.trim().substring(0,2)+".."}this.addFold(a,r)},this.getCommentFoldRange=function(e,t,i){var n=new a(this,e,t),o=n.getCurrentToken();if(o&&/^comment|string/.test(o.type)){var s=new r,l=new RegExp(o.type.replace(/\..*/,"\\."));if(1!=i){do o=n.stepBackward();while(o&&l.test(o.type));n.stepForward()}if(s.start.row=n.getCurrentTokenRow(),s.start.column=n.getCurrentTokenColumn()+2,n=new a(this,e,t),-1!=i){do o=n.stepForward();while(o&&l.test(o.type));o=n.stepBackward()}else o=n.getCurrentToken();return s.end.row=n.getCurrentTokenRow(),s.end.column=n.getCurrentTokenColumn()+o.value.length-2,s}},this.foldAll=function(e,t,i){void 0==i&&(i=1e5);var n=this.foldWidgets;if(n){t=t||this.getLength(),e=e||0;for(var r=e;t>r;r++)if(null==n[r]&&(n[r]=this.getFoldWidget(r)),"start"==n[r]){var o=this.getFoldWidgetRange(r);if(o&&o.isMultiLine()&&o.end.row<=t&&o.start.row>=e){r=o.end.row;try{var s=this.addFold("...",o);s&&(s.collapseChildren=i)}catch(a){}}}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle!=e){this.$foldStyle=e,"manual"==e&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)}},this.$setFolding=function(e){if(this.$foldMode!=e){if(this.$foldMode=e,this.off("change",this.$updateFoldWidgets),this.off("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets),this._signal("changeAnnotation"),!e||"manual"==this.$foldStyle)return void(this.foldWidgets=null);this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.$tokenizerUpdateFoldWidgets=this.tokenizerUpdateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets),this.on("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets)}},this.getParentFoldRangeData=function(e,t){var i=this.foldWidgets;if(!i||t&&i[e])return{};for(var n,r=e-1;r>=0;){var o=i[r];if(null==o&&(o=i[r]=this.getFoldWidget(r)),"start"==o){var s=this.getFoldWidgetRange(r);if(n||(n=s),s&&s.end.row>=e)break}r--}return{range:-1!==r&&s,firstRange:n}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var i={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},n=this.$toggleFoldWidget(e,i);if(!n){var r=t.target||t.srcElement;r&&/ace_fold-widget/.test(r.className)&&(r.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(this.getFoldWidget){var i=this.getFoldWidget(e),n=this.getLine(e),r="end"===i?-1:1,o=this.getFoldAt(e,-1===r?0:n.length,r);if(o)return void(t.children||t.all?this.removeFold(o):this.expandFold(o));var s=this.getFoldWidgetRange(e,!0);if(s&&!s.isMultiLine()&&(o=this.getFoldAt(s.start.row,s.start.column,1),o&&s.isEqual(o.range)))return void this.removeFold(o);if(t.siblings){var a=this.getParentFoldRangeData(e);if(a.range)var l=a.range.start.row+1,c=a.range.end.row;this.foldAll(l,c,t.all?1e4:0)}else t.children?(c=s?s.end.row:this.getLength(),this.foldAll(e+1,c,t.all?1e4:0)):s&&(t.all&&(s.collapseChildren=1e4),this.addFold("...",s));return s}},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var i=this.$toggleFoldWidget(t,{});if(!i){var n=this.getParentFoldRangeData(t,!0);if(i=n.range||n.firstRange){t=i.start.row;var r=this.getFoldAt(t,this.getLine(t).length,1);r?this.removeFold(r):this.addFold("...",i)}}},this.updateFoldWidgets=function(e){var t=e.start.row,i=e.end.row-t;if(0===i)this.foldWidgets[t]=null;else if("remove"==e.action)this.foldWidgets.splice(t,i+1,null);else{var n=Array(i+1);n.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,n)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,o=e("./fold_line").FoldLine,s=e("./fold").Fold,a=e("../token_iterator").TokenIterator;t.Folding=n}),ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,i){"use strict";function n(){this.findMatchingBracket=function(e,t){if(0==e.column)return null;var i=t||this.getLine(e.row).charAt(e.column-1);if(""==i)return null;var n=i.match(/([\(\[\{])|([\)\]\}])/);return n?n[1]?this.$findClosingBracket(n[1],e):this.$findOpeningBracket(n[2],e):null},this.getBracketRange=function(e){var t,i=this.getLine(e.row),n=!0,r=i.charAt(e.column-1),s=r&&r.match(/([\(\[\{])|([\)\]\}])/);if(s||(r=i.charAt(e.column),e={row:e.row,column:e.column+1},s=r&&r.match(/([\(\[\{])|([\)\]\}])/),n=!1),!s)return null;if(s[1]){var a=this.$findClosingBracket(s[1],e);if(!a)return null;t=o.fromPoints(e,a),n||(t.end.column++,t.start.column--),t.cursor=t.end}else{var a=this.$findOpeningBracket(s[2],e);if(!a)return null;t=o.fromPoints(a,e),n||(t.start.column++,t.end.column--),t.cursor=t.start}return t},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(e,t,i){ +var n=this.$brackets[e],o=1,s=new r(this,t.row,t.column),a=s.getCurrentToken();if(a||(a=s.stepForward()),a){i||(i=new RegExp("(\\.?"+a.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)")+")+"));for(var l=t.column-s.getCurrentTokenColumn()-2,c=a.value;;){for(;l>=0;){var h=c.charAt(l);if(h==n){if(o-=1,0==o)return{row:s.getCurrentTokenRow(),column:l+s.getCurrentTokenColumn()}}else h==e&&(o+=1);l-=1}do a=s.stepBackward();while(a&&!i.test(a.type));if(null==a)break;c=a.value,l=c.length-1}return null}},this.$findClosingBracket=function(e,t,i){var n=this.$brackets[e],o=1,s=new r(this,t.row,t.column),a=s.getCurrentToken();if(a||(a=s.stepForward()),a){i||(i=new RegExp("(\\.?"+a.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)")+")+"));for(var l=t.column-s.getCurrentTokenColumn();;){for(var c=a.value,h=c.length;h>l;){var d=c.charAt(l);if(d==n){if(o-=1,0==o)return{row:s.getCurrentTokenRow(),column:l+s.getCurrentTokenColumn()}}else d==e&&(o+=1);l+=1}do a=s.stepForward();while(a&&!i.test(a.type));if(null==a)break;l=0}return null}}}var r=e("../token_iterator").TokenIterator,o=e("../range").Range;t.BracketMatch=n}),ace.define("ace/edit_session",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/config","ace/lib/event_emitter","ace/selection","ace/mode/text","ace/range","ace/document","ace/background_tokenizer","ace/search_highlight","ace/edit_session/folding","ace/edit_session/bracket_match"],function(e,t,i){"use strict";var n=e("./lib/oop"),r=e("./lib/lang"),o=e("./config"),s=e("./lib/event_emitter").EventEmitter,a=e("./selection").Selection,l=e("./mode/text").Mode,c=e("./range").Range,h=e("./document").Document,d=e("./background_tokenizer").BackgroundTokenizer,u=e("./search_highlight").SearchHighlight,f=function(e,t){this.$breakpoints=[],this.$decorations=[],this.$frontMarkers={},this.$backMarkers={},this.$markerId=1,this.$undoSelect=!0,this.$foldData=[],this.$foldData.toString=function(){return this.join("\n")},this.on("changeFold",this.onChangeFold.bind(this)),this.$onChange=this.onChange.bind(this),"object"==typeof e&&e.getLine||(e=new h(e)),this.setDocument(e),this.selection=new a(this),o.resetOptions(this),this.setMode(t),o._signal("session",this)};(function(){function e(e){return 4352>e?!1:e>=4352&&4447>=e||e>=4515&&4519>=e||e>=4602&&4607>=e||e>=9001&&9002>=e||e>=11904&&11929>=e||e>=11931&&12019>=e||e>=12032&&12245>=e||e>=12272&&12283>=e||e>=12288&&12350>=e||e>=12353&&12438>=e||e>=12441&&12543>=e||e>=12549&&12589>=e||e>=12593&&12686>=e||e>=12688&&12730>=e||e>=12736&&12771>=e||e>=12784&&12830>=e||e>=12832&&12871>=e||e>=12880&&13054>=e||e>=13056&&19903>=e||e>=19968&&42124>=e||e>=42128&&42182>=e||e>=43360&&43388>=e||e>=44032&&55203>=e||e>=55216&&55238>=e||e>=55243&&55291>=e||e>=63744&&64255>=e||e>=65040&&65049>=e||e>=65072&&65106>=e||e>=65108&&65126>=e||e>=65128&&65131>=e||e>=65281&&65376>=e||e>=65504&&65510>=e}n.implement(this,s),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e)return this.$docRowCache=[],void(this.$screenRowCache=[]);var t=this.$docRowCache.length,i=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>i&&(this.$docRowCache.splice(i,t),this.$screenRowCache.splice(i,t))},this.$getRowCacheIndex=function(e,t){for(var i=0,n=e.length-1;n>=i;){var r=i+n>>1,o=e[r];if(t>o)i=r+1;else{if(!(o>t))return r;n=r-1}}return i-1},this.resetCaches=function(){this.$modified=!0,this.$wrapData=[],this.$rowLengthCache=[],this.$resetRowCache(0),this.bgTokenizer&&this.bgTokenizer.start(0)},this.onChangeFold=function(e){var t=e.data;this.$resetRowCache(t.start.row)},this.onChange=function(e){this.$modified=!0,this.$resetRowCache(e.start.row);var t=this.$updateInternalDataOnChange(e);this.$fromUndo||!this.$undoManager||e.ignore||(this.$deltasDoc.push(e),t&&0!=t.length&&this.$deltasFold.push({action:"removeFolds",folds:t}),this.$informUndoManager.schedule()),this.bgTokenizer&&this.bgTokenizer.$updateOnChange(e),this._signal("change",e)},this.setValue=function(e){this.doc.setValue(e),this.selection.moveTo(0,0),this.$resetRowCache(0),this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.setUndoManager(this.$undoManager),this.getUndoManager().reset()},this.getValue=this.toString=function(){return this.doc.getValue()},this.getSelection=function(){return this.selection},this.getState=function(e){return this.bgTokenizer.getState(e)},this.getTokens=function(e){return this.bgTokenizer.getTokens(e)},this.getTokenAt=function(e,t){var i,n=this.bgTokenizer.getTokens(e),r=0;if(null==t)o=n.length-1,r=this.getLine(e).length;else for(var o=0;o=t));o++);return(i=n[o])?(i.index=o,i.start=r-i.value.length,i):null},this.setUndoManager=function(e){if(this.$undoManager=e,this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel(),e){var t=this;this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.$deltasFold.length&&(t.$deltas.push({group:"fold",deltas:t.$deltasFold}),t.$deltasFold=[]),t.$deltasDoc.length&&(t.$deltas.push({group:"doc",deltas:t.$deltasDoc}),t.$deltasDoc=[]),t.$deltas.length>0&&e.execute({action:"aceupdate",args:[t.$deltas,t],merge:t.mergeUndoDeltas}),t.mergeUndoDeltas=!1,t.$deltas=[]},this.$informUndoManager=r.delayedCall(this.$syncInformUndoManager)}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?r.stringRepeat(" ",this.getTabSize()):" "},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(n=!!i.charAt(t-1).match(this.tokenRe)),n||(n=!!i.charAt(t).match(this.tokenRe)),n)var r=this.tokenRe;else if(/^\s+$/.test(i.slice(t-1,t+1)))var r=/\s/;else var r=this.nonTokenRe;var o=t;if(o>0){do o--;while(o>=0&&i.charAt(o).match(r));o++}for(var s=t;se&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){if(this.$modified=!1,this.$useWrapMode)return this.screenWidth=this.$wrapLimit;for(var t=this.doc.getAllLines(),i=this.$rowLengthCache,n=0,r=0,o=this.$foldData[r],s=o?o.start.row:1/0,a=t.length,l=0;a>l;l++){if(l>s){if(l=o.end.row+1,l>=a)break;o=this.$foldData[r++],s=o?o.start.row:1/0}null==i[l]&&(i[l]=this.$getStringScreenWidth(t[l])[0]),i[l]>n&&(n=i[l])}this.screenWidth=n}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},this.undoChanges=function(e,t){if(e.length){this.$fromUndo=!0;for(var i=null,n=e.length-1;-1!=n;n--){var r=e[n];"doc"==r.group?(this.doc.revertDeltas(r.deltas),i=this.$getUndoSelection(r.deltas,!0,i)):r.deltas.forEach(function(e){this.addFolds(e.folds)},this)}return this.$fromUndo=!1,i&&this.$undoSelect&&!t&&this.selection.setSelectionRange(i),i}},this.redoChanges=function(e,t){if(e.length){this.$fromUndo=!0;for(var i=null,n=0;ne.end.column&&(o.start.column+=a),o.end.row==e.end.row&&o.end.column>e.end.column&&(o.end.column+=a)),s&&o.start.row>=e.end.row&&(o.start.row+=s,o.end.row+=s)}if(o.end=this.insert(o.start,n),r.length){var l=e.start,h=o.start,s=h.row-l.row,a=h.column-l.column;this.addFolds(r.map(function(e){return e=e.clone(),e.start.row==l.row&&(e.start.column+=a),e.end.row==l.row&&(e.end.column+=a),e.start.row+=s,e.end.row+=s,e}))}return o},this.indentRows=function(e,t,i){i=i.replace(/\t/g,this.getTabString());for(var n=e;t>=n;n++)this.doc.insertInLine({row:n,column:0},i)},this.outdentRows=function(e){for(var t=e.collapseRows(),i=new c(0,0,0,0),n=this.getTabSize(),r=t.start.row;r<=t.end.row;++r){var o=this.getLine(r);i.start.row=r,i.end.row=r;for(var s=0;n>s&&" "==o.charAt(s);++s);n>s&&" "==o.charAt(s)?(i.start.column=s,i.end.column=s+1):(i.start.column=0,i.end.column=s),this.remove(i)}},this.$moveLines=function(e,t,i){if(e=this.getRowFoldStart(e),t=this.getRowFoldEnd(t),0>i){var n=this.getRowFoldStart(e+i);if(0>n)return 0;var r=n-e}else if(i>0){var n=this.getRowFoldEnd(t+i);if(n>this.doc.getLength()-1)return 0;var r=n-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var r=t-e+1}var o=new c(e,0,t,Number.MAX_VALUE),s=this.getFoldsInRange(o).map(function(e){return e=e.clone(),e.start.row+=r,e.end.row+=r,e}),a=0==i?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+r,a),s.length&&this.addFolds(s),r},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return 0>t?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){if(t=Math.max(0,t),0>e)e=0,t=0;else{var i=this.doc.getLength();e>=i?(e=i-1,t=this.doc.getLine(i-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){if(this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0),e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){this.$wrapLimitRange.min===e&&this.$wrapLimitRange.max===t||(this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$useWrapMode&&this._signal("changeWrapMode"))},this.adjustWrapLimit=function(e,t){var i=this.$wrapLimitRange;i.max<0&&(i={min:t,max:t});var n=this.$constrainWrapLimit(e,i.min,i.max);return n!=this.$wrapLimit&&n>1?(this.$wrapLimit=n,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,i){return t&&(e=Math.max(t,e)),i&&(e=Math.min(i,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,i=e.action,n=e.start,r=e.end,o=n.row,s=r.row,a=s-o,l=null;if(this.$updating=!0,0!=a)if("remove"===i){this[t?"$wrapData":"$rowLengthCache"].splice(o,a);var c=this.$foldData;l=this.getFoldsInRange(e),this.removeFolds(l);var h=this.getFoldLine(r.row),d=0;if(h){h.addRemoveChars(r.row,r.column,n.column-r.column),h.shiftRow(-a);var u=this.getFoldLine(o);u&&u!==h&&(u.merge(h),h=u),d=c.indexOf(h)+1}for(d;d=r.row&&h.shiftRow(-a)}s=o}else{var f=Array(a);f.unshift(o,0);var p=t?this.$wrapData:this.$rowLengthCache;p.splice.apply(p,f);var c=this.$foldData,h=this.getFoldLine(o),d=0;if(h){var m=h.range.compareInside(n.row,n.column);0==m?(h=h.split(n.row,n.column),h&&(h.shiftRow(a),h.addRemoveChars(s,0,r.column-n.column))):-1==m&&(h.addRemoveChars(o,0,r.column-n.column),h.shiftRow(a)),d=c.indexOf(h)+1}for(d;d=o&&h.shiftRow(a)}}else{a=Math.abs(e.start.column-e.end.column),"remove"===i&&(l=this.getFoldsInRange(e),this.removeFolds(l),a=-a);var h=this.getFoldLine(o);h&&h.addRemoveChars(o,n.column,a)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(o,s):this.$updateRowLengthCache(o,s),l},this.$updateRowLengthCache=function(e,t,i){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var i,n,r=this.doc.getAllLines(),o=this.getTabSize(),s=this.$wrapData,l=this.$wrapLimit,c=e;for(t=Math.min(t,r.length-1);t>=c;)n=this.getFoldLine(c,n),n?(i=[],n.walk(function(e,t,n,o){var s;if(null!=e){s=this.$getDisplayTokens(e,i.length),s[0]=a;for(var l=1;lt-y;){var A=c+t-y;if(e[A-1]>=p&&e[A]>=p)o(A);else if(e[A]!=a&&e[A]!=h){for(var C=Math.max(A-(t-(t>>2)),c-1);A>C&&e[A]C&&e[A]C&&e[A]==f;)A--}else for(;A>C&&e[A]C?o(++A):(A=c+t,e[A]==i&&A--,o(A-y))}else{for(A;A!=c-1&&e[A]!=a;A--);if(A>c){o(A);continue}for(A=c+t;Ac;c++)s.push(g)}else 32==l?s.push(p):l>39&&48>l||l>57&&64>l?s.push(f):l>=4352&&e(l)?s.push(t,i):s.push(t)}return s},this.$getStringScreenWidth=function(t,i,n){if(0==i)return[0,0];null==i&&(i=1/0),n=n||0;var r,o;for(o=0;o=4352&&e(r)?2:1,!(n>i));o++);return[n,o]},this.lineWidgets=null,this.getRowLength=function(e){if(this.lineWidgets)var t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0;else t=0;return this.$useWrapMode&&this.$wrapData[e]?this.$wrapData[e].length+1+t:1+t},this.getRowLineCount=function(e){return this.$useWrapMode&&this.$wrapData[e]?this.$wrapData[e].length+1:1},this.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),i=this.$wrapData[t.row];return i.length&&i[0]e)return{row:0,column:0};var i,n,r=0,o=0,s=0,a=0,l=this.$screenRowCache,c=this.$getRowCacheIndex(l,e),h=l.length;if(h&&c>=0)var s=l[c],r=this.$docRowCache[c],d=e>l[h-1];else var d=!h;for(var u=this.getLength()-1,f=this.getNextFoldLine(r),p=f?f.start.row:1/0;e>=s&&(a=this.getRowLength(r),!(s+a>e||r>=u));)s+=a,r++,r>p&&(r=f.end.row+1,f=this.getNextFoldLine(r,f),p=f?f.start.row:1/0),d&&(this.$docRowCache.push(r),this.$screenRowCache.push(s));if(f&&f.start.row<=r)i=this.getFoldDisplayLine(f),r=f.start.row;else{if(e>=s+a||r>u)return{row:u,column:this.getLine(u).length};i=this.getLine(r),f=null}var m=0;if(this.$useWrapMode){var g=this.$wrapData[r];if(g){var v=Math.floor(e-s);n=g[v],v>0&&g.length&&(m=g.indent,o=g[v-1]||g[g.length-1],i=i.substring(o))}}return o+=this.$getStringScreenWidth(i,t-m)[1],this.$useWrapMode&&o>=n&&(o=n-1),f?f.idxToPosition(o):{row:r,column:o}},this.documentToScreenPosition=function(e,t){if("undefined"==typeof t)var i=this.$clipPositionToDocument(e.row,e.column);else i=this.$clipPositionToDocument(e,t);e=i.row,t=i.column;var n=0,r=null,o=null;o=this.getFoldAt(e,t,1),o&&(e=o.start.row,t=o.start.column);var s,a=0,l=this.$docRowCache,c=this.$getRowCacheIndex(l,e),h=l.length;if(h&&c>=0)var a=l[c],n=this.$screenRowCache[c],d=e>l[h-1];else var d=!h;for(var u=this.getNextFoldLine(a),f=u?u.start.row:1/0;e>a;){if(a>=f){if(s=u.end.row+1,s>e)break;u=this.getNextFoldLine(s,u),f=u?u.start.row:1/0}else s=a+1;n+=this.getRowLength(a),a=s,d&&(this.$docRowCache.push(a),this.$screenRowCache.push(n))}var p="";u&&a>=f?(p=this.getFoldDisplayLine(u,e,t),r=u.start.row):(p=this.getLine(e).substring(0,t),r=e);var m=0;if(this.$useWrapMode){var g=this.$wrapData[r];if(g){for(var v=0;p.length>=g[v];)n++,v++;p=p.substring(g[v-1]||0,p.length),m=v>0?g.indent:0}}return{row:n,column:m+this.$getStringScreenWidth(p)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(this.$useWrapMode)for(var i=this.$wrapData.length,n=0,r=0,t=this.$foldData[r++],o=t?t.start.row:1/0;i>n;){var s=this.$wrapData[n];e+=s?s.length+1:1,n++,n>o&&(n=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:1/0)}else{e=this.getLength();for(var a=this.$foldData,r=0;ri));o++);return[n,o]})},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker()}}).call(f.prototype),e("./edit_session/folding").Folding.call(f.prototype),e("./edit_session/bracket_match").BracketMatch.call(f.prototype),o.defineOptions(f.prototype,"session",{wrap:{set:function(e){if(e&&"off"!=e?"free"==e?e=!0:"printMargin"==e?e=-1:"string"==typeof e&&(e=parseInt(e,10)||!1):e=!1,this.$wrap!=e)if(this.$wrap=e,e){var t="number"==typeof e?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}else this.setUseWrapMode(!1)},get:function(){return this.getUseWrapMode()?-1==this.$wrap?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e="auto"==e?"text"!=this.$mode.type:"text"!=e,e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$modified=!0,this.$resetRowCache(0),this.$updateWrapData(0,this.getLength()-1)))},initialValue:"auto"},indentedSoftWrap:{initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){isNaN(e)||this.$tabSize===e||(this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize"))},initialValue:4,handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId}}}),t.EditSession=f}),ace.define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,i){"use strict";var n=e("./lib/lang"),r=e("./lib/oop"),o=e("./range").Range,s=function(){this.$options={}};(function(){this.set=function(e){return r.mixin(this.$options,e),this},this.getOptions=function(){return n.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$options,i=this.$matchIterator(e,t);if(!i)return!1;var n=null;return i.forEach(function(e,i,r){if(e.start)n=e;else{var s=e.offset+(r||0);if(n=new o(i,s,i,s+e.length),!e.length&&t.start&&t.start.start&&0!=t.skipCurrent&&n.isEqual(t.start))return n=null,!1}return!0}),n},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var i=t.range,r=i?e.getLines(i.start.row,i.end.row):e.doc.getAllLines(),s=[],a=t.re;if(t.$isMultiLine){var l,c=a.length,h=r.length-c;e:for(var d=a.offset||0;h>=d;d++){for(var u=0;c>u;u++)if(-1==r[d+u].search(a[u]))continue e;var f=r[d],p=r[d+c-1],m=f.length-f.match(a[0])[0].length,g=p.match(a[c-1])[0].length;l&&l.end.row===d&&l.end.column>m||(s.push(l=new o(d,m,d+c-1,g)),c>2&&(d=d+c-2))}}else for(var v=0;vv&&s[v].start.columnv&&s[u].end.column>C&&s[u].end.row==i.end.row;)u--;for(s=s.slice(v,u+1),v=0,u=s.length;u>v;v++)s[v].start.row+=i.start.row,s[v].end.row+=i.start.row}return s},this.replace=function(e,t){var i=this.$options,n=this.$assembleRegExp(i);if(i.$isMultiLine)return t;if(n){var r=n.exec(e);if(!r||r[0].length!=e.length)return null;if(t=e.replace(n,t),i.preserveCase){t=t.split("");for(var o=Math.min(e.length,e.length);o--;){var s=e[o];s&&s.toLowerCase()!=s?t[o]=t[o].toUpperCase():t[o]=t[o].toLowerCase()}t=t.join("")}return t}},this.$matchIterator=function(e,t){var i=this.$assembleRegExp(t);if(!i)return!1;var r;if(t.$isMultiLine)var s=i.length,a=function(t,n,a){var l=t.search(i[0]);if(-1!=l){for(var c=1;s>c;c++)if(t=e.getLine(n+c),-1==t.search(i[c]))return;var h=t.match(i[s-1])[0].length,d=new o(n,l,n+s-1,h);return 1==i.offset?(d.start.row--,d.start.column=Number.MAX_VALUE):a&&(d.start.column+=a),r(d)?!0:void 0}};else if(t.backwards)var a=function(e,t,o){for(var s=n.getMatchOffsets(e,i),a=s.length-1;a>=0;a--)if(r(s[a],t,o))return!0};else var a=function(e,t,o){for(var s=n.getMatchOffsets(e,i),a=0;a=s;n--)if(i(e.getLine(n),n))return;if(0!=t.wrap)for(n=a,s=o.row;n>=s;n--)if(i(e.getLine(n),n))return}}:function(i){var n=o.row,r=e.getLine(n).substr(o.column);if(!i(r,n,o.column)){for(n+=1;a>=n;n++)if(i(e.getLine(n),n))return;if(0!=t.wrap)for(n=s,a=o.row;a>=n;n++)if(i(e.getLine(n),n))return}};return{forEach:l}}}).call(s.prototype),t.Search=s}),ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,i){"use strict";function n(e,t){this.platform=t||(s.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function r(e,t){n.call(this,e,t),this.$singleCommand=!1}var o=e("../lib/keys"),s=e("../lib/useragent"),a=o.KEY_MODS;r.prototype=n.prototype,function(){function e(e){return"object"==typeof e&&e.bindKey&&e.bindKey.position||0}this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var i=e&&("string"==typeof e?e:e.name);e=this.commands[i],t||delete this.commands[i]; +var n=this.commandKeyBinding;for(var r in n){var o=n[r];if(o==e)delete n[r];else if(Array.isArray(o)){var s=o.indexOf(e);-1!=s&&(o.splice(s,1),1==o.length&&(n[r]=o[0]))}}},this.bindKey=function(e,t,i){return"object"==typeof e&&e&&(void 0==i&&(i=e.position),e=e[this.platform]),e?"function"==typeof t?this.addCommand({exec:t,bindKey:e,name:t.name||e}):void e.split("|").forEach(function(e){var n="";if(-1!=e.indexOf(" ")){var r=e.split(/\s+/);e=r.pop(),r.forEach(function(e){var t=this.parseKeys(e),i=a[t.hashId]+t.key;n+=(n?" ":"")+i,this._addCommandToBinding(n,"chainKeys")},this),n+=" "}var o=this.parseKeys(e),s=a[o.hashId]+o.key;this._addCommandToBinding(n+s,t,i)},this):void 0},this._addCommandToBinding=function(t,i,n){var r,o=this.commandKeyBinding;if(i)if(!o[t]||this.$singleCommand)o[t]=i;else{Array.isArray(o[t])?-1!=(r=o[t].indexOf(i))&&o[t].splice(r,1):o[t]=[o[t]],"number"!=typeof n&&(n=n||i.isDefault?-100:e(i));var s=o[t];for(r=0;rn)break}s.splice(r,0,i)}else delete o[t]},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var i=e[t];if(i){if("string"==typeof i)return this.bindKey(i,t);"function"==typeof i&&(i={exec:i}),"object"==typeof i&&(i.name||(i.name=t),this.addCommand(i))}},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),i=t.pop(),n=o[i];if(o.FUNCTION_KEYS[n])i=o.FUNCTION_KEYS[n].toLowerCase();else{if(!t.length)return{key:i,hashId:-1};if(1==t.length&&"shift"==t[0])return{key:i.toUpperCase(),hashId:-1}}for(var r=0,s=t.length;s--;){var a=o.KEY_MODS[t[s]];if(null==a)return"undefined"!=typeof console&&console.error("invalid modifier "+t[s]+" in "+e),!1;r|=a}return{key:i,hashId:r}},this.findKeyCommand=function(e,t){var i=a[e]+t;return this.commandKeyBinding[i]},this.handleKeyboard=function(e,t,i,n){if(!(0>n)){var r=a[t]+i,o=this.commandKeyBinding[r];return e.$keyChain&&(e.$keyChain+=" "+r,o=this.commandKeyBinding[e.$keyChain]||o),!o||"chainKeys"!=o&&"chainKeys"!=o[o.length-1]?(e.$keyChain&&(t&&4!=t||1!=i.length?(-1==t||n>0)&&(e.$keyChain=""):e.$keyChain=e.$keyChain.slice(0,-r.length-1)),{command:o}):(e.$keyChain=e.$keyChain||r,{command:"null"})}},this.getStatusText=function(e,t){return t.$keyChain||""}}.call(n.prototype),t.HashHandler=n,t.MultiHashHandler=r}),ace.define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,i){"use strict";var n=e("../lib/oop"),r=e("../keyboard/hash_handler").MultiHashHandler,o=e("../lib/event_emitter").EventEmitter,s=function(e,t){r.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};n.inherits(s,r),function(){n.implement(this,o),this.exec=function(e,t,i){if(Array.isArray(e)){for(var n=e.length;n--;)if(this.exec(e[n],t,i))return!0;return!1}if("string"==typeof e&&(e=this.commands[e]),!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;var r={editor:t,command:e,args:i};return r.returnValue=this._emit("exec",r),this._signal("afterExec",r),r.returnValue!==!1},this.toggleRecording=function(e){return this.$inReplay?void 0:(e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0))},this.replay=function(e){if(!this.$inReplay&&this.macro){if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){"string"==typeof t?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}}},this.trimMacro=function(e){return e.map(function(e){return"string"!=typeof e[0]&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(s.prototype),t.CommandManager=s}),ace.define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,i){"use strict";function n(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),o=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",bindKey:n("Ctrl-,","Command-,"),exec:function(e){o.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",bindKey:n("Alt-E","Ctrl-E"),exec:function(e){o.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",bindKey:n("Alt-Shift-E","Ctrl-Shift-E"),exec:function(e){o.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",bindKey:n("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",bindKey:n(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:n("Ctrl-L","Command-L"),exec:function(e){var t=parseInt(prompt("Enter line number:"),10);isNaN(t)||e.gotoLine(t)},readOnly:!0},{name:"fold",bindKey:n("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:n("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",bindKey:n("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",bindKey:n("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",bindKey:n(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",bindKey:n("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",bindKey:n("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",bindKey:n("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",bindKey:n("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",bindKey:n("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",bindKey:n("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",bindKey:n("Ctrl-F","Command-F"),exec:function(e){o.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",bindKey:n("Ctrl-Shift-Home","Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",bindKey:n("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",bindKey:n("Shift-Up","Shift-Up"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",bindKey:n("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",bindKey:n("Ctrl-Shift-End","Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",bindKey:n("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",bindKey:n("Shift-Down","Shift-Down"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",bindKey:n("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",bindKey:n("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",bindKey:n("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",bindKey:n("Alt-Shift-Left","Command-Shift-Left"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",bindKey:n("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",bindKey:n("Shift-Left","Shift-Left"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",bindKey:n("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",bindKey:n("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",bindKey:n("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",bindKey:n("Alt-Shift-Right","Command-Shift-Right"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",bindKey:n("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",bindKey:n("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",bindKey:n("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:n(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:n("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:n(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",bindKey:n("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",bindKey:n("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",bindKey:n("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",bindKey:n("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",bindKey:n("Ctrl-P","Ctrl-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",bindKey:n("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",bindKey:n("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",bindKey:n(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",exec:function(e){},readOnly:!0},{name:"cut",exec:function(e){var t=e.getSelectionRange();e._emit("cut",t),e.selection.isEmpty()||(e.session.remove(t),e.clearSelection())},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",bindKey:n("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",bindKey:n("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",bindKey:n("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",bindKey:n("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",bindKey:n("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",bindKey:n("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",bindKey:n("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",bindKey:n("Ctrl-H","Command-Option-F"),exec:function(e){o.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",bindKey:n("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",bindKey:n("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",bindKey:n("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",bindKey:n("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",bindKey:n("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",bindKey:n("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",bindKey:n("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",bindKey:n("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",bindKey:n("Shift-Delete",null),exec:function(e){return e.selection.isEmpty()?void e.remove("left"):!1},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",bindKey:n("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",bindKey:n("Alt-Delete","Ctrl-K"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",bindKey:n("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",bindKey:n("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",bindKey:n("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",bindKey:n("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",bindKey:n("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",bindKey:n("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",bindKey:n(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",bindKey:n("Ctrl-T","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",bindKey:n("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",bindKey:n("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"expandtoline",bindKey:n("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"joinlines",bindKey:n(null,null),exec:function(e){for(var t=e.selection.isBackwards(),i=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),n=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(i.row).length,a=e.session.doc.getTextRange(e.selection.getRange()),l=a.replace(/\n\s*/," ").length,c=e.session.doc.getLine(i.row),h=i.row+1;h<=n.row+1;h++){var d=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(h)));0!==d.length&&(d=" "+d),c+=d}n.row+10?(e.selection.moveCursorTo(i.row,i.column),e.selection.selectTo(i.row,i.column+l)):(o=e.session.doc.getLine(i.row).length>o?o+1:o,e.selection.moveCursorTo(i.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",bindKey:n(null,null),exec:function(e){var t=e.session.doc.getLength()-1,i=e.session.doc.getLine(t).length,n=e.selection.rangeList.ranges,r=[];n.length<1&&(n=[e.selection.getRange()]);for(var o=0;o0&&this.$blockScrolling--;var i=t&&t.scrollIntoView;if(i){switch(i){case"center-animate":i="animate";case"center":this.renderer.scrollCursorIntoView(null,.5);break;case"animate":case"cursor":this.renderer.scrollCursorIntoView();break;case"selectionPart":var n=this.selection.getRange(),r=this.renderer.layerConfig;(n.start.row>=r.lastRow||n.end.row<=r.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead)}"animate"==i&&this.renderer.animateScrolling(this.curOp.scrollTop)}this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=["backspace","del","insertstring"],this.$historyTracker=function(e){if(this.$mergeUndoDeltas){var t=this.prevOp,i=this.$mergeableCommands,n=t.command&&e.command.name==t.command.name;if("insertstring"==e.command.name){var r=e.args;void 0===this.mergeNextCommand&&(this.mergeNextCommand=!0),n=n&&this.mergeNextCommand&&(!/\s/.test(r)||/\s/.test(t.args)),this.mergeNextCommand=!0}else n=n&&-1!==i.indexOf(e.command.name);"always"!=this.$mergeUndoDeltas&&Date.now()-this.sequenceStartTime>2e3&&(n=!1),n?this.session.mergeUndoDeltas=!0:-1!==i.indexOf(e.command.name)&&(this.sequenceStartTime=Date.now())}},this.setKeyboardHandler=function(e,t){if(e&&"string"==typeof e){this.$keybindingId=e;var i=this;v.loadModule(["keybinding",e],function(n){i.$keybindingId==e&&i.keyBinding.setKeyboardHandler(n&&n.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session!=e){this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange);var i=this.session.getSelection();i.off("changeCursor",this.$onCursorChange),i.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this})}},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?1==t?this.navigateFileEnd():-1==t&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||r.computedStyle(this.container,"fontSize")},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){if(this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null),!this.$highlightPending){var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(t&&t.bgTokenizer){var i=t.findMatchingBracket(e.getCursorPosition());if(i)var n=new f(i.row,i.column,i.row,i.column+1);else if(t.$mode.getMatching)var n=t.$mode.getMatching(e.session);n&&(t.$bracketHighlight=t.addMarker(n,"ace_bracket","text"))}},50)}},this.$highlightTags=function(){if(!this.$highlightTagPending){var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(t&&t.bgTokenizer){var i=e.getCursorPosition(),n=new w(e.session,i.row,i.column),r=n.getCurrentToken();if(!r||!/\b(?:tag-open|tag-name)/.test(r.type))return t.removeMarker(t.$tagHighlight),void(t.$tagHighlight=null);if(-1==r.type.indexOf("tag-open")||(r=n.stepForward())){var o=r.value,s=0,a=n.stepBackward();if("<"==a.value){do a=r,r=n.stepForward(),r&&r.value===o&&-1!==r.type.indexOf("tag-name")&&("<"===a.value?s++:"=0)}else{do r=a,a=n.stepBackward(),r&&r.value===o&&-1!==r.type.indexOf("tag-name")&&("<"===a.value?s++:"=s);n.stepForward()}if(!r)return t.removeMarker(t.$tagHighlight),void(t.$tagHighlight=null);var l=n.getCurrentTokenRow(),c=n.getCurrentTokenColumn(),h=new f(l,c,l,c+r.value.length);t.$tagHighlight&&0!==h.compareRange(t.$backMarkers[t.$tagHighlight].range)&&(t.removeMarker(t.$tagHighlight),t.$tagHighlight=null),h&&!t.$tagHighlight&&(t.$tagHighlight=t.addMarker(h,"ace_bracket","text"))}}},50)}},this.focus=function(){var e=this;setTimeout(function(){e.textInput.focus()}),this.textInput.focus()},this.isFocused=function(){return this.textInput.isFocused()},this.blur=function(){this.textInput.blur()},this.onFocus=function(e){this.$isFocused||(this.$isFocused=!0,this.renderer.showCursor(),this.renderer.visualizeFocus(),this._emit("focus",e))},this.onBlur=function(e){this.$isFocused&&(this.$isFocused=!1,this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._emit("blur",e))},this.$cursorChange=function(){this.renderer.updateCursor()},this.onDocumentChange=function(e){var t=this.session.$useWrapMode,i=e.start.row==e.end.row?e.end.row:1/0;this.renderer.updateLines(e.start.row,i,t),this._signal("change",e),this.$cursorChange(),this.$updateHighlightActiveLine()},this.onTokenizerUpdate=function(e){var t=e.data;this.renderer.updateLines(t.first,t.last)},this.onScrollTopChange=function(){this.renderer.scrollToY(this.session.getScrollTop())},this.onScrollLeftChange=function(){this.renderer.scrollToX(this.session.getScrollLeft())},this.onCursorChange=function(){this.$cursorChange(),this.$blockScrolling||(v.warn("Automatically scrolling cursor into view after selection change","this will be disabled in the next version","set editor.$blockScrolling = Infinity to disable this message"),this.renderer.scrollCursorIntoView()),this.$highlightBrackets(),this.$highlightTags(),this.$updateHighlightActiveLine(),this._signal("changeSelection")},this.$updateHighlightActiveLine=function(){var e,t=this.getSession();if(this.$highlightActiveLine&&("line"==this.$selectionStyle&&this.selection.isMultiLine()||(e=this.getCursorPosition()),!this.renderer.$maxLines||1!==this.session.getLength()||this.renderer.$minLines>1||(e=!1)),t.$highlightLineMarker&&!e)t.removeMarker(t.$highlightLineMarker.id),t.$highlightLineMarker=null;else if(!t.$highlightLineMarker&&e){var i=new f(e.row,e.column,e.row,1/0);i.id=t.addMarker(i,"ace_active-line","screenLine"),t.$highlightLineMarker=i}else e&&(t.$highlightLineMarker.start.row=e.row,t.$highlightLineMarker.end.row=e.row,t.$highlightLineMarker.start.column=e.column,t._signal("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;if(t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null, +this.selection.isEmpty())this.$updateHighlightActiveLine();else{var i=this.selection.getRange(),n=this.getSelectionStyle();t.$selectionMarker=t.addMarker(i,"ace_selection",n)}var r=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(r),this._signal("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(!t.isEmpty()&&!t.isMultiLine()){var i=t.start.column-1,n=t.end.column+1,r=e.getLine(t.start.row),o=r.length,s=r.substring(Math.max(i,0),Math.min(n,o));if(!(i>=0&&/^[\w\d]/.test(s)||o>=n&&/[\w\d]$/.test(s))&&(s=r.substring(t.start.column,t.end.column),/^[\w\d]+$/.test(s))){var a=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:s});return a}}},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText();return this._signal("copy",e),e},this.onCopy=function(){this.commands.exec("copy",this)},this.onCut=function(){this.commands.exec("cut",this)},this.onPaste=function(e,t){var i={text:e,event:t};this.commands.exec("paste",this,i)},this.$handlePaste=function(e){"string"==typeof e&&(e={text:e}),this._signal("paste",e);var t=e.text;if(!this.inMultiSelectMode||this.inVirtualSelectionMode)this.insert(t);else{var i=t.split(/\r\n|\r|\n/),n=this.selection.rangeList.ranges;if(i.length>n.length||i.length<2||!i[1])return this.commands.exec("insertstring",this,t);for(var r=n.length;r--;){var o=n[r];o.isEmpty()||this.session.remove(o),this.session.insert(o.start,i[r])}}},this.execCommand=function(e,t){return this.commands.exec(e,this,t)},this.insert=function(e,t){var i=this.session,n=i.getMode(),r=this.getCursorPosition();if(this.getBehavioursEnabled()&&!t){var o=n.transformAction(i.getState(r.row),"insertion",this,i,e);o&&(e!==o.text&&(this.session.mergeUndoDeltas=!1,this.$mergeNextCommand=!1),e=o.text)}if(" "==e&&(e=this.session.getTabString()),this.selection.isEmpty()){if(this.session.getOverwrite()){var s=new f.fromPoints(r,r);s.end.column+=e.length,this.session.remove(s)}}else{var s=this.getSelectionRange();r=this.session.remove(s),this.clearSelection()}if("\n"==e||"\r\n"==e){var a=i.getLine(r.row);if(r.column>a.search(/\S|$/)){var l=a.substr(r.column).search(/\S|$/);i.doc.removeInLine(r.row,r.column,r.column+l)}}this.clearSelection();var c=r.column,h=i.getState(r.row),a=i.getLine(r.row),d=n.checkOutdent(h,a,e);i.insert(r,e);if(o&&o.selection&&(2==o.selection.length?this.selection.setSelectionRange(new f(r.row,c+o.selection[0],r.row,c+o.selection[1])):this.selection.setSelectionRange(new f(r.row+o.selection[0],o.selection[1],r.row+o.selection[2],o.selection[3]))),i.getDocument().isNewLine(e)){var u=n.getNextLineIndent(h,a.slice(0,r.column),i.getTabString());i.insert({row:r.row+1,column:0},u)}d&&n.autoOutdent(h,i,r.row)},this.onTextInput=function(e){this.keyBinding.onTextInput(e)},this.onCommandKey=function(e,t,i){this.keyBinding.onCommandKey(e,t,i)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&("left"==e?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var i=this.session,n=i.getState(t.start.row),r=i.getMode().transformAction(n,"deletion",this,i,t);if(0===t.end.column){var o=i.getTextRange(t);if("\n"==o[o.length-1]){var s=i.getLine(t.end.row);/^\s+$/.test(s)&&(t.end.column=s.length)}}r&&(t=r)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(this.selection.isEmpty()){var e=this.getCursorPosition(),t=e.column;if(0!==t){var i,n,r=this.session.getLine(e.row);tt.toLowerCase()?1:0});for(var n=new f(0,0,0,0),r=e.first;r<=e.last;r++){var o=t.getLine(r);n.start.row=r,n.end.row=r,n.end.column=o.length,t.replace(n,i[r-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),i=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,i,e)},this.getNumberAt=function(e,t){var i=/[\-]?[0-9]+(?:\.[0-9]+)?/g;i.lastIndex=0;for(var n=this.session.getLine(e);i.lastIndex=t){var o={value:r[0],start:r.index,end:r.index+r[0].length};return o}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,i=this.selection.getCursor().column,n=new f(t,i-1,t,i),r=this.session.getTextRange(n);if(!isNaN(parseFloat(r))&&isFinite(r)){var o=this.getNumberAt(t,i);if(o){var s=o.value.indexOf(".")>=0?o.start+o.value.indexOf(".")+1:o.end,a=o.start+o.value.length-s,l=parseFloat(o.value);l*=Math.pow(10,a),e*=s!==o.end&&s>i?Math.pow(10,o.end-i-1):Math.pow(10,o.end-i),l+=e,l/=Math.pow(10,a);var c=l.toFixed(a),h=new f(t,o.start,t,o.end);this.session.replace(h,c),this.moveCursorTo(t,Math.max(o.start+1,i+c.length-o.value.length))}}},this.removeLines=function(){var e=this.$getSelectedRows();this.session.removeFullLines(e.first,e.last),this.clearSelection()},this.duplicateSelection=function(){var e=this.selection,t=this.session,i=e.getRange(),n=e.isBackwards();if(i.isEmpty()){var r=i.start.row;t.duplicateLines(r,r)}else{var o=n?i.start:i.end,s=t.insert(o,t.getTextRange(i),!1);i.start=o,i.end=s,e.setSelectionRange(i,n)}},this.moveLinesDown=function(){this.$moveLines(1,!1)},this.moveLinesUp=function(){this.$moveLines(-1,!1)},this.moveText=function(e,t,i){return this.session.moveText(e,t,i)},this.copyLinesUp=function(){this.$moveLines(-1,!0)},this.copyLinesDown=function(){this.$moveLines(1,!0)},this.$moveLines=function(e,t){var i,n,r=this.selection;if(!r.inMultiSelectMode||this.inVirtualSelectionMode){var o=r.toOrientedRange();i=this.$getSelectedRows(o),n=this.session.$moveLines(i.first,i.last,t?0:e),t&&-1==e&&(n=0),o.moveBy(n,0),r.fromOrientedRange(o)}else{var s=r.rangeList.ranges;r.rangeList.detach(this.session),this.inVirtualSelectionMode=!0;for(var a=0,l=0,c=s.length,h=0;c>h;h++){var d=h;s[h].moveBy(a,0),i=this.$getSelectedRows(s[h]);for(var u=i.first,f=i.last;++hf+1)break;f=p.last}for(h--,a=this.session.$moveLines(u,f,t?0:e),t&&-1==e&&(d=h+1);h>=d;)s[d].moveBy(a,0),d++;t||(a=0),l+=a}r.fromOrientedRange(r.ranges[0]),r.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},this.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var i=this.renderer,n=this.renderer.layerConfig,r=e*Math.floor(n.height/n.lineHeight);this.$blockScrolling++,t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(r,0)}):t===!1&&(this.selection.moveCursorBy(r,0),this.selection.clearSelection()),this.$blockScrolling--;var o=i.scrollTop;i.scrollBy(0,r*n.lineHeight),null!=t&&i.scrollCursorIntoView(null,.5),i.animateScrolling(o)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,i,n){this.renderer.scrollToLine(e,t,i,n)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var i=this.getCursorPosition(),n=new w(this.session,i.row,i.column),r=n.getCurrentToken(),o=r||n.stepForward();if(o){var s,a,l=!1,c={},h=i.column-o.start,d={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(o.value.match(/[{}()\[\]]/g)){for(;h=0;--o)this.$tryReplace(i[o],e)&&n++;return this.selection.setSelectionRange(r),this.$blockScrolling-=1,n},this.$tryReplace=function(e,t){var i=this.session.getTextRange(e);return t=this.$search.replace(i,t),null!==t?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,i){t||(t={}),"string"==typeof e||e instanceof RegExp?t.needle=e:"object"==typeof e&&n.mixin(t,e);var r=this.selection.getRange();null==t.needle&&(e=this.session.getTextRange(r)||this.$search.$options.needle,e||(r=this.session.getWordRange(r.start.row,r.start.column),e=this.session.getTextRange(r)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:r});var o=this.$search.find(this.session);return t.preventScroll?o:o?(this.revealRange(o,i),o):(t.backwards?r.start=r.end:r.end=r.start,void this.selection.setRange(r))},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.$blockScrolling+=1,this.session.unfold(e),this.selection.setSelectionRange(e),this.$blockScrolling-=1;var i=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(i)},this.undo=function(){this.$blockScrolling++,this.session.getUndoManager().undo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.$blockScrolling++,this.session.getUndoManager().redo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy()},this.setAutoScrollEditorIntoView=function(e){if(e){var t,i=this,n=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var r=this.$scrollAnchor;r.style.cssText="position:absolute",this.container.insertBefore(r,this.container.firstChild);var o=this.on("changeSelection",function(){n=!0}),s=this.renderer.on("beforeRender",function(){n&&(t=i.renderer.container.getBoundingClientRect())}),a=this.renderer.on("afterRender",function(){if(n&&t&&(i.isFocused()||i.searchBox&&i.searchBox.isFocused())){var e=i.renderer,o=e.$cursorLayer.$pixelPos,s=e.layerConfig,a=o.top-s.offset;n=o.top>=0&&a+t.top<0?!0:o.topwindow.innerHeight?!1:null,null!=n&&(r.style.top=a+"px",r.style.left=o.left+"px",r.style.height=s.lineHeight+"px",r.scrollIntoView(n)),n=t=null}});this.setAutoScrollEditorIntoView=function(e){e||(delete this.setAutoScrollEditorIntoView,this.off("changeSelection",o),this.renderer.off("afterRender",a),this.renderer.off("beforeRender",s))}}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;t&&(t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&"wide"!=e,r.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e)))}}).call(y.prototype),v.defineOptions(y.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.$resetCursorStyle()},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.keybindingId},handlesSet:!0},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",showLineNumbers:"renderer",showGutter:"renderer",displayIndentGuides:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"}),t.Editor=y}),ace.define("ace/undomanager",["require","exports","module"],function(e,t,i){"use strict";var n=function(){this.reset()};(function(){function e(e){return{action:e.action,start:e.start,end:e.end,lines:1==e.lines.length?null:e.lines,text:1==e.lines.length?e.lines[0]:null}}function t(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines||[e.text]}}function i(e,t){for(var i=new Array(e.length),n=0;n0},this.hasRedo=function(){return this.$redoStack.length>0},this.markClean=function(){this.dirtyCounter=0},this.isClean=function(){return 0===this.dirtyCounter},this.$serializeDeltas=function(t){return i(t,e)},this.$deserializeDeltas=function(e){return i(e,t)}}).call(n.prototype),t.UndoManager=n}),ace.define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter"],function(e,t,i){"use strict";var n=e("../lib/dom"),r=e("../lib/oop"),o=e("../lib/lang"),s=e("../lib/event_emitter").EventEmitter,a=function(e){this.element=n.createElement("div"),this.element.className="ace_layer ace_gutter-layer",e.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$annotations=[],this.$updateAnnotations=this.$updateAnnotations.bind(this),this.$cells=[]};(function(){r.implement(this,s),this.setSession=function(e){this.session&&this.session.removeEventListener("change",this.$updateAnnotations),this.session=e,e&&e.on("change",this.$updateAnnotations)},this.addGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.addGutterDecoration"),this.session.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.removeGutterDecoration"),this.session.removeGutterDecoration(e,t)},this.setAnnotations=function(e){this.$annotations=[];for(var t=0;ts&&(m=o.end.row+1,o=t.getNextFoldLine(m,o),s=o?o.start.row:1/0),m>r){for(;this.$cells.length>p+1;)f=this.$cells.pop(),this.element.removeChild(f.element);break}f=this.$cells[++p],f||(f={element:null,textNode:null,foldWidget:null},f.element=n.createElement("div"),f.textNode=document.createTextNode(""),f.element.appendChild(f.textNode),this.element.appendChild(f.element),this.$cells[p]=f);var g="ace_gutter-cell ";l[m]&&(g+=l[m]),c[m]&&(g+=c[m]),this.$annotations[m]&&(g+=this.$annotations[m].className),f.element.className!=g&&(f.element.className=g);var v=t.getRowLength(m)*e.lineHeight+"px";if(v!=f.element.style.height&&(f.element.style.height=v),a){var w=a[m];null==w&&(w=a[m]=t.getFoldWidget(m))}if(w){f.foldWidget||(f.foldWidget=n.createElement("span"),f.element.appendChild(f.foldWidget));var g="ace_fold-widget ace_"+w;g+="start"==w&&m==s&&mi.right-t.right?"foldWidgets":void 0}}).call(a.prototype),t.Gutter=a}),ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,i){"use strict";var n=e("../range").Range,r=e("../lib/dom"),o=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){function e(e,t,i,n){return(e?1:0)|(t?2:0)|(i?4:0)|(n?8:0)}this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.update=function(e){var e=e||this.config;if(e){this.config=e;var t=[];for(var i in this.markers){var n=this.markers[i];if(n.range){var r=n.range.clipRows(e.firstRow,e.lastRow);if(!r.isEmpty())if(r=r.toScreenRange(this.session),n.renderer){var o=this.$getTop(r.start.row,e),s=this.$padding+r.start.column*e.characterWidth;n.renderer(t,r,s,o,e)}else"fullLine"==n.type?this.drawFullLineMarker(t,r,n.clazz,e):"screenLine"==n.type?this.drawScreenLineMarker(t,r,n.clazz,e):r.isMultiLine()?"text"==n.type?this.drawTextMarker(t,r,n.clazz,e):this.drawMultiLineMarker(t,r,n.clazz,e):this.drawSingleLineMarker(t,r,n.clazz+" ace_start ace_br15",e)}else n.update(t,this,this.session,e)}this.element.innerHTML=t.join("")}},this.$getTop=function(e,t){return(e-t.firstRowScreen)*t.lineHeight},this.drawTextMarker=function(t,i,r,o,s){for(var a=this.session,l=i.start.row,c=i.end.row,h=l,d=0,u=0,f=a.getScreenLastRowColumn(h),p=new n(h,i.start.column,h,u);c>=h;h++)p.start.row=p.end.row=h,p.start.column=h==l?i.start.column:a.getRowWrapIndent(h),p.end.column=f,d=u,u=f,f=c>h+1?a.getScreenLastRowColumn(h+1):h==c?0:i.end.column,this.drawSingleLineMarker(t,p,r+(h==l?" ace_start":"")+" ace_br"+e(h==l||h==l+1&&i.start.column,u>d,u>f,h==c),o,h==c?0:1,s)},this.drawMultiLineMarker=function(e,t,i,n,r){var o=this.$padding,s=n.lineHeight,a=this.$getTop(t.start.row,n),l=o+t.start.column*n.characterWidth;r=r||"",e.push("
"),a=this.$getTop(t.end.row,n);var c=t.end.column*n.characterWidth;if(e.push("
"), +s=(t.end.row-t.start.row-1)*n.lineHeight,!(0>=s)){a=this.$getTop(t.start.row+1,n);var h=(t.start.column?1:0)|(t.end.column?0:8);e.push("
")}},this.drawSingleLineMarker=function(e,t,i,n,r,o){var s=n.lineHeight,a=(t.end.column+(r||0)-t.start.column)*n.characterWidth,l=this.$getTop(t.start.row,n),c=this.$padding+t.start.column*n.characterWidth;e.push("
")},this.drawFullLineMarker=function(e,t,i,n,r){var o=this.$getTop(t.start.row,n),s=n.lineHeight;t.start.row!=t.end.row&&(s+=this.$getTop(t.end.row,n)-o),e.push("
")},this.drawScreenLineMarker=function(e,t,i,n,r){var o=this.$getTop(t.start.row,n),s=n.lineHeight;e.push("
")}}).call(o.prototype),t.Marker=o}),ace.define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,i){"use strict";var n=e("../lib/oop"),r=e("../lib/dom"),o=e("../lib/lang"),s=(e("../lib/useragent"),e("../lib/event_emitter").EventEmitter),a=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this)};(function(){n.implement(this,s),this.EOF_CHAR="¶",this.EOL_CHAR_LF="¬",this.EOL_CHAR_CRLF="¤",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR="—",this.SPACE_CHAR="·",this.$padding=0,this.$updateEolChar=function(){var e="\n"==this.session.doc.getNewLineCharacter()?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;return this.EOL_CHAR!=e?(this.EOL_CHAR=e,!0):void 0},this.setPadding=function(e){this.$padding=e,this.element.style.padding="0 "+e+"px"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;for(var t=this.$tabStrings=[0],i=1;e+1>i;i++)this.showInvisibles?t.push(""+o.stringRepeat(this.TAB_CHAR,i)+""):t.push(o.stringRepeat(" ",i));if(this.displayIndentGuides){this.$indentGuideRe=/\s\S| \t|\t |\s$/;var n="ace_indent-guide",r="",s="";if(this.showInvisibles){n+=" ace_invisible",r=" ace_invisible_space",s=" ace_invisible_tab";var a=o.stringRepeat(this.SPACE_CHAR,this.tabSize),l=o.stringRepeat(this.TAB_CHAR,this.tabSize)}else var a=o.stringRepeat(" ",this.tabSize),l=a;this.$tabStrings[" "]=""+a+"",this.$tabStrings[" "]=""+l+""}},this.updateLines=function(e,t,i){this.config.lastRow==e.lastRow&&this.config.firstRow==e.firstRow||this.scrollLines(e),this.config=e;for(var n=Math.max(t,e.firstRow),r=Math.min(i,e.lastRow),o=this.element.childNodes,s=0,a=e.firstRow;n>a;a++){var l=this.session.getFoldLine(a);if(l){if(l.containsRow(n)){n=l.start.row;break}a=l.end.row}s++}for(var a=n,l=this.session.getNextFoldLine(a),c=l?l.start.row:1/0;;){if(a>c&&(a=l.end.row+1,l=this.session.getNextFoldLine(a,l),c=l?l.start.row:1/0),a>r)break;var h=o[s++];if(h){var d=[];this.$renderLine(d,a,!this.$useLineGroups(),a==c?l:!1),h.style.height=e.lineHeight*this.session.getRowLength(a)+"px",h.innerHTML=d.join("")}a++}},this.scrollLines=function(e){var t=this.config;if(this.config=e,!t||t.lastRow0;n--)i.removeChild(i.firstChild);if(t.lastRow>e.lastRow)for(var n=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);n>0;n--)i.removeChild(i.lastChild);if(e.firstRowt.lastRow){var r=this.$renderLinesFragment(e,t.lastRow+1,e.lastRow);i.appendChild(r)}},this.$renderLinesFragment=function(e,t,i){for(var n=this.element.ownerDocument.createDocumentFragment(),o=t,s=this.session.getNextFoldLine(o),a=s?s.start.row:1/0;;){if(o>a&&(o=s.end.row+1,s=this.session.getNextFoldLine(o,s),a=s?s.start.row:1/0),o>i)break;var l=r.createElement("div"),c=[];if(this.$renderLine(c,o,!1,o==a?s:!1),l.innerHTML=c.join(""),this.$useLineGroups())l.className="ace_line_group",n.appendChild(l),l.style.height=e.lineHeight*this.session.getRowLength(o)+"px";else for(;l.firstChild;)n.appendChild(l.firstChild);o++}return n},this.update=function(e){this.config=e;for(var t=[],i=e.firstRow,n=e.lastRow,r=i,o=this.session.getNextFoldLine(r),s=o?o.start.row:1/0;;){if(r>s&&(r=o.end.row+1,o=this.session.getNextFoldLine(r,o),s=o?o.start.row:1/0),r>n)break;this.$useLineGroups()&&t.push("
"),this.$renderLine(t,r,!1,r==s?o:!1),this.$useLineGroups()&&t.push("
"),r++}this.element.innerHTML=t.join("")},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,i,n){var r=this,s=/\t|&|<|>|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\u3000\uFEFF\uFFF9-\uFFFC])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g,a=function(e,i,n,s,a){if(i)return r.showInvisibles?""+o.stringRepeat(r.SPACE_CHAR,e.length)+"":e;if("&"==e)return"&";if("<"==e)return"<";if(">"==e)return">";if(" "==e){var l=r.session.getScreenTabSize(t+s);return t+=l-1,r.$tabStrings[l]}if(" "==e){var c=r.showInvisibles?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",h=r.showInvisibles?r.SPACE_CHAR:"";return t+=1,""+h+""}return n?""+r.SPACE_CHAR+"":(t+=1,""+e+"")},l=n.replace(s,a);if(this.$textToken[i.type])e.push(l);else{var c="ace_"+i.type.replace(/\./g," ace_"),h="";"fold"==i.type&&(h=" style='width:"+i.value.length*this.config.characterWidth+"px;' "),e.push("",l,"")}return t+n.length},this.renderIndentGuide=function(e,t,i){var n=t.search(this.$indentGuideRe);return 0>=n||n>=i?t:" "==t[0]?(n-=n%this.tabSize,e.push(o.stringRepeat(this.$tabStrings[" "],n/this.tabSize)),t.substr(n)):" "==t[0]?(e.push(o.stringRepeat(this.$tabStrings[" "],n)),t.substr(n)):t},this.$renderWrappedLine=function(e,t,i,n){for(var r=0,s=0,a=i[0],l=0,c=0;c=a;)l=this.$renderToken(e,l,h,d.substring(0,a-r)),d=d.substring(a-r),r=a,n||e.push("
","
"),e.push(o.stringRepeat(" ",i.indent)),s++,l=0,a=i[s]||Number.MAX_VALUE;0!=d.length&&(r+=d.length,l=this.$renderToken(e,l,h,d))}}},this.$renderSimpleLine=function(e,t){var i=0,n=t[0],r=n.value;this.displayIndentGuides&&(r=this.renderIndentGuide(e,r)),r&&(i=this.$renderToken(e,i,n,r));for(var o=1;o"),r.length){var o=this.session.getRowSplitData(t);o&&o.length?this.$renderWrappedLine(e,r,o,i):this.$renderSimpleLine(e,r)}this.showInvisibles&&(n&&(t=n.end.row),e.push("",t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,"")),i||e.push("
")},this.$getFoldLineTokens=function(e,t){function i(e,t,i){for(var n=0,o=0;o+e[n].value.lengthi-t&&(s=s.substring(0,i-t)),r.push({type:e[n].type,value:s}),o=t+s.length,n+=1}for(;i>o&&ni?r.push({type:e[n].type,value:s.substring(0,i-o)}):r.push(e[n]),o+=s.length,n+=1}}var n=this.session,r=[],o=n.getTokens(e);return t.walk(function(e,t,s,a,l){null!=e?r.push({type:"fold",value:e}):(l&&(o=n.getTokens(t)),o.length&&i(o,a,s))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(a.prototype),t.Text=a}),ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,i){"use strict";var n,r=e("../lib/dom"),o=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),void 0===n&&(n=!("opacity"in this.element.style)),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=(n?this.$updateVisibility:this.$updateOpacity).bind(this)};(function(){this.$updateVisibility=function(e){for(var t=this.cursors,i=t.length;i--;)t[i].style.visibility=e?"":"hidden"},this.$updateOpacity=function(e){for(var t=this.cursors,i=t.length;i--;)t[i].style.opacity=e?"":"0"},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e==this.smoothBlinking||n||(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.$updateCursors=this.$updateOpacity.bind(this),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;if(clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0),this.isBlinking&&this.blinkInterval&&this.isVisible){this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()}},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var i=this.session.documentToScreenPosition(e),n=this.$padding+i.column*this.config.characterWidth,r=(i.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:n,top:r}},this.update=function(e){this.config=e;var t=this.session.$selectionMarkers,i=0,n=0;void 0!==t&&0!==t.length||(t=[{cursor:null}]);for(var i=0,r=t.length;r>i;i++){var o=this.getPixelPosition(t[i].cursor,!0);if(!((o.top>e.height+e.offset||o.top<0)&&i>1)){var s=(this.cursors[n++]||this.addCursor()).style;this.drawCursor?this.drawCursor(s,o,e,t[i],this.session):(s.left=o.left+"px",s.top=o.top+"px",s.width=e.characterWidth+"px",s.height=e.lineHeight+"px")}}for(;this.cursors.length>n;)this.removeCursor();var a=this.session.getOverwrite();this.$setOverwrite(a),this.$pixelPos=o,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(o.prototype),t.Cursor=o}),ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,i){"use strict";var n=e("./lib/oop"),r=e("./lib/dom"),o=e("./lib/event"),s=e("./lib/event_emitter").EventEmitter,a=function(e){this.element=r.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=r.createElement("div"),this.inner.className="ace_scrollbar-inner",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,o.addListener(this.element,"scroll",this.onScroll.bind(this)),o.addListener(this.element,"mousedown",o.preventDefault)};(function(){n.implement(this,s),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e}}).call(a.prototype);var l=function(e,t){a.call(this,e),this.scrollTop=0,t.$scrollbarWidth=this.width=r.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px"};n.inherits(l,a),function(){this.classSuffix="-v",this.onScroll=function(){this.skipEvent||(this.scrollTop=this.element.scrollTop,this._emit("scroll",{data:this.scrollTop})),this.skipEvent=!1},this.getWidth=function(){return this.isVisible?this.width:0},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=function(e){this.inner.style.height=e+"px"},this.setScrollHeight=function(e){this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=this.element.scrollTop=e)}}.call(l.prototype);var c=function(e,t){a.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px"};n.inherits(c,a),function(){this.classSuffix="-h",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollWidth=function(e){this.inner.style.width=e+"px"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(c.prototype),t.ScrollBar=l,t.ScrollBarV=l,t.ScrollBarH=c,t.VScrollBar=l,t.HScrollBar=c}),ace.define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,i){"use strict";var n=e("./lib/event"),r=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.window=t||window};(function(){this.schedule=function(e){if(this.changes=this.changes|e,!this.pending&&this.changes){this.pending=!0;var t=this;n.nextFrame(function(){t.pending=!1;for(var e;e=t.changes;)t.changes=0,t.onRender(e)},this.window)}}}).call(r.prototype),t.RenderLoop=r}),ace.define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,i){var n=e("../lib/oop"),r=e("../lib/dom"),o=e("../lib/lang"),s=e("../lib/useragent"),a=e("../lib/event_emitter").EventEmitter,l=0,c=t.FontMetrics=function(e){this.el=r.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=r.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=r.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),l||this.$testFractionalRect(),this.$measureNode.innerHTML=o.stringRepeat("X",l),this.$characterSize={width:0,height:0},this.checkForSizeChanges()};(function(){n.implement(this,a),this.$characterSize={width:0,height:0},this.$testFractionalRect=function(){var e=r.createElement("div");this.$setMeasureNodeStyles(e.style),e.style.width="0.2px",document.documentElement.appendChild(e);var t=e.getBoundingClientRect().width;l=t>0&&1>t?50:100,e.parentNode.removeChild(e)},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",s.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(){var e=this.$measureSizes();if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=setInterval(function(){e.checkForSizeChanges()},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(){if(50===l){var e=null;try{e=this.$measureNode.getBoundingClientRect()}catch(t){e={width:0,height:0}}var i={height:e.height,width:e.width/l}}else var i={height:this.$measureNode.clientHeight,width:this.$measureNode.clientWidth/l};return 0===i.width||0===i.height?null:i},this.$measureCharWidth=function(e){this.$main.innerHTML=o.stringRepeat(e,l);var t=this.$main.getBoundingClientRect();return t.width/l},this.getCharacterWidth=function(e){var t=this.charSizes[e];return void 0===t&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)}}).call(c.prototype)}),ace.define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/lib/useragent","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter"],function(e,t,i){"use strict";var n=e("./lib/oop"),r=e("./lib/dom"),o=e("./config"),s=e("./lib/useragent"),a=e("./layer/gutter").Gutter,l=e("./layer/marker").Marker,c=e("./layer/text").Text,h=e("./layer/cursor").Cursor,d=e("./scrollbar").HScrollBar,u=e("./scrollbar").VScrollBar,f=e("./renderloop").RenderLoop,p=e("./layer/font_metrics").FontMetrics,m=e("./lib/event_emitter").EventEmitter,g='.ace_editor { position: relative; overflow: hidden; font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace; direction: ltr; } .ace_scroller { position: absolute; overflow: hidden; top: 0; bottom: 0; background-color: inherit; -ms-user-select: none; -moz-user-select: none; -webkit-user-select: none; user-select: none; cursor: text; } .ace_content { position: absolute; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; min-width: 100%; } .ace_dragging .ace_scroller:before{ position: absolute; top: 0; left: 0; right: 0; bottom: 0; content: \'\'; background: rgba(250, 250, 250, 0.01); z-index: 1000; } .ace_dragging.ace_dark .ace_scroller:before{ background: rgba(0, 0, 0, 0.01); } .ace_selecting, .ace_selecting * { cursor: text !important; } .ace_gutter { position: absolute; overflow : hidden; width: auto; top: 0; bottom: 0; left: 0; cursor: default; z-index: 4; -ms-user-select: none; -moz-user-select: none; -webkit-user-select: none; user-select: none; } .ace_gutter-active-line { position: absolute; left: 0; right: 0; } .ace_scroller.ace_scroll-left { box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset; } .ace_gutter-cell { padding-left: 19px; padding-right: 6px; background-repeat: no-repeat; } .ace_gutter-cell.ace_error { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg=="); background-repeat: no-repeat; background-position: 2px center; } .ace_gutter-cell.ace_warning { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg=="); background-position: 2px center; } .ace_gutter-cell.ace_info { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII="); background-position: 2px center; } .ace_dark .ace_gutter-cell.ace_info { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC"); } .ace_scrollbar { position: absolute; right: 0; bottom: 0; z-index: 6; } .ace_scrollbar-inner { position: absolute; cursor: text; left: 0; top: 0; } .ace_scrollbar-v{ overflow-x: hidden; overflow-y: scroll; top: 0; } .ace_scrollbar-h { overflow-x: scroll; overflow-y: hidden; left: 0; } .ace_print-margin { position: absolute; height: 100%; } .ace_text-input { position: absolute; z-index: 0; width: 0.5em; height: 1em; opacity: 0; background: transparent; -moz-appearance: none; appearance: none; border: none; resize: none; outline: none; overflow: hidden; font: inherit; padding: 0 1px; margin: 0 -1px; text-indent: -1em; -ms-user-select: text; -moz-user-select: text; -webkit-user-select: text; user-select: text; white-space: pre!important; } .ace_text-input.ace_composition { background: inherit; color: inherit; z-index: 1000; opacity: 1; text-indent: 0; } .ace_layer { z-index: 1; position: absolute; overflow: hidden; word-wrap: normal; white-space: pre; height: 100%; width: 100%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; pointer-events: none; } .ace_gutter-layer { position: relative; width: auto; text-align: right; pointer-events: auto; } .ace_text-layer { font: inherit !important; } .ace_cjk { display: inline-block; text-align: center; } .ace_cursor-layer { z-index: 4; } .ace_cursor { z-index: 4; position: absolute; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; border-left: 2px solid; transform: translatez(0); } .ace_slim-cursors .ace_cursor { border-left-width: 1px; } .ace_overwrite-cursors .ace_cursor { border-left-width: 0; border-bottom: 1px solid; } .ace_hidden-cursors .ace_cursor { opacity: 0.2; } .ace_smooth-blinking .ace_cursor { -webkit-transition: opacity 0.18s; transition: opacity 0.18s; } .ace_editor.ace_multiselect .ace_cursor { border-left-width: 1px; } .ace_marker-layer .ace_step, .ace_marker-layer .ace_stack { position: absolute; z-index: 3; } .ace_marker-layer .ace_selection { position: absolute; z-index: 5; } .ace_marker-layer .ace_bracket { position: absolute; z-index: 6; } .ace_marker-layer .ace_active-line { position: absolute; z-index: 2; } .ace_marker-layer .ace_selected-word { position: absolute; z-index: 4; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } .ace_line .ace_fold { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; display: inline-block; height: 11px; margin-top: -2px; vertical-align: middle; background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="), url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII="); background-repeat: no-repeat, repeat-x; background-position: center center, top left; color: transparent; border: 1px solid black; border-radius: 2px; cursor: pointer; pointer-events: auto; } .ace_dark .ace_fold { } .ace_fold:hover{ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="), url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC"); } .ace_tooltip { background-color: #FFF; background-image: -webkit-linear-gradient(top, transparent, rgba(0, 0, 0, 0.1)); background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1)); border: 1px solid gray; border-radius: 1px; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); color: black; max-width: 100%; padding: 3px 4px; position: fixed; z-index: 999999; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; cursor: default; white-space: pre; word-wrap: break-word; line-height: normal; font-style: normal; font-weight: normal; letter-spacing: normal; pointer-events: none; } .ace_folding-enabled > .ace_gutter-cell { padding-right: 13px; } .ace_fold-widget { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; margin: 0 -12px 0 1px; display: none; width: 11px; vertical-align: top; background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg=="); background-repeat: no-repeat; background-position: center; border-radius: 3px; border: 1px solid transparent; cursor: pointer; } .ace_folding-enabled .ace_fold-widget { display: inline-block; } .ace_fold-widget.ace_end { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg=="); } .ace_fold-widget.ace_closed { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA=="); } .ace_fold-widget:hover { border: 1px solid rgba(0, 0, 0, 0.3); background-color: rgba(255, 255, 255, 0.2); box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7); } .ace_fold-widget:active { border: 1px solid rgba(0, 0, 0, 0.4); background-color: rgba(0, 0, 0, 0.05); box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8); } .ace_dark .ace_fold-widget { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC"); } .ace_dark .ace_fold-widget.ace_end { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg=="); } .ace_dark .ace_fold-widget.ace_closed { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg=="); } .ace_dark .ace_fold-widget:hover { box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2); background-color: rgba(255, 255, 255, 0.1); } .ace_dark .ace_fold-widget:active { box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2); } .ace_fold-widget.ace_invalid { background-color: #FFB4B4; border-color: #DE5555; } .ace_fade-fold-widgets .ace_fold-widget { -webkit-transition: opacity 0.4s ease 0.05s; transition: opacity 0.4s ease 0.05s; opacity: 0; } .ace_fade-fold-widgets:hover .ace_fold-widget { -webkit-transition: opacity 0.05s ease 0.05s; transition: opacity 0.05s ease 0.05s; opacity:1; } .ace_underline { text-decoration: underline; } .ace_bold { font-weight: bold; } .ace_nobold .ace_bold { font-weight: normal; } .ace_italic { font-style: italic; } .ace_error-marker { background-color: rgba(255, 0, 0,0.2); position: absolute; z-index: 9; } .ace_highlight-marker { background-color: rgba(255, 255, 0,0.2); position: absolute; z-index: 8; } .ace_br1 {border-top-left-radius : 3px;} .ace_br2 {border-top-right-radius : 3px;} .ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;} .ace_br4 {border-bottom-right-radius: 3px;} .ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;} .ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;} .ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;} .ace_br8 {border-bottom-left-radius : 3px;} .ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;} .ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;} .ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;} .ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;} .ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;} .ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;} .ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;} '; +r.importCssString(g,"ace_editor.css");var v=function(e,t){var i=this;this.container=e||r.createElement("div"),this.$keepTextAreaAtCursor=!s.isOldIE,r.addCssClass(this.container,"ace_editor"),this.setTheme(t),this.$gutter=r.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=r.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=r.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new a(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new l(this.content);var n=this.$textLayer=new c(this.content);this.canvas=n.element,this.$markerFront=new l(this.content),this.$cursorLayer=new h(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new u(this.container,this),this.scrollBarH=new d(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){i.$scrollAnimation||i.session.setScrollTop(e.data-i.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){i.$scrollAnimation||i.session.setScrollLeft(e.data-i.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new p(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener("changeCharacterSize",function(e){i.updateCharacterSize(),i.onResize(!0,i.gutterWidth,i.$size.width,i.$size.height),i._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$loop=new f(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),o.resetOptions(this),o._emit("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,n.implement(this,m),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e),e&&(this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode))},this.updateLines=function(e,t,i){if(void 0===t&&(t=1/0),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow||this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar()},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,i,n){if(!(this.resizing>2)){this.resizing>0?this.resizing++:this.resizing=e?1:0;var r=this.container;n||(n=r.clientHeight||r.scrollHeight),i||(i=r.clientWidth||r.scrollWidth);var o=this.$updateCachedSize(e,t,i,n);if(!this.$size.scrollerHeight||!i&&!n)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(o|this.$changes,!0):this.$loop.schedule(o|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null}},this.$updateCachedSize=function(e,t,i,n){n-=this.$extraHeight||0;var r=0,o=this.$size,s={width:o.width,height:o.height,scrollerHeight:o.scrollerHeight,scrollerWidth:o.scrollerWidth};return n&&(e||o.height!=n)&&(o.height=n,r|=this.CHANGE_SIZE,o.scrollerHeight=o.height,this.$horizScroll&&(o.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",r|=this.CHANGE_SCROLL),i&&(e||o.width!=i)&&(r|=this.CHANGE_SIZE,o.width=i,null==t&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,this.scrollBarH.element.style.left=this.scroller.style.left=t+"px",o.scrollerWidth=Math.max(0,i-t-this.scrollBarV.getWidth()),this.scrollBarH.element.style.right=this.scroller.style.right=this.scrollBarV.getWidth()+"px",this.scroller.style.bottom=this.scrollBarH.getHeight()+"px",(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)&&(r|=this.CHANGE_FULL)),o.$dirty=!i||!n,r&&this._signal("resize",s),r},this.onGutterResize=function(){var e=this.$showGutter?this.$gutter.offsetWidth:0;e!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,e,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):(this.$computeLayerConfig(),this.$loop.schedule(this.CHANGE_MARKER))},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-2*this.$padding,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updateGutterLineHighlight=function(){var e=this.$cursorLayer.$pixelPos,t=this.layerConfig.lineHeight;if(this.session.getUseWrapMode()){var i=this.session.selection.getCursor();i.column=0,e=this.$cursorLayer.getPixelPosition(i,!0),t*=this.session.getRowLength(i.row)}this.$gutterLineHighlight.style.top=e.top-this.layerConfig.offset+"px",this.$gutterLineHighlight.style.height=t+"px"},this.$updatePrintMargin=function(){if(this.$showPrintMargin||this.$printMarginEl){if(!this.$printMarginEl){var e=r.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=r.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=this.characterWidth*this.$printMarginColumn+this.$padding+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&-1==this.session.$wrap&&this.adjustWrapLimit()}},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.scroller},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){if(this.$keepTextAreaAtCursor){var e=this.layerConfig,t=this.$cursorLayer.$pixelPos.top,i=this.$cursorLayer.$pixelPos.left;t-=e.offset;var n=this.textarea.style,r=this.lineHeight;if(0>t||t>e.height-r)return void(n.top=n.left="0");var o=this.characterWidth;if(this.$composition){var s=this.textarea.value.replace(/^\x01+/,"");o*=this.session.$getStringScreenWidth(s)[0]+2,r+=2}i-=this.scrollLeft,i>this.$size.scrollerWidth-o&&(i=this.$size.scrollerWidth-o),i+=this.gutterWidth,n.height=r+"px",n.width=o+"px",n.left=Math.min(i,this.$size.scrollerWidth-o)+"px",n.top=Math.min(t,this.$size.height-r)+"px"}},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(0===this.layerConfig.offset?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,i=this.session.documentToScreenRow(t,0)*e.lineHeight;return i-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,i,n){var r=this.scrollMargin;r.top=0|e,r.bottom=0|t,r.right=0|n,r.left=0|i,r.v=r.top+r.bottom,r.h=r.left+r.right,r.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-r.top),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){if(this.$changes&&(e|=this.$changes,this.$changes=0),!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t)return void(this.$changes|=e);if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender");var i=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){if(e|=this.$computeLayerConfig(),i.firstRow!=this.layerConfig.firstRow&&i.firstRowScreen==this.layerConfig.firstRowScreen){var n=this.scrollTop+(i.firstRow-this.layerConfig.firstRow)*this.lineHeight;n>0&&(this.scrollTop=n,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig())}i=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),this.$gutterLayer.element.style.marginTop=-i.offset+"px",this.content.style.marginTop=-i.offset+"px",this.content.style.width=i.width+2*this.$padding+"px",this.content.style.height=i.minHeight+"px"}return e&this.CHANGE_H_SCROLL&&(this.content.style.marginLeft=-this.scrollLeft+"px",this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left"),e&this.CHANGE_FULL?(this.$textLayer.update(i),this.$showGutter&&this.$gutterLayer.update(i),this.$markerBack.update(i),this.$markerFront.update(i),this.$cursorLayer.update(i),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),void this._signal("afterRender")):e&this.CHANGE_SCROLL?(e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(i):this.$textLayer.scrollLines(i),this.$showGutter&&this.$gutterLayer.update(i),this.$markerBack.update(i),this.$markerFront.update(i),this.$cursorLayer.update(i),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this.$moveTextAreaToCursor(),void this._signal("afterRender")):(e&this.CHANGE_TEXT?(this.$textLayer.update(i),this.$showGutter&&this.$gutterLayer.update(i)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(i):(e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER)&&this.$showGutter&&this.$gutterLayer.update(i),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(i),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(i),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(i),void this._signal("afterRender"))},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,i=Math.max((this.$minLines||1)*this.lineHeight,Math.min(t,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(i+=this.scrollBarH.getHeight());var n=e>t;if(i!=this.desiredHeight||this.$size.height!=this.desiredHeight||n!=this.$vScroll){n!=this.$vScroll&&(this.$vScroll=n,this.scrollBarV.setVisible(n));var r=this.container.clientWidth;this.container.style.height=i+"px",this.$updateCachedSize(!0,this.$gutterWidth,r,i),this.desiredHeight=i,this._signal("autosize")}},this.$computeLayerConfig=function(){var e=this.session,t=this.$size,i=t.height<=2*this.lineHeight,n=this.session.getScreenLength(),r=n*this.lineHeight,o=this.$getLongestLine(),s=!i&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-o-2*this.$padding<0),a=this.$horizScroll!==s;a&&(this.$horizScroll=s,this.scrollBarH.setVisible(s));var l=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var c=this.scrollTop%this.lineHeight,h=t.scrollerHeight+this.lineHeight,d=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;r+=d;var u=this.scrollMargin;this.session.setScrollTop(Math.max(-u.top,Math.min(this.scrollTop,r-t.scrollerHeight+u.bottom))),this.session.setScrollLeft(Math.max(-u.left,Math.min(this.scrollLeft,o+2*this.$padding-t.scrollerWidth+u.right)));var f=!i&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-r+d<0||this.scrollTop>u.top),p=l!==f;p&&(this.$vScroll=f,this.scrollBarV.setVisible(f));var m,g,v=Math.ceil(h/this.lineHeight)-1,w=Math.max(0,Math.round((this.scrollTop-c)/this.lineHeight)),y=w+v,A=this.lineHeight;w=e.screenToDocumentRow(w,0);var C=e.getFoldLine(w);C&&(w=C.start.row),m=e.documentToScreenRow(w,0),g=e.getRowLength(w)*A,y=Math.min(e.screenToDocumentRow(y,0),e.getLength()-1),h=t.scrollerHeight+e.getRowLength(y)*A+g,c=this.scrollTop-m*A;var E=0;return this.layerConfig.width!=o&&(E=this.CHANGE_H_SCROLL),(a||p)&&(E=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),p&&(o=this.$getLongestLine())),this.layerConfig={width:o,padding:this.$padding,firstRow:w,firstRowScreen:m,lastRow:y,lineHeight:A,characterWidth:this.characterWidth,minHeight:h,maxHeight:r,offset:c,gutterOffset:Math.max(0,Math.ceil((c+t.height-t.scrollerHeight)/A)),height:this.$size.scrollerHeight},E},this.$updateLines=function(){var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var i=this.layerConfig;return e>i.lastRow+1||to?(t&&l+s>o+this.lineHeight&&(o-=t*this.$size.scrollerHeight),0===o&&(o=-this.scrollMargin.top),this.session.setScrollTop(o)):l+this.$size.scrollerHeight-ar?(ri;++i)r.push(o(i/this.STEPS,e,t-e));return r},this.scrollToLine=function(e,t,i,n){var r=this.$cursorLayer.getPixelPosition({row:e,column:0}),o=r.top;t&&(o-=this.$size.scrollerHeight/2);var s=this.scrollTop;this.session.setScrollTop(o),i!==!1&&this.animateScrolling(s,n)},this.animateScrolling=function(e,t){var i=this.scrollTop;if(this.$animatedScroll){var n=this;if(e!=i){if(this.$scrollAnimation){var r=this.$scrollAnimation.steps;if(r.length&&(e=r[0],e==i))return}var o=n.$calcSteps(e,i);this.$scrollAnimation={from:e,to:i,steps:o},clearInterval(this.$timer),n.session.setScrollTop(o.shift()),n.session.$scrollTop=i,this.$timer=setInterval(function(){o.length?(n.session.setScrollTop(o.shift()),n.session.$scrollTop=i):null!=i?(n.session.$scrollTop=-1,n.session.setScrollTop(i),i=null):(n.$timer=clearInterval(n.$timer),n.$scrollAnimation=null,t&&t())},10)}}},this.scrollToY=function(e){this.scrollTop!==e&&(this.$loop.schedule(this.CHANGE_SCROLL),this.scrollTop=e)},this.scrollToX=function(e){this.scrollLeft!==e&&(this.scrollLeft=e),this.$loop.schedule(this.CHANGE_H_SCROLL)},this.scrollTo=function(e,t){this.session.setScrollTop(t),this.session.setScrollLeft(t)},this.scrollBy=function(e,t){t&&this.session.setScrollTop(this.session.getScrollTop()+t),e&&this.session.setScrollLeft(this.session.getScrollLeft()+e)},this.isScrollableBy=function(e,t){return 0>t&&this.session.getScrollTop()>=1-this.scrollMargin.top?!0:t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom?!0:0>e&&this.session.getScrollLeft()>=1-this.scrollMargin.left?!0:e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right?!0:void 0},this.pixelToScreenCoordinates=function(e,t){var i=this.scroller.getBoundingClientRect(),n=(e+this.scrollLeft-i.left-this.$padding)/this.characterWidth,r=Math.floor((t+this.scrollTop-i.top)/this.lineHeight),o=Math.round(n);return{row:r,column:o,side:n-o>0?1:-1}},this.screenToTextCoordinates=function(e,t){var i=this.scroller.getBoundingClientRect(),n=Math.round((e+this.scrollLeft-i.left-this.$padding)/this.characterWidth),r=(t+this.scrollTop-i.top)/this.lineHeight;return this.session.screenToDocumentPosition(r,Math.max(n,0))},this.textToScreenCoordinates=function(e,t){var i=this.scroller.getBoundingClientRect(),n=this.session.documentToScreenPosition(e,t),r=this.$padding+Math.round(n.column*this.characterWidth),o=n.row*this.lineHeight;return{pageX:i.left+r-this.scrollLeft,pageY:i.top+o-this.scrollTop}},this.visualizeFocus=function(){r.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){r.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition||(this.$composition={keepTextAreaAtCursor:this.$keepTextAreaAtCursor,cssText:this.textarea.style.cssText}),this.$keepTextAreaAtCursor=!0,r.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor()},this.setCompositionText=function(e){this.$moveTextAreaToCursor()},this.hideComposition=function(){this.$composition&&(r.removeCssClass(this.textarea,"ace_composition"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null)},this.setTheme=function(e,t){function i(i){if(n.$themeId!=e)return t&&t();if(i.cssClass){r.importCssString(i.cssText,i.cssClass,n.container.ownerDocument),n.theme&&r.removeCssClass(n.container,n.theme.cssClass);var o="padding"in i?i.padding:"padding"in(n.theme||{})?4:n.$padding;n.$padding&&o!=n.$padding&&n.setPadding(o),n.$theme=i.cssClass,n.theme=i,r.addCssClass(n.container,i.cssClass),r.setCssClass(n.container,"ace_dark",i.isDark),n.$size&&(n.$size.width=0,n.$updateSizeAsync()),n._dispatchEvent("themeLoaded",{theme:i}),t&&t()}}var n=this;if(this.$themeId=e,n._dispatchEvent("themeChange",{theme:e}),e&&"string"!=typeof e)i(e);else{var s=e||this.$options.theme.initialValue;o.loadModule(["theme",s],i)}},this.getTheme=function(){return this.$themeId},this.setStyle=function(e,t){r.setCssClass(this.container,e,t!==!1)},this.unsetStyle=function(e){r.removeCssClass(this.container,e)},this.setCursorStyle=function(e){this.scroller.style.cursor!=e&&(this.scroller.style.cursor=e)},this.setMouseCursor=function(e){this.scroller.style.cursor=e},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(v.prototype),o.defineOptions(v.prototype,"renderer",{animatedScroll:{initialValue:!1},showInvisibles:{set:function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!1},showPrintMargin:{set:function(){this.$updatePrintMargin()},initialValue:!0},printMarginColumn:{set:function(){this.$updatePrintMargin()},initialValue:80},printMargin:{set:function(e){"number"==typeof e&&(this.$printMarginColumn=e),this.$showPrintMargin=!!e,this.$updatePrintMargin()},get:function(){return this.$showPrintMargin&&this.$printMarginColumn}},showGutter:{set:function(e){this.$gutter.style.display=e?"block":"none",this.$loop.schedule(this.CHANGE_FULL),this.onGutterResize()},initialValue:!0},fadeFoldWidgets:{set:function(e){r.setCssClass(this.$gutter,"ace_fade-fold-widgets",e)},initialValue:!1},showFoldWidgets:{set:function(e){this.$gutterLayer.setShowFoldWidgets(e)},initialValue:!0},showLineNumbers:{set:function(e){this.$gutterLayer.setShowLineNumbers(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},displayIndentGuides:{set:function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!0},highlightGutterLine:{set:function(e){return this.$gutterLineHighlight?(this.$gutterLineHighlight.style.display=e?"":"none",void(this.$cursorLayer.$pixelPos&&this.$updateGutterLineHighlight())):(this.$gutterLineHighlight=r.createElement("div"),this.$gutterLineHighlight.className="ace_gutter-active-line",void this.$gutter.appendChild(this.$gutterLineHighlight))},initialValue:!1,value:!0},hScrollBarAlwaysVisible:{set:function(e){this.$hScrollBarAlwaysVisible&&this.$horizScroll||this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},vScrollBarAlwaysVisible:{set:function(e){this.$vScrollBarAlwaysVisible&&this.$vScroll||this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},fontSize:{set:function(e){"number"==typeof e&&(e+="px"),this.container.style.fontSize=e,this.updateFontSize()},initialValue:12},fontFamily:{set:function(e){this.container.style.fontFamily=e,this.updateFontSize()}},maxLines:{set:function(e){this.updateFull()}},minLines:{set:function(e){this.updateFull()}},scrollPastEnd:{set:function(e){e=+e||0,this.$scrollPastEnd!=e&&(this.$scrollPastEnd=e,this.$loop.schedule(this.CHANGE_SCROLL))},initialValue:0,handlesSet:!0},fixedWidthGutter:{set:function(e){this.$gutterLayer.$fixedWidth=!!e,this.$loop.schedule(this.CHANGE_GUTTER)}},theme:{set:function(e){this.setTheme(e)},get:function(){return this.$themeId||this.theme},initialValue:"./theme/textmate",handlesSet:!0}}),t.VirtualRenderer=v}),ace.define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/net","ace/lib/event_emitter","ace/config"],function(e,t,n){"use strict";var r=e("../lib/oop"),o=e("../lib/net"),s=e("../lib/event_emitter").EventEmitter,a=e("../config"),l=function(t,n,r,o){if(this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.onMessage=this.onMessage.bind(this),e.nameToUrl&&!e.toUrl&&(e.toUrl=e.nameToUrl),a.get("packaged")||!e.toUrl)o=o||a.moduleUrl(n.id,"worker");else{var s=this.$normalizePath;o=o||s(e.toUrl("ace/worker/worker.js",null,"_"));var l={};t.forEach(function(t){l[t]=s(e.toUrl(t,null,"_").replace(/(\.js)?(\?.*)?$/,""))})}try{var c=n.src,h=i(66),d=new h([c],{type:"application/javascript"}),u=(window.URL||window.webkitURL).createObjectURL(d);this.$worker=new Worker(u)}catch(f){if(!(f instanceof window.DOMException))throw f;var d=this.$workerBlob(o),p=window.URL||window.webkitURL,m=p.createObjectURL(d);this.$worker=new Worker(m),p.revokeObjectURL(m)}this.$worker.postMessage({init:!0,tlns:l,module:n.id,classname:r}),this.callbackId=1,this.callbacks={},this.$worker.onmessage=this.onMessage};(function(){r.implement(this,s),this.onMessage=function(e){var t=e.data;switch(t.type){case"event":this._signal(t.name,{data:t.data});break;case"call":var i=this.callbacks[t.id];i&&(i(t.data),delete this.callbacks[t.id]);break;case"error":this.reportError(t.data);break;case"log":window.console&&console.log&&console.log.apply(console,t.data)}},this.reportError=function(e){window.console&&console.error&&console.error(e)},this.$normalizePath=function(e){return o.qualifyURL(e)},this.terminate=function(){this._signal("terminate",{}),this.deltaQueue=null,this.$worker.terminate(),this.$worker=null,this.$doc&&this.$doc.off("change",this.changeListener),this.$doc=null},this.send=function(e,t){this.$worker.postMessage({command:e,args:t})},this.call=function(e,t,i){if(i){var n=this.callbackId++;this.callbacks[n]=i,t.push(n)}this.send(e,t)},this.emit=function(e,t){try{this.$worker.postMessage({event:e,data:{data:t.data}})}catch(i){console.error(i.stack)}},this.attachToDocument=function(e){this.$doc&&this.terminate(),this.$doc=e,this.call("setValue",[e.getValue()]),e.on("change",this.changeListener)},this.changeListener=function(e){this.deltaQueue||(this.deltaQueue=[],setTimeout(this.$sendDeltaQueue,0)),"insert"==e.action?this.deltaQueue.push(e.start,e.lines):this.deltaQueue.push(e.start,e.end)},this.$sendDeltaQueue=function(){var e=this.deltaQueue;e&&(this.deltaQueue=null,e.length>50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e}))},this.$workerBlob=function(e){var t="importScripts('"+o.qualifyURL(e)+"');";try{return new Blob([t],{type:"application/javascript"})}catch(i){var n=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder,r=new n;return r.append(t),r.getBlob("application/javascript")}}}).call(l.prototype);var c=function(e,t,i){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.callbackId=1,this.callbacks={},this.messageBuffer=[];var n=null,r=!1,o=Object.create(s),l=this;this.$worker={},this.$worker.terminate=function(){},this.$worker.postMessage=function(e){l.messageBuffer.push(e),n&&(r?setTimeout(c):c())},this.setEmitSync=function(e){r=e};var c=function(){var e=l.messageBuffer.shift();e.command?n[e.command].apply(n,e.args):e.event&&o._signal(e.event,e.data)};o.postMessage=function(e){l.onMessage({data:e})},o.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},o.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},a.loadModule(["worker",t],function(e){for(n=new e[i](o);l.messageBuffer.length;)c()})};c.prototype=l.prototype,t.UIWorkerClient=c,t.WorkerClient=l}),ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,i){"use strict";var n=e("./range").Range,r=e("./lib/event_emitter").EventEmitter,o=e("./lib/oop"),s=function(e,t,i,n,r,o){var s=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=r,this.othersClass=o,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=n,this.$onCursorChange=function(){setTimeout(function(){s.onCursorChange()})},this.$pos=i;var a=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=a.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){o.implement(this,r),this.setup=function(){var e=this,t=this.doc,i=this.session;this.selectionBefore=i.selection.toJSON(),i.selection.inMultiSelectMode&&i.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var r=this.pos;r.$insertRight=!0,r.detach(),r.markerId=i.addMarker(new n(r.row,r.column,r.row,r.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(i){var n=t.createAnchor(i.row,i.column);n.$insertRight=!0,n.detach(),e.others.push(n)}),i.setUndoSelect(!1)},this.showOtherMarkers=function(){if(!this.othersActive){var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(i){i.markerId=e.addMarker(new n(i.row,i.column,i.row,i.column+t.length),t.othersClass,null,!1)})}},this.hideOtherMarkers=function(){if(this.othersActive){this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,o=t.start.column-this.pos.column;if(this.updateAnchors(e),r&&(this.length+=i),r&&!this.session.$fromUndo)if("insert"===e.action)for(var s=this.others.length-1;s>=0;s--){var a=this.others[s],l={row:a.row,column:a.column+o};this.doc.insertMergedLines(l,e.lines)}else if("remove"===e.action)for(var s=this.others.length-1;s>=0;s--){var a=this.others[s],l={row:a.row,column:a.column+o};this.doc.remove(new n(l.row,l.column,l.row,l.column-i))}this.$updating=!1,this.updateMarkers()}},this.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},this.updateMarkers=function(){if(!this.$updating){var e=this,t=this.session,i=function(i,r){t.removeMarker(i.markerId),i.markerId=t.addMarker(new n(i.row,i.column,i.row,i.column+e.length),r,null,!1)};i(this.pos,this.mainClass);for(var r=this.others.length;r--;)i(this.others[r],this.othersClass)}},this.onCursorChange=function(e){if(!this.$updating&&this.session){var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))}},this.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},this.cancel=function(){if(-1!==this.$undoStackDepth){for(var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth,i=0;t>i;i++)e.undo(!0);this.selectionBefore&&this.session.selection.fromJSON(this.selectionBefore)}}}).call(s.prototype),t.PlaceHolder=s}),ace.define("ace/mouse/multi_select_handler",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,i){function n(e,t){return e.row==t.row&&e.column==t.column}function r(e){var t=e.domEvent,i=t.altKey,r=t.shiftKey,a=t.ctrlKey,l=e.getAccelKey(),c=e.getButton();if(a&&s.isMac&&(c=t.button),e.editor.inMultiSelectMode&&2==c)return void e.editor.textInput.onContextMenu(e.domEvent);if(!a&&!i&&!l)return void(0===c&&e.editor.inMultiSelectMode&&e.editor.exitMultiSelectMode());if(0===c){var h,d=e.editor,u=d.selection,f=d.inMultiSelectMode,p=e.getDocumentPosition(),m=u.getCursor(),g=e.inSelection()||u.isEmpty()&&n(p,m),v=e.x,w=e.y,y=function(e){v=e.clientX,w=e.clientY},A=d.session,C=d.renderer.pixelToScreenCoordinates(v,w),E=C;if(d.$mouseHandler.$enableJumpToDef)a&&i||l&&i?h=r?"block":"add":i&&d.$blockSelectEnabled&&(h="block");else if(l&&!i){if(h="add",!f&&r)return}else i&&d.$blockSelectEnabled&&(h="block");if(h&&s.isMac&&t.ctrlKey&&d.$mouseHandler.cancelContextMenu(),"add"==h){if(!f&&g)return;if(!f){var b=u.toOrientedRange();d.addSelectionMarker(b)}var F=u.rangeList.rangeAtPoint(p);d.$blockScrolling++,d.inVirtualSelectionMode=!0,r&&(F=null,b=u.ranges[0]||b,d.removeSelectionMarker(b)),d.once("mouseup",function(){var e=u.toOrientedRange();F&&e.isEmpty()&&n(F.cursor,e.cursor)?u.substractPoint(e.cursor):(r?u.substractPoint(b.cursor):b&&(d.removeSelectionMarker(b),u.addRange(b)),u.addRange(e)),d.$blockScrolling--,d.inVirtualSelectionMode=!1})}else if("block"==h){e.stop(),d.inVirtualSelectionMode=!0;var x,S=[],$=function(){var e=d.renderer.pixelToScreenCoordinates(v,w),t=A.screenToDocumentPosition(e.row,e.column);n(E,e)&&n(t,u.lead)||(E=e,d.$blockScrolling++,d.selection.moveToPosition(t),d.renderer.scrollCursorIntoView(),d.removeSelectionMarkers(S),S=u.rectangularRangeBlock(E,C),d.$mouseHandler.$clickSelection&&1==S.length&&S[0].isEmpty()&&(S[0]=d.$mouseHandler.$clickSelection.clone()),S.forEach(d.addSelectionMarker,d),d.updateSelectionMarkers(),d.$blockScrolling--)};d.$blockScrolling++,f&&!l?u.toSingleRange():!f&&l&&(x=u.toOrientedRange(),d.addSelectionMarker(x)),r?C=A.documentToScreenPosition(u.lead):u.moveToPosition(p),d.$blockScrolling--,E={row:-1,column:-1};var D=function(e){clearInterval(B),d.removeSelectionMarkers(S),S.length||(S=[u.toOrientedRange()]),d.$blockScrolling++,x&&(d.removeSelectionMarker(x),u.toSingleRange(x));for(var t=0;t1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)}},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);return t?(this.$onRemoveRange(t),t[0]):void 0},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length?this.$onRemoveRange(e):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){if(this.rangeCount=this.rangeList.ranges.length,1==this.rangeCount&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var i=e.length;i--;){var n=this.ranges.indexOf(e[i]);this.ranges.splice(n,1)}this._signal("removeRange",{ranges:e}),0===this.rangeCount&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){this.rangeList||(this.rangeList=new a,this.ranges=[],this.rangeCount=0)},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],i=l.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(i,t.cursor==t.start)}else{var i=this.getRange(),n=this.isBackwards(),r=i.start.row,o=i.end.row;if(r==o){if(n)var s=i.end,a=i.start;else var s=i.start,a=i.end;return this.addRange(l.fromPoints(a,a)),void this.addRange(l.fromPoints(s,s))}var c=[],h=this.getLineRange(r,!0);h.start.column=i.start.column,c.push(h);for(var d=r+1;o>d;d++)c.push(this.getLineRange(d,!0));h=this.getLineRange(o,!0),h.end.column=i.end.column,c.push(h),c.forEach(this.addRange,this)}},this.toggleBlockSelection=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],i=l.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(i,t.cursor==t.start)}else{var n=this.session.documentToScreenPosition(this.selectionLead),r=this.session.documentToScreenPosition(this.selectionAnchor),o=this.rectangularRangeBlock(n,r);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,i){var n=[],o=e.columns&&(s=0),0>h&&(h=0),h==d&&(i=!0);for(var u=h;d>=u;u++){var f=l.fromPoints(this.session.screenToDocumentPosition(u,s),this.session.screenToDocumentPosition(u,a));if(f.isEmpty()){if(p&&r(f.end,p))break;var p=f.end}f.cursor=o?f.start:f.end,n.push(f)}if(c&&n.reverse(),!i){for(var m=n.length-1;n[m].isEmpty()&&m>0;)m--;if(m>0)for(var g=0;n[g].isEmpty();)g++;for(var v=m;v>=g;v--)n[v].isEmpty()&&n.splice(v,1)}return n}}.call(c.prototype);var v=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(e.marker){this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);-1!=t&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length}},this.removeSelectionMarkers=function(e){for(var t=this.session.$selectionMarkers,i=e.length;i--;){var n=e[i];if(n.marker){this.session.removeMarker(n.marker);var r=t.indexOf(n);-1!=r&&t.splice(r,1)}}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){this.inMultiSelectMode||(this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers())},this.$onSingleSelect=function(e){this.session.multiSelect.inVirtualMode||(this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection"))},this.$onMultiSelectExec=function(e){var t=e.command,i=e.editor;if(i.multiSelect){if(t.multiSelectAction)"forEach"==t.multiSelectAction?n=i.forEachSelection(t,e.args):"forEachLine"==t.multiSelectAction?n=i.forEachSelection(t,e.args,!0):"single"==t.multiSelectAction?(i.exitMultiSelectMode(),n=t.exec(i,e.args||{})):n=t.multiSelectAction(i,e.args||{});else{var n=t.exec(i,e.args||{});i.multiSelect.addRange(i.multiSelect.toOrientedRange()),i.multiSelect.mergeOverlappingRanges()}return n}},this.forEachSelection=function(e,t,i){if(!this.inVirtualSelectionMode){var n,r=i&&i.keepOrder,o=1==i||i&&i.$byLines,s=this.session,a=this.selection,l=a.rangeList,h=(r?a:l).ranges;if(!h.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var d=a._eventRegistry;a._eventRegistry={};var u=new c(s);this.inVirtualSelectionMode=!0;for(var f=h.length;f--;){if(o)for(;f>0&&h[f].start.row==h[f-1].end.row;)f--;u.fromOrientedRange(h[f]),u.index=f,this.selection=s.selection=u;var p=e.exec?e.exec(this,t||{}):e(this,t||{});n||void 0===p||(n=p),u.toOrientedRange(h[f])}u.detach(),this.selection=s.selection=a,this.inVirtualSelectionMode=!1,a._eventRegistry=d,a.mergeOverlappingRanges();var m=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),m&&m.from==m.to&&this.renderer.animateScrolling(m.from),n}},this.exitMultiSelectMode=function(){this.inMultiSelectMode&&!this.inVirtualSelectionMode&&this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){for(var t=this.multiSelect.rangeList.ranges,i=[],n=0;ne?a.unshift(a.pop()):a.push(a.shift());for(var r=n.length;r--;){var o=n[r],s=o.clone();t.replace(o,a[r]),o.start.row=s.start.row,o.start.column=s.start.column}},this.selectMore=function(e,t,i){var r=this.session,o=r.multiSelect,s=o.toOrientedRange();if(!s.isEmpty()||(s=r.getWordRange(s.start.row,s.start.column),s.cursor=-1==e?s.start:s.end,this.multiSelect.addRange(s),!i)){var a=r.getTextRange(s),l=n(r,a,e);l&&(l.cursor=-1==e?l.start:l.end,this.$blockScrolling+=1,this.session.unfold(l),this.multiSelect.addRange(l),this.$blockScrolling-=1,this.renderer.scrollCursorIntoView(null,.5)),t&&this.multiSelect.substractPoint(s.cursor)}},this.alignCursors=function(){var e=this.session,t=e.multiSelect,i=t.ranges,n=-1,r=i.filter(function(e){return e.cursor.row==n?!0:void(n=e.cursor.row)});if(i.length&&r.length!=i.length-1){r.forEach(function(e){t.substractPoint(e.cursor)});var o=0,s=1/0,a=i.map(function(t){var i=t.cursor,n=e.getLine(i.row),r=n.substr(i.column).search(/\S/g);return-1==r&&(r=0),i.column>o&&(o=i.column),s>r&&(s=r),r});i.forEach(function(t,i){var n=t.cursor,r=o-n.column,c=a[i]-s;r>c?e.insert(n,u.stringRepeat(" ",r-c)):e.remove(new l(n.row,n.column,n.row,n.column-r+c)),t.start.column=t.end.column=o,t.start.row=t.end.row=n.row,t.cursor=t.end}),t.fromOrientedRange(i[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}else{var c=this.selection.getRange(),h=c.start.row,d=c.end.row,f=h==d;if(f){var p,m=this.session.getLength();do p=this.session.getLine(d);while(/[=:]/.test(p)&&++d0);0>h&&(h=0),d>=m&&(d=m-1)}var g=this.session.removeFullLines(h,d);g=this.$reAlignText(g,f),this.session.insert({row:h,column:0},g.join("\n")+"\n"),f||(c.start.column=0,c.end.column=g[g.length-1].length),this.selection.setRange(c)}},this.$reAlignText=function(e,t){function i(e){return u.stringRepeat(" ",e)}function n(e){return e[2]?i(s)+e[2]+i(a-e[2].length+l)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function r(e){return e[2]?i(s+a-e[2].length)+e[2]+i(l," ")+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function o(e){return e[2]?i(s)+e[2]+i(l)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var s,a,l,c=!0,h=!0;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?null==s?(s=t[1].length,a=t[2].length,l=t[3].length,t):(s+a+l!=t[1].length+t[2].length+t[3].length&&(h=!1),s!=t[1].length&&(c=!1),s>t[1].length&&(s=t[1].length),at[3].length&&(l=t[3].length),t):[e]}).map(t?n:c?h?r:n:o)}}).call(v.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var i=e.oldSession;i&&(i.multiSelect.off("addRange",this.$onAddRange),i.multiSelect.off("removeRange",this.$onRemoveRange),i.multiSelect.off("multiSelect",this.$onMultiSelect),i.multiSelect.off("singleSelect",this.$onSingleSelect),i.multiSelect.lead.off("change",this.$checkMultiselectChange),i.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=o,e("./config").defineOptions(v.prototype,"editor",{enableMultiselect:{set:function(e){o(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",h)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",h))},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),ace.define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,i){"use strict";var n=e("../../range").Range,r=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,i){var n=e.getLine(i);return this.foldingStartMarker.test(n)?"start":"markbeginend"==t&&this.foldingStopMarker&&this.foldingStopMarker.test(n)?"end":""},this.getFoldWidgetRange=function(e,t,i){return null},this.indentationBlock=function(e,t,i){var r=/\S/,o=e.getLine(t),s=o.search(r);if(-1!=s){for(var a=i||o.length,l=e.getLength(),c=t,h=t;++t=d)break;h=t}}if(h>c){var u=e.getLine(h).length;return new n(c,a,h,u)}}},this.openingBracketBlock=function(e,t,i,r,o){var s={row:i,column:r+1},a=e.$findClosingBracket(t,s,o);if(a){var l=e.foldWidgets[a.row];return null==l&&(l=e.getFoldWidget(a.row)),"start"==l&&a.row>s.row&&(a.row--,a.column=e.getLine(a.row).length),n.fromPoints(s,a)}},this.closingBracketBlock=function(e,t,i,r,o){var s={row:i,column:r},a=e.$findOpeningBracket(t,s);return a?(a.column++,s.column--,n.fromPoints(a,s)):void 0}}).call(r.prototype)}),ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,i){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter { background: #f0f0f0; color: #333; } .ace-tm .ace_print-margin { width: 1px; background: #e8e8e8; } .ace-tm .ace_fold { background-color: #6B72E6; } .ace-tm { background-color: #FFFFFF; color: black; } .ace-tm .ace_cursor { color: black; } .ace-tm .ace_invisible { color: rgb(191, 191, 191); } .ace-tm .ace_storage, .ace-tm .ace_keyword { color: blue; } .ace-tm .ace_constant { color: rgb(197, 6, 11); } .ace-tm .ace_constant.ace_buildin { color: rgb(88, 72, 246); } .ace-tm .ace_constant.ace_language { color: rgb(88, 92, 246); } .ace-tm .ace_constant.ace_library { color: rgb(6, 150, 14); } .ace-tm .ace_invalid { background-color: rgba(255, 0, 0, 0.1); color: red; } .ace-tm .ace_support.ace_function { color: rgb(60, 76, 114); } .ace-tm .ace_support.ace_constant { color: rgb(6, 150, 14); } .ace-tm .ace_support.ace_type, .ace-tm .ace_support.ace_class { color: rgb(109, 121, 222); } .ace-tm .ace_keyword.ace_operator { color: rgb(104, 118, 135); } .ace-tm .ace_string { color: rgb(3, 106, 7); } .ace-tm .ace_comment { color: rgb(76, 136, 107); } .ace-tm .ace_comment.ace_doc { color: rgb(0, 102, 255); } .ace-tm .ace_comment.ace_doc.ace_tag { color: rgb(128, 159, 191); } .ace-tm .ace_constant.ace_numeric { color: rgb(0, 0, 205); } .ace-tm .ace_variable { color: rgb(49, 132, 149); } .ace-tm .ace_xml-pe { color: rgb(104, 104, 91); } .ace-tm .ace_entity.ace_name.ace_function { color: #0000A2; } .ace-tm .ace_heading { color: rgb(12, 7, 255); } .ace-tm .ace_list { color:rgb(185, 6, 144); } .ace-tm .ace_meta.ace_tag { color:rgb(0, 22, 142); } .ace-tm .ace_string.ace_regex { color: rgb(255, 0, 0) } .ace-tm .ace_marker-layer .ace_selection { background: rgb(181, 213, 255); } .ace-tm.ace_multiselect .ace_selection.ace_start { box-shadow: 0 0 3px 0px white; } .ace-tm .ace_marker-layer .ace_step { background: rgb(252, 255, 0); } .ace-tm .ace_marker-layer .ace_stack { background: rgb(164, 229, 101); } .ace-tm .ace_marker-layer .ace_bracket { margin: -1px 0 0 -1px; border: 1px solid rgb(192, 192, 192); } .ace-tm .ace_marker-layer .ace_active-line { background: rgba(0, 0, 0, 0.07); } .ace-tm .ace_gutter-active-line { background-color : #dcdcdc; } .ace-tm .ace_marker-layer .ace_selected-word { background: rgb(250, 250, 255); border: 1px solid rgb(200, 200, 250); } .ace-tm .ace_indent-guide { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y; } ';var n=e("../lib/dom");n.importCssString(t.cssText,t.cssClass)}),ace.define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"],function(e,t,i){"use strict";function n(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeFold",this.updateOnFold),this.session.on("changeEditor",this.$onChangeEditor)}var r=(e("./lib/oop"),e("./lib/dom"));e("./range").Range;(function(){this.getRowLength=function(e){var t;return t=this.lineWidgets?this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:0,this.$useWrapMode&&this.$wrapData[e]?this.$wrapData[e].length+1+t:1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&!t.hidden&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach(),this.editor!=e&&(this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on("beforeRender",this.measureWidgets),e.renderer.on("afterRender",this.renderWidgets)))},this.detach=function(e){var t=this.editor;if(t){this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var i=this.session.lineWidgets;i&&i.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})}},this.updateOnFold=function(e,t){var i=t.lineWidgets;if(i&&e.action){for(var n=e.data,r=n.start.row,o=n.end.row,s="add"==e.action,a=r+1;o>a;a++)i[a]&&(i[a].hidden=s);i[o]&&(s?i[r]?i[o].hidden=s:i[r]=i[o]:(i[r]==i[o]&&(i[r]=void 0),i[o].hidden=s))}},this.updateOnChange=function(e){var t=this.session.lineWidgets;if(t){var i=e.start.row,n=e.end.row-i;if(0===n);else if("remove"==e.action){var r=t.splice(i+1,n);r.forEach(function(e){e&&this.removeLineWidget(e)},this),this.$updateRows()}else{var o=new Array(n);o.unshift(i,0),t.splice.apply(t,o),this.$updateRows()}}},this.$updateRows=function(){var e=this.session.lineWidgets;if(e){var t=!0;e.forEach(function(e,i){if(e)for(t=!1,e.row=i;e.$oldWidget;)e.$oldWidget.row=i,e=e.$oldWidget}),t&&(this.session.lineWidgets=null)}},this.addLineWidget=function(e){this.session.lineWidgets||(this.session.lineWidgets=new Array(this.session.getLength()));var t=this.session.lineWidgets[e.row];t&&(e.$oldWidget=t,t.el&&t.el.parentNode&&(t.el.parentNode.removeChild(t.el),t._inDocument=!1)),this.session.lineWidgets[e.row]=e,e.session=this.session;var i=this.editor.renderer;e.html&&!e.el&&(e.el=r.createElement("div"),e.el.innerHTML=e.html),e.el&&(r.addCssClass(e.el,"ace_lineWidgetContainer"),e.el.style.position="absolute",e.el.style.zIndex=5,i.container.appendChild(e.el),e._inDocument=!0),e.coverGutter||(e.el.style.zIndex=3),e.pixelHeight||(e.pixelHeight=e.el.offsetHeight),null==e.rowCount&&(e.rowCount=e.pixelHeight/i.layerConfig.lineHeight);var n=this.session.getFoldAt(e.row,0);if(e.$fold=n,n){var o=this.session.lineWidgets;e.row!=n.end.row||o[n.start.row]?e.hidden=!0:o[n.start.row]=e}return this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows(),this.renderWidgets(null,i),this.onWidgetChanged(e),e},this.removeLineWidget=function(e){if(e._inDocument=!1,e.session=null,e.el&&e.el.parentNode&&e.el.parentNode.removeChild(e.el),e.editor&&e.editor.destroy)try{e.editor.destroy()}catch(t){}if(this.session.lineWidgets){var i=this.session.lineWidgets[e.row];if(i==e)this.session.lineWidgets[e.row]=e.$oldWidget,e.$oldWidget&&this.onWidgetChanged(e.$oldWidget);else for(;i;){if(i.$oldWidget==e){i.$oldWidget=e.$oldWidget;break}i=i.$oldWidget}}this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows()},this.getWidgetsAtRow=function(e){for(var t=this.session.lineWidgets,i=t&&t[e],n=[];i;)n.push(i),i=i.$oldWidget;return n},this.onWidgetChanged=function(e){this.session._changedWidgets.push(e),this.editor&&this.editor.renderer.updateFull()},this.measureWidgets=function(e,t){var i=this.session._changedWidgets,n=t.layerConfig;if(i&&i.length){for(var r=1/0,o=0;oa&&(a=0)),s.rowCount!=a&&(s.rowCount=a,s.row0&&!n[r];)r--;this.firstRow=i.firstRow,this.lastRow=i.lastRow,t.$cursorLayer.config=i;for(var s=r;o>=s;s++){var a=n[s];if(a&&a.el)if(a.hidden)a.el.style.top=-100-(a.pixelHeight||0)+"px";else{a._inDocument||(a._inDocument=!0,t.container.appendChild(a.el));var l=t.$cursorLayer.getPixelPosition({row:s,column:0},!0).top;a.coverLine||(l+=i.lineHeight*this.session.getRowLineCount(a.row)),a.el.style.top=l-i.offset+"px";var c=a.coverGutter?0:t.gutterWidth;a.fixedWidth||(c-=t.scrollLeft),a.el.style.left=c+"px",a.fullWidth&&a.screenWidth&&(a.el.style.minWidth=i.width+2*i.padding+"px"),a.fixedWidth?a.el.style.right=t.scrollBar.getWidth()+"px":a.el.style.right=""}}}}}).call(n.prototype),t.LineWidgets=n}),ace.define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"],function(e,t,i){"use strict";function n(e,t,i){for(var n=0,r=e.length-1;r>=n;){var o=n+r>>1,s=i(t,e[o]);if(s>0)n=o+1;else{if(!(0>s))return o;r=o-1}}return-(n+1)}function r(e,t,i){var r=e.getAnnotations().sort(a.comparePoints);if(r.length){var o=n(r,{row:t,column:-1},a.comparePoints);0>o&&(o=-o-1),o>=r.length?o=i>0?0:r.length-1:0===o&&0>i&&(o=r.length-1);var s=r[o];if(s&&i){if(s.row===t){do s=r[o+=i];while(s&&s.row===t);if(!s)return r.slice()}var l=[];t=s.row; +do l[0>i?"unshift":"push"](s),s=r[o+=i];while(s&&s.row==t);return l.length&&l}}}var o=e("../line_widgets").LineWidgets,s=e("../lib/dom"),a=e("../range").Range;t.showErrorMarker=function(e,t){var i=e.session;i.widgetManager||(i.widgetManager=new o(i),i.widgetManager.attach(e));var n=e.getCursorPosition(),a=n.row,l=i.widgetManager.getWidgetsAtRow(a).filter(function(e){return"errorMarker"==e.type})[0];l?l.destroy():a-=t;var c,h=r(i,a,t);if(h){var d=h[0];n.column=(d.pos&&"number"!=typeof d.column?d.pos.sc:d.column)||0,n.row=d.row,c=e.renderer.$gutterLayer.$annotations[n.row]}else{if(l)return;c={text:["Looks good!"],className:"ace_ok"}}e.session.unfold(n.row),e.selection.moveToPosition(n);var u={row:n.row,fixedWidth:!0,coverGutter:!0,el:s.createElement("div"),type:"errorMarker"},f=u.el.appendChild(s.createElement("div")),p=u.el.appendChild(s.createElement("div"));p.className="error_widget_arrow "+c.className;var m=e.renderer.$cursorLayer.getPixelPosition(n).left;p.style.left=m+e.renderer.gutterWidth-5+"px",u.el.className="error_widget_wrapper",f.className="error_widget "+c.className,f.innerHTML=c.text.join("
"),f.appendChild(s.createElement("div"));var g=function(e,t,i){return 0!==t||"esc"!==i&&"return"!==i?void 0:(u.destroy(),{command:"null"})};u.destroy=function(){e.$mouseHandler.isMousePressed||(e.keyBinding.removeKeyboardHandler(g),i.widgetManager.removeLineWidget(u),e.off("changeSelection",u.destroy),e.off("changeSession",u.destroy),e.off("mouseup",u.destroy),e.off("change",u.destroy))},e.keyBinding.addKeyboardHandler(g),e.on("changeSelection",u.destroy),e.on("changeSession",u.destroy),e.on("mouseup",u.destroy),e.on("change",u.destroy),e.session.widgetManager.addLineWidget(u),u.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:u.el.offsetHeight})},s.importCssString(" .error_widget_wrapper { background: inherit; color: inherit; border:none } .error_widget { border-top: solid 2px; border-bottom: solid 2px; margin: 5px 0; padding: 10px 40px; white-space: pre-wrap; } .error_widget.ace_error, .error_widget_arrow.ace_error{ border-color: #ff5a5a } .error_widget.ace_warning, .error_widget_arrow.ace_warning{ border-color: #F1D817 } .error_widget.ace_info, .error_widget_arrow.ace_info{ border-color: #5a5a5a } .error_widget.ace_ok, .error_widget_arrow.ace_ok{ border-color: #5aaa5a } .error_widget_arrow { position: absolute; border: solid 5px; border-top-color: transparent!important; border-right-color: transparent!important; border-left-color: transparent!important; top: -5px; } ","")}),ace.define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"],function(e,t,i){"use strict";e("./lib/fixoldbrowsers");var n=e("./lib/dom"),r=e("./lib/event"),o=e("./editor").Editor,s=e("./edit_session").EditSession,a=e("./undomanager").UndoManager,l=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.acequire=e,t.edit=function(e){if("string"==typeof e){var i=e;if(e=document.getElementById(i),!e)throw new Error("ace.edit can't find div #"+i)}if(e&&e.env&&e.env.editor instanceof o)return e.env.editor;var s="";if(e&&/input|textarea/i.test(e.tagName)){var a=e;s=a.value,e=n.createElement("pre"),a.parentNode.replaceChild(e,a)}else e&&(s=n.getInnerText(e),e.innerHTML="");var c=t.createEditSession(s),h=new o(new l(e));h.setSession(c);var d={document:c,editor:h,onResize:h.resize.bind(h,null)};return a&&(d.textarea=a),r.addListener(window,"resize",d.onResize),h.on("destroy",function(){r.removeListener(window,"resize",d.onResize),d.editor.container.env=null}),h.container.env=h.env=d,h},t.createEditSession=function(e,t){var i=new s(e,t);return i.setUndoManager(new a),i},t.EditSession=s,t.UndoManager=a,t.version="1.2.3"}),function(){ace.acequire(["ace/ace"],function(e){e&&e.config.init(!0),window.ace||(window.ace=e);for(var t in e)e.hasOwnProperty(t)&&(window.ace[t]=e[t])})}(),e.exports=window.ace.acequire("ace/ace")},function(e,t){e.exports=function(){throw new Error("define cannot be used indirect")}},function(e,t){(function(t){function i(){if(t.Blob)try{return new Blob(["asdf"],{type:"text/plain"}),Blob}catch(e){}var i=t.WebKitBlobBuilder||t.MozBlobBuilder||t.MSBlobBuilder;return function(e,t){var n=new i,r=t.endings,o=t.type;if(r)for(var s=0,a=e.length;a>s;++s)n.append(e[s],r);else for(var s=0,a=e.length;a>s;++s)n.append(e[s]);return o?n.getBlob(o):n.getBlob()}}e.exports=i()}).call(t,function(){return this}())},function(e,t,i){ace.define("ace/mode/json_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,i){"use strict";var n=e("../lib/oop"),r=e("./text_highlight_rules").TextHighlightRules,o=function(){this.$rules={start:[{token:"variable",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]\\s*(?=:)'},{token:"string",regex:'"',next:"string"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:"invalid.illegal",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"invalid.illegal",regex:"\\/\\/.*$"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],string:[{token:"constant.language.escape",regex:/\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\\/bfnrt])/},{token:"string",regex:'[^"\\\\]+'},{token:"string",regex:'"',next:"start"},{token:"string",regex:"",next:"start"}]}};n.inherits(o,r),t.JsonHighlightRules=o}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,i){"use strict";var n=e("../range").Range,r=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var i=e.getLine(t),r=i.match(/^(\s*\})/);if(!r)return 0;var o=r[1].length,s=e.findMatchingBracket({row:t,column:o});if(!s||s.row==t)return 0;var a=this.$getIndent(e.getLine(s.row));e.replace(new n(t,0,t,o-1),a)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(r.prototype),t.MatchingBraceOutdent=r}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,i){"use strict";var n,r=e("../../lib/oop"),o=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,a=e("../../lib/lang"),l=["text","paren.rparen","punctuation.operator"],c=["text","paren.rparen","punctuation.operator","comment"],h={},d=function(e){var t=-1;return e.multiSelect&&(t=e.selection.index,h.rangeCount!=e.multiSelect.rangeCount&&(h={rangeCount:e.multiSelect.rangeCount})),h[t]?n=h[t]:void(n=h[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""})},u=function(e,t,i,n){var r=e.end.row-e.start.row;return{text:i+t+n,selection:[0,e.start.column+1,r,e.end.column+(r?0:1)]}},f=function(){this.add("braces","insertion",function(e,t,i,r,o){var s=i.getCursorPosition(),l=r.doc.getLine(s.row);if("{"==o){d(i);var c=i.getSelectionRange(),h=r.doc.getTextRange(c);if(""!==h&&"{"!==h&&i.getWrapBehavioursEnabled())return u(c,h,"{","}");if(f.isSaneInsertion(i,r))return/[\]\}\)]/.test(l[s.column])||i.inMultiSelectMode?(f.recordAutoInsert(i,r,"}"),{text:"{}",selection:[1,1]}):(f.recordMaybeInsert(i,r,"{"),{text:"{",selection:[1,1]})}else if("}"==o){d(i);var p=l.substring(s.column,s.column+1);if("}"==p){var m=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(null!==m&&f.isAutoInsertedClosing(s,l,o))return f.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if("\n"==o||"\r\n"==o){d(i);var g="";f.isMaybeInsertedClosing(s,l)&&(g=a.stringRepeat("}",n.maybeInsertedBrackets),f.clearMaybeInsertedClosing());var p=l.substring(s.column,s.column+1);if("}"===p){var v=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!v)return null;var w=this.$getIndent(r.getLine(v.row))}else{if(!g)return void f.clearMaybeInsertedClosing();var w=this.$getIndent(l)}var y=w+r.getTabString();return{text:"\n"+y+"\n"+w+g,selection:[1,y.length,1,y.length]}}f.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,i,r,o){var s=r.doc.getTextRange(o);if(!o.isMultiLine()&&"{"==s){d(i);var a=r.doc.getLine(o.start.row),l=a.substring(o.end.column,o.end.column+1);if("}"==l)return o.end.column++,o;n.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,i,n,r){if("("==r){d(i);var o=i.getSelectionRange(),s=n.doc.getTextRange(o);if(""!==s&&i.getWrapBehavioursEnabled())return u(o,s,"(",")");if(f.isSaneInsertion(i,n))return f.recordAutoInsert(i,n,")"),{text:"()",selection:[1,1]}}else if(")"==r){d(i);var a=i.getCursorPosition(),l=n.doc.getLine(a.row),c=l.substring(a.column,a.column+1);if(")"==c){var h=n.$findOpeningBracket(")",{column:a.column+1,row:a.row});if(null!==h&&f.isAutoInsertedClosing(a,l,r))return f.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,i,n,r){var o=n.doc.getTextRange(r);if(!r.isMultiLine()&&"("==o){d(i);var s=n.doc.getLine(r.start.row),a=s.substring(r.start.column+1,r.start.column+2);if(")"==a)return r.end.column++,r}}),this.add("brackets","insertion",function(e,t,i,n,r){if("["==r){d(i);var o=i.getSelectionRange(),s=n.doc.getTextRange(o);if(""!==s&&i.getWrapBehavioursEnabled())return u(o,s,"[","]");if(f.isSaneInsertion(i,n))return f.recordAutoInsert(i,n,"]"),{text:"[]",selection:[1,1]}}else if("]"==r){d(i);var a=i.getCursorPosition(),l=n.doc.getLine(a.row),c=l.substring(a.column,a.column+1);if("]"==c){var h=n.$findOpeningBracket("]",{column:a.column+1,row:a.row});if(null!==h&&f.isAutoInsertedClosing(a,l,r))return f.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,i,n,r){var o=n.doc.getTextRange(r);if(!r.isMultiLine()&&"["==o){d(i);var s=n.doc.getLine(r.start.row),a=s.substring(r.start.column+1,r.start.column+2);if("]"==a)return r.end.column++,r}}),this.add("string_dquotes","insertion",function(e,t,i,n,r){if('"'==r||"'"==r){d(i);var o=r,s=i.getSelectionRange(),a=n.doc.getTextRange(s);if(""!==a&&"'"!==a&&'"'!=a&&i.getWrapBehavioursEnabled())return u(s,a,o,o);if(!a){var l=i.getCursorPosition(),c=n.doc.getLine(l.row),h=c.substring(l.column-1,l.column),f=c.substring(l.column,l.column+1),p=n.getTokenAt(l.row,l.column),m=n.getTokenAt(l.row,l.column+1);if("\\"==h&&p&&/escape/.test(p.type))return null;var g,v=p&&/string|escape/.test(p.type),w=!m||/string|escape/.test(m.type);if(f==o)g=v!==w;else{if(v&&!w)return null;if(v&&w)return null;var y=n.$mode.tokenRe;y.lastIndex=0;var A=y.test(h);y.lastIndex=0;var C=y.test(h);if(A||C)return null;if(f&&!/[\s;,.})\]\\]/.test(f))return null;g=!0}return{text:g?o+o:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,i,n,r){var o=n.doc.getTextRange(r);if(!r.isMultiLine()&&('"'==o||"'"==o)){d(i);var s=n.doc.getLine(r.start.row),a=s.substring(r.start.column+1,r.start.column+2);if(a==o)return r.end.column++,r}})};f.isSaneInsertion=function(e,t){var i=e.getCursorPosition(),n=new s(t,i.row,i.column);if(!this.$matchTokenType(n.getCurrentToken()||"text",l)){var r=new s(t,i.row,i.column+1);if(!this.$matchTokenType(r.getCurrentToken()||"text",l))return!1}return n.stepForward(),n.getCurrentTokenRow()!==i.row||this.$matchTokenType(n.getCurrentToken()||"text",c)},f.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},f.recordAutoInsert=function(e,t,i){var r=e.getCursorPosition(),o=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,o,n.autoInsertedLineEnd[0])||(n.autoInsertedBrackets=0),n.autoInsertedRow=r.row,n.autoInsertedLineEnd=i+o.substr(r.column),n.autoInsertedBrackets++},f.recordMaybeInsert=function(e,t,i){var r=e.getCursorPosition(),o=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,o)||(n.maybeInsertedBrackets=0),n.maybeInsertedRow=r.row,n.maybeInsertedLineStart=o.substr(0,r.column)+i,n.maybeInsertedLineEnd=o.substr(r.column),n.maybeInsertedBrackets++},f.isAutoInsertedClosing=function(e,t,i){return n.autoInsertedBrackets>0&&e.row===n.autoInsertedRow&&i===n.autoInsertedLineEnd[0]&&t.substr(e.column)===n.autoInsertedLineEnd},f.isMaybeInsertedClosing=function(e,t){return n.maybeInsertedBrackets>0&&e.row===n.maybeInsertedRow&&t.substr(e.column)===n.maybeInsertedLineEnd&&t.substr(0,e.column)==n.maybeInsertedLineStart},f.popAutoInsertedClosing=function(){n.autoInsertedLineEnd=n.autoInsertedLineEnd.substr(1),n.autoInsertedBrackets--},f.clearMaybeInsertedClosing=function(){n&&(n.maybeInsertedBrackets=0,n.maybeInsertedRow=-1)},r.inherits(f,o),t.CstyleBehaviour=f}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,i){"use strict";var n=e("../../lib/oop"),r=e("../../range").Range,o=e("./fold_mode").FoldMode,s=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};n.inherits(s,o),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,i){var n=e.getLine(i);if(this.singleLineBlockCommentRe.test(n)&&!this.startRegionRe.test(n)&&!this.tripleStarBlockCommentRe.test(n))return"";var r=this._getFoldWidgetBase(e,t,i);return!r&&this.startRegionRe.test(n)?"start":r},this.getFoldWidgetRange=function(e,t,i,n){var r=e.getLine(i);if(this.startRegionRe.test(r))return this.getCommentRegionBlock(e,r,i);var o=r.match(this.foldingStartMarker);if(o){var s=o.index;if(o[1])return this.openingBracketBlock(e,o[1],i,s);var a=e.getCommentFoldRange(i,s+o[0].length,1);return a&&!a.isMultiLine()&&(n?a=this.getSectionRange(e,i):"all"!=t&&(a=null)),a}if("markbegin"!==t){var o=r.match(this.foldingStopMarker);if(o){var s=o.index+o[0].length;return o[1]?this.closingBracketBlock(e,o[1],i,s):e.getCommentFoldRange(i,s,-1)}}},this.getSectionRange=function(e,t){var i=e.getLine(t),n=i.search(/\S/),o=t,s=i.length;t+=1;for(var a=t,l=e.getLength();++tc)break;var h=this.getFoldWidgetRange(e,"all",t);if(h){if(h.start.row<=o)break;if(h.isMultiLine())t=h.end.row;else if(n==c)break}a=t}}return new r(o,s,a,e.getLine(a).length)},this.getCommentRegionBlock=function(e,t,i){for(var n=t.search(/\s*$/),o=e.getLength(),s=i,a=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,l=1;++is?new r(s,n,h,t.length):void 0}}.call(s.prototype)}),ace.define("ace/mode/json",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/json_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),o=e("./text").Mode,s=e("./json_highlight_rules").JsonHighlightRules,a=e("./matching_brace_outdent").MatchingBraceOutdent,l=e("./behaviour/cstyle").CstyleBehaviour,c=e("./folding/cstyle").FoldMode,h=e("../worker/worker_client").WorkerClient,d=function(){this.HighlightRules=s,this.$outdent=new a,this.$behaviour=new l,this.foldingRules=new c};r.inherits(d,o),function(){this.getNextLineIndent=function(e,t,i){var n=this.$getIndent(t);if("start"==e){var r=t.match(/^.*[\{\(\[]\s*$/);r&&(n+=i)}return n},this.checkOutdent=function(e,t,i){return this.$outdent.checkOutdent(t,i)},this.autoOutdent=function(e,t,i){this.$outdent.autoOutdent(t,i)},this.createWorker=function(e){var t=new h(["ace"],i(68),"JsonWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/json"}.call(d.prototype),t.Mode=d})},function(e,t){e.exports.id="ace/mode/json_worker",e.exports.src='"no use strict";(function(window){function resolveModuleId(id,paths){for(var testPath=id,tail="";testPath;){var alias=paths[testPath];if("string"==typeof alias)return alias+tail;if(alias)return alias.location.replace(/\\/*$/,"/")+(tail||alias.main||alias.name);if(alias===!1)return"";var i=testPath.lastIndexOf("/");if(-1===i)break;tail=testPath.substr(i)+tail,testPath=testPath.slice(0,i)}return id}if(!(void 0!==window.window&&window.document||window.acequire&&window.define)){window.console||(window.console=function(){var msgs=Array.prototype.slice.call(arguments,0);postMessage({type:"log",data:msgs})},window.console.error=window.console.warn=window.console.log=window.console.trace=window.console),window.window=window,window.ace=window,window.onerror=function(message,file,line,col,err){postMessage({type:"error",data:{message:message,data:err.data,file:file,line:line,col:col,stack:err.stack}})},window.normalizeModule=function(parentId,moduleName){if(-1!==moduleName.indexOf("!")){var chunks=moduleName.split("!");return window.normalizeModule(parentId,chunks[0])+"!"+window.normalizeModule(parentId,chunks[1])}if("."==moduleName.charAt(0)){var base=parentId.split("/").slice(0,-1).join("/");for(moduleName=(base?base+"/":"")+moduleName;-1!==moduleName.indexOf(".")&&previous!=moduleName;){var previous=moduleName;moduleName=moduleName.replace(/^\\.\\//,"").replace(/\\/\\.\\//,"/").replace(/[^\\/]+\\/\\.\\.\\//,"")}}return moduleName},window.acequire=function acequire(parentId,id){if(id||(id=parentId,parentId=null),!id.charAt)throw Error("worker.js acequire() accepts only (parentId, id) as arguments");id=window.normalizeModule(parentId,id);var module=window.acequire.modules[id];if(module)return module.initialized||(module.initialized=!0,module.exports=module.factory().exports),module.exports;if(!window.acequire.tlns)return console.log("unable to load "+id);var path=resolveModuleId(id,window.acequire.tlns);return".js"!=path.slice(-3)&&(path+=".js"),window.acequire.id=id,window.acequire.modules[id]={},importScripts(path),window.acequire(parentId,id)},window.acequire.modules={},window.acequire.tlns={},window.define=function(id,deps,factory){if(2==arguments.length?(factory=deps,"string"!=typeof id&&(deps=id,id=window.acequire.id)):1==arguments.length&&(factory=id,deps=[],id=window.acequire.id),"function"!=typeof factory)return window.acequire.modules[id]={exports:factory,initialized:!0},void 0;deps.length||(deps=["require","exports","module"]);var req=function(childId){return window.acequire(id,childId)};window.acequire.modules[id]={exports:{},factory:function(){var module=this,returnExports=factory.apply(this,deps.map(function(dep){switch(dep){case"require":return req;case"exports":return module.exports;case"module":return module;default:return req(dep)}}));return returnExports&&(module.exports=returnExports),module}}},window.define.amd={},acequire.tlns={},window.initBaseUrls=function(topLevelNamespaces){for(var i in topLevelNamespaces)acequire.tlns[i]=topLevelNamespaces[i]},window.initSender=function(){var EventEmitter=window.acequire("ace/lib/event_emitter").EventEmitter,oop=window.acequire("ace/lib/oop"),Sender=function(){};return function(){oop.implement(this,EventEmitter),this.callback=function(data,callbackId){postMessage({type:"call",id:callbackId,data:data})},this.emit=function(name,data){postMessage({type:"event",name:name,data:data})}}.call(Sender.prototype),new Sender};var main=window.main=null,sender=window.sender=null;window.onmessage=function(e){var msg=e.data;if(msg.event&&sender)sender._signal(msg.event,msg.data);else if(msg.command)if(main[msg.command])main[msg.command].apply(main,msg.args);else{if(!window[msg.command])throw Error("Unknown command:"+msg.command);window[msg.command].apply(window,msg.args)}else if(msg.init){window.initBaseUrls(msg.tlns),acequire("ace/lib/es5-shim"),sender=window.sender=window.initSender();var clazz=acequire(msg.module)[msg.classname];main=window.main=new clazz(sender)}}}})(this),ace.define("ace/lib/oop",["require","exports","module"],function(acequire,exports){"use strict";exports.inherits=function(ctor,superCtor){ctor.super_=superCtor,ctor.prototype=Object.create(superCtor.prototype,{constructor:{value:ctor,enumerable:!1,writable:!0,configurable:!0}})},exports.mixin=function(obj,mixin){for(var key in mixin)obj[key]=mixin[key];return obj},exports.implement=function(proto,mixin){exports.mixin(proto,mixin)}}),ace.define("ace/range",["require","exports","module"],function(acequire,exports){"use strict";var comparePoints=function(p1,p2){return p1.row-p2.row||p1.column-p2.column},Range=function(startRow,startColumn,endRow,endColumn){this.start={row:startRow,column:startColumn},this.end={row:endRow,column:endColumn}};(function(){this.isEqual=function(range){return this.start.row===range.start.row&&this.end.row===range.end.row&&this.start.column===range.start.column&&this.end.column===range.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(row,column){return 0==this.compare(row,column)},this.compareRange=function(range){var cmp,end=range.end,start=range.start;return cmp=this.compare(end.row,end.column),1==cmp?(cmp=this.compare(start.row,start.column),1==cmp?2:0==cmp?1:0):-1==cmp?-2:(cmp=this.compare(start.row,start.column),-1==cmp?-1:1==cmp?42:0)},this.comparePoint=function(p){return this.compare(p.row,p.column)},this.containsRange=function(range){return 0==this.comparePoint(range.start)&&0==this.comparePoint(range.end)},this.intersects=function(range){var cmp=this.compareRange(range);return-1==cmp||0==cmp||1==cmp},this.isEnd=function(row,column){return this.end.row==row&&this.end.column==column},this.isStart=function(row,column){return this.start.row==row&&this.start.column==column},this.setStart=function(row,column){"object"==typeof row?(this.start.column=row.column,this.start.row=row.row):(this.start.row=row,this.start.column=column)},this.setEnd=function(row,column){"object"==typeof row?(this.end.column=row.column,this.end.row=row.row):(this.end.row=row,this.end.column=column)},this.inside=function(row,column){return 0==this.compare(row,column)?this.isEnd(row,column)||this.isStart(row,column)?!1:!0:!1},this.insideStart=function(row,column){return 0==this.compare(row,column)?this.isEnd(row,column)?!1:!0:!1},this.insideEnd=function(row,column){return 0==this.compare(row,column)?this.isStart(row,column)?!1:!0:!1},this.compare=function(row,column){return this.isMultiLine()||row!==this.start.row?this.start.row>row?-1:row>this.end.row?1:this.start.row===row?column>=this.start.column?0:-1:this.end.row===row?this.end.column>=column?0:1:0:this.start.column>column?-1:column>this.end.column?1:0},this.compareStart=function(row,column){return this.start.row==row&&this.start.column==column?-1:this.compare(row,column)},this.compareEnd=function(row,column){return this.end.row==row&&this.end.column==column?1:this.compare(row,column)},this.compareInside=function(row,column){return this.end.row==row&&this.end.column==column?1:this.start.row==row&&this.start.column==column?-1:this.compare(row,column)},this.clipRows=function(firstRow,lastRow){if(this.end.row>lastRow)var end={row:lastRow+1,column:0};else if(firstRow>this.end.row)var end={row:firstRow,column:0};if(this.start.row>lastRow)var start={row:lastRow+1,column:0};else if(firstRow>this.start.row)var start={row:firstRow,column:0};return Range.fromPoints(start||this.start,end||this.end)},this.extend=function(row,column){var cmp=this.compare(row,column);if(0==cmp)return this;if(-1==cmp)var start={row:row,column:column};else var end={row:row,column:column};return Range.fromPoints(start||this.start,end||this.end)},this.isEmpty=function(){return this.start.row===this.end.row&&this.start.column===this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return Range.fromPoints(this.start,this.end)},this.collapseRows=function(){return 0==this.end.column?new Range(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new Range(this.start.row,0,this.end.row,0)},this.toScreenRange=function(session){var screenPosStart=session.documentToScreenPosition(this.start),screenPosEnd=session.documentToScreenPosition(this.end);return new Range(screenPosStart.row,screenPosStart.column,screenPosEnd.row,screenPosEnd.column)},this.moveBy=function(row,column){this.start.row+=row,this.start.column+=column,this.end.row+=row,this.end.column+=column}}).call(Range.prototype),Range.fromPoints=function(start,end){return new Range(start.row,start.column,end.row,end.column)},Range.comparePoints=comparePoints,Range.comparePoints=function(p1,p2){return p1.row-p2.row||p1.column-p2.column},exports.Range=Range}),ace.define("ace/apply_delta",["require","exports","module"],function(acequire,exports){"use strict";exports.applyDelta=function(docLines,delta){var row=delta.start.row,startColumn=delta.start.column,line=docLines[row]||"";switch(delta.action){case"insert":var lines=delta.lines;if(1===lines.length)docLines[row]=line.substring(0,startColumn)+delta.lines[0]+line.substring(startColumn);else{var args=[row,1].concat(delta.lines);docLines.splice.apply(docLines,args),docLines[row]=line.substring(0,startColumn)+docLines[row],docLines[row+delta.lines.length-1]+=line.substring(startColumn)}break;case"remove":var endColumn=delta.end.column,endRow=delta.end.row;row===endRow?docLines[row]=line.substring(0,startColumn)+line.substring(endColumn):docLines.splice(row,endRow-row+1,line.substring(0,startColumn)+docLines[endRow].substring(endColumn))}}}),ace.define("ace/lib/event_emitter",["require","exports","module"],function(acequire,exports){"use strict";var EventEmitter={},stopPropagation=function(){this.propagationStopped=!0},preventDefault=function(){this.defaultPrevented=!0};EventEmitter._emit=EventEmitter._dispatchEvent=function(eventName,e){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var listeners=this._eventRegistry[eventName]||[],defaultHandler=this._defaultHandlers[eventName];if(listeners.length||defaultHandler){"object"==typeof e&&e||(e={}),e.type||(e.type=eventName),e.stopPropagation||(e.stopPropagation=stopPropagation),e.preventDefault||(e.preventDefault=preventDefault),listeners=listeners.slice();for(var i=0;listeners.length>i&&(listeners[i](e,this),!e.propagationStopped);i++);return defaultHandler&&!e.defaultPrevented?defaultHandler(e,this):void 0}},EventEmitter._signal=function(eventName,e){var listeners=(this._eventRegistry||{})[eventName];if(listeners){listeners=listeners.slice();for(var i=0;listeners.length>i;i++)listeners[i](e,this)}},EventEmitter.once=function(eventName,callback){var _self=this;callback&&this.addEventListener(eventName,function newCallback(){_self.removeEventListener(eventName,newCallback),callback.apply(null,arguments)})},EventEmitter.setDefaultHandler=function(eventName,callback){var handlers=this._defaultHandlers;if(handlers||(handlers=this._defaultHandlers={_disabled_:{}}),handlers[eventName]){var old=handlers[eventName],disabled=handlers._disabled_[eventName];disabled||(handlers._disabled_[eventName]=disabled=[]),disabled.push(old);var i=disabled.indexOf(callback);-1!=i&&disabled.splice(i,1)}handlers[eventName]=callback},EventEmitter.removeDefaultHandler=function(eventName,callback){var handlers=this._defaultHandlers;if(handlers){var disabled=handlers._disabled_[eventName];if(handlers[eventName]==callback)handlers[eventName],disabled&&this.setDefaultHandler(eventName,disabled.pop());else if(disabled){var i=disabled.indexOf(callback);-1!=i&&disabled.splice(i,1)}}},EventEmitter.on=EventEmitter.addEventListener=function(eventName,callback,capturing){this._eventRegistry=this._eventRegistry||{};var listeners=this._eventRegistry[eventName];return listeners||(listeners=this._eventRegistry[eventName]=[]),-1==listeners.indexOf(callback)&&listeners[capturing?"unshift":"push"](callback),callback},EventEmitter.off=EventEmitter.removeListener=EventEmitter.removeEventListener=function(eventName,callback){this._eventRegistry=this._eventRegistry||{};var listeners=this._eventRegistry[eventName];if(listeners){var index=listeners.indexOf(callback);-1!==index&&listeners.splice(index,1)}},EventEmitter.removeAllListeners=function(eventName){this._eventRegistry&&(this._eventRegistry[eventName]=[])},exports.EventEmitter=EventEmitter}),ace.define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(acequire,exports){"use strict";var oop=acequire("./lib/oop"),EventEmitter=acequire("./lib/event_emitter").EventEmitter,Anchor=exports.Anchor=function(doc,row,column){this.$onChange=this.onChange.bind(this),this.attach(doc),column===void 0?this.setPosition(row.row,row.column):this.setPosition(row,column)};(function(){function $pointsInOrder(point1,point2,equalPointsInOrder){var bColIsAfter=equalPointsInOrder?point1.column<=point2.column:point1.columnthis.row)){var point=$getTransformedPoint(delta,{row:this.row,column:this.column},this.$insertRight);this.setPosition(point.row,point.column,!0)}},this.setPosition=function(row,column,noClip){var pos;if(pos=noClip?{row:row,column:column}:this.$clipPositionToDocument(row,column),this.row!=pos.row||this.column!=pos.column){var old={row:this.row,column:this.column};this.row=pos.row,this.column=pos.column,this._signal("change",{old:old,value:pos})}},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(doc){this.document=doc||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(row,column){var pos={};return row>=this.document.getLength()?(pos.row=Math.max(0,this.document.getLength()-1),pos.column=this.document.getLine(pos.row).length):0>row?(pos.row=0,pos.column=0):(pos.row=row,pos.column=Math.min(this.document.getLine(pos.row).length,Math.max(0,column))),0>column&&(pos.column=0),pos}}).call(Anchor.prototype)}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(acequire,exports){"use strict";var oop=acequire("./lib/oop"),applyDelta=acequire("./apply_delta").applyDelta,EventEmitter=acequire("./lib/event_emitter").EventEmitter,Range=acequire("./range").Range,Anchor=acequire("./anchor").Anchor,Document=function(textOrLines){this.$lines=[""],0===textOrLines.length?this.$lines=[""]:Array.isArray(textOrLines)?this.insertMergedLines({row:0,column:0},textOrLines):this.insert({row:0,column:0},textOrLines)};(function(){oop.implement(this,EventEmitter),this.setValue=function(text){var len=this.getLength()-1;this.remove(new Range(0,0,len,this.getLine(len).length)),this.insert({row:0,column:0},text)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(row,column){return new Anchor(this,row,column)},this.$split=0==="aaa".split(/a/).length?function(text){return text.replace(/\\r\\n|\\r/g,"\\n").split("\\n")}:function(text){return text.split(/\\r\\n|\\r|\\n/)},this.$detectNewLine=function(text){var match=text.match(/^.*?(\\r\\n|\\r|\\n)/m);this.$autoNewLine=match?match[1]:"\\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\\r\\n";case"unix":return"\\n";default:return this.$autoNewLine||"\\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(newLineMode){this.$newLineMode!==newLineMode&&(this.$newLineMode=newLineMode,this._signal("changeNewLineMode"))},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(text){return"\\r\\n"==text||"\\r"==text||"\\n"==text},this.getLine=function(row){return this.$lines[row]||""},this.getLines=function(firstRow,lastRow){return this.$lines.slice(firstRow,lastRow+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(range){return this.getLinesForRange(range).join(this.getNewLineCharacter())},this.getLinesForRange=function(range){var lines;if(range.start.row===range.end.row)lines=[this.getLine(range.start.row).substring(range.start.column,range.end.column)];else{lines=this.getLines(range.start.row,range.end.row),lines[0]=(lines[0]||"").substring(range.start.column);var l=lines.length-1;range.end.row-range.start.row==l&&(lines[l]=lines[l].substring(0,range.end.column))}return lines},this.insertLines=function(row,lines){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(row,lines)},this.removeLines=function(firstRow,lastRow){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(firstRow,lastRow)},this.insertNewLine=function(position){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, [\'\', \'\']) instead."),this.insertMergedLines(position,["",""])},this.insert=function(position,text){return 1>=this.getLength()&&this.$detectNewLine(text),this.insertMergedLines(position,this.$split(text))},this.insertInLine=function(position,text){var start=this.clippedPos(position.row,position.column),end=this.pos(position.row,position.column+text.length);return this.applyDelta({start:start,end:end,action:"insert",lines:[text]},!0),this.clonePos(end)},this.clippedPos=function(row,column){var length=this.getLength();void 0===row?row=length:0>row?row=0:row>=length&&(row=length-1,column=void 0);var line=this.getLine(row);return void 0==column&&(column=line.length),column=Math.min(Math.max(column,0),line.length),{row:row,column:column}},this.clonePos=function(pos){return{row:pos.row,column:pos.column}},this.pos=function(row,column){return{row:row,column:column}},this.$clipPosition=function(position){var length=this.getLength();return position.row>=length?(position.row=Math.max(0,length-1),position.column=this.getLine(length-1).length):(position.row=Math.max(0,position.row),position.column=Math.min(Math.max(position.column,0),this.getLine(position.row).length)),position},this.insertFullLines=function(row,lines){row=Math.min(Math.max(row,0),this.getLength());var column=0;this.getLength()>row?(lines=lines.concat([""]),column=0):(lines=[""].concat(lines),row--,column=this.$lines[row].length),this.insertMergedLines({row:row,column:column},lines)},this.insertMergedLines=function(position,lines){var start=this.clippedPos(position.row,position.column),end={row:start.row+lines.length-1,column:(1==lines.length?start.column:0)+lines[lines.length-1].length};return this.applyDelta({start:start,end:end,action:"insert",lines:lines}),this.clonePos(end)},this.remove=function(range){var start=this.clippedPos(range.start.row,range.start.column),end=this.clippedPos(range.end.row,range.end.column);return this.applyDelta({start:start,end:end,action:"remove",lines:this.getLinesForRange({start:start,end:end})}),this.clonePos(start)},this.removeInLine=function(row,startColumn,endColumn){var start=this.clippedPos(row,startColumn),end=this.clippedPos(row,endColumn);return this.applyDelta({start:start,end:end,action:"remove",lines:this.getLinesForRange({start:start,end:end})},!0),this.clonePos(start)},this.removeFullLines=function(firstRow,lastRow){firstRow=Math.min(Math.max(0,firstRow),this.getLength()-1),lastRow=Math.min(Math.max(0,lastRow),this.getLength()-1);var deleteFirstNewLine=lastRow==this.getLength()-1&&firstRow>0,deleteLastNewLine=this.getLength()-1>lastRow,startRow=deleteFirstNewLine?firstRow-1:firstRow,startCol=deleteFirstNewLine?this.getLine(startRow).length:0,endRow=deleteLastNewLine?lastRow+1:lastRow,endCol=deleteLastNewLine?0:this.getLine(endRow).length,range=new Range(startRow,startCol,endRow,endCol),deletedLines=this.$lines.slice(firstRow,lastRow+1);return this.applyDelta({start:range.start,end:range.end,action:"remove",lines:this.getLinesForRange(range)}),deletedLines},this.removeNewLine=function(row){this.getLength()-1>row&&row>=0&&this.applyDelta({start:this.pos(row,this.getLine(row).length),end:this.pos(row+1,0),action:"remove",lines:["",""]})},this.replace=function(range,text){if(range instanceof Range||(range=Range.fromPoints(range.start,range.end)),0===text.length&&range.isEmpty())return range.start;if(text==this.getTextRange(range))return range.end;this.remove(range);var end;return end=text?this.insert(range.start,text):range.start},this.applyDeltas=function(deltas){for(var i=0;deltas.length>i;i++)this.applyDelta(deltas[i])},this.revertDeltas=function(deltas){for(var i=deltas.length-1;i>=0;i--)this.revertDelta(deltas[i])},this.applyDelta=function(delta,doNotValidate){var isInsert="insert"==delta.action;(isInsert?1>=delta.lines.length&&!delta.lines[0]:!Range.comparePoints(delta.start,delta.end))||(isInsert&&delta.lines.length>2e4&&this.$splitAndapplyLargeDelta(delta,2e4),applyDelta(this.$lines,delta,doNotValidate),this._signal("change",delta))},this.$splitAndapplyLargeDelta=function(delta,MAX){for(var lines=delta.lines,l=lines.length,row=delta.start.row,column=delta.start.column,from=0,to=0;;){from=to,to+=MAX-1;var chunk=lines.slice(from,to);if(to>l){delta.lines=chunk,delta.start.row=row+from,delta.start.column=column;break}chunk.push(""),this.applyDelta({start:this.pos(row+from,column),end:this.pos(row+to,column=0),action:delta.action,lines:chunk},!0)}},this.revertDelta=function(delta){this.applyDelta({start:this.clonePos(delta.start),end:this.clonePos(delta.end),action:"insert"==delta.action?"remove":"insert",lines:delta.lines.slice()})},this.indexToPosition=function(index,startRow){for(var lines=this.$lines||this.getAllLines(),newlineLength=this.getNewLineCharacter().length,i=startRow||0,l=lines.length;l>i;i++)if(index-=lines[i].length+newlineLength,0>index)return{row:i,column:index+lines[i].length+newlineLength};return{row:l-1,column:lines[l-1].length}},this.positionToIndex=function(pos,startRow){for(var lines=this.$lines||this.getAllLines(),newlineLength=this.getNewLineCharacter().length,index=0,row=Math.min(pos.row,lines.length),i=startRow||0;row>i;++i)index+=lines[i].length+newlineLength;return index+pos.column}}).call(Document.prototype),exports.Document=Document}),ace.define("ace/lib/lang",["require","exports","module"],function(acequire,exports){"use strict";exports.last=function(a){return a[a.length-1]},exports.stringReverse=function(string){return string.split("").reverse().join("")},exports.stringRepeat=function(string,count){for(var result="";count>0;)1&count&&(result+=string),(count>>=1)&&(string+=string);return result};var trimBeginRegexp=/^\\s\\s*/,trimEndRegexp=/\\s\\s*$/;exports.stringTrimLeft=function(string){return string.replace(trimBeginRegexp,"")},exports.stringTrimRight=function(string){return string.replace(trimEndRegexp,"")},exports.copyObject=function(obj){var copy={};for(var key in obj)copy[key]=obj[key];return copy},exports.copyArray=function(array){for(var copy=[],i=0,l=array.length;l>i;i++)copy[i]=array[i]&&"object"==typeof array[i]?this.copyObject(array[i]):array[i];return copy},exports.deepCopy=function deepCopy(obj){if("object"!=typeof obj||!obj)return obj;var copy;if(Array.isArray(obj)){copy=[];for(var key=0;obj.length>key;key++)copy[key]=deepCopy(obj[key]);return copy}var cons=obj.constructor;if(cons===RegExp)return obj;copy=cons();for(var key in obj)copy[key]=deepCopy(obj[key]);return copy},exports.arrayToMap=function(arr){for(var map={},i=0;arr.length>i;i++)map[arr[i]]=1;return map},exports.createMap=function(props){var map=Object.create(null);for(var i in props)map[i]=props[i];return map},exports.arrayRemove=function(array,value){for(var i=0;array.length>=i;i++)value===array[i]&&array.splice(i,1)},exports.escapeRegExp=function(str){return str.replace(/([.*+?^${}()|[\\]\\/\\\\])/g,"\\\\$1")},exports.escapeHTML=function(str){return str.replace(/&/g,"&").replace(/"/g,""").replace(/\'/g,"'").replace(/i;i+=2){if(Array.isArray(data[i+1]))var d={action:"insert",start:data[i],lines:data[i+1]};else var d={action:"remove",start:data[i],end:data[i+1]};doc.applyDelta(d,!0)}return _self.$timeout?deferredUpdate.schedule(_self.$timeout):(_self.onUpdate(),void 0)})};(function(){this.$timeout=500,this.setTimeout=function(timeout){this.$timeout=timeout},this.setValue=function(value){this.doc.setValue(value),this.deferredUpdate.schedule(this.$timeout)},this.getValue=function(callbackId){this.sender.callback(this.doc.getValue(),callbackId)},this.onUpdate=function(){},this.isPending=function(){return this.deferredUpdate.isPending()}}).call(Mirror.prototype)}),ace.define("ace/mode/json/json_parse",["require","exports","module"],function(){"use strict";var at,ch,text,value,escapee={\'"\':\'"\',"\\\\":"\\\\","/":"/",b:"\\b",f:"\\f",n:"\\n",r:"\\r",t:" "},error=function(m){throw{name:"SyntaxError",message:m,at:at,text:text}},next=function(c){return c&&c!==ch&&error("Expected \'"+c+"\' instead of \'"+ch+"\'"),ch=text.charAt(at),at+=1,ch},number=function(){var number,string="";for("-"===ch&&(string="-",next("-"));ch>="0"&&"9">=ch;)string+=ch,next();if("."===ch)for(string+=".";next()&&ch>="0"&&"9">=ch;)string+=ch;if("e"===ch||"E"===ch)for(string+=ch,next(),("-"===ch||"+"===ch)&&(string+=ch,next());ch>="0"&&"9">=ch;)string+=ch,next();return number=+string,isNaN(number)?(error("Bad number"),void 0):number},string=function(){var hex,i,uffff,string="";if(\'"\'===ch)for(;next();){if(\'"\'===ch)return next(),string;if("\\\\"===ch)if(next(),"u"===ch){for(uffff=0,i=0;4>i&&(hex=parseInt(next(),16),isFinite(hex));i+=1)uffff=16*uffff+hex;string+=String.fromCharCode(uffff)}else{if("string"!=typeof escapee[ch])break;string+=escapee[ch]}else string+=ch}error("Bad string")},white=function(){for(;ch&&" ">=ch;)next()},word=function(){switch(ch){case"t":return next("t"),next("r"),next("u"),next("e"),!0;case"f":return next("f"),next("a"),next("l"),next("s"),next("e"),!1;case"n":return next("n"),next("u"),next("l"),next("l"),null}error("Unexpected \'"+ch+"\'")},array=function(){var array=[];if("["===ch){if(next("["),white(),"]"===ch)return next("]"),array;for(;ch;){if(array.push(value()),white(),"]"===ch)return next("]"),array;next(","),white()}}error("Bad array")},object=function(){var key,object={};if("{"===ch){if(next("{"),white(),"}"===ch)return next("}"),object;for(;ch;){if(key=string(),white(),next(":"),Object.hasOwnProperty.call(object,key)&&error(\'Duplicate key "\'+key+\'"\'),object[key]=value(),white(),"}"===ch)return next("}"),object;next(","),white()}}error("Bad object")};return value=function(){switch(white(),ch){case"{":return object();case"[":return array();case\'"\':return string();case"-":return number();default:return ch>="0"&&"9">=ch?number():word()}},function(source,reviver){var result;return text=source,at=0,ch=" ",result=value(),white(),ch&&error("Syntax error"),"function"==typeof reviver?function walk(holder,key){var k,v,value=holder[key];if(value&&"object"==typeof value)for(k in value)Object.hasOwnProperty.call(value,k)&&(v=walk(value,k),void 0!==v?value[k]=v:delete value[k]);return reviver.call(holder,key,value)}({"":result},""):result}}),ace.define("ace/mode/json_worker",["require","exports","module","ace/lib/oop","ace/worker/mirror","ace/mode/json/json_parse"],function(acequire,exports){"use strict";var oop=acequire("../lib/oop"),Mirror=acequire("../worker/mirror").Mirror,parse=acequire("./json/json_parse"),JsonWorker=exports.JsonWorker=function(sender){Mirror.call(this,sender),this.setTimeout(200)};oop.inherits(JsonWorker,Mirror),function(){this.onUpdate=function(){var value=this.doc.getValue(),errors=[];try{value&&parse(value)}catch(e){var pos=this.doc.indexToPosition(e.at-1);errors.push({row:pos.row,column:pos.column,text:e.message,type:"error"})}this.sender.emit("annotate",errors)}}.call(JsonWorker.prototype)}),ace.define("ace/lib/es5-shim",["require","exports","module"],function(){function Empty(){}function doesDefinePropertyWork(object){try{return Object.defineProperty(object,"sentinel",{}),"sentinel"in object}catch(exception){}}function toInteger(n){return n=+n,n!==n?n=0:0!==n&&n!==1/0&&n!==-(1/0)&&(n=(n>0||-1)*Math.floor(Math.abs(n))),n}Function.prototype.bind||(Function.prototype.bind=function(that){var target=this;if("function"!=typeof target)throw new TypeError("Function.prototype.bind called on incompatible "+target);var args=slice.call(arguments,1),bound=function(){if(this instanceof bound){var result=target.apply(this,args.concat(slice.call(arguments)));return Object(result)===result?result:this}return target.apply(that,args.concat(slice.call(arguments)))};return target.prototype&&(Empty.prototype=target.prototype,bound.prototype=new Empty,Empty.prototype=null),bound});var defineGetter,defineSetter,lookupGetter,lookupSetter,supportsAccessors,call=Function.prototype.call,prototypeOfArray=Array.prototype,prototypeOfObject=Object.prototype,slice=prototypeOfArray.slice,_toString=call.bind(prototypeOfObject.toString),owns=call.bind(prototypeOfObject.hasOwnProperty);if((supportsAccessors=owns(prototypeOfObject,"__defineGetter__"))&&(defineGetter=call.bind(prototypeOfObject.__defineGetter__),defineSetter=call.bind(prototypeOfObject.__defineSetter__),lookupGetter=call.bind(prototypeOfObject.__lookupGetter__),lookupSetter=call.bind(prototypeOfObject.__lookupSetter__)),2!=[1,2].splice(0).length)if(function(){function makeArray(l){var a=Array(l+2);return a[0]=a[1]=0,a}var lengthBefore,array=[];return array.splice.apply(array,makeArray(20)),array.splice.apply(array,makeArray(26)),lengthBefore=array.length,array.splice(5,0,"XXX"),lengthBefore+1==array.length,lengthBefore+1==array.length?!0:void 0\n}()){var array_splice=Array.prototype.splice;Array.prototype.splice=function(start,deleteCount){return arguments.length?array_splice.apply(this,[void 0===start?0:start,void 0===deleteCount?this.length-start:deleteCount].concat(slice.call(arguments,2))):[]}}else Array.prototype.splice=function(pos,removeCount){var length=this.length;pos>0?pos>length&&(pos=length):void 0==pos?pos=0:0>pos&&(pos=Math.max(length+pos,0)),length>pos+removeCount||(removeCount=length-pos);var removed=this.slice(pos,pos+removeCount),insert=slice.call(arguments,2),add=insert.length;if(pos===length)add&&this.push.apply(this,insert);else{var remove=Math.min(removeCount,length-pos),tailOldPos=pos+remove,tailNewPos=tailOldPos+add-remove,tailCount=length-tailOldPos,lengthAfterRemove=length-remove;if(tailOldPos>tailNewPos)for(var i=0;tailCount>i;++i)this[tailNewPos+i]=this[tailOldPos+i];else if(tailNewPos>tailOldPos)for(i=tailCount;i--;)this[tailNewPos+i]=this[tailOldPos+i];if(add&&pos===lengthAfterRemove)this.length=lengthAfterRemove,this.push.apply(this,insert);else for(this.length=lengthAfterRemove+add,i=0;add>i;++i)this[pos+i]=insert[i]}return removed};Array.isArray||(Array.isArray=function(obj){return"[object Array]"==_toString(obj)});var boxedString=Object("a"),splitString="a"!=boxedString[0]||!(0 in boxedString);if(Array.prototype.forEach||(Array.prototype.forEach=function(fun){var object=toObject(this),self=splitString&&"[object String]"==_toString(this)?this.split(""):object,thisp=arguments[1],i=-1,length=self.length>>>0;if("[object Function]"!=_toString(fun))throw new TypeError;for(;length>++i;)i in self&&fun.call(thisp,self[i],i,object)}),Array.prototype.map||(Array.prototype.map=function(fun){var object=toObject(this),self=splitString&&"[object String]"==_toString(this)?this.split(""):object,length=self.length>>>0,result=Array(length),thisp=arguments[1];if("[object Function]"!=_toString(fun))throw new TypeError(fun+" is not a function");for(var i=0;length>i;i++)i in self&&(result[i]=fun.call(thisp,self[i],i,object));return result}),Array.prototype.filter||(Array.prototype.filter=function(fun){var value,object=toObject(this),self=splitString&&"[object String]"==_toString(this)?this.split(""):object,length=self.length>>>0,result=[],thisp=arguments[1];if("[object Function]"!=_toString(fun))throw new TypeError(fun+" is not a function");for(var i=0;length>i;i++)i in self&&(value=self[i],fun.call(thisp,value,i,object)&&result.push(value));return result}),Array.prototype.every||(Array.prototype.every=function(fun){var object=toObject(this),self=splitString&&"[object String]"==_toString(this)?this.split(""):object,length=self.length>>>0,thisp=arguments[1];if("[object Function]"!=_toString(fun))throw new TypeError(fun+" is not a function");for(var i=0;length>i;i++)if(i in self&&!fun.call(thisp,self[i],i,object))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(fun){var object=toObject(this),self=splitString&&"[object String]"==_toString(this)?this.split(""):object,length=self.length>>>0,thisp=arguments[1];if("[object Function]"!=_toString(fun))throw new TypeError(fun+" is not a function");for(var i=0;length>i;i++)if(i in self&&fun.call(thisp,self[i],i,object))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(fun){var object=toObject(this),self=splitString&&"[object String]"==_toString(this)?this.split(""):object,length=self.length>>>0;if("[object Function]"!=_toString(fun))throw new TypeError(fun+" is not a function");if(!length&&1==arguments.length)throw new TypeError("reduce of empty array with no initial value");var result,i=0;if(arguments.length>=2)result=arguments[1];else for(;;){if(i in self){result=self[i++];break}if(++i>=length)throw new TypeError("reduce of empty array with no initial value")}for(;length>i;i++)i in self&&(result=fun.call(void 0,result,self[i],i,object));return result}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(fun){var object=toObject(this),self=splitString&&"[object String]"==_toString(this)?this.split(""):object,length=self.length>>>0;if("[object Function]"!=_toString(fun))throw new TypeError(fun+" is not a function");if(!length&&1==arguments.length)throw new TypeError("reduceRight of empty array with no initial value");var result,i=length-1;if(arguments.length>=2)result=arguments[1];else for(;;){if(i in self){result=self[i--];break}if(0>--i)throw new TypeError("reduceRight of empty array with no initial value")}do i in this&&(result=fun.call(void 0,result,self[i],i,object));while(i--);return result}),Array.prototype.indexOf&&-1==[0,1].indexOf(1,2)||(Array.prototype.indexOf=function(sought){var self=splitString&&"[object String]"==_toString(this)?this.split(""):toObject(this),length=self.length>>>0;if(!length)return-1;var i=0;for(arguments.length>1&&(i=toInteger(arguments[1])),i=i>=0?i:Math.max(0,length+i);length>i;i++)if(i in self&&self[i]===sought)return i;return-1}),Array.prototype.lastIndexOf&&-1==[0,1].lastIndexOf(0,-3)||(Array.prototype.lastIndexOf=function(sought){var self=splitString&&"[object String]"==_toString(this)?this.split(""):toObject(this),length=self.length>>>0;if(!length)return-1;var i=length-1;for(arguments.length>1&&(i=Math.min(i,toInteger(arguments[1]))),i=i>=0?i:length-Math.abs(i);i>=0;i--)if(i in self&&sought===self[i])return i;return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(object){return object.__proto__||(object.constructor?object.constructor.prototype:prototypeOfObject)}),!Object.getOwnPropertyDescriptor){var ERR_NON_OBJECT="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(object,property){if("object"!=typeof object&&"function"!=typeof object||null===object)throw new TypeError(ERR_NON_OBJECT+object);if(owns(object,property)){var descriptor,getter,setter;if(descriptor={enumerable:!0,configurable:!0},supportsAccessors){var prototype=object.__proto__;object.__proto__=prototypeOfObject;var getter=lookupGetter(object,property),setter=lookupSetter(object,property);if(object.__proto__=prototype,getter||setter)return getter&&(descriptor.get=getter),setter&&(descriptor.set=setter),descriptor}return descriptor.value=object[property],descriptor}}}if(Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(object){return Object.keys(object)}),!Object.create){var createEmpty;createEmpty=null===Object.prototype.__proto__?function(){return{__proto__:null}}:function(){var empty={};for(var i in empty)empty[i]=null;return empty.constructor=empty.hasOwnProperty=empty.propertyIsEnumerable=empty.isPrototypeOf=empty.toLocaleString=empty.toString=empty.valueOf=empty.__proto__=null,empty},Object.create=function(prototype,properties){var object;if(null===prototype)object=createEmpty();else{if("object"!=typeof prototype)throw new TypeError("typeof prototype["+typeof prototype+"] != \'object\'");var Type=function(){};Type.prototype=prototype,object=new Type,object.__proto__=prototype}return void 0!==properties&&Object.defineProperties(object,properties),object}}if(Object.defineProperty){var definePropertyWorksOnObject=doesDefinePropertyWork({}),definePropertyWorksOnDom="undefined"==typeof document||doesDefinePropertyWork(document.createElement("div"));if(!definePropertyWorksOnObject||!definePropertyWorksOnDom)var definePropertyFallback=Object.defineProperty}if(!Object.defineProperty||definePropertyFallback){var ERR_NON_OBJECT_DESCRIPTOR="Property description must be an object: ",ERR_NON_OBJECT_TARGET="Object.defineProperty called on non-object: ",ERR_ACCESSORS_NOT_SUPPORTED="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(object,property,descriptor){if("object"!=typeof object&&"function"!=typeof object||null===object)throw new TypeError(ERR_NON_OBJECT_TARGET+object);if("object"!=typeof descriptor&&"function"!=typeof descriptor||null===descriptor)throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR+descriptor);if(definePropertyFallback)try{return definePropertyFallback.call(Object,object,property,descriptor)}catch(exception){}if(owns(descriptor,"value"))if(supportsAccessors&&(lookupGetter(object,property)||lookupSetter(object,property))){var prototype=object.__proto__;object.__proto__=prototypeOfObject,delete object[property],object[property]=descriptor.value,object.__proto__=prototype}else object[property]=descriptor.value;else{if(!supportsAccessors)throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);owns(descriptor,"get")&&defineGetter(object,property,descriptor.get),owns(descriptor,"set")&&defineSetter(object,property,descriptor.set)}return object}}Object.defineProperties||(Object.defineProperties=function(object,properties){for(var property in properties)owns(properties,property)&&Object.defineProperty(object,property,properties[property]);return object}),Object.seal||(Object.seal=function(object){return object}),Object.freeze||(Object.freeze=function(object){return object});try{Object.freeze(function(){})}catch(exception){Object.freeze=function(freezeObject){return function(object){return"function"==typeof object?object:freezeObject(object)}}(Object.freeze)}if(Object.preventExtensions||(Object.preventExtensions=function(object){return object}),Object.isSealed||(Object.isSealed=function(){return!1}),Object.isFrozen||(Object.isFrozen=function(){return!1}),Object.isExtensible||(Object.isExtensible=function(object){if(Object(object)===object)throw new TypeError;for(var name="";owns(object,name);)name+="?";object[name]=!0;var returnValue=owns(object,name);return delete object[name],returnValue}),!Object.keys){var hasDontEnumBug=!0,dontEnums=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],dontEnumsLength=dontEnums.length;for(var key in{toString:null})hasDontEnumBug=!1;Object.keys=function(object){if("object"!=typeof object&&"function"!=typeof object||null===object)throw new TypeError("Object.keys called on a non-object");var keys=[];for(var name in object)owns(object,name)&&keys.push(name);if(hasDontEnumBug)for(var i=0,ii=dontEnumsLength;ii>i;i++){var dontEnum=dontEnums[i];owns(object,dontEnum)&&keys.push(dontEnum)}return keys}}Date.now||(Date.now=function(){return(new Date).getTime()});var ws=" \\n\x0B\\f\\r   ᠎              \\u2028\\u2029\ufeff";if(!String.prototype.trim||ws.trim()){ws="["+ws+"]";var trimBeginRegexp=RegExp("^"+ws+ws+"*"),trimEndRegexp=RegExp(ws+ws+"*$");String.prototype.trim=function(){return(this+"").replace(trimBeginRegexp,"").replace(trimEndRegexp,"")}}var toObject=function(o){if(null==o)throw new TypeError("can\'t convert "+o+" to object");return Object(o)}});'; +},function(e,t){ace.define("ace/ext/searchbox",["require","exports","module","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/keyboard/hash_handler","ace/lib/keys"],function(e,t,i){"use strict";var n=e("../lib/dom"),r=e("../lib/lang"),o=e("../lib/event"),s=" .ace_search { background-color: #ddd; border: 1px solid #cbcbcb; border-top: 0 none; max-width: 325px; overflow: hidden; margin: 0; padding: 4px; padding-right: 6px; padding-bottom: 0; position: absolute; top: 0px; z-index: 99; white-space: normal; } .ace_search.left { border-left: 0 none; border-radius: 0px 0px 5px 0px; left: 0; } .ace_search.right { border-radius: 0px 0px 0px 5px; border-right: 0 none; right: 0; } .ace_search_form, .ace_replace_form { border-radius: 3px; border: 1px solid #cbcbcb; float: left; margin-bottom: 4px; overflow: hidden; } .ace_search_form.ace_nomatch { outline: 1px solid red; } .ace_search_field { background-color: white; border-right: 1px solid #cbcbcb; border: 0 none; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; float: left; height: 22px; outline: 0; padding: 0 7px; width: 214px; margin: 0; } .ace_searchbtn, .ace_replacebtn { background: #fff; border: 0 none; border-left: 1px solid #dcdcdc; cursor: pointer; float: left; height: 22px; margin: 0; position: relative; } .ace_searchbtn:last-child, .ace_replacebtn:last-child { border-top-right-radius: 3px; border-bottom-right-radius: 3px; } .ace_searchbtn:disabled { background: none; cursor: default; } .ace_searchbtn { background-position: 50% 50%; background-repeat: no-repeat; width: 27px; } .ace_searchbtn.prev { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiSU1NZUAC/6E0I0yACYskCpsJiySKIiY0SUZk40FyTEgCjGgKwTRAgAEAQJUIPCE+qfkAAAAASUVORK5CYII=); } .ace_searchbtn.next { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNpiTE1NZQCC/0DMyIAKwGJMUAYDEo3M/s+EpvM/mkKwCQxYjIeLMaELoLMBAgwAU7UJObTKsvAAAAAASUVORK5CYII=); } .ace_searchbtn_close { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0; border-radius: 50%; border: 0 none; color: #656565; cursor: pointer; float: right; font: 16px/16px Arial; height: 14px; margin: 5px 1px 9px 5px; padding: 0; text-align: center; width: 14px; } .ace_searchbtn_close:hover { background-color: #656565; background-position: 50% 100%; color: white; } .ace_replacebtn.prev { width: 54px } .ace_replacebtn.next { width: 27px } .ace_button { margin-left: 2px; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -o-user-select: none; -ms-user-select: none; user-select: none; overflow: hidden; opacity: 0.7; border: 1px solid rgba(100,100,100,0.23); padding: 1px; -moz-box-sizing: border-box; box-sizing: border-box; color: black; } .ace_button:hover { background-color: #eee; opacity:1; } .ace_button:active { background-color: #ddd; } .ace_button.checked { border-color: #3399ff; opacity:1; } .ace_search_options{ margin-bottom: 3px; text-align: right; -webkit-user-select: none; -moz-user-select: none; -o-user-select: none; -ms-user-select: none; user-select: none; }",a=e("../keyboard/hash_handler").HashHandler,l=e("../lib/keys");n.importCssString(s,"ace_searchbox");var c=''.replace(/>\s+/g,">"),h=function(e,t,i){var r=n.createElement("div");r.innerHTML=c,this.element=r.firstChild,this.$init(),this.setEditor(e)};(function(){this.setEditor=function(e){e.searchBox=this,e.container.appendChild(this.element),this.editor=e},this.$initElements=function(e){this.searchBox=e.querySelector(".ace_search_form"),this.replaceBox=e.querySelector(".ace_replace_form"),this.searchOptions=e.querySelector(".ace_search_options"),this.regExpOption=e.querySelector("[action=toggleRegexpMode]"),this.caseSensitiveOption=e.querySelector("[action=toggleCaseSensitive]"),this.wholeWordOption=e.querySelector("[action=toggleWholeWords]"),this.searchInput=this.searchBox.querySelector(".ace_search_field"),this.replaceInput=this.replaceBox.querySelector(".ace_search_field")},this.$init=function(){var e=this.element;this.$initElements(e);var t=this;o.addListener(e,"mousedown",function(e){setTimeout(function(){t.activeInput.focus()},0),o.stopPropagation(e)}),o.addListener(e,"click",function(e){var i=e.target||e.srcElement,n=i.getAttribute("action");n&&t[n]?t[n]():t.$searchBarKb.commands[n]&&t.$searchBarKb.commands[n].exec(t),o.stopPropagation(e)}),o.addCommandKeyListener(e,function(e,i,n){var r=l.keyCodeToString(n),s=t.$searchBarKb.findKeyCommand(i,r);s&&s.exec&&(s.exec(t),o.stopEvent(e))}),this.$onChange=r.delayedCall(function(){t.find(!1,!1)}),o.addListener(this.searchInput,"input",function(){t.$onChange.schedule(20)}),o.addListener(this.searchInput,"focus",function(){t.activeInput=t.searchInput,t.searchInput.value&&t.highlight()}),o.addListener(this.replaceInput,"focus",function(){t.activeInput=t.replaceInput,t.searchInput.value&&t.highlight()})},this.$closeSearchBarKb=new a([{bindKey:"Esc",name:"closeSearchBar",exec:function(e){e.searchBox.hide()}}]),this.$searchBarKb=new a,this.$searchBarKb.bindKeys({"Ctrl-f|Command-f":function(e){var t=e.isReplace=!e.isReplace;e.replaceBox.style.display=t?"":"none",e.searchInput.focus()},"Ctrl-H|Command-Option-F":function(e){e.replaceBox.style.display="",e.replaceInput.focus()},"Ctrl-G|Command-G":function(e){e.findNext()},"Ctrl-Shift-G|Command-Shift-G":function(e){e.findPrev()},esc:function(e){setTimeout(function(){e.hide()})},Return:function(e){e.activeInput==e.replaceInput&&e.replace(),e.findNext()},"Shift-Return":function(e){e.activeInput==e.replaceInput&&e.replace(),e.findPrev()},"Alt-Return":function(e){e.activeInput==e.replaceInput&&e.replaceAll(),e.findAll()},Tab:function(e){(e.activeInput==e.replaceInput?e.searchInput:e.replaceInput).focus()}}),this.$searchBarKb.addCommands([{name:"toggleRegexpMode",bindKey:{win:"Alt-R|Alt-/",mac:"Ctrl-Alt-R|Ctrl-Alt-/"},exec:function(e){e.regExpOption.checked=!e.regExpOption.checked,e.$syncOptions()}},{name:"toggleCaseSensitive",bindKey:{win:"Alt-C|Alt-I",mac:"Ctrl-Alt-R|Ctrl-Alt-I"},exec:function(e){e.caseSensitiveOption.checked=!e.caseSensitiveOption.checked,e.$syncOptions()}},{name:"toggleWholeWords",bindKey:{win:"Alt-B|Alt-W",mac:"Ctrl-Alt-B|Ctrl-Alt-W"},exec:function(e){e.wholeWordOption.checked=!e.wholeWordOption.checked,e.$syncOptions()}}]),this.$syncOptions=function(){n.setCssClass(this.regExpOption,"checked",this.regExpOption.checked),n.setCssClass(this.wholeWordOption,"checked",this.wholeWordOption.checked),n.setCssClass(this.caseSensitiveOption,"checked",this.caseSensitiveOption.checked),this.find(!1,!1)},this.highlight=function(e){this.editor.session.highlight(e||this.editor.$search.$options.re),this.editor.renderer.updateBackMarkers()},this.find=function(e,t,i){var r=this.editor.find(this.searchInput.value,{skipCurrent:e,backwards:t,wrap:!0,regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked,preventScroll:i}),o=!r&&this.searchInput.value;n.setCssClass(this.searchBox,"ace_nomatch",o),this.editor._emit("findSearchBox",{match:!o}),this.highlight()},this.findNext=function(){this.find(!0,!1)},this.findPrev=function(){this.find(!0,!0)},this.findAll=function(){var e=this.editor.findAll(this.searchInput.value,{regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked}),t=!e&&this.searchInput.value;n.setCssClass(this.searchBox,"ace_nomatch",t),this.editor._emit("findSearchBox",{match:!t}),this.highlight(),this.hide()},this.replace=function(){this.editor.getReadOnly()||this.editor.replace(this.replaceInput.value)},this.replaceAndFindNext=function(){this.editor.getReadOnly()||(this.editor.replace(this.replaceInput.value),this.findNext())},this.replaceAll=function(){this.editor.getReadOnly()||this.editor.replaceAll(this.replaceInput.value)},this.hide=function(){this.element.style.display="none",this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb),this.editor.focus()},this.show=function(e,t){this.element.style.display="",this.replaceBox.style.display=t?"":"none",this.isReplace=t,e&&(this.searchInput.value=e),this.find(!1,!1,!0),this.searchInput.focus(),this.searchInput.select(),this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb)},this.isFocused=function(){var e=document.activeElement;return e==this.searchInput||e==this.replaceInput}}).call(h.prototype),t.SearchBox=h,t.Search=function(e,t){var i=e.searchBox||new h(e);i.show(e.session.getTextRange(),t)}}),function(){ace.acequire(["ace/ext/searchbox"],function(){})}()},function(e,t){ace.define("ace/theme/jsoneditor",["require","exports","module","ace/lib/dom"],function(e,t,i){t.isDark=!1,t.cssClass="ace-jsoneditor",t.cssText='.ace-jsoneditor .ace_gutter { background: #ebebeb; color: #333 } .ace-jsoneditor.ace_editor { font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif; line-height: 1.3; } .ace-jsoneditor .ace_print-margin { width: 1px; background: #e8e8e8 } .ace-jsoneditor .ace_scroller { background-color: #FFFFFF } .ace-jsoneditor .ace_text-layer { color: gray } .ace-jsoneditor .ace_variable { color: #1a1a1a } .ace-jsoneditor .ace_cursor { border-left: 2px solid #000000 } .ace-jsoneditor .ace_overwrite-cursors .ace_cursor { border-left: 0px; border-bottom: 1px solid #000000 } .ace-jsoneditor .ace_marker-layer .ace_selection { background: lightgray } .ace-jsoneditor.ace_multiselect .ace_selection.ace_start { box-shadow: 0 0 3px 0px #FFFFFF; border-radius: 2px } .ace-jsoneditor .ace_marker-layer .ace_step { background: rgb(255, 255, 0) } .ace-jsoneditor .ace_marker-layer .ace_bracket { margin: -1px 0 0 -1px; border: 1px solid #BFBFBF } .ace-jsoneditor .ace_marker-layer .ace_active-line { background: #FFFBD1 } .ace-jsoneditor .ace_gutter-active-line { background-color : #dcdcdc } .ace-jsoneditor .ace_marker-layer .ace_selected-word { border: 1px solid lightgray } .ace-jsoneditor .ace_invisible { color: #BFBFBF } .ace-jsoneditor .ace_keyword, .ace-jsoneditor .ace_meta, .ace-jsoneditor .ace_support.ace_constant.ace_property-value { color: #AF956F } .ace-jsoneditor .ace_keyword.ace_operator { color: #484848 } .ace-jsoneditor .ace_keyword.ace_other.ace_unit { color: #96DC5F } .ace-jsoneditor .ace_constant.ace_language { color: darkorange } .ace-jsoneditor .ace_constant.ace_numeric { color: red } .ace-jsoneditor .ace_constant.ace_character.ace_entity { color: #BF78CC } .ace-jsoneditor .ace_invalid { color: #FFFFFF; background-color: #FF002A; } .ace-jsoneditor .ace_fold { background-color: #AF956F; border-color: #000000 } .ace-jsoneditor .ace_storage, .ace-jsoneditor .ace_support.ace_class, .ace-jsoneditor .ace_support.ace_function, .ace-jsoneditor .ace_support.ace_other, .ace-jsoneditor .ace_support.ace_type { color: #C52727 } .ace-jsoneditor .ace_string { color: green } .ace-jsoneditor .ace_comment { color: #BCC8BA } .ace-jsoneditor .ace_entity.ace_name.ace_tag, .ace-jsoneditor .ace_entity.ace_other.ace_attribute-name { color: #606060 } .ace-jsoneditor .ace_markup.ace_underline { text-decoration: underline } .ace-jsoneditor .ace_indent-guide { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y }';var n=e("../lib/dom");n.importCssString(t.cssText,t.cssClass)})}])}); +//# sourceMappingURL=jsoneditor.map \ No newline at end of file From e2a6c08f19f9ede0abd045b032e168292771407c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 22 Aug 2016 17:44:22 -0700 Subject: [PATCH 222/332] cleanup prints --- scripts/system/html/js/entityProperties.js | 42 ++++++---------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index bf477a3ed5..7721d06982 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -141,7 +141,6 @@ function createEmitTextPropertyUpdateFunction(propertyName) { return function() { var properties = {}; properties[propertyName] = this.value; - console.log('properties at set thingo yo') EventBridge.emitWebEvent( JSON.stringify({ type: "update", @@ -283,16 +282,13 @@ function setUserDataFromEditor() { var json = null; try { json = editor.get(); - console.log('its good json') } catch (e) { - console.log('failed to parse json', e) alert('Invalid JSON code - look for red X in your code ', +e) } if (json === null) { return; } else { var text = editor.getText() - console.log('editor text at send:', text) EventBridge.emitWebEvent( JSON.stringify({ type: "update", @@ -312,12 +308,9 @@ function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, d var parsedData = {}; try { if ($('#userdata-editor').css('height') === "0px") { - console.log('GET JSON FROM EDITOR') parsedData = editor.getJSON(); } else { - console.log('GET JSON FROM TEXT AREA'); parsedData = JSON.parse(userDataElement.value); - } } catch (e) {} @@ -367,9 +360,6 @@ function createJSONEditor() { }, onError: function(e) { alert('JSON editor:' + e) - }, - onChange: function() { - console.log('editor did change') } }; editor = new JSONEditor(container, options); @@ -412,14 +402,12 @@ function hideUserDataTextArea() { }; function showStaticUserData() { - console.log('showing static userdata') $('#static-userdata').show(); $('#static-userdata').css('height', $('#userdata-editor').height()) $('#static-userdata').text(editor.getText()); }; function removeStaticUserData() { - console.log('hiding static userdata') $('#static-userdata').hide(); }; @@ -432,9 +420,7 @@ function getEditorJSON() { }; function deleteJSONEditor() { - console.log('should delete editor') if (editor !== null) { - console.log('has one so do it') editor.destroy(); } }; @@ -769,20 +755,18 @@ function loaded() { //its json if (Object.keys(json).length === 0 && json.constructor === Object) { //it's an empty object - console.log('empty object') } createJSONEditor(); setEditorJSON(json) showSaveUserDataButton(); hideNewJSONEditorButton(); - console.log('did parse json successfully') } catch (e) { - console.log('error parsing json') + //normal text + elUserData.value = properties.userData; showUserDataTextArea(); hideSaveUserDataButton(); - //normal text } elHyperlinkHref.value = properties.href; @@ -1063,29 +1047,23 @@ function loaded() { elClearUserData.addEventListener("click", function() { deleteJSONEditor(); - console.log('CLEAR USER DATA WAS CLICKED w') elUserData.value = ""; showUserDataTextArea(); - console.log('CLEAR USER DATA WAS CLICKED x') showNewJSONEditorButton(); - console.log('CLEAR USER DATA WAS CLICKED y') hideSaveUserDataButton(); - console.log('CLEAR USER DATA WAS CLICKED z') - var properties = {}; - properties['userData'] = elUserData.value; - console.log('properties at set thingo yo', elUserData.value) - EventBridge.emitWebEvent( - JSON.stringify({ - type: "update", - properties: properties, - }) - ); + var properties = {}; + properties['userData'] = elUserData.value; + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: properties, + }) + ); }); elSaveUserData.addEventListener("click", function() { - console.log('click on save json button') setUserDataFromEditor(); }); From f7389dc059f12d23a892e467ac1fb46a3b4795f1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 23 Aug 2016 16:28:32 +1200 Subject: [PATCH 223/332] Fix server card window scale --- interface/resources/qml/AddressBarDialog.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index f2bdc5ba6d..8e68906406 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -219,6 +219,7 @@ Window { Window { width: 750; height: 500; + scale: 0.8 // Reset scale of Window to 1.0 (counteract address bar's scale value of 1.25) HifiControls.WebView { anchors.fill: parent; id: storyCardHTML; From d0a25e7783d8ec5773a52a1a30a4cafbb3ead887 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 22 Aug 2016 23:51:35 -0700 Subject: [PATCH 224/332] fix switching entities --- scripts/system/html/entityProperties.html | 3 ++- scripts/system/html/js/entityProperties.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index f5e7ffea63..c39fa00cec 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -67,9 +67,10 @@
+ - +
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 7721d06982..825ae0e72d 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -766,6 +766,7 @@ function loaded() { elUserData.value = properties.userData; showUserDataTextArea(); + showNewJSONEditorButton(); hideSaveUserDataButton(); } From d9a39553bd2321abad94b01a472a3f087afa6d9e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 23 Aug 2016 00:14:53 -0700 Subject: [PATCH 225/332] style edits and saved message --- scripts/system/html/css/edit-style.css | 6 ++++++ scripts/system/html/css/jsoneditor.css | 2 +- scripts/system/html/entityProperties.html | 2 +- scripts/system/html/js/entityProperties.js | 14 ++++++++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 58f8833db3..8a6c7786bf 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1096,4 +1096,10 @@ input#reset-to-natural-dimensions { margin-top:5px; margin-bottom:10px; background-color: #2e2e2e; +} + +#userdata-saved{ + margin-top:5px; + font-size:16px; + display:none; } \ No newline at end of file diff --git a/scripts/system/html/css/jsoneditor.css b/scripts/system/html/css/jsoneditor.css index aea0696609..4d63380a62 100644 --- a/scripts/system/html/css/jsoneditor.css +++ b/scripts/system/html/css/jsoneditor.css @@ -212,7 +212,6 @@ div.jsoneditor { position: relative; padding: 0; line-height: 100%; - min-height: 200px; } div.jsoneditor-tree table.jsoneditor-tree { @@ -225,6 +224,7 @@ div.jsoneditor-tree table.jsoneditor-tree { div.jsoneditor-outer { width: 100%; height: 100%; + min-height: 150px; margin: -35px 0 0 0; padding: 0 0 0 0; -moz-box-sizing: border-box; diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index c39fa00cec..0eeb817640 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -70,7 +70,7 @@ - + Saved!
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 825ae0e72d..81c13b63e9 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1064,8 +1064,16 @@ function loaded() { }); + var savedJSONTimer = null; elSaveUserData.addEventListener("click", function() { setUserDataFromEditor(); + $('#userdata-saved').show(); + if (savedJSONTimer !== null) { + clearTimeout(savedJSONTimer); + } + savedJSONTimer = setTimeout(function() { + $('#userdata-saved').hide(); + }, 1500) }); elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); @@ -1080,6 +1088,12 @@ function loaded() { showSaveUserDataButton(); }); + elJSONEditor.addEventListener('mouseenter', function() { + console.log('mouse in the json editor') + }) + elJSONEditor.addEventListener('mouseleave', function() { + console.log('mouse leaves the json editor') + }) var colorChangeFunction = createEmitColorPropertyUpdateFunction( 'color', elColorRed, elColorGreen, elColorBlue); elColorRed.addEventListener('change', colorChangeFunction); From 6ec429fa0bce6eddb02f66e1930e69625daccc18 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 23 Aug 2016 00:19:03 -0700 Subject: [PATCH 226/332] remove unused event listeners --- scripts/system/html/js/entityProperties.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 81c13b63e9..5d6ce8f97e 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1088,12 +1088,6 @@ function loaded() { showSaveUserDataButton(); }); - elJSONEditor.addEventListener('mouseenter', function() { - console.log('mouse in the json editor') - }) - elJSONEditor.addEventListener('mouseleave', function() { - console.log('mouse leaves the json editor') - }) var colorChangeFunction = createEmitColorPropertyUpdateFunction( 'color', elColorRed, elColorGreen, elColorBlue); elColorRed.addEventListener('change', colorChangeFunction); From 7601876641f41f90c28c8cfdb3f38b7664a2175e Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 23 Aug 2016 09:44:28 -0700 Subject: [PATCH 227/332] Scale the activity card back up to match the window being scaled down, so that it covers the address bar. --- interface/resources/qml/AddressBarDialog.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8e68906406..2536fdade9 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -217,8 +217,8 @@ Window { } Window { - width: 750; - height: 500; + width: 938; + height: 625; scale: 0.8 // Reset scale of Window to 1.0 (counteract address bar's scale value of 1.25) HifiControls.WebView { anchors.fill: parent; From 9bafd82b2c03c1f786785acfd4e485e0030d864b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 9 Aug 2016 15:49:00 -0700 Subject: [PATCH 228/332] marketplace now pops up a marketplace web entity --- scripts/system/libraries/WebBuddy.js | 95 ++++++++++++++++++++++++++++ scripts/system/marketplace.js | 37 ++++++++--- 2 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 scripts/system/libraries/WebBuddy.js diff --git a/scripts/system/libraries/WebBuddy.js b/scripts/system/libraries/WebBuddy.js new file mode 100644 index 0000000000..d584bcc564 --- /dev/null +++ b/scripts/system/libraries/WebBuddy.js @@ -0,0 +1,95 @@ +// +// WebBuddy.js +// +// Created by Anthony J. Thibault on 8/8/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +var NEGATIVE_ONE = 65535; + +// ctor +WebBuddy = function (url) { + + var ASPECT = 4.0 / 3.0; + var WIDTH = 0.4; + + var spawnPoint = Vec3.sum(Vec3.sum(MyAvatar.position, Vec3.multiply(1.0, Quat.getFront(MyAvatar.orientation))), + {x: 0, y: 0.5, z: 0}); + + var webEntityPosition = spawnPoint; + var webEntityRotation = MyAvatar.orientation; + this.webEntityID = Entities.addEntity({ + type: "Web", + sourceUrl: url, + dimensions: {x: WIDTH, y: WIDTH * ASPECT, z: 0.1}, + position: webEntityPosition, + rotation: webEntityRotation, + name: "web", + dynamic: true, + angularDamping: 0.9, + damping: 0.9, + gravity: {x: 0, y: 0, z: 0}, + shapeType: "box", + userData: JSON.stringify({ + "grabbableKey": {"grabbable": true} + }), + parentID: MyAvatar.sessionUUID, + parentJointIndex: NEGATIVE_ONE + }); + + this.state = "idle"; + + // compute the room/sensor matrix of the entity. + var invRoomMat = Mat4.inverse(MyAvatar.sensorToWorldMatrix); + var entityWorldMat = Mat4.createFromRotAndTrans(webEntityRotation, webEntityPosition); + this.entityRoomMat = Mat4.multiply(invRoomMat, entityWorldMat); + + var _this = this; + this.updateFunc = function (dt) { + _this.update(dt); + }; + Script.update.connect(this.updateFunc); +}; + +WebBuddy.prototype.destroy = function () { + Entities.deleteEntity(this.webEntityID); + Script.update.disconnect(this.updateFunc); +}; + +WebBuddy.prototype.update = function (dt) { + + var props = Entities.getEntityProperties(this.webEntityID, ["position", "rotation", "parentID", "parentJointIndex"]); + var entityWorldMat; + + if (this.state === "idle") { + + if (props.parentID !== MyAvatar.sessionUUID || props.parentJointIndex !== NEGATIVE_ONE) { + this.state = "held"; + return; + } + + // convert the sensor/room matrix of the entity into world space, using the current sensorToWorldMatrix + var roomMat = MyAvatar.sensorToWorldMatrix; + entityWorldMat = Mat4.multiply(roomMat, this.entityRoomMat); + + // slam the world space position and orientation + Entities.editEntity(this.webEntityID, { + position: Mat4.extractTranslation(entityWorldMat), + rotation: Mat4.extractRotation(entityWorldMat) + }); + } else if (this.state === "held") { + if (props.parentID === MyAvatar.sessionUUID && props.parentJointIndex === NEGATIVE_ONE) { + + // re-compute the room/sensor matrix for the avatar now that it has been released. + var invRoomMat = Mat4.inverse(MyAvatar.sensorToWorldMatrix); + entityWorldMat = Mat4.createFromRotAndTrans(props.rotation, props.position); + this.entityRoomMat = Mat4.multiply(invRoomMat, entityWorldMat); + + this.state = "idle"; + return; + } + } +}; + diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js index 356ed8e8cf..26d46d6219 100644 --- a/scripts/system/marketplace.js +++ b/scripts/system/marketplace.js @@ -8,6 +8,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* global WebBuddy */ +Script.include("./libraries/WebBuddy.js"); + var toolIconUrl = Script.resolvePath("assets/images/tools/"); var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; @@ -22,26 +25,44 @@ var marketplaceWindow = new OverlayWebWindow({ var toolHeight = 50; var toolWidth = 50; var TOOLBAR_MARGIN_Y = 0; +var marketplaceVisible = false; +var marketplaceWebBuddy; +function shouldShowWebBuddyMarketplace() { + var rightPose = Controller.getPoseValue(Controller.Standard.RightHand); + var leftPose = Controller.getPoseValue(Controller.Standard.LeftHand); + return HMD.active && (leftPose.valid || rightPose.valid); +} function showMarketplace(marketplaceID) { - var url = MARKETPLACE_URL; - if (marketplaceID) { - url = url + "/items/" + marketplaceID; + if (shouldShowWebBuddyMarketplace()) { + marketplaceWebBuddy = new WebBuddy("https://metaverse.highfidelity.com/marketplace"); + } else { + var url = MARKETPLACE_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + marketplaceWindow.setURL(url); + marketplaceWindow.setVisible(true); } - marketplaceWindow.setURL(url); - marketplaceWindow.setVisible(true); + marketplaceVisible = true; UserActivityLogger.openedMarketplace(); } function hideMarketplace() { - marketplaceWindow.setVisible(false); - marketplaceWindow.setURL("about:blank"); + if (marketplaceWindow.visible) { + marketplaceWindow.setVisible(false); + marketplaceWindow.setURL("about:blank"); + } else { + marketplaceWebBuddy.destroy(); + marketplaceWebBuddy = null; + } + marketplaceVisible = false; } function toggleMarketplace() { - if (marketplaceWindow.visible) { + if (marketplaceVisible) { hideMarketplace(); } else { showMarketplace(); From 6704ae9ad4774628d07c3d5851cb0a23cd9536a2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 10 Aug 2016 11:07:23 -0700 Subject: [PATCH 229/332] Added dpi parameter to userData (this is temporary) --- .../src/RenderableWebEntityItem.cpp | 8 +++---- libraries/entities/src/WebEntityItem.cpp | 23 +++++++++++++++++++ libraries/entities/src/WebEntityItem.h | 1 + scripts/system/libraries/WebBuddy.js | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 79af620661..b78cff3b25 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -26,7 +26,6 @@ #include "EntityTreeRenderer.h" -const float DPI = 30.47f; const float METERS_TO_INCHES = 39.3701f; static uint32_t _currentWebCount { 0 }; // Don't allow more than 100 concurrent web views @@ -87,7 +86,7 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { QTouchEvent::TouchPoint point; point.setId(event.getID()); point.setState(Qt::TouchPointReleased); - glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * DPI); + glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); QPointF windowPoint(windowPos.x, windowPos.y); point.setPos(windowPoint); QList touchPoints; @@ -125,7 +124,8 @@ void RenderableWebEntityItem::render(RenderArgs* args) { _lastRenderTime = usecTimestampNow(); glm::vec2 dims = glm::vec2(getDimensions()); - dims *= METERS_TO_INCHES * DPI; + + dims *= METERS_TO_INCHES * _dpi; // The offscreen surface is idempotent for resizes (bails early // if it's a no-op), so it's safe to just call resize every frame // without worrying about excessive overhead. @@ -185,7 +185,7 @@ void RenderableWebEntityItem::handlePointerEvent(const PointerEvent& event) { return; } - glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * DPI); + glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); QPointF windowPoint(windowPos.x, windowPos.y); if (event.getType() == PointerEvent::Move) { diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index b31e47608f..c97eedc56a 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -20,8 +21,26 @@ #include "EntityTree.h" #include "EntityTreeElement.h" +const float DEFAULT_DPI = 30.47f; + const QString WebEntityItem::DEFAULT_SOURCE_URL("http://www.google.com"); +static float parseDPIFromUserData(QString str) { + QJsonParseError error; + auto doc = QJsonDocument::fromJson(str.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) { + return DEFAULT_DPI; + } + QJsonObject obj = doc.object(); + + QJsonValue dpiValue = obj.value("dpi"); + if (!dpiValue.isDouble()) { + return DEFAULT_DPI; + } + double dpi = dpiValue.toDouble(); + return (float)dpi; +} + EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity { new WebEntityItem(entityID) }; entity->setProperties(properties); @@ -30,6 +49,7 @@ EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const Ent WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { _type = EntityTypes::Web; + _dpi = DEFAULT_DPI; } const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f; @@ -62,6 +82,9 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) { setLastEdited(properties._lastEdited); } + // AJT: TODO MAKE THIS A REAL PROPERTY + _dpi = parseDPIFromUserData(getUserData()); + return somethingChanged; } diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index 7c4e9f801a..2179f34e36 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -56,6 +56,7 @@ public: protected: QString _sourceUrl; + float _dpi; }; #endif // hifi_WebEntityItem_h diff --git a/scripts/system/libraries/WebBuddy.js b/scripts/system/libraries/WebBuddy.js index d584bcc564..33b6c74c93 100644 --- a/scripts/system/libraries/WebBuddy.js +++ b/scripts/system/libraries/WebBuddy.js @@ -33,7 +33,8 @@ WebBuddy = function (url) { gravity: {x: 0, y: 0, z: 0}, shapeType: "box", userData: JSON.stringify({ - "grabbableKey": {"grabbable": true} + "grabbableKey": {"grabbable": true}, + "dpi": 75 }), parentID: MyAvatar.sessionUUID, parentJointIndex: NEGATIVE_ONE From 31ed378dbf02b267ef21e4d78a42a395380d768e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 10 Aug 2016 15:08:44 -0700 Subject: [PATCH 230/332] Added dpi entity property for web-entities --- .../entities/src/EntityItemProperties.cpp | 14 ++++++++ libraries/entities/src/EntityItemProperties.h | 2 ++ .../src/EntityItemPropertiesDefaults.h | 2 ++ libraries/entities/src/EntityPropertyFlags.h | 1 + libraries/entities/src/WebEntityItem.cpp | 36 ++++++++----------- libraries/entities/src/WebEntityItem.h | 7 +++- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + scripts/system/libraries/WebBuddy.js | 4 +-- 9 files changed, 43 insertions(+), 26 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index b3d810c0eb..06c91b4f32 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -335,6 +335,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_OWNING_AVATAR_ID, owningAvatarID); CHECK_PROPERTY_CHANGE(PROP_SHAPE, shape); + CHECK_PROPERTY_CHANGE(PROP_DPI, dpi); changedProperties += _animation.getChangedProperties(); changedProperties += _keyLight.getChangedProperties(); @@ -504,6 +505,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Web only if (_type == EntityTypes::Web) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DPI, dpi); } // PolyVoxel only @@ -726,6 +728,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(clientOnly, bool, setClientOnly); COPY_PROPERTY_FROM_QSCRIPTVALUE(owningAvatarID, QUuid, setOwningAvatarID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI); + _lastEdited = usecTimestampNow(); } @@ -903,6 +907,8 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool); ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool); + ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t); + // FIXME - these are not yet handled //ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); @@ -1065,6 +1071,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem if (properties.getType() == EntityTypes::Web) { APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl()); + APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI()); } if (properties.getType() == EntityTypes::Text) { @@ -1364,6 +1371,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int if (properties.getType() == EntityTypes::Web) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI); } if (properties.getType() == EntityTypes::Text) { @@ -1642,6 +1650,8 @@ void EntityItemProperties::markAllChanged() { _clientOnlyChanged = true; _owningAvatarIDChanged = true; + + _dpiChanged = true; } // The minimum bounding box for the entity. @@ -1977,6 +1987,10 @@ QList EntityItemProperties::listChangedProperties() { out += "ghostingAllowed"; } + if (dpiChanged()) { + out += "dpi"; + } + if (shapeChanged()) { out += "shape"; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 22d740e0dd..4591dabc51 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -215,6 +215,8 @@ public: DEFINE_PROPERTY(PROP_CLIENT_ONLY, ClientOnly, clientOnly, bool, false); DEFINE_PROPERTY_REF(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID); + DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI); + static QString getBackgroundModeString(BackgroundMode mode); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index 4ec0bea7df..3ab827a222 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -73,4 +73,6 @@ const bool ENTITY_ITEM_DEFAULT_BILLBOARDED = false; const QString ENTITY_ITEM_DEFAULT_NAME = QString(""); +const uint16_t ENTITY_ITEM_DEFAULT_DPI = 30; + #endif // hifi_EntityItemPropertiesDefaults_h diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 0baef0fa25..e09db1e867 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -176,6 +176,7 @@ enum EntityPropertyList { PROP_OWNING_AVATAR_ID, // doesn't go over wire PROP_SHAPE, + PROP_DPI, PROP_LOCAL_VELOCITY, // only used to convert values to and from scripts PROP_LOCAL_ANGULAR_VELOCITY, // only used to convert values to and from scripts diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index c97eedc56a..1a8bf074d2 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -21,26 +21,8 @@ #include "EntityTree.h" #include "EntityTreeElement.h" -const float DEFAULT_DPI = 30.47f; - const QString WebEntityItem::DEFAULT_SOURCE_URL("http://www.google.com"); -static float parseDPIFromUserData(QString str) { - QJsonParseError error; - auto doc = QJsonDocument::fromJson(str.toUtf8(), &error); - if (error.error != QJsonParseError::NoError) { - return DEFAULT_DPI; - } - QJsonObject obj = doc.object(); - - QJsonValue dpiValue = obj.value("dpi"); - if (!dpiValue.isDouble()) { - return DEFAULT_DPI; - } - double dpi = dpiValue.toDouble(); - return (float)dpi; -} - EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity { new WebEntityItem(entityID) }; entity->setProperties(properties); @@ -49,7 +31,7 @@ EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const Ent WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { _type = EntityTypes::Web; - _dpi = DEFAULT_DPI; + _dpi = ENTITY_ITEM_DEFAULT_DPI; } const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f; @@ -62,6 +44,7 @@ void WebEntityItem::setDimensions(const glm::vec3& value) { EntityItemProperties WebEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(sourceUrl, getSourceUrl); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(dpi, getDPI); return properties; } @@ -70,6 +53,7 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) { somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class SET_ENTITY_PROPERTY_FROM_PROPERTIES(sourceUrl, setSourceUrl); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(dpi, setDPI); if (somethingChanged) { bool wantDebug = false; @@ -82,9 +66,6 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) { setLastEdited(properties._lastEdited); } - // AJT: TODO MAKE THIS A REAL PROPERTY - _dpi = parseDPIFromUserData(getUserData()); - return somethingChanged; } @@ -97,6 +78,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_SOURCE_URL, QString, setSourceUrl); + READ_ENTITY_PROPERTY(PROP_DPI, uint16_t, setDPI); return bytesRead; } @@ -106,6 +88,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); requestedProperties += PROP_SOURCE_URL; + requestedProperties += PROP_DPI; return requestedProperties; } @@ -119,6 +102,7 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, _sourceUrl); + APPEND_ENTITY_PROPERTY(PROP_DPI, _dpi); } bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -146,3 +130,11 @@ void WebEntityItem::setSourceUrl(const QString& value) { } const QString& WebEntityItem::getSourceUrl() const { return _sourceUrl; } + +void WebEntityItem::setDPI(uint16_t value) { + _dpi = value; +} + +uint16_t WebEntityItem::getDPI() const { + return _dpi; +} diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index 2179f34e36..5a8097a9f1 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -54,9 +54,14 @@ public: virtual void setSourceUrl(const QString& value); const QString& getSourceUrl() const; + virtual bool wantsHandControllerPointerEvents() const override { return true; } + + void setDPI(uint16_t value); + uint16_t getDPI() const; + protected: QString _sourceUrl; - float _dpi; + uint16_t _dpi; }; #endif // hifi_WebEntityItem_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index e9d61a827a..217c2ca3fb 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -47,7 +47,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: - return VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS; + return VERSION_WEB_ENTITIES_SUPPORT_DPI; case PacketType::AvatarIdentity: case PacketType::AvatarData: case PacketType::BulkAvatarData: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 40524e2288..54fdfb84c7 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -186,6 +186,7 @@ const PacketVersion VERSION_ENTITIES_MORE_SHAPES = 59; const PacketVersion VERSION_ENTITIES_PROPERLY_ENCODE_SHAPE_EDITS = 60; const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_STATIC_MESH = 61; const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS = 62; +const PacketVersion VERSION_WEB_ENTITIES_SUPPORT_DPI = 63; enum class AvatarMixerPacketVersion : PacketVersion { TranslationSupport = 17, diff --git a/scripts/system/libraries/WebBuddy.js b/scripts/system/libraries/WebBuddy.js index 33b6c74c93..3525a3d726 100644 --- a/scripts/system/libraries/WebBuddy.js +++ b/scripts/system/libraries/WebBuddy.js @@ -34,10 +34,10 @@ WebBuddy = function (url) { shapeType: "box", userData: JSON.stringify({ "grabbableKey": {"grabbable": true}, - "dpi": 75 }), parentID: MyAvatar.sessionUUID, - parentJointIndex: NEGATIVE_ONE + parentJointIndex: NEGATIVE_ONE, + dpi: 45 }); this.state = "idle"; From d607abc8f7c51484fcc0f4e8a1092a8849763621 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 10 Aug 2016 16:25:20 -0700 Subject: [PATCH 231/332] Add dpi to edit.js Also, cap the maximum webEntity texture size to 4096 --- .../src/RenderableWebEntityItem.cpp | 21 ++++++++++++++++--- .../src/RenderableWebEntityItem.h | 1 + scripts/system/html/entityProperties.html | 4 ++++ scripts/system/html/js/entityProperties.js | 3 +++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index b78cff3b25..bb3f508d9b 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -33,6 +33,8 @@ static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 100; // If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND; +static int MAX_WINDOW_SIZE = 4096; + EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity{ new RenderableWebEntityItem(entityID) }; entity->setProperties(properties); @@ -98,6 +100,19 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { return true; } +glm::vec2 RenderableWebEntityItem::getWindowSize() const { + glm::vec2 dims = glm::vec2(getDimensions()); + dims *= METERS_TO_INCHES * _dpi; + + // ensure no side is never larger then MAX_WINDOW_SIZE + float max = (dims.x > dims.y) ? dims.x : dims.y; + if (max > MAX_WINDOW_SIZE) { + dims *= MAX_WINDOW_SIZE / max; + } + + return dims; +} + void RenderableWebEntityItem::render(RenderArgs* args) { checkFading(); @@ -123,13 +138,13 @@ void RenderableWebEntityItem::render(RenderArgs* args) { } _lastRenderTime = usecTimestampNow(); - glm::vec2 dims = glm::vec2(getDimensions()); - dims *= METERS_TO_INCHES * _dpi; + glm::vec2 windowSize = getWindowSize(); + // The offscreen surface is idempotent for resizes (bails early // if it's a no-op), so it's safe to just call resize every frame // without worrying about excessive overhead. - _webSurface->resize(QSize(dims.x, dims.y)); + _webSurface->resize(QSize(windowSize.x, windowSize.y)); PerformanceTimer perfTimer("RenderableWebEntityItem::render"); Q_ASSERT(getType() == EntityTypes::Web); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index ee8a484109..7bfd40864b 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -47,6 +47,7 @@ public: private: bool buildWebSurface(EntityTreeRenderer* renderer); void destroyWebSurface(); + glm::vec2 getWindowSize() const; OffscreenQmlSurface* _webSurface{ nullptr }; QMetaObject::Connection _connection; diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 01826b8e10..55af62eb54 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -543,6 +543,10 @@
+
+ + +
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 5898f85e90..961758e16d 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -427,6 +427,7 @@ function loaded() { var elWebSections = document.querySelectorAll(".web-section"); allSections.push(elWebSections); var elWebSourceURL = document.getElementById("property-web-source-url"); + var elWebDPI = document.getElementById("property-web-dpi"); var elDescription = document.getElementById("property-description"); @@ -706,6 +707,7 @@ function loaded() { } elWebSourceURL.value = properties.sourceUrl; + elWebDPI.value = properties.dpi; } else if (properties.type == "Text") { for (var i = 0; i < elTextSections.length; i++) { elTextSections[i].style.display = 'table'; @@ -959,6 +961,7 @@ function loaded() { elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape')); elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl')); + elWebDPI.addEventListener('change', createEmitNumberPropertyUpdateFunction('dpi')); elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL')); elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType')); From eafae1fcf373df514fdee375f8ebb6ed22d76384 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 17 Aug 2016 13:26:35 -0700 Subject: [PATCH 232/332] Added tablet shell around web-buddy --- scripts/system/libraries/WebBuddy.js | 84 +++++++++++++++++++++------- 1 file changed, 65 insertions(+), 19 deletions(-) diff --git a/scripts/system/libraries/WebBuddy.js b/scripts/system/libraries/WebBuddy.js index 3525a3d726..cb6f29d943 100644 --- a/scripts/system/libraries/WebBuddy.js +++ b/scripts/system/libraries/WebBuddy.js @@ -7,44 +7,89 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + var NEGATIVE_ONE = 65535; +var RAD_TO_DEG = 180 / Math.PI; +var X_AXIS = {x: 1, y: 0, z: 0}; +var Y_AXIS = {x: 0, y: 1, z: 0}; + +var TABLET_URL = "https://s3.amazonaws.com/hifi-public/tony/tablet.fbx"; + +// returns object with two fields: +// * position - position in front of the user +// * rotation - rotation of entity so it faces the user. +function calcSpawnInfo() { + var front; + var pitchBackRotation = Quat.angleAxis(20.0, X_AXIS); + if (HMD.active) { + front = Quat.getFront(HMD.orientation); + var yawOnlyRotation = Quat.angleAxis(Math.atan2(front.x, front.z) * RAD_TO_DEG, Y_AXIS); + return { + position: Vec3.sum(Vec3.sum(HMD.position, Vec3.multiply(0.6, front)), Vec3.multiply(-0.5, Y_AXIS)), + rotation: Quat.multiply(yawOnlyRotation, pitchBackRotation) + }; + } else { + front = Quat.getFront(MyAvatar.orientation); + return { + position: Vec3.sum(Vec3.sum(MyAvatar.position, Vec3.multiply(0.6, front)), {x: 0, y: 0.6, z: 0}), + rotation: Quat.multiply(MyAvatar.orientation, pitchBackRotation) + }; + } +} + // ctor WebBuddy = function (url) { var ASPECT = 4.0 / 3.0; var WIDTH = 0.4; + var HEIGHT = WIDTH * ASPECT; + var DEPTH = 0.025; - var spawnPoint = Vec3.sum(Vec3.sum(MyAvatar.position, Vec3.multiply(1.0, Quat.getFront(MyAvatar.orientation))), - {x: 0, y: 0.5, z: 0}); + var spawnInfo = calcSpawnInfo(); + + var tabletEntityPosition = spawnInfo.position; + var tabletEntityRotation = spawnInfo.rotation; + this.tabletEntityID = Entities.addEntity({ + name: "tablet", + type: "Model", + modelURL: TABLET_URL, + position: tabletEntityPosition, + rotation: tabletEntityRotation, + userData: JSON.stringify({ + "grabbableKey": {"grabbable": true} + }), + dimensions: {x: WIDTH, y: HEIGHT, z: DEPTH}, + parentID: MyAvatar.sessionUUID, + parentJointIndex: NEGATIVE_ONE + }); + + var WEB_ENTITY_REDUCTION_FACTOR = {x: 0.78, y: 0.85}; + var WEB_ENTITY_Z_OFFSET = -0.01; + + var webEntityRotation = Quat.multiply(spawnInfo.rotation, Quat.angleAxis(180, Y_AXIS)); + var webEntityPosition = Vec3.sum(spawnInfo.position, Vec3.multiply(WEB_ENTITY_Z_OFFSET, Quat.getFront(webEntityRotation))); - var webEntityPosition = spawnPoint; - var webEntityRotation = MyAvatar.orientation; this.webEntityID = Entities.addEntity({ + name: "web", type: "Web", sourceUrl: url, - dimensions: {x: WIDTH, y: WIDTH * ASPECT, z: 0.1}, + dimensions: {x: WIDTH * WEB_ENTITY_REDUCTION_FACTOR.x, + y: HEIGHT * WEB_ENTITY_REDUCTION_FACTOR.y, + z: 0.1}, position: webEntityPosition, rotation: webEntityRotation, - name: "web", - dynamic: true, - angularDamping: 0.9, - damping: 0.9, - gravity: {x: 0, y: 0, z: 0}, shapeType: "box", - userData: JSON.stringify({ - "grabbableKey": {"grabbable": true}, - }), - parentID: MyAvatar.sessionUUID, - parentJointIndex: NEGATIVE_ONE, - dpi: 45 + dpi: 45, + parentID: this.tabletEntityID, + parentJointIndex: NEGATIVE_ONE }); this.state = "idle"; // compute the room/sensor matrix of the entity. var invRoomMat = Mat4.inverse(MyAvatar.sensorToWorldMatrix); - var entityWorldMat = Mat4.createFromRotAndTrans(webEntityRotation, webEntityPosition); + var entityWorldMat = Mat4.createFromRotAndTrans(tabletEntityRotation, tabletEntityPosition); this.entityRoomMat = Mat4.multiply(invRoomMat, entityWorldMat); var _this = this; @@ -56,12 +101,13 @@ WebBuddy = function (url) { WebBuddy.prototype.destroy = function () { Entities.deleteEntity(this.webEntityID); + Entities.deleteEntity(this.tabletEntityID); Script.update.disconnect(this.updateFunc); }; WebBuddy.prototype.update = function (dt) { - var props = Entities.getEntityProperties(this.webEntityID, ["position", "rotation", "parentID", "parentJointIndex"]); + var props = Entities.getEntityProperties(this.tabletEntityID, ["position", "rotation", "parentID", "parentJointIndex"]); var entityWorldMat; if (this.state === "idle") { @@ -76,7 +122,7 @@ WebBuddy.prototype.update = function (dt) { entityWorldMat = Mat4.multiply(roomMat, this.entityRoomMat); // slam the world space position and orientation - Entities.editEntity(this.webEntityID, { + Entities.editEntity(this.tabletEntityID, { position: Mat4.extractTranslation(entityWorldMat), rotation: Mat4.extractRotation(entityWorldMat) }); From af2c31f29b925c979ed70787270f61d1ca9b8a14 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 17 Aug 2016 13:33:16 -0700 Subject: [PATCH 233/332] Renamed WebBuddy to WebTablet --- .../libraries/{WebBuddy.js => WebTablet.js} | 8 ++++---- scripts/system/marketplace.js | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) rename scripts/system/libraries/{WebBuddy.js => WebTablet.js} (97%) diff --git a/scripts/system/libraries/WebBuddy.js b/scripts/system/libraries/WebTablet.js similarity index 97% rename from scripts/system/libraries/WebBuddy.js rename to scripts/system/libraries/WebTablet.js index cb6f29d943..6a0a3fcbb1 100644 --- a/scripts/system/libraries/WebBuddy.js +++ b/scripts/system/libraries/WebTablet.js @@ -1,5 +1,5 @@ // -// WebBuddy.js +// WebTablet.js // // Created by Anthony J. Thibault on 8/8/2016 // Copyright 2016 High Fidelity, Inc. @@ -39,7 +39,7 @@ function calcSpawnInfo() { } // ctor -WebBuddy = function (url) { +WebTablet = function (url) { var ASPECT = 4.0 / 3.0; var WIDTH = 0.4; @@ -99,13 +99,13 @@ WebBuddy = function (url) { Script.update.connect(this.updateFunc); }; -WebBuddy.prototype.destroy = function () { +WebTablet.prototype.destroy = function () { Entities.deleteEntity(this.webEntityID); Entities.deleteEntity(this.tabletEntityID); Script.update.disconnect(this.updateFunc); }; -WebBuddy.prototype.update = function (dt) { +WebTablet.prototype.update = function (dt) { var props = Entities.getEntityProperties(this.tabletEntityID, ["position", "rotation", "parentID", "parentJointIndex"]); var entityWorldMat; diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js index 26d46d6219..af0be21077 100644 --- a/scripts/system/marketplace.js +++ b/scripts/system/marketplace.js @@ -8,8 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global WebBuddy */ -Script.include("./libraries/WebBuddy.js"); +/* global WebTablet */ +Script.include("./libraries/WebTablet.js"); var toolIconUrl = Script.resolvePath("assets/images/tools/"); @@ -26,17 +26,17 @@ var toolHeight = 50; var toolWidth = 50; var TOOLBAR_MARGIN_Y = 0; var marketplaceVisible = false; -var marketplaceWebBuddy; +var marketplaceWebTablet; -function shouldShowWebBuddyMarketplace() { +function shouldShowWebTablet() { var rightPose = Controller.getPoseValue(Controller.Standard.RightHand); var leftPose = Controller.getPoseValue(Controller.Standard.LeftHand); return HMD.active && (leftPose.valid || rightPose.valid); } function showMarketplace(marketplaceID) { - if (shouldShowWebBuddyMarketplace()) { - marketplaceWebBuddy = new WebBuddy("https://metaverse.highfidelity.com/marketplace"); + if (shouldShowWebTablet()) { + marketplaceWebTablet = new WebTablet("https://metaverse.highfidelity.com/marketplace"); } else { var url = MARKETPLACE_URL; if (marketplaceID) { @@ -55,8 +55,8 @@ function hideMarketplace() { marketplaceWindow.setVisible(false); marketplaceWindow.setURL("about:blank"); } else { - marketplaceWebBuddy.destroy(); - marketplaceWebBuddy = null; + marketplaceWebTablet.destroy(); + marketplaceWebTablet = null; } marketplaceVisible = false; } From df17913801b8ae2edc76f6b50445bbed68f7af2e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 17 Aug 2016 18:18:13 -0700 Subject: [PATCH 234/332] Mipmap generation, trilinear,anisotropic filtering for OffscreenQmlSurface --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 4 ++- libraries/gl/src/gl/OglplusHelpers.cpp | 32 ++++++++++++++------- libraries/gl/src/gl/OglplusHelpers.h | 2 ++ 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 366bc96ca3..c47312f9f6 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -166,7 +166,7 @@ private: QMyQuickRenderControl* _renderControl{ nullptr }; FramebufferPtr _fbo; RenderbufferPtr _depthStencil; - TextureRecycler _textures; + TextureRecycler _textures { true }; GLTextureEscrow _escrow; uint64_t _lastRenderTime{ 0 }; @@ -399,6 +399,8 @@ void OffscreenQmlRenderThread::render() { glGetError(); } + Context::Bound(oglplus::Texture::Target::_2D, *texture).GenerateMipmap(); + // FIXME probably unecessary DefaultFramebuffer().Bind(Framebuffer::Target::Draw); _quickWindow->resetOpenGLState(); diff --git a/libraries/gl/src/gl/OglplusHelpers.cpp b/libraries/gl/src/gl/OglplusHelpers.cpp index 1154042b4a..d31800dc4c 100644 --- a/libraries/gl/src/gl/OglplusHelpers.cpp +++ b/libraries/gl/src/gl/OglplusHelpers.cpp @@ -509,16 +509,28 @@ TexturePtr TextureRecycler::getNextTexture() { using namespace oglplus; if (_readyTextures.empty()) { TexturePtr newTexture(new Texture()); - Context::Bound(oglplus::Texture::Target::_2D, *newTexture) - .MinFilter(TextureMinFilter::Linear) - .MagFilter(TextureMagFilter::Linear) - .WrapS(TextureWrap::ClampToEdge) - .WrapT(TextureWrap::ClampToEdge) - .Image2D( - 0, PixelDataInternalFormat::RGBA8, - _size.x, _size.y, - 0, PixelDataFormat::RGB, PixelDataType::UnsignedByte, nullptr - ); + + if (_useMipmaps) { + Context::Bound(oglplus::Texture::Target::_2D, *newTexture) + .MinFilter(TextureMinFilter::LinearMipmapLinear) + .MagFilter(TextureMagFilter::Linear) + .WrapS(TextureWrap::ClampToEdge) + .WrapT(TextureWrap::ClampToEdge) + .Anisotropy(8.0f) + .LODBias(-0.2f) + .Image2D(0, PixelDataInternalFormat::RGBA8, + _size.x, _size.y, + 0, PixelDataFormat::RGB, PixelDataType::UnsignedByte, nullptr); + } else { + Context::Bound(oglplus::Texture::Target::_2D, *newTexture) + .MinFilter(TextureMinFilter::Linear) + .MagFilter(TextureMagFilter::Linear) + .WrapS(TextureWrap::ClampToEdge) + .WrapT(TextureWrap::ClampToEdge) + .Image2D(0, PixelDataInternalFormat::RGBA8, + _size.x, _size.y, + 0, PixelDataFormat::RGB, PixelDataType::UnsignedByte, nullptr); + } GLuint texId = GetName(*newTexture); _allTextures[texId] = TexInfo{ newTexture, _size }; _readyTextures.push(newTexture); diff --git a/libraries/gl/src/gl/OglplusHelpers.h b/libraries/gl/src/gl/OglplusHelpers.h index fe5822c4be..ab47689312 100644 --- a/libraries/gl/src/gl/OglplusHelpers.h +++ b/libraries/gl/src/gl/OglplusHelpers.h @@ -190,6 +190,7 @@ using BasicFramebufferWrapperPtr = std::shared_ptr; class TextureRecycler { public: + TextureRecycler(bool useMipmaps) : _useMipmaps(useMipmaps) {} void setSize(const uvec2& size); void clear(); TexturePtr getNextTexture(); @@ -212,4 +213,5 @@ private: Map _allTextures; Queue _readyTextures; uvec2 _size{ 1920, 1080 }; + bool _useMipmaps; }; From e7dd9c44780b5c32a06f292617edc20758e3d1f9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 17 Aug 2016 18:19:10 -0700 Subject: [PATCH 235/332] handheld marketplace should work for hydras --- scripts/system/marketplace.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js index af0be21077..cb82ab9060 100644 --- a/scripts/system/marketplace.js +++ b/scripts/system/marketplace.js @@ -31,7 +31,8 @@ var marketplaceWebTablet; function shouldShowWebTablet() { var rightPose = Controller.getPoseValue(Controller.Standard.RightHand); var leftPose = Controller.getPoseValue(Controller.Standard.LeftHand); - return HMD.active && (leftPose.valid || rightPose.valid); + var hasHydra = !!Controller.Hardware.Hydra; + return HMD.active && (leftPose.valid || rightPose.valid || hasHydra); } function showMarketplace(marketplaceID) { From c6ea64926c6770834f2879f4e5c44fcfc93a32ef Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 18 Aug 2016 17:04:56 -0700 Subject: [PATCH 236/332] Sensor space entity support You can do this by parenting an entity to an avatar's -2 joint index. This will mean that the entity will follow the avatar as it moves in the world, but will not follow the avatar's position as it moves in sensor space. Essentially, this gives you the ability to place objects in the user's physical room. WebTablets now are located in this feature and no longer jitter. --- interface/src/avatar/Avatar.cpp | 32 ++++++++--- interface/src/avatar/MyAvatar.cpp | 11 ++-- interface/src/avatar/MyAvatar.h | 13 ++--- libraries/avatars/src/AvatarData.cpp | 44 +++++++++++---- libraries/avatars/src/AvatarData.h | 9 ++++ .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- scripts/system/libraries/WebTablet.js | 53 +------------------ 8 files changed, 81 insertions(+), 86 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index d3a38271a0..31fb2abe53 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -59,6 +59,8 @@ const float DISPLAYNAME_ALPHA = 1.0f; const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); +const int SENSOR_TO_WORLD_MATRIX_INDEX = 65534; + namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { return ItemKey::Builder::opaqueShape(); @@ -851,15 +853,33 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const { } glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { - glm::quat rotation; - _skeletonModel->getAbsoluteJointRotationInRigFrame(index, rotation); - return Quaternions::Y_180 * rotation; + if (index == SENSOR_TO_WORLD_MATRIX_INDEX) { + glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); + bool success; + Transform avatarTransform; + Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform()); + glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix(); + return glmExtractRotation(invAvatarMat * sensorToWorldMatrix); + } else { + glm::quat rotation; + _skeletonModel->getAbsoluteJointRotationInRigFrame(index, rotation); + return Quaternions::Y_180 * rotation; + } } glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const { - glm::vec3 translation; - _skeletonModel->getAbsoluteJointTranslationInRigFrame(index, translation); - return Quaternions::Y_180 * translation; + if (index == SENSOR_TO_WORLD_MATRIX_INDEX) { + glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); + bool success; + Transform avatarTransform; + Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform()); + glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix(); + return extractTranslation(invAvatarMat * sensorToWorldMatrix); + } else { + glm::vec3 translation; + _skeletonModel->getAbsoluteJointTranslationInRigFrame(index, translation); + return Quaternions::Y_180 * translation; + } } int Avatar::getJointIndex(const QString& name) const { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 782ecbcc64..5687b5025c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -107,7 +107,6 @@ MyAvatar::MyAvatar(RigPointer rig) : _hmdSensorOrientation(), _hmdSensorPosition(), _bodySensorMatrix(), - _sensorToWorldMatrix(), _goToPending(false), _goToPosition(), _goToOrientation(), @@ -511,13 +510,9 @@ void MyAvatar::simulate(float deltaTime) { updateAvatarEntities(); } -// thread-safe -glm::mat4 MyAvatar::getSensorToWorldMatrix() const { - return _sensorToWorldMatrixCache.get(); -} - -// As far as I know no HMD system supports a play area of a kilometer in radius. +// As far as I know no HMD system supports a play area of a kilometer in radius. static const float MAX_HMD_ORIGIN_DISTANCE = 1000.0f; + // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD // as it moves through the world. @@ -526,7 +521,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { _hmdSensorMatrix = hmdSensorMatrix; auto newHmdSensorPosition = extractTranslation(hmdSensorMatrix); - if (newHmdSensorPosition != _hmdSensorPosition && + if (newHmdSensorPosition != _hmdSensorPosition && glm::length(newHmdSensorPosition) > MAX_HMD_ORIGIN_DISTANCE) { qWarning() << "Invalid HMD sensor position " << newHmdSensorPosition; // Ignore unreasonable HMD sensor data diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d8ff679db6..1f212a1fec 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -79,8 +79,6 @@ class MyAvatar : public Avatar { Q_PROPERTY(controller::Pose leftHandTipPose READ getLeftHandTipPose) Q_PROPERTY(controller::Pose rightHandTipPose READ getRightHandTipPose) - Q_PROPERTY(glm::mat4 sensorToWorldMatrix READ getSensorToWorldMatrix) - Q_PROPERTY(float energy READ getEnergy WRITE setEnergy) Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled) @@ -110,9 +108,6 @@ public: const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; } const glm::vec2& getHMDSensorFacingMovingAverage() const { return _hmdSensorFacingMovingAverage; } - // thread safe - Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const; - Q_INVOKABLE void setOrientationVar(const QVariant& newOrientationVar); Q_INVOKABLE QVariant getOrientationVar() const; @@ -415,6 +410,10 @@ private: bool _useSnapTurn { true }; bool _clearOverlayWhenMoving { true }; + // working copy of sensorToWorldMatrix. + // See AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access + glm::mat4 _sensorToWorldMatrix; + // cache of the current HMD sensor position and orientation // in sensor space. glm::mat4 _hmdSensorMatrix; @@ -427,10 +426,6 @@ private: // in sensor space. glm::mat4 _bodySensorMatrix; - // used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers. - glm::mat4 _sensorToWorldMatrix; - ThreadSafeValueCache _sensorToWorldMatrixCache { glm::mat4() }; - struct FollowHelper { FollowHelper(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d0c7b3912c..e90f063cff 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -53,15 +53,18 @@ namespace AvatarDataPacket { // NOTE: AvatarDataPackets start with a uint16_t sequence number that is not reflected in the Header structure. PACKED_BEGIN struct Header { - float position[3]; // skeletal model's position - float globalPosition[3]; // avatar's position - uint16_t localOrientation[3]; // avatar's local euler angles (degrees, compressed) relative to the thing it's attached to - uint16_t scale; // (compressed) 'ratio' encoding uses sign bit as flag. - float lookAtPosition[3]; // world space position that eyes are focusing on. - float audioLoudness; // current loundess of microphone + float position[3]; // skeletal model's position + float globalPosition[3]; // avatar's position + uint16_t localOrientation[3]; // avatar's local euler angles (degrees, compressed) relative to the thing it's attached to + uint16_t scale; // (compressed) 'ratio' encoding uses sign bit as flag. + float lookAtPosition[3]; // world space position that eyes are focusing on. + float audioLoudness; // current loundess of microphone + uint8_t sensorToWorldQuat[6]; // 6 byte compressed quaternion part of sensor to world matrix + uint16_t sensorToWorldScale; // uniform scale of sensor to world matrix + float sensorToWorldTrans[3]; // fourth column of sensor to world matrix uint8_t flags; } PACKED_END; - const size_t HEADER_SIZE = 49; + const size_t HEADER_SIZE = 69; // only present if HAS_REFERENTIAL flag is set in header.flags PACKED_BEGIN struct ParentInfo { @@ -93,6 +96,9 @@ namespace AvatarDataPacket { */ } +static const int TRANSLATION_COMPRESSION_RADIX = 12; +static const int SENSOR_TO_WORLD_SCALE_RADIX = 10; + #define ASSERT(COND) do { if (!(COND)) { abort(); } } while(0) AvatarData::AvatarData() : @@ -210,6 +216,14 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { header->lookAtPosition[2] = _headData->_lookAtPosition.z; header->audioLoudness = _headData->_audioLoudness; + glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); + packOrientationQuatToSixBytes(header->sensorToWorldQuat, glmExtractRotation(sensorToWorldMatrix)); + glm::vec3 scale = extractScale(sensorToWorldMatrix); + packFloatScalarToSignedTwoByteFixed((uint8_t*)&header->sensorToWorldScale, scale.x, SENSOR_TO_WORLD_SCALE_RADIX); + header->sensorToWorldTrans[0] = sensorToWorldMatrix[0][3]; + header->sensorToWorldTrans[1] = sensorToWorldMatrix[1][3]; + header->sensorToWorldTrans[2] = sensorToWorldMatrix[2][3]; + setSemiNibbleAt(header->flags, KEY_STATE_START_BIT, _keyState); // hand state bool isFingerPointing = _handState & IS_FINGER_POINTING_FLAG; @@ -346,8 +360,6 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { *destinationBuffer++ = validity; } - const int TRANSLATION_COMPRESSION_RADIX = 12; - validityBit = 0; validity = *validityPosition++; for (int i = 0; i < _jointData.size(); i ++) { @@ -500,6 +512,14 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } _headData->_audioLoudness = audioLoudness; + glm::quat sensorToWorldQuat; + unpackOrientationQuatFromSixBytes(header->sensorToWorldQuat, sensorToWorldQuat); + float sensorToWorldScale; + unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&header->sensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX); + glm::vec3 sensorToWorldTrans(header->sensorToWorldTrans[0], header->sensorToWorldTrans[1], header->sensorToWorldTrans[2]); + glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans); + _sensorToWorldMatrixCache.set(sensorToWorldMatrix); + { // bitFlags and face data uint8_t bitItems = header->flags; @@ -616,7 +636,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { // each joint translation component is stored in 6 bytes. const int COMPRESSED_TRANSLATION_SIZE = 6; PACKET_READ_CHECK(JointTranslation, numValidJointTranslations * COMPRESSED_TRANSLATION_SIZE); - const int TRANSLATION_COMPRESSION_RADIX = 12; for (int i = 0; i < numJoints; i++) { JointData& data = _jointData[i]; @@ -1718,6 +1737,11 @@ AvatarEntityIDs AvatarData::getAndClearRecentlyDetachedIDs() { return result; } +// thread-safe +glm::mat4 AvatarData::getSensorToWorldMatrix() const { + return _sensorToWorldMatrixCache.get(); +} + QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& value) { QScriptValue obj = engine->newObject(); obj.setProperty("intersects", value.intersects); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b88cec3e05..572657e921 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -54,6 +54,7 @@ typedef unsigned long long quint64; #include #include #include +#include #include "AABox.h" #include "HeadData.h" @@ -171,6 +172,8 @@ class AvatarData : public QObject, public SpatiallyNestable { Q_PROPERTY(QUuid sessionUUID READ getSessionUUID) + Q_PROPERTY(glm::mat4 sensorToWorldMatrix READ getSensorToWorldMatrix) + public: static const QString FRAME_NAME; @@ -351,6 +354,9 @@ public: void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; } AvatarEntityIDs getAndClearRecentlyDetachedIDs(); + // thread safe + Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const; + public slots: void sendAvatarDataPacket(); void sendIdentityPacket(); @@ -425,6 +431,9 @@ protected: bool _avatarEntityDataLocallyEdited { false }; bool _avatarEntityDataChanged { false }; + // used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers. + ThreadSafeValueCache _sensorToWorldMatrixCache { glm::mat4() }; + private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); static QUrl _defaultFullAvatarModelUrl; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 217c2ca3fb..0d25d4f1be 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -52,7 +52,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::AbsoluteSixByteRotations); + return static_cast(AvatarMixerPacketVersion::SensorToWorldMat); case PacketType::ICEServerHeartbeat: return 18; // ICE Server Heartbeat signing case PacketType::AssetGetInfo: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 54fdfb84c7..3ecdb75a18 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -192,7 +192,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { TranslationSupport = 17, SoftAttachmentSupport, AvatarEntities, - AbsoluteSixByteRotations + AbsoluteSixByteRotations, + SensorToWorldMat }; enum class DomainConnectRequestVersion : PacketVersion { diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 6a0a3fcbb1..34368e475b 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -8,8 +8,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var NEGATIVE_ONE = 65535; - var RAD_TO_DEG = 180 / Math.PI; var X_AXIS = {x: 1, y: 0, z: 0}; var Y_AXIS = {x: 0, y: 1, z: 0}; @@ -61,7 +59,7 @@ WebTablet = function (url) { }), dimensions: {x: WIDTH, y: HEIGHT, z: DEPTH}, parentID: MyAvatar.sessionUUID, - parentJointIndex: NEGATIVE_ONE + parentJointIndex: -2 }); var WEB_ENTITY_REDUCTION_FACTOR = {x: 0.78, y: 0.85}; @@ -82,61 +80,14 @@ WebTablet = function (url) { shapeType: "box", dpi: 45, parentID: this.tabletEntityID, - parentJointIndex: NEGATIVE_ONE + parentJointIndex: -1 }); this.state = "idle"; - - // compute the room/sensor matrix of the entity. - var invRoomMat = Mat4.inverse(MyAvatar.sensorToWorldMatrix); - var entityWorldMat = Mat4.createFromRotAndTrans(tabletEntityRotation, tabletEntityPosition); - this.entityRoomMat = Mat4.multiply(invRoomMat, entityWorldMat); - - var _this = this; - this.updateFunc = function (dt) { - _this.update(dt); - }; - Script.update.connect(this.updateFunc); }; WebTablet.prototype.destroy = function () { Entities.deleteEntity(this.webEntityID); Entities.deleteEntity(this.tabletEntityID); - Script.update.disconnect(this.updateFunc); -}; - -WebTablet.prototype.update = function (dt) { - - var props = Entities.getEntityProperties(this.tabletEntityID, ["position", "rotation", "parentID", "parentJointIndex"]); - var entityWorldMat; - - if (this.state === "idle") { - - if (props.parentID !== MyAvatar.sessionUUID || props.parentJointIndex !== NEGATIVE_ONE) { - this.state = "held"; - return; - } - - // convert the sensor/room matrix of the entity into world space, using the current sensorToWorldMatrix - var roomMat = MyAvatar.sensorToWorldMatrix; - entityWorldMat = Mat4.multiply(roomMat, this.entityRoomMat); - - // slam the world space position and orientation - Entities.editEntity(this.tabletEntityID, { - position: Mat4.extractTranslation(entityWorldMat), - rotation: Mat4.extractRotation(entityWorldMat) - }); - } else if (this.state === "held") { - if (props.parentID === MyAvatar.sessionUUID && props.parentJointIndex === NEGATIVE_ONE) { - - // re-compute the room/sensor matrix for the avatar now that it has been released. - var invRoomMat = Mat4.inverse(MyAvatar.sensorToWorldMatrix); - entityWorldMat = Mat4.createFromRotAndTrans(props.rotation, props.position); - this.entityRoomMat = Mat4.multiply(invRoomMat, entityWorldMat); - - this.state = "idle"; - return; - } - } }; From 6be737993ebc0580e4f1d50305a0660690f9b208 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 18 Aug 2016 18:18:49 -0700 Subject: [PATCH 237/332] bug fix for sensorToWorld translation transmission --- libraries/avatars/src/AvatarData.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index e90f063cff..9088ee0577 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -220,9 +220,9 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { packOrientationQuatToSixBytes(header->sensorToWorldQuat, glmExtractRotation(sensorToWorldMatrix)); glm::vec3 scale = extractScale(sensorToWorldMatrix); packFloatScalarToSignedTwoByteFixed((uint8_t*)&header->sensorToWorldScale, scale.x, SENSOR_TO_WORLD_SCALE_RADIX); - header->sensorToWorldTrans[0] = sensorToWorldMatrix[0][3]; - header->sensorToWorldTrans[1] = sensorToWorldMatrix[1][3]; - header->sensorToWorldTrans[2] = sensorToWorldMatrix[2][3]; + header->sensorToWorldTrans[0] = sensorToWorldMatrix[3][0]; + header->sensorToWorldTrans[1] = sensorToWorldMatrix[3][1]; + header->sensorToWorldTrans[2] = sensorToWorldMatrix[3][2]; setSemiNibbleAt(header->flags, KEY_STATE_START_BIT, _keyState); // hand state @@ -518,6 +518,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&header->sensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX); glm::vec3 sensorToWorldTrans(header->sensorToWorldTrans[0], header->sensorToWorldTrans[1], header->sensorToWorldTrans[2]); glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans); + _sensorToWorldMatrixCache.set(sensorToWorldMatrix); { // bitFlags and face data From b2dff8aa77f28c130efddc474f330d940b790752 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 19 Aug 2016 15:25:46 -0700 Subject: [PATCH 238/332] Easier to click buttons on web entities with shaky hand controllers There is an angular and time dead spot on webEntity for scrolling vs clicking. Currently, it's 150 ms and 3 degrees. See POINTER_PRESS_TO_MOVE_DELAY and POINTER_PRESS_TO_MOVE_DEADSPOT_ANGLE * Fix for warnings when clicking on window.open() links in WebEntity --- interface/resources/qml/controls/WebView.qml | 4 +- .../src/RenderableWebEntityItem.cpp | 2 + .../system/controllers/handControllerGrab.js | 60 +++++++++++++------ .../controllers/handControllerPointer.js | 2 +- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index bba91c64f6..7285db22d2 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -55,9 +55,11 @@ WebEngineView { } onNewViewRequested:{ + if (desktop) { var component = Qt.createComponent("../Browser.qml"); var newWindow = component.createObject(desktop); - request.openIn(newWindow.webView) + request.openIn(newWindow.webView); + } } // This breaks the webchannel used for passing messages. Fixed in Qt 5.6 diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index bb3f508d9b..9221fec140 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -68,6 +69,7 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { _webSurface->load("WebView.qml"); _webSurface->resume(); _webSurface->getRootItem()->setProperty("url", _sourceUrl); + _webSurface->getRootContext()->setContextProperty("desktop", QVariant()); _connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) { _texture = textureId; }); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index ab1fe76a91..ac1c844cc9 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -234,6 +234,10 @@ CONTROLLER_STATE_MACHINE[STATE_ENTITY_TOUCHING] = { updateMethod: "entityTouching" }; +function angleBetween(a, b) { + return Math.acos(Vec3.dot(Vec3.normalize(a), Vec3.normalize(b))); +} + function projectOntoEntityXYPlane(entityID, worldPos) { var props = entityPropertiesCache.getProps(entityID); var invRot = Quat.inverse(props.rotation); @@ -1975,7 +1979,8 @@ function MyController(hand) { var handPosition = this.getHandPosition(); // the center of the equipped object being far from the hand isn't enough to auto-unequip -- we also // need to fail the findEntities test. - var nearPickedCandidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); + var TEAR_AWAY_DISTANCE = 0.04; + var nearPickedCandidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS + TEAR_AWAY_DISTANCE); if (nearPickedCandidateEntities.indexOf(this.grabbedEntity) == -1) { // for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip. print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." + @@ -2128,6 +2133,11 @@ function MyController(hand) { Entities.sendMousePressOnEntity(this.grabbedEntity, pointerEvent); Entities.sendClickDownOnEntity(this.grabbedEntity, pointerEvent); + + this.touchingEnterTimer = 0; + this.touchingEnterPointerEvent = pointerEvent; + this.touchingEnterPointerEvent.button = "None"; + this.deadspotExpired = false; } }; @@ -2135,27 +2145,37 @@ function MyController(hand) { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand); if (intersectInfo) { - var pointerEvent = { - type: "Release", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), - pos3D: intersectInfo.point, - normal: intersectInfo.normal, - direction: intersectInfo.searchRay.direction, - button: "Primary", - isPrimaryButton: true, - isSecondaryButton: false, - isTertiaryButton: false - }; + var pointerEvent; + if (this.deadspotExpired) { + pointerEvent = { + type: "Release", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "Primary", + isPrimaryButton: false, + isSecondaryButton: false, + isTertiaryButton: false + }; + } else { + pointerEvent = this.touchingEnterPointerEvent; + pointerEvent.button = "Primary"; + pointerEvent.isPrimaryButton = false; + } Entities.sendMouseReleaseOnEntity(this.grabbedEntity, pointerEvent); Entities.sendClickReleaseOnEntity(this.grabbedEntity, pointerEvent); Entities.sendHoverLeaveEntity(this.grabbedEntity, pointerEvent); - this.focusedEntity = null; } + this.focusedEntity = null; }; - this.entityTouching = function() { + this.entityTouching = function(dt) { + + this.touchingEnterTimer += dt; + entityPropertiesCache.addEntity(this.grabbedEntity); if (!this.triggerSmoothedGrab()) { @@ -2184,8 +2204,14 @@ function MyController(hand) { isTertiaryButton: false }; - Entities.sendMouseMoveOnEntity(this.grabbedEntity, pointerEvent); - Entities.sendHoldingClickOnEntity(this.grabbedEntity, pointerEvent); + var POINTER_PRESS_TO_MOVE_DELAY = 0.15; // seconds + var POINTER_PRESS_TO_MOVE_DEADSPOT_ANGLE = 0.05; // radians ~ 3 degrees + if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || + angleBetween(pointerEvent.direction, this.touchingEnterPointerEvent.direction) > POINTER_PRESS_TO_MOVE_DEADSPOT_ANGLE) { + Entities.sendMouseMoveOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendHoldingClickOnEntity(this.grabbedEntity, pointerEvent); + this.deadspotExpired = true; + } this.intersectionDistance = intersectInfo.distance; this.searchIndicatorOn(intersectInfo.searchRay); diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 4d6e9d7f60..5888b53a27 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -121,7 +121,7 @@ function ignoreMouseActivity() { return true; } var pos = Reticle.position; - if (pos.x == -1 && pos.y == -1) { + if (!pos || (pos.x == -1 && pos.y == -1)) { return true; } // Only we know if we moved it, which is why this script has to replace depthReticle.js From 137f795f2951b083e762daf44cabdd2a3b7276db Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Tue, 23 Aug 2016 11:22:01 -0700 Subject: [PATCH 239/332] Made clara marketplace its own .js --- scripts/defaultScripts.js | 2 +- scripts/system/marketplace.js | 78 ----------------------------------- 2 files changed, 1 insertion(+), 79 deletions(-) delete mode 100644 scripts/system/marketplace.js diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index dc252afcf1..0d76498e84 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -15,7 +15,7 @@ Script.load("system/users.js"); Script.load("system/mute.js"); Script.load("system/goto.js"); Script.load("system/hmd.js"); -Script.load("system/marketplace.js"); +Script.load("system/marketplaces/marketplace.js"); Script.load("system/edit.js"); Script.load("system/mod.js"); Script.load("system/selectAudioDevice.js"); diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js deleted file mode 100644 index 01aa4c76ce..0000000000 --- a/scripts/system/marketplace.js +++ /dev/null @@ -1,78 +0,0 @@ -// -// marketplace.js -// -// Created by Eric Levin on 8 Jan 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var toolIconUrl = Script.resolvePath("assets/images/tools/"); -var qml = Script.resolvePath("../../resources/qml/MarketplaceComboBox.qml") - -var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; - -var marketplaceWindow = new OverlayWindow({ - title: "Marketplace", - source: qml, - width: 900, - height: 700, - toolWindow: false, - visible: false, -}); - -var toolHeight = 50; -var toolWidth = 50; -var TOOLBAR_MARGIN_Y = 0; - - -function showMarketplace(marketplaceID) { - var url = MARKETPLACE_URL; - if (marketplaceID) { - url = url + "/items/" + marketplaceID; - } - marketplaceWindow.setVisible(true); - - UserActivityLogger.openedMarketplace(); -} - -function hideMarketplace() { - marketplaceWindow.setVisible(false); -} - -function toggleMarketplace() { - if (marketplaceWindow.visible) { - hideMarketplace(); - } else { - showMarketplace(); - } -} - -var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); - -var browseExamplesButton = toolBar.addButton({ - imageURL: toolIconUrl + "market.svg", - objectName: "marketplace", - buttonState: 1, - defaultState: 1, - hoverState: 3, - alpha: 0.9 -}); - -function onExamplesWindowVisibilityChanged() { - browseExamplesButton.writeProperty('buttonState', marketplaceWindow.visible ? 0 : 1); - browseExamplesButton.writeProperty('defaultState', marketplaceWindow.visible ? 0 : 1); - browseExamplesButton.writeProperty('hoverState', marketplaceWindow.visible ? 2 : 3); -} -function onClick() { - toggleMarketplace(); -} -browseExamplesButton.clicked.connect(onClick); -marketplaceWindow.visibleChanged.connect(onExamplesWindowVisibilityChanged); - -Script.scriptEnding.connect(function () { - toolBar.removeButton("marketplace"); - browseExamplesButton.clicked.disconnect(onClick); - marketplaceWindow.visibleChanged.disconnect(onExamplesWindowVisibilityChanged); -}); From 2d38d47bcca4e88e12714351cc2f8f00375cb6b6 Mon Sep 17 00:00:00 2001 From: elisa-lj11 Date: Tue, 23 Aug 2016 11:24:03 -0700 Subject: [PATCH 240/332] separated clara.js from marketplace.js --- scripts/system/marketplaces/clara.js | 79 ++++++++++++++++++++++ scripts/system/marketplaces/marketplace.js | 77 +++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 scripts/system/marketplaces/clara.js create mode 100644 scripts/system/marketplaces/marketplace.js diff --git a/scripts/system/marketplaces/clara.js b/scripts/system/marketplaces/clara.js new file mode 100644 index 0000000000..7b48f49135 --- /dev/null +++ b/scripts/system/marketplaces/clara.js @@ -0,0 +1,79 @@ +// +// clara.js +// +// Created by Eric Levin on 8 Jan 2016 +// Edited by Elisa Lupin-Jimenez on 23 Aug 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var toolIconUrl = Script.resolvePath("../assets/images/tools/"); +var qml = Script.resolvePath("../../../resources/qml/MarketplaceComboBox.qml") + +var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; + +var marketplaceWindow = new OverlayWindow({ + title: "Marketplace", + source: qml, + width: 900, + height: 700, + toolWindow: false, + visible: false, +}); + +var toolHeight = 50; +var toolWidth = 50; +var TOOLBAR_MARGIN_Y = 0; + + +function showMarketplace(marketplaceID) { + var url = MARKETPLACE_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + marketplaceWindow.setVisible(true); + + UserActivityLogger.openedMarketplace(); +} + +function hideMarketplace() { + marketplaceWindow.setVisible(false); +} + +function toggleMarketplace() { + if (marketplaceWindow.visible) { + hideMarketplace(); + } else { + showMarketplace(); + } +} + +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + +var browseExamplesButton = toolBar.addButton({ + imageURL: toolIconUrl + "market.svg", + objectName: "marketplace", + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9 +}); + +function onExamplesWindowVisibilityChanged() { + browseExamplesButton.writeProperty('buttonState', marketplaceWindow.visible ? 0 : 1); + browseExamplesButton.writeProperty('defaultState', marketplaceWindow.visible ? 0 : 1); + browseExamplesButton.writeProperty('hoverState', marketplaceWindow.visible ? 2 : 3); +} +function onClick() { + toggleMarketplace(); +} +browseExamplesButton.clicked.connect(onClick); +marketplaceWindow.visibleChanged.connect(onExamplesWindowVisibilityChanged); + +Script.scriptEnding.connect(function () { + toolBar.removeButton("marketplace"); + browseExamplesButton.clicked.disconnect(onClick); + marketplaceWindow.visibleChanged.disconnect(onExamplesWindowVisibilityChanged); +}); diff --git a/scripts/system/marketplaces/marketplace.js b/scripts/system/marketplaces/marketplace.js new file mode 100644 index 0000000000..a43ef6c977 --- /dev/null +++ b/scripts/system/marketplaces/marketplace.js @@ -0,0 +1,77 @@ +// +// marketplace.js +// +// Created by Eric Levin on 8 Jan 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var toolIconUrl = Script.resolvePath("../assets/images/tools/"); + +var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; +var marketplaceWindow = new OverlayWebWindow({ + title: "Marketplace", + source: "about:blank", + width: 900, + height: 700, + visible: false +}); + +var toolHeight = 50; +var toolWidth = 50; +var TOOLBAR_MARGIN_Y = 0; + + +function showMarketplace(marketplaceID) { + var url = MARKETPLACE_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + marketplaceWindow.setURL(url); + marketplaceWindow.setVisible(true); + + UserActivityLogger.openedMarketplace(); +} + +function hideMarketplace() { + marketplaceWindow.setVisible(false); + marketplaceWindow.setURL("about:blank"); +} + +function toggleMarketplace() { + if (marketplaceWindow.visible) { + hideMarketplace(); + } else { + showMarketplace(); + } +} + +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + +var browseExamplesButton = toolBar.addButton({ + imageURL: toolIconUrl + "market.svg", + objectName: "marketplace", + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9 +}); + +function onExamplesWindowVisibilityChanged() { + browseExamplesButton.writeProperty('buttonState', marketplaceWindow.visible ? 0 : 1); + browseExamplesButton.writeProperty('defaultState', marketplaceWindow.visible ? 0 : 1); + browseExamplesButton.writeProperty('hoverState', marketplaceWindow.visible ? 2 : 3); +} +function onClick() { + toggleMarketplace(); +} +browseExamplesButton.clicked.connect(onClick); +marketplaceWindow.visibleChanged.connect(onExamplesWindowVisibilityChanged); + +Script.scriptEnding.connect(function () { + toolBar.removeButton("marketplace"); + browseExamplesButton.clicked.disconnect(onClick); + marketplaceWindow.visibleChanged.disconnect(onExamplesWindowVisibilityChanged); +}); \ No newline at end of file From 5166a33370fac052f10f277e3b7bd74945eb08f6 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 23 Aug 2016 11:53:20 -0700 Subject: [PATCH 241/332] autosave after 1.5 s no changes --- scripts/system/html/js/entityProperties.js | 39 ++++++++++++++++------ 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 5d6ce8f97e..9f58fbe04d 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -23,6 +23,8 @@ var ICON_FOR_TYPE = { Multiple: "" } +var EDITOR_TIMEOUT_DURATION = 1500; + var colorPickers = []; debugPrint = function(message) { @@ -347,6 +349,7 @@ function setTextareaScrolling(element) { }; var editor = null; +var editorTimeout = null; function createJSONEditor() { var container = document.getElementById("userdata-editor"); @@ -360,6 +363,15 @@ function createJSONEditor() { }, onError: function(e) { alert('JSON editor:' + e) + }, + onChange: function() { + if (editorTimeout !== null) { + clearTimeout(editorTimeout); + } + editorTimeout = setTimeout(function() { + saveJSONUserData(); + }, EDITOR_TIMEOUT_DURATION) + //saveJSONUserData(); } }; editor = new JSONEditor(container, options); @@ -412,7 +424,8 @@ function removeStaticUserData() { }; function setEditorJSON(json) { - return editor.set(json) + editor.set(json) + editor.expandAll(); }; function getEditorJSON() { @@ -425,6 +438,19 @@ function deleteJSONEditor() { } }; +var savedJSONTimer = null; + +function saveJSONUserData() { + setUserDataFromEditor(); + $('#userdata-saved').show(); + if (savedJSONTimer !== null) { + clearTimeout(savedJSONTimer); + } + savedJSONTimer = setTimeout(function() { + $('#userdata-saved').hide(); + }, 1500) +} + function loaded() { openEventBridge(function() { var allSections = []; @@ -1064,16 +1090,9 @@ function loaded() { }); - var savedJSONTimer = null; + elSaveUserData.addEventListener("click", function() { - setUserDataFromEditor(); - $('#userdata-saved').show(); - if (savedJSONTimer !== null) { - clearTimeout(savedJSONTimer); - } - savedJSONTimer = setTimeout(function() { - $('#userdata-saved').hide(); - }, 1500) + saveJSONUserData(); }); elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); From fcf459645d1ea975f1ef04f3aabfb383f8389b46 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 23 Aug 2016 12:02:57 -0700 Subject: [PATCH 242/332] center after teleport --- scripts/system/controllers/teleport.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 77d0109b9f..f713c0db48 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -564,6 +564,8 @@ function Teleporter() { } }, SMOOTH_ARRIVAL_SPACING); + + HMD.centerUI(); } } From fb6fa687e8d1f46985fc57a17d6c6b5eba57bfe0 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 23 Aug 2016 12:07:59 -0700 Subject: [PATCH 243/332] in the right place... thanks howard! --- scripts/system/controllers/teleport.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index f713c0db48..0aa5820b0f 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -553,6 +553,7 @@ function Teleporter() { _this.smoothArrivalInterval = Script.setInterval(function() { if (_this.arrivalPoints.length === 0) { Script.clearInterval(_this.smoothArrivalInterval); + HMD.centerUI(); return; } var landingPoint = _this.arrivalPoints.shift(); @@ -564,8 +565,8 @@ function Teleporter() { } }, SMOOTH_ARRIVAL_SPACING); - - HMD.centerUI(); + + } } From 477ed8828476f19c6a9687a9460b949316f8409b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 23 Aug 2016 14:34:28 -0700 Subject: [PATCH 244/332] remove autosave, disable save button until change --- scripts/system/html/entityProperties.html | 4 +-- scripts/system/html/js/entityProperties.js | 42 ++++++++++++++++------ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 0eeb817640..dfa87f86ad 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -67,9 +67,9 @@
- + - + Saved!
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 9f58fbe04d..b9135a1048 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -348,8 +348,11 @@ function setTextareaScrolling(element) { element.setAttribute("scrolling", isScrolling ? "true" : "false"); }; + + var editor = null; var editorTimeout = null; +var lastJSONString=null; function createJSONEditor() { var container = document.getElementById("userdata-editor"); @@ -365,13 +368,15 @@ function createJSONEditor() { alert('JSON editor:' + e) }, onChange: function() { - if (editorTimeout !== null) { - clearTimeout(editorTimeout); + console.log('changed') + if(currentJSONString==='{"":""}'){ + console.log('its empty') + return; } - editorTimeout = setTimeout(function() { - saveJSONUserData(); - }, EDITOR_TIMEOUT_DURATION) - //saveJSONUserData(); + $('#userdata-save').attr('disabled',false) + var currentJSONString = editor.getText(); + + } }; editor = new JSONEditor(container, options); @@ -448,9 +453,18 @@ function saveJSONUserData() { } savedJSONTimer = setTimeout(function() { $('#userdata-saved').hide(); + $('#userdata-save').attr('disabled',true) }, 1500) } +function bindAllNonJSONEditorElements(){ + var inputs = $('input'); + var i; + for(i=0;i Date: Tue, 23 Aug 2016 14:55:46 -0700 Subject: [PATCH 245/332] detect unset skybox --- interface/src/Application.cpp | 2 +- libraries/model/src/model/Skybox.h | 1 + libraries/procedural/src/procedural/Procedural.h | 1 + libraries/procedural/src/procedural/ProceduralSkybox.cpp | 4 ++++ libraries/procedural/src/procedural/ProceduralSkybox.h | 7 ++++--- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ef27f4b9b0..30e2cfd9c6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4326,7 +4326,7 @@ namespace render { } case model::SunSkyStage::SKY_BOX: { auto skybox = skyStage->getSkybox(); - if (skybox) { + if (!skybox->empty()) { PerformanceTimer perfTimer("skybox"); skybox->render(batch, args->getViewFrustum()); break; diff --git a/libraries/model/src/model/Skybox.h b/libraries/model/src/model/Skybox.h index 8c9e7bb1d6..b2e004a2e9 100755 --- a/libraries/model/src/model/Skybox.h +++ b/libraries/model/src/model/Skybox.h @@ -35,6 +35,7 @@ public: void setCubemap(const gpu::TexturePointer& cubemap); const gpu::TexturePointer& getCubemap() const { return _cubemap; } + virtual bool empty() { return _schemaBuffer.get().color == vec3(0) && !_cubemap; } virtual void clear() { setCubemap(nullptr); } void prepare(gpu::Batch& batch, int textureSlot = SKYBOX_SKYMAP_SLOT, int bufferSlot = SKYBOX_CONSTANTS_SLOT) const; diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index fdca85bbae..c128da0be0 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -38,6 +38,7 @@ public: void parse(const QString& userDataJson); bool ready(); + bool enabled() { return _enabled; } void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation); const gpu::ShaderPointer& getShader() const { return _shader; } diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index 843df3aa8d..83122578e7 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -26,6 +26,10 @@ ProceduralSkybox::ProceduralSkybox() : model::Skybox() { _procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); } +bool ProceduralSkybox::empty() { + return !_procedural.enabled() && Skybox::empty(); +} + void ProceduralSkybox::clear() { // Parse and prepare a procedural with no shaders to release textures parse(QString()); diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.h b/libraries/procedural/src/procedural/ProceduralSkybox.h index b38e481317..30412d559c 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.h +++ b/libraries/procedural/src/procedural/ProceduralSkybox.h @@ -20,13 +20,14 @@ class ProceduralSkybox: public model::Skybox { public: ProceduralSkybox(); - virtual ~ProceduralSkybox() {}; + ~ProceduralSkybox() override {}; void parse(const QString& userData) { _procedural.parse(userData); } - virtual void clear() override; + bool empty() override; + void clear() override; - virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const override; + void render(gpu::Batch& batch, const ViewFrustum& frustum) const override; static void render(gpu::Batch& batch, const ViewFrustum& frustum, const ProceduralSkybox& skybox); protected: From 3abbc98857e88a48393ef164105ed7e035e3bf48 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 23 Aug 2016 15:48:04 -0700 Subject: [PATCH 246/332] save when focus on inputs --- scripts/system/html/js/entityProperties.js | 39 +++++++++++++--------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index b9135a1048..a6cf84b230 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -352,7 +352,7 @@ function setTextareaScrolling(element) { var editor = null; var editorTimeout = null; -var lastJSONString=null; +var lastJSONString = null; function createJSONEditor() { var container = document.getElementById("userdata-editor"); @@ -368,12 +368,10 @@ function createJSONEditor() { alert('JSON editor:' + e) }, onChange: function() { - console.log('changed') - if(currentJSONString==='{"":""}'){ - console.log('its empty') + if (currentJSONString === '{"":""}') { return; } - $('#userdata-save').attr('disabled',false) + $('#userdata-save').attr('disabled', false) var currentJSONString = editor.getText(); @@ -440,6 +438,7 @@ function getEditorJSON() { function deleteJSONEditor() { if (editor !== null) { editor.destroy(); + editor = null; } }; @@ -448,20 +447,29 @@ var savedJSONTimer = null; function saveJSONUserData() { setUserDataFromEditor(); $('#userdata-saved').show(); + $('#userdata-save').attr('disabled', true) if (savedJSONTimer !== null) { clearTimeout(savedJSONTimer); } savedJSONTimer = setTimeout(function() { $('#userdata-saved').hide(); - $('#userdata-save').attr('disabled',true) + }, 1500) } -function bindAllNonJSONEditorElements(){ +function bindAllNonJSONEditorElements() { var inputs = $('input'); var i; - for(i=0;i 1) { + deleteJSONEditor(); var selections = data.selections; var ids = []; @@ -787,7 +800,6 @@ function loaded() { FIXME: See FIXME for property-script-url. elScriptTimestamp.value = properties.scriptTimestamp; */ - //deleteJSONEditor(); hideUserDataTextArea(); var json = null; try { @@ -798,7 +810,6 @@ function loaded() { } if (editor === null) { createJSONEditor(); - console.log('CREATING NEW EDITOR') } setEditorJSON(json); @@ -974,7 +985,7 @@ function loaded() { elLocked.removeAttribute('disabled'); } else { enableProperties(); - elSaveUserData.disabled=true; + elSaveUserData.disabled = true; } var activeElement = document.activeElement; @@ -1115,9 +1126,7 @@ function loaded() { }); elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); - // elJSONEditor.addEventListener('mouseleave',function(){ - // saveJSONUserData(); - // }); + elNewJSONEditor.addEventListener('click', function() { deleteJSONEditor(); createJSONEditor(); @@ -1387,7 +1396,7 @@ function loaded() { clicked = true; }; } - bindMouseLeaveForJSONEditor(); + bindAllNonJSONEditorElements(); }); // Collapsible sections From a5054487ba579fd32c5433d1600ae2473a757060 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 23 Aug 2016 18:18:43 -0700 Subject: [PATCH 247/332] save on no selections, select another entity, etc --- scripts/system/edit.js | 5 +- scripts/system/html/js/entityProperties.js | 89 +++++++++++++++++----- 2 files changed, 72 insertions(+), 22 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index a3e5476bf9..51d52367d3 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1427,7 +1427,10 @@ var PropertiesTool = function (opts) { } pushCommandForSelections(); selectionManager._update(); - } else if (data.type === "showMarketplace") { + }else if(data.type==='saveUserData'){ + Entities.editEntity(data.id,data.properties) + } + else if (data.type === "showMarketplace") { showMarketplace(); } else if (data.type === "action") { if (data.action === "moveSelectionToGrid") { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index a6cf84b230..82706c073d 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -26,7 +26,7 @@ var ICON_FOR_TYPE = { var EDITOR_TIMEOUT_DURATION = 1500; var colorPickers = []; - +var lastEntityID=null; debugPrint = function(message) { EventBridge.emitWebEvent( JSON.stringify({ @@ -84,7 +84,7 @@ function showElements(els, show) { function createEmitCheckedPropertyUpdateFunction(propertyName) { return function() { EventBridge.emitWebEvent( - '{ "type":"update", "properties":{"' + propertyName + '":' + this.checked + '}}' + '{"id":' + lastEntityID + ', "type":"update", "properties":{"' + propertyName + '":' + this.checked + '}}' ); }; } @@ -106,6 +106,7 @@ function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) { properties[group][propertyName] = this.checked; EventBridge.emitWebEvent( JSON.stringify({ + id: lastEntityID, type: "update", properties: properties }) @@ -119,7 +120,7 @@ function createEmitNumberPropertyUpdateFunction(propertyName, decimals) { var value = parseFloat(this.value).toFixed(decimals); EventBridge.emitWebEvent( - '{ "type":"update", "properties":{"' + propertyName + '":' + value + '}}' + '{"id":' + lastEntityID + ', "type":"update", "properties":{"' + propertyName + '":' + value + '}}' ); }; } @@ -131,6 +132,7 @@ function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) { properties[group][propertyName] = this.value; EventBridge.emitWebEvent( JSON.stringify({ + id: lastEntityID, type: "update", properties: properties, }) @@ -145,6 +147,7 @@ function createEmitTextPropertyUpdateFunction(propertyName) { properties[propertyName] = this.value; EventBridge.emitWebEvent( JSON.stringify({ + id: lastEntityID, type: "update", properties: properties, }) @@ -159,6 +162,7 @@ function createEmitGroupTextPropertyUpdateFunction(group, propertyName) { properties[group][propertyName] = this.value; EventBridge.emitWebEvent( JSON.stringify({ + id: lastEntityID, type: "update", properties: properties, }) @@ -169,6 +173,7 @@ function createEmitGroupTextPropertyUpdateFunction(group, propertyName) { function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) { return function() { var data = { + id: lastEntityID, type: "update", properties: {} }; @@ -184,6 +189,7 @@ function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) { function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) { return function() { var data = { + id: lastEntityID, type: "update", properties: {} }; @@ -200,6 +206,7 @@ function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, el function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) { return function() { var data = { + id: lastEntityID, type: "update", properties: {} }; @@ -220,6 +227,7 @@ function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) function emitColorPropertyUpdate(property, red, green, blue, group) { var data = { + id: lastEntityID, type: "update", properties: {} }; @@ -244,6 +252,7 @@ function emitColorPropertyUpdate(property, red, green, blue, group) { function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) { return function() { var data = { + id: lastEntityID, type: "update", properties: {} }; @@ -273,6 +282,7 @@ function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElemen EventBridge.emitWebEvent( JSON.stringify({ + id: lastEntityID, type: "update", properties: _properties }) @@ -280,7 +290,7 @@ function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElemen } -function setUserDataFromEditor() { +function setUserDataFromEditor(noUpdate) { var json = null; try { json = editor.get(); @@ -291,20 +301,35 @@ function setUserDataFromEditor() { return; } else { var text = editor.getText() - EventBridge.emitWebEvent( - JSON.stringify({ - type: "update", - properties: { - userData: text - }, - }) - ); + console.log('saving as text:', text) + if (noUpdate === true) { + EventBridge.emitWebEvent( + JSON.stringify({ + id: lastEntityID, + type: "saveUserData", + properties: { + userData: text + }, + }) + ); + return; + } else { + EventBridge.emitWebEvent( + JSON.stringify({ + id: lastEntityID, + type: "update", + properties: { + userData: text + }, + }) + ); + } + } } - function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, defaultValue) { var properties = {}; var parsedData = {}; @@ -337,6 +362,7 @@ function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, d EventBridge.emitWebEvent( JSON.stringify({ + id: lastEntityID, type: "update", properties: properties, }) @@ -368,11 +394,12 @@ function createJSONEditor() { alert('JSON editor:' + e) }, onChange: function() { + var currentJSONString = editor.getText(); + if (currentJSONString === '{"":""}') { return; } $('#userdata-save').attr('disabled', false) - var currentJSONString = editor.getText(); } @@ -444,8 +471,9 @@ function deleteJSONEditor() { var savedJSONTimer = null; -function saveJSONUserData() { - setUserDataFromEditor(); +function saveJSONUserData(noUpdate) { + console.log('start saving userdata') + setUserDataFromEditor(noUpdate); $('#userdata-saved').show(); $('#userdata-save').attr('disabled', true) if (savedJSONTimer !== null) { @@ -453,7 +481,7 @@ function saveJSONUserData() { } savedJSONTimer = setTimeout(function() { $('#userdata-saved').hide(); - + }, 1500) } @@ -467,12 +495,23 @@ function bindAllNonJSONEditorElements() { if (e.target.id === "userdata-new-editor" || e.target.id === "userdata-clear") { return; } else { - saveJSONUserData(); + console.log('save on focus',e) + saveJSONUserData(true); } }) } } +function unbindAllInputs() { + var inputs = $('input'); + var i; + for (i = 0; i < inputs.length; i++) { + var input = inputs[i]; + var field = $(input); + field.unbind(); + } +} + function loaded() { openEventBridge(function() { var allSections = []; @@ -667,7 +706,9 @@ function loaded() { if (data.type == "update") { if (data.selections.length == 0) { - if (editor !== null) { + if (editor !== null && lastEntityID!==null) { + console.log('save on no selections') + saveJSONUserData(true); deleteJSONEditor(); } elTypeIcon.style.display = "none"; @@ -707,9 +748,13 @@ function loaded() { disableProperties(); } else { - properties = data.selections[0].properties; + if (lastEntityID !== properties.id && lastEntityID!==null) { + console.log('save on init') + saveJSONUserData(true); + } + lastEntityID = properties.id; elID.innerHTML = properties.id; elType.innerHTML = properties.type; @@ -1112,6 +1157,7 @@ function loaded() { properties['userData'] = elUserData.value; EventBridge.emitWebEvent( JSON.stringify({ + id: lastEntityID, type: "update", properties: properties, }) @@ -1122,7 +1168,8 @@ function loaded() { elSaveUserData.addEventListener("click", function() { - saveJSONUserData(); + console.log('save on click') + saveJSONUserData(true); }); elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); From 2f4617cd5e3cec5239ca58f26fddb2469e35a200 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 23 Aug 2016 18:20:33 -0700 Subject: [PATCH 248/332] quiet some prints --- scripts/system/edit.js | 11 +++++------ scripts/system/html/js/entityProperties.js | 8 +------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 51d52367d3..964fc0306f 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -143,7 +143,7 @@ function showMarketplace(marketplaceID) { if (marketplaceID) { url = url + "/items/" + marketplaceID; } - print("setting marketplace URL to " + url); + // print("setting marketplace URL to " + url); marketplaceWindow.setURL(url); marketplaceWindow.setVisible(true); marketplaceWindow.raise(); @@ -216,7 +216,6 @@ var toolBar = (function () { } function initialize() { - print("QQQ creating edit toolbar"); Script.scriptEnding.connect(cleanup); Window.domainChanged.connect(function () { @@ -794,7 +793,7 @@ var modelMenuAddedDelete = false; var originalLightsArePickable = Entities.getLightsArePickable(); function setupModelMenus() { - print("setupModelMenus()"); + // print("setupModelMenus()"); // adj our menuitems Menu.addMenuItem({ menuName: "Edit", @@ -803,7 +802,7 @@ function setupModelMenus() { grouping: "Advanced" }); if (!Menu.menuItemExists("Edit", "Delete")) { - print("no delete... adding ours"); + // print("no delete... adding ours"); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", @@ -815,7 +814,7 @@ function setupModelMenus() { }); modelMenuAddedDelete = true; } else { - print("delete exists... don't add ours"); + // print("delete exists... don't add ours"); } Menu.addMenuItem({ @@ -1047,7 +1046,7 @@ function deleteSelectedEntities() { SelectionManager.clearSelections(); pushCommandForSelections([], savedProperties); } else { - print(" Delete Entity.... not holding..."); + // print(" Delete Entity.... not holding..."); } } diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 82706c073d..5ad228af1b 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -301,7 +301,6 @@ function setUserDataFromEditor(noUpdate) { return; } else { var text = editor.getText() - console.log('saving as text:', text) if (noUpdate === true) { EventBridge.emitWebEvent( JSON.stringify({ @@ -472,7 +471,6 @@ function deleteJSONEditor() { var savedJSONTimer = null; function saveJSONUserData(noUpdate) { - console.log('start saving userdata') setUserDataFromEditor(noUpdate); $('#userdata-saved').show(); $('#userdata-save').attr('disabled', true) @@ -495,7 +493,6 @@ function bindAllNonJSONEditorElements() { if (e.target.id === "userdata-new-editor" || e.target.id === "userdata-clear") { return; } else { - console.log('save on focus',e) saveJSONUserData(true); } }) @@ -707,7 +704,6 @@ function loaded() { if (data.selections.length == 0) { if (editor !== null && lastEntityID!==null) { - console.log('save on no selections') saveJSONUserData(true); deleteJSONEditor(); } @@ -749,8 +745,7 @@ function loaded() { } else { properties = data.selections[0].properties; - if (lastEntityID !== properties.id && lastEntityID!==null) { - console.log('save on init') + if (lastEntityID !== properties.id && lastEntityID!==null && editor!==null) { saveJSONUserData(true); } @@ -1168,7 +1163,6 @@ function loaded() { elSaveUserData.addEventListener("click", function() { - console.log('save on click') saveJSONUserData(true); }); From c4c6b30440db678e9f1a1787fb9f19a961ee5bc0 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 23 Aug 2016 12:02:57 -0700 Subject: [PATCH 249/332] center after teleport --- scripts/system/controllers/teleport.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 77d0109b9f..f713c0db48 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -564,6 +564,8 @@ function Teleporter() { } }, SMOOTH_ARRIVAL_SPACING); + + HMD.centerUI(); } } From 48bab49210c69cff1f9d86a1e13d4e0dc731de3f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 23 Aug 2016 12:07:59 -0700 Subject: [PATCH 250/332] in the right place... thanks howard! --- scripts/system/controllers/teleport.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index f713c0db48..0aa5820b0f 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -553,6 +553,7 @@ function Teleporter() { _this.smoothArrivalInterval = Script.setInterval(function() { if (_this.arrivalPoints.length === 0) { Script.clearInterval(_this.smoothArrivalInterval); + HMD.centerUI(); return; } var landingPoint = _this.arrivalPoints.shift(); @@ -564,8 +565,8 @@ function Teleporter() { } }, SMOOTH_ARRIVAL_SPACING); - - HMD.centerUI(); + + } } From 1939c6915aee67d19b85701915d0aec8dee1a25d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 24 Aug 2016 10:05:27 -0700 Subject: [PATCH 251/332] Bug-fixes for marketplace toggling --- scripts/system/marketplace.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js index cb82ab9060..11b5a084cf 100644 --- a/scripts/system/marketplace.js +++ b/scripts/system/marketplace.js @@ -55,7 +55,7 @@ function hideMarketplace() { if (marketplaceWindow.visible) { marketplaceWindow.setVisible(false); marketplaceWindow.setURL("about:blank"); - } else { + } else if (marketplaceWebTablet) { marketplaceWebTablet.destroy(); marketplaceWebTablet = null; } @@ -81,16 +81,19 @@ var browseExamplesButton = toolBar.addButton({ alpha: 0.9 }); -function onExamplesWindowVisibilityChanged() { +function onMarketplaceWindowVisibilityChanged() { browseExamplesButton.writeProperty('buttonState', marketplaceWindow.visible ? 0 : 1); browseExamplesButton.writeProperty('defaultState', marketplaceWindow.visible ? 0 : 1); browseExamplesButton.writeProperty('hoverState', marketplaceWindow.visible ? 2 : 3); + marketplaceVisible = marketplaceWindow.visible; } + function onClick() { toggleMarketplace(); } + browseExamplesButton.clicked.connect(onClick); -marketplaceWindow.visibleChanged.connect(onExamplesWindowVisibilityChanged); +marketplaceWindow.visibleChanged.connect(onMarketplaceWindowVisibilityChanged); Script.scriptEnding.connect(function () { toolBar.removeButton("marketplace"); From 6c99a330093807f25e85e7204373f191925155c1 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 23 Aug 2016 14:55:46 -0700 Subject: [PATCH 252/332] detect unset skybox --- interface/src/Application.cpp | 2 +- libraries/model/src/model/Skybox.h | 1 + libraries/procedural/src/procedural/Procedural.h | 1 + libraries/procedural/src/procedural/ProceduralSkybox.cpp | 4 ++++ libraries/procedural/src/procedural/ProceduralSkybox.h | 7 ++++--- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 836b48b3fb..6b4eb8d140 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4306,7 +4306,7 @@ namespace render { } case model::SunSkyStage::SKY_BOX: { auto skybox = skyStage->getSkybox(); - if (skybox) { + if (!skybox->empty()) { PerformanceTimer perfTimer("skybox"); skybox->render(batch, args->getViewFrustum()); break; diff --git a/libraries/model/src/model/Skybox.h b/libraries/model/src/model/Skybox.h index 8c9e7bb1d6..b2e004a2e9 100755 --- a/libraries/model/src/model/Skybox.h +++ b/libraries/model/src/model/Skybox.h @@ -35,6 +35,7 @@ public: void setCubemap(const gpu::TexturePointer& cubemap); const gpu::TexturePointer& getCubemap() const { return _cubemap; } + virtual bool empty() { return _schemaBuffer.get().color == vec3(0) && !_cubemap; } virtual void clear() { setCubemap(nullptr); } void prepare(gpu::Batch& batch, int textureSlot = SKYBOX_SKYMAP_SLOT, int bufferSlot = SKYBOX_CONSTANTS_SLOT) const; diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index fdca85bbae..c128da0be0 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -38,6 +38,7 @@ public: void parse(const QString& userDataJson); bool ready(); + bool enabled() { return _enabled; } void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation); const gpu::ShaderPointer& getShader() const { return _shader; } diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index 843df3aa8d..83122578e7 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -26,6 +26,10 @@ ProceduralSkybox::ProceduralSkybox() : model::Skybox() { _procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); } +bool ProceduralSkybox::empty() { + return !_procedural.enabled() && Skybox::empty(); +} + void ProceduralSkybox::clear() { // Parse and prepare a procedural with no shaders to release textures parse(QString()); diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.h b/libraries/procedural/src/procedural/ProceduralSkybox.h index b38e481317..30412d559c 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.h +++ b/libraries/procedural/src/procedural/ProceduralSkybox.h @@ -20,13 +20,14 @@ class ProceduralSkybox: public model::Skybox { public: ProceduralSkybox(); - virtual ~ProceduralSkybox() {}; + ~ProceduralSkybox() override {}; void parse(const QString& userData) { _procedural.parse(userData); } - virtual void clear() override; + bool empty() override; + void clear() override; - virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const override; + void render(gpu::Batch& batch, const ViewFrustum& frustum) const override; static void render(gpu::Batch& batch, const ViewFrustum& frustum, const ProceduralSkybox& skybox); protected: From 45e3f926a1c417654df4eaf683fa2f3562a6c992 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 24 Aug 2016 14:19:27 -0700 Subject: [PATCH 253/332] removed unused prints and cleanup --- scripts/system/edit.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 964fc0306f..d34d4acffc 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -143,7 +143,6 @@ function showMarketplace(marketplaceID) { if (marketplaceID) { url = url + "/items/" + marketplaceID; } - // print("setting marketplace URL to " + url); marketplaceWindow.setURL(url); marketplaceWindow.setVisible(true); marketplaceWindow.raise(); @@ -793,7 +792,6 @@ var modelMenuAddedDelete = false; var originalLightsArePickable = Entities.getLightsArePickable(); function setupModelMenus() { - // print("setupModelMenus()"); // adj our menuitems Menu.addMenuItem({ menuName: "Edit", @@ -802,7 +800,6 @@ function setupModelMenus() { grouping: "Advanced" }); if (!Menu.menuItemExists("Edit", "Delete")) { - // print("no delete... adding ours"); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", @@ -813,8 +810,6 @@ function setupModelMenus() { grouping: "Advanced" }); modelMenuAddedDelete = true; - } else { - // print("delete exists... don't add ours"); } Menu.addMenuItem({ @@ -1045,8 +1040,6 @@ function deleteSelectedEntities() { } SelectionManager.clearSelections(); pushCommandForSelections([], savedProperties); - } else { - // print(" Delete Entity.... not holding..."); } } @@ -1162,7 +1155,6 @@ function getPositionToImportEntity() { return position; } function importSVO(importURL) { - print("Import URL requested: " + importURL); if (!Entities.canAdjustLocks()) { Window.alert(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); return; @@ -1426,10 +1418,9 @@ var PropertiesTool = function (opts) { } pushCommandForSelections(); selectionManager._update(); - }else if(data.type==='saveUserData'){ - Entities.editEntity(data.id,data.properties) - } - else if (data.type === "showMarketplace") { + } else if(data.type === 'saveUserData'){ + Entities.editEntity(data.id, data.properties) + } else if (data.type === "showMarketplace") { showMarketplace(); } else if (data.type === "action") { if (data.action === "moveSelectionToGrid") { From 384c3dd5438c1e7466019ebb04ec6a4280420be4 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 24 Aug 2016 14:49:41 -0700 Subject: [PATCH 254/332] cleanup and handle a bug when changing from normal text userdata to another field --- scripts/system/html/js/entityProperties.js | 26 +++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 5ad228af1b..a20906fe34 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -26,7 +26,7 @@ var ICON_FOR_TYPE = { var EDITOR_TIMEOUT_DURATION = 1500; var colorPickers = []; -var lastEntityID=null; +var lastEntityID = null; debugPrint = function(message) { EventBridge.emitWebEvent( JSON.stringify({ @@ -333,11 +333,7 @@ function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, d var properties = {}; var parsedData = {}; try { - if ($('#userdata-editor').css('height') === "0px") { - parsedData = editor.getJSON(); - } else { - parsedData = JSON.parse(userDataElement.value); - } + parsedData = JSON.parse(userDataElement.value); } catch (e) {} if (!(groupName in parsedData)) { @@ -393,7 +389,7 @@ function createJSONEditor() { alert('JSON editor:' + e) }, onChange: function() { - var currentJSONString = editor.getText(); + var currentJSONString = editor.getText(); if (currentJSONString === '{"":""}') { return; @@ -493,7 +489,10 @@ function bindAllNonJSONEditorElements() { if (e.target.id === "userdata-new-editor" || e.target.id === "userdata-clear") { return; } else { - saveJSONUserData(true); + if ($('#userdata-editor').css('height') !== "0px") { + saveJSONUserData(true); + + } } }) } @@ -703,7 +702,7 @@ function loaded() { if (data.type == "update") { if (data.selections.length == 0) { - if (editor !== null && lastEntityID!==null) { + if (editor !== null && lastEntityID !== null) { saveJSONUserData(true); deleteJSONEditor(); } @@ -745,7 +744,7 @@ function loaded() { } else { properties = data.selections[0].properties; - if (lastEntityID !== properties.id && lastEntityID!==null && editor!==null) { + if (lastEntityID !== properties.id && lastEntityID !== null && editor !== null) { saveJSONUserData(true); } @@ -843,11 +842,8 @@ function loaded() { hideUserDataTextArea(); var json = null; try { - json = JSON.parse(properties.userData) - //its json - if (Object.keys(json).length === 0 && json.constructor === Object) { - //it's an empty object - } + json = JSON.parse(properties.userData); + if (editor === null) { createJSONEditor(); } From 5305ec7febd9e418a9ed9ccab9802c356742687c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 24 Aug 2016 14:51:15 -0700 Subject: [PATCH 255/332] html beautify --- scripts/system/html/entityProperties.html | 1112 ++++++++++----------- 1 file changed, 539 insertions(+), 573 deletions(-) diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index dfa87f86ad..26c7f53997 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -7,588 +7,554 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html --> - - - Properties - - - - - - - - - - - - - - -
-
-
- -
-
- - -
-
- - -
-
-
- -
- - -
-
- - -
- -
- -
-
- - - - Saved! -
-
-
- -
-
- - -
- - - - - - -
- M -
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
- - - -
-
-
-
- - -
-
- - -
-
-
-
- -
-
-
- - -
-
-
-
-
- -
-
-
-
-
-
- -
- - -
-
- - -
-
- - -
- - -
- M -
-
- -
-
-
-
-
-
-
- - -
-
-
- -
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- - -
- M -
-
- - -
-
- - -
- -
-
-
- Collides With + + Properties + + + + + + + + + + + + + +
+
+
+
-
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
-
-
-
-
- Grabbing -
-
-
- - -
- -
- - -
- -
- - -
-
-
- -
- -
-
- - -
-
- - -
-
-
- - - -
- - -
- M -
-
- - -
- -
- - -
-
-
- - -
- -
-
- - + +
-
- - -
-
- - -
-
- - +
+ +
-
-
- - +
+ +
+ + +
+
+ + +
+ +
+ +
+
+ + + + Saved! +
+
+
+ +
+
+ + +
+ + +
+ M +
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+ + + +
+
+
+
+ +
- - + +
-
- - +
+
+
+ +
+
+
+ + +
+
+
+
+
+ +
+
+
+
+
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ M +
+
+ +
+
+
+
+
+
+
+ + +
+
+
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ M +
+
+ + +
+
+ + +
+
+
+
+ Collides With +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ Grabbing +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+ + +
+
+ + +
+
+
+ + + +
+
+ M +
+
+ + +
+ +
+ + +
+
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + +
+
+ + +
+
+ M +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ M +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+
+ +
+ +
+ +
+
+
+ +
+
+
+
+
+
+
+ + +
+
+ M +
+
+ + +
+
+ M +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
-
-
- - -
-
- - -
- -
- M -
-
- - -
-
- - -
-
- - -
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
-
-
- - -
- M -
-
- - -
-
- - -
-
- - -
- -
- -
-
-
- -
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
- - -
- -
- -
-
-
-
-
-
-
-
-
- - -
- -
-
-
-
-
-
-
- -
- -
- - -
- -
-
-
- -
-
-
-
-
-
-
- - -
- - -
- M -
-
- - -
- - -
- M -
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - + + \ No newline at end of file From af2fb5d7ed4f816d4755cfba4ace8a9c661411e5 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 24 Aug 2016 14:54:00 -0700 Subject: [PATCH 256/332] beautify more html --- scripts/system/html/entityList.html | 112 +++++++++++++------------- scripts/system/html/gridControls.html | 112 ++++++++++++-------------- 2 files changed, 107 insertions(+), 117 deletions(-) diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 2088898613..b19a55b5a2 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -7,73 +7,71 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html --> - - - - - - - - - - -
- -
- - -
- - -
- -
-
- Y -
- - + + + + + + + + + +
+ +
+ +
+ +
-
- - +
+
+ Y +
+ + +
+
+
+
+ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - -
TypeNameFile
TypeName
URL
??
TypeNameFile
TypeName
URL
??
-
- No entities found within a 100 meter radius. Try moving to a different location and refreshing. + + +
+ No entities found within a 100 meter radius. Try moving to a different location and refreshing. +
-
- - + + \ No newline at end of file diff --git a/scripts/system/html/gridControls.html b/scripts/system/html/gridControls.html index f7f206d702..cd646fed51 100644 --- a/scripts/system/html/gridControls.html +++ b/scripts/system/html/gridControls.html @@ -7,67 +7,59 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html --> - - - - - - - - - - - - -
- -
- -
- -
- - -
- -
- - -
- -
-
- - + + + + + + + + + + + +
+
+
-
- - +
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ +
+
+
+
+
+
+
+ + + +
- -
- - -
- -
-
- -
-
-
-
-
-
- -
- - - - -
-
- - + + \ No newline at end of file From fce207ba5daf9d2219d5edb895f1099b2ab07f92 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 24 Aug 2016 15:31:13 -0700 Subject: [PATCH 257/332] eslint fix --- scripts/system/marketplace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js index 11b5a084cf..e4f439f30d 100644 --- a/scripts/system/marketplace.js +++ b/scripts/system/marketplace.js @@ -98,5 +98,5 @@ marketplaceWindow.visibleChanged.connect(onMarketplaceWindowVisibilityChanged); Script.scriptEnding.connect(function () { toolBar.removeButton("marketplace"); browseExamplesButton.clicked.disconnect(onClick); - marketplaceWindow.visibleChanged.disconnect(onExamplesWindowVisibilityChanged); + marketplaceWindow.visibleChanged.disconnect(onMarketplaceWindowVisibilityChanged); }); From 1e95e067c029cb26b8e6de76163788e1cbd81fc9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 24 Aug 2016 15:46:55 -0700 Subject: [PATCH 258/332] warning fixes for llvm/gcc --- libraries/animation/src/AnimInverseKinematics.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index b516e56750..7985251002 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -146,7 +146,6 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector maxError) { maxError = error; - HACK_MAX_ERROR_JOINT = targets[i].getIndex(); } } } From c5628b615c7939e40a0d108b2d5edfd1482cec80 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 24 Aug 2016 16:05:22 -0700 Subject: [PATCH 259/332] Make sure we start fade in once an item becomes ready --- libraries/render-utils/src/MeshPartPayload.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 63082a8995..1c8c89d6db 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -533,10 +533,21 @@ void ModelMeshPartPayload::startFade() { void ModelMeshPartPayload::render(RenderArgs* args) const { PerformanceTimer perfTimer("ModelMeshPartPayload::render"); - if (!_model->_readyWhenAdded || !_model->_isVisible || !_hasStartedFade) { + if (!_model->_readyWhenAdded || !_model->_isVisible) { return; // bail asap } + // If we didn't start the fade in, check if we are ready to now.... + if (!_hasStartedFade && _model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { + const_cast(*this).startFade(); + } + + // If we still didn't start the fade in, bail + if (!_hasStartedFade) { + return; + } + + // When an individual mesh parts like this finishes its fade, we will mark the Model as // having render items that need updating bool nextIsFading = _isFading ? isStillFading() : false; From c234f1b65deaa5a035a4a88bce395ee389d1ec9c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 24 Aug 2016 16:26:53 -0700 Subject: [PATCH 260/332] marketplace.js: merge fix for WebTablet.js path --- scripts/system/marketplaces/marketplace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/marketplaces/marketplace.js b/scripts/system/marketplaces/marketplace.js index bb036f92d8..2bd6033e62 100644 --- a/scripts/system/marketplaces/marketplace.js +++ b/scripts/system/marketplaces/marketplace.js @@ -9,7 +9,7 @@ // /* global WebTablet */ -Script.include("./libraries/WebTablet.js"); +Script.include("../libraries/WebTablet.js"); var toolIconUrl = Script.resolvePath("../assets/images/tools/"); From 5cdb30357f180f6b9fd7bbb66d1fa81cc3fe969c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 24 Aug 2016 18:03:56 -0700 Subject: [PATCH 261/332] Add checking for failed downloads in the textures --- .../src/model-networking/ModelCache.cpp | 17 +++++++++++++++-- libraries/networking/src/ResourceCache.h | 3 +++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 3b7092ce8d..306a19c308 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -330,9 +330,22 @@ bool Geometry::areTexturesLoaded() const { if (!_areTexturesLoaded) { for (auto& material : _materials) { // Check if material textures are loaded - if (std::any_of(material->_textures.cbegin(), material->_textures.cend(), - [](const NetworkMaterial::Textures::value_type& it) { return it.texture && !it.texture->isLoaded(); })) { + bool materialMissingTexture = std::any_of(material->_textures.cbegin(), material->_textures.cend(), + [](const NetworkMaterial::Textures::value_type& it) { + auto texture = it.texture; + if (!texture) { + return false; + } + // Failed texture downloads need to be considered as 'loaded' + // or the object will never fade in + bool finished = texture->isLoaded() || texture->isFailed(); + if (!finished) { + return true; + } + return false; + }); + if (materialMissingTexture) { return false; } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 11b80ca349..a2a5b4cbbe 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -282,6 +282,9 @@ public: /// Checks whether the resource has loaded. virtual bool isLoaded() const { return _loaded; } + /// Checks whether the resource has failed to download. + virtual bool isFailed() const { return _failedToLoad; } + /// For loading resources, returns the number of bytes received. qint64 getBytesReceived() const { return _bytesReceived; } From e086792eac89af80dd7a48131b1cf1117fb785ca Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 12 Jul 2016 17:09:33 -0700 Subject: [PATCH 262/332] enforce coding standards --- libraries/physics/src/ShapeManager.cpp | 4 ++-- libraries/physics/src/ShapeManager.h | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 4fa660239c..35046adcfd 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -30,13 +30,13 @@ ShapeManager::~ShapeManager() { btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { if (info.getType() == SHAPE_TYPE_NONE) { - return NULL; + return nullptr; } const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube if (4.0f * glm::length2(info.getHalfExtents()) < MIN_SHAPE_DIAGONAL_SQUARED) { // tiny shapes are not supported // qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal; - return NULL; + return nullptr; } DoubleHashKey key = info.getHash(); diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index 0c411f5f62..cdb2b78789 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -43,11 +43,12 @@ public: private: bool releaseShapeByKey(const DoubleHashKey& key); - struct ShapeReference { + class ShapeReference { + public: int refCount; btCollisionShape* shape; DoubleHashKey key; - ShapeReference() : refCount(0), shape(NULL) {} + ShapeReference() : refCount(0), shape(nullptr) {} }; btHashMap _shapeMap; From af1be8ccd4e6a1a309d9ceb613f037eba3d24657 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 12 Jul 2016 17:26:17 -0700 Subject: [PATCH 263/332] remove whitespace --- libraries/shared/src/DoubleHashKey.cpp | 6 +++--- libraries/shared/src/DoubleHashKey.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/shared/src/DoubleHashKey.cpp b/libraries/shared/src/DoubleHashKey.cpp index 794604d21b..ded2f073eb 100644 --- a/libraries/shared/src/DoubleHashKey.cpp +++ b/libraries/shared/src/DoubleHashKey.cpp @@ -12,7 +12,7 @@ #include "DoubleHashKey.h" const uint32_t NUM_PRIMES = 64; -const uint32_t PRIMES[] = { +const uint32_t PRIMES[] = { 4194301U, 4194287U, 4194277U, 4194271U, 4194247U, 4194217U, 4194199U, 4194191U, 4194187U, 4194181U, 4194173U, 4194167U, 4194143U, 4194137U, 4194131U, 4194107U, 4194103U, 4194023U, 4194011U, 4194007U, 4193977U, 4193971U, 4193963U, 4193957U, @@ -27,8 +27,8 @@ uint32_t DoubleHashKey::hashFunction(uint32_t value, uint32_t primeIndex) { uint32_t hash = PRIMES[primeIndex % NUM_PRIMES] * (value + 1U); hash += ~(hash << 15); hash ^= (hash >> 10); - hash += (hash << 3); - hash ^= (hash >> 6); + hash += (hash << 3); + hash ^= (hash >> 6); hash += ~(hash << 11); return hash ^ (hash >> 16); } diff --git a/libraries/shared/src/DoubleHashKey.h b/libraries/shared/src/DoubleHashKey.h index 3b08bf7c1a..ca92a7197f 100644 --- a/libraries/shared/src/DoubleHashKey.h +++ b/libraries/shared/src/DoubleHashKey.h @@ -22,9 +22,9 @@ public: DoubleHashKey() : _hash(0), _hash2(0) { } - DoubleHashKey(uint32_t value, uint32_t primeIndex = 0) : - _hash(hashFunction(value, primeIndex)), - _hash2(hashFunction2(value)) { + DoubleHashKey(uint32_t value, uint32_t primeIndex = 0) : + _hash(hashFunction(value, primeIndex)), + _hash2(hashFunction2(value)) { } void clear() { _hash = 0; _hash2 = 0; } From 9f26836b436d5d31428650216be7ae43abce0e41 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 13 Jul 2016 14:03:53 -0700 Subject: [PATCH 264/332] added basic CollisionGeometryCache container --- .../physics/src/CollisionGeometryCache.cpp | 79 +++++++++++++++++++ .../physics/src/CollisionGeometryCache.h | 62 +++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 libraries/physics/src/CollisionGeometryCache.cpp create mode 100644 libraries/physics/src/CollisionGeometryCache.h diff --git a/libraries/physics/src/CollisionGeometryCache.cpp b/libraries/physics/src/CollisionGeometryCache.cpp new file mode 100644 index 0000000000..b474d44c32 --- /dev/null +++ b/libraries/physics/src/CollisionGeometryCache.cpp @@ -0,0 +1,79 @@ +// +// CollisionGeometryCache.cpp +// libraries/physcis/src +// +// Created by Andrew Meadows 2016.07.13 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +//#include + +//#include "ShapeFactory.h" +#include "CollisionGeometryCache.h" + +int foo = 0; + +GeometryPointer createGeometryFromShape(CollisionGeometryCache::Key key) { + return std::make_shared(++foo); +} + +CollisionGeometryCache::CollisionGeometryCache() { +} + +CollisionGeometryCache::~CollisionGeometryCache() { + _geometryMap.clear(); + _pendingGarbage.clear(); +} + +GeometryPointer CollisionGeometryCache::getGeometry(CollisionGeometryCache::Key key) { + if (!key) { + return GeometryPointer(); + } + GeometryPointer geometry = 0; + + CollisionGeometryMap::const_iterator itr = _geometryMap.find(key); + if (itr != _geometryMap.end()) { + // make geometry and add it to map + geometry = createGeometryFromShape(key); + if (geometry) { + _geometryMap.insert(std::make_pair(key, geometry)); + } + } + return geometry; +} + +bool CollisionGeometryCache::releaseGeometry(CollisionGeometryCache::Key key) { + if (!key) { + return false; + } + CollisionGeometryMap::const_iterator itr = _geometryMap.find(key); + if (itr != _geometryMap.end()) { + assert((*itr).second.use_count() != 1); + if ((*itr).second.use_count() == 2) { + // we hold all of the references inside the cache so we'll try to delete later + _pendingGarbage.push_back(key); + } + return true; + } + return false; +} + +void CollisionGeometryCache::collectGarbage() { + int numShapes = _pendingGarbage.size(); + for (int i = 0; i < numShapes; ++i) { + CollisionGeometryCache::Key key = _pendingGarbage[i]; + CollisionGeometryMap::const_iterator itr = _geometryMap.find(key); + if (itr != _geometryMap.end()) { + if ((*itr).second.use_count() == 1) { + // we hold the only reference + _geometryMap.erase(itr); + } + } + } + _pendingGarbage.clear(); +} + diff --git a/libraries/physics/src/CollisionGeometryCache.h b/libraries/physics/src/CollisionGeometryCache.h new file mode 100644 index 0000000000..f6a7a6f3e8 --- /dev/null +++ b/libraries/physics/src/CollisionGeometryCache.h @@ -0,0 +1,62 @@ +// +// CollisionGeometryCache.h +// libraries/physcis/src +// +// Created by Andrew Meadows 2016.07.13 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_CollisionGeometryCache +#define hifi_CollisionGeometryCache + +#include +#include +#include +//#include +//#include + +class btCollisionShape; + +// BEGIN TEST HACK +using GeometryPointer = std::shared_ptr; +// END TEST HACK + +namespace std { + template <> + struct hash { + std::size_t operator()(btCollisionShape* key) const { + return (hash()((void*)key)); + } + }; +} + +class CollisionGeometryCache { +public: + using Key = btCollisionShape const *; + + CollisionGeometryCache(); + ~CollisionGeometryCache(); + + /// \return pointer to geometry + GeometryPointer getGeometry(Key key); + + /// \return true if geometry was found and released + bool releaseGeometry(Key key); + + /// delete geometries that have zero references + void collectGarbage(); + + // validation methods + uint32_t getNumGeometries() const { return (uint32_t)_geometryMap.size(); } + bool hasGeometry(Key key) const { return _geometryMap.find(key) == _geometryMap.end(); } + +private: + using CollisionGeometryMap = std::unordered_map; + CollisionGeometryMap _geometryMap; + std::vector _pendingGarbage; +}; + +#endif // hifi_CollisionGeometryCache From 8d3f592e68b6c435531c0038dd55351467ab378c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 13 Jul 2016 15:17:50 -0700 Subject: [PATCH 265/332] rename class to be more correct --- ...Cache.cpp => CollisionRenderMeshCache.cpp} | 30 +++++++++---------- ...etryCache.h => CollisionRenderMeshCache.h} | 28 ++++++++--------- 2 files changed, 28 insertions(+), 30 deletions(-) rename libraries/physics/src/{CollisionGeometryCache.cpp => CollisionRenderMeshCache.cpp} (62%) rename libraries/physics/src/{CollisionGeometryCache.h => CollisionRenderMeshCache.h} (60%) diff --git a/libraries/physics/src/CollisionGeometryCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp similarity index 62% rename from libraries/physics/src/CollisionGeometryCache.cpp rename to libraries/physics/src/CollisionRenderMeshCache.cpp index b474d44c32..14d02951a1 100644 --- a/libraries/physics/src/CollisionGeometryCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -1,5 +1,5 @@ // -// CollisionGeometryCache.cpp +// CollisionRenderMeshCache.cpp // libraries/physcis/src // // Created by Andrew Meadows 2016.07.13 @@ -13,32 +13,32 @@ //#include //#include "ShapeFactory.h" -#include "CollisionGeometryCache.h" +#include "CollisionRenderMeshCache.h" int foo = 0; -GeometryPointer createGeometryFromShape(CollisionGeometryCache::Key key) { +MeshPointer createMeshFromShape(CollisionRenderMeshCache::Key key) { return std::make_shared(++foo); } -CollisionGeometryCache::CollisionGeometryCache() { +CollisionRenderMeshCache::CollisionRenderMeshCache() { } -CollisionGeometryCache::~CollisionGeometryCache() { +CollisionRenderMeshCache::~CollisionRenderMeshCache() { _geometryMap.clear(); _pendingGarbage.clear(); } -GeometryPointer CollisionGeometryCache::getGeometry(CollisionGeometryCache::Key key) { +MeshPointer CollisionRenderMeshCache::getMesh(CollisionRenderMeshCache::Key key) { if (!key) { - return GeometryPointer(); + return MeshPointer(); } - GeometryPointer geometry = 0; + MeshPointer geometry = 0; - CollisionGeometryMap::const_iterator itr = _geometryMap.find(key); + CollisionMeshMap::const_iterator itr = _geometryMap.find(key); if (itr != _geometryMap.end()) { // make geometry and add it to map - geometry = createGeometryFromShape(key); + geometry = createMeshFromShape(key); if (geometry) { _geometryMap.insert(std::make_pair(key, geometry)); } @@ -46,11 +46,11 @@ GeometryPointer CollisionGeometryCache::getGeometry(CollisionGeometryCache::Key return geometry; } -bool CollisionGeometryCache::releaseGeometry(CollisionGeometryCache::Key key) { +bool CollisionRenderMeshCache::releaseMesh(CollisionRenderMeshCache::Key key) { if (!key) { return false; } - CollisionGeometryMap::const_iterator itr = _geometryMap.find(key); + CollisionMeshMap::const_iterator itr = _geometryMap.find(key); if (itr != _geometryMap.end()) { assert((*itr).second.use_count() != 1); if ((*itr).second.use_count() == 2) { @@ -62,11 +62,11 @@ bool CollisionGeometryCache::releaseGeometry(CollisionGeometryCache::Key key) { return false; } -void CollisionGeometryCache::collectGarbage() { +void CollisionRenderMeshCache::collectGarbage() { int numShapes = _pendingGarbage.size(); for (int i = 0; i < numShapes; ++i) { - CollisionGeometryCache::Key key = _pendingGarbage[i]; - CollisionGeometryMap::const_iterator itr = _geometryMap.find(key); + CollisionRenderMeshCache::Key key = _pendingGarbage[i]; + CollisionMeshMap::const_iterator itr = _geometryMap.find(key); if (itr != _geometryMap.end()) { if ((*itr).second.use_count() == 1) { // we hold the only reference diff --git a/libraries/physics/src/CollisionGeometryCache.h b/libraries/physics/src/CollisionRenderMeshCache.h similarity index 60% rename from libraries/physics/src/CollisionGeometryCache.h rename to libraries/physics/src/CollisionRenderMeshCache.h index f6a7a6f3e8..3b39ef80f8 100644 --- a/libraries/physics/src/CollisionGeometryCache.h +++ b/libraries/physics/src/CollisionRenderMeshCache.h @@ -1,5 +1,5 @@ // -// CollisionGeometryCache.h +// CollisionRenderMeshCache.h // libraries/physcis/src // // Created by Andrew Meadows 2016.07.13 @@ -9,19 +9,17 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_CollisionGeometryCache -#define hifi_CollisionGeometryCache +#ifndef hifi_CollisionRenderMeshCache_h +#define hifi_CollisionRenderMeshCache_h #include #include #include -//#include -//#include class btCollisionShape; // BEGIN TEST HACK -using GeometryPointer = std::shared_ptr; +using MeshPointer = std::shared_ptr; // END TEST HACK namespace std { @@ -33,30 +31,30 @@ namespace std { }; } -class CollisionGeometryCache { +class CollisionRenderMeshCache { public: using Key = btCollisionShape const *; - CollisionGeometryCache(); - ~CollisionGeometryCache(); + CollisionRenderMeshCache(); + ~CollisionRenderMeshCache(); /// \return pointer to geometry - GeometryPointer getGeometry(Key key); + MeshPointer getMesh(Key key); /// \return true if geometry was found and released - bool releaseGeometry(Key key); + bool releaseMesh(Key key); /// delete geometries that have zero references void collectGarbage(); // validation methods uint32_t getNumGeometries() const { return (uint32_t)_geometryMap.size(); } - bool hasGeometry(Key key) const { return _geometryMap.find(key) == _geometryMap.end(); } + bool hasMesh(Key key) const { return _geometryMap.find(key) == _geometryMap.end(); } private: - using CollisionGeometryMap = std::unordered_map; - CollisionGeometryMap _geometryMap; + using CollisionMeshMap = std::unordered_map; + CollisionMeshMap _geometryMap; std::vector _pendingGarbage; }; -#endif // hifi_CollisionGeometryCache +#endif // hifi_CollisionRenderMeshCache_h From d59c997e64b994b2b32d928d2c8be36e06ceffc7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 13 Jul 2016 16:32:22 -0700 Subject: [PATCH 266/332] remove stubbery, make physics lib depend on model --- libraries/model/src/model/Geometry.h | 2 +- libraries/physics/CMakeLists.txt | 2 +- .../physics/src/CollisionRenderMeshCache.cpp | 47 ++++++++++++------- .../physics/src/CollisionRenderMeshCache.h | 10 ++-- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index a3e95c68c0..4256f0be03 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -130,7 +130,7 @@ protected: void evalVertexStream(); }; -typedef std::shared_ptr< Mesh > MeshPointer; +using MeshPointer = std::shared_ptr< Mesh >; class Geometry { diff --git a/libraries/physics/CMakeLists.txt b/libraries/physics/CMakeLists.txt index b734c9ac2e..7733c019e0 100644 --- a/libraries/physics/CMakeLists.txt +++ b/libraries/physics/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME physics) setup_hifi_library() -link_hifi_libraries(shared fbx entities) +link_hifi_libraries(shared fbx entities model) target_bullet() diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index 14d02951a1..e563c05e90 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -9,16 +9,31 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "CollisionRenderMeshCache.h" + #include //#include //#include "ShapeFactory.h" -#include "CollisionRenderMeshCache.h" +#include -int foo = 0; -MeshPointer createMeshFromShape(CollisionRenderMeshCache::Key key) { - return std::make_shared(++foo); +model::MeshPointer createMeshFromShape(const btCollisionShape* shape) { + if (!shape) { + return std::make_shared(); + } + int32_t shapeType = shape->getShapeType(); + if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE) { + const btCompoundShape* compoundShape = static_cast(shape); + int32_t numSubShapes = compoundShape->getNumChildShapes(); + for (int i = 0; i < numSubShapes; ++i) { + const btCollisionShape* childShape = compoundShape->getChildShape(i); + std::cout << "adebug " << i << " " << (void*)(childShape) << std::endl; // adebug + } + } else if (shape->isConvex()) { + std::cout << "adebug " << (void*)(shape)<< std::endl; // adebug + } + return std::make_shared(); } CollisionRenderMeshCache::CollisionRenderMeshCache() { @@ -29,21 +44,19 @@ CollisionRenderMeshCache::~CollisionRenderMeshCache() { _pendingGarbage.clear(); } -MeshPointer CollisionRenderMeshCache::getMesh(CollisionRenderMeshCache::Key key) { - if (!key) { - return MeshPointer(); - } - MeshPointer geometry = 0; - - CollisionMeshMap::const_iterator itr = _geometryMap.find(key); - if (itr != _geometryMap.end()) { - // make geometry and add it to map - geometry = createMeshFromShape(key); - if (geometry) { - _geometryMap.insert(std::make_pair(key, geometry)); +model::MeshPointer CollisionRenderMeshCache::getMesh(CollisionRenderMeshCache::Key key) { + model::MeshPointer mesh; + if (key) { + CollisionMeshMap::const_iterator itr = _geometryMap.find(key); + if (itr != _geometryMap.end()) { + // make mesh and add it to map + mesh = createMeshFromShape(key); + if (mesh) { + _geometryMap.insert(std::make_pair(key, mesh)); + } } } - return geometry; + return mesh; } bool CollisionRenderMeshCache::releaseMesh(CollisionRenderMeshCache::Key key) { diff --git a/libraries/physics/src/CollisionRenderMeshCache.h b/libraries/physics/src/CollisionRenderMeshCache.h index 3b39ef80f8..03083048d8 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.h +++ b/libraries/physics/src/CollisionRenderMeshCache.h @@ -16,11 +16,9 @@ #include #include -class btCollisionShape; +#include -// BEGIN TEST HACK -using MeshPointer = std::shared_ptr; -// END TEST HACK +class btCollisionShape; namespace std { template <> @@ -39,7 +37,7 @@ public: ~CollisionRenderMeshCache(); /// \return pointer to geometry - MeshPointer getMesh(Key key); + model::MeshPointer getMesh(Key key); /// \return true if geometry was found and released bool releaseMesh(Key key); @@ -52,7 +50,7 @@ public: bool hasMesh(Key key) const { return _geometryMap.find(key) == _geometryMap.end(); } private: - using CollisionMeshMap = std::unordered_map; + using CollisionMeshMap = std::unordered_map; CollisionMeshMap _geometryMap; std::vector _pendingGarbage; }; From d0295f3876204e00bd3677c53e82497636e68e20 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 14 Jul 2016 11:43:23 -0700 Subject: [PATCH 267/332] remove cruft include --- libraries/physics/src/ShapeFactory.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 67072d46d4..8576a9caee 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -10,7 +10,6 @@ // #include -#include #include // for MILLIMETERS_PER_METER From 1e95e489cba359b80dd4566bf579a419b323ba13 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 14 Jul 2016 11:43:33 -0700 Subject: [PATCH 268/332] make mesh around btConvexShape, copy to model::Mesh --- .../physics/src/CollisionRenderMeshCache.cpp | 59 +++++++++++++++++-- .../physics/src/CollisionRenderMeshCache.h | 2 +- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index e563c05e90..c7ce892e52 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -12,28 +12,77 @@ #include "CollisionRenderMeshCache.h" #include -//#include -//#include "ShapeFactory.h" #include +#include +#include // for MAX_HULL_POINTS + +float verts[3 * MAX_HULL_POINTS]; + +void copyHullToMesh(const btShapeHull& hull, model::MeshPointer mesh) { + if ((bool)mesh) { + const uint32_t* hullIndices = hull.getIndexPointer(); + int32_t numIndices = hull.numIndices(); + assert(numIndices <= 6 * MAX_HULL_POINTS); + + { // new part + model::Mesh::Part part; + part._startIndex = mesh->getIndexBuffer().getNumElements(); + part._numIndices = (model::Index)numIndices; + part._baseVertex = mesh->getVertexBuffer().getNumElements(); + + gpu::BufferView::Size numBytes = sizeof(model::Mesh::Part); + const gpu::Byte* data = reinterpret_cast(&part); + mesh->getPartBuffer()._buffer->append(numBytes, data); + } + + { // new vertices + const btVector3* hullVertices = hull.getVertexPointer(); + int32_t numVertices = hull.numVertices(); + assert(numVertices <= MAX_HULL_POINTS); + for (int32_t i = 0; i < numVertices; ++i) { + float* data = verts + 3 * i; + data[0] = hullVertices[i].getX(); + data[1] = hullVertices[i].getY(); + data[2] = hullVertices[i].getZ(); + } + + gpu::BufferView::Size numBytes = sizeof(float) * (3 * numVertices); + const gpu::Byte* data = reinterpret_cast(verts); + mesh->getVertexBuffer()._buffer->append(numBytes, data); + } + + { // new indices + gpu::BufferView::Size numBytes = (gpu::BufferView::Size)(sizeof(uint32_t) * hull.numIndices()); + const gpu::Byte* data = reinterpret_cast(hullIndices); + mesh->getIndexBuffer()._buffer->append(numBytes, data); + } + } +} model::MeshPointer createMeshFromShape(const btCollisionShape* shape) { if (!shape) { return std::make_shared(); } + model::MeshPointer mesh = std::make_shared(); int32_t shapeType = shape->getShapeType(); if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE) { + const btScalar MARGIN = 0.0f; const btCompoundShape* compoundShape = static_cast(shape); int32_t numSubShapes = compoundShape->getNumChildShapes(); for (int i = 0; i < numSubShapes; ++i) { const btCollisionShape* childShape = compoundShape->getChildShape(i); - std::cout << "adebug " << i << " " << (void*)(childShape) << std::endl; // adebug + if (childShape->isConvex()) { + const btConvexShape* convexShape = static_cast(childShape); + btShapeHull shapeHull(convexShape); + shapeHull.buildHull(MARGIN); + copyHullToMesh(shapeHull, mesh); + } } } else if (shape->isConvex()) { - std::cout << "adebug " << (void*)(shape)<< std::endl; // adebug } - return std::make_shared(); + return mesh; } CollisionRenderMeshCache::CollisionRenderMeshCache() { diff --git a/libraries/physics/src/CollisionRenderMeshCache.h b/libraries/physics/src/CollisionRenderMeshCache.h index 03083048d8..05da3750c3 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.h +++ b/libraries/physics/src/CollisionRenderMeshCache.h @@ -31,7 +31,7 @@ namespace std { class CollisionRenderMeshCache { public: - using Key = btCollisionShape const *; + using Key = const btCollisionShape*; CollisionRenderMeshCache(); ~CollisionRenderMeshCache(); From 06d40afeacbaacb13de1e0b2e924481d9282b00f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 14 Jul 2016 13:30:57 -0700 Subject: [PATCH 269/332] don't forget to use the transform of child shapes --- .../physics/src/CollisionRenderMeshCache.cpp | 102 +++++++++--------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index c7ce892e52..6cf2f91ce2 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -20,67 +20,71 @@ float verts[3 * MAX_HULL_POINTS]; -void copyHullToMesh(const btShapeHull& hull, model::MeshPointer mesh) { - if ((bool)mesh) { - const uint32_t* hullIndices = hull.getIndexPointer(); - int32_t numIndices = hull.numIndices(); - assert(numIndices <= 6 * MAX_HULL_POINTS); +void copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, model::MeshPointer mesh) { + assert((bool)mesh); + assert(shape); - { // new part - model::Mesh::Part part; - part._startIndex = mesh->getIndexBuffer().getNumElements(); - part._numIndices = (model::Index)numIndices; - part._baseVertex = mesh->getVertexBuffer().getNumElements(); + btShapeHull hull(shape); + const btScalar MARGIN = 0.0f; + hull.buildHull(MARGIN); - gpu::BufferView::Size numBytes = sizeof(model::Mesh::Part); - const gpu::Byte* data = reinterpret_cast(&part); - mesh->getPartBuffer()._buffer->append(numBytes, data); + const uint32_t* hullIndices = hull.getIndexPointer(); + int32_t numIndices = hull.numIndices(); + assert(numIndices <= 6 * MAX_HULL_POINTS); + + { // new part + model::Mesh::Part part; + part._startIndex = mesh->getIndexBuffer().getNumElements(); + part._numIndices = (model::Index)numIndices; + part._baseVertex = mesh->getVertexBuffer().getNumElements(); + + gpu::BufferView::Size numBytes = sizeof(model::Mesh::Part); + const gpu::Byte* data = reinterpret_cast(&part); + mesh->getPartBuffer()._buffer->append(numBytes, data); + } + + { // new vertices + const btVector3* hullVertices = hull.getVertexPointer(); + int32_t numVertices = hull.numVertices(); + assert(numVertices <= MAX_HULL_POINTS); + for (int32_t i = 0; i < numVertices; ++i) { + btVector3 transformedPoint = transform * hullVertices[i]; + memcpy(transformedPoint.m_floats, verts + 3 * i, 3 * sizeof(float)); + //data[0] = transformedPoint.getX(); + //data[1] = transformedPoint.getY(); + //data[2] = transformedPoint.getZ(); } - { // new vertices - const btVector3* hullVertices = hull.getVertexPointer(); - int32_t numVertices = hull.numVertices(); - assert(numVertices <= MAX_HULL_POINTS); - for (int32_t i = 0; i < numVertices; ++i) { - float* data = verts + 3 * i; - data[0] = hullVertices[i].getX(); - data[1] = hullVertices[i].getY(); - data[2] = hullVertices[i].getZ(); - } + gpu::BufferView::Size numBytes = sizeof(float) * (3 * numVertices); + const gpu::Byte* data = reinterpret_cast(verts); + mesh->getVertexBuffer()._buffer->append(numBytes, data); + } - gpu::BufferView::Size numBytes = sizeof(float) * (3 * numVertices); - const gpu::Byte* data = reinterpret_cast(verts); - mesh->getVertexBuffer()._buffer->append(numBytes, data); - } - - { // new indices - gpu::BufferView::Size numBytes = (gpu::BufferView::Size)(sizeof(uint32_t) * hull.numIndices()); - const gpu::Byte* data = reinterpret_cast(hullIndices); - mesh->getIndexBuffer()._buffer->append(numBytes, data); - } + { // new indices + gpu::BufferView::Size numBytes = (gpu::BufferView::Size)(sizeof(uint32_t) * hull.numIndices()); + const gpu::Byte* data = reinterpret_cast(hullIndices); + mesh->getIndexBuffer()._buffer->append(numBytes, data); } } model::MeshPointer createMeshFromShape(const btCollisionShape* shape) { - if (!shape) { - return std::make_shared(); - } model::MeshPointer mesh = std::make_shared(); - int32_t shapeType = shape->getShapeType(); - if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE) { - const btScalar MARGIN = 0.0f; - const btCompoundShape* compoundShape = static_cast(shape); - int32_t numSubShapes = compoundShape->getNumChildShapes(); - for (int i = 0; i < numSubShapes; ++i) { - const btCollisionShape* childShape = compoundShape->getChildShape(i); - if (childShape->isConvex()) { - const btConvexShape* convexShape = static_cast(childShape); - btShapeHull shapeHull(convexShape); - shapeHull.buildHull(MARGIN); - copyHullToMesh(shapeHull, mesh); + if (shape) { + int32_t shapeType = shape->getShapeType(); + if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE) { + const btCompoundShape* compoundShape = static_cast(shape); + int32_t numSubShapes = compoundShape->getNumChildShapes(); + for (int i = 0; i < numSubShapes; ++i) { + const btCollisionShape* childShape = compoundShape->getChildShape(i); + if (childShape->isConvex()) { + const btConvexShape* convexShape = static_cast(childShape); + copyShapeToMesh(compoundShape->getChildTransform(i), convexShape, mesh); + } } + } else if (shape->isConvex()) { + const btConvexShape* convexShape = static_cast(shape); + copyShapeToMesh(btTransform(), convexShape, mesh); } - } else if (shape->isConvex()) { } return mesh; } From b79af55e16f56f9b5e20a3e73e7fb6e4d87b5379 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 14 Jul 2016 13:35:03 -0700 Subject: [PATCH 270/332] fix ref accounting for garbage collection --- libraries/physics/src/CollisionRenderMeshCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index 6cf2f91ce2..3714d9d464 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -119,7 +119,7 @@ bool CollisionRenderMeshCache::releaseMesh(CollisionRenderMeshCache::Key key) { CollisionMeshMap::const_iterator itr = _geometryMap.find(key); if (itr != _geometryMap.end()) { assert((*itr).second.use_count() != 1); - if ((*itr).second.use_count() == 2) { + if ((*itr).second.use_count() == 1) { // we hold all of the references inside the cache so we'll try to delete later _pendingGarbage.push_back(key); } From e473edff4adc4b1458b6cab964ddd7c09d3af467 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 14 Jul 2016 14:04:26 -0700 Subject: [PATCH 271/332] fix shapeManagerTests --- tests/physics/src/ShapeManagerTests.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/physics/src/ShapeManagerTests.cpp b/tests/physics/src/ShapeManagerTests.cpp index c8805132fa..24d4a5ab35 100644 --- a/tests/physics/src/ShapeManagerTests.cpp +++ b/tests/physics/src/ShapeManagerTests.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "ShapeManagerTests.h" @@ -197,6 +198,7 @@ void ShapeManagerTests::addCompoundShape() { ShapeInfo::PointCollection pointCollection; int numHulls = 5; glm::vec3 offsetNormal(1.0f, 0.0f, 0.0f); + Extents extents; for (int i = 0; i < numHulls; ++i) { glm::vec3 offset = (float)(i - numHulls/2) * offsetNormal; ShapeInfo::PointList pointList; @@ -204,13 +206,16 @@ void ShapeManagerTests::addCompoundShape() { for (int j = 0; j < numHullPoints; ++j) { glm::vec3 point = radius * tetrahedron[j] + offset; pointList.push_back(point); + extents.addPoint(point); } pointCollection.push_back(pointList); } // create the ShapeInfo ShapeInfo info; - info.setPointCollection(hulls); + glm::vec3 halfExtents = 0.5f * (extents.maximum - extents.minimum); + info.setParams(SHAPE_TYPE_COMPOUND, halfExtents); + info.setPointCollection(pointCollection); // create the shape ShapeManager shapeManager; From c1216cbcaf98fedfda93be43576fcb24a5cf87f8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 14 Jul 2016 15:05:47 -0700 Subject: [PATCH 272/332] stubbery for CollisionRenderMeshCacheTests --- tests/physics/CMakeLists.txt | 2 +- .../src/CollisionRenderMeshCacheTests.cpp | 41 +++++++++++++++++++ .../src/CollisionRenderMeshCacheTests.h | 24 +++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/physics/src/CollisionRenderMeshCacheTests.cpp create mode 100644 tests/physics/src/CollisionRenderMeshCacheTests.h diff --git a/tests/physics/CMakeLists.txt b/tests/physics/CMakeLists.txt index cc3df5ea8e..755886ebbf 100644 --- a/tests/physics/CMakeLists.txt +++ b/tests/physics/CMakeLists.txt @@ -2,7 +2,7 @@ # Declare dependencies macro (SETUP_TESTCASE_DEPENDENCIES) target_bullet() - link_hifi_libraries(shared physics) + link_hifi_libraries(shared physics gpu model) package_libraries_for_deployment() endmacro () diff --git a/tests/physics/src/CollisionRenderMeshCacheTests.cpp b/tests/physics/src/CollisionRenderMeshCacheTests.cpp new file mode 100644 index 0000000000..58b45f6400 --- /dev/null +++ b/tests/physics/src/CollisionRenderMeshCacheTests.cpp @@ -0,0 +1,41 @@ +// +// CollisionRenderMeshCacheTests.cpp +// tests/physics/src +// +// Created by Andrew Meadows on 2014.10.30 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include + +#include "CollisionRenderMeshCacheTests.h" + +QTEST_MAIN(CollisionRenderMeshCacheTests) + + +void CollisionRenderMeshCacheTests::test001() { + CollisionRenderMeshCache cache; + + // create a compound shape + int32_t numSubShapes = 3; + + + // get the mesh + + // get the mesh again + + // forget the mesh once + + // collect garbage + + // forget the mesh a second time + + // collect garbage + +} + diff --git a/tests/physics/src/CollisionRenderMeshCacheTests.h b/tests/physics/src/CollisionRenderMeshCacheTests.h new file mode 100644 index 0000000000..d927bf8cab --- /dev/null +++ b/tests/physics/src/CollisionRenderMeshCacheTests.h @@ -0,0 +1,24 @@ +// +// CollisionRenderMeshCacheTests.h +// tests/physics/src +// +// Created by Andrew Meadows on 2014.10.30 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_CollisionRenderMeshCacheTests_h +#define hifi_CollisionRenderMeshCacheTests_h + +#include + +class CollisionRenderMeshCacheTests : public QObject { + Q_OBJECT + +private slots: + void test001(); +}; + +#endif // hifi_CollisionRenderMeshCacheTests_h From 0d84e6ece5c8d99ef0f8572991edcb389e9df31b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 14 Jul 2016 18:03:25 -0700 Subject: [PATCH 273/332] first CollisionRenderMeshCache unit test --- .../src/CollisionRenderMeshCacheTests.cpp | 242 +++++++++++++++++- tests/physics/src/MeshUtil.cpp | 45 ++++ tests/physics/src/MeshUtil.h | 61 +++++ 3 files changed, 343 insertions(+), 5 deletions(-) create mode 100644 tests/physics/src/MeshUtil.cpp create mode 100644 tests/physics/src/MeshUtil.h diff --git a/tests/physics/src/CollisionRenderMeshCacheTests.cpp b/tests/physics/src/CollisionRenderMeshCacheTests.cpp index 58b45f6400..b4ed49b605 100644 --- a/tests/physics/src/CollisionRenderMeshCacheTests.cpp +++ b/tests/physics/src/CollisionRenderMeshCacheTests.cpp @@ -9,20 +9,123 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include - #include "CollisionRenderMeshCacheTests.h" +#include +#include + +#include +#include + +#include +#include // for MAX_HULL_POINTS + +#include "MeshUtil.cpp" + + QTEST_MAIN(CollisionRenderMeshCacheTests) +btVector3 directions[] = { + btVector3(1.0f, 1.0f, 1.0f), + btVector3(1.0f, 1.0f, -1.0f), + btVector3(1.0f, -1.0f, 1.0f), + btVector3(1.0f, -1.0f, -1.0f), + btVector3(-1.0f, 1.0f, 1.0f), + btVector3(-1.0f, 1.0f, -1.0f), + btVector3(-1.0f, -1.0f, 1.0f), + btVector3(-1.0f, -1.0f, -1.0f) +}; +void computeCubePoints(const btVector3& center, btScalar radius, uint32_t numPoints, + btAlignedObjectArray& points) { + points.reserve(points.size() + 8); + for (uint32_t i = 0; i < 8; ++i) { + points.push_back(center + radius * directions[i]); + } +} + +float randomFloat() { + return (float)rand() / (float)RAND_MAX; +} + +btBoxShape* createRandomCubeShape() { + //const btScalar MAX_RADIUS = 3.0; + //const btScalar MIN_RADIUS = 0.5; + //btScalar radius = randomFloat() * (MAX_RADIUS - MIN_RADIUS) + MIN_RADIUS; + btScalar radius = 0.5f; + btVector3 halfExtents(radius, radius, radius); + + btBoxShape* shape = new btBoxShape(halfExtents); + return shape; +} + +void CollisionRenderMeshCacheTests::test001() { + // make a box shape + btBoxShape* box = createRandomCubeShape(); + + // wrap it with a ShapeHull + btShapeHull hull(box); + //const btScalar MARGIN = 0.01f; + const btScalar MARGIN = 0.00f; + hull.buildHull(MARGIN); + + // verify the vertex count is capped + uint32_t numVertices = (uint32_t)hull.numVertices(); + QVERIFY(numVertices <= MAX_HULL_POINTS); + + // verify the mesh is inside the radius + btVector3 halfExtents = box->getHalfExtentsWithMargin(); + btScalar acceptableRadiusError = 0.01f; + btScalar maxRadius = halfExtents.length() + acceptableRadiusError; + const btVector3* meshVertices = hull.getVertexPointer(); + for (uint32_t i = 0; i < numVertices; ++i) { + btVector3 vertex = meshVertices[i]; + QVERIFY(vertex.length() <= maxRadius); + } + + // verify the index count is capped + uint32_t numIndices = (uint32_t)hull.numIndices(); + QVERIFY(numIndices < 6 * MAX_HULL_POINTS); + + // verify the index count is a multiple of 3 + QVERIFY(numIndices % 3 == 0); + + // verify the mesh is closed + const uint32_t* meshIndices = hull.getIndexPointer(); + bool isClosed = MeshUtil::isClosedManifold(meshIndices, numIndices); + QVERIFY(isClosed); + + // verify the triangle normals are outward using right-hand-rule + const uint32_t INDICES_PER_TRIANGLE = 3; + for (uint32_t i = 0; i < numIndices; i += INDICES_PER_TRIANGLE) { + btVector3 A = meshVertices[meshIndices[i]]; + btVector3 B = meshVertices[meshIndices[i+1]]; + btVector3 C = meshVertices[meshIndices[i+2]]; + + btVector3 face = (B - A).cross(C - B); + btVector3 center = (A + B + C) / 3.0f; + QVERIFY(face.dot(center) > 0.0f); + } + + delete box; +} + +#ifdef FOO void CollisionRenderMeshCacheTests::test001() { CollisionRenderMeshCache cache; // create a compound shape - int32_t numSubShapes = 3; + btScalar radiusA = 1.0f; + btScalar radiusB = 1.5f; + btScalar radiusC = 2.75f; + + btVector3 centerA(radiusA, 0.0f, 0.0f); + btVector3 centerB(0.0f, radiusB, 0.0f); + btVector3 centerC(0.0f, 0.0f, radiusC); + + btCompoundShape compoundShape = new btCompoundShape(); + for (uint32_t i = 0; i < numSubShapes; ++i) { + } // get the mesh @@ -38,4 +141,133 @@ void CollisionRenderMeshCacheTests::test001() { // collect garbage } +#endif // FOO +/* +void CollisionRenderMeshCacheTests::addManyShapes() { + ShapeManager shapeManager; + + QVector shapes; + + int numSizes = 100; + float startSize = 1.0f; + float endSize = 99.0f; + float deltaSize = (endSize - startSize) / (float)numSizes; + ShapeInfo info; + for (int i = 0; i < numSizes; ++i) { + // make a sphere + float s = startSize + (float)i * deltaSize; + glm::vec3 scale(s, 1.23f + s, s - 0.573f); + info.setBox(0.5f * scale); + btCollisionShape* shape = shapeManager.getShape(info); + shapes.push_back(shape); + QCOMPARE(shape != nullptr, true); + + // make a box + float radius = 0.5f * s; + info.setSphere(radius); + shape = shapeManager.getShape(info); + shapes.push_back(shape); + QCOMPARE(shape != nullptr, true); + } + + // verify shape count + int numShapes = shapeManager.getNumShapes(); + QCOMPARE(numShapes, 2 * numSizes); + + // release each shape by pointer + for (int i = 0; i < numShapes; ++i) { + btCollisionShape* shape = shapes[i]; + bool success = shapeManager.releaseShape(shape); + QCOMPARE(success, true); + } + + // verify zero references + for (int i = 0; i < numShapes; ++i) { + btCollisionShape* shape = shapes[i]; + int numReferences = shapeManager.getNumReferences(shape); + QCOMPARE(numReferences, 0); + } +} + +void CollisionRenderMeshCacheTests::addBoxShape() { + ShapeInfo info; + glm::vec3 halfExtents(1.23f, 4.56f, 7.89f); + info.setBox(halfExtents); + + ShapeManager shapeManager; + btCollisionShape* shape = shapeManager.getShape(info); + + ShapeInfo otherInfo = info; + btCollisionShape* otherShape = shapeManager.getShape(otherInfo); + QCOMPARE(shape, otherShape); +} + +void CollisionRenderMeshCacheTests::addSphereShape() { + ShapeInfo info; + float radius = 1.23f; + info.setSphere(radius); + + ShapeManager shapeManager; + btCollisionShape* shape = shapeManager.getShape(info); + + ShapeInfo otherInfo = info; + btCollisionShape* otherShape = shapeManager.getShape(otherInfo); + QCOMPARE(shape, otherShape); +} + +void CollisionRenderMeshCacheTests::addCompoundShape() { + // initialize some points for generating tetrahedral convex hulls + QVector tetrahedron; + tetrahedron.push_back(glm::vec3(1.0f, 1.0f, 1.0f)); + tetrahedron.push_back(glm::vec3(1.0f, -1.0f, -1.0f)); + tetrahedron.push_back(glm::vec3(-1.0f, 1.0f, -1.0f)); + tetrahedron.push_back(glm::vec3(-1.0f, -1.0f, 1.0f)); + int numHullPoints = tetrahedron.size(); + + // compute the points of the hulls + ShapeInfo::PointCollection pointCollection; + int numHulls = 5; + glm::vec3 offsetNormal(1.0f, 0.0f, 0.0f); + for (int i = 0; i < numHulls; ++i) { + glm::vec3 offset = (float)(i - numHulls/2) * offsetNormal; + ShapeInfo::PointList pointList; + float radius = (float)(i + 1); + for (int j = 0; j < numHullPoints; ++j) { + glm::vec3 point = radius * tetrahedron[j] + offset; + pointList.push_back(point); + } + pointCollection.push_back(pointList); + } + + // create the ShapeInfo + ShapeInfo info; + info.setPointCollection(hulls); + + // create the shape + ShapeManager shapeManager; + btCollisionShape* shape = shapeManager.getShape(info); + QVERIFY(shape != nullptr); + + // verify the shape is correct type + QCOMPARE(shape->getShapeType(), (int)COMPOUND_SHAPE_PROXYTYPE); + + // verify the shape has correct number of children + btCompoundShape* compoundShape = static_cast(shape); + QCOMPARE(compoundShape->getNumChildShapes(), numHulls); + + // verify manager has only one shape + QCOMPARE(shapeManager.getNumShapes(), 1); + QCOMPARE(shapeManager.getNumReferences(info), 1); + + // release the shape + shapeManager.releaseShape(shape); + QCOMPARE(shapeManager.getNumShapes(), 1); + QCOMPARE(shapeManager.getNumReferences(info), 0); + + // collect garbage + shapeManager.collectGarbage(); + QCOMPARE(shapeManager.getNumShapes(), 0); + QCOMPARE(shapeManager.getNumReferences(info), 0); +} +*/ diff --git a/tests/physics/src/MeshUtil.cpp b/tests/physics/src/MeshUtil.cpp new file mode 100644 index 0000000000..d3eb815948 --- /dev/null +++ b/tests/physics/src/MeshUtil.cpp @@ -0,0 +1,45 @@ +// +// MeshUtil.cpp +// tests/physics/src +// +// Created by Andrew Meadows 2016.07.14 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "MeshUtil.h" + +#include + +// returns false if any edge has only one adjacent triangle +bool MeshUtil::isClosedManifold(const uint32_t* meshIndices, uint32_t numIndices) { + using EdgeList = std::unordered_map; + EdgeList edges; + + // count the triangles for each edge + const uint32_t TRIANGLE_STRIDE = 3; + for (uint32_t i = 0; i < numIndices; i += TRIANGLE_STRIDE) { + MeshUtil::TriangleEdge edge; + // the triangles indices are stored in sequential order + for (uint32_t j = 0; j < 3; ++j) { + edge.setIndices(meshIndices[i + j], meshIndices[i + ((j + 1) % 3)]); + + EdgeList::iterator edgeEntry = edges.find(edge); + if (edgeEntry == edges.end()) { + edges.insert(std::pair(edge, 1)); + } else { + edgeEntry->second += 1; + } + } + } + // scan for outside edge + for (auto& edgeEntry : edges) { + if (edgeEntry.second == 1) { + return false; + } + } + return true; +} + diff --git a/tests/physics/src/MeshUtil.h b/tests/physics/src/MeshUtil.h new file mode 100644 index 0000000000..82d33d631b --- /dev/null +++ b/tests/physics/src/MeshUtil.h @@ -0,0 +1,61 @@ +// +// MeshUtil.h +// tests/physics/src +// +// Created by Andrew Meadows 2016.07.14 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MeshUtil_h +#define hifi_MeshUtil_h + +#include + +namespace MeshUtil { + +class TriangleEdge { +public: + TriangleEdge() {} + TriangleEdge(uint32_t A, uint32_t B) { + setIndices(A, B); + } + void setIndices(uint32_t A, uint32_t B) { + if (A < B) { + _indexA = A; + _indexB = B; + } else { + _indexA = B; + _indexB = A; + } + } + bool operator==(const TriangleEdge& other) const { + return _indexA == other._indexA && _indexB == other._indexB; + } + + uint32_t getIndexA() const { return _indexA; } + uint32_t getIndexB() const { return _indexB; } +private: + uint32_t _indexA { (uint32_t)(-1) }; + uint32_t _indexB { (uint32_t)(-1) }; +}; + +bool isClosedManifold(const uint32_t* meshIndices, uint32_t numIndices); + +} // MeshUtil namespace + +namespace std { + template <> + struct hash { + std::size_t operator()(const MeshUtil::TriangleEdge& edge) const { + // use Cantor's pairing function to generate a hash of ZxZ --> Z + uint32_t ab = edge.getIndexA() + edge.getIndexB(); + return hash()((ab * (ab + 1)) / 2 + edge.getIndexB()); + } + }; +} + + +#endif // hifi_MeshUtil_h From cbacb02010d139add3bf88ce99cd51aa581191e7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 15 Jul 2016 11:28:11 -0700 Subject: [PATCH 274/332] more unit tests for CollisionRenderMeshCache --- .../physics/src/CollisionRenderMeshCache.cpp | 41 ++- .../physics/src/CollisionRenderMeshCache.h | 10 +- .../src/CollisionRenderMeshCacheTests.cpp | 342 +++++++++--------- .../src/CollisionRenderMeshCacheTests.h | 4 +- 4 files changed, 208 insertions(+), 189 deletions(-) diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index 3714d9d464..14386c6f48 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -68,9 +68,19 @@ void copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, m } model::MeshPointer createMeshFromShape(const btCollisionShape* shape) { - model::MeshPointer mesh = std::make_shared(); - if (shape) { - int32_t shapeType = shape->getShapeType(); + model::MeshPointer mesh; + if (!shape) { + return mesh; + } + + int32_t shapeType = shape->getShapeType(); + if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE || shape->isConvex()) { + // create the mesh and allocate buffers for it + mesh = std::make_shared(); + mesh->setVertexBuffer(gpu::BufferView(new gpu::Buffer(), mesh->getVertexBuffer()._element)); + mesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(), mesh->getIndexBuffer()._element)); + mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(), mesh->getPartBuffer()._element)); + if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE) { const btCompoundShape* compoundShape = static_cast(shape); int32_t numSubShapes = compoundShape->getNumChildShapes(); @@ -81,7 +91,8 @@ model::MeshPointer createMeshFromShape(const btCollisionShape* shape) { copyShapeToMesh(compoundShape->getChildTransform(i), convexShape, mesh); } } - } else if (shape->isConvex()) { + } else { + // shape is convex const btConvexShape* convexShape = static_cast(shape); copyShapeToMesh(btTransform(), convexShape, mesh); } @@ -93,20 +104,22 @@ CollisionRenderMeshCache::CollisionRenderMeshCache() { } CollisionRenderMeshCache::~CollisionRenderMeshCache() { - _geometryMap.clear(); + _meshMap.clear(); _pendingGarbage.clear(); } model::MeshPointer CollisionRenderMeshCache::getMesh(CollisionRenderMeshCache::Key key) { model::MeshPointer mesh; if (key) { - CollisionMeshMap::const_iterator itr = _geometryMap.find(key); - if (itr != _geometryMap.end()) { + CollisionMeshMap::const_iterator itr = _meshMap.find(key); + if (itr == _meshMap.end()) { // make mesh and add it to map mesh = createMeshFromShape(key); if (mesh) { - _geometryMap.insert(std::make_pair(key, mesh)); + _meshMap.insert(std::make_pair(key, mesh)); } + } else { + mesh = itr->second; } } return mesh; @@ -116,12 +129,12 @@ bool CollisionRenderMeshCache::releaseMesh(CollisionRenderMeshCache::Key key) { if (!key) { return false; } - CollisionMeshMap::const_iterator itr = _geometryMap.find(key); - if (itr != _geometryMap.end()) { + CollisionMeshMap::const_iterator itr = _meshMap.find(key); + if (itr != _meshMap.end()) { assert((*itr).second.use_count() != 1); + _pendingGarbage.push_back(key); if ((*itr).second.use_count() == 1) { // we hold all of the references inside the cache so we'll try to delete later - _pendingGarbage.push_back(key); } return true; } @@ -132,11 +145,11 @@ void CollisionRenderMeshCache::collectGarbage() { int numShapes = _pendingGarbage.size(); for (int i = 0; i < numShapes; ++i) { CollisionRenderMeshCache::Key key = _pendingGarbage[i]; - CollisionMeshMap::const_iterator itr = _geometryMap.find(key); - if (itr != _geometryMap.end()) { + CollisionMeshMap::const_iterator itr = _meshMap.find(key); + if (itr != _meshMap.end()) { if ((*itr).second.use_count() == 1) { // we hold the only reference - _geometryMap.erase(itr); + _meshMap.erase(itr); } } } diff --git a/libraries/physics/src/CollisionRenderMeshCache.h b/libraries/physics/src/CollisionRenderMeshCache.h index 05da3750c3..ad3c86562b 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.h +++ b/libraries/physics/src/CollisionRenderMeshCache.h @@ -23,8 +23,8 @@ class btCollisionShape; namespace std { template <> struct hash { - std::size_t operator()(btCollisionShape* key) const { - return (hash()((void*)key)); + std::size_t operator()(btCollisionShape* shape) const { + return (hash()((void*)shape)); } }; } @@ -46,12 +46,12 @@ public: void collectGarbage(); // validation methods - uint32_t getNumGeometries() const { return (uint32_t)_geometryMap.size(); } - bool hasMesh(Key key) const { return _geometryMap.find(key) == _geometryMap.end(); } + uint32_t getNumMeshes() const { return (uint32_t)_meshMap.size(); } + bool hasMesh(Key key) const { return _meshMap.find(key) == _meshMap.end(); } private: using CollisionMeshMap = std::unordered_map; - CollisionMeshMap _geometryMap; + CollisionMeshMap _meshMap; std::vector _pendingGarbage; }; diff --git a/tests/physics/src/CollisionRenderMeshCacheTests.cpp b/tests/physics/src/CollisionRenderMeshCacheTests.cpp index b4ed49b605..085c9a2fe3 100644 --- a/tests/physics/src/CollisionRenderMeshCacheTests.cpp +++ b/tests/physics/src/CollisionRenderMeshCacheTests.cpp @@ -25,48 +25,53 @@ QTEST_MAIN(CollisionRenderMeshCacheTests) -btVector3 directions[] = { - btVector3(1.0f, 1.0f, 1.0f), - btVector3(1.0f, 1.0f, -1.0f), - btVector3(1.0f, -1.0f, 1.0f), - btVector3(1.0f, -1.0f, -1.0f), - btVector3(-1.0f, 1.0f, 1.0f), - btVector3(-1.0f, 1.0f, -1.0f), - btVector3(-1.0f, -1.0f, 1.0f), - btVector3(-1.0f, -1.0f, -1.0f) +const float INV_SQRT_THREE = 0.577350269f; + +const uint32_t numSphereDirections = 6 + 8; +btVector3 sphereDirections[] = { + btVector3(1.0f, 0.0f, 0.0f), + btVector3(-1.0f, 0.0f, 0.0f), + btVector3(0.0f, 1.0f, 0.0f), + btVector3(0.0f, -1.0f, 0.0f), + btVector3(0.0f, 0.0f, 1.0f), + btVector3(0.0f, 0.0f, -1.0f), + btVector3(INV_SQRT_THREE, INV_SQRT_THREE, INV_SQRT_THREE), + btVector3(INV_SQRT_THREE, INV_SQRT_THREE, -INV_SQRT_THREE), + btVector3(INV_SQRT_THREE, -INV_SQRT_THREE, INV_SQRT_THREE), + btVector3(INV_SQRT_THREE, -INV_SQRT_THREE, -INV_SQRT_THREE), + btVector3(-INV_SQRT_THREE, INV_SQRT_THREE, INV_SQRT_THREE), + btVector3(-INV_SQRT_THREE, INV_SQRT_THREE, -INV_SQRT_THREE), + btVector3(-INV_SQRT_THREE, -INV_SQRT_THREE, INV_SQRT_THREE), + btVector3(-INV_SQRT_THREE, -INV_SQRT_THREE, -INV_SQRT_THREE) }; -void computeCubePoints(const btVector3& center, btScalar radius, uint32_t numPoints, - btAlignedObjectArray& points) { - points.reserve(points.size() + 8); - for (uint32_t i = 0; i < 8; ++i) { - points.push_back(center + radius * directions[i]); - } -} - float randomFloat() { - return (float)rand() / (float)RAND_MAX; + return 2.0f * ((float)rand() / (float)RAND_MAX) - 1.0f; } -btBoxShape* createRandomCubeShape() { - //const btScalar MAX_RADIUS = 3.0; - //const btScalar MIN_RADIUS = 0.5; - //btScalar radius = randomFloat() * (MAX_RADIUS - MIN_RADIUS) + MIN_RADIUS; - btScalar radius = 0.5f; - btVector3 halfExtents(radius, radius, radius); - - btBoxShape* shape = new btBoxShape(halfExtents); +btBoxShape* createBoxShape(const btVector3& extent) { + btBoxShape* shape = new btBoxShape(0.5f * extent); return shape; } -void CollisionRenderMeshCacheTests::test001() { +btConvexHullShape* createConvexHull(float radius) { + btConvexHullShape* hull = new btConvexHullShape(); + for (uint32_t i = 0; i < numSphereDirections; ++i) { + btVector3 point = radius * sphereDirections[i]; + hull->addPoint(point, false); + } + hull->recalcLocalAabb(); + return hull; +} + +void CollisionRenderMeshCacheTests::testShapeHullManifold() { // make a box shape - btBoxShape* box = createRandomCubeShape(); + btVector3 extent(1.0f, 2.0f, 3.0f); + btBoxShape* box = createBoxShape(extent); // wrap it with a ShapeHull btShapeHull hull(box); - //const btScalar MARGIN = 0.01f; - const btScalar MARGIN = 0.00f; + const float MARGIN = 0.0f; hull.buildHull(MARGIN); // verify the vertex count is capped @@ -75,8 +80,8 @@ void CollisionRenderMeshCacheTests::test001() { // verify the mesh is inside the radius btVector3 halfExtents = box->getHalfExtentsWithMargin(); - btScalar acceptableRadiusError = 0.01f; - btScalar maxRadius = halfExtents.length() + acceptableRadiusError; + float ACCEPTABLE_EXTENTS_ERROR = 0.01f; + float maxRadius = halfExtents.length() + ACCEPTABLE_EXTENTS_ERROR; const btVector3* meshVertices = hull.getVertexPointer(); for (uint32_t i = 0; i < numVertices; ++i) { btVector3 vertex = meshVertices[i]; @@ -107,167 +112,166 @@ void CollisionRenderMeshCacheTests::test001() { QVERIFY(face.dot(center) > 0.0f); } + // delete unmanaged memory delete box; } -#ifdef FOO -void CollisionRenderMeshCacheTests::test001() { - CollisionRenderMeshCache cache; +void CollisionRenderMeshCacheTests::testCompoundShape() { + uint32_t numSubShapes = 3; - // create a compound shape - btScalar radiusA = 1.0f; - btScalar radiusB = 1.5f; - btScalar radiusC = 2.75f; + btVector3 centers[] = { + btVector3(1.0f, 0.0f, 0.0f), + btVector3(0.0f, -2.0f, 0.0f), + btVector3(0.0f, 0.0f, 3.0f), + }; - btVector3 centerA(radiusA, 0.0f, 0.0f); - btVector3 centerB(0.0f, radiusB, 0.0f); - btVector3 centerC(0.0f, 0.0f, radiusC); + float radii[] = { 3.0f, 2.0f, 1.0f }; - btCompoundShape compoundShape = new btCompoundShape(); + btCompoundShape* compoundShape = new btCompoundShape(); for (uint32_t i = 0; i < numSubShapes; ++i) { + btTransform transform; + transform.setOrigin(centers[i]); + btConvexHullShape* hull = createConvexHull(radii[i]); + compoundShape->addChildShape(transform, hull); } + // create the cache + CollisionRenderMeshCache cache; + QVERIFY(cache.getNumMeshes() == 0); - // get the mesh + // get the mesh once + model::MeshPointer mesh = cache.getMesh(compoundShape); + QVERIFY((bool)mesh); + QVERIFY(cache.getNumMeshes() == 1); // get the mesh again + model::MeshPointer mesh2 = cache.getMesh(compoundShape); + QVERIFY(mesh2 == mesh); + QVERIFY(cache.getNumMeshes() == 1); // forget the mesh once + cache.releaseMesh(compoundShape); + mesh.reset(); + QVERIFY(cache.getNumMeshes() == 1); - // collect garbage + // collect garbage (should still cache mesh) + cache.collectGarbage(); + QVERIFY(cache.getNumMeshes() == 1); - // forget the mesh a second time + // forget the mesh a second time (should still cache mesh) + cache.releaseMesh(compoundShape); + mesh2.reset(); + QVERIFY(cache.getNumMeshes() == 1); - // collect garbage + // collect garbage (should no longer cache mesh) + cache.collectGarbage(); + QVERIFY(cache.getNumMeshes() == 0); + // delete unmanaged memory + for (int i = 0; i < compoundShape->getNumChildShapes(); ++i) { + delete compoundShape->getChildShape(i); + } + delete compoundShape; } -#endif // FOO -/* -void CollisionRenderMeshCacheTests::addManyShapes() { - ShapeManager shapeManager; - - QVector shapes; - - int numSizes = 100; - float startSize = 1.0f; - float endSize = 99.0f; - float deltaSize = (endSize - startSize) / (float)numSizes; - ShapeInfo info; - for (int i = 0; i < numSizes; ++i) { - // make a sphere - float s = startSize + (float)i * deltaSize; - glm::vec3 scale(s, 1.23f + s, s - 0.573f); - info.setBox(0.5f * scale); - btCollisionShape* shape = shapeManager.getShape(info); - shapes.push_back(shape); - QCOMPARE(shape != nullptr, true); - - // make a box - float radius = 0.5f * s; - info.setSphere(radius); - shape = shapeManager.getShape(info); - shapes.push_back(shape); - QCOMPARE(shape != nullptr, true); +void CollisionRenderMeshCacheTests::testMultipleShapes() { + // shapeA is compound of hulls + uint32_t numSubShapes = 3; + btVector3 centers[] = { + btVector3(1.0f, 0.0f, 0.0f), + btVector3(0.0f, -2.0f, 0.0f), + btVector3(0.0f, 0.0f, 3.0f), + }; + float radii[] = { 3.0f, 2.0f, 1.0f }; + btCompoundShape* shapeA = new btCompoundShape(); + for (uint32_t i = 0; i < numSubShapes; ++i) { + btTransform transform; + transform.setOrigin(centers[i]); + btConvexHullShape* hull = createConvexHull(radii[i]); + shapeA->addChildShape(transform, hull); } - // verify shape count - int numShapes = shapeManager.getNumShapes(); - QCOMPARE(numShapes, 2 * numSizes); - - // release each shape by pointer - for (int i = 0; i < numShapes; ++i) { - btCollisionShape* shape = shapes[i]; - bool success = shapeManager.releaseShape(shape); - QCOMPARE(success, true); + // shapeB is compound of boxes + btVector3 extents[] = { + btVector3(1.0f, 2.0f, 3.0f), + btVector3(2.0f, 3.0f, 1.0f), + btVector3(3.0f, 1.0f, 2.0f), + }; + btCompoundShape* shapeB = new btCompoundShape(); + for (uint32_t i = 0; i < numSubShapes; ++i) { + btTransform transform; + transform.setOrigin(centers[i]); + btBoxShape* box = createBoxShape(extents[i]); + shapeB->addChildShape(transform, box); } - // verify zero references - for (int i = 0; i < numShapes; ++i) { - btCollisionShape* shape = shapes[i]; - int numReferences = shapeManager.getNumReferences(shape); - QCOMPARE(numReferences, 0); + // shapeC is just a box + btVector3 extentC(7.0f, 3.0f, 5.0f); + btBoxShape* shapeC = createBoxShape(extentC); + + // create the cache + CollisionRenderMeshCache cache; + QVERIFY(cache.getNumMeshes() == 0); + + // get the meshes + model::MeshPointer meshA = cache.getMesh(shapeA); + model::MeshPointer meshB = cache.getMesh(shapeB); + model::MeshPointer meshC = cache.getMesh(shapeC); + QVERIFY((bool)meshA); + QVERIFY((bool)meshB); + QVERIFY((bool)meshC); + QVERIFY(cache.getNumMeshes() == 3); + + // get the meshes again + model::MeshPointer meshA2 = cache.getMesh(shapeA); + model::MeshPointer meshB2 = cache.getMesh(shapeB); + model::MeshPointer meshC2 = cache.getMesh(shapeC); + QVERIFY(meshA == meshA2); + QVERIFY(meshB == meshB2); + QVERIFY(meshC == meshC2); + QVERIFY(cache.getNumMeshes() == 3); + + // forget the meshes once + cache.releaseMesh(shapeA); + cache.releaseMesh(shapeB); + cache.releaseMesh(shapeC); + meshA2.reset(); + meshB2.reset(); + meshC2.reset(); + QVERIFY(cache.getNumMeshes() == 3); + + // collect garbage (should still cache mesh) + cache.collectGarbage(); + QVERIFY(cache.getNumMeshes() == 3); + + // forget again, one mesh at a time... + // shapeA... + cache.releaseMesh(shapeA); + meshA.reset(); + QVERIFY(cache.getNumMeshes() == 3); + cache.collectGarbage(); + QVERIFY(cache.getNumMeshes() == 2); + // shapeB... + cache.releaseMesh(shapeB); + meshB.reset(); + QVERIFY(cache.getNumMeshes() == 2); + cache.collectGarbage(); + QVERIFY(cache.getNumMeshes() == 1); + // shapeC... + cache.releaseMesh(shapeC); + meshC.reset(); + QVERIFY(cache.getNumMeshes() == 1); + cache.collectGarbage(); + QVERIFY(cache.getNumMeshes() == 0); + + // delete unmanaged memory + for (int i = 0; i < shapeA->getNumChildShapes(); ++i) { + delete shapeA->getChildShape(i); } -} - -void CollisionRenderMeshCacheTests::addBoxShape() { - ShapeInfo info; - glm::vec3 halfExtents(1.23f, 4.56f, 7.89f); - info.setBox(halfExtents); - - ShapeManager shapeManager; - btCollisionShape* shape = shapeManager.getShape(info); - - ShapeInfo otherInfo = info; - btCollisionShape* otherShape = shapeManager.getShape(otherInfo); - QCOMPARE(shape, otherShape); -} - -void CollisionRenderMeshCacheTests::addSphereShape() { - ShapeInfo info; - float radius = 1.23f; - info.setSphere(radius); - - ShapeManager shapeManager; - btCollisionShape* shape = shapeManager.getShape(info); - - ShapeInfo otherInfo = info; - btCollisionShape* otherShape = shapeManager.getShape(otherInfo); - QCOMPARE(shape, otherShape); -} - -void CollisionRenderMeshCacheTests::addCompoundShape() { - // initialize some points for generating tetrahedral convex hulls - QVector tetrahedron; - tetrahedron.push_back(glm::vec3(1.0f, 1.0f, 1.0f)); - tetrahedron.push_back(glm::vec3(1.0f, -1.0f, -1.0f)); - tetrahedron.push_back(glm::vec3(-1.0f, 1.0f, -1.0f)); - tetrahedron.push_back(glm::vec3(-1.0f, -1.0f, 1.0f)); - int numHullPoints = tetrahedron.size(); - - // compute the points of the hulls - ShapeInfo::PointCollection pointCollection; - int numHulls = 5; - glm::vec3 offsetNormal(1.0f, 0.0f, 0.0f); - for (int i = 0; i < numHulls; ++i) { - glm::vec3 offset = (float)(i - numHulls/2) * offsetNormal; - ShapeInfo::PointList pointList; - float radius = (float)(i + 1); - for (int j = 0; j < numHullPoints; ++j) { - glm::vec3 point = radius * tetrahedron[j] + offset; - pointList.push_back(point); - } - pointCollection.push_back(pointList); + delete shapeA; + for (int i = 0; i < shapeB->getNumChildShapes(); ++i) { + delete shapeB->getChildShape(i); } - - // create the ShapeInfo - ShapeInfo info; - info.setPointCollection(hulls); - - // create the shape - ShapeManager shapeManager; - btCollisionShape* shape = shapeManager.getShape(info); - QVERIFY(shape != nullptr); - - // verify the shape is correct type - QCOMPARE(shape->getShapeType(), (int)COMPOUND_SHAPE_PROXYTYPE); - - // verify the shape has correct number of children - btCompoundShape* compoundShape = static_cast(shape); - QCOMPARE(compoundShape->getNumChildShapes(), numHulls); - - // verify manager has only one shape - QCOMPARE(shapeManager.getNumShapes(), 1); - QCOMPARE(shapeManager.getNumReferences(info), 1); - - // release the shape - shapeManager.releaseShape(shape); - QCOMPARE(shapeManager.getNumShapes(), 1); - QCOMPARE(shapeManager.getNumReferences(info), 0); - - // collect garbage - shapeManager.collectGarbage(); - QCOMPARE(shapeManager.getNumShapes(), 0); - QCOMPARE(shapeManager.getNumReferences(info), 0); + delete shapeB; + delete shapeC; } -*/ diff --git a/tests/physics/src/CollisionRenderMeshCacheTests.h b/tests/physics/src/CollisionRenderMeshCacheTests.h index d927bf8cab..640314a2a0 100644 --- a/tests/physics/src/CollisionRenderMeshCacheTests.h +++ b/tests/physics/src/CollisionRenderMeshCacheTests.h @@ -18,7 +18,9 @@ class CollisionRenderMeshCacheTests : public QObject { Q_OBJECT private slots: - void test001(); + void testShapeHullManifold(); + void testCompoundShape(); + void testMultipleShapes(); }; #endif // hifi_CollisionRenderMeshCacheTests_h From 1ed76ae44215b3fa1ad85ee2bb554e02d6195ff0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 15 Jul 2016 14:11:40 -0700 Subject: [PATCH 275/332] added virtual ObjectMotionState::setShape() --- libraries/physics/src/ObjectMotionState.cpp | 37 +++++++++++---------- libraries/physics/src/ObjectMotionState.h | 3 +- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index f915121718..64b1e9c527 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -64,16 +64,17 @@ ShapeManager* ObjectMotionState::getShapeManager() { ObjectMotionState::ObjectMotionState(btCollisionShape* shape) : _motionType(MOTION_TYPE_STATIC), - _shape(shape), + _shape(nullptr), _body(nullptr), _mass(0.0f), _lastKinematicStep(worldSimulationStep) { + setShape(shape); } ObjectMotionState::~ObjectMotionState() { assert(!_body); - releaseShape(); + setShape(nullptr); _type = MOTIONSTATE_TYPE_INVALID; } @@ -114,13 +115,6 @@ glm::vec3 ObjectMotionState::getBodyAngularVelocity() const { return bulletToGLM(_body->getAngularVelocity()); } -void ObjectMotionState::releaseShape() { - if (_shape) { - shapeManager->releaseShape(_shape); - _shape = nullptr; - } -} - void ObjectMotionState::setMotionType(PhysicsMotionType motionType) { _motionType = motionType; } @@ -165,6 +159,15 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) { } } +void ObjectMotionState::setShape(btCollisionShape* shape) { + if (_shape != shape) { + if (_shape) { + getShapeManager()->releaseShape(_shape); + } + _shape = shape; + } +} + void ObjectMotionState::handleEasyChanges(uint32_t& flags) { if (flags & Simulation::DIRTY_POSITION) { btTransform worldTrans = _body->getWorldTransform(); @@ -265,15 +268,15 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* return true; } } - getShapeManager()->releaseShape(_shape); - if (_shape != newShape) { - _shape = newShape; - _body->setCollisionShape(_shape); - - updateCCDConfiguration(); - } else { - // huh... the shape didn't actually change, so we clear the DIRTY_SHAPE flag + if (_shape == newShape) { + // the shape didn't actually change, so we clear the DIRTY_SHAPE flag flags &= ~Simulation::DIRTY_SHAPE; + // and clear the reference we just created + getShapeManager()->releaseShape(_shape); + } else { + _body->setCollisionShape(newShape); + setShape(newShape); + updateCCDConfiguration(); } } if (flags & EASY_DIRTY_PHYSICS_FLAGS) { diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index c0c10c6f71..ca9b80a3b3 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -113,8 +113,6 @@ public: btCollisionShape* getShape() const { return _shape; } btRigidBody* getRigidBody() const { return _body; } - void releaseShape(); - virtual bool isMoving() const = 0; // These pure virtual methods must be implemented for each MotionState type @@ -157,6 +155,7 @@ protected: void updateCCDConfiguration(); void setRigidBody(btRigidBody* body); + virtual void setShape(btCollisionShape* shape); MotionStateType _type = MOTIONSTATE_TYPE_INVALID; // type of MotionState PhysicsMotionType _motionType; // type of motion: KINEMATIC, DYNAMIC, or STATIC From b2dfa49a6ffe600bb3af355259c0ea2ab6eccb10 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 15 Jul 2016 15:24:57 -0700 Subject: [PATCH 276/332] stubbery for alerting entity that shape changed --- .../entities-renderer/src/RenderableModelEntityItem.h | 2 ++ libraries/entities/src/EntityItem.h | 7 +++++++ libraries/physics/src/EntityMotionState.cpp | 8 ++++++++ libraries/physics/src/EntityMotionState.h | 7 ++++--- libraries/physics/src/ObjectMotionState.h | 2 +- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index a3d9e4db98..22469cf0ef 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -61,6 +61,8 @@ public: virtual bool isReadyToComputeShape() override; virtual void computeShapeInfo(ShapeInfo& info) override; + void setCollisionShape(const btCollisionShape* shape) override {} + virtual bool contains(const glm::vec3& point) const override; virtual bool shouldBePhysical() const override; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 9fe83b53cb..1ad1d938a7 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -44,6 +44,7 @@ class EntityTreeElementExtraEncodeData; class EntityActionInterface; class EntityItemProperties; class EntityTree; +class btCollisionShape; typedef std::shared_ptr EntityTreePointer; typedef std::shared_ptr EntityActionPointer; typedef std::shared_ptr EntityTreeElementPointer; @@ -324,6 +325,12 @@ public: /// return preferred shape type (actual physical shape may differ) virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; } + virtual void setCollisionShape(const btCollisionShape* shape) {} + + // these are only needed because the names don't match + virtual const glm::quat getRotation() const { return getOrientation(); } + virtual void setRotation(glm::quat orientation) { setOrientation(orientation); } + // updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags virtual void updateRegistrationPoint(const glm::vec3& value); void updatePosition(const glm::vec3& value); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 08d207fa72..4b6823dd8a 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -271,6 +271,14 @@ btCollisionShape* EntityMotionState::computeNewShape() { return getShapeManager()->getShape(shapeInfo); } +void EntityMotionState::setShape(btCollisionShape* shape) { + bool shapeChanged = (_shape != shape); + ObjectMotionState::setShape(shape); + if (shapeChanged) { + _entity->setCollisionShape(_shape); + } +} + bool EntityMotionState::isCandidateForOwnership() const { assert(_body); assert(_entity); diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 8f1532bf8f..2c39da9164 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -88,9 +88,10 @@ protected: bool entityTreeIsLocked() const; #endif - virtual bool isReadyToComputeShape() const override; - virtual btCollisionShape* computeNewShape() override; - virtual void setMotionType(PhysicsMotionType motionType); + bool isReadyToComputeShape() const override; + btCollisionShape* computeNewShape() override; + void setShape(btCollisionShape* shape) override; + void setMotionType(PhysicsMotionType motionType) override; // In the glorious future (when entities lib depends on physics lib) the EntityMotionState will be // properly "owned" by the EntityItem and will be deleted by it in the dtor. In pursuit of that diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index ca9b80a3b3..ccaef17fd1 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -151,7 +151,7 @@ public: protected: virtual bool isReadyToComputeShape() const = 0; virtual btCollisionShape* computeNewShape() = 0; - void setMotionType(PhysicsMotionType motionType); + virtual void setMotionType(PhysicsMotionType motionType); void updateCCDConfiguration(); void setRigidBody(btRigidBody* body); From 499150bbed02387aa881002116ea137440bc960a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 15 Jul 2016 15:56:52 -0700 Subject: [PATCH 277/332] finish hook alert for new entity collision shape --- libraries/entities-renderer/src/RenderableModelEntityItem.cpp | 4 ++++ libraries/entities-renderer/src/RenderableModelEntityItem.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index ac447417aa..2ff5e236fc 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -917,6 +917,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } } +void RenderableModelEntityItem::setCollisionShape(const btCollisionShape* shape) { + // TODO: generate collision mesh and update _model +} + bool RenderableModelEntityItem::contains(const glm::vec3& point) const { if (EntityItem::contains(point) && _model && _model->isCollisionLoaded()) { return _model->getCollisionFBXGeometry().convexHullContains(worldToEntity(point)); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 22469cf0ef..412cb6f296 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -61,7 +61,7 @@ public: virtual bool isReadyToComputeShape() override; virtual void computeShapeInfo(ShapeInfo& info) override; - void setCollisionShape(const btCollisionShape* shape) override {} + void setCollisionShape(const btCollisionShape* shape) override; virtual bool contains(const glm::vec3& point) const override; From 726928c14c061d958b96362d73e88efda367ccdf Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 19 Jul 2016 09:38:54 -0700 Subject: [PATCH 278/332] creae collision Geometry from mesh --- libraries/entities-renderer/CMakeLists.txt | 2 +- .../src/RenderableModelEntityItem.cpp | 17 ++++++++++++- .../src/RenderableModelEntityItem.h | 1 + .../src/model-networking/ModelCache.h | 7 +++--- .../physics/src/CollisionRenderMeshCache.cpp | 24 ++++++++++++------- .../physics/src/CollisionRenderMeshCache.h | 4 +++- libraries/render-utils/src/Model.cpp | 22 +++++++++++++++++ libraries/render-utils/src/Model.h | 3 ++- 8 files changed, 64 insertions(+), 16 deletions(-) diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 0063f4a701..9218b94fe1 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME entities-renderer) AUTOSCRIBE_SHADER_LIB(gpu model procedural render render-utils) setup_hifi_library(Widgets Network Script) -link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils) +link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils physics) target_bullet() diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 2ff5e236fc..cdf94017ff 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -917,8 +918,22 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } } +static CollisionRenderMeshCache collisionMeshCache; + void RenderableModelEntityItem::setCollisionShape(const btCollisionShape* shape) { - // TODO: generate collision mesh and update _model + const void* key = static_cast(shape); + if (_collisionMeshKey != key) { + if (_collisionMeshKey) { + // releasing the shape is not strictly necessary, but + // we do it as hint to the cache's garbage collection system + collisionMeshCache.releaseMesh(_collisionMeshKey); + } + _collisionMeshKey = key; + model::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey); + if (_model) { + _model->setCollisionMesh(mesh); + } + } } bool RenderableModelEntityItem::contains(const glm::vec3& point) const { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 412cb6f296..1f44260f65 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -119,6 +119,7 @@ private: bool getAnimationFrame(); bool _needsJointSimulation { false }; + const void* _collisionMeshKey { nullptr }; }; #endif // hifi_RenderableModelEntityItem_h diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 5c1aafdd45..fa93d3c899 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -111,13 +111,12 @@ public: QUrl getURL() const { return (bool)_resource ? _resource->getURL() : QUrl(); } -signals: - void finished(bool success); - -private: void startWatching(); void stopWatching(); +signals: + void finished(bool success); + private slots: void resourceFinished(bool success); void resourceRefreshed(); diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index 14386c6f48..22f0b986ab 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -67,12 +67,16 @@ void copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, m } } -model::MeshPointer createMeshFromShape(const btCollisionShape* shape) { +model::MeshPointer createMeshFromShape(const void* pointer) { model::MeshPointer mesh; - if (!shape) { + if (!pointer) { return mesh; } + // pointer must actually be a const btCollisionShape*, but it only + // needs to be valid here when its render mesh is created. + const btCollisionShape* shape = static_cast(pointer); + int32_t shapeType = shape->getShapeType(); if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE || shape->isConvex()) { // create the mesh and allocate buffers for it @@ -84,7 +88,7 @@ model::MeshPointer createMeshFromShape(const btCollisionShape* shape) { if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE) { const btCompoundShape* compoundShape = static_cast(shape); int32_t numSubShapes = compoundShape->getNumChildShapes(); - for (int i = 0; i < numSubShapes; ++i) { + for (int32_t i = 0; i < numSubShapes; ++i) { const btCollisionShape* childShape = compoundShape->getChildShape(i); if (childShape->isConvex()) { const btConvexShape* convexShape = static_cast(childShape); @@ -122,6 +126,10 @@ model::MeshPointer CollisionRenderMeshCache::getMesh(CollisionRenderMeshCache::K mesh = itr->second; } } + const uint32_t MAX_NUM_PENDING_GARBAGE = 20; + if (_pendingGarbage.size() > MAX_NUM_PENDING_GARBAGE) { + collectGarbage(); + } return mesh; } @@ -131,19 +139,19 @@ bool CollisionRenderMeshCache::releaseMesh(CollisionRenderMeshCache::Key key) { } CollisionMeshMap::const_iterator itr = _meshMap.find(key); if (itr != _meshMap.end()) { + // we hold at least one reference, and the outer scope also holds at least one + // so we assert that the reference count is not 1 assert((*itr).second.use_count() != 1); + _pendingGarbage.push_back(key); - if ((*itr).second.use_count() == 1) { - // we hold all of the references inside the cache so we'll try to delete later - } return true; } return false; } void CollisionRenderMeshCache::collectGarbage() { - int numShapes = _pendingGarbage.size(); - for (int i = 0; i < numShapes; ++i) { + uint32_t numShapes = _pendingGarbage.size(); + for (int32_t i = 0; i < numShapes; ++i) { CollisionRenderMeshCache::Key key = _pendingGarbage[i]; CollisionMeshMap::const_iterator itr = _meshMap.find(key); if (itr != _meshMap.end()) { diff --git a/libraries/physics/src/CollisionRenderMeshCache.h b/libraries/physics/src/CollisionRenderMeshCache.h index ad3c86562b..0c789f3da9 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.h +++ b/libraries/physics/src/CollisionRenderMeshCache.h @@ -18,6 +18,7 @@ #include +/* class btCollisionShape; namespace std { @@ -28,10 +29,11 @@ namespace std { } }; } +*/ class CollisionRenderMeshCache { public: - using Key = const btCollisionShape*; + using Key = const void*; // must actually be a const btCollisionShape* CollisionRenderMeshCache(); ~CollisionRenderMeshCache(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index a6e1bb53f4..35e40403cf 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1336,6 +1336,28 @@ bool Model::initWhenReady(render::ScenePointer scene) { return false; } +class CollisionRenderGeometry : public Geometry { +public: + CollisionRenderGeometry(model::MeshPointer mesh) { + _fbxGeometry = std::make_shared(); + std::shared_ptr meshes = std::make_shared(); + meshes->push_back(mesh); + _meshes = meshes; + _meshParts = std::shared_ptr(); + } +}; + +void Model::setCollisionMesh(model::MeshPointer mesh) { + _collisionWatcher.stopWatching(); + _collisionGeometry = std::make_shared(mesh); + + if (_showCollisionHull) { + // TODO: need to trigger: + // (a) reconstruction of RenderItems + // (b) and reinsertion into scene if we are showing collision geometry + } +} + ModelBlender::ModelBlender() : _pendingBlenders(0) { } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 15c8e0326a..d6580b8413 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -249,6 +249,7 @@ public slots: signals: void setURLFinished(bool success); void setCollisionModelURLFinished(bool success); + void setCollisionMesh(model::MeshPointer mesh); protected: @@ -282,7 +283,7 @@ protected: bool getJointPosition(int jointIndex, glm::vec3& position) const; Geometry::Pointer _renderGeometry; // only ever set by its watcher - Geometry::Pointer _collisionGeometry; // only ever set by its watcher + Geometry::Pointer _collisionGeometry; GeometryResourceWatcher _renderWatcher; GeometryResourceWatcher _collisionWatcher; From d8fa0d1dd1f0c4b5ff6ef817941f1b7e241b8965 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 19 Jul 2016 09:43:59 -0700 Subject: [PATCH 279/332] segregateMeshGroups() --> createRenderItems() --- libraries/render-utils/src/Model.cpp | 8 ++++---- libraries/render-utils/src/Model.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 35e40403cf..afb5cbcfb6 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -575,7 +575,7 @@ void Model::renderSetup(RenderArgs* args) { } if (!_meshGroupsKnown && isLoaded()) { - segregateMeshGroups(); + createRenderItems(); } } @@ -600,7 +600,7 @@ bool Model::addToScene(std::shared_ptr scene, bool showCollisionHull) { if ((!_meshGroupsKnown || showCollisionHull != _showCollisionHull) && isLoaded()) { _showCollisionHull = showCollisionHull; - segregateMeshGroups(); + createRenderItems(); } bool somethingAdded = false; @@ -1236,7 +1236,7 @@ AABox Model::getRenderableMeshBound() const { } } -void Model::segregateMeshGroups() { +void Model::createRenderItems() { Geometry::Pointer geometry; bool showingCollisionHull = false; if (_showCollisionHull && _collisionGeometry) { @@ -1303,7 +1303,7 @@ void Model::segregateMeshGroups() { bool Model::initWhenReady(render::ScenePointer scene) { if (isActive() && isRenderable() && !_meshGroupsKnown && isLoaded()) { - segregateMeshGroups(); + createRenderItems(); render::PendingChanges pendingChanges; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index d6580b8413..55a55a3d27 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -377,7 +377,7 @@ protected: void recalculateMeshBoxes(bool pickAgainstTriangles = false); - void segregateMeshGroups(); // used to calculate our list of translucent vs opaque meshes + void createRenderItems(); // used to calculate our list of translucent vs opaque meshes static model::MaterialPointer _collisionHullMaterial; bool _meshGroupsKnown; From 6b0ae654ba3aa143960cad2cbcfbd57caf6a54a0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 21 Jul 2016 17:11:04 -0700 Subject: [PATCH 280/332] cleanup logic around creating RenderItems --- .../src/RenderableModelEntityItem.cpp | 47 ++-- .../src/RenderableModelEntityItem.h | 2 - .../src/RenderableZoneEntityItem.cpp | 2 +- .../render-utils/src/MeshPartPayload.cpp | 5 +- libraries/render-utils/src/Model.cpp | 236 +++++++++++------- libraries/render-utils/src/Model.h | 24 +- 6 files changed, 195 insertions(+), 121 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index cdf94017ff..6e5d1c7959 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -177,6 +177,24 @@ void RenderableModelEntityItem::doInitialModelSimulation() { _needsInitialSimulation = false; } +// TODO: we need a solution for changes to the postion/rotation/etc of a model... +// this current code path only addresses that in this setup case... not the changing/moving case +bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) { + if (!_model && renderArgs) { + // TODO: this getModel() appears to be about 3% of model render time. We should optimize + PerformanceTimer perfTimer("getModel"); + EntityTreeRenderer* renderer = static_cast(renderArgs->_renderer); + getModel(renderer); + } + if (renderArgs && _model && _needsInitialSimulation && _model->isActive() && _model->isLoaded()) { + // make sure to simulate so everything gets set up correctly for rendering + doInitialModelSimulation(); + _model->renderSetup(renderArgs); + } + bool ready = !_needsInitialSimulation && _model && _model->readyToAddToScene(); + return ready; +} + class RenderableModelEntityItemMeta { public: RenderableModelEntityItemMeta(EntityItemPointer entity) : entity(entity){ } @@ -215,21 +233,21 @@ namespace render { bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { _myMetaItem = scene->allocateID(); - + auto renderData = std::make_shared(self); auto renderPayload = std::make_shared(renderData); - + pendingChanges.resetItem(_myMetaItem, renderPayload); - + if (_model) { render::Item::Status::Getters statusGetters; makeEntityItemStatusGetters(getThisPointer(), statusGetters); - - // note: we don't care if the model fails to add items, we always added our meta item and therefore we return - // true so that the system knows our meta item is in the scene! - _model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull); + + // note: we don't mind if the model fails to add, we'll retry (in render()) until it succeeds + _model->addToScene(scene, pendingChanges, statusGetters); } + // we've successfully added _myMetaItem so we always return true return true; } @@ -416,19 +434,20 @@ void RenderableModelEntityItem::render(RenderArgs* args) { // Remap textures for the next frame to avoid flicker remapTextures(); - // check to see if when we added our models to the scene they were ready, if they were not ready, then - // fix them up in the scene - bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0 - && getShapeType() == SHAPE_TYPE_COMPOUND; - if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) { - _showCollisionHull = shouldShowCollisionHull; + // update whether the model should be showing collision mesh + // (this may flag for fixupInScene) + bool shouldShowCollisionHull = getShapeType() != SHAPE_TYPE_STATIC_MESH && + (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0; + _model->setShowCollisionMesh(shouldShowCollisionHull); + + if (_model->needsFixupInScene()) { render::PendingChanges pendingChanges; _model->removeFromScene(scene, pendingChanges); render::Item::Status::Getters statusGetters; makeEntityItemStatusGetters(getThisPointer(), statusGetters); - _model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull); + _model->addToScene(scene, pendingChanges, statusGetters); scene->enqueuePendingChanges(pendingChanges); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 1f44260f65..16cd9c8bc5 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -114,8 +114,6 @@ private: render::ItemID _myMetaItem{ render::Item::INVALID_ITEM_ID }; - bool _showCollisionHull = false; - bool getAnimationFrame(); bool _needsJointSimulation { false }; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index ef47a777c2..9aa52f5ad3 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -122,7 +122,7 @@ void RenderableZoneEntityItem::render(RenderArgs* args) { _model->removeFromScene(scene, pendingChanges); render::Item::Status::Getters statusGetters; makeEntityItemStatusGetters(getThisPointer(), statusGetters); - _model->addToScene(scene, pendingChanges, false); + _model->addToScene(scene, pendingChanges); scene->enqueuePendingChanges(pendingChanges); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 1c8c89d6db..8913a62d9d 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -414,8 +414,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown // to false to rebuild out mesh groups. if (_meshIndex < 0 || _meshIndex >= (int)networkMeshes.size() || _meshIndex > geometry.meshes.size()) { - _model->_meshGroupsKnown = false; // regenerate these lists next time around. - _model->_readyWhenAdded = false; // in case any of our users are using scenes + _model->_needsFixupInScene = true; // trigger remove/add cycle _model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid return ShapeKey::Builder::invalid(); } @@ -533,7 +532,7 @@ void ModelMeshPartPayload::startFade() { void ModelMeshPartPayload::render(RenderArgs* args) const { PerformanceTimer perfTimer("ModelMeshPartPayload::render"); - if (!_model->_readyWhenAdded || !_model->_isVisible) { + if (!_model->addedToScene() || !_model->isVisible()) { return; // bail asap } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index afb5cbcfb6..075e550dd5 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -100,7 +100,6 @@ Model::Model(RigPointer rig, QObject* parent) : _calculatedMeshPartBoxesValid(false), _calculatedMeshBoxesValid(false), _calculatedMeshTrianglesValid(false), - _meshGroupsKnown(false), _isWireframe(false), _rig(rig) { @@ -121,19 +120,33 @@ Model::~Model() { AbstractViewStateInterface* Model::_viewState = NULL; +void Model::setShowCollisionMesh(bool value) { + if (_showCollisionHull != value) { + _showCollisionHull = value; + _needsFixupInScene = true; + } +} + bool Model::needsFixupInScene() const { - if (readyToAddToScene()) { - if (_needsUpdateTextures && _renderGeometry->areTexturesLoaded()) { - _needsUpdateTextures = false; + if ((_needsFixupInScene || !_addedToScene) && !_needsReload && isLoaded()) { + if (_showCollisionHull && _collisionGeometry) { return true; } - if (!_readyWhenAdded) { + if (!_meshStates.isEmpty() || (_renderGeometry && _renderGeometry->getMeshes().empty())) { + if (_needsUpdateTextures) { + if (!_renderGeometry->areTexturesLoaded()) { + return false; + } + _needsUpdateTextures = false; + } return true; } } return false; } +// TODO?: should we combine translation and rotation into single method to avoid double-work? +// (figure out where we call these) void Model::setTranslation(const glm::vec3& translation) { _translation = translation; updateRenderItems(); @@ -172,6 +185,9 @@ void Model::setOffset(const glm::vec3& offset) { } void Model::updateRenderItems() { + if (!_addedToScene) { + return; + } _needsUpdateClusterMatrices = true; _renderItemsNeedUpdate = false; @@ -574,8 +590,8 @@ void Model::renderSetup(RenderArgs* args) { } } - if (!_meshGroupsKnown && isLoaded()) { - createRenderItems(); + if (!_addedToScene && isLoaded()) { + createRenderItemSet(); } } @@ -596,43 +612,46 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr scen bool Model::addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, - render::Item::Status::Getters& statusGetters, - bool showCollisionHull) { - if ((!_meshGroupsKnown || showCollisionHull != _showCollisionHull) && isLoaded()) { - _showCollisionHull = showCollisionHull; - createRenderItems(); + render::Item::Status::Getters& statusGetters) { + bool readyToRender = (_showCollisionHull && _collisionGeometry) || isLoaded(); + if (!_addedToScene && readyToRender) { + createRenderItemSet(); } bool somethingAdded = false; - - if (_modelMeshRenderItems.empty()) { - foreach (auto renderItem, _modelMeshRenderItemsSet) { - auto item = scene->allocateID(); - auto renderPayload = std::make_shared(renderItem); - if (statusGetters.size()) { - renderPayload->addStatusGetters(statusGetters); + if (_showCollisionHull && _collisionGeometry) { + if (_collisionRenderItems.empty()) { + foreach (auto renderItem, _collisionRenderItemsSet) { + auto item = scene->allocateID(); + auto renderPayload = std::make_shared(renderItem); + if (statusGetters.size()) { + renderPayload->addStatusGetters(statusGetters); + } + pendingChanges.resetItem(item, renderPayload); + _collisionRenderItems.insert(item, renderPayload); } - pendingChanges.resetItem(item, renderPayload); - _modelMeshRenderItems.insert(item, renderPayload); - somethingAdded = true; + somethingAdded = !_collisionRenderItems.empty(); } - } - if (_collisionRenderItems.empty()) { - foreach (auto renderItem, _collisionRenderItemsSet) { - auto item = scene->allocateID(); - auto renderPayload = std::make_shared(renderItem); - if (statusGetters.size()) { - renderPayload->addStatusGetters(statusGetters); + } else { + if (_modelMeshRenderItems.empty()) { + foreach (auto renderItem, _modelMeshRenderItemsSet) { + auto item = scene->allocateID(); + auto renderPayload = std::make_shared(renderItem); + if (statusGetters.size()) { + renderPayload->addStatusGetters(statusGetters); + } + pendingChanges.resetItem(item, renderPayload); + _modelMeshRenderItems.insert(item, renderPayload); } - pendingChanges.resetItem(item, renderPayload); - _collisionRenderItems.insert(item, renderPayload); - somethingAdded = true; + somethingAdded = !_modelMeshRenderItems.empty(); } } - updateRenderItems(); - - _readyWhenAdded = readyToAddToScene(); + if (somethingAdded) { + _addedToScene = true; + updateRenderItems(); + _needsFixupInScene = false; + } return somethingAdded; } @@ -643,13 +662,13 @@ void Model::removeFromScene(std::shared_ptr scene, render::Pendin } _modelMeshRenderItems.clear(); _modelMeshRenderItemsSet.clear(); + foreach (auto item, _collisionRenderItems.keys()) { pendingChanges.removeItem(item); } _collisionRenderItems.clear(); _collisionRenderItemsSet.clear(); - _meshGroupsKnown = false; - _readyWhenAdded = false; + _addedToScene = false; } void Model::renderDebugMeshBoxes(gpu::Batch& batch) { @@ -804,6 +823,7 @@ int Model::getLastFreeJointIndex(int jointIndex) const { void Model::setTextures(const QVariantMap& textures) { if (isLoaded()) { _needsUpdateTextures = true; + _needsFixupInScene = true; _renderGeometry->setTextures(textures); } } @@ -825,8 +845,8 @@ void Model::setURL(const QUrl& url) { _needsReload = true; _needsUpdateTextures = true; - _meshGroupsKnown = false; _visualGeometryRequestFailed = false; + _needsFixupInScene = true; invalidCalculatedMeshBoxes(); deleteGeometry(); @@ -1236,21 +1256,21 @@ AABox Model::getRenderableMeshBound() const { } } -void Model::createRenderItems() { - Geometry::Pointer geometry; - bool showingCollisionHull = false; +void Model::createRenderItemSet() { if (_showCollisionHull && _collisionGeometry) { - if (isCollisionLoaded()) { - geometry = _collisionGeometry; - showingCollisionHull = true; - } else { - return; + if (_collisionRenderItemsSet.empty()) { + createCollisionRenderItemSet(); } } else { - assert(isLoaded()); - geometry = _renderGeometry; + if (_modelMeshRenderItemsSet.empty()) { + createVisibleRenderItemSet(); + } } - const auto& meshes = geometry->getMeshes(); +}; + +void Model::createVisibleRenderItemSet() { + assert(isLoaded()); + const auto& meshes = _renderGeometry->getMeshes(); // all of our mesh vectors must match in size if ((int)meshes.size() != _meshStates.size()) { @@ -1259,13 +1279,9 @@ void Model::createRenderItems() { } // We should not have any existing renderItems if we enter this section of code - Q_ASSERT(_modelMeshRenderItems.isEmpty()); Q_ASSERT(_modelMeshRenderItemsSet.isEmpty()); - Q_ASSERT(_collisionRenderItems.isEmpty()); - Q_ASSERT(_collisionRenderItemsSet.isEmpty()); _modelMeshRenderItemsSet.clear(); - _collisionRenderItemsSet.clear(); Transform transform; transform.setTranslation(_translation); @@ -1280,60 +1296,98 @@ void Model::createRenderItems() { uint32_t numMeshes = (uint32_t)meshes.size(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); - if (mesh) { + if (!mesh) { + continue; + } - // Create the render payloads - int numParts = (int)mesh->getNumParts(); - for (int partIndex = 0; partIndex < numParts; partIndex++) { - if (showingCollisionHull) { - if (_collisionHullMaterials.empty()) { - initCollisionHullMaterials(); - } - _collisionRenderItemsSet << std::make_shared(mesh, partIndex, _collisionHullMaterials[partIndex % NUM_COLLISION_HULL_COLORS], transform, offset); - } else { - _modelMeshRenderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); - } - - shapeID++; - } + // Create the render payloads + int numParts = (int)mesh->getNumParts(); + for (int partIndex = 0; partIndex < numParts; partIndex++) { + _modelMeshRenderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); + shapeID++; } } - _meshGroupsKnown = true; +} + +void Model::createCollisionRenderItemSet() { + assert((bool)_collisionGeometry); + if (_collisionHullMaterials.empty()) { + initCollisionHullMaterials(); + } + + const auto& meshes = _collisionGeometry->getMeshes(); + + // We should not have any existing renderItems if we enter this section of code + Q_ASSERT(_collisionRenderItemsSet.isEmpty()); + + Transform transform; + transform.setTranslation(_translation); + transform.setRotation(_rotation); + + Transform offset; + offset.setScale(_scale); + offset.postTranslate(_offset); + + // Run through all of the meshes, and place them into their segregated, but unsorted buckets + uint32_t numMeshes = (uint32_t)meshes.size(); + for (uint32_t i = 0; i < numMeshes; i++) { + const auto& mesh = meshes.at(i); + if (!mesh) { + continue; + } + + // Create the render payloads + int numParts = (int)mesh->getNumParts(); + for (int partIndex = 0; partIndex < numParts; partIndex++) { + model::MaterialPointer& material = _collisionHullMaterials[partIndex % NUM_COLLISION_HULL_COLORS]; + _collisionRenderItemsSet << std::make_shared(mesh, partIndex, material, transform, offset); + } + } +} + +bool Model::isRenderable() const { + return !_meshStates.isEmpty() || (isLoaded() && _renderGeometry->getMeshes().empty()); } bool Model::initWhenReady(render::ScenePointer scene) { - if (isActive() && isRenderable() && !_meshGroupsKnown && isLoaded()) { - createRenderItems(); + // NOTE: this only called by SkeletonModel + if (_addedToScene || !isRenderable()) { + return false; + } - render::PendingChanges pendingChanges; + createRenderItemSet(); - Transform transform; - transform.setTranslation(_translation); - transform.setRotation(_rotation); + render::PendingChanges pendingChanges; - Transform offset; - offset.setScale(_scale); - offset.postTranslate(_offset); - - foreach (auto renderItem, _modelMeshRenderItemsSet) { - auto item = scene->allocateID(); - auto renderPayload = std::make_shared(renderItem); - _modelMeshRenderItems.insert(item, renderPayload); - pendingChanges.resetItem(item, renderPayload); - } + bool addedPendingChanges = false; + if (_showCollisionHull && _collisionGeometry) { foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); _collisionRenderItems.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); } - scene->enqueuePendingChanges(pendingChanges); - updateRenderItems(); - - _readyWhenAdded = true; - return true; + addedPendingChanges = !_collisionRenderItems.empty(); + } else { + foreach (auto renderItem, _modelMeshRenderItemsSet) { + auto item = scene->allocateID(); + auto renderPayload = std::make_shared(renderItem); + _modelMeshRenderItems.insert(item, renderPayload); + pendingChanges.resetItem(item, renderPayload); + } + addedPendingChanges = !_modelMeshRenderItems.empty(); } - return false; + _addedToScene = addedPendingChanges; + if (addedPendingChanges) { + scene->enqueuePendingChanges(pendingChanges); + // NOTE: updateRender items enqueues identical pendingChanges (using a lambda) + // so it looks like we're doing double work here, but I don't want to remove the call + // for fear there is some sideeffect we'll miss. -- Andrew 2016.07.21 + // TODO: figure out if we really need this call to updateRenderItems() or not. + updateRenderItems(); + } + + return true; } class CollisionRenderGeometry : public Geometry { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 55a55a3d27..b30b0b0d76 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -81,24 +81,25 @@ public: // new Scene/Engine rendering support void setVisibleInScene(bool newValue, std::shared_ptr scene); bool needsFixupInScene() const; + + void setShowCollisionMesh(bool value); + bool readyToAddToScene(RenderArgs* renderArgs = nullptr) const { return !_needsReload && isRenderable() && isActive(); } bool needsReload() const { return _needsReload; } bool initWhenReady(render::ScenePointer scene); bool addToScene(std::shared_ptr scene, - render::PendingChanges& pendingChanges, - bool showCollisionHull = false) { + render::PendingChanges& pendingChanges) { auto getters = render::Item::Status::Getters(0); - return addToScene(scene, pendingChanges, getters, showCollisionHull); + return addToScene(scene, pendingChanges, getters); } bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, - render::Item::Status::Getters& statusGetters, - bool showCollisionHull = false); + render::Item::Status::Getters& statusGetters); void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); void renderSetup(RenderArgs* args); - bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _renderGeometry->getMeshes().empty()); } + bool isRenderable() const; bool isVisible() const { return _isVisible; } @@ -239,6 +240,7 @@ public: // returns 'true' if needs fullUpdate after geometry change bool updateGeometry(); + void setCollisionMesh(model::MeshPointer mesh); void setLoadingPriority(float priority) { _loadingPriority = priority; } @@ -249,9 +251,9 @@ public slots: signals: void setURLFinished(bool success); void setCollisionModelURLFinished(bool success); - void setCollisionMesh(model::MeshPointer mesh); protected: + bool addedToScene() const { return _addedToScene; } void setPupilDilation(float dilation) { _pupilDilation = dilation; } float getPupilDilation() const { return _pupilDilation; } @@ -377,10 +379,11 @@ protected: void recalculateMeshBoxes(bool pickAgainstTriangles = false); - void createRenderItems(); // used to calculate our list of translucent vs opaque meshes + void createRenderItemSet(); + void createVisibleRenderItemSet(); + void createCollisionRenderItemSet(); static model::MaterialPointer _collisionHullMaterial; - bool _meshGroupsKnown; bool _isWireframe; @@ -397,7 +400,8 @@ protected: QSet> _modelMeshRenderItemsSet; QMap _modelMeshRenderItems; - bool _readyWhenAdded { false }; + bool _addedToScene { false }; // has been added to scene + bool _needsFixupInScene { true }; // needs to be removed/re-added to scene bool _needsReload { true }; bool _needsUpdateClusterMatrices { true }; bool _showCollisionHull { false }; From 25fb7aacad3e9e49674ae8686c27aa20a48375fb Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 26 Jul 2016 07:53:11 -0700 Subject: [PATCH 281/332] ShapeManager hands out const shape pointers --- interface/src/avatar/AvatarManager.cpp | 2 +- interface/src/avatar/AvatarMotionState.cpp | 4 ++-- interface/src/avatar/AvatarMotionState.h | 4 ++-- libraries/physics/src/PhysicalEntitySimulation.cpp | 2 +- libraries/physics/src/PhysicsEngine.cpp | 6 +++--- libraries/physics/src/ShapeFactory.cpp | 14 +++++++++----- libraries/physics/src/ShapeFactory.h | 4 ++-- libraries/physics/src/ShapeManager.cpp | 4 ++-- libraries/physics/src/ShapeManager.h | 4 ++-- 9 files changed, 24 insertions(+), 20 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index bd76d2bd81..441130bd83 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -367,7 +367,7 @@ void AvatarManager::addAvatarToSimulation(Avatar* avatar) { ShapeInfo shapeInfo; avatar->computeShapeInfo(shapeInfo); - btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); if (shape) { // we don't add to the simulation now, we put it on a list to be added later AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index 9cc3095ae2..335245670b 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -17,7 +17,7 @@ #include "AvatarMotionState.h" #include "BulletUtil.h" -AvatarMotionState::AvatarMotionState(Avatar* avatar, btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { +AvatarMotionState::AvatarMotionState(Avatar* avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { assert(_avatar); _type = MOTIONSTATE_TYPE_AVATAR; if (_shape) { @@ -47,7 +47,7 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const { } // virtual and protected -btCollisionShape* AvatarMotionState::computeNewShape() { +const btCollisionShape* AvatarMotionState::computeNewShape() { ShapeInfo shapeInfo; _avatar->computeShapeInfo(shapeInfo); return getShapeManager()->getShape(shapeInfo); diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 04aa5ea57c..66824d6e37 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -20,7 +20,7 @@ class Avatar; class AvatarMotionState : public ObjectMotionState { public: - AvatarMotionState(Avatar* avatar, btCollisionShape* shape); + AvatarMotionState(Avatar* avatar, const btCollisionShape* shape); virtual PhysicsMotionType getMotionType() const override { return _motionType; } @@ -72,7 +72,7 @@ protected: ~AvatarMotionState(); virtual bool isReadyToComputeShape() const override { return true; } - virtual btCollisionShape* computeNewShape() override; + virtual const btCollisionShape* computeNewShape() override; // The AvatarMotionState keeps a RAW backpointer to its Avatar because all AvatarMotionState // instances are "owned" by their corresponding Avatar instance and are deleted in the Avatar dtor. diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 9714059e7c..be420604b3 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -225,7 +225,7 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re << "at" << entity->getPosition() << " will be reduced"; } } - btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); if (shape) { EntityMotionState* motionState = new EntityMotionState(shape, entity); entity->setPhysicsInfo(static_cast(motionState)); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index aa6c1b4e40..1c6d5d535e 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -76,7 +76,7 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { switch(motionType) { case MOTION_TYPE_KINEMATIC: { if (!body) { - btCollisionShape* shape = motionState->getShape(); + btCollisionShape* shape = const_cast(motionState->getShape()); assert(shape); body = new btRigidBody(mass, motionState, shape, inertia); motionState->setRigidBody(body); @@ -93,7 +93,7 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { } case MOTION_TYPE_DYNAMIC: { mass = motionState->getMass(); - btCollisionShape* shape = motionState->getShape(); + btCollisionShape* shape = const_cast(motionState->getShape()); assert(shape); shape->calculateLocalInertia(mass, inertia); if (!body) { @@ -120,7 +120,7 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { default: { if (!body) { assert(motionState->getShape()); - body = new btRigidBody(mass, motionState, motionState->getShape(), inertia); + body = new btRigidBody(mass, motionState, const_cast(motionState->getShape()), inertia); motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 8576a9caee..f11b0f95dc 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -247,7 +247,7 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) { delete dataArray; } -btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { +const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { btCollisionShape* shape = NULL; int type = info.getType(); switch(type) { @@ -359,10 +359,14 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { return shape; } -void ShapeFactory::deleteShape(btCollisionShape* shape) { +void ShapeFactory::deleteShape(const btCollisionShape* shape) { assert(shape); - if (shape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) { - btCompoundShape* compoundShape = static_cast(shape); + // ShapeFactory is responsible for deleting all shapes, even the const ones that are stored + // in the ShapeManager, so we must cast to non-const here when deleting. + // so we cast to non-const here when deleting memory. + btCollisionShape* nonConstShape = const_cast(shape); + if (nonConstShape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) { + btCompoundShape* compoundShape = static_cast(nonConstShape); const int numChildShapes = compoundShape->getNumChildShapes(); for (int i = 0; i < numChildShapes; i ++) { btCollisionShape* childShape = compoundShape->getChildShape(i); @@ -374,7 +378,7 @@ void ShapeFactory::deleteShape(btCollisionShape* shape) { } } } - delete shape; + delete nonConstShape; } // the dataArray must be created before we create the StaticMeshShape diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 6202612eb9..a1022104dd 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -20,8 +20,8 @@ // translates between ShapeInfo and btShape namespace ShapeFactory { - btCollisionShape* createShapeFromInfo(const ShapeInfo& info); - void deleteShape(btCollisionShape* shape); + const btCollisionShape* createShapeFromInfo(const ShapeInfo& info); + void deleteShape(const btCollisionShape* shape); //btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info); //void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray); diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 35046adcfd..b61fb0037b 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -28,7 +28,7 @@ ShapeManager::~ShapeManager() { _shapeMap.clear(); } -btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { +const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { if (info.getType() == SHAPE_TYPE_NONE) { return nullptr; } @@ -45,7 +45,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { shapeRef->refCount++; return shapeRef->shape; } - btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); + const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); if (shape) { ShapeReference newRef; newRef.refCount = 1; diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index cdb2b78789..261c06ddb9 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -26,7 +26,7 @@ public: ~ShapeManager(); /// \return pointer to shape - btCollisionShape* getShape(const ShapeInfo& info); + const btCollisionShape* getShape(const ShapeInfo& info); /// \return true if shape was found and released bool releaseShape(const btCollisionShape* shape); @@ -46,7 +46,7 @@ private: class ShapeReference { public: int refCount; - btCollisionShape* shape; + const btCollisionShape* shape; DoubleHashKey key; ShapeReference() : refCount(0), shape(nullptr) {} }; From daff897fc4bd16e0a7b71a77b6fda6355aafbf4d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 26 Jul 2016 07:54:31 -0700 Subject: [PATCH 282/332] const shapes, and use *MotionState::setShape() --- libraries/physics/src/EntityMotionState.cpp | 14 ++++++++------ libraries/physics/src/EntityMotionState.h | 4 ++-- libraries/physics/src/ObjectMotionState.cpp | 12 ++++++------ libraries/physics/src/ObjectMotionState.h | 10 +++++----- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 4b6823dd8a..b70927d501 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -46,7 +46,7 @@ bool entityTreeIsLocked() { EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer entity) : - ObjectMotionState(shape), + ObjectMotionState(nullptr), _entityPtr(entity), _entity(entity.get()), _serverPosition(0.0f), @@ -71,6 +71,9 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer assert(_entity); assert(entityTreeIsLocked()); setMass(_entity->computeMass()); + // we need the side-effects of EntityMotionState::setShape() so we call it explicitly here + // rather than pass the legit shape pointer to the ObjectMotionState ctor above. + setShape(shape); } EntityMotionState::~EntityMotionState() { @@ -264,17 +267,16 @@ bool EntityMotionState::isReadyToComputeShape() const { } // virtual and protected -btCollisionShape* EntityMotionState::computeNewShape() { +const btCollisionShape* EntityMotionState::computeNewShape() { ShapeInfo shapeInfo; assert(entityTreeIsLocked()); _entity->computeShapeInfo(shapeInfo); return getShapeManager()->getShape(shapeInfo); } -void EntityMotionState::setShape(btCollisionShape* shape) { - bool shapeChanged = (_shape != shape); - ObjectMotionState::setShape(shape); - if (shapeChanged) { +void EntityMotionState::setShape(const btCollisionShape* shape) { + if (_shape != shape) { + ObjectMotionState::setShape(shape); _entity->setCollisionShape(_shape); } } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 2c39da9164..194d82805f 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -89,8 +89,8 @@ protected: #endif bool isReadyToComputeShape() const override; - btCollisionShape* computeNewShape() override; - void setShape(btCollisionShape* shape) override; + const btCollisionShape* computeNewShape() override; + void setShape(const btCollisionShape* shape) override; void setMotionType(PhysicsMotionType motionType) override; // In the glorious future (when entities lib depends on physics lib) the EntityMotionState will be diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 64b1e9c527..38f079c1d4 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -62,14 +62,13 @@ ShapeManager* ObjectMotionState::getShapeManager() { return shapeManager; } -ObjectMotionState::ObjectMotionState(btCollisionShape* shape) : +ObjectMotionState::ObjectMotionState(const btCollisionShape* shape) : _motionType(MOTION_TYPE_STATIC), - _shape(nullptr), + _shape(shape), _body(nullptr), _mass(0.0f), _lastKinematicStep(worldSimulationStep) { - setShape(shape); } ObjectMotionState::~ObjectMotionState() { @@ -154,12 +153,13 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) { _body = body; if (_body) { _body->setUserPointer(this); + assert(_body->getCollisionShape() == _shape); } updateCCDConfiguration(); } } -void ObjectMotionState::setShape(btCollisionShape* shape) { +void ObjectMotionState::setShape(const btCollisionShape* shape) { if (_shape != shape) { if (_shape) { getShapeManager()->releaseShape(_shape); @@ -254,7 +254,7 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* if (!isReadyToComputeShape()) { return false; } - btCollisionShape* newShape = computeNewShape(); + const btCollisionShape* newShape = computeNewShape(); if (!newShape) { qCDebug(physics) << "Warning: failed to generate new shape!"; // failed to generate new shape! --> keep old shape and remove shape-change flag @@ -274,7 +274,7 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* // and clear the reference we just created getShapeManager()->releaseShape(_shape); } else { - _body->setCollisionShape(newShape); + _body->setCollisionShape(const_cast(newShape)); setShape(newShape); updateCCDConfiguration(); } diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index ccaef17fd1..a7894998a8 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -78,7 +78,7 @@ public: static void setShapeManager(ShapeManager* manager); static ShapeManager* getShapeManager(); - ObjectMotionState(btCollisionShape* shape); + ObjectMotionState(const btCollisionShape* shape); ~ObjectMotionState(); virtual void handleEasyChanges(uint32_t& flags); @@ -110,7 +110,7 @@ public: virtual PhysicsMotionType computePhysicsMotionType() const = 0; - btCollisionShape* getShape() const { return _shape; } + const btCollisionShape* getShape() const { return _shape; } btRigidBody* getRigidBody() const { return _body; } virtual bool isMoving() const = 0; @@ -150,17 +150,17 @@ public: protected: virtual bool isReadyToComputeShape() const = 0; - virtual btCollisionShape* computeNewShape() = 0; + virtual const btCollisionShape* computeNewShape() = 0; virtual void setMotionType(PhysicsMotionType motionType); void updateCCDConfiguration(); void setRigidBody(btRigidBody* body); - virtual void setShape(btCollisionShape* shape); + virtual void setShape(const btCollisionShape* shape); MotionStateType _type = MOTIONSTATE_TYPE_INVALID; // type of MotionState PhysicsMotionType _motionType; // type of motion: KINEMATIC, DYNAMIC, or STATIC - btCollisionShape* _shape; + const btCollisionShape* _shape; btRigidBody* _body; float _mass; From 26f5d3cfaa382dcf075ba7f8f503e7b2305bbcb3 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 26 Jul 2016 07:55:34 -0700 Subject: [PATCH 283/332] fix render geometry for collision shapes --- .../physics/src/CollisionRenderMeshCache.cpp | 84 ++++++++++++------- libraries/render-utils/src/Model.cpp | 23 +++-- 2 files changed, 67 insertions(+), 40 deletions(-) diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index 22f0b986ab..ef2f9013ef 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -18,53 +18,69 @@ #include // for MAX_HULL_POINTS -float verts[3 * MAX_HULL_POINTS]; +const int32_t MAX_HULL_INDICES = 6 * MAX_HULL_POINTS; +float tempVertexBuffer[3 * MAX_HULL_POINTS]; +model::Index tempIndexBuffer[MAX_HULL_INDICES]; -void copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, model::MeshPointer mesh) { - assert((bool)mesh); +//void copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, model::MeshPointer mesh) { +void copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, + gpu::BufferView& vertices, gpu::BufferView& indices, gpu::BufferView& parts) { assert(shape); btShapeHull hull(shape); const btScalar MARGIN = 0.0f; hull.buildHull(MARGIN); - const uint32_t* hullIndices = hull.getIndexPointer(); - int32_t numIndices = hull.numIndices(); - assert(numIndices <= 6 * MAX_HULL_POINTS); + int32_t numHullIndices = hull.numIndices(); + assert(numHullIndices <= MAX_HULL_INDICES); + + int32_t numHullVertices = hull.numVertices(); + assert(numHullVertices <= MAX_HULL_POINTS); { // new part model::Mesh::Part part; - part._startIndex = mesh->getIndexBuffer().getNumElements(); - part._numIndices = (model::Index)numIndices; - part._baseVertex = mesh->getVertexBuffer().getNumElements(); + part._startIndex = indices.getNumElements(); + part._numIndices = (model::Index)numHullIndices; + // FIXME: the render code cannot handle the case where part._baseVertex != 0 + //part._baseVertex = vertices.getNumElements(); // DOES NOT WORK + part._baseVertex = 0; gpu::BufferView::Size numBytes = sizeof(model::Mesh::Part); const gpu::Byte* data = reinterpret_cast(&part); - mesh->getPartBuffer()._buffer->append(numBytes, data); + parts._buffer->append(numBytes, data); + parts._size = parts._buffer->getSize(); } + model::Index indexOffset = vertices.getNumElements(); { // new vertices const btVector3* hullVertices = hull.getVertexPointer(); - int32_t numVertices = hull.numVertices(); - assert(numVertices <= MAX_HULL_POINTS); - for (int32_t i = 0; i < numVertices; ++i) { + assert(numHullVertices <= MAX_HULL_POINTS); + for (int32_t i = 0; i < numHullVertices; ++i) { btVector3 transformedPoint = transform * hullVertices[i]; - memcpy(transformedPoint.m_floats, verts + 3 * i, 3 * sizeof(float)); - //data[0] = transformedPoint.getX(); - //data[1] = transformedPoint.getY(); - //data[2] = transformedPoint.getZ(); + memcpy(tempVertexBuffer + 3 * i, transformedPoint.m_floats, 3 * sizeof(float)); } - gpu::BufferView::Size numBytes = sizeof(float) * (3 * numVertices); - const gpu::Byte* data = reinterpret_cast(verts); - mesh->getVertexBuffer()._buffer->append(numBytes, data); + gpu::BufferView::Size numBytes = sizeof(float) * (3 * numHullVertices); + const gpu::Byte* data = reinterpret_cast(tempVertexBuffer); + vertices._buffer->append(numBytes, data); + vertices._size = vertices._buffer->getSize(); } { // new indices - gpu::BufferView::Size numBytes = (gpu::BufferView::Size)(sizeof(uint32_t) * hull.numIndices()); - const gpu::Byte* data = reinterpret_cast(hullIndices); - mesh->getIndexBuffer()._buffer->append(numBytes, data); + const uint32_t* hullIndices = hull.getIndexPointer(); + // FIXME: the render code cannot handle the case where part._baseVertex != 0 + // so we must add an offset to each index + for (int32_t i = 0; i < numHullIndices; ++i) { + tempIndexBuffer[i] = hullIndices[i] + indexOffset; + } + const gpu::Byte* data = reinterpret_cast(tempIndexBuffer); + gpu::BufferView::Size numBytes = (gpu::BufferView::Size)(sizeof(model::Index) * numHullIndices); + indices._buffer->append(numBytes, data); + indices._size = indices._buffer->getSize(); } + + gpu::BufferView::Iterator partItr = parts.cbegin(); + gpu::Size numParts = parts.getNumElements(); } model::MeshPointer createMeshFromShape(const void* pointer) { @@ -73,17 +89,18 @@ model::MeshPointer createMeshFromShape(const void* pointer) { return mesh; } - // pointer must actually be a const btCollisionShape*, but it only - // needs to be valid here when its render mesh is created. + // pointer must be a const btCollisionShape* (cast to void*), but it only + // needs to be valid here when its render mesh is created, after this call + // the cache doesn't care what happens to the shape behind the pointer const btCollisionShape* shape = static_cast(pointer); int32_t shapeType = shape->getShapeType(); if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE || shape->isConvex()) { // create the mesh and allocate buffers for it mesh = std::make_shared(); - mesh->setVertexBuffer(gpu::BufferView(new gpu::Buffer(), mesh->getVertexBuffer()._element)); - mesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(), mesh->getIndexBuffer()._element)); - mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(), mesh->getPartBuffer()._element)); + gpu::BufferView vertices(new gpu::Buffer(), mesh->getVertexBuffer()._element); + gpu::BufferView indices(new gpu::Buffer(), mesh->getIndexBuffer()._element); + gpu::BufferView parts(new gpu::Buffer(), mesh->getPartBuffer()._element); if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE) { const btCompoundShape* compoundShape = static_cast(shape); @@ -92,14 +109,19 @@ model::MeshPointer createMeshFromShape(const void* pointer) { const btCollisionShape* childShape = compoundShape->getChildShape(i); if (childShape->isConvex()) { const btConvexShape* convexShape = static_cast(childShape); - copyShapeToMesh(compoundShape->getChildTransform(i), convexShape, mesh); + copyShapeToMesh(compoundShape->getChildTransform(i), convexShape, vertices, indices, parts); } } } else { // shape is convex const btConvexShape* convexShape = static_cast(shape); - copyShapeToMesh(btTransform(), convexShape, mesh); + btTransform transform; + transform.setIdentity(); + copyShapeToMesh(transform, convexShape, vertices, indices, parts); } + mesh->setVertexBuffer(vertices); + mesh->setIndexBuffer(indices); + mesh->setPartBuffer(parts); } return mesh; } @@ -151,7 +173,7 @@ bool CollisionRenderMeshCache::releaseMesh(CollisionRenderMeshCache::Key key) { void CollisionRenderMeshCache::collectGarbage() { uint32_t numShapes = _pendingGarbage.size(); - for (int32_t i = 0; i < numShapes; ++i) { + for (uint32_t i = 0; i < numShapes; ++i) { CollisionRenderMeshCache::Key key = _pendingGarbage[i]; CollisionMeshMap::const_iterator itr = _meshMap.find(key); if (itr != _meshMap.end()) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 075e550dd5..bbb0294e21 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -219,10 +219,6 @@ void Model::updateRenderItems() { modelMeshOffset.postTranslate(self->_offset); } - // only apply offset only, collision mesh does not share the same unit scale as the FBX file's mesh. - Transform collisionMeshOffset; - collisionMeshOffset.postTranslate(self->_offset); - uint32_t deleteGeometryCounter = self->_deleteGeometryCounter; render::PendingChanges pendingChanges; @@ -243,6 +239,11 @@ void Model::updateRenderItems() { }); } + // collision mesh does not share the same unit scale as the FBX file's mesh: only apply offset + Transform collisionMeshOffset; + // adebug FIXME: recover correct behavior for collisionURL shapes + //collisionMeshOffset.postTranslate(self->_offset); + collisionMeshOffset.setIdentity(); foreach (auto itemID, self->_collisionRenderItems.keys()) { pendingChanges.updateItem(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) { // update the model transform for this render item. @@ -1321,12 +1322,16 @@ void Model::createCollisionRenderItemSet() { Q_ASSERT(_collisionRenderItemsSet.isEmpty()); Transform transform; - transform.setTranslation(_translation); - transform.setRotation(_rotation); + transform.setIdentity(); + // adebug FIXME: recover correct behavior for collisionURL + //transform.setTranslation(_translation); + //transform.setRotation(_rotation); Transform offset; - offset.setScale(_scale); - offset.postTranslate(_offset); + // adebug FIXME: recover correct behavior for collisionURL + offset.setIdentity(); + //offset.setScale(_scale); + //offset.postTranslate(_offset); // Run through all of the meshes, and place them into their segregated, but unsorted buckets uint32_t numMeshes = (uint32_t)meshes.size(); @@ -1339,7 +1344,7 @@ void Model::createCollisionRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - model::MaterialPointer& material = _collisionHullMaterials[partIndex % NUM_COLLISION_HULL_COLORS]; + model::MaterialPointer& material = _collisionHullMaterials[partIndex % NUM_COLLISION_HULL_COLORS]; _collisionRenderItemsSet << std::make_shared(mesh, partIndex, material, transform, offset); } } From 3b772d2fe6ff000ddee1c760ec503647c1b0d70e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 26 Jul 2016 17:06:40 -0700 Subject: [PATCH 284/332] fallback to SIMPLE_COMPOUND when can't do COMPOUND --- libraries/entities/src/ModelEntityItem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 8e925b2f79..b098247524 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -282,8 +282,8 @@ ShapeType ModelEntityItem::computeTrueShapeType() const { type = SHAPE_TYPE_COMPOUND; } if (type == SHAPE_TYPE_COMPOUND && !hasCompoundShapeURL()) { - // no compoundURL set --> fall back to NONE - type = SHAPE_TYPE_NONE; + // no compoundURL set --> fall back to SIMPLE_COMPOUND + type = SHAPE_TYPE_SIMPLE_COMPOUND; } return type; } From 481d6990a3b188f53d36236e7aeaefb0e9c367ee Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 26 Jul 2016 17:07:16 -0700 Subject: [PATCH 285/332] trigger collision shape render mesh creation --- libraries/render-utils/src/Model.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index bbb0294e21..546631232e 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1411,6 +1411,7 @@ void Model::setCollisionMesh(model::MeshPointer mesh) { _collisionGeometry = std::make_shared(mesh); if (_showCollisionHull) { + _needsFixupInScene = true; // TODO: need to trigger: // (a) reconstruction of RenderItems // (b) and reinsertion into scene if we are showing collision geometry From 1f84ef3053317ba5e1ba0259ffe31f7574f8599e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 26 Jul 2016 17:07:43 -0700 Subject: [PATCH 286/332] compute normals for collision mesh geometry --- .../physics/src/CollisionRenderMeshCache.cpp | 91 ++++++++++++------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index ef2f9013ef..8b27bbae24 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -19,17 +19,19 @@ #include // for MAX_HULL_POINTS const int32_t MAX_HULL_INDICES = 6 * MAX_HULL_POINTS; -float tempVertexBuffer[3 * MAX_HULL_POINTS]; +const int32_t MAX_HULL_NORMALS = MAX_HULL_INDICES; +float tempVertices[MAX_HULL_NORMALS]; model::Index tempIndexBuffer[MAX_HULL_INDICES]; -//void copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, model::MeshPointer mesh) { -void copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, - gpu::BufferView& vertices, gpu::BufferView& indices, gpu::BufferView& parts) { +bool copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, + gpu::BufferView& vertices, gpu::BufferView& indices, gpu::BufferView& parts, + gpu::BufferView& normals) { assert(shape); btShapeHull hull(shape); - const btScalar MARGIN = 0.0f; - hull.buildHull(MARGIN); + if (!hull.buildHull(shape->getMargin())) { + return false; + } int32_t numHullIndices = hull.numIndices(); assert(numHullIndices <= MAX_HULL_INDICES); @@ -51,20 +53,8 @@ void copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, parts._size = parts._buffer->getSize(); } + const int32_t SIZE_OF_VEC3 = 3 * sizeof(float); model::Index indexOffset = vertices.getNumElements(); - { // new vertices - const btVector3* hullVertices = hull.getVertexPointer(); - assert(numHullVertices <= MAX_HULL_POINTS); - for (int32_t i = 0; i < numHullVertices; ++i) { - btVector3 transformedPoint = transform * hullVertices[i]; - memcpy(tempVertexBuffer + 3 * i, transformedPoint.m_floats, 3 * sizeof(float)); - } - - gpu::BufferView::Size numBytes = sizeof(float) * (3 * numHullVertices); - const gpu::Byte* data = reinterpret_cast(tempVertexBuffer); - vertices._buffer->append(numBytes, data); - vertices._size = vertices._buffer->getSize(); - } { // new indices const uint32_t* hullIndices = hull.getIndexPointer(); @@ -78,9 +68,37 @@ void copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, indices._buffer->append(numBytes, data); indices._size = indices._buffer->getSize(); } + { // new vertices + const btVector3* hullVertices = hull.getVertexPointer(); + assert(numHullVertices <= MAX_HULL_POINTS); + for (int32_t i = 0; i < numHullVertices; ++i) { + btVector3 transformedPoint = transform * hullVertices[i]; + memcpy(tempVertices + 3 * i, transformedPoint.m_floats, SIZE_OF_VEC3); + } + gpu::BufferView::Size numBytes = sizeof(float) * (3 * numHullVertices); + const gpu::Byte* data = reinterpret_cast(tempVertices); + vertices._buffer->append(numBytes, data); + vertices._size = vertices._buffer->getSize(); + } + { // new normals + // compute average point + btVector3 avgVertex(0.0f, 0.0f, 0.0f); + const btVector3* hullVertices = hull.getVertexPointer(); + for (int i = 0; i < numHullVertices; ++i) { + avgVertex += hullVertices[i]; + } + avgVertex = transform * (avgVertex * (1.0f / (float)numHullVertices)); - gpu::BufferView::Iterator partItr = parts.cbegin(); - gpu::Size numParts = parts.getNumElements(); + for (int i = 0; i < numHullVertices; ++i) { + btVector3 norm = (transform * hullVertices[i] - avgVertex).normalize(); + memcpy(tempVertices + 3 * i, norm.m_floats, SIZE_OF_VEC3); + } + gpu::BufferView::Size numBytes = sizeof(float) * (3 * numHullVertices); + const gpu::Byte* data = reinterpret_cast(tempVertices); + normals._buffer->append(numBytes, data); + normals._size = vertices._buffer->getSize(); + } + return true; } model::MeshPointer createMeshFromShape(const void* pointer) { @@ -96,12 +114,13 @@ model::MeshPointer createMeshFromShape(const void* pointer) { int32_t shapeType = shape->getShapeType(); if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE || shape->isConvex()) { - // create the mesh and allocate buffers for it - mesh = std::make_shared(); - gpu::BufferView vertices(new gpu::Buffer(), mesh->getVertexBuffer()._element); - gpu::BufferView indices(new gpu::Buffer(), mesh->getIndexBuffer()._element); - gpu::BufferView parts(new gpu::Buffer(), mesh->getPartBuffer()._element); + // allocate buffers for it + gpu::BufferView vertices(new gpu::Buffer(), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + gpu::BufferView indices(new gpu::Buffer(), gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::INDEX)); + gpu::BufferView parts(new gpu::Buffer(), gpu::Element(gpu::VEC4, gpu::UINT32, gpu::PART)); + gpu::BufferView normals(new gpu::Buffer(), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + int32_t numSuccesses = 0; if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE) { const btCompoundShape* compoundShape = static_cast(shape); int32_t numSubShapes = compoundShape->getNumChildShapes(); @@ -109,7 +128,9 @@ model::MeshPointer createMeshFromShape(const void* pointer) { const btCollisionShape* childShape = compoundShape->getChildShape(i); if (childShape->isConvex()) { const btConvexShape* convexShape = static_cast(childShape); - copyShapeToMesh(compoundShape->getChildTransform(i), convexShape, vertices, indices, parts); + if (copyShapeToMesh(compoundShape->getChildTransform(i), convexShape, vertices, indices, parts, normals)) { + numSuccesses++; + } } } } else { @@ -117,11 +138,19 @@ model::MeshPointer createMeshFromShape(const void* pointer) { const btConvexShape* convexShape = static_cast(shape); btTransform transform; transform.setIdentity(); - copyShapeToMesh(transform, convexShape, vertices, indices, parts); + if (copyShapeToMesh(transform, convexShape, vertices, indices, parts, normals)) { + numSuccesses++; + } + } + if (numSuccesses > 0) { + mesh = std::make_shared(); + mesh->setVertexBuffer(vertices); + mesh->setIndexBuffer(indices); + mesh->setPartBuffer(parts); + mesh->addAttribute(gpu::Stream::NORMAL, normals); + } else { + // TODO: log failure message here } - mesh->setVertexBuffer(vertices); - mesh->setIndexBuffer(indices); - mesh->setPartBuffer(parts); } return mesh; } From 329c5432ed67a116316c818b3f486d4c464ad9cf Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 26 Jul 2016 17:28:02 -0700 Subject: [PATCH 287/332] _showCollisionHull --> _showCollisionGeometry --- libraries/render-utils/src/Model.cpp | 30 ++++++++++++++-------------- libraries/render-utils/src/Model.h | 3 +-- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 546631232e..4e4edb0202 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -37,9 +37,9 @@ float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; #define HTTP_INVALID_COM "http://invalid.com" const int NUM_COLLISION_HULL_COLORS = 24; -std::vector _collisionHullMaterials; +std::vector _collisionMaterials; -void initCollisionHullMaterials() { +void initCollisionMaterials() { // generates bright colors in red, green, blue, yellow, magenta, and cyan spectrums // (no browns, greys, or dark shades) float component[NUM_COLLISION_HULL_COLORS] = { @@ -50,7 +50,7 @@ void initCollisionHullMaterials() { 1.0f, 1.0f, 1.0f, 1.0f, 0.8f, 0.6f, 0.4f, 0.2f }; - _collisionHullMaterials.reserve(NUM_COLLISION_HULL_COLORS); + _collisionMaterials.reserve(NUM_COLLISION_HULL_COLORS); // each component gets the same cuve // but offset by a multiple of one third the full width @@ -72,7 +72,7 @@ void initCollisionHullMaterials() { material->setAlbedo(glm::vec3(red, green, blue)); material->setMetallic(0.02f); material->setRoughness(0.5f); - _collisionHullMaterials.push_back(material); + _collisionMaterials.push_back(material); } } } @@ -121,15 +121,15 @@ Model::~Model() { AbstractViewStateInterface* Model::_viewState = NULL; void Model::setShowCollisionMesh(bool value) { - if (_showCollisionHull != value) { - _showCollisionHull = value; + if (_showCollisionGeometry != value) { + _showCollisionGeometry = value; _needsFixupInScene = true; } } bool Model::needsFixupInScene() const { if ((_needsFixupInScene || !_addedToScene) && !_needsReload && isLoaded()) { - if (_showCollisionHull && _collisionGeometry) { + if (_showCollisionGeometry && _collisionGeometry) { return true; } if (!_meshStates.isEmpty() || (_renderGeometry && _renderGeometry->getMeshes().empty())) { @@ -614,13 +614,13 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr scen bool Model::addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters) { - bool readyToRender = (_showCollisionHull && _collisionGeometry) || isLoaded(); + bool readyToRender = (_showCollisionGeometry && _collisionGeometry) || isLoaded(); if (!_addedToScene && readyToRender) { createRenderItemSet(); } bool somethingAdded = false; - if (_showCollisionHull && _collisionGeometry) { + if (_showCollisionGeometry && _collisionGeometry) { if (_collisionRenderItems.empty()) { foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); @@ -1258,7 +1258,7 @@ AABox Model::getRenderableMeshBound() const { } void Model::createRenderItemSet() { - if (_showCollisionHull && _collisionGeometry) { + if (_showCollisionGeometry && _collisionGeometry) { if (_collisionRenderItemsSet.empty()) { createCollisionRenderItemSet(); } @@ -1312,8 +1312,8 @@ void Model::createVisibleRenderItemSet() { void Model::createCollisionRenderItemSet() { assert((bool)_collisionGeometry); - if (_collisionHullMaterials.empty()) { - initCollisionHullMaterials(); + if (_collisionMaterials.empty()) { + initCollisionMaterials(); } const auto& meshes = _collisionGeometry->getMeshes(); @@ -1344,7 +1344,7 @@ void Model::createCollisionRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - model::MaterialPointer& material = _collisionHullMaterials[partIndex % NUM_COLLISION_HULL_COLORS]; + model::MaterialPointer& material = _collisionMaterials[partIndex % NUM_COLLISION_HULL_COLORS]; _collisionRenderItemsSet << std::make_shared(mesh, partIndex, material, transform, offset); } } @@ -1365,7 +1365,7 @@ bool Model::initWhenReady(render::ScenePointer scene) { render::PendingChanges pendingChanges; bool addedPendingChanges = false; - if (_showCollisionHull && _collisionGeometry) { + if (_showCollisionGeometry && _collisionGeometry) { foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); @@ -1410,7 +1410,7 @@ void Model::setCollisionMesh(model::MeshPointer mesh) { _collisionWatcher.stopWatching(); _collisionGeometry = std::make_shared(mesh); - if (_showCollisionHull) { + if (_showCollisionGeometry) { _needsFixupInScene = true; // TODO: need to trigger: // (a) reconstruction of RenderItems diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index b30b0b0d76..9a0f2e6713 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -382,7 +382,6 @@ protected: void createRenderItemSet(); void createVisibleRenderItemSet(); void createCollisionRenderItemSet(); - static model::MaterialPointer _collisionHullMaterial; bool _isWireframe; @@ -404,7 +403,7 @@ protected: bool _needsFixupInScene { true }; // needs to be removed/re-added to scene bool _needsReload { true }; bool _needsUpdateClusterMatrices { true }; - bool _showCollisionHull { false }; + bool _showCollisionGeometry { false }; mutable bool _needsUpdateTextures { true }; friend class ModelMeshPartPayload; From 191b7f0f39e3b5c92a060b650d82b4aed89b3e76 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 27 Jul 2016 13:52:50 -0700 Subject: [PATCH 288/332] variable namechange: Hull --> Mesh --- libraries/entities-renderer/src/RenderableModelEntityItem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 6e5d1c7959..b80a003e8e 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -436,9 +436,9 @@ void RenderableModelEntityItem::render(RenderArgs* args) { // update whether the model should be showing collision mesh // (this may flag for fixupInScene) - bool shouldShowCollisionHull = getShapeType() != SHAPE_TYPE_STATIC_MESH && + bool shouldShowCollisionMesh = getShapeType() != SHAPE_TYPE_STATIC_MESH && (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0; - _model->setShowCollisionMesh(shouldShowCollisionHull); + _model->setShowCollisionMesh(shouldShowCollisionMesh); if (_model->needsFixupInScene()) { render::PendingChanges pendingChanges; From fe4198530f83030cb20bfbed76be698173944005 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 27 Jul 2016 13:52:55 -0700 Subject: [PATCH 289/332] don't render raw compound collision geometry --- .../render-utils/src/MeshPartPayload.cpp | 4 +- libraries/render-utils/src/MeshPartPayload.h | 2 +- libraries/render-utils/src/Model.cpp | 46 ++++++++----------- libraries/render-utils/src/Model.h | 1 + 4 files changed, 22 insertions(+), 31 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 8913a62d9d..42dd41c739 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -46,11 +46,9 @@ template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderAr } } -MeshPartPayload::MeshPartPayload(const std::shared_ptr& mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform) { - +MeshPartPayload::MeshPartPayload(const std::shared_ptr& mesh, int partIndex, model::MaterialPointer material) { updateMeshPart(mesh, partIndex); updateMaterial(material); - updateTransform(transform, offsetTransform); } void MeshPartPayload::updateMeshPart(const std::shared_ptr& drawMesh, int partIndex) { diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 29478b3b4e..a934863846 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -26,7 +26,7 @@ class Model; class MeshPartPayload { public: MeshPartPayload() {} - MeshPartPayload(const std::shared_ptr& mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform); + MeshPartPayload(const std::shared_ptr& mesh, int partIndex, model::MaterialPointer material); typedef render::Payload Payload; typedef Payload::DataPointer Pointer; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 4e4edb0202..ce8ebfb00a 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -121,15 +121,17 @@ Model::~Model() { AbstractViewStateInterface* Model::_viewState = NULL; void Model::setShowCollisionMesh(bool value) { - if (_showCollisionGeometry != value) { - _showCollisionGeometry = value; - _needsFixupInScene = true; + if (_readyToShowCollisionGeometry) { + if (_showCollisionGeometry != value) { + _showCollisionGeometry = value; + _needsFixupInScene = true; + } } } bool Model::needsFixupInScene() const { if ((_needsFixupInScene || !_addedToScene) && !_needsReload && isLoaded()) { - if (_showCollisionGeometry && _collisionGeometry) { + if (_showCollisionGeometry && _readyToShowCollisionGeometry && _collisionGeometry) { return true; } if (!_meshStates.isEmpty() || (_renderGeometry && _renderGeometry->getMeshes().empty())) { @@ -241,8 +243,6 @@ void Model::updateRenderItems() { // collision mesh does not share the same unit scale as the FBX file's mesh: only apply offset Transform collisionMeshOffset; - // adebug FIXME: recover correct behavior for collisionURL shapes - //collisionMeshOffset.postTranslate(self->_offset); collisionMeshOffset.setIdentity(); foreach (auto itemID, self->_collisionRenderItems.keys()) { pendingChanges.updateItem(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) { @@ -614,13 +614,13 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr scen bool Model::addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters) { - bool readyToRender = (_showCollisionGeometry && _collisionGeometry) || isLoaded(); + bool readyToRender = (_showCollisionGeometry && _readyToShowCollisionGeometry && _collisionGeometry) || isLoaded(); if (!_addedToScene && readyToRender) { createRenderItemSet(); } bool somethingAdded = false; - if (_showCollisionGeometry && _collisionGeometry) { + if (_showCollisionGeometry && _readyToShowCollisionGeometry && _collisionGeometry) { if (_collisionRenderItems.empty()) { foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); @@ -1258,7 +1258,7 @@ AABox Model::getRenderableMeshBound() const { } void Model::createRenderItemSet() { - if (_showCollisionGeometry && _collisionGeometry) { + if (_showCollisionGeometry && _readyToShowCollisionGeometry && _collisionGeometry) { if (_collisionRenderItemsSet.empty()) { createCollisionRenderItemSet(); } @@ -1321,18 +1321,6 @@ void Model::createCollisionRenderItemSet() { // We should not have any existing renderItems if we enter this section of code Q_ASSERT(_collisionRenderItemsSet.isEmpty()); - Transform transform; - transform.setIdentity(); - // adebug FIXME: recover correct behavior for collisionURL - //transform.setTranslation(_translation); - //transform.setRotation(_rotation); - - Transform offset; - // adebug FIXME: recover correct behavior for collisionURL - offset.setIdentity(); - //offset.setScale(_scale); - //offset.postTranslate(_offset); - // Run through all of the meshes, and place them into their segregated, but unsorted buckets uint32_t numMeshes = (uint32_t)meshes.size(); for (uint32_t i = 0; i < numMeshes; i++) { @@ -1345,7 +1333,7 @@ void Model::createCollisionRenderItemSet() { int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { model::MaterialPointer& material = _collisionMaterials[partIndex % NUM_COLLISION_HULL_COLORS]; - _collisionRenderItemsSet << std::make_shared(mesh, partIndex, material, transform, offset); + _collisionRenderItemsSet << std::make_shared(mesh, partIndex, material); } } } @@ -1365,7 +1353,7 @@ bool Model::initWhenReady(render::ScenePointer scene) { render::PendingChanges pendingChanges; bool addedPendingChanges = false; - if (_showCollisionGeometry && _collisionGeometry) { + if (_showCollisionGeometry && _readyToShowCollisionGeometry && _collisionGeometry) { foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); @@ -1387,7 +1375,7 @@ bool Model::initWhenReady(render::ScenePointer scene) { scene->enqueuePendingChanges(pendingChanges); // NOTE: updateRender items enqueues identical pendingChanges (using a lambda) // so it looks like we're doing double work here, but I don't want to remove the call - // for fear there is some sideeffect we'll miss. -- Andrew 2016.07.21 + // for fear there is some side effect we'll miss. -- Andrew 2016.07.21 // TODO: figure out if we really need this call to updateRenderItems() or not. updateRenderItems(); } @@ -1410,11 +1398,15 @@ void Model::setCollisionMesh(model::MeshPointer mesh) { _collisionWatcher.stopWatching(); _collisionGeometry = std::make_shared(mesh); + // HACK: we don't want to show the _collisionGeometry until we're ready (e.g. it has been created) + // hence we track whether it has been created using _readyToShowCollisionGeoemtry, because there + // is an ambiguous case where _collisionGeometry is valid (from CompoundURL) but has not yet been + // properly computed (zeroed offset transform) using the CollisionRenderMeshCache. + // + // TODO: At the moment we create the collision mesh for every model that has a collision shape + // as soon as we know the shape, but we SHOULD only ever create the render mesh when we need it. if (_showCollisionGeometry) { _needsFixupInScene = true; - // TODO: need to trigger: - // (a) reconstruction of RenderItems - // (b) and reinsertion into scene if we are showing collision geometry } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 9a0f2e6713..3fc681261a 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -404,6 +404,7 @@ protected: bool _needsReload { true }; bool _needsUpdateClusterMatrices { true }; bool _showCollisionGeometry { false }; + bool _readyToShowCollisionGeometry { false }; mutable bool _needsUpdateTextures { true }; friend class ModelMeshPartPayload; From ef597265d76bcc1f3a0e7e0ba7a75b7816f1800e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 28 Jul 2016 07:30:27 -0700 Subject: [PATCH 290/332] remove unimplemented declaration --- libraries/networking/src/ResourceCache.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index a2a5b4cbbe..eba84dddd4 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -226,8 +226,6 @@ private: void resetResourceCounters(); void removeResource(const QUrl& url, qint64 size = 0); - void getResourceAsynchronously(const QUrl& url); - static int _requestLimit; static int _requestsActive; From b0a0293e6e0a19b0efa911c96a23ea9065f20208 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 28 Jul 2016 07:31:32 -0700 Subject: [PATCH 291/332] move compoundResource out of Model class --- .../src/EntityTreeRenderer.cpp | 18 -------- .../src/EntityTreeRenderer.h | 1 - .../src/RenderableModelEntityItem.cpp | 46 +++++++++++++------ .../src/RenderableModelEntityItem.h | 2 + libraries/entities/src/EntityTree.h | 1 - libraries/render-utils/src/Model.cpp | 43 ++++------------- libraries/render-utils/src/Model.h | 9 ---- 7 files changed, 42 insertions(+), 78 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 39031acaa1..209a27739b 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -499,22 +499,6 @@ ModelPointer EntityTreeRenderer::getModelForEntityItem(EntityItemPointer entityI return result; } -const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(EntityItemPointer entityItem) { - const FBXGeometry* result = NULL; - - if (entityItem->getType() == EntityTypes::Model) { - std::shared_ptr modelEntityItem = - std::dynamic_pointer_cast(entityItem); - if (modelEntityItem->hasCompoundShapeURL()) { - ModelPointer model = modelEntityItem->getModel(this); - if (model && model->isCollisionLoaded()) { - result = &model->getCollisionFBXGeometry(); - } - } - } - return result; -} - void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) { std::static_pointer_cast(_tree)->processEraseMessage(message, sourceNode); } @@ -536,7 +520,6 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString model->setLoadingPriority(loadingPriority); model->init(); model->setURL(QUrl(url)); - model->setCollisionModelURL(QUrl(collisionUrl)); return model; } @@ -553,7 +536,6 @@ ModelPointer EntityTreeRenderer::updateModel(ModelPointer model, const QString& } model->setURL(QUrl(newUrl)); - model->setCollisionModelURL(QUrl(collisionUrl)); return model; } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 5d634fa6ca..5664f33041 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -65,7 +65,6 @@ public: virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) override; virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) override; - virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem) override; /// clears the tree virtual void clear() override; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index b80a003e8e..84646d7607 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -494,7 +494,7 @@ ModelPointer RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL(), renderer->getEntityLoadingPriority(*this)); _needsInitialSimulation = true; // If we need to change URLs, update it *after rendering* (to avoid access violations) - } else if ((QUrl(getModelURL()) != _model->getURL() || QUrl(getCompoundShapeURL()) != _model->getCollisionURL())) { + } else if (QUrl(getModelURL()) != _model->getURL()) { QMetaObject::invokeMethod(_myRenderer, "updateModel", Qt::QueuedConnection, Q_ARG(ModelPointer, _model), Q_ARG(const QString&, getModelURL()), @@ -566,6 +566,18 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori face, surfaceNormal, extraInfo, precisionPicking); } +void RenderableModelEntityItem::setShapeType(ShapeType type) { + ModelEntityItem::setShapeType(type); + if (_shapeType == SHAPE_TYPE_COMPOUND) { + if (!_compoundShapeResource && !_compoundShapeURL.isEmpty()) { + _compoundShapeResource = DependencyManager::get()->getGeometryResource(getCompoundShapeURL()); + } + } else if (_compoundShapeResource && !_compoundShapeURL.isEmpty()) { + // the compoundURL has been set but the shapeType does not agree + _compoundShapeResource.reset(); + } +} + void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) { auto currentCompoundShapeURL = getCompoundShapeURL(); ModelEntityItem::setCompoundShapeURL(url); @@ -575,6 +587,9 @@ void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) { if (tree) { QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID())); } + if (_shapeType == SHAPE_TYPE_COMPOUND) { + _compoundShapeResource = DependencyManager::get()->getGeometryResource(url); + } } } @@ -582,7 +597,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { ShapeType type = getShapeType(); if (type == SHAPE_TYPE_COMPOUND) { - if (!_model || _model->getCollisionURL().isEmpty()) { + if (!_model || _compoundShapeURL.isEmpty()) { EntityTreePointer tree = getTree(); if (tree) { QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID())); @@ -595,15 +610,18 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { return false; } - if (_model->isLoaded() && _model->isCollisionLoaded()) { - // we have both URLs AND both geometries AND they are both fully loaded. - if (_needsInitialSimulation) { - // the _model's offset will be wrong until _needsInitialSimulation is false - PerformanceTimer perfTimer("_model->simulate"); - doInitialModelSimulation(); + if (_model->isLoaded()) { + if (_compoundShapeResource && _compoundShapeResource->isLoaded()) { + // we have both URLs AND both geometries AND they are both fully loaded. + if (_needsInitialSimulation) { + // the _model's offset will be wrong until _needsInitialSimulation is false + PerformanceTimer perfTimer("_model->simulate"); + doInitialModelSimulation(); + } + return true; + } else if (!_compoundShapeURL.isEmpty()) { + _compoundShapeResource = DependencyManager::get()->getGeometryResource(_compoundShapeURL); } - - return true; } // the model is still being downloaded. @@ -625,8 +643,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // should never fall in here when collision model not fully loaded // hence we assert that all geometries exist and are loaded - assert(_model && _model->isLoaded() && _model->isCollisionLoaded()); - const FBXGeometry& collisionGeometry = _model->getCollisionFBXGeometry(); + assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); + const FBXGeometry& collisionGeometry = _compoundShapeResource->getFBXGeometry(); ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); pointCollection.clear(); @@ -956,8 +974,8 @@ void RenderableModelEntityItem::setCollisionShape(const btCollisionShape* shape) } bool RenderableModelEntityItem::contains(const glm::vec3& point) const { - if (EntityItem::contains(point) && _model && _model->isCollisionLoaded()) { - return _model->getCollisionFBXGeometry().convexHullContains(worldToEntity(point)); + if (EntityItem::contains(point) && _model && _compoundShapeResource && _compoundShapeResource->isLoaded()) { + return _compoundShapeResource->getFBXGeometry().convexHullContains(worldToEntity(point)); } return false; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 16cd9c8bc5..09468dfae0 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -56,6 +56,7 @@ public: virtual bool needsToCallUpdate() const override; virtual void update(const quint64& now) override; + virtual void setShapeType(ShapeType type) override; virtual void setCompoundShapeURL(const QString& url) override; virtual bool isReadyToComputeShape() override; @@ -100,6 +101,7 @@ private: QVariantMap parseTexturesToMap(QString textures); void remapTextures(); + GeometryResource::Pointer _compoundShapeResource; ModelPointer _model = nullptr; bool _needsInitialSimulation = true; bool _needsModelReload = true; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 15daf3bf3c..7dc999aac2 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -40,7 +40,6 @@ class EntityItemFBXService { public: virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) = 0; virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) = 0; - virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem) = 0; }; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ce8ebfb00a..d1269f769a 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -82,7 +82,6 @@ Model::Model(RigPointer rig, QObject* parent) : _renderGeometry(), _collisionGeometry(), _renderWatcher(_renderGeometry), - _collisionWatcher(_collisionGeometry), _translation(0.0f), _rotation(), _scale(1.0f, 1.0f, 1.0f), @@ -111,7 +110,6 @@ Model::Model(RigPointer rig, QObject* parent) : setSnapModelToRegistrationPoint(true, glm::vec3(0.5f)); connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, &Model::loadURLFinished); - connect(&_collisionWatcher, &GeometryResourceWatcher::finished, this, &Model::loadCollisionModelURLFinished); } Model::~Model() { @@ -121,17 +119,15 @@ Model::~Model() { AbstractViewStateInterface* Model::_viewState = NULL; void Model::setShowCollisionMesh(bool value) { - if (_readyToShowCollisionGeometry) { - if (_showCollisionGeometry != value) { - _showCollisionGeometry = value; - _needsFixupInScene = true; - } + if (_showCollisionGeometry != value) { + _showCollisionGeometry = value; + _needsFixupInScene = true; } } bool Model::needsFixupInScene() const { if ((_needsFixupInScene || !_addedToScene) && !_needsReload && isLoaded()) { - if (_showCollisionGeometry && _readyToShowCollisionGeometry && _collisionGeometry) { + if (_showCollisionGeometry && _collisionGeometry) { return true; } if (!_meshStates.isEmpty() || (_renderGeometry && _renderGeometry->getMeshes().empty())) { @@ -614,13 +610,13 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr scen bool Model::addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters) { - bool readyToRender = (_showCollisionGeometry && _readyToShowCollisionGeometry && _collisionGeometry) || isLoaded(); + bool readyToRender = (_showCollisionGeometry && _collisionGeometry) || isLoaded(); if (!_addedToScene && readyToRender) { createRenderItemSet(); } bool somethingAdded = false; - if (_showCollisionGeometry && _readyToShowCollisionGeometry && _collisionGeometry) { + if (_showCollisionGeometry && _collisionGeometry) { if (_collisionRenderItems.empty()) { foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); @@ -864,23 +860,6 @@ void Model::loadURLFinished(bool success) { emit setURLFinished(success); } -void Model::setCollisionModelURL(const QUrl& url) { - if (_collisionUrl == url && _collisionWatcher.getURL() == url) { - return; - } - _collisionUrl = url; - _collisionGeometryRequestFailed = false; - _collisionWatcher.setResource(DependencyManager::get()->getGeometryResource(url)); -} - -void Model::loadCollisionModelURLFinished(bool success) { - if (!success) { - _collisionGeometryRequestFailed = true; - } - - emit setCollisionModelURLFinished(success); -} - bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { return _rig->getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation); } @@ -1258,7 +1237,7 @@ AABox Model::getRenderableMeshBound() const { } void Model::createRenderItemSet() { - if (_showCollisionGeometry && _readyToShowCollisionGeometry && _collisionGeometry) { + if (_showCollisionGeometry && _collisionGeometry) { if (_collisionRenderItemsSet.empty()) { createCollisionRenderItemSet(); } @@ -1353,7 +1332,7 @@ bool Model::initWhenReady(render::ScenePointer scene) { render::PendingChanges pendingChanges; bool addedPendingChanges = false; - if (_showCollisionGeometry && _readyToShowCollisionGeometry && _collisionGeometry) { + if (_showCollisionGeometry && _collisionGeometry) { foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); @@ -1395,14 +1374,8 @@ public: }; void Model::setCollisionMesh(model::MeshPointer mesh) { - _collisionWatcher.stopWatching(); _collisionGeometry = std::make_shared(mesh); - // HACK: we don't want to show the _collisionGeometry until we're ready (e.g. it has been created) - // hence we track whether it has been created using _readyToShowCollisionGeoemtry, because there - // is an ambiguous case where _collisionGeometry is valid (from CompoundURL) but has not yet been - // properly computed (zeroed offset transform) using the CollisionRenderMeshCache. - // // TODO: At the moment we create the collision mesh for every model that has a collision shape // as soon as we know the shape, but we SHOULD only ever create the render mesh when we need it. if (_showCollisionGeometry) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 3fc681261a..edb8f0b6ae 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -115,7 +115,6 @@ public: const QVector& vertices, const QVector& normals); bool isLoaded() const { return (bool)_renderGeometry; } - bool isCollisionLoaded() const { return (bool)_collisionGeometry; } void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; } bool isWireframe() const { return _isWireframe; } @@ -142,13 +141,9 @@ public: /// Provided as a convenience, will crash if !isLoaded() // And so that getGeometry() isn't chained everywhere const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return _renderGeometry->getFBXGeometry(); } - /// Provided as a convenience, will crash if !isCollisionLoaded() - const FBXGeometry& getCollisionFBXGeometry() const { assert(isCollisionLoaded()); return _collisionGeometry->getFBXGeometry(); } // Set the model to use for collisions. // Should only be called from the model's rendering thread to avoid access violations of changed geometry. - Q_INVOKABLE void setCollisionModelURL(const QUrl& url); - const QUrl& getCollisionURL() const { return _collisionUrl; } bool isActive() const { return isLoaded(); } @@ -246,7 +241,6 @@ public: public slots: void loadURLFinished(bool success); - void loadCollisionModelURLFinished(bool success); signals: void setURLFinished(bool success); @@ -288,7 +282,6 @@ protected: Geometry::Pointer _collisionGeometry; GeometryResourceWatcher _renderWatcher; - GeometryResourceWatcher _collisionWatcher; glm::vec3 _translation; glm::quat _rotation; @@ -356,7 +349,6 @@ protected: QVector _blendshapeCoefficients; QUrl _url; - QUrl _collisionUrl; bool _isVisible; gpu::Buffers _blendedVertexBuffers; @@ -404,7 +396,6 @@ protected: bool _needsReload { true }; bool _needsUpdateClusterMatrices { true }; bool _showCollisionGeometry { false }; - bool _readyToShowCollisionGeometry { false }; mutable bool _needsUpdateTextures { true }; friend class ModelMeshPartPayload; From eb65be5478af994c813b3c3a34a130218d07e9d7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 28 Jul 2016 07:37:54 -0700 Subject: [PATCH 292/332] remove cruft argument from allocateModel() --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 5 ++--- libraries/entities-renderer/src/EntityTreeRenderer.h | 2 +- .../entities-renderer/src/RenderableModelEntityItem.cpp | 5 ++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 209a27739b..25352255fa 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -503,15 +503,14 @@ void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const Sha std::static_pointer_cast(_tree)->processEraseMessage(message, sourceNode); } -ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority) { +ModelPointer EntityTreeRenderer::allocateModel(const QString& url, loadingPriority) { ModelPointer model = nullptr; // Only create and delete models on the thread that owns the EntityTreeRenderer if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "allocateModel", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ModelPointer, model), - Q_ARG(const QString&, url), - Q_ARG(const QString&, collisionUrl)); + Q_ARG(const QString&, url)); return model; } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 5664f33041..7ca11ccdbb 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -73,7 +73,7 @@ public: void reloadEntityScripts(); /// if a renderable entity item needs a model, we will allocate it for them - Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority = 0.0f); + Q_INVOKABLE ModelPointer allocateModel(const QString& url, float loadingPriority = 0.0f); /// if a renderable entity item needs to update the URL of a model, we will handle that for the entity Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl, const QString& collisionUrl); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 84646d7607..93ad4e17d5 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -491,14 +491,13 @@ ModelPointer RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { if (!getModelURL().isEmpty()) { // If we don't have a model, allocate one *immediately* if (!_model) { - _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL(), renderer->getEntityLoadingPriority(*this)); + _model = _myRenderer->allocateModel(getModelURL(), renderer->getEntityLoadingPriority(*this)); _needsInitialSimulation = true; // If we need to change URLs, update it *after rendering* (to avoid access violations) } else if (QUrl(getModelURL()) != _model->getURL()) { QMetaObject::invokeMethod(_myRenderer, "updateModel", Qt::QueuedConnection, Q_ARG(ModelPointer, _model), - Q_ARG(const QString&, getModelURL()), - Q_ARG(const QString&, getCompoundShapeURL())); + Q_ARG(const QString&, getModelURL())); _needsInitialSimulation = true; } // Else we can just return the _model From 68dd66daec3e855344e07ceacb0e54a7ce9c02e4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 28 Jul 2016 09:33:25 -0700 Subject: [PATCH 293/332] create collision render geometry only when needed --- .../src/RenderableModelEntityItem.cpp | 38 +++++++++++------ .../src/RenderableModelEntityItem.h | 1 + libraries/render-utils/src/Model.cpp | 42 +++++-------------- libraries/render-utils/src/Model.h | 3 -- 4 files changed, 37 insertions(+), 47 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 93ad4e17d5..2d03f76ae6 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -29,6 +29,9 @@ #include "RenderableModelEntityItem.h" #include "RenderableEntityItem.h" +static CollisionRenderMeshCache collisionMeshCache; + + EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity{ new RenderableModelEntityItem(entityID, properties.getDimensionsInitialized()) }; entity->setProperties(properties); @@ -434,11 +437,28 @@ void RenderableModelEntityItem::render(RenderArgs* args) { // Remap textures for the next frame to avoid flicker remapTextures(); - // update whether the model should be showing collision mesh - // (this may flag for fixupInScene) - bool shouldShowCollisionMesh = getShapeType() != SHAPE_TYPE_STATIC_MESH && + // update whether the model should be showing collision mesh (this may flag for fixupInScene) + ShapeType type = getShapeType(); + bool shouldShowCollisionGeometry = type != SHAPE_TYPE_STATIC_MESH && + type != SHAPE_TYPE_NONE && (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0; - _model->setShowCollisionMesh(shouldShowCollisionMesh); + if (shouldShowCollisionGeometry != _showCollisionGeometry) { + _showCollisionGeometry = shouldShowCollisionGeometry; + if (_showCollisionGeometry) { + // NOTE: it is OK if _collisionMeshKey is nullptr + model::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey); + // NOTE: the model will render the collisionGeometry if it has one + _model->setCollisionMesh(mesh); + } else { + // release mesh + if (_collisionMeshKey) { + collisionMeshCache.releaseMesh(_collisionMeshKey); + } + // clear model's collision geometry + model::MeshPointer mesh = nullptr; + _model->setCollisionMesh(mesh); + } + } if (_model->needsFixupInScene()) { render::PendingChanges pendingChanges; @@ -954,21 +974,15 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } } -static CollisionRenderMeshCache collisionMeshCache; - void RenderableModelEntityItem::setCollisionShape(const btCollisionShape* shape) { const void* key = static_cast(shape); if (_collisionMeshKey != key) { if (_collisionMeshKey) { - // releasing the shape is not strictly necessary, but - // we do it as hint to the cache's garbage collection system collisionMeshCache.releaseMesh(_collisionMeshKey); } _collisionMeshKey = key; - model::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey); - if (_model) { - _model->setCollisionMesh(mesh); - } + // toggle _showCollisionGeometry forces re-evaluation later + _showCollisionGeometry = !_showCollisionGeometry; } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 09468dfae0..3c2333e679 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -119,6 +119,7 @@ private: bool getAnimationFrame(); bool _needsJointSimulation { false }; + bool _showCollisionGeometry { false }; const void* _collisionMeshKey { nullptr }; }; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d1269f769a..ca0994b837 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -118,29 +118,8 @@ Model::~Model() { AbstractViewStateInterface* Model::_viewState = NULL; -void Model::setShowCollisionMesh(bool value) { - if (_showCollisionGeometry != value) { - _showCollisionGeometry = value; - _needsFixupInScene = true; - } -} - bool Model::needsFixupInScene() const { - if ((_needsFixupInScene || !_addedToScene) && !_needsReload && isLoaded()) { - if (_showCollisionGeometry && _collisionGeometry) { - return true; - } - if (!_meshStates.isEmpty() || (_renderGeometry && _renderGeometry->getMeshes().empty())) { - if (_needsUpdateTextures) { - if (!_renderGeometry->areTexturesLoaded()) { - return false; - } - _needsUpdateTextures = false; - } - return true; - } - } - return false; + return (_needsFixupInScene || !_addedToScene) && !_needsReload && isLoaded(); } // TODO?: should we combine translation and rotation into single method to avoid double-work? @@ -610,13 +589,13 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr scen bool Model::addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters) { - bool readyToRender = (_showCollisionGeometry && _collisionGeometry) || isLoaded(); + bool readyToRender = _collisionGeometry || isLoaded(); if (!_addedToScene && readyToRender) { createRenderItemSet(); } bool somethingAdded = false; - if (_showCollisionGeometry && _collisionGeometry) { + if (_collisionGeometry) { if (_collisionRenderItems.empty()) { foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); @@ -1237,7 +1216,7 @@ AABox Model::getRenderableMeshBound() const { } void Model::createRenderItemSet() { - if (_showCollisionGeometry && _collisionGeometry) { + if (_collisionGeometry) { if (_collisionRenderItemsSet.empty()) { createCollisionRenderItemSet(); } @@ -1332,7 +1311,7 @@ bool Model::initWhenReady(render::ScenePointer scene) { render::PendingChanges pendingChanges; bool addedPendingChanges = false; - if (_showCollisionGeometry && _collisionGeometry) { + if (_collisionGeometry) { foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); @@ -1374,13 +1353,12 @@ public: }; void Model::setCollisionMesh(model::MeshPointer mesh) { - _collisionGeometry = std::make_shared(mesh); - - // TODO: At the moment we create the collision mesh for every model that has a collision shape - // as soon as we know the shape, but we SHOULD only ever create the render mesh when we need it. - if (_showCollisionGeometry) { - _needsFixupInScene = true; + if (mesh) { + _collisionGeometry = std::make_shared(mesh); + } else { + _collisionGeometry.reset(); } + _needsFixupInScene = true; } ModelBlender::ModelBlender() : diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index edb8f0b6ae..08a3a2fc54 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -82,8 +82,6 @@ public: void setVisibleInScene(bool newValue, std::shared_ptr scene); bool needsFixupInScene() const; - void setShowCollisionMesh(bool value); - bool readyToAddToScene(RenderArgs* renderArgs = nullptr) const { return !_needsReload && isRenderable() && isActive(); } @@ -395,7 +393,6 @@ protected: bool _needsFixupInScene { true }; // needs to be removed/re-added to scene bool _needsReload { true }; bool _needsUpdateClusterMatrices { true }; - bool _showCollisionGeometry { false }; mutable bool _needsUpdateTextures { true }; friend class ModelMeshPartPayload; From 83426fd5a8c0c4804e584fbff2bd24d7800c9baf Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 28 Jul 2016 10:30:28 -0700 Subject: [PATCH 294/332] handle transitions to/from STATIC_MESH when rendering collision geometry --- .../src/RenderableModelEntityItem.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 2d03f76ae6..d47c9afab6 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -438,13 +438,11 @@ void RenderableModelEntityItem::render(RenderArgs* args) { remapTextures(); // update whether the model should be showing collision mesh (this may flag for fixupInScene) - ShapeType type = getShapeType(); - bool shouldShowCollisionGeometry = type != SHAPE_TYPE_STATIC_MESH && - type != SHAPE_TYPE_NONE && - (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0; - if (shouldShowCollisionGeometry != _showCollisionGeometry) { - _showCollisionGeometry = shouldShowCollisionGeometry; - if (_showCollisionGeometry) { + bool showingCollisionGeometry = (bool)(args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS); + if (showingCollisionGeometry != _showCollisionGeometry) { + ShapeType type = getShapeType(); + _showCollisionGeometry = showingCollisionGeometry; + if (_showCollisionGeometry && type != SHAPE_TYPE_STATIC_MESH && type != SHAPE_TYPE_NONE) { // NOTE: it is OK if _collisionMeshKey is nullptr model::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey); // NOTE: the model will render the collisionGeometry if it has one From 269b7ae7c163644b6b2549b69b0ecfb3d751f9d8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 28 Jul 2016 11:44:54 -0700 Subject: [PATCH 295/332] remove commented out cruft --- libraries/physics/src/CollisionRenderMeshCache.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/libraries/physics/src/CollisionRenderMeshCache.h b/libraries/physics/src/CollisionRenderMeshCache.h index 0c789f3da9..910b43996e 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.h +++ b/libraries/physics/src/CollisionRenderMeshCache.h @@ -18,18 +18,6 @@ #include -/* -class btCollisionShape; - -namespace std { - template <> - struct hash { - std::size_t operator()(btCollisionShape* shape) const { - return (hash()((void*)shape)); - } - }; -} -*/ class CollisionRenderMeshCache { public: From 48c73acae0616e674558da5a9871dd960bfd3e28 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 28 Jul 2016 11:46:02 -0700 Subject: [PATCH 296/332] restore privacy of GeometryResourceWatcher methods --- libraries/model-networking/src/model-networking/ModelCache.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index fa93d3c899..bcca846625 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -111,6 +111,7 @@ public: QUrl getURL() const { return (bool)_resource ? _resource->getURL() : QUrl(); } +private: void startWatching(); void stopWatching(); From e5547122599d5f06d4d93c3e8d1012903c763e9e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 28 Jul 2016 12:07:13 -0700 Subject: [PATCH 297/332] remove entities-renderer dependency on physics lib --- libraries/entities-renderer/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 9218b94fe1..0063f4a701 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME entities-renderer) AUTOSCRIBE_SHADER_LIB(gpu model procedural render render-utils) setup_hifi_library(Widgets Network Script) -link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils physics) +link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils) target_bullet() From d801b3521c66671833b810fa2f42e9a9b0ec5794 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 28 Jul 2016 14:18:39 -0700 Subject: [PATCH 298/332] fix warnings on windows --- libraries/physics/src/CollisionRenderMeshCache.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index 8b27bbae24..bd6b761f4e 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -41,7 +41,7 @@ bool copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, { // new part model::Mesh::Part part; - part._startIndex = indices.getNumElements(); + part._startIndex = (model::Index)indices.getNumElements(); part._numIndices = (model::Index)numHullIndices; // FIXME: the render code cannot handle the case where part._baseVertex != 0 //part._baseVertex = vertices.getNumElements(); // DOES NOT WORK @@ -54,7 +54,7 @@ bool copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, } const int32_t SIZE_OF_VEC3 = 3 * sizeof(float); - model::Index indexOffset = vertices.getNumElements(); + model::Index indexOffset = (model::Index)vertices.getNumElements(); { // new indices const uint32_t* hullIndices = hull.getIndexPointer(); @@ -201,7 +201,7 @@ bool CollisionRenderMeshCache::releaseMesh(CollisionRenderMeshCache::Key key) { } void CollisionRenderMeshCache::collectGarbage() { - uint32_t numShapes = _pendingGarbage.size(); + uint32_t numShapes = (uint32_t)_pendingGarbage.size(); for (uint32_t i = 0; i < numShapes; ++i) { CollisionRenderMeshCache::Key key = _pendingGarbage[i]; CollisionMeshMap::const_iterator itr = _meshMap.find(key); From 2f16aca114194575d734fbd983a8bbb5da9fca01 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 17 Aug 2016 09:57:53 -0700 Subject: [PATCH 299/332] remove cruft and fix typo in rebase --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 2 +- libraries/entities-renderer/src/RenderableModelEntityItem.cpp | 2 ++ libraries/render-utils/src/Model.h | 3 --- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 25352255fa..bc045a99d7 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -503,7 +503,7 @@ void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const Sha std::static_pointer_cast(_tree)->processEraseMessage(message, sourceNode); } -ModelPointer EntityTreeRenderer::allocateModel(const QString& url, loadingPriority) { +ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loadingPriority) { ModelPointer model = nullptr; // Only create and delete models on the thread that owns the EntityTreeRenderer diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index d47c9afab6..3ba48b931d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -180,6 +180,7 @@ void RenderableModelEntityItem::doInitialModelSimulation() { _needsInitialSimulation = false; } +/* // TODO: we need a solution for changes to the postion/rotation/etc of a model... // this current code path only addresses that in this setup case... not the changing/moving case bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) { @@ -197,6 +198,7 @@ bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) { bool ready = !_needsInitialSimulation && _model && _model->readyToAddToScene(); return ready; } +*/ class RenderableModelEntityItemMeta { public: diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 08a3a2fc54..ff702d1ec5 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -82,9 +82,6 @@ public: void setVisibleInScene(bool newValue, std::shared_ptr scene); bool needsFixupInScene() const; - bool readyToAddToScene(RenderArgs* renderArgs = nullptr) const { - return !_needsReload && isRenderable() && isActive(); - } bool needsReload() const { return _needsReload; } bool initWhenReady(render::ScenePointer scene); bool addToScene(std::shared_ptr scene, From e4317adcf19f24737f282d4fa88e4c25ea8ed3c4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 17 Aug 2016 09:58:39 -0700 Subject: [PATCH 300/332] remove cruft --- .../src/RenderableModelEntityItem.cpp | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 3ba48b931d..970969f1c9 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -180,26 +180,6 @@ void RenderableModelEntityItem::doInitialModelSimulation() { _needsInitialSimulation = false; } -/* -// TODO: we need a solution for changes to the postion/rotation/etc of a model... -// this current code path only addresses that in this setup case... not the changing/moving case -bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) { - if (!_model && renderArgs) { - // TODO: this getModel() appears to be about 3% of model render time. We should optimize - PerformanceTimer perfTimer("getModel"); - EntityTreeRenderer* renderer = static_cast(renderArgs->_renderer); - getModel(renderer); - } - if (renderArgs && _model && _needsInitialSimulation && _model->isActive() && _model->isLoaded()) { - // make sure to simulate so everything gets set up correctly for rendering - doInitialModelSimulation(); - _model->renderSetup(renderArgs); - } - bool ready = !_needsInitialSimulation && _model && _model->readyToAddToScene(); - return ready; -} -*/ - class RenderableModelEntityItemMeta { public: RenderableModelEntityItemMeta(EntityItemPointer entity) : entity(entity){ } From acaa77b75a3ea064575fde92e48d19892d390cd9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 22 Aug 2016 09:06:13 -0700 Subject: [PATCH 301/332] remove cruft from debug efforts --- libraries/entities/src/EntityItem.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 1ad1d938a7..e572bf4de8 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -327,10 +327,6 @@ public: virtual void setCollisionShape(const btCollisionShape* shape) {} - // these are only needed because the names don't match - virtual const glm::quat getRotation() const { return getOrientation(); } - virtual void setRotation(glm::quat orientation) { setOrientation(orientation); } - // updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags virtual void updateRegistrationPoint(const glm::vec3& value); void updatePosition(const glm::vec3& value); From 19cd4648adfd8216cc30f71e42a295a407bede03 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 24 Aug 2016 11:21:47 -0700 Subject: [PATCH 302/332] scale collision render mesh correctly --- libraries/entities/src/EntityItem.cpp | 2 +- libraries/physics/src/CollisionRenderMeshCache.cpp | 4 ---- libraries/render-utils/src/Model.cpp | 9 +++++++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 29cbfd79e6..68636415f8 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2213,4 +2213,4 @@ void EntityItem::globalizeProperties(EntityItemProperties& properties, const QSt } QUuid empty; properties.setParentID(empty); -} \ No newline at end of file +} diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index bd6b761f4e..517e25e1c4 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -190,10 +190,6 @@ bool CollisionRenderMeshCache::releaseMesh(CollisionRenderMeshCache::Key key) { } CollisionMeshMap::const_iterator itr = _meshMap.find(key); if (itr != _meshMap.end()) { - // we hold at least one reference, and the outer scope also holds at least one - // so we assert that the reference count is not 1 - assert((*itr).second.use_count() != 1); - _pendingGarbage.push_back(key); return true; } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ca0994b837..6f435b2c8d 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -166,6 +166,11 @@ void Model::updateRenderItems() { return; } + glm::vec3 scale = getScale(); + if (_collisionGeometry) { + // _collisionGeometry is already scaled + scale = glm::vec3(1.0f); + } _needsUpdateClusterMatrices = true; _renderItemsNeedUpdate = false; @@ -173,7 +178,7 @@ void Model::updateRenderItems() { // the application will ensure only the last lambda is actually invoked. void* key = (void*)this; std::weak_ptr weakSelf = shared_from_this(); - AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf]() { + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() { // do nothing, if the model has already been destroyed. auto self = weakSelf.lock(); @@ -184,7 +189,7 @@ void Model::updateRenderItems() { render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); Transform modelTransform; - modelTransform.setScale(self->_scale); + modelTransform.setScale(scale); modelTransform.setTranslation(self->_translation); modelTransform.setRotation(self->_rotation); From 502146b1718df33b43ca9505d41185073c6dc194 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 24 Aug 2016 17:50:13 -0700 Subject: [PATCH 303/332] distribute reg offset to compound children --- libraries/physics/src/ShapeFactory.cpp | 28 ++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index f11b0f95dc..a67e0053da 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -346,14 +346,26 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) } if (shape) { if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) { - // this shape has an offset, which we support by wrapping the true shape - // in a btCompoundShape with a local transform - auto compound = new btCompoundShape(); - btTransform trans; - trans.setIdentity(); - trans.setOrigin(glmToBullet(info.getOffset())); - compound->addChildShape(trans, shape); - shape = compound; + // we need to apply an offset + btTransform offset; + offset.setIdentity(); + offset.setOrigin(glmToBullet(info.getOffset())); + + if (shape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) { + // this shape is already compound + // walk through the child shapes and adjust their transforms + btCompoundShape* compound = static_cast(shape); + int32_t numSubShapes = compound->getNumChildShapes(); + for (int32_t i = 0; i < numSubShapes; ++i) { + compound->updateChildTransform(i, offset * compound->getChildTransform(i), false); + } + compound->recalculateLocalAabb(); + } else { + // wrap this shape in a compound + auto compound = new btCompoundShape(); + compound->addChildShape(offset, shape); + shape = compound; + } } } return shape; From b95cb566cd377a6ff968ede4852c3bd9b5eda5f6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 24 Aug 2016 17:51:33 -0700 Subject: [PATCH 304/332] fix collision mesh for non-trivial registration --- .../src/RenderableModelEntityItem.cpp | 34 ++++++++++--------- .../src/RenderableModelEntityItem.h | 2 +- libraries/render-utils/src/Model.cpp | 9 ++++- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 970969f1c9..fc3245f322 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -631,7 +631,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { return true; } -void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { +void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { const uint32_t TRIANGLE_STRIDE = 3; const uint32_t QUAD_STRIDE = 4; @@ -645,7 +645,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); const FBXGeometry& collisionGeometry = _compoundShapeResource->getFBXGeometry(); - ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); + ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); uint32_t i = 0; @@ -721,15 +721,14 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { glm::vec3 scaleToFit = dimensions / _model->getFBXGeometry().getUnscaledMeshExtents().size(); // multiply each point by scale before handing the point-set off to the physics engine. // also determine the extents of the collision model. + glm::vec3 registrationOffset = dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()); for (int32_t i = 0; i < pointCollection.size(); i++) { for (int32_t j = 0; j < pointCollection[i].size(); j++) { - // compensate for registration - pointCollection[i][j] += _model->getOffset(); - // scale so the collision points match the model points - pointCollection[i][j] *= scaleToFit; + // back compensate for registration so we can apply that offset to the shapeInfo later + pointCollection[i][j] = scaleToFit * (pointCollection[i][j] + _model->getOffset()) - registrationOffset; } } - info.setParams(type, dimensions, _compoundShapeURL); + shapeInfo.setParams(type, dimensions, _compoundShapeURL); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { // should never fall in here when model not fully loaded assert(_model && _model->isLoaded()); @@ -742,29 +741,31 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { const FBXGeometry& fbxGeometry = _model->getFBXGeometry(); int numFbxMeshes = fbxGeometry.meshes.size(); int totalNumVertices = 0; + glm::mat4 invRegistraionOffset = glm::translate(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); for (int i = 0; i < numFbxMeshes; i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); if (mesh.clusters.size() > 0) { const FBXCluster& cluster = mesh.clusters.at(0); auto jointMatrix = _model->getRig()->getJointTransform(cluster.jointIndex); - localTransforms.push_back(jointMatrix * cluster.inverseBindMatrix); + // we backtranslate by the registration offset so we can apply that offset to the shapeInfo later + localTransforms.push_back(invRegistraionOffset * jointMatrix * cluster.inverseBindMatrix); } else { glm::mat4 identity; - localTransforms.push_back(identity); + localTransforms.push_back(invRegistraionOffset); } totalNumVertices += mesh.vertices.size(); } const int32_t MAX_VERTICES_PER_STATIC_MESH = 1e6; if (totalNumVertices > MAX_VERTICES_PER_STATIC_MESH) { qWarning() << "model" << getModelURL() << "has too many vertices" << totalNumVertices << "and will collide as a box."; - info.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions); + shapeInfo.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions); return; } auto& meshes = _model->getGeometry()->getMeshes(); int32_t numMeshes = (int32_t)(meshes.size()); - ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); + ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { pointCollection.resize(numMeshes); @@ -772,7 +773,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { pointCollection.resize(1); } - ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); + ShapeInfo::TriangleIndices& triangleIndices = shapeInfo.getTriangleIndices(); triangleIndices.clear(); Extents extents; @@ -946,12 +947,13 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } } - info.setParams(type, 0.5f * dimensions, _modelURL); + shapeInfo.setParams(type, 0.5f * dimensions, _modelURL); } else { - ModelEntityItem::computeShapeInfo(info); - info.setParams(type, 0.5f * dimensions); - adjustShapeInfoByRegistration(info); + ModelEntityItem::computeShapeInfo(shapeInfo); + shapeInfo.setParams(type, 0.5f * dimensions); } + // finally apply the registration offset to the shapeInfo + adjustShapeInfoByRegistration(shapeInfo); } void RenderableModelEntityItem::setCollisionShape(const btCollisionShape* shape) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 3c2333e679..e785e61d22 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -60,7 +60,7 @@ public: virtual void setCompoundShapeURL(const QString& url) override; virtual bool isReadyToComputeShape() override; - virtual void computeShapeInfo(ShapeInfo& info) override; + virtual void computeShapeInfo(ShapeInfo& shapeInfo) override; void setCollisionShape(const btCollisionShape* shape) override; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6f435b2c8d..3a7308c277 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1284,6 +1284,11 @@ void Model::createCollisionRenderItemSet() { // We should not have any existing renderItems if we enter this section of code Q_ASSERT(_collisionRenderItemsSet.isEmpty()); + Transform identity; + identity.setIdentity(); + Transform offset; + offset.postTranslate(_offset); + // Run through all of the meshes, and place them into their segregated, but unsorted buckets uint32_t numMeshes = (uint32_t)meshes.size(); for (uint32_t i = 0; i < numMeshes; i++) { @@ -1296,7 +1301,9 @@ void Model::createCollisionRenderItemSet() { int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { model::MaterialPointer& material = _collisionMaterials[partIndex % NUM_COLLISION_HULL_COLORS]; - _collisionRenderItemsSet << std::make_shared(mesh, partIndex, material); + auto payload = std::make_shared(mesh, partIndex, material); + payload->updateTransform(identity, offset); + _collisionRenderItemsSet << payload; } } } From c337cc92e5ebc8ef805ae3d5152c80cf0325feae Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 25 Aug 2016 08:51:53 -0700 Subject: [PATCH 305/332] remove crufty comment --- libraries/render-utils/src/Model.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index ff702d1ec5..7a193b1d47 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -137,9 +137,6 @@ public: // And so that getGeometry() isn't chained everywhere const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return _renderGeometry->getFBXGeometry(); } - // Set the model to use for collisions. - // Should only be called from the model's rendering thread to avoid access violations of changed geometry. - bool isActive() const { return isLoaded(); } bool didVisualGeometryRequestFail() const { return _visualGeometryRequestFailed; } @@ -176,6 +173,7 @@ public: bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const; bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const; + /// \param jointIndex index of joint in model structure /// \param rotation[out] rotation of joint in model-frame /// \return true if joint exists From d7bb5559877b21f7bfe6df3254a7e30012273a6b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 25 Aug 2016 08:53:08 -0700 Subject: [PATCH 306/332] make menu wording more correct --- interface/src/Menu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.h b/interface/src/Menu.h index d47b6842a5..ee00644746 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -137,7 +137,7 @@ namespace MenuOption { const QString Overlays = "Overlays"; const QString PackageModel = "Package Model..."; const QString Pair = "Pair"; - const QString PhysicsShowHulls = "Draw Collision Hulls"; + const QString PhysicsShowHulls = "Draw Collision Shapes"; const QString PhysicsShowOwned = "Highlight Simulation Ownership"; const QString PipelineWarnings = "Log Render Pipeline Warnings"; const QString Preferences = "General..."; From 13a12a2a13279f45dd20659380786966ca19f56f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 11 Aug 2016 18:07:29 -0700 Subject: [PATCH 307/332] Script.include() default scripts not Script.load() --- scripts/defaultScripts.js | 90 ++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 20 deletions(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 5aa3c6945b..c7ea18d883 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -8,25 +8,75 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +var DEFAULT_SCRIPTS = [ + "system/progress.js", + "system/away.js", + "system/users.js", + "system/mute.js", + "system/goto.js", + "system/hmd.js", + "system/marketplaces/marketplace.js", + "system/edit.js", + "system/mod.js", + "system/selectAudioDevice.js", + "system/notifications.js", + "system/controllers/handControllerGrab.js", + "system/controllers/handControllerPointer.js", + "system/controllers/squeezeHands.js", + "system/controllers/grab.js", + "system/controllers/teleport.js", + "system/controllers/toggleAdvancedMovementForHandControllers.js, + "system/dialTone.js", + "system/firstPersonHMD.js", + "system/snapshot.js" +]; -Script.load("system/progress.js"); -Script.load("system/away.js"); -Script.load("system/users.js"); -Script.load("system/mute.js"); -Script.load("system/goto.js"); -Script.load("system/hmd.js"); -Script.load("system/marketplaces/marketplace.js"); -Script.load("system/edit.js"); -Script.load("system/mod.js"); -Script.load("system/selectAudioDevice.js"); -Script.load("system/notifications.js"); -Script.load("system/controllers/handControllerGrab.js"); -Script.load("system/controllers/handControllerPointer.js"); -Script.load("system/controllers/squeezeHands.js"); -Script.load("system/controllers/grab.js"); -Script.load("system/controllers/teleport.js"); -Script.load("system/controllers/toggleAdvancedMovementForHandControllers.js") -Script.load("system/dialTone.js"); -Script.load("system/firstPersonHMD.js"); -Script.load("system/snapshot.js"); +// add a menu item for debugging +var MENU_CATEGORY = "Developer" +var MENU_ITEM = "Debug defaultScripts.js"; +var debuggingDefaultScripts = false; +if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_ITEM)) { + Menu.addMenuItem({ + menuName: MENU_CATEGORY, + menuItemName: MENU_ITEM, + isCheckable: true, + isChecked: false, + grouping: "Advanced" + }); +} + +// start all scripts +if (Menu.isOptionChecked(MENU_ITEM)) { + // we're debugging individual default scripts + // so we load each into its own ScriptEngine instance + debuggingDefaultScripts = true; + for (var i in DEFAULT_SCRIPTS) { + Script.load(DEFAULT_SCRIPTS[i]); + } +} else { + // include all default scripts into this ScriptEngine + for (var i in DEFAULT_SCRIPTS) { + Script.include(DEFAULT_SCRIPTS[i]); + } +} + +function stopLoadedScripts() { + if (debuggingDefaultScripts) { + // remove debug script loads + var runningScripts = ScriptDiscoveryService.getRunning(); + for (var i in runningScripts) { + var scriptName = runningScripts[i].name; + for (var j in DEFAULT_SCRIPTS) { + if (DEFAULT_SCRIPTS[j].slice(-scriptName.length) == scriptName) { + ScriptDiscoveryService.stopScript(runningScripts[i].url); + } + } + } + if (!Menu.isOptionChecked(MENU_ITEM)) { + Menu.removeMenuItem(MENU_CATEGORY, MENU_ITEM); + } + } +} + +Script.scriptEnding.connect(stopLoadedScripts); From 3690b38c65b2cb870a67c0854fa98b3276e9032f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 22 Aug 2016 14:47:50 -0700 Subject: [PATCH 308/332] fix warnings found by eslint in defaultScripts.js --- scripts/defaultScripts.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index c7ea18d883..444816dfd7 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -32,7 +32,7 @@ var DEFAULT_SCRIPTS = [ ]; // add a menu item for debugging -var MENU_CATEGORY = "Developer" +var MENU_CATEGORY = "Developer"; var MENU_ITEM = "Debug defaultScripts.js"; var debuggingDefaultScripts = false; @@ -56,8 +56,8 @@ if (Menu.isOptionChecked(MENU_ITEM)) { } } else { // include all default scripts into this ScriptEngine - for (var i in DEFAULT_SCRIPTS) { - Script.include(DEFAULT_SCRIPTS[i]); + for (var j in DEFAULT_SCRIPTS) { + Script.include(DEFAULT_SCRIPTS[j]); } } @@ -68,7 +68,7 @@ function stopLoadedScripts() { for (var i in runningScripts) { var scriptName = runningScripts[i].name; for (var j in DEFAULT_SCRIPTS) { - if (DEFAULT_SCRIPTS[j].slice(-scriptName.length) == scriptName) { + if (DEFAULT_SCRIPTS[j].slice(-scriptName.length) === scriptName) { ScriptDiscoveryService.stopScript(runningScripts[i].url); } } From 6f6a7bcc44452f24a920ca50e0682f5e60125754 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 23 Aug 2016 12:34:32 -0700 Subject: [PATCH 309/332] wrap default scripts with local scope --- scripts/defaultScripts.js | 3 +++ scripts/system/away.js | 23 ++++++++++------ scripts/system/controllers/grab.js | 17 +++++++----- .../system/controllers/handControllerGrab.js | 10 +++---- .../controllers/handControllerPointer.js | 8 +++--- scripts/system/controllers/squeezeHands.js | 6 +++++ scripts/system/controllers/teleport.js | 9 +++++-- ...oggleAdvancedMovementForHandControllers.js | 16 ++++++++---- scripts/system/dialTone.js | 8 ++++-- scripts/system/edit.js | 11 ++++---- scripts/system/firstPersonHMD.js | 6 +++++ scripts/system/goto.js | 6 +++++ scripts/system/hmd.js | 11 +++++--- scripts/system/marketplaces/clara.js | 6 +++++ scripts/system/mod.js | 12 ++++++--- scripts/system/mute.js | 9 +++++-- scripts/system/notifications.js | 18 ++++++++----- scripts/system/progress.js | 7 +++-- scripts/system/selectAudioDevice.js | 26 ++++++++++++------- scripts/system/users.js | 6 +++++ 20 files changed, 156 insertions(+), 62 deletions(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 444816dfd7..dc0f1217c6 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -1,3 +1,6 @@ +"use strict"; +/* jslint vars: true, plusplus: true */ + // // defaultScripts.js // examples diff --git a/scripts/system/away.js b/scripts/system/away.js index 290cda0d64..716fe1340e 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -1,8 +1,8 @@ "use strict"; -/*jslint vars: true, plusplus: true*/ -/*global HMD, AudioDevice, MyAvatar, Controller, Script, Overlays, print*/ + // // away.js +// // examples // // Created by Howard Stearns 11/3/15 @@ -13,9 +13,11 @@ // // Goes into "paused" when the '.' key (and automatically when started in HMD), and normal when pressing any key. // See MAIN CONTROL, below, for what "paused" actually does. + +(function() { // BEGIN LOCAL_SCOPE + var OVERLAY_WIDTH = 1920; var OVERLAY_HEIGHT = 1080; -var OVERLAY_RATIO = OVERLAY_WIDTH / OVERLAY_HEIGHT; var OVERLAY_DATA = { width: OVERLAY_WIDTH, height: OVERLAY_HEIGHT, @@ -51,7 +53,11 @@ var AWAY_INTRO = { var _animation = AnimationCache.prefetch(AWAY_INTRO.url); function playAwayAnimation() { - MyAvatar.overrideAnimation(AWAY_INTRO.url, AWAY_INTRO.playbackRate, AWAY_INTRO.loopFlag, AWAY_INTRO.startFrame, AWAY_INTRO.endFrame); + MyAvatar.overrideAnimation(AWAY_INTRO.url, + AWAY_INTRO.playbackRate, + AWAY_INTRO.loopFlag, + AWAY_INTRO.startFrame, + AWAY_INTRO.endFrame); } function stopAwayAnimation() { @@ -74,8 +80,6 @@ function moveCloserToCamera(positionAtHUD) { } function showOverlay() { - var properties = {visible: true}; - if (HMD.active) { // make sure desktop version is hidden Overlays.editOverlay(overlay, { visible: false }); @@ -252,8 +256,9 @@ function maybeGoAway() { } } - // If the mouse has gone from captured, to non-captured state, then it likely means the person is still in the HMD, but - // tabbed away from the application (meaning they don't have mouse control) and they likely want to go into an away state + // If the mouse has gone from captured, to non-captured state, then it likely means the person is still in the HMD, + // but tabbed away from the application (meaning they don't have mouse control) and they likely want to go into + // an away state if (Reticle.mouseCaptured !== wasMouseCaptured) { wasMouseCaptured = !wasMouseCaptured; if (!wasMouseCaptured) { @@ -298,3 +303,5 @@ Script.scriptEnding.connect(function () { Controller.mousePressEvent.disconnect(goActive); Controller.keyPressEvent.disconnect(maybeGoActive); }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index ef39e95880..dc57fe4451 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -1,3 +1,5 @@ +"use strict"; + // grab.js // examples // @@ -9,7 +11,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/*global print, Mouse, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ + +(function() { // BEGIN LOCAL_SCOPE Script.include("../libraries/utils.js"); // objects that appear smaller than this can't be grabbed @@ -344,7 +347,6 @@ Grabber.prototype.pressEvent = function(event) { mouse.startDrag(event); - var now = Date.now(); this.lastHeartBeat = 0; var clickedEntity = pickResults.entityID; @@ -385,7 +387,7 @@ Grabber.prototype.pressEvent = function(event) { beacon.updatePosition(this.startPosition); - if(!entityIsGrabbedByOther(this.entityID)){ + if (!entityIsGrabbedByOther(this.entityID)) { this.moveEvent(event); } @@ -452,7 +454,7 @@ Grabber.prototype.moveEvent = function(event) { // see if something added/restored gravity var entityProperties = Entities.getEntityProperties(this.entityID); - if (Vec3.length(entityProperties.gravity) != 0) { + if (Vec3.length(entityProperties.gravity) !== 0.0) { this.originalGravity = entityProperties.gravity; } this.currentPosition = entityProperties.position; @@ -500,7 +502,8 @@ Grabber.prototype.moveEvent = function(event) { }; } else { var cameraPosition = Camera.getPosition(); - newPointOnPlane = mouseIntersectionWithPlane(this.pointOnPlane, this.planeNormal, mouse.current, this.maxDistance); + newPointOnPlane = mouseIntersectionWithPlane( + this.pointOnPlane, this.planeNormal, mouse.current, this.maxDistance); var relativePosition = Vec3.subtract(newPointOnPlane, cameraPosition); var distance = Vec3.length(relativePosition); if (distance > this.maxDistance) { @@ -625,7 +628,7 @@ function editEvent(channel, message, sender, localOnly) { return; } try { - data = JSON.parse(message); + var data = JSON.parse(message); if ("enabled" in data) { enabled = !data["enabled"]; } @@ -640,3 +643,5 @@ Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); Messages.subscribe("edit-events"); Messages.messageReceived.connect(editEvent); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index ac1c844cc9..32e0b047de 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1,4 +1,5 @@ "use strict"; + // handControllerGrab.js // // Created by Eric Levin on 9/2/15 @@ -10,7 +11,8 @@ // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global setEntityCustomData, getEntityCustomData, vec3toStr, flatten, Xform */ + +(function() { // BEGIN LOCAL_SCOPE Script.include("/~/system/libraries/utils.js"); Script.include("/~/system/libraries/Xform.js"); @@ -26,8 +28,6 @@ var WANT_DEBUG_SEARCH_NAME = null; // these tune time-averaging and "on" value for analog trigger // -var SPARK_MODEL_SCALE_FACTOR = 0.75; - var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing var TRIGGER_OFF_VALUE = 0.1; var TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab @@ -88,7 +88,6 @@ var COLORS_GRAB_DISTANCE_HOLD = { }; -var LINE_LENGTH = 500; var PICK_MAX_DISTANCE = 500; // max length of pick-ray // @@ -129,7 +128,6 @@ var ZERO_VEC = { var NULL_UUID = "{00000000-0000-0000-0000-000000000000}"; // these control how long an abandoned pointer line or action will hang around -var LIFETIME = 10; var ACTION_TTL = 15; // seconds var ACTION_TTL_REFRESH = 5; var PICKS_PER_SECOND_PER_HAND = 60; @@ -2643,3 +2641,5 @@ function cleanup() { Script.scriptEnding.connect(cleanup); Script.update.connect(update); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 5888b53a27..6b62674be5 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -1,6 +1,4 @@ "use strict"; -/*jslint vars: true, plusplus: true*/ -/*globals Script, Overlays, Controller, Reticle, HMD, Camera, Entities, MyAvatar, Settings, Menu, ScriptDiscoveryService, Window, Vec3, Quat, print*/ // // handControllerPointer.js @@ -13,6 +11,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE + // Control the "mouse" using hand controller. (HMD and desktop.) // First-person only. // Starts right handed, but switches to whichever is free: Whichever hand was NOT most recently squeezed. @@ -440,7 +440,7 @@ function clearSystemLaser() { HMD.disableHandLasers(BOTH_HUD_LASERS); systemLaserOn = false; weMovedReticle = true; - Reticle.position = { x: -1, y: -1 }; + Reticle.position = { x: -1, y: -1 }; } function setColoredLaser() { // answer trigger state if lasers supported, else falsey. var color = (activeTrigger.state === 'full') ? LASER_TRIGGER_COLOR_XYZW : LASER_SEARCH_COLOR_XYZW; @@ -508,3 +508,5 @@ Script.scriptEnding.connect(function () { Script.clearInterval(settingsChecker); OffscreenFlags.navigationFocusDisabled = false; }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/controllers/squeezeHands.js b/scripts/system/controllers/squeezeHands.js index f549f911c9..f06529f2b2 100644 --- a/scripts/system/controllers/squeezeHands.js +++ b/scripts/system/controllers/squeezeHands.js @@ -1,3 +1,5 @@ +"use strict"; + // // controllers/squeezeHands.js // @@ -10,6 +12,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE + var lastLeftTrigger = 0; var lastRightTrigger = 0; var leftHandOverlayAlpha = 0; @@ -82,3 +86,5 @@ function shutdown() { Script.scriptEnding.connect(shutdown); init(); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 0aa5820b0f..f3e94d23de 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -1,3 +1,5 @@ +"use strict"; + // Created by james b. pollack @imgntn on 7/2/2016 // Copyright 2016 High Fidelity, Inc. // @@ -6,6 +8,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +(function() { // BEGIN LOCAL_SCOPE + var inTeleportMode = false; var SMOOTH_ARRIVAL_SPACING = 33; @@ -594,7 +598,6 @@ function getAvatarFootOffset() { } }) - var myPosition = MyAvatar.position; var offset = upperLeg + lowerLeg + foot + toe + toeTop; offset = offset / 100; return offset; @@ -737,4 +740,6 @@ var handleHandMessages = function(channel, message, sender) { } Messages.subscribe('Hifi-Teleport-Disabler'); -Messages.messageReceived.connect(handleHandMessages); \ No newline at end of file +Messages.messageReceived.connect(handleHandMessages); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 3a75482770..f5ab42cf53 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -1,14 +1,18 @@ +"use strict"; + // Created by james b. pollack @imgntn on 8/18/2016 // Copyright 2016 High Fidelity, Inc. // -//advanced movements settings are in individual controller json files -//what we do is check the status of the 'advance movement' checkbox when you enter HMD mode -//if 'advanced movement' is checked...we give you the defaults that are in the json. -//if 'advanced movement' is not checked... we override the advanced controls with basic ones. +// advanced movements settings are in individual controller json files +// what we do is check the status of the 'advance movement' checkbox when you enter HMD mode +// if 'advanced movement' is checked...we give you the defaults that are in the json. +// if 'advanced movement' is not checked... we override the advanced controls with basic ones. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +(function() { // BEGIN LOCAL_SCOPE + var mappingName, basicMapping, isChecked; var TURN_RATE = 1000; @@ -138,4 +142,6 @@ HMD.displayModeChanged.connect(function(isHMDMode) { } } -}); \ No newline at end of file +}); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/dialTone.js b/scripts/system/dialTone.js index 64ef62d61a..02624645d5 100644 --- a/scripts/system/dialTone.js +++ b/scripts/system/dialTone.js @@ -1,3 +1,5 @@ +"use strict"; + // // dialTone.js // examples @@ -10,9 +12,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE + // setup the local sound we're going to use - - var connectSound = SoundCache.getSound(Script.resolvePath("assets/sounds/hello.wav")); var disconnectSound = SoundCache.getSound(Script.resolvePath("assets/sounds/goodbye.wav")); var micMutedSound = SoundCache.getSound(Script.resolvePath("assets/sounds/goodbye.wav")); @@ -36,3 +38,5 @@ AudioDevice.muteToggled.connect(function () { Audio.playSound(micMutedSound, soundOptions); } }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/edit.js b/scripts/system/edit.js index d34d4acffc..bcf31726bc 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1,3 +1,5 @@ +"use strict"; + // newEditEntities.js // examples // @@ -11,13 +13,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE + var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; var EDIT_TOGGLE_BUTTON = "com.highfidelity.interface.system.editButton"; var SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system"; var EDIT_TOOLBAR = "com.highfidelity.interface.toolbar.edit"; -/* globals SelectionDisplay, SelectionManager, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Toolbars, - progressDialog, tooltip, ParticleExplorerTool */ Script.include([ "libraries/stringHelpers.js", "libraries/dataViewHelpers.js", @@ -94,7 +96,6 @@ var SHOULD_SHOW_PROPERTY_MENU = false; var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain."; var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain."; -var mode = 0; var isActive = false; var IMPORTING_SVO_OVERLAY_WIDTH = 144; @@ -502,8 +503,6 @@ var orientation; var intersection; -var SCALE_FACTOR = 200.0; - function rayPlaneIntersection(pickRay, point, normal) { // // // This version of the test returns the intersection of a line with a plane @@ -1726,3 +1725,5 @@ entityListTool.webView.webEventReceived.connect(function (data) { } } }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/firstPersonHMD.js b/scripts/system/firstPersonHMD.js index 082c6304be..5fdee1b7b5 100644 --- a/scripts/system/firstPersonHMD.js +++ b/scripts/system/firstPersonHMD.js @@ -1,3 +1,5 @@ +"use strict"; + // // firstPersonHMD.js // system @@ -9,9 +11,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE + // Automatically enter first person mode when entering HMD mode HMD.displayModeChanged.connect(function(isHMDMode) { if (isHMDMode) { Camera.setModeString("first person"); } }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/goto.js b/scripts/system/goto.js index 2ed98c689b..9116142293 100644 --- a/scripts/system/goto.js +++ b/scripts/system/goto.js @@ -1,3 +1,5 @@ +"use strict"; + // // goto.js // scripts/system/ @@ -9,6 +11,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE + var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); @@ -38,3 +42,5 @@ Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); DialogsManager.addressBarShown.disconnect(onAddressBarShown); }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index ac1918b001..84ff6b3c89 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -1,3 +1,5 @@ +"use strict"; + // // hmd.js // scripts/system/ @@ -9,6 +11,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE + var headset; // The preferred headset. Default to the first one found in the following list. var displayMenuName = "Display"; var desktopMenuItemName = "Desktop"; @@ -36,13 +40,13 @@ if (headset) { visible: true, hoverState: 2, defaultState: 0, - alpha: 0.9, + alpha: 0.9 }); onHmdChanged(HMD.active); - + button.clicked.connect(onClicked); HMD.displayModeChanged.connect(onHmdChanged); - + Script.scriptEnding.connect(function () { toolBar.removeButton("hmdToggle"); button.clicked.disconnect(onClicked); @@ -50,3 +54,4 @@ if (headset) { }); } +}()); // END LOCAL_SCOPE diff --git a/scripts/system/marketplaces/clara.js b/scripts/system/marketplaces/clara.js index 7b48f49135..67c2d5503c 100644 --- a/scripts/system/marketplaces/clara.js +++ b/scripts/system/marketplaces/clara.js @@ -1,3 +1,5 @@ +"use strict"; + // // clara.js // @@ -9,6 +11,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE + var toolIconUrl = Script.resolvePath("../assets/images/tools/"); var qml = Script.resolvePath("../../../resources/qml/MarketplaceComboBox.qml") @@ -77,3 +81,5 @@ Script.scriptEnding.connect(function () { browseExamplesButton.clicked.disconnect(onClick); marketplaceWindow.visibleChanged.disconnect(onExamplesWindowVisibilityChanged); }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/mod.js b/scripts/system/mod.js index 43afb94c9d..e69c2ce2f6 100644 --- a/scripts/system/mod.js +++ b/scripts/system/mod.js @@ -1,3 +1,5 @@ +"use strict"; + // // mod.js // scripts/system/ @@ -9,6 +11,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE + // grab the toolbar var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); @@ -39,7 +43,7 @@ function removeOverlays() { // enumerate the overlays and remove them var modOverlayKeys = Object.keys(modOverlays); - for (i = 0; i < modOverlayKeys.length; ++i) { + for (var i = 0; i < modOverlayKeys.length; ++i) { var avatarID = modOverlayKeys[i]; Overlays.deleteOverlay(modOverlays[avatarID]); } @@ -72,7 +76,7 @@ function updateOverlays() { var identifiers = AvatarList.getAvatarIdentifiers(); - for (i = 0; i < identifiers.length; ++i) { + for (var i = 0; i < identifiers.length; ++i) { var avatarID = identifiers[i]; if (avatarID === null) { @@ -138,7 +142,7 @@ function handleSelectedOverlay(clickedOverlay) { // see this is one of our mod overlays var modOverlayKeys = Object.keys(modOverlays) - for (i = 0; i < modOverlayKeys.length; ++i) { + for (var i = 0; i < modOverlayKeys.length; ++i) { var avatarID = modOverlayKeys[i]; var modOverlay = modOverlays[avatarID]; @@ -214,3 +218,5 @@ Script.scriptEnding.connect(function() { removeOverlays(); triggerMapping.disable(); }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/mute.js b/scripts/system/mute.js index 4ea8aee546..722ed65b3d 100644 --- a/scripts/system/mute.js +++ b/scripts/system/mute.js @@ -1,3 +1,5 @@ +"use strict"; + // // goto.js // scripts/system/ @@ -9,8 +11,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); +(function() { // BEGIN LOCAL_SCOPE +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); var button = toolBar.addButton({ objectName: "mute", @@ -33,7 +36,7 @@ function onMuteToggled() { } onMuteToggled(); function onClicked(){ - var menuItem = "Mute Microphone"; + var menuItem = "Mute Microphone"; Menu.setIsOptionChecked(menuItem, !Menu.isOptionChecked(menuItem)); } button.clicked.connect(onClicked); @@ -44,3 +47,5 @@ Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); AudioDevice.muteToggled.disconnect(onMuteToggled); }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 34f9814d8a..673b48961d 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -1,3 +1,5 @@ +"use strict"; + // // notifications.js // Version 0.801 @@ -56,6 +58,8 @@ // } // } +(function() { // BEGIN LOCAL_SCOPE + Script.include("./libraries/soundArray.js"); var width = 340.0; //width of notification overlay @@ -105,7 +109,7 @@ var NotificationType = { return NotificationType.UNKNOWN; } var preMenuItemName = menuItemName.substr(0, menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length); - for (type in this.properties) { + for (var type in this.properties) { if (this.properties[type].text === preMenuItemName) { return parseInt(type) + 1; } @@ -120,7 +124,7 @@ var NotificationType = { var randomSounds = new SoundArray({ localOnly: true }, true); var numberOfSounds = 2; for (var i = 1; i <= numberOfSounds; i++) { - + randomSounds.addSound(Script.resolvePath("assets/sounds/notification-general"+ i + ".raw")); } @@ -230,9 +234,9 @@ function calculate3DOverlayPositions(noticeWidth, noticeHeight, y) { }; } -// Pushes data to each array and sets up data for 2nd dimension array +// Pushes data to each array and sets up data for 2nd dimension array // to handle auxiliary data not carried by the overlay class -// specifically notification "heights", "times" of creation, and . +// specifically notification "heights", "times" of creation, and . function notify(notice, button, height, imageProperties, image) { var notificationText, noticeWidth, @@ -584,7 +588,7 @@ function setup() { isChecked: Settings.getValue(PLAY_NOTIFICATION_SOUNDS_SETTING) }); Menu.addSeparator(MENU_NAME, "Play sounds for:"); - for (type in NotificationType.properties) { + for (var type in NotificationType.properties) { checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE + (parseInt(type) + 1)); checked = checked === '' ? true : checked; Menu.addMenuItem({ @@ -620,7 +624,7 @@ LODManager.LODDecreased.connect(function() { var warningText = "\n" + "Due to the complexity of the content, the \n" + "level of detail has been decreased. " - + "You can now see: \n" + + "You can now see: \n" + LODManager.getLODFeedbackText(); if (lodTextID == false) { @@ -641,3 +645,5 @@ Window.domainConnectionRefused.connect(onDomainConnectionRefused); Window.snapshotTaken.connect(onSnapshotTaken); setup(); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/progress.js b/scripts/system/progress.js index 7c1c475e99..2b1efa94af 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -1,3 +1,5 @@ +"use strict"; + // // progress.js // examples @@ -11,7 +13,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -(function() { +(function() { // BEGIN LOCAL_SCOPE function debug() { return; @@ -283,4 +285,5 @@ GlobalServices.updateDownloadInfo(); Script.setInterval(update, 1000/60); Script.scriptEnding.connect(tearDown); -}()); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/selectAudioDevice.js b/scripts/system/selectAudioDevice.js index 663f96b59c..a44295fd3c 100644 --- a/scripts/system/selectAudioDevice.js +++ b/scripts/system/selectAudioDevice.js @@ -1,3 +1,5 @@ +"use strict"; + // // audioDeviceExample.js // examples @@ -11,6 +13,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE + if (typeof String.prototype.startsWith != 'function') { String.prototype.startsWith = function (str){ return this.slice(0, str.length) == str; @@ -62,11 +66,11 @@ function setupAudioMenus() { for(var i = 0; i < outputDevices.length; i++) { var thisDeviceSelected = (outputDevices[i] == selectedOutputDevice); var menuItem = "Use " + outputDevices[i] + " for Output"; - Menu.addMenuItem({ - menuName: "Audio > Devices", - menuItemName: menuItem, - isCheckable: true, - isChecked: thisDeviceSelected + Menu.addMenuItem({ + menuName: "Audio > Devices", + menuItemName: menuItem, + isCheckable: true, + isChecked: thisDeviceSelected }); if (thisDeviceSelected) { selectedOutputMenu = menuItem; @@ -86,11 +90,11 @@ function setupAudioMenus() { for(var i = 0; i < inputDevices.length; i++) { var thisDeviceSelected = (inputDevices[i] == selectedInputDevice); var menuItem = "Use " + inputDevices[i] + " for Input"; - Menu.addMenuItem({ - menuName: "Audio > Devices", - menuItemName: menuItem, - isCheckable: true, - isChecked: thisDeviceSelected + Menu.addMenuItem({ + menuName: "Audio > Devices", + menuItemName: menuItem, + isCheckable: true, + isChecked: thisDeviceSelected }); if (thisDeviceSelected) { selectedInputMenu = menuItem; @@ -212,3 +216,5 @@ Script.scriptEnding.connect(function () { Menu.menuItemEvent.disconnect(menuItemEvent); Script.update.disconnect(checkHMDAudio); }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/users.js b/scripts/system/users.js index 853067b90b..a56656a074 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -1,3 +1,5 @@ +"use strict"; + // // users.js // examples @@ -9,6 +11,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE + var PopUpMenu = function (properties) { var value = properties.value, promptOverlay, @@ -1189,3 +1193,5 @@ var usersWindow = (function () { setUp(); Script.scriptEnding.connect(tearDown); }()); + +}()); // END LOCAL_SCOPE From fefe5e30ec4b3e431c7a61f299594562266e20ec Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 23 Aug 2016 12:54:09 -0700 Subject: [PATCH 310/332] don't use globals and remove double definitions --- scripts/system/controllers/grab.js | 21 ++++----------------- scripts/system/progress.js | 2 +- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index dc57fe4451..05303aa0a7 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -15,8 +15,7 @@ (function() { // BEGIN LOCAL_SCOPE Script.include("../libraries/utils.js"); -// objects that appear smaller than this can't be grabbed -var MAX_SOLID_ANGLE = 0.01; +var MAX_SOLID_ANGLE = 0.01; // objects that appear smaller than this can't be grabbed var ZERO_VEC3 = { x: 0, @@ -42,18 +41,6 @@ var DEFAULT_GRABBABLE_DATA = { }; -var MAX_SOLID_ANGLE = 0.01; // objects that appear smaller than this can't be grabbed -var ZERO_VEC3 = { - x: 0, - y: 0, - z: 0 -}; -var IDENTITY_QUAT = { - x: 0, - y: 0, - z: 0, - w: 0 -}; var ACTION_TTL = 10; // seconds var enabled = true; @@ -124,7 +111,7 @@ function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event, maxDistanc } // Mouse class stores mouse click and drag info -Mouse = function() { +function Mouse() { this.current = { x: 0, y: 0 @@ -194,7 +181,7 @@ var mouse = new Mouse(); // Beacon class stores info for drawing a line at object's target position -Beacon = function() { +function Beacon() { this.height = 0.10; this.overlayID = Overlays.addOverlay("line3d", { color: { @@ -246,7 +233,7 @@ var beacon = new Beacon(); // Grabber class stores and computes info for grab behavior -Grabber = function() { +function Grabber() { this.isGrabbing = false; this.entityID = null; this.actionID = null; diff --git a/scripts/system/progress.js b/scripts/system/progress.js index 2b1efa94af..d7f6713c69 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -245,7 +245,7 @@ } function updateProgressBarLocation() { - viewport = Controller.getViewportDimensions(); + var viewport = Controller.getViewportDimensions(); windowWidth = viewport.x; windowHeight = viewport.y; From a0911d2b9dc2fd2ada4b215e78a29447b0070a0e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 25 Aug 2016 09:04:06 -0700 Subject: [PATCH 311/332] add local scope wrappers to new default scripts --- scripts/system/marketplaces/marketplace.js | 4 ++++ scripts/system/snapshot.js | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/scripts/system/marketplaces/marketplace.js b/scripts/system/marketplaces/marketplace.js index 2bd6033e62..349c3f70e1 100644 --- a/scripts/system/marketplaces/marketplace.js +++ b/scripts/system/marketplaces/marketplace.js @@ -8,6 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE + /* global WebTablet */ Script.include("../libraries/WebTablet.js"); @@ -100,3 +102,5 @@ Script.scriptEnding.connect(function () { browseExamplesButton.clicked.disconnect(onClick); marketplaceWindow.visibleChanged.disconnect(onMarketplaceWindowVisibilityChanged); }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 2f3b8862c2..c9876af039 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -7,6 +7,9 @@ // Distributed under the Apache License, Version 2.0 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +(function() { // BEGIN LOCAL_SCOPE + var SNAPSHOT_DELAY = 500; // 500ms var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); var resetOverlays; @@ -152,3 +155,5 @@ Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); Window.snapshotShared.disconnect(snapshotShared); }); + +}()); // END LOCAL_SCOPE From 376756d41399521c42bc52d6d8a714df6c0e3c76 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 25 Aug 2016 09:58:36 -0700 Subject: [PATCH 312/332] include the codec name in the agent avatar audio stream --- assignment-client/src/Agent.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 65e193dec6..350d359412 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -392,7 +392,6 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) { const int16_t* nextSoundOutput = NULL; if (_avatarSound) { - const QByteArray& soundByteArray = _avatarSound->getByteArray(); nextSoundOutput = reinterpret_cast(soundByteArray.data() + _numAvatarSoundSentBytes); @@ -442,6 +441,10 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) { audioPacket->writePrimitive(headOrientation); } else if (nextSoundOutput) { + // write the codec + QString codecName; + audioPacket->writeString(codecName); + // assume scripted avatar audio is mono and set channel flag to zero audioPacket->writePrimitive((quint8)0); From da387a958a3f58b5e1a6c185da2ea1620c058f02 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 25 Aug 2016 10:26:05 -0700 Subject: [PATCH 313/332] fix typo --- scripts/defaultScripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index dc0f1217c6..98825c594d 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -28,7 +28,7 @@ var DEFAULT_SCRIPTS = [ "system/controllers/squeezeHands.js", "system/controllers/grab.js", "system/controllers/teleport.js", - "system/controllers/toggleAdvancedMovementForHandControllers.js, + "system/controllers/toggleAdvancedMovementForHandControllers.js", "system/dialTone.js", "system/firstPersonHMD.js", "system/snapshot.js" From e82f6a90afa8b8ddb82a5a80dc86c79aa40ce72c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 25 Aug 2016 11:04:23 -0700 Subject: [PATCH 314/332] Improved web entity rendering * fading now works properly * web browsers are now fully opaque. --- interface/src/ui/overlays/Web3DOverlay.cpp | 7 ++- .../src/RenderableWebEntityItem.cpp | 16 +++++- .../src/RenderableWebEntityItem.h | 2 + libraries/render-utils/src/GeometryCache.cpp | 53 +++++++++++++++---- libraries/render-utils/src/GeometryCache.h | 16 +++--- .../src/simple_opaque_web_browser.slf | 30 +++++++++++ ...slf => simple_transparent_web_browser.slf} | 24 +++------ 7 files changed, 112 insertions(+), 36 deletions(-) create mode 100644 libraries/render-utils/src/simple_opaque_web_browser.slf rename libraries/render-utils/src/{simple_srgb_textured_unlit_no_tex_alpha.slf => simple_transparent_web_browser.slf} (58%) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 618785ca66..46fc2dfc36 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -28,6 +28,7 @@ static const float DPI = 30.47f; static const float INCHES_TO_METERS = 1.0f / 39.3701f; +static float OPAQUE_ALPHA_THRESHOLD = 0.99f; QString const Web3DOverlay::TYPE = "web3d"; @@ -106,7 +107,11 @@ void Web3DOverlay::render(RenderArgs* args) { batch.setModelTransform(transform); auto geometryCache = DependencyManager::get(); - geometryCache->bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(batch); + if (color.a < OPAQUE_ALPHA_THRESHOLD) { + geometryCache->bindTransparentWebBrowserProgram(batch); + } else { + geometryCache->bindOpaqueWebBrowserProgram(batch); + } geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color); batch.setResourceTexture(0, args->_whiteTexture); // restore default white color after me } diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 9221fec140..4a42df561e 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -35,6 +35,7 @@ static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 100; static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND; static int MAX_WINDOW_SIZE = 4096; +static float OPAQUE_ALPHA_THRESHOLD = 0.99f; EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity{ new RenderableWebEntityItem(entityID) }; @@ -144,7 +145,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { glm::vec2 windowSize = getWindowSize(); // The offscreen surface is idempotent for resizes (bails early - // if it's a no-op), so it's safe to just call resize every frame + // if it's a no-op), so it's safe to just call resize every frame // without worrying about excessive overhead. _webSurface->resize(QSize(windowSize.x, windowSize.y)); @@ -164,9 +165,14 @@ void RenderableWebEntityItem::render(RenderArgs* args) { } float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; + batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); - DependencyManager::get()->bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(batch); + if (fadeRatio < OPAQUE_ALPHA_THRESHOLD) { + DependencyManager::get()->bindTransparentWebBrowserProgram(batch); + } else { + DependencyManager::get()->bindOpaqueWebBrowserProgram(batch); + } DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio)); } @@ -291,3 +297,9 @@ void RenderableWebEntityItem::update(const quint64& now) { destroyWebSurface(); } } + + +bool RenderableWebEntityItem::isTransparent() { + float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; + return fadeRatio < OPAQUE_ALPHA_THRESHOLD; +} diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 7bfd40864b..03234ce690 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -44,6 +44,8 @@ public: SIMPLE_RENDERABLE(); + virtual bool isTransparent() override; + private: bool buildWebSurface(EntityTreeRenderer* renderer); void destroyWebSurface(); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 01cec78eae..a3a9303156 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -35,7 +35,8 @@ #include "simple_vert.h" #include "simple_textured_frag.h" #include "simple_textured_unlit_frag.h" -#include "simple_srgb_textured_unlit_no_tex_alpha_frag.h" +#include "simple_opaque_web_browser_frag.h" +#include "simple_transparent_web_browser_frag.h" #include "glowLine_vert.h" #include "glowLine_geom.h" #include "glowLine_frag.h" @@ -1763,25 +1764,55 @@ inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) { return a.getRaw() == b.getRaw(); } -void GeometryCache::bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(gpu::Batch& batch) { - batch.setPipeline(getSimpleSRGBTexturedUnlitNoTexAlphaPipeline()); +void GeometryCache::bindOpaqueWebBrowserProgram(gpu::Batch& batch) { + batch.setPipeline(getOpaqueWebBrowserProgram()); // Set a default normal map batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, - DependencyManager::get()->getNormalFittingTexture()); + DependencyManager::get()->getNormalFittingTexture()); } -gpu::PipelinePointer GeometryCache::getSimpleSRGBTexturedUnlitNoTexAlphaPipeline() { - // Compile the shaders, once +gpu::PipelinePointer GeometryCache::getOpaqueWebBrowserProgram() { static std::once_flag once; std::call_once(once, [&]() { auto VS = gpu::Shader::createVertex(std::string(simple_vert)); - auto PS = gpu::Shader::createPixel(std::string(simple_srgb_textured_unlit_no_tex_alpha_frag)); + auto PS = gpu::Shader::createPixel(std::string(simple_opaque_web_browser_frag)); - _simpleSRGBTexturedUnlitNoTexAlphaShader = gpu::Shader::createProgram(VS, PS); + _simpleOpaqueWebBrowserShader = gpu::Shader::createProgram(VS, PS); gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), render::ShapePipeline::Slot::MAP::NORMAL_FITTING)); - gpu::Shader::makeProgram(*_simpleSRGBTexturedUnlitNoTexAlphaShader, slotBindings); + gpu::Shader::makeProgram(*_simpleOpaqueWebBrowserShader, slotBindings); + auto state = std::make_shared(); + state->setCullMode(gpu::State::CULL_NONE); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setBlendFunction(false, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + _simpleOpaqueWebBrowserPipeline = gpu::Pipeline::create(_simpleOpaqueWebBrowserShader, state); + }); + + return _simpleOpaqueWebBrowserPipeline; +} + +void GeometryCache::bindTransparentWebBrowserProgram(gpu::Batch& batch) { + batch.setPipeline(getTransparentWebBrowserProgram()); + // Set a default normal map + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, + DependencyManager::get()->getNormalFittingTexture()); +} + +gpu::PipelinePointer GeometryCache::getTransparentWebBrowserProgram() { + static std::once_flag once; + std::call_once(once, [&]() { + auto VS = gpu::Shader::createVertex(std::string(simple_vert)); + auto PS = gpu::Shader::createPixel(std::string(simple_transparent_web_browser_frag)); + + _simpleTransparentWebBrowserShader = gpu::Shader::createProgram(VS, PS); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), render::ShapePipeline::Slot::MAP::NORMAL_FITTING)); + gpu::Shader::makeProgram(*_simpleTransparentWebBrowserShader, slotBindings); auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_NONE); state->setDepthTest(true, true, gpu::LESS_EQUAL); @@ -1789,10 +1820,10 @@ gpu::PipelinePointer GeometryCache::getSimpleSRGBTexturedUnlitNoTexAlphaPipeline gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - _simpleSRGBTexturedUnlitNoTexAlphaPipeline = gpu::Pipeline::create(_simpleSRGBTexturedUnlitNoTexAlphaShader, state); + _simpleTransparentWebBrowserPipeline = gpu::Pipeline::create(_simpleTransparentWebBrowserShader, state); }); - return _simpleSRGBTexturedUnlitNoTexAlphaPipeline; + return _simpleTransparentWebBrowserPipeline; } void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 9099daf7c0..244683759d 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -149,8 +149,7 @@ public: int allocateID() { return _nextID++; } static const int UNKNOWN_ID; - - + // Bind the pipeline and get the state to render static geometry void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false); @@ -158,8 +157,11 @@ public: gpu::PipelinePointer getSimplePipeline(bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false); - void bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(gpu::Batch& batch); - gpu::PipelinePointer getSimpleSRGBTexturedUnlitNoTexAlphaPipeline(); + void bindOpaqueWebBrowserProgram(gpu::Batch& batch); + gpu::PipelinePointer getOpaqueWebBrowserProgram(); + + void bindTransparentWebBrowserProgram(gpu::Batch& batch); + gpu::PipelinePointer getTransparentWebBrowserProgram(); render::ShapePipelinePointer getOpaqueShapePipeline() { return GeometryCache::_simpleOpaquePipeline; } render::ShapePipelinePointer getTransparentShapePipeline() { return GeometryCache::_simpleTransparentPipeline; } @@ -423,9 +425,11 @@ private: gpu::PipelinePointer _glowLinePipeline; QHash _simplePrograms; - gpu::ShaderPointer _simpleSRGBTexturedUnlitNoTexAlphaShader; - gpu::PipelinePointer _simpleSRGBTexturedUnlitNoTexAlphaPipeline; + gpu::ShaderPointer _simpleOpaqueWebBrowserShader; + gpu::PipelinePointer _simpleOpaqueWebBrowserPipeline; + gpu::ShaderPointer _simpleTransparentWebBrowserShader; + gpu::PipelinePointer _simpleTransparentWebBrowserPipeline; }; #endif // hifi_GeometryCache_h diff --git a/libraries/render-utils/src/simple_opaque_web_browser.slf b/libraries/render-utils/src/simple_opaque_web_browser.slf new file mode 100644 index 0000000000..2921d6aea0 --- /dev/null +++ b/libraries/render-utils/src/simple_opaque_web_browser.slf @@ -0,0 +1,30 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// simple_opaque_web_browser.frag +// fragment shader +// +// Created by Anthony Thibault on 7/25/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Color.slh@> +<@include DeferredBufferWrite.slh@> + +// the albedo texture +uniform sampler2D originalTexture; + +// the interpolated normal +in vec3 _normal; +in vec4 _color; +in vec2 _texCoord0; + +void main(void) { + vec4 texel = texture(originalTexture, _texCoord0.st); + texel = colorToLinearRGBA(texel); + packDeferredFragmentUnlit(normalize(_normal), 1.0, _color.rgb * texel.rgb); +} diff --git a/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf b/libraries/render-utils/src/simple_transparent_web_browser.slf similarity index 58% rename from libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf rename to libraries/render-utils/src/simple_transparent_web_browser.slf index 38b7e1002c..b7606985e6 100644 --- a/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf +++ b/libraries/render-utils/src/simple_transparent_web_browser.slf @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// simple_srgb_texture_unlit_no_tex_alpha.frag +// simple_transparent_web_browser.frag // fragment shader // // Created by Anthony Thibault on 7/25/16. @@ -26,19 +26,11 @@ in vec2 _texCoord0; void main(void) { vec4 texel = texture(originalTexture, _texCoord0.st); texel = colorToLinearRGBA(texel); - - const float ALPHA_THRESHOLD = 0.999; - if (_color.a < ALPHA_THRESHOLD) { - packDeferredFragmentTranslucent( - normalize(_normal), - _color.a, - _color.rgb * texel.rgb, - DEFAULT_FRESNEL, - DEFAULT_ROUGHNESS); - } else { - packDeferredFragmentUnlit( - normalize(_normal), - 1.0, - _color.rgb * texel.rgb); - } + packDeferredFragmentTranslucent( + normalize(_normal), + _color.a, + _color.rgb * texel.rgb, + DEFAULT_FRESNEL, + DEFAULT_ROUGHNESS); } + From 24162f0f3d26a235bb5a9833a2f5e9e4efa87aeb Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 25 Aug 2016 12:37:23 -0700 Subject: [PATCH 315/332] set skybox default color to empty color --- interface/src/Application.cpp | 19 +++++++++++++------ libraries/model/src/model/Skybox.h | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5a129bc980..b2095dbdfe 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1220,7 +1220,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _defaultSkyboxAmbientTexture = textureCache->getImageTexture(skyboxAmbientUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", true } }); _defaultSkybox->setCubemap(_defaultSkyboxTexture); - _defaultSkybox->setColor({ 1.0, 1.0, 1.0 }); EntityItem::setEntitiesShouldFadeFunction([this]() { SharedNodePointer entityServerNode = DependencyManager::get()->soloNodeOfType(NodeType::EntityServer); @@ -4327,8 +4326,9 @@ namespace render { sceneKeyLight->setIntensity(DEFAULT_SKYBOX_INTENSITY); sceneKeyLight->setAmbientIntensity(DEFAULT_SKYBOX_AMBIENT_INTENSITY); sceneKeyLight->setDirection(DEFAULT_SKYBOX_DIRECTION); - // fall through: render a skybox, if available + // fall through: render a skybox (if available), or the defaults (if requested) } + case model::SunSkyStage::SKY_BOX: { auto skybox = skyStage->getSkybox(); if (!skybox->empty()) { @@ -4336,25 +4336,32 @@ namespace render { skybox->render(batch, args->getViewFrustum()); break; } - // fall through: render defaults, if available + // fall through: render defaults (if requested) } + case model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE: { if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) { auto scene = DependencyManager::get()->getStage(); auto sceneKeyLight = scene->getKeyLight(); auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture(); - // do not set the ambient sphere - it peaks too high, and causes flashing when turning + // set the ambient sphere uniformly - the defaultSkyboxAmbientTexture has peaks that cause flashing when turning + sceneKeyLight->setAmbientSphere(DependencyManager::get()->getWhiteTexture()->getIrradiance()); sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture); + // fall through: render defaults skybox + } else { + break; } - // fall through: render defaults, if available } + case model::SunSkyStage::SKY_DEFAULT_TEXTURE: if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) { qApp->getDefaultSkybox()->render(batch, args->getViewFrustum()); } + break; + + // Any other cases require no extra rendering case model::SunSkyStage::NO_BACKGROUND: default: - // this line intentionally left blank break; } } diff --git a/libraries/model/src/model/Skybox.h b/libraries/model/src/model/Skybox.h index b2e004a2e9..f12d259411 100755 --- a/libraries/model/src/model/Skybox.h +++ b/libraries/model/src/model/Skybox.h @@ -51,7 +51,7 @@ protected: class Schema { public: - glm::vec3 color { 1.0f, 1.0f, 1.0f }; + glm::vec3 color { 0.0f, 0.0f, 0.0f }; float blend { 0.0f }; }; From 9be073cc12c014c5c636f18c4d18c758eb9268ae Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 25 Aug 2016 16:03:12 -0700 Subject: [PATCH 316/332] handle some more cases --- scripts/system/edit.js | 10 ++++- scripts/system/html/js/entityProperties.js | 44 ++++++++++++++-------- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index bcf31726bc..1727857ee8 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1369,7 +1369,12 @@ var PropertiesTool = function (opts) { }); webView.webEventReceived.connect(function (data) { - data = JSON.parse(data); + try{ + data = JSON.parse(data); + } + catch(e){ + return; + } var i, properties, dY, diff, newPosition; if (data.type === "print") { if (data.message) { @@ -1418,7 +1423,8 @@ var PropertiesTool = function (opts) { pushCommandForSelections(); selectionManager._update(); } else if(data.type === 'saveUserData'){ - Entities.editEntity(data.id, data.properties) + var actualID = data.id.split('"')[1] + var success = Entities.editEntity(actualID, data.properties) } else if (data.type === "showMarketplace") { showMarketplace(); } else if (data.type === "action") { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 41a1266434..6337ef00af 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -333,7 +333,13 @@ function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, d var properties = {}; var parsedData = {}; try { - parsedData = JSON.parse(userDataElement.value); + if ($('#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) {} if (!(groupName in parsedData)) { @@ -441,7 +447,11 @@ function hideUserDataTextArea() { function showStaticUserData() { $('#static-userdata').show(); $('#static-userdata').css('height', $('#userdata-editor').height()) - $('#static-userdata').text(editor.getText()); + if (editor !== null) { + $('#static-userdata').text(editor.getText()); + } + + }; function removeStaticUserData() { @@ -450,7 +460,10 @@ function removeStaticUserData() { function setEditorJSON(json) { editor.set(json) - editor.expandAll(); + if (editor.hasOwnProperty('expandAll')) { + editor.expandAll(); + } + }; function getEditorJSON() { @@ -745,11 +758,11 @@ function loaded() { } else { properties = data.selections[0].properties; - if (lastEntityID !== properties.id && lastEntityID !== null && editor !== null) { + if (lastEntityID !== '"' + properties.id + '"' && lastEntityID !== null && editor !== null) { saveJSONUserData(true); } - lastEntityID = properties.id; + lastEntityID = '"' + properties.id + '"'; elID.innerHTML = properties.id; elType.innerHTML = properties.type; @@ -840,26 +853,27 @@ function loaded() { FIXME: See FIXME for property-script-url. elScriptTimestamp.value = properties.scriptTimestamp; */ - hideUserDataTextArea(); + var json = null; try { json = JSON.parse(properties.userData); - + } catch (e) { + //normal text + deleteJSONEditor(); + elUserData.value = properties.userData; + showUserDataTextArea(); + showNewJSONEditorButton(); + hideSaveUserDataButton(); + } + if (json !== null) { if (editor === null) { createJSONEditor(); } setEditorJSON(json); showSaveUserDataButton(); + hideUserDataTextArea(); hideNewJSONEditorButton(); - - } catch (e) { - //normal text - - elUserData.value = properties.userData; - showUserDataTextArea(); - showNewJSONEditorButton(); - hideSaveUserDataButton(); } elHyperlinkHref.value = properties.href; From ee190db5eb8fa4bbbd416b9a8fe6fde32a633e37 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 25 Aug 2016 16:27:58 -0700 Subject: [PATCH 317/332] some comments --- scripts/system/edit.js | 1 + scripts/system/html/js/entityProperties.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 1727857ee8..84afca4a01 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1423,6 +1423,7 @@ var PropertiesTool = function (opts) { pushCommandForSelections(); selectionManager._update(); } else if(data.type === 'saveUserData'){ + //the event bridge and json parsing handle our avatar id string differently. var actualID = data.id.split('"')[1] var success = Entities.editEntity(actualID, data.properties) } else if (data.type === "showMarketplace") { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 6337ef00af..8ce3fbbe00 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -758,9 +758,11 @@ function loaded() { } else { properties = data.selections[0].properties; + if (lastEntityID !== '"' + properties.id + '"' && lastEntityID !== null && editor !== null) { saveJSONUserData(true); } + //the event bridge and json parsing handle our avatar id string differently. lastEntityID = '"' + properties.id + '"'; elID.innerHTML = properties.id; From 8cc65ea9653244dbbaae7bcbb66ff099a0cb2ab0 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 25 Aug 2016 16:31:42 -0700 Subject: [PATCH 318/332] Adrressing review issues --- libraries/render-utils/src/AmbientOcclusionEffect.cpp | 3 --- libraries/render-utils/src/AmbientOcclusionEffect.h | 2 +- libraries/render-utils/src/SurfaceGeometryPass.cpp | 3 +-- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 613bc22c84..86223e9877 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -377,9 +377,6 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext float tHeight = occlusionViewport.w / (float)framebufferSize.y; - //_parametersBuffer->ditheringInfo.y += 0.25f; - - auto occlusionPipeline = getOcclusionPipeline(); auto firstHBlurPipeline = getHBlurPipeline(); auto lastVBlurPipeline = getVBlurPipeline(); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 7f3ff3f64f..8bb4600d3c 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -205,7 +205,7 @@ public: DebugAmbientOcclusionConfig() : render::Job::Config(true) {} bool showCursorPixel{ false }; - glm::vec2 debugCursorTexcoord{ 0.5, 0.5 }; + glm::vec2 debugCursorTexcoord{ 0.5f, 0.5f }; signals: void dirty(); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index cc8d04e929..7a5a34c756 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -74,7 +74,6 @@ void LinearDepthFramebuffer::allocate() { // For Linear Depth: _linearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); - // _linearDepthTexture->autoGenerateMips(5); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); @@ -82,7 +81,7 @@ void LinearDepthFramebuffer::allocate() { // For Downsampling: _halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); - _halfLinearDepthTexture->autoGenerateMips(5); + _halfLinearDepthTexture->autoGenerateMips(5); _halfNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); From 5e82093e69643868cc7053fae3cfaebd51bdc04a Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 25 Aug 2016 18:08:03 -0700 Subject: [PATCH 319/332] set skybox clear color to empty color --- libraries/model/src/model/Skybox.cpp | 5 +++++ libraries/model/src/model/Skybox.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index cb3fb43630..4a66c18f98 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -49,6 +49,11 @@ void Skybox::updateSchemaBuffer() const { } } +void Skybox::clear() { + _schemaBuffer.edit().color = vec3(0); + setCubemap(nullptr); +} + void Skybox::prepare(gpu::Batch& batch, int textureSlot, int bufferSlot) const { if (bufferSlot > -1) { batch.setUniformBuffer(bufferSlot, _schemaBuffer); diff --git a/libraries/model/src/model/Skybox.h b/libraries/model/src/model/Skybox.h index f12d259411..1e6d30bbc1 100755 --- a/libraries/model/src/model/Skybox.h +++ b/libraries/model/src/model/Skybox.h @@ -36,7 +36,7 @@ public: const gpu::TexturePointer& getCubemap() const { return _cubemap; } virtual bool empty() { return _schemaBuffer.get().color == vec3(0) && !_cubemap; } - virtual void clear() { setCubemap(nullptr); } + virtual void clear(); void prepare(gpu::Batch& batch, int textureSlot = SKYBOX_SKYMAP_SLOT, int bufferSlot = SKYBOX_CONSTANTS_SLOT) const; virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const; From 996346980d980b7d47a2e73dbacace2d0d78fe5c Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 25 Aug 2016 18:08:22 -0700 Subject: [PATCH 320/332] clear the skybox between scenes --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 39031acaa1..2aa84bff03 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -113,17 +113,19 @@ void EntityTreeRenderer::resetEntitiesScriptEngine() { void EntityTreeRenderer::clear() { leaveAllEntities(); + // unload and stop the engine if (_entitiesScriptEngine) { - // Unload and stop the engine here (instead of in its deleter) to - // avoid marshalling unload signals back to this thread + // do this here (instead of in deleter) to avoid marshalling unload signals back to this thread _entitiesScriptEngine->unloadAllEntityScripts(); _entitiesScriptEngine->stop(); } + // reset the engine if (_wantScripts && !_shuttingDown) { resetEntitiesScriptEngine(); } + // remove all entities from the scene auto scene = _viewState->getMain3DScene(); render::PendingChanges pendingChanges; foreach(auto entity, _entitiesInScene) { @@ -132,6 +134,10 @@ void EntityTreeRenderer::clear() { scene->enqueuePendingChanges(pendingChanges); _entitiesInScene.clear(); + // reset the zone to the default (while we load the next scene) + _bestZone = nullptr; + applyZonePropertiesToScene(_bestZone); + OctreeRenderer::clear(); } From 4c63ad1ac866d0d056e124efd7bb7a11776e3061 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 26 Aug 2016 10:19:31 -0700 Subject: [PATCH 321/332] code cleanup --- scripts/system/edit.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 84afca4a01..bb52637ef5 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1369,10 +1369,10 @@ var PropertiesTool = function (opts) { }); webView.webEventReceived.connect(function (data) { - try{ + try { data = JSON.parse(data); } - catch(e){ + catch(e) { return; } var i, properties, dY, diff, newPosition; @@ -1424,8 +1424,8 @@ var PropertiesTool = function (opts) { selectionManager._update(); } else if(data.type === 'saveUserData'){ //the event bridge and json parsing handle our avatar id string differently. - var actualID = data.id.split('"')[1] - var success = Entities.editEntity(actualID, data.properties) + var actualID = data.id.split('"')[1]; + Entities.editEntity(actualID, data.properties); } else if (data.type === "showMarketplace") { showMarketplace(); } else if (data.type === "action") { From f6bd04780d95f7906581e3bc794dccbf26c7f554 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 26 Aug 2016 11:56:40 -0700 Subject: [PATCH 322/332] add a print to catch errors --- scripts/system/edit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index bb52637ef5..8dc6e144f7 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1373,6 +1373,7 @@ var PropertiesTool = function (opts) { data = JSON.parse(data); } catch(e) { + print('web event was not valid json.') return; } var i, properties, dY, diff, newPosition; From 53a22819180a37271c5d1bac25fae8c000268fb4 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 26 Aug 2016 11:57:15 -0700 Subject: [PATCH 323/332] better --- scripts/system/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 8dc6e144f7..1a50fb0d8b 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1373,7 +1373,7 @@ var PropertiesTool = function (opts) { data = JSON.parse(data); } catch(e) { - print('web event was not valid json.') + print('Edit.js received web event that was not valid json.') return; } var i, properties, dY, diff, newPosition; From 7ffa2120069785ba68b04ab9519ee60428117c49 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 26 Aug 2016 19:04:24 -0700 Subject: [PATCH 324/332] Don't use textures that are in the transfer pipeline --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 9 +------- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 27 +++++++++++++++-------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 342b2611d5..818d95d756 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -243,15 +243,8 @@ bool GLTexture::isReady() const { return false; } - // If we're out of date, but the transfer is in progress, report ready - // as a special case auto syncState = _syncState.load(); - - if (isOutdated()) { - return Idle != syncState; - } - - if (Idle != syncState) { + if (isOutdated() || Idle != syncState) { return false; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 6ac83d7116..d050afbb59 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -21,6 +21,9 @@ struct GLFilterMode { class GLTexture : public GLObject { public: + static const uint16_t INVALID_MIP { (uint16_t)-1 }; + static const uint8_t INVALID_FACE { (uint8_t)-1 }; + static void initTextureTransferHelper(); static std::shared_ptr _textureTransferHelper; @@ -58,18 +61,24 @@ public: return object; } - if (object->isReady()) { - // Do we need to reduce texture memory usage? - if (object->isOverMaxMemory() && texturePointer->incremementMinMip()) { - // WARNING, this code path will essentially `delete this`, - // so no dereferencing of this instance should be done past this point - object = new GLTextureType(backend.shared_from_this(), texture, object); - _textureTransferHelper->transferTexture(texturePointer); - } - } else if (object->isOutdated()) { + if (object->isOutdated()) { // Object might be outdated, if so, start the transfer // (outdated objects that are already in transfer will have reported 'true' for ready() _textureTransferHelper->transferTexture(texturePointer); + return nullptr; + } + + if (!object->isReady()) { + return nullptr; + } + + // Do we need to reduce texture memory usage? + if (object->isOverMaxMemory() && texturePointer->incremementMinMip()) { + // WARNING, this code path will essentially `delete this`, + // so no dereferencing of this instance should be done past this point + object = new GLTextureType(backend.shared_from_this(), texture, object); + _textureTransferHelper->transferTexture(texturePointer); + return nullptr; } return object; From 95b5a23c7abaf56464262a868a4381e06b3338fa Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 26 Aug 2016 19:04:46 -0700 Subject: [PATCH 325/332] Properly destroy GL programs --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index a37ac37adb..7c369f4124 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -582,7 +582,7 @@ void GLBackend::releaseShader(GLuint id) const { void GLBackend::releaseProgram(GLuint id) const { Lock lock(_trashMutex); - _shadersTrash.push_back(id); + _programsTrash.push_back(id); } void GLBackend::releaseQuery(GLuint id) const { From 02c79d9714239e525e4486dee834d6d80fc136c1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 26 Aug 2016 19:05:29 -0700 Subject: [PATCH 326/332] Fix the OpenVR issues on AMD --- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 42 +++++++++++++--------- plugins/openvr/src/OpenVrDisplayPlugin.h | 2 ++ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 0cb95e5747..4eb371719b 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -56,7 +56,7 @@ public: using Condition = std::condition_variable; using Lock = std::unique_lock; friend class OpenVrDisplayPlugin; - OffscreenGLCanvas _canvas; + std::shared_ptr _canvas; BasicFramebufferWrapperPtr _framebuffer; ProgramPtr _program; ShapeWrapperPtr _plane; @@ -68,9 +68,7 @@ public: OpenVrSubmitThread(OpenVrDisplayPlugin& plugin) : _plugin(plugin) { - _canvas.create(plugin._container->getPrimaryContext()); - _canvas.doneCurrent(); - _canvas.moveToThreadWithContext(this); + setObjectName("OpenVR Submit Thread"); } void updateReprojectionProgram() { @@ -131,19 +129,20 @@ public: void run() override { QThread::currentThread()->setPriority(QThread::Priority::TimeCriticalPriority); - _canvas.makeCurrent(); + assert(_canvas->thread() == QThread::currentThread()); + _canvas->makeCurrent(); glDisable(GL_DEPTH_TEST); glViewport(0, 0, _plugin._renderTargetSize.x, _plugin._renderTargetSize.y); _framebuffer = std::make_shared(); _framebuffer->Init(_plugin._renderTargetSize); updateReprojectionProgram(); _plane = loadPlane(_program); - _canvas.doneCurrent(); + _canvas->doneCurrent(); while (!_quit) { - _canvas.makeCurrent(); + _canvas->makeCurrent(); updateSource(); if (!_current.texture) { - _canvas.doneCurrent(); + _canvas->doneCurrent(); QThread::usleep(1); continue; } @@ -199,15 +198,15 @@ public: _presented.notify_one(); }); } - _canvas.doneCurrent(); + _canvas->doneCurrent(); } - _canvas.makeCurrent(); + _canvas->makeCurrent(); _plane.reset(); _program.reset(); _framebuffer.reset(); - _canvas.doneCurrent(); - + _canvas->doneCurrent(); + _canvas->moveToThreadWithContext(qApp->thread()); } void update(const CompositeInfo& newCompositeInfo) { @@ -307,10 +306,17 @@ bool OpenVrDisplayPlugin::internalActivate() { } #if OPENVR_THREADED_SUBMIT - withMainThreadContext([&] { - _submitThread = std::make_shared(*this); - }); - _submitThread->setObjectName("OpenVR Submit Thread"); + _submitThread = std::make_shared(*this); + if (!_submitCanvas) { + withMainThreadContext([&] { + _submitCanvas = std::make_shared(); + _submitCanvas->setObjectName("OpenVRSubmitContext"); + _submitCanvas->create(_container->getPrimaryContext()); + _submitCanvas->doneCurrent(); + }); + } + _submitCanvas->moveToThreadWithContext(_submitThread.get()); + assert(_submitCanvas->thread() == _submitThread.get()); #endif return Parent::internalActivate(); @@ -348,6 +354,8 @@ void OpenVrDisplayPlugin::customizeContext() { } _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture, false); } + assert(_submitCanvas->thread() == _submitThread.get()); + _submitThread->_canvas = _submitCanvas; _submitThread->start(QThread::HighPriority); #endif } @@ -358,6 +366,8 @@ void OpenVrDisplayPlugin::uncustomizeContext() { #if OPENVR_THREADED_SUBMIT _submitThread->_quit = true; _submitThread->wait(); + _submitThread.reset(); + assert(_submitCanvas->thread() == qApp->thread()); #endif } diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index ba51511fe8..7b8869ae93 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -19,6 +19,7 @@ const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device p #if OPENVR_THREADED_SUBMIT class OpenVrSubmitThread; +class OffscreenGLCanvas; static const size_t COMPOSITING_BUFFER_SIZE = 3; struct CompositeInfo { @@ -78,6 +79,7 @@ private: CompositeInfo::Array _compositeInfos; size_t _renderingIndex { 0 }; std::shared_ptr _submitThread; + std::shared_ptr _submitCanvas; friend class OpenVrSubmitThread; #endif }; From 1652965ac2f4a2092bbe20c8b4c25c7f71502bb6 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 26 Aug 2016 19:07:44 -0700 Subject: [PATCH 327/332] Destroy contexts properly --- libraries/gl/src/gl/OffscreenGLCanvas.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index e7f821a49f..2f8b29325e 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -23,7 +23,8 @@ OffscreenGLCanvas::OffscreenGLCanvas() : _context(new QOpenGLContext), _offscree } OffscreenGLCanvas::~OffscreenGLCanvas() { - _context->doneCurrent(); + // A context with logging enabled needs to be current when it's destroyed + _context->makeCurrent(_offscreenSurface); delete _context; _context = nullptr; From e4334be0cf7e7b631e7f5a0044dcc3dc23363c41 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 26 Aug 2016 19:08:15 -0700 Subject: [PATCH 328/332] Use synchronous logging (may impact performance when debugging is enabled) --- libraries/gl/src/gl/GLHelpers.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index 633c4ef0eb..f464001f60 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -102,5 +102,6 @@ void GLDebug::setupLogger(QObject* window) { QObject::connect(logger, &QOpenGLDebugLogger::messageLogged, window, [&](const QOpenGLDebugMessage & debugMessage) { GLDebug::log(debugMessage); }); + logger->startLogging(QOpenGLDebugLogger::SynchronousLogging); } } \ No newline at end of file From 0213dd0359d3b2767917e458ca674d46e701b26b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 26 Aug 2016 19:08:50 -0700 Subject: [PATCH 329/332] Don't bottleneck the render-perf tool on new frame creation --- tests/render-perf/src/main.cpp | 73 +++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index eec7e994ae..f7e6b6a6d2 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -172,8 +172,8 @@ void main(void) { extern QThread* RENDER_THREAD; -class RenderThread : public GenericQueueThread { - using Parent = GenericQueueThread; +class RenderThread : public GenericThread { + using Parent = GenericThread; public: QOpenGLContextWrapper* _displayContext{ nullptr }; QSurface* _displaySurface{ nullptr }; @@ -187,8 +187,15 @@ public: std::shared_ptr _backend; std::vector _frameTimes; size_t _frameIndex; + std::mutex _frameLock; + std::queue _pendingFrames; + gpu::FramePointer _activeFrame; static const size_t FRAME_TIME_BUFFER_SIZE{ 8192 }; + void submitFrame(const gpu::FramePointer& frame) { + std::unique_lock lock(_frameLock); + _pendingFrames.push(frame); + } void initialize(QOpenGLContextWrapper* displayContext, QWindow* surface) { @@ -203,6 +210,7 @@ public: _displayContext->makeCurrent(_displaySurface); DependencyManager::get()->init(); _displayContext->doneCurrent(); + std::unique_lock lock(_mutex); Parent::initialize(); if (isThreaded()) { _displayContext->moveToThread(thread()); @@ -211,6 +219,11 @@ public: void setup() override { RENDER_THREAD = QThread::currentThread(); + QThread::currentThread()->setPriority(QThread::HighestPriority); + // Wait until the context has been moved to this thread + { + std::unique_lock lock(_mutex); + } _displayContext->makeCurrent(_displaySurface); glewExperimental = true; glewInit(); @@ -229,12 +242,21 @@ public: //_textOverlay = new TextOverlay(glm::uvec2(800, 600)); glViewport(0, 0, 800, 600); + (void)CHECK_GL_ERROR(); _elapsed.start(); } void shutdown() override { + _activeFrame.reset(); + while (!_pendingFrames.empty()) { + _gpuContext->consumeFrameUpdates(_pendingFrames.front()); + _pendingFrames.pop(); + } _presentPipeline.reset(); _gpuContext.reset(); + if (isThreaded()) { + _displayContext->moveToThread(qApp->thread()); + } } void renderFrame(gpu::FramePointer& frame) { @@ -256,9 +278,7 @@ public: presentBatch.draw(gpu::TRIANGLE_STRIP, 4); _gpuContext->executeBatch(presentBatch); } - } - { - //_textOverlay->render(); + (void)CHECK_GL_ERROR(); } _displayContext->swapBuffers(_displaySurface); _fpsCounter.increment(); @@ -269,6 +289,8 @@ public: _frameCount = 0; _elapsed.restart(); } + (void)CHECK_GL_ERROR(); + _displayContext->doneCurrent(); } void report() { @@ -292,10 +314,30 @@ public: } } - bool processQueueItems(const Queue& items) override { - for (auto frame : items) { + + bool process() override { + std::queue pendingFrames; + { + std::unique_lock lock(_frameLock); + pendingFrames.swap(_pendingFrames); + } + + while (!pendingFrames.empty()) { + _activeFrame = pendingFrames.front(); + if (_activeFrame) { + _gpuContext->consumeFrameUpdates(_activeFrame); + } + pendingFrames.pop(); + } + + if (!_activeFrame) { + QThread::msleep(1); + return true; + } + + { auto start = usecTimestampNow(); - renderFrame(frame); + renderFrame(_activeFrame); auto duration = usecTimestampNow() - start; auto frameBufferIndex = _frameIndex % FRAME_TIME_BUFFER_SIZE; _frameTimes[frameBufferIndex] = duration; @@ -416,6 +458,7 @@ public: } QTestWindow() { + installEventFilter(this); _camera.movementSpeed = 50.0f; QThreadPool::globalInstance()->setMaxThreadCount(2); QThread::currentThread()->setPriority(QThread::HighestPriority); @@ -457,7 +500,7 @@ public: _renderThread.initialize(&_context, this); // FIXME use a wait condition QThread::msleep(1000); - _renderThread.queueItem(gpu::FramePointer()); + _renderThread.submitFrame(gpu::FramePointer()); _initContext.makeCurrent(); // Render engine init _renderEngine->addJob("RenderShadowTask", _cullFunctor); @@ -479,7 +522,6 @@ public: } virtual ~QTestWindow() { - _renderThread.terminate(); getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts _renderEngine.reset(); _main3DScene.reset(); @@ -497,6 +539,15 @@ public: } protected: + + bool eventFilter(QObject *obj, QEvent *event) override { + if (event->type() == QEvent::Close) { + _renderThread.terminate(); + } + + return QWindow::eventFilter(obj, event); + } + void keyPressEvent(QKeyEvent* event) override { switch (event->key()) { case Qt::Key_F1: @@ -742,7 +793,7 @@ private: frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){ DependencyManager::get()->releaseFramebuffer(framebuffer); }; - _renderThread.queueItem(frame); + _renderThread.submitFrame(frame); if (!_renderThread.isThreaded()) { _renderThread.process(); } From 587d015c57450d8fbbc0dbffe63449cd9f472b05 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 26 Aug 2016 19:09:26 -0700 Subject: [PATCH 330/332] Give names to offscreen contexts for easier debugging --- interface/src/Application.cpp | 2 ++ libraries/gl/src/gl/OffscreenQmlSurface.cpp | 1 + libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp | 1 + 3 files changed, 4 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b2095dbdfe..aaf38e826b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1483,6 +1483,7 @@ void Application::initializeGL() { _glWidget->makeCurrent(); _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->setObjectName("ChromiumShareContext"); _chromiumShareContext->create(_glWidget->context()->contextHandle()); _chromiumShareContext->makeCurrent(); qt_gl_set_global_share_context(_chromiumShareContext->getContext()); @@ -1529,6 +1530,7 @@ void Application::initializeGL() { _idleLoopStdev.reset(); _offscreenContext = new OffscreenGLCanvas(); + _offscreenContext->setObjectName("MainThreadContext"); _offscreenContext->create(_glWidget->context()->contextHandle()); _offscreenContext->makeCurrent(); diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index c47312f9f6..7973ed4b4f 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -195,6 +195,7 @@ QEvent* OffscreenQmlRenderThread::Queue::take() { } OffscreenQmlRenderThread::OffscreenQmlRenderThread(OffscreenQmlSurface* surface, QOpenGLContext* shareContext) : _surface(surface) { + _canvas.setObjectName("OffscreenQmlRenderCanvas"); qDebug() << "Building QML Renderer"; if (!_canvas.create(shareContext)) { qWarning("Failed to create OffscreenGLCanvas"); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp index 7acb736063..766c134b16 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp @@ -21,6 +21,7 @@ using namespace gpu::gl; GLTextureTransferHelper::GLTextureTransferHelper() { #ifdef THREADED_TEXTURE_TRANSFER _canvas = QSharedPointer(new OffscreenGLCanvas(), &QObject::deleteLater); + _canvas->setObjectName("TextureTransferCanvas"); _canvas->create(QOpenGLContextWrapper::currentContext()); if (!_canvas->makeCurrent()) { qFatal("Unable to create texture transfer context"); From 92fa1d57769965d7f36794b587bc6ede645c348e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 27 Aug 2016 15:08:38 -0700 Subject: [PATCH 331/332] Use stream draw for object position updates --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTransform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTransform.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTransform.cpp index 314e7d79be..ace0e73cf9 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTransform.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTransform.cpp @@ -38,7 +38,7 @@ void GL45Backend::transferTransformState(const Batch& batch) const { } if (!batch._objects.empty()) { - glNamedBufferData(_transform._objectBuffer, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_DYNAMIC_DRAW); + glNamedBufferData(_transform._objectBuffer, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_STREAM_DRAW); } if (!batch._namedData.empty()) { From 01af8899bf40348b40076938c1ba9b68eaf12d55 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 29 Aug 2016 10:47:22 -0700 Subject: [PATCH 332/332] silence address bar dialog qml warning --- interface/src/ui/AddressBarDialog.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index 3197770433..2dad742ebb 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -22,7 +22,7 @@ class AddressBarDialog : public OffscreenQmlDialog { Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged) Q_PROPERTY(bool forwardEnabled READ forwardEnabled NOTIFY forwardEnabledChanged) Q_PROPERTY(bool useFeed READ useFeed WRITE setUseFeed NOTIFY useFeedChanged) - Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl) + Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl NOTIFY metaverseServerUrlChanged) public: AddressBarDialog(QQuickItem* parent = nullptr); @@ -37,6 +37,7 @@ signals: void forwardEnabledChanged(); void useFeedChanged(); void receivedHifiSchemeURL(const QString& url); + void metaverseServerUrlChanged(); // While it is a constant, qml will complain about not seeing a change signal. protected: void displayAddressOfflineMessage();