diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8ead24d939..a10e42e87e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1886,9 +1886,9 @@ void Application::initializeGL() { assert(items.canCast()); static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { - _renderEngine->addJob("Forward", items.get()); + _renderEngine->addJob("Forward", items); } else { - _renderEngine->addJob("RenderDeferredTask", items.get()); + _renderEngine->addJob("RenderDeferredTask", items); } _renderEngine->load(); _renderEngine->registerScene(_main3DScene); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index fd5e07918e..63684dcf0f 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -43,6 +43,8 @@ #include "AddressManager.h" #include +#include "ZoneRenderer.h" + EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : _wantScripts(wantScripts), @@ -266,6 +268,9 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVectorgetAvatarPosition() + glm::vec3((float)TREE_SCALE); } +bool EntityTreeRenderer::applyLayeredZones() { + // from the list of zones we are going to build a selection list the Render Item corresponding to the zones + // in the expected layered order and update the scene with it + auto scene = _viewState->getMain3DScene(); + if (scene) { + render::Transaction transaction; + render::ItemIDs list; + + for (auto& zone : _layeredZones) { + auto id = std::dynamic_pointer_cast(zone.zone)->getRenderItemID(); + list.push_back(id); + } + render::Selection selection("RankedZones", list); + transaction.resetSelection(selection); + + scene->enqueueTransaction(transaction); + } else { + qCWarning(entitiesrenderer) << "EntityTreeRenderer::applyLayeredZones(), Unexpected null scene, possibly during application shutdown"; + } + + return true; +} + + bool EntityTreeRenderer::applyZoneAndHasSkybox(const std::shared_ptr& zone) { auto textureCache = DependencyManager::get(); auto scene = DependencyManager::get(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 753f25310c..ec9f707962 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -150,6 +150,7 @@ private: bool applyZoneAndHasSkybox(const std::shared_ptr& zone); bool layerZoneAndHasSkybox(const std::shared_ptr& zone); bool applySkyboxAndHasAmbient(); + bool applyLayeredZones(); void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false, const bool unloadFirst = false); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index bbb7ed0c01..d86a22509b 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -41,6 +41,8 @@ public: virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) override; virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) override; + render::ItemID getRenderItemID() const { return _myMetaItem; } + private: virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); } virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 22aa95090c..35bf9c0bde 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include "FramebufferCache.h" #include "HitEffect.h" #include "TextureCache.h" +#include "ZoneRenderer.h" #include "AmbientOcclusionEffect.h" #include "AntialiasingEffect.h" @@ -48,7 +50,9 @@ using namespace render; extern void initOverlay3DPipelines(render::ShapePlumber& plumber); extern void initDeferredPipelines(render::ShapePlumber& plumber); -RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) { +void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { + auto items = input.get(); + // Prepare the ShapePipelines ShapePlumberPointer shapePlumber = std::make_shared(); initDeferredPipelines(*shapePlumber); @@ -65,129 +69,130 @@ RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) { // Filter the non antialiaased overlays const int LAYER_NO_AA = 3; - const auto nonAAOverlays = addJob("Filter2DWebOverlays", overlayOpaques, LAYER_NO_AA); + const auto nonAAOverlays = task.addJob("Filter2DWebOverlays", overlayOpaques, LAYER_NO_AA); // Prepare deferred, generate the shared Deferred Frame Transform - const auto deferredFrameTransform = addJob("DeferredFrameTransform"); - const auto lightingModel = addJob("LightingModel"); + const auto deferredFrameTransform = task.addJob("DeferredFrameTransform"); + const auto lightingModel = task.addJob("LightingModel"); // GPU jobs: Start preparing the primary, deferred and lighting buffer - const auto primaryFramebuffer = addJob("PreparePrimaryBuffer"); + const auto primaryFramebuffer = task.addJob("PreparePrimaryBuffer"); - const auto opaqueRangeTimer = addJob("BeginOpaqueRangeTimer", "DrawOpaques"); + const auto opaqueRangeTimer = task.addJob("BeginOpaqueRangeTimer", "DrawOpaques"); const auto prepareDeferredInputs = PrepareDeferred::Inputs(primaryFramebuffer, lightingModel).hasVarying(); - const auto prepareDeferredOutputs = addJob("PrepareDeferred", prepareDeferredInputs); + const auto prepareDeferredOutputs = task.addJob("PrepareDeferred", prepareDeferredInputs); const auto deferredFramebuffer = prepareDeferredOutputs.getN(0); const auto lightingFramebuffer = prepareDeferredOutputs.getN(1); // Render opaque objects in DeferredBuffer const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).hasVarying(); - addJob("DrawOpaqueDeferred", opaqueInputs, shapePlumber); + task.addJob("DrawOpaqueDeferred", opaqueInputs, shapePlumber); // Once opaque is all rendered create stencil background - addJob("DrawOpaqueStencil", deferredFramebuffer); + task.addJob("DrawOpaqueStencil", deferredFramebuffer); - addJob("OpaqueRangeTimer", opaqueRangeTimer); + task.addJob("OpaqueRangeTimer", opaqueRangeTimer); // Opaque all rendered // Linear Depth Pass const auto linearDepthPassInputs = LinearDepthPass::Inputs(deferredFrameTransform, deferredFramebuffer).hasVarying(); - const auto linearDepthPassOutputs = addJob("LinearDepth", linearDepthPassInputs); + const auto linearDepthPassOutputs = task.addJob("LinearDepth", linearDepthPassInputs); const auto linearDepthTarget = linearDepthPassOutputs.getN(0); // Curvature pass const auto surfaceGeometryPassInputs = SurfaceGeometryPass::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget).hasVarying(); - const auto surfaceGeometryPassOutputs = addJob("SurfaceGeometry", surfaceGeometryPassInputs); + const auto surfaceGeometryPassOutputs = task.addJob("SurfaceGeometry", surfaceGeometryPassInputs); const auto surfaceGeometryFramebuffer = surfaceGeometryPassOutputs.getN(0); const auto curvatureFramebuffer = surfaceGeometryPassOutputs.getN(1); const auto midCurvatureNormalFramebuffer = surfaceGeometryPassOutputs.getN(2); const auto lowCurvatureNormalFramebuffer = surfaceGeometryPassOutputs.getN(3); // Simply update the scattering resource - const auto scatteringResource = addJob("Scattering"); + const auto scatteringResource = task.addJob("Scattering"); // AO job const auto ambientOcclusionInputs = AmbientOcclusionEffect::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget).hasVarying(); - const auto ambientOcclusionOutputs = addJob("AmbientOcclusion", ambientOcclusionInputs); + const auto ambientOcclusionOutputs = task.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); + task.addJob("DrawLight", lights); // Light Clustering // Create the cluster grid of lights, cpu job for now const auto lightClusteringPassInputs = LightClusteringPass::Inputs(deferredFrameTransform, lightingModel, linearDepthTarget).hasVarying(); - const auto lightClusters = addJob("LightClustering", lightClusteringPassInputs); + const auto lightClusters = task.addJob("LightClustering", lightClusteringPassInputs); // DeferredBuffer is complete, now let's shade it into the LightingBuffer const auto deferredLightingInputs = RenderDeferred::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource, lightClusters).hasVarying(); - addJob("RenderDeferred", deferredLightingInputs); + task.addJob("RenderDeferred", deferredLightingInputs); // Use Stencil and draw background in Lighting buffer to complete filling in the opaque const auto backgroundInputs = DrawBackgroundDeferred::Inputs(background, lightingModel).hasVarying(); - addJob("DrawBackgroundDeferred", backgroundInputs); + task.addJob("DrawBackgroundDeferred", backgroundInputs); // Render transparent objects forward in LightingBuffer const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).hasVarying(); - addJob("DrawTransparentDeferred", transparentsInputs, shapePlumber); + task.addJob("DrawTransparentDeferred", transparentsInputs, shapePlumber); // LIght Cluster Grid Debuging job { const auto debugLightClustersInputs = DebugLightClusters::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, linearDepthTarget, lightClusters).hasVarying(); - addJob("DebugLightClusters", debugLightClustersInputs); + task.addJob("DebugLightClusters", debugLightClustersInputs); } - const auto toneAndPostRangeTimer = addJob("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing"); + const auto toneAndPostRangeTimer = task.addJob("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing"); // Lighting Buffer ready for tone mapping const auto toneMappingInputs = render::Varying(ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer)); - addJob("ToneMapping", toneMappingInputs); + task.addJob("ToneMapping", toneMappingInputs); { // DEbug the bounds of the rendered items, still look at the zbuffer - addJob("DrawMetaBounds", metas); - addJob("DrawOpaqueBounds", opaques); - addJob("DrawTransparentBounds", transparents); + task.addJob("DrawMetaBounds", metas); + task.addJob("DrawOpaqueBounds", opaques); + task.addJob("DrawTransparentBounds", transparents); + + task.addJob("ZoneRenderer", opaques); } // Overlays const auto overlayOpaquesInputs = DrawOverlay3D::Inputs(overlayOpaques, lightingModel).hasVarying(); const auto overlayTransparentsInputs = DrawOverlay3D::Inputs(overlayTransparents, lightingModel).hasVarying(); - addJob("DrawOverlay3DOpaque", overlayOpaquesInputs, true); - addJob("DrawOverlay3DTransparent", overlayTransparentsInputs, false); + task.addJob("DrawOverlay3DOpaque", overlayOpaquesInputs, true); + task.addJob("DrawOverlay3DTransparent", overlayTransparentsInputs, false); { // DEbug the bounds of the rendered OVERLAY items, still look at the zbuffer - addJob("DrawOverlayOpaqueBounds", overlayOpaques); - addJob("DrawOverlayTransparentBounds", overlayTransparents); + task.addJob("DrawOverlayOpaqueBounds", overlayOpaques); + task.addJob("DrawOverlayTransparentBounds", overlayTransparents); } // Debugging stages { // Debugging Deferred buffer job const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); - addJob("DebugDeferredBuffer", debugFramebuffers); + task.addJob("DebugDeferredBuffer", debugFramebuffers); const auto debugSubsurfaceScatteringInputs = DebugSubsurfaceScattering::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource).hasVarying(); - addJob("DebugScattering", debugSubsurfaceScatteringInputs); + task.addJob("DebugScattering", debugSubsurfaceScatteringInputs); const auto debugAmbientOcclusionInputs = DebugAmbientOcclusion::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget, ambientOcclusionUniforms).hasVarying(); - addJob("DebugAmbientOcclusion", debugAmbientOcclusionInputs); - + task.addJob("DebugAmbientOcclusion", debugAmbientOcclusionInputs); // Scene Octree Debugging job { - addJob("DrawSceneOctree", spatialSelection); - addJob("DrawItemSelection", spatialSelection); + task.addJob("DrawSceneOctree", spatialSelection); + task.addJob("DrawItemSelection", spatialSelection); } // Status icon rendering job @@ -195,22 +200,22 @@ RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) { // Grab a texture map representing the different status icons and assign that to the drawStatsuJob auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath, NetworkTexture::STRICT_TEXTURE); - addJob("DrawStatus", opaques, DrawStatus(statusIconMap)); + task.addJob("DrawStatus", opaques, DrawStatus(statusIconMap)); } } // AA job to be revisited - addJob("Antialiasing", primaryFramebuffer); + task.addJob("Antialiasing", primaryFramebuffer); // Draw 2DWeb non AA const auto nonAAOverlaysInputs = DrawOverlay3D::Inputs(nonAAOverlays, lightingModel).hasVarying(); - addJob("Draw2DWebSurfaces", nonAAOverlaysInputs, false); + task.addJob("Draw2DWebSurfaces", nonAAOverlaysInputs, false); - addJob("ToneAndPostRangeTimer", toneAndPostRangeTimer); + task.addJob("ToneAndPostRangeTimer", toneAndPostRangeTimer); // Blit! - addJob("Blit", primaryFramebuffer); + task.addJob("Blit", primaryFramebuffer); } void BeginGPURangeTimer::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer) { diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 8a95447e67..660cae88b0 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -192,11 +192,14 @@ public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer); }; -class RenderDeferredTask : public render::Task { +class RenderDeferredTask { public: - using JobModel = Model; + using Input = RenderFetchCullSortTask::Output; + using JobModel = render::Task::ModelI; - RenderDeferredTask(RenderFetchCullSortTask::Output items); + RenderDeferredTask() {} + + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); }; #endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 49090c2f5f..8a9d7dfbf3 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -29,7 +29,9 @@ using namespace render; extern void initForwardPipelines(ShapePlumber& plumber); -RenderForwardTask::RenderForwardTask(RenderFetchCullSortTask::Output items) { +void RenderForwardTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { + auto items = input.get(); + // Prepare the ShapePipelines ShapePlumberPointer shapePlumber = std::make_shared(); initForwardPipelines(*shapePlumber); @@ -44,17 +46,17 @@ RenderForwardTask::RenderForwardTask(RenderFetchCullSortTask::Output items) { const auto background = items[RenderFetchCullSortTask::BACKGROUND]; const auto spatialSelection = items[RenderFetchCullSortTask::SPATIAL_SELECTION]; - const auto framebuffer = addJob("PrepareFramebuffer"); + const auto framebuffer = task.addJob("PrepareFramebuffer"); - addJob("DrawOpaques", opaques, shapePlumber); - addJob("Stencil"); - addJob("DrawBackground", background); + task.addJob("DrawOpaques", opaques, shapePlumber); + task.addJob("Stencil"); + task.addJob("DrawBackground", background); // Bounds do not draw on stencil buffer, so they must come last - addJob("DrawBounds", opaques); + task.addJob("DrawBounds", opaques); // Blit! - addJob("Blit", framebuffer); + task.addJob("Blit", framebuffer); } void PrepareFramebuffer::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 62cbca4382..f78ce8f317 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -16,11 +16,14 @@ #include #include "LightingModel.h" -class RenderForwardTask : public render::Task { +class RenderForwardTask { public: - using JobModel = Model; + using Input = RenderFetchCullSortTask::Output; + using JobModel = render::Task::ModelI; - RenderForwardTask(RenderFetchCullSortTask::Output items); + RenderForwardTask() {} + + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); }; class PrepareFramebuffer { diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index eb46b8f46e..ddfe038a1a 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -90,7 +90,7 @@ void RenderShadowMap::run(const render::SceneContextPointer& sceneContext, const }); } -RenderShadowTask::RenderShadowTask(CullFunctor cullFunctor) { +void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, CullFunctor cullFunctor) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; // Prepare the ShapePipeline @@ -115,28 +115,28 @@ RenderShadowTask::RenderShadowTask(CullFunctor cullFunctor) { skinProgram, state); } - const auto cachedMode = addJob("Setup"); + const auto cachedMode = task.addJob("Setup"); // CPU jobs: // Fetch and cull the items from the scene auto shadowFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); - const auto shadowSelection = addJob("FetchShadowSelection", shadowFilter); - const auto culledShadowSelection = addJob("CullShadowSelection", shadowSelection, cullFunctor, RenderDetails::SHADOW, shadowFilter); + const auto shadowSelection = task.addJob("FetchShadowSelection", shadowFilter); + const auto culledShadowSelection = task.addJob("CullShadowSelection", shadowSelection, cullFunctor, RenderDetails::SHADOW, shadowFilter); // Sort - const auto sortedPipelines = addJob("PipelineSortShadowSort", culledShadowSelection); - const auto sortedShapes = addJob("DepthSortShadowMap", sortedPipelines); + const auto sortedPipelines = task.addJob("PipelineSortShadowSort", culledShadowSelection); + const auto sortedShapes = task.addJob("DepthSortShadowMap", sortedPipelines); // GPU jobs: Render to shadow map - addJob("RenderShadowMap", sortedShapes, shapePlumber); + task.addJob("RenderShadowMap", sortedShapes, shapePlumber); - addJob("Teardown", cachedMode); + task.addJob("Teardown", cachedMode); } void RenderShadowTask::configure(const Config& configuration) { DependencyManager::get()->setShadowMapEnabled(configuration.enabled); // This is a task, so must still propogate configure() to its Jobs - Task::configure(configuration); +// Task::configure(configuration); } void RenderShadowSetup::run(const SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, Output& output) { diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index d2e58f8362..9190034b2e 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -41,12 +41,13 @@ signals: void dirty(); }; -class RenderShadowTask : public render::Task { +class RenderShadowTask { public: using Config = RenderShadowTaskConfig; - using JobModel = Model; + using JobModel = render::Task::Model; - RenderShadowTask(render::CullFunctor shouldRender); + RenderShadowTask() {} + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor shouldRender); void configure(const Config& configuration); }; diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp new file mode 100644 index 0000000000..2cabe58c33 --- /dev/null +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -0,0 +1,27 @@ +// +// ZoneRenderer.cpp +// render/src/render-utils +// +// Created by Sam Gateau on 4/4/2017. +// Copyright 2017 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 "ZoneRenderer.h" + +#include +#include + +using namespace render; + +const Selection::Name ZoneRendererTask::ZONES_SELECTION { "RankedZones" }; + +void ZoneRendererTask::build(JobModel& task, const Varying& input, Varying& ouput) { + + const auto zoneItems = task.addJob("FilterZones", input, ZONES_SELECTION.c_str()); + + // just draw them... + task.addJob("DrawZones", zoneItems); +} + diff --git a/libraries/render-utils/src/ZoneRenderer.h b/libraries/render-utils/src/ZoneRenderer.h new file mode 100644 index 0000000000..1218608400 --- /dev/null +++ b/libraries/render-utils/src/ZoneRenderer.h @@ -0,0 +1,52 @@ +// +// ZoneRenderer.h +// render/src/render-utils +// +// Created by Sam Gateau on 4/4/2017. +// Copyright 2017 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_ZoneRenderer_h +#define hifi_ZoneRenderer_h + +#include "render/Engine.h" + +class ZoneRendererConfig : public render::Task::Config { + Q_OBJECT + Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) +public: + + ZoneRendererConfig() : render::Task::Config(false) {} + + int maxDrawn { -1 }; + +signals: + void dirty(); + +protected: +}; + +class ZoneRendererTask { +public: + + static const render::Selection::Name ZONES_SELECTION; + + + using Inputs = render::ItemBounds; + using Config = ZoneRendererConfig; + using JobModel = render::Task::ModelI; + + ZoneRendererTask() {} + + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); + + void configure(const Config& config) { _maxDrawn = config.maxDrawn; } + +protected: + int _maxDrawn; // initialized by Config +}; + +#endif \ No newline at end of file diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 42f95f458f..e4ba5af13f 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -16,8 +16,6 @@ #include #include -#include -#include using namespace render; @@ -306,19 +304,3 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re std::static_pointer_cast(renderContext->jobConfig)->numItems = (int)outItems.size(); } - - -void FilterItemLayer::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { - auto& scene = sceneContext->_scene; - - // Clear previous values - outItems.clear(); - - // For each item, filter it into one bucket - for (auto itemBound : inItems) { - auto& item = scene->getItem(itemBound.id); - if (item.getLayer() == _keepLayer) { - outItems.emplace_back(itemBound); - } - } -} \ No newline at end of file diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index f613faa2e6..243b16e733 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -109,85 +109,6 @@ namespace render { void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems); }; - class FilterItemSelectionConfig : public Job::Config { - Q_OBJECT - Q_PROPERTY(int numItems READ getNumItems) - public: - int numItems{ 0 }; - int getNumItems() { return numItems; } - }; - - class FilterItemSelection { - public: - using Config = FilterItemSelectionConfig; - using JobModel = Job::ModelIO; - - FilterItemSelection() {} - FilterItemSelection(const ItemFilter& filter) : - _filter(filter) {} - - ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; - - void configure(const Config& config); - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); - }; - - class MultiFilterItemConfig : public Job::Config { - Q_OBJECT - Q_PROPERTY(int numItems READ getNumItems) - public: - int numItems{ 0 }; - int getNumItems() { return numItems; } - }; - - template - class MultiFilterItem { - public: - using ItemFilterArray = std::array; - using ItemBoundsArray = VaryingArray; - using Config = MultiFilterItemConfig; - using JobModel = Job::ModelIO; - - MultiFilterItem() {} - MultiFilterItem(const ItemFilterArray& filters) : - _filters(filters) {} - - ItemFilterArray _filters; - - void configure(const Config& config) {} - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBoundsArray& outItems) { - auto& scene = sceneContext->_scene; - - // Clear previous values - for (size_t i = 0; i < NUM_FILTERS; i++) { - outItems[i].template edit().clear(); - } - - // For each item, filter it into one bucket - for (auto itemBound : inItems) { - auto& item = scene->getItem(itemBound.id); - auto itemKey = item.getKey(); - for (size_t i = 0; i < NUM_FILTERS; i++) { - if (_filters[i].test(itemKey)) { - outItems[i].template edit().emplace_back(itemBound); - } - } - } - } - }; - - class FilterItemLayer { - public: - using JobModel = Job::ModelIO; - - FilterItemLayer() {} - FilterItemLayer(int keepLayer) : - _keepLayer(keepLayer) {} - - int _keepLayer { 0 }; - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); - }; } #endif // hifi_render_CullTask_h; \ No newline at end of file diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index e8537e3452..3deb15a320 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -150,6 +150,7 @@ const gpu::PipelinePointer DrawBounds::getPipeline() { _cornerLocation = program->getUniforms().findLocation("inBoundPos"); _scaleLocation = program->getUniforms().findLocation("inBoundDim"); + _colorLocation = program->getUniforms().findLocation("inColor"); auto state = std::make_shared(); state->setDepthTest(true, false, gpu::LESS_EQUAL); @@ -184,12 +185,17 @@ void DrawBounds::run(const SceneContextPointer& sceneContext, const RenderContex assert(_scaleLocation >= 0); // Render bounds + float numItems = (float) items.size(); + float itemNum = 0.0f; for (const auto& item : items) { + glm::vec4 color(glm::vec3(itemNum / numItems), 1.0f); batch._glUniform3fv(_cornerLocation, 1, (const float*)(&item.bound.getCorner())); batch._glUniform3fv(_scaleLocation, 1, (const float*)(&item.bound.getScale())); + batch._glUniform4fv(_colorLocation, 1, (const float*)(&color)); static const int NUM_VERTICES_PER_CUBE = 24; batch.draw(gpu::LINES, NUM_VERTICES_PER_CUBE, 0); + itemNum += 1.0f; } }); } diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index a9c5f3a4d8..9a74802888 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -52,7 +52,7 @@ class DrawBounds { public: class Config : public render::JobConfig { public: - Config() : JobConfig(false) {} + Config(bool enabled = false) : JobConfig(enabled) {} }; using Inputs = render::ItemBounds; @@ -67,6 +67,7 @@ private: gpu::PipelinePointer _boundsPipeline; int _cornerLocation { -1 }; int _scaleLocation { -1 }; + int _colorLocation { -1 }; }; } diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index 970cc142e0..08efb7b281 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -22,10 +22,22 @@ using namespace render; -Engine::Engine() : +class EngineTask { +public: + + using JobModel = Task::Model; + + EngineTask() {} + + void build(JobModel& task, const Varying& in, Varying& out) { + task.addJob("Stats"); + } +}; + +Engine::Engine() : Task("Engine", EngineTask::JobModel::create()), _sceneContext(std::make_shared()), - _renderContext(std::make_shared()) { - addJob("Stats"); + _renderContext(std::make_shared()) +{ } void Engine::load() { diff --git a/libraries/render/src/render/FilterTask.cpp b/libraries/render/src/render/FilterTask.cpp new file mode 100644 index 0000000000..f1ab6b844a --- /dev/null +++ b/libraries/render/src/render/FilterTask.cpp @@ -0,0 +1,113 @@ +// +// FilterTask.cpp +// render/src/render +// +// Created by Sam Gateau on 2/2/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 "FilterTask.h" + +#include +#include + +#include +#include +#include +#include + +using namespace render; + +void FilterLayeredItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { + auto& scene = sceneContext->_scene; + + // Clear previous values + outItems.clear(); + + // For each item, filter it into one bucket + for (auto itemBound : inItems) { + auto& item = scene->getItem(itemBound.id); + if (item.getLayer() == _keepLayer) { + outItems.emplace_back(itemBound); + } + } +} + +void SliceItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { + outItems.clear(); + std::static_pointer_cast(renderContext->jobConfig)->setNumItems((int)inItems.size()); + + if (_rangeOffset < 0) return; + + int maxItemNum = std::min(_rangeOffset + _rangeLength, (int)inItems.size()); + + + for (int i = _rangeOffset; i < maxItemNum; i++) { + outItems.emplace_back(inItems[i]); + } + +} + +void SelectItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { + auto selection = sceneContext->_scene->getSelection(_name); + const auto& selectedItems = selection.getItems(); + outItems.clear(); + + if (!selectedItems.empty()) { + outItems.reserve(selectedItems.size()); + + for (auto src : inItems) { + if (selection.contains(src.id)) { + outItems.emplace_back(src); + } + } + } +} + +void SelectSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { + auto selection = sceneContext->_scene->getSelection(_name); + const auto& selectedItems = selection.getItems(); + outItems.clear(); + + if (!selectedItems.empty()) { + struct Pair { int src; int dst; }; + std::vector indices; + indices.reserve(selectedItems.size()); + + // Collect + for (int srcIndex = 0; ((std::size_t) srcIndex < inItems.size()) && (indices.size() < selectedItems.size()) ; srcIndex++ ) { + int index = selection.find(inItems[srcIndex].id); + if (index != Selection::NOT_FOUND) { + indices.emplace_back( Pair{ srcIndex, index } ); + } + } + + // Then sort + if (!indices.empty()) { + std::sort(indices.begin(), indices.end(), [] (Pair a, Pair b) { + return (a.dst < b.dst); + }); + + for (auto& pair: indices) { + outItems.emplace_back(inItems[pair.src]); + } + } + } +} + +void MetaToSubItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemIDs& outItems) { + auto& scene = sceneContext->_scene; + + // Now we have a selection of items to render + outItems.clear(); + + for (auto idBound : inItems) { + auto& item = scene->getItem(idBound.id); + + item.fetchMetaSubItems(outItems); + } +} + diff --git a/libraries/render/src/render/FilterTask.h b/libraries/render/src/render/FilterTask.h new file mode 100644 index 0000000000..00019fa1ae --- /dev/null +++ b/libraries/render/src/render/FilterTask.h @@ -0,0 +1,147 @@ +// +// FilterTask.h +// render/src/render +// +// Created by Sam Gateau on 2/2/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_render_FilterTask_h +#define hifi_render_FilterTask_h + +#include "Engine.h" +#include "ViewFrustum.h" + +namespace render { + + class MultiFilterItemsConfig : public Job::Config { + Q_OBJECT + Q_PROPERTY(int numItems READ getNumItems) + public: + int numItems{ 0 }; + int getNumItems() { return numItems; } + }; + + // Filter inbound of items into multiple buckets defined from the job's Filter array + template + class MultiFilterItems { + public: + using ItemFilterArray = std::array; + using ItemBoundsArray = VaryingArray; + using Config = MultiFilterItemsConfig; + using JobModel = Job::ModelIO; + + MultiFilterItems() {} + MultiFilterItems(const ItemFilterArray& filters) : + _filters(filters) {} + + ItemFilterArray _filters; + + void configure(const Config& config) {} + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBoundsArray& outItems) { + auto& scene = sceneContext->_scene; + + // Clear previous values + for (size_t i = 0; i < NUM_FILTERS; i++) { + outItems[i].template edit().clear(); + } + + // For each item, filter it into one bucket + for (auto itemBound : inItems) { + auto& item = scene->getItem(itemBound.id); + auto itemKey = item.getKey(); + for (size_t i = 0; i < NUM_FILTERS; i++) { + if (_filters[i].test(itemKey)) { + outItems[i].template edit().emplace_back(itemBound); + } + } + } + } + }; + + // Filter the items belonging to the job's keep layer + class FilterLayeredItems { + public: + using JobModel = Job::ModelIO; + + FilterLayeredItems() {} + FilterLayeredItems(int keepLayer) : + _keepLayer(keepLayer) {} + + int _keepLayer { 0 }; + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); + }; + + // SliceItems job config defining the slice range + class SliceItemsConfig : public Job::Config { + Q_OBJECT + Q_PROPERTY(int rangeOffset MEMBER rangeOffset) + Q_PROPERTY(int rangeLength MEMBER rangeLength) + Q_PROPERTY(int numItems READ getNumItems NOTIFY dirty()) + int numItems { 0 }; + public: + int rangeOffset{ -1 }; + int rangeLength{ 1 }; + int getNumItems() { return numItems; } + void setNumItems(int n) { numItems = n; emit dirty(); } + signals: + void dirty(); + }; + + // Keep items in the job slice (defined from config) + class SliceItems { + public: + using Config = SliceItemsConfig; + using JobModel = Job::ModelIO; + + SliceItems() {} + int _rangeOffset{ -1 }; + int _rangeLength{ 1 }; + + void configure(const Config& config) { + _rangeOffset = config.rangeOffset; + _rangeLength = config.rangeLength; + } + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); + }; + + // Keep items belonging to the job selection + class SelectItems { + public: + using JobModel = Job::ModelIO; + + std::string _name; + SelectItems(const Selection::Name& name) : _name(name) {} + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); + }; + + // Same as SelectItems but reorder the output to match the selection order + class SelectSortItems { + public: + using JobModel = Job::ModelIO; + + std::string _name; + SelectSortItems(const Selection::Name& name) : _name(name) {} + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); + }; + + // From meta-Items, generate the sub-items + class MetaToSubItems { + public: + using JobModel = Job::ModelIO; + + MetaToSubItems() {} + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemIDs& outItems); + }; + +} + +#endif // hifi_render_FilterTask_h; \ No newline at end of file diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index 69c415dffd..d735afa52d 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -12,21 +12,22 @@ #include "RenderFetchCullSortTask.h" #include "CullTask.h" +#include "FilterTask.h" #include "SortTask.h" using namespace render; -RenderFetchCullSortTask::RenderFetchCullSortTask(CullFunctor cullFunctor) { +void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varying& output, CullFunctor cullFunctor) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; // CPU jobs: // Fetch and cull the items from the scene auto spatialFilter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); - const auto spatialSelection = addJob("FetchSceneSelection", spatialFilter); - const auto culledSpatialSelection = addJob("CullSceneSelection", spatialSelection, cullFunctor, RenderDetails::ITEM, spatialFilter); + const auto spatialSelection = task.addJob("FetchSceneSelection", spatialFilter); + const auto culledSpatialSelection = task.addJob("CullSceneSelection", spatialSelection, cullFunctor, RenderDetails::ITEM, spatialFilter); // Overlays are not culled - const auto nonspatialSelection = addJob("FetchOverlaySelection"); + const auto nonspatialSelection = task.addJob("FetchOverlaySelection"); // Multi filter visible items into different buckets const int NUM_SPATIAL_FILTERS = 4; @@ -36,34 +37,34 @@ RenderFetchCullSortTask::RenderFetchCullSortTask(CullFunctor cullFunctor) { const int LIGHT_BUCKET = 2; const int META_BUCKET = 3; const int BACKGROUND_BUCKET = 2; - MultiFilterItem::ItemFilterArray spatialFilters = { { + MultiFilterItems::ItemFilterArray spatialFilters = { { ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::transparentShape(), ItemFilter::Builder::light(), ItemFilter::Builder::meta() } }; - MultiFilterItem::ItemFilterArray nonspatialFilters = { { + MultiFilterItems::ItemFilterArray nonspatialFilters = { { ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::transparentShape(), ItemFilter::Builder::background() } }; const auto filteredSpatialBuckets = - addJob>("FilterSceneSelection", culledSpatialSelection, spatialFilters) - .get::ItemBoundsArray>(); + task.addJob>("FilterSceneSelection", culledSpatialSelection, spatialFilters) + .get::ItemBoundsArray>(); const auto filteredNonspatialBuckets = - addJob>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters) - .get::ItemBoundsArray>(); + task.addJob>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters) + .get::ItemBoundsArray>(); // Extract opaques / transparents / lights / overlays - const auto opaques = addJob("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]); - const auto transparents = addJob("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); + const auto opaques = task.addJob("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]); + const auto transparents = task.addJob("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto lights = filteredSpatialBuckets[LIGHT_BUCKET]; const auto metas = filteredSpatialBuckets[META_BUCKET]; - const auto overlayOpaques = addJob("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]); - const auto overlayTransparents = addJob("DepthSortOverlayTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); + const auto overlayOpaques = task.addJob("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]); + const auto overlayTransparents = task.addJob("DepthSortOverlayTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET]; - setOutput(Output{{ + output = Varying(Output{{ opaques, transparents, lights, metas, overlayOpaques, overlayTransparents, background, spatialSelection }}); } diff --git a/libraries/render/src/render/RenderFetchCullSortTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h index 1af74939c9..12bcb9786d 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -17,7 +17,7 @@ #include "Task.h" #include "CullTask.h" -class RenderFetchCullSortTask : public render::Task { +class RenderFetchCullSortTask { public: enum Buckets { @@ -34,9 +34,11 @@ public: }; using Output = std::array; - using JobModel = ModelO; + using JobModel = render::Task::ModelO; - RenderFetchCullSortTask(render::CullFunctor cullFunctor); + RenderFetchCullSortTask() {} + + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor); }; #endif // hifi_RenderFetchCullSortTask_h diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 537d8c1337..0e77b389a0 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -18,8 +18,8 @@ using namespace render; void Transaction::resetItem(ItemID id, const PayloadPointer& payload) { if (payload) { - _resetItems.push_back(id); - _resetPayloads.push_back(payload); + _resetItems.emplace_back(id); + _resetPayloads.emplace_back(payload); } else { qCDebug(renderlogging) << "WARNING: Transaction::resetItem with a null payload!"; removeItem(id); @@ -27,12 +27,16 @@ void Transaction::resetItem(ItemID id, const PayloadPointer& payload) { } void Transaction::removeItem(ItemID id) { - _removedItems.push_back(id); + _removedItems.emplace_back(id); } void Transaction::updateItem(ItemID id, const UpdateFunctorPointer& functor) { - _updatedItems.push_back(id); - _updateFunctors.push_back(functor); + _updatedItems.emplace_back(id); + _updateFunctors.emplace_back(functor); +} + +void Transaction::resetSelection(const Selection& selection) { + _resetSelections.emplace_back(selection); } void Transaction::merge(const Transaction& transaction) { @@ -41,8 +45,10 @@ void Transaction::merge(const Transaction& transaction) { _removedItems.insert(_removedItems.end(), transaction._removedItems.begin(), transaction._removedItems.end()); _updatedItems.insert(_updatedItems.end(), transaction._updatedItems.begin(), transaction._updatedItems.end()); _updateFunctors.insert(_updateFunctors.end(), transaction._updateFunctors.begin(), transaction._updateFunctors.end()); + _resetSelections.insert(_resetSelections.end(), transaction._resetSelections.begin(), transaction._resetSelections.end()); } + Scene::Scene(glm::vec3 origin, float size) : _masterSpatialTree(origin, size) { @@ -112,6 +118,13 @@ void Scene::processTransactionQueue() { // Update the numItemsAtomic counter AFTER the pending changes went through _numAllocatedItems.exchange(maxID); } + + if (consolidatedTransaction.touchTransactions()) { + std::unique_lock lock(_selectionsMutex); + + // resets and potential NEW items + resetSelections(consolidatedTransaction._resetSelections); + } } void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) { @@ -202,3 +215,25 @@ void Scene::updateItems(const ItemIDs& ids, UpdateFunctors& functors) { updateFunctor++; } } + +// THis fucntion is thread safe +Selection Scene::getSelection(const Selection::Name& name) const { + std::unique_lock lock(_selectionsMutex); + auto found = _selections.find(name); + if (found == _selections.end()) { + return Selection(); + } else { + return (*found).second; + } +} + +void Scene::resetSelections(const Selections& selections) { + for (auto selection : selections) { + auto found = _selections.find(selection.getName()); + if (found == _selections.end()) { + _selections.insert(SelectionMap::value_type(selection.getName(), selection)); + } else { + (*found).second = selection; + } + } +} diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 09a725205d..fc0a8c1fca 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -14,6 +14,7 @@ #include "Item.h" #include "SpatialTree.h" +#include "Selection.h" namespace render { @@ -33,6 +34,7 @@ public: Transaction() {} ~Transaction() {} + // Item transactions void resetItem(ItemID id, const PayloadPointer& payload); void removeItem(ItemID id); @@ -43,14 +45,22 @@ public: void updateItem(ItemID id, const UpdateFunctorPointer& functor); void updateItem(ItemID id) { updateItem(id, nullptr); } + // Selection transactions + void resetSelection(const Selection& selection); + void merge(const Transaction& transaction); + // Checkers if there is work to do when processing the transaction + bool touchTransactions() const { return !_resetSelections.empty(); } + ItemIDs _resetItems; Payloads _resetPayloads; ItemIDs _removedItems; ItemIDs _updatedItems; UpdateFunctors _updateFunctors; + Selections _resetSelections; + protected: }; typedef std::queue TransactionQueue; @@ -81,6 +91,10 @@ public: // Process the pending transactions queued void processTransactionQueue(); + // Access a particular selection (empty if doesn't exist) + // Thread safe + Selection getSelection(const Selection::Name& name) const; + // This next call are NOT threadsafe, you have to call them from the correct thread to avoid any potential issues // Access a particular item form its ID @@ -114,6 +128,17 @@ protected: void removeItems(const ItemIDs& ids); void updateItems(const ItemIDs& ids, UpdateFunctors& functors); + + // The Selection map + mutable std::mutex _selectionsMutex; // mutable so it can be used in the thread safe getSelection const method + SelectionMap _selections; + + void resetSelections(const Selections& selections); + // More actions coming to selections soon: + // void removeFromSelection(const Selection& selection); + // void appendToSelection(const Selection& selection); + // void mergeWithSelection(const Selection& selection); + friend class Engine; }; diff --git a/libraries/render/src/render/Selection.cpp b/libraries/render/src/render/Selection.cpp new file mode 100644 index 0000000000..46941ae440 --- /dev/null +++ b/libraries/render/src/render/Selection.cpp @@ -0,0 +1,68 @@ +// +// Selection.cpp +// render/src/render +// +// Created by Sam Gateau on 4/4/2017. +// Copyright 2017 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 "Selection.h" + +#include "Logging.h" + +using namespace render; + + +Selection::~Selection() { + +} + +Selection::Selection() : +_name(), +_items() +{ +} + +Selection::Selection(const Selection& selection) : +_name(selection._name), +_items(selection._items) +{ +} + +Selection& Selection::operator= (const Selection& selection) { + _name = (selection._name); + _items = (selection._items); + return (*this); +} + +Selection::Selection(Selection&& selection) : +_name(selection._name), +_items(selection._items) +{ +} + + +Selection& Selection::operator= (Selection&& selection) { + _name = (selection._name); + _items = (selection._items); + return (*this); +} + +Selection::Selection(const Name& name, const ItemIDs& items) : + _name(name), + _items(items) +{ +} + +int Selection::find(ItemID id) const { + int index = 0; + for (auto selected : _items) { + if (selected == id) { + return index; + } + index++; + } + return NOT_FOUND; +} diff --git a/libraries/render/src/render/Selection.h b/libraries/render/src/render/Selection.h new file mode 100644 index 0000000000..2c7ce710cd --- /dev/null +++ b/libraries/render/src/render/Selection.h @@ -0,0 +1,54 @@ +// +// Selection.h +// render/src/render +// +// Created by Sam Gateau on 4/4/2017. +// Copyright 2017 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_render_Selection_h +#define hifi_render_Selection_h + +#include "Item.h" + +namespace render { + + class Selection { + public: + using Name = std::string; + + ~Selection(); + Selection(); + Selection(const Selection& selection); + Selection& operator = (const Selection& selection); + Selection(Selection&& selection); + Selection& operator = (Selection&& selection); + + Selection(const Name& name, const ItemIDs& items); + + const Name& getName() const { return _name; } + + const ItemIDs& getItems() const { return _items; } + + bool isEmpty() const { return _items.empty(); } + + // Test if the ID is in the selection, return the index or -1 if not present + static const int NOT_FOUND{ -1 }; + + int find(ItemID id) const; + bool contains(ItemID id) const { return find(id) > NOT_FOUND; } + + protected: + Name _name; + ItemIDs _items; + }; + using Selections = std::vector; + + using SelectionMap = std::map; + +} + +#endif \ No newline at end of file diff --git a/libraries/render/src/render/Task.cpp b/libraries/render/src/render/Task.cpp index c1a917d26a..13476f102e 100644 --- a/libraries/render/src/render/Task.cpp +++ b/libraries/render/src/render/Task.cpp @@ -15,12 +15,42 @@ using namespace render; +void TaskConfig::connectChildConfig(QConfigPointer childConfig, const std::string& name) { + childConfig->setParent(this); + childConfig->setObjectName(name.c_str()); + + // Connect loaded->refresh + QObject::connect(childConfig.get(), SIGNAL(loaded()), this, SLOT(refresh())); + static const char* DIRTY_SIGNAL = "dirty()"; + if (childConfig->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) { + // Connect dirty->refresh if defined + QObject::connect(childConfig.get(), SIGNAL(dirty()), this, SLOT(refresh())); + } +} + +void TaskConfig::transferChildrenConfigs(QConfigPointer source) { + if (!source) { + return; + } + // Transfer children to the new configuration + auto children = source->children(); + for (auto& child : children) { + child->setParent(this); + QObject::connect(child, SIGNAL(loaded()), this, SLOT(refresh())); + static const char* DIRTY_SIGNAL = "dirty()"; + if (child->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) { + // Connect dirty->refresh if defined + QObject::connect(child, SIGNAL(dirty()), this, SLOT(refresh())); + } + } +} + void TaskConfig::refresh() { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "refresh", Qt::BlockingQueuedConnection); return; } - _task->configure(*this); + _task->applyConfiguration(); } diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index 4e3aa79c8d..03824bd14d 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -301,6 +301,7 @@ public: }; class Job; +class JobConcept; class Task; class JobNoIO {}; @@ -415,6 +416,8 @@ signals: class TaskConfig : public JobConfig { Q_OBJECT public: + using QConfigPointer = std::shared_ptr; + using Persistent = PersistentConfig; TaskConfig() = default ; @@ -428,12 +431,15 @@ public: return findChild(name); } + void connectChildConfig(QConfigPointer childConfig, const std::string& name); + void transferChildrenConfigs(QConfigPointer source); + public slots: void refresh(); private: - friend class Task; - Task* _task; + friend Task; + JobConcept* _task; }; template void jobConfigure(T& data, const C& configuration) { @@ -458,73 +464,37 @@ template void jobRun(T& data, const SceneContextPoin data.run(sceneContext, renderContext, input, output); } -class GPUJobConfig : public JobConfig { - Q_OBJECT - Q_PROPERTY(double gpuRunTime READ getGPURunTime) - Q_PROPERTY(double batchRunTime READ getBatchRunTime) - - double _msGPURunTime { 0.0 }; - double _msBatchRunTime { 0.0 }; +// The guts of a job +class JobConcept { public: - using Persistent = PersistentConfig; + using Config = JobConfig; + using QConfigPointer = std::shared_ptr; - GPUJobConfig() = default; - GPUJobConfig(bool enabled) : JobConfig(enabled) {} + JobConcept(QConfigPointer config) : _config(config) {} + virtual ~JobConcept() = default; - // Running Time measurement on GPU and for Batch execution - void setGPUBatchRunTime(double msGpuTime, double msBatchTime) { _msGPURunTime = msGpuTime; _msBatchRunTime = msBatchTime; } - double getGPURunTime() const { return _msGPURunTime; } - double getBatchRunTime() const { return _msBatchRunTime; } -}; + virtual const Varying getInput() const { return Varying(); } + virtual const Varying getOutput() const { return Varying(); } -class GPUTaskConfig : public TaskConfig { - Q_OBJECT - Q_PROPERTY(double gpuRunTime READ getGPURunTime) - Q_PROPERTY(double batchRunTime READ getBatchRunTime) + virtual QConfigPointer& getConfiguration() { return _config; } + virtual void applyConfiguration() = 0; - double _msGPURunTime { 0.0 }; - double _msBatchRunTime { 0.0 }; -public: + virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0; - using Persistent = PersistentConfig; +protected: + void setCPURunTime(double mstime) { std::static_pointer_cast(_config)->setCPURunTime(mstime); } + QConfigPointer _config; - GPUTaskConfig() = default; - GPUTaskConfig(bool enabled) : TaskConfig(enabled) {} - - // Running Time measurement on GPU and for Batch execution - void setGPUBatchRunTime(double msGpuTime, double msBatchTime) { _msGPURunTime = msGpuTime; _msBatchRunTime = msBatchTime; } - double getGPURunTime() const { return _msGPURunTime; } - double getBatchRunTime() const { return _msBatchRunTime; } + friend class Job; }; class Job { public: + using Concept = JobConcept; using Config = JobConfig; using QConfigPointer = std::shared_ptr; using None = JobNoIO; - - // The guts of a job - class Concept { - public: - Concept(QConfigPointer config) : _config(config) {} - virtual ~Concept() = default; - - virtual const Varying getInput() const { return Varying(); } - virtual const Varying getOutput() const { return Varying(); } - - virtual QConfigPointer& getConfiguration() { return _config; } - virtual void applyConfiguration() = 0; - - virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0; - - protected: - void setCPURunTime(double mstime) { std::static_pointer_cast(_config)->setCPURunTime(mstime); } - - QConfigPointer _config; - - friend class Job; - }; using ConceptPointer = std::shared_ptr; template class Model : public Concept { @@ -541,11 +511,20 @@ public: const Varying getOutput() const override { return _output; } template - Model(const Varying& input, A&&... args) : - Concept(std::make_shared()), _data(Data(std::forward(args)...)), _input(input), _output(Output()) { + Model(const Varying& input, QConfigPointer config, A&&... args) : + Concept(config), + _data(Data(std::forward(args)...)), + _input(input), + _output(Output()) { applyConfiguration(); } + template + static std::shared_ptr create(const Varying& input, A&&... args) { + return std::make_shared(input, std::make_shared(), std::forward(args)...); + } + + void applyConfiguration() override { jobConfigure(_data, *std::static_pointer_cast(_config)); } @@ -585,125 +564,180 @@ public: _concept->setCPURunTime((double)(usecTimestampNow() - start) / 1000.0); } - protected: +protected: ConceptPointer _concept; std::string _name = ""; }; // A task is a specialized job to run a collection of other jobs -// It is defined with JobModel = Task::Model -class Task { +// It can be created on any type T by aliasing the type JobModel in the class T +// using JobModel = Task::Model +// The class T is expected to have a "build" method acting as a constructor. +// The build method is where child Jobs can be added internally to the task +// where the input of the task can be setup to feed the child jobs +// and where the output of the task is defined +class Task : public Job { public: using Config = TaskConfig; using QConfigPointer = Job::QConfigPointer; + using None = Job::None; + using Concept = Job::Concept; + using Jobs = std::vector; - template class Model : public Job::Concept { + Task(std::string name, ConceptPointer concept) : Job(name, concept) {} + + class TaskConcept : public Concept { + public: + Varying _input; + Varying _output; + Jobs _jobs; + + const Varying getInput() const override { return _input; } + const Varying getOutput() const override { return _output; } + + TaskConcept(const Varying& input, QConfigPointer config) : Concept(config), _input(input) {} + + // Create a new job in the container's queue; returns the job's output + template const Varying addJob(std::string name, const Varying& input, NA&&... args) { + _jobs.emplace_back(name, (NT::JobModel::create(input, std::forward(args)...))); + + // Conect the child config to this task's config + std::static_pointer_cast(getConfiguration())->connectChildConfig(_jobs.back().getConfiguration(), name); + + return _jobs.back().getOutput(); + } + template const Varying addJob(std::string name, NA&&... args) { + const auto input = Varying(typename NT::JobModel::Input()); + return addJob(name, input, std::forward(args)...); + } + }; + + template class TaskModel : public TaskConcept { public: using Data = T; - using Config = C; - using Input = Job::None; + using Input = I; + using Output = O; Data _data; - const Varying getOutput() const override { return _data._output; } + TaskModel(const Varying& input, QConfigPointer config) : + TaskConcept(input, config), + _data(Data()) {} template - Model(const Varying& input, A&&... args) : - Concept(nullptr), _data(Data(std::forward(args)...)) { + static std::shared_ptr create(const Varying& input, A&&... args) { + auto model = std::make_shared(input, std::make_shared()); + // std::static_pointer_cast(model->_config)->_task = model.get(); + + model->_data.build(*(model), model->_input, model->_output, std::forward(args)...); + // Recreate the Config to use the templated type - _data.template createConfiguration(); - _config = _data.getConfiguration(); - applyConfiguration(); + model->createConfiguration(); + model->applyConfiguration(); + + return model; + } + + template + static std::shared_ptr create(A&&... args) { + const auto input = Varying(Input()); + return create(input, std::forward(args)...); + } + + void createConfiguration() { + // A brand new config + auto config = std::make_shared(); + // Make sure we transfer the former children configs to the new config + config->transferChildrenConfigs(_config); + // swap + _config = config; + // Capture this + std::static_pointer_cast(_config)->_task = this; + } + + QConfigPointer& getConfiguration() override { + if (!_config) { + createConfiguration(); + } + return _config; } void applyConfiguration() override { jobConfigure(_data, *std::static_pointer_cast(_config)); + for (auto& job : _jobs) { + job.applyConfiguration(); + } } void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) override { - auto config = std::static_pointer_cast(_config); + auto config = std::static_pointer_cast(_config); if (config->alwaysEnabled || config->enabled) { - for (auto job : _data._jobs) { + for (auto job : _jobs) { job.run(sceneContext, renderContext); } } } }; - template using ModelO = Model; + template using Model = TaskModel; + template using ModelI = TaskModel; + template using ModelO = TaskModel; + template using ModelIO = TaskModel; - using Jobs = std::vector; - - // Create a new job in the container's queue; returns the job's output + // Create a new job in the Task's queue; returns the job's output template const Varying addJob(std::string name, const Varying& input, A&&... args) { - _jobs.emplace_back(name, std::make_shared(input, std::forward(args)...)); - QConfigPointer config = _jobs.back().getConfiguration(); - config->setParent(getConfiguration().get()); - config->setObjectName(name.c_str()); - - // Connect loaded->refresh - QObject::connect(config.get(), SIGNAL(loaded()), getConfiguration().get(), SLOT(refresh())); - static const char* DIRTY_SIGNAL = "dirty()"; - if (config->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) { - // Connect dirty->refresh if defined - QObject::connect(config.get(), SIGNAL(dirty()), getConfiguration().get(), SLOT(refresh())); - } - - return _jobs.back().getOutput(); + return std::static_pointer_cast( _concept)->addJob(name, input, std::forward(args)...); } template const Varying addJob(std::string name, A&&... args) { const auto input = Varying(typename T::JobModel::Input()); - return addJob(name, input, std::forward(args)...); - } - - template void setOutput(O&& output) { - _output = Varying(output); - } - - template void createConfiguration() { - auto config = std::make_shared(); - if (_config) { - // Transfer children to the new configuration - auto children = _config->children(); - for (auto& child : children) { - child->setParent(config.get()); - QObject::connect(child, SIGNAL(loaded()), config.get(), SLOT(refresh())); - static const char* DIRTY_SIGNAL = "dirty()"; - if (child->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) { - // Connect dirty->refresh if defined - QObject::connect(child, SIGNAL(dirty()), config.get(), SLOT(refresh())); - } - } - } - _config = config; - std::static_pointer_cast(_config)->_task = this; + return std::static_pointer_cast( _concept)->addJob(name, input, std::forward(args)...); } std::shared_ptr getConfiguration() { - if (!_config) { - createConfiguration(); - } - return std::static_pointer_cast(_config); - } - - void configure(const QObject& configuration) { - for (auto& job : _jobs) { - job.applyConfiguration(); - - } - } - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - for (auto job : _jobs) { - job.run(sceneContext, renderContext); - } + return std::static_pointer_cast(_concept->getConfiguration()); } protected: - template friend class Model; +}; - QConfigPointer _config; - Jobs _jobs; - Varying _output; +// Versions of the COnfig integrating a gpu & batch timer +class GPUJobConfig : public JobConfig { + Q_OBJECT + Q_PROPERTY(double gpuRunTime READ getGPURunTime) + Q_PROPERTY(double batchRunTime READ getBatchRunTime) + + double _msGPURunTime { 0.0 }; + double _msBatchRunTime { 0.0 }; +public: + using Persistent = PersistentConfig; + + GPUJobConfig() = default; + GPUJobConfig(bool enabled) : JobConfig(enabled) {} + + // Running Time measurement on GPU and for Batch execution + void setGPUBatchRunTime(double msGpuTime, double msBatchTime) { _msGPURunTime = msGpuTime; _msBatchRunTime = msBatchTime; } + double getGPURunTime() const { return _msGPURunTime; } + double getBatchRunTime() const { return _msBatchRunTime; } +}; + +class GPUTaskConfig : public TaskConfig { + Q_OBJECT + Q_PROPERTY(double gpuRunTime READ getGPURunTime) + Q_PROPERTY(double batchRunTime READ getBatchRunTime) + + double _msGPURunTime { 0.0 }; + double _msBatchRunTime { 0.0 }; +public: + + using Persistent = PersistentConfig; + + + GPUTaskConfig() = default; + GPUTaskConfig(bool enabled) : TaskConfig(enabled) {} + + // Running Time measurement on GPU and for Batch execution + void setGPUBatchRunTime(double msGpuTime, double msBatchTime) { _msGPURunTime = msGpuTime; _msBatchRunTime = msBatchTime; } + double getGPURunTime() const { return _msGPURunTime; } + double getBatchRunTime() const { return _msBatchRunTime; } }; } diff --git a/libraries/render/src/render/drawItemBounds.slv b/libraries/render/src/render/drawItemBounds.slv index cf1eab7163..af1e31fbf4 100644 --- a/libraries/render/src/render/drawItemBounds.slv +++ b/libraries/render/src/render/drawItemBounds.slv @@ -20,7 +20,7 @@ uniform vec3 inBoundPos; uniform vec3 inBoundDim; -uniform ivec4 inCellLocation; +uniform vec4 inColor; out vec4 varColor; out vec2 varTexcoord; @@ -59,9 +59,7 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, pos, gl_Position)$> - bool subcell = bool((inCellLocation.z)); - float cellDepth = float(inCellLocation.w); - varColor = vec4(colorWheel(fract(cellDepth / 5.0)), 1.0 - float(subcell)); + varColor = vec4(colorWheel(inColor.x), 1.0); varTexcoord = vec2(cubeVec.w, length(inBoundDim)); } \ No newline at end of file diff --git a/scripts/developer/utilities/render/debugDeferredLighting.js b/scripts/developer/utilities/render/debugDeferredLighting.js index 625f71f8a4..bffa3a2e15 100644 --- a/scripts/developer/utilities/render/debugDeferredLighting.js +++ b/scripts/developer/utilities/render/debugDeferredLighting.js @@ -13,7 +13,7 @@ var qml = Script.resolvePath('deferredLighting.qml'); var window = new OverlayWindow({ title: 'Lighting', source: qml, - width: 400, height:280, + width: 400, height:350, }); window.setPosition(Window.innerWidth - 420, 50); window.closed.connect(function() { Script.stop(); }); diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index 778e0e1905..229a2d1c3b 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -189,6 +189,11 @@ Column { checked: Render.getConfig("DrawOverlayTransparentBounds")["enabled"] onCheckedChanged: { Render.getConfig("DrawOverlayTransparentBounds")["enabled"] = checked } } + CheckBox { + text: "Zones" + checked: Render.getConfig("DrawZones")["enabled"] + onCheckedChanged: { Render.getConfig("ZoneRenderer")["enabled"] = checked; Render.getConfig("DrawZones")["enabled"] = checked; } + } } } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index acc8bd3548..5806dd803a 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -27,8 +27,8 @@ Script.include("/~/system/libraries/controllers.js"); // add lines where the hand ray picking is happening // -var WANT_DEBUG = true; -var WANT_DEBUG_STATE = true; +var WANT_DEBUG = false; +var WANT_DEBUG_STATE = false; var WANT_DEBUG_SEARCH_NAME = null; var FORCE_IGNORE_IK = false; @@ -1051,8 +1051,6 @@ function MyController(hand) { this.homeButtonTouched = false; this.editTriggered = false; - this.controllerJointIndex = getControllerJointIndex(this.hand); - // Until there is some reliable way to keep track of a "stack" of parentIDs, we'll have problems // when more than one avatar does parenting grabs on things. This script tries to work // around this with two associative arrays: previousParentID and previousParentJointIndex. If @@ -1736,6 +1734,7 @@ function MyController(hand) { this.off = function(deltaTime, timestamp) { + this.controllerJointIndex = getControllerJointIndex(this.hand); this.checkForUnexpectedChildren(); if (this.editTriggered) { diff --git a/scripts/system/fingerPaint.js b/scripts/system/fingerPaint.js index 27206ef9fa..88245503e8 100644 --- a/scripts/system/fingerPaint.js +++ b/scripts/system/fingerPaint.js @@ -23,6 +23,7 @@ HIFI_POINT_INDEX_MESSAGE_CHANNEL = "Hifi-Point-Index", HIFI_GRAB_DISABLE_MESSAGE_CHANNEL = "Hifi-Grab-Disable", HIFI_POINTER_DISABLE_MESSAGE_CHANNEL = "Hifi-Pointer-Disable"; + HOW_TO_EXIT_MESSAGE = "Press B on your controller to exit FingerPainting mode"; function paintBrush(name) { // Paints in 3D. @@ -319,6 +320,15 @@ } } + function howToExitTutorial() { + HMD.requestShowHandControllers(); + setControllerPartLayer('button_b', 'highlight'); + messageWindow = Window.alert(HOW_TO_EXIT_MESSAGE); + setControllerPartLayer('button_b', 'blank'); + HMD.requestHideHandControllers(); + Settings.setValue("FingerPaintTutorialComplete", true); + } + function enableProcessing() { // Connect controller API to handController objects. leftHand = handController("left"); @@ -328,7 +338,12 @@ controllerMapping.from(Controller.Standard.LeftGrip).to(leftHand.onGripPress); controllerMapping.from(Controller.Standard.RT).to(rightHand.onTriggerPress); controllerMapping.from(Controller.Standard.RightGrip).to(rightHand.onGripPress); + controllerMapping.from(Controller.Standard.B).to(onButtonClicked); Controller.enableMapping(CONTROLLER_MAPPING_NAME); + + if (!Settings.getValue("FingerPaintTutorialComplete")) { + howToExitTutorial(); + } // Connect handController outputs to paintBrush objects. leftBrush = paintBrush("left"); @@ -433,6 +448,17 @@ button.clicked.disconnect(onButtonClicked); tablet.removeButton(button); } + + /** + * A controller is made up of parts, and each part can have multiple "layers," + * which are really just different texures. For example, the "trigger" part + * has "normal" and "highlight" layers. + */ + function setControllerPartLayer(part, layer) { + data = {}; + data[part] = layer; + Messages.sendLocalMessage('Controller-Set-Part-Layer', JSON.stringify(data)); + } setUp(); Script.scriptEnding.connect(tearDown); diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 3f4f53c104..a1120ee3e1 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -543,9 +543,9 @@ public: assert(items.canCast()); static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { - _renderEngine->addJob("RenderForwardTask", items.get()); + _renderEngine->addJob("RenderForwardTask", items); } else { - _renderEngine->addJob("RenderDeferredTask", items.get()); + _renderEngine->addJob("RenderDeferredTask", items); } _renderEngine->load(); _renderEngine->registerScene(_main3DScene); diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 26fa51e896..97528b9b3b 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -118,7 +118,7 @@ function findEntitiesWithTag(tag) { } /** - * A controller in made up of parts, and each part can have multiple "layers," + * A controller is made up of parts, and each part can have multiple "layers," * which are really just different texures. For example, the "trigger" part * has "normal" and "highlight" layers. */