Merge branch 'master' of github.com:highfidelity/hifi into bullet-constraints-1

This commit is contained in:
Seth Alves 2017-04-14 16:26:30 -07:00
commit 371118a6c0
34 changed files with 921 additions and 337 deletions

View file

@ -1886,9 +1886,9 @@ void Application::initializeGL() {
assert(items.canCast<RenderFetchCullSortTask::Output>());
static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";
if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) {
_renderEngine->addJob<RenderForwardTask>("Forward", items.get<RenderFetchCullSortTask::Output>());
_renderEngine->addJob<RenderForwardTask>("Forward", items);
} else {
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", items.get<RenderFetchCullSortTask::Output>());
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", items);
}
_renderEngine->load();
_renderEngine->registerScene(_main3DScene);

View file

@ -43,6 +43,8 @@
#include "AddressManager.h"
#include <Rig.h>
#include "ZoneRenderer.h"
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
AbstractScriptingServicesInterface* scriptingServices) :
_wantScripts(wantScripts),
@ -266,6 +268,9 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector<EntityIt
}
}
_layeredZones.apply();
applyLayeredZones();
didUpdate = true;
});
@ -343,6 +348,30 @@ void EntityTreeRenderer::forceRecheckEntities() {
_avatarPosition = _viewState->getAvatarPosition() + 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<RenderableZoneEntityItem>(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<ZoneEntityItem>& zone) {
auto textureCache = DependencyManager::get<TextureCache>();
auto scene = DependencyManager::get<SceneScriptingInterface>();

View file

@ -150,6 +150,7 @@ private:
bool applyZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone);
bool layerZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone);
bool applySkyboxAndHasAmbient();
bool applyLayeredZones();
void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false, const bool unloadFirst = false);

View file

@ -41,6 +41,8 @@ public:
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) override;
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> 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(); }

View file

@ -19,6 +19,7 @@
#include <gpu/Context.h>
#include <render/CullTask.h>
#include <render/FilterTask.h>
#include <render/SortTask.h>
#include <render/DrawTask.h>
#include <render/DrawStatus.h>
@ -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<Input>();
// Prepare the ShapePipelines
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
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<FilterItemLayer>("Filter2DWebOverlays", overlayOpaques, LAYER_NO_AA);
const auto nonAAOverlays = task.addJob<FilterLayeredItems>("Filter2DWebOverlays", overlayOpaques, LAYER_NO_AA);
// Prepare deferred, generate the shared Deferred Frame Transform
const auto deferredFrameTransform = addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform");
const auto lightingModel = addJob<MakeLightingModel>("LightingModel");
const auto deferredFrameTransform = task.addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform");
const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel");
// GPU jobs: Start preparing the primary, deferred and lighting buffer
const auto primaryFramebuffer = addJob<PreparePrimaryFramebuffer>("PreparePrimaryBuffer");
const auto primaryFramebuffer = task.addJob<PreparePrimaryFramebuffer>("PreparePrimaryBuffer");
const auto opaqueRangeTimer = addJob<BeginGPURangeTimer>("BeginOpaqueRangeTimer", "DrawOpaques");
const auto opaqueRangeTimer = task.addJob<BeginGPURangeTimer>("BeginOpaqueRangeTimer", "DrawOpaques");
const auto prepareDeferredInputs = PrepareDeferred::Inputs(primaryFramebuffer, lightingModel).hasVarying();
const auto prepareDeferredOutputs = addJob<PrepareDeferred>("PrepareDeferred", prepareDeferredInputs);
const auto prepareDeferredOutputs = task.addJob<PrepareDeferred>("PrepareDeferred", prepareDeferredInputs);
const auto deferredFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(0);
const auto lightingFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(1);
// Render opaque objects in DeferredBuffer
const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).hasVarying();
addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaqueInputs, shapePlumber);
task.addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaqueInputs, shapePlumber);
// Once opaque is all rendered create stencil background
addJob<DrawStencilDeferred>("DrawOpaqueStencil", deferredFramebuffer);
task.addJob<DrawStencilDeferred>("DrawOpaqueStencil", deferredFramebuffer);
addJob<EndGPURangeTimer>("OpaqueRangeTimer", opaqueRangeTimer);
task.addJob<EndGPURangeTimer>("OpaqueRangeTimer", opaqueRangeTimer);
// Opaque all rendered
// Linear Depth Pass
const auto linearDepthPassInputs = LinearDepthPass::Inputs(deferredFrameTransform, deferredFramebuffer).hasVarying();
const auto linearDepthPassOutputs = addJob<LinearDepthPass>("LinearDepth", linearDepthPassInputs);
const auto linearDepthPassOutputs = task.addJob<LinearDepthPass>("LinearDepth", linearDepthPassInputs);
const auto linearDepthTarget = linearDepthPassOutputs.getN<LinearDepthPass::Outputs>(0);
// Curvature pass
const auto surfaceGeometryPassInputs = SurfaceGeometryPass::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget).hasVarying();
const auto surfaceGeometryPassOutputs = addJob<SurfaceGeometryPass>("SurfaceGeometry", surfaceGeometryPassInputs);
const auto surfaceGeometryPassOutputs = task.addJob<SurfaceGeometryPass>("SurfaceGeometry", surfaceGeometryPassInputs);
const auto surfaceGeometryFramebuffer = surfaceGeometryPassOutputs.getN<SurfaceGeometryPass::Outputs>(0);
const auto curvatureFramebuffer = surfaceGeometryPassOutputs.getN<SurfaceGeometryPass::Outputs>(1);
const auto midCurvatureNormalFramebuffer = surfaceGeometryPassOutputs.getN<SurfaceGeometryPass::Outputs>(2);
const auto lowCurvatureNormalFramebuffer = surfaceGeometryPassOutputs.getN<SurfaceGeometryPass::Outputs>(3);
// Simply update the scattering resource
const auto scatteringResource = addJob<SubsurfaceScattering>("Scattering");
const auto scatteringResource = task.addJob<SubsurfaceScattering>("Scattering");
// AO job
const auto ambientOcclusionInputs = AmbientOcclusionEffect::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget).hasVarying();
const auto ambientOcclusionOutputs = addJob<AmbientOcclusionEffect>("AmbientOcclusion", ambientOcclusionInputs);
const auto ambientOcclusionOutputs = task.addJob<AmbientOcclusionEffect>("AmbientOcclusion", ambientOcclusionInputs);
const auto ambientOcclusionFramebuffer = ambientOcclusionOutputs.getN<AmbientOcclusionEffect::Outputs>(0);
const auto ambientOcclusionUniforms = ambientOcclusionOutputs.getN<AmbientOcclusionEffect::Outputs>(1);
// Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now.
addJob<DrawLight>("DrawLight", lights);
task.addJob<DrawLight>("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<LightClusteringPass>("LightClustering", lightClusteringPassInputs);
const auto lightClusters = task.addJob<LightClusteringPass>("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>("RenderDeferred", deferredLightingInputs);
task.addJob<RenderDeferred>("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>("DrawBackgroundDeferred", backgroundInputs);
task.addJob<DrawBackgroundDeferred>("DrawBackgroundDeferred", backgroundInputs);
// Render transparent objects forward in LightingBuffer
const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).hasVarying();
addJob<DrawDeferred>("DrawTransparentDeferred", transparentsInputs, shapePlumber);
task.addJob<DrawDeferred>("DrawTransparentDeferred", transparentsInputs, shapePlumber);
// LIght Cluster Grid Debuging job
{
const auto debugLightClustersInputs = DebugLightClusters::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, linearDepthTarget, lightClusters).hasVarying();
addJob<DebugLightClusters>("DebugLightClusters", debugLightClustersInputs);
task.addJob<DebugLightClusters>("DebugLightClusters", debugLightClustersInputs);
}
const auto toneAndPostRangeTimer = addJob<BeginGPURangeTimer>("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing");
const auto toneAndPostRangeTimer = task.addJob<BeginGPURangeTimer>("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing");
// Lighting Buffer ready for tone mapping
const auto toneMappingInputs = render::Varying(ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer));
addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
{ // DEbug the bounds of the rendered items, still look at the zbuffer
addJob<DrawBounds>("DrawMetaBounds", metas);
addJob<DrawBounds>("DrawOpaqueBounds", opaques);
addJob<DrawBounds>("DrawTransparentBounds", transparents);
task.addJob<DrawBounds>("DrawMetaBounds", metas);
task.addJob<DrawBounds>("DrawOpaqueBounds", opaques);
task.addJob<DrawBounds>("DrawTransparentBounds", transparents);
task.addJob<ZoneRendererTask>("ZoneRenderer", opaques);
}
// Overlays
const auto overlayOpaquesInputs = DrawOverlay3D::Inputs(overlayOpaques, lightingModel).hasVarying();
const auto overlayTransparentsInputs = DrawOverlay3D::Inputs(overlayTransparents, lightingModel).hasVarying();
addJob<DrawOverlay3D>("DrawOverlay3DOpaque", overlayOpaquesInputs, true);
addJob<DrawOverlay3D>("DrawOverlay3DTransparent", overlayTransparentsInputs, false);
task.addJob<DrawOverlay3D>("DrawOverlay3DOpaque", overlayOpaquesInputs, true);
task.addJob<DrawOverlay3D>("DrawOverlay3DTransparent", overlayTransparentsInputs, false);
{ // DEbug the bounds of the rendered OVERLAY items, still look at the zbuffer
addJob<DrawBounds>("DrawOverlayOpaqueBounds", overlayOpaques);
addJob<DrawBounds>("DrawOverlayTransparentBounds", overlayTransparents);
task.addJob<DrawBounds>("DrawOverlayOpaqueBounds", overlayOpaques);
task.addJob<DrawBounds>("DrawOverlayTransparentBounds", overlayTransparents);
}
// Debugging stages
{
// Debugging Deferred buffer job
const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer));
addJob<DebugDeferredBuffer>("DebugDeferredBuffer", debugFramebuffers);
task.addJob<DebugDeferredBuffer>("DebugDeferredBuffer", debugFramebuffers);
const auto debugSubsurfaceScatteringInputs = DebugSubsurfaceScattering::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel,
surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource).hasVarying();
addJob<DebugSubsurfaceScattering>("DebugScattering", debugSubsurfaceScatteringInputs);
task.addJob<DebugSubsurfaceScattering>("DebugScattering", debugSubsurfaceScatteringInputs);
const auto debugAmbientOcclusionInputs = DebugAmbientOcclusion::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget, ambientOcclusionUniforms).hasVarying();
addJob<DebugAmbientOcclusion>("DebugAmbientOcclusion", debugAmbientOcclusionInputs);
task.addJob<DebugAmbientOcclusion>("DebugAmbientOcclusion", debugAmbientOcclusionInputs);
// Scene Octree Debugging job
{
addJob<DrawSceneOctree>("DrawSceneOctree", spatialSelection);
addJob<DrawItemSelection>("DrawItemSelection", spatialSelection);
task.addJob<DrawSceneOctree>("DrawSceneOctree", spatialSelection);
task.addJob<DrawItemSelection>("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<TextureCache>()->getImageTexture(iconMapPath, NetworkTexture::STRICT_TEXTURE);
addJob<DrawStatus>("DrawStatus", opaques, DrawStatus(statusIconMap));
task.addJob<DrawStatus>("DrawStatus", opaques, DrawStatus(statusIconMap));
}
}
// AA job to be revisited
addJob<Antialiasing>("Antialiasing", primaryFramebuffer);
task.addJob<Antialiasing>("Antialiasing", primaryFramebuffer);
// Draw 2DWeb non AA
const auto nonAAOverlaysInputs = DrawOverlay3D::Inputs(nonAAOverlays, lightingModel).hasVarying();
addJob<DrawOverlay3D>("Draw2DWebSurfaces", nonAAOverlaysInputs, false);
task.addJob<DrawOverlay3D>("Draw2DWebSurfaces", nonAAOverlaysInputs, false);
addJob<EndGPURangeTimer>("ToneAndPostRangeTimer", toneAndPostRangeTimer);
task.addJob<EndGPURangeTimer>("ToneAndPostRangeTimer", toneAndPostRangeTimer);
// Blit!
addJob<Blit>("Blit", primaryFramebuffer);
task.addJob<Blit>("Blit", primaryFramebuffer);
}
void BeginGPURangeTimer::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer) {

View file

@ -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<RenderDeferredTask>;
using Input = RenderFetchCullSortTask::Output;
using JobModel = render::Task::ModelI<RenderDeferredTask, Input>;
RenderDeferredTask(RenderFetchCullSortTask::Output items);
RenderDeferredTask() {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
};
#endif // hifi_RenderDeferredTask_h

View file

@ -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<Input>();
// Prepare the ShapePipelines
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
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>("PrepareFramebuffer");
const auto framebuffer = task.addJob<PrepareFramebuffer>("PrepareFramebuffer");
addJob<Draw>("DrawOpaques", opaques, shapePlumber);
addJob<Stencil>("Stencil");
addJob<DrawBackground>("DrawBackground", background);
task.addJob<Draw>("DrawOpaques", opaques, shapePlumber);
task.addJob<Stencil>("Stencil");
task.addJob<DrawBackground>("DrawBackground", background);
// Bounds do not draw on stencil buffer, so they must come last
addJob<DrawBounds>("DrawBounds", opaques);
task.addJob<DrawBounds>("DrawBounds", opaques);
// Blit!
addJob<Blit>("Blit", framebuffer);
task.addJob<Blit>("Blit", framebuffer);
}
void PrepareFramebuffer::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext,

View file

@ -16,11 +16,14 @@
#include <render/RenderFetchCullSortTask.h>
#include "LightingModel.h"
class RenderForwardTask : public render::Task {
class RenderForwardTask {
public:
using JobModel = Model<RenderForwardTask>;
using Input = RenderFetchCullSortTask::Output;
using JobModel = render::Task::ModelI<RenderForwardTask, Input>;
RenderForwardTask(RenderFetchCullSortTask::Output items);
RenderForwardTask() {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
};
class PrepareFramebuffer {

View file

@ -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<RenderShadowSetup>("Setup");
const auto cachedMode = task.addJob<RenderShadowSetup>("Setup");
// CPU jobs:
// Fetch and cull the items from the scene
auto shadowFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered();
const auto shadowSelection = addJob<FetchSpatialTree>("FetchShadowSelection", shadowFilter);
const auto culledShadowSelection = addJob<CullSpatialSelection>("CullShadowSelection", shadowSelection, cullFunctor, RenderDetails::SHADOW, shadowFilter);
const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowSelection", shadowFilter);
const auto culledShadowSelection = task.addJob<CullSpatialSelection>("CullShadowSelection", shadowSelection, cullFunctor, RenderDetails::SHADOW, shadowFilter);
// Sort
const auto sortedPipelines = addJob<PipelineSortShapes>("PipelineSortShadowSort", culledShadowSelection);
const auto sortedShapes = addJob<DepthSortShapes>("DepthSortShadowMap", sortedPipelines);
const auto sortedPipelines = task.addJob<PipelineSortShapes>("PipelineSortShadowSort", culledShadowSelection);
const auto sortedShapes = task.addJob<DepthSortShapes>("DepthSortShadowMap", sortedPipelines);
// GPU jobs: Render to shadow map
addJob<RenderShadowMap>("RenderShadowMap", sortedShapes, shapePlumber);
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapes, shapePlumber);
addJob<RenderShadowTeardown>("Teardown", cachedMode);
task.addJob<RenderShadowTeardown>("Teardown", cachedMode);
}
void RenderShadowTask::configure(const Config& configuration) {
DependencyManager::get<DeferredLightingEffect>()->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) {

View file

@ -41,12 +41,13 @@ signals:
void dirty();
};
class RenderShadowTask : public render::Task {
class RenderShadowTask {
public:
using Config = RenderShadowTaskConfig;
using JobModel = Model<RenderShadowTask, Config>;
using JobModel = render::Task::Model<RenderShadowTask, Config>;
RenderShadowTask(render::CullFunctor shouldRender);
RenderShadowTask() {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor shouldRender);
void configure(const Config& configuration);
};

View file

@ -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 <render/FilterTask.h>
#include <render/DrawTask.h>
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<render::SelectSortItems>("FilterZones", input, ZONES_SELECTION.c_str());
// just draw them...
task.addJob<DrawBounds>("DrawZones", zoneItems);
}

View file

@ -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, Inputs, Config>;
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

View file

@ -16,8 +16,6 @@
#include <OctreeUtils.h>
#include <PerfStat.h>
#include <ViewFrustum.h>
#include <gpu/Context.h>
using namespace render;
@ -306,19 +304,3 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re
std::static_pointer_cast<Config>(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);
}
}
}

View file

@ -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, ItemBounds, ItemBounds, Config>;
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 <int NUM_FILTERS>
class MultiFilterItem {
public:
using ItemFilterArray = std::array<ItemFilter, NUM_FILTERS>;
using ItemBoundsArray = VaryingArray<ItemBounds, NUM_FILTERS>;
using Config = MultiFilterItemConfig;
using JobModel = Job::ModelIO<MultiFilterItem, ItemBounds, ItemBoundsArray, Config>;
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<ItemBounds>().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<ItemBounds>().emplace_back(itemBound);
}
}
}
}
};
class FilterItemLayer {
public:
using JobModel = Job::ModelIO<FilterItemLayer, ItemBounds, ItemBounds>;
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;

View file

@ -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<gpu::State>();
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;
}
});
}

View file

@ -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 };
};
}

View file

@ -22,10 +22,22 @@
using namespace render;
Engine::Engine() :
class EngineTask {
public:
using JobModel = Task::Model<EngineTask>;
EngineTask() {}
void build(JobModel& task, const Varying& in, Varying& out) {
task.addJob<EngineStats>("Stats");
}
};
Engine::Engine() : Task("Engine", EngineTask::JobModel::create()),
_sceneContext(std::make_shared<SceneContext>()),
_renderContext(std::make_shared<RenderContext>()) {
addJob<EngineStats>("Stats");
_renderContext(std::make_shared<RenderContext>())
{
}
void Engine::load() {

View file

@ -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 <algorithm>
#include <assert.h>
#include <OctreeUtils.h>
#include <PerfStat.h>
#include <ViewFrustum.h>
#include <gpu/Context.h>
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<Config>(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<Pair> 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);
}
}

View file

@ -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 <int NUM_FILTERS>
class MultiFilterItems {
public:
using ItemFilterArray = std::array<ItemFilter, NUM_FILTERS>;
using ItemBoundsArray = VaryingArray<ItemBounds, NUM_FILTERS>;
using Config = MultiFilterItemsConfig;
using JobModel = Job::ModelIO<MultiFilterItems, ItemBounds, ItemBoundsArray, Config>;
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<ItemBounds>().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<ItemBounds>().emplace_back(itemBound);
}
}
}
}
};
// Filter the items belonging to the job's keep layer
class FilterLayeredItems {
public:
using JobModel = Job::ModelIO<FilterLayeredItems, ItemBounds, ItemBounds>;
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, ItemBounds, ItemBounds, Config>;
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<SelectItems, ItemBounds, ItemBounds>;
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<SelectSortItems, ItemBounds, ItemBounds>;
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, ItemBounds, ItemIDs>;
MetaToSubItems() {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemIDs& outItems);
};
}
#endif // hifi_render_FilterTask_h;

View file

@ -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<FetchSpatialTree>("FetchSceneSelection", spatialFilter);
const auto culledSpatialSelection = addJob<CullSpatialSelection>("CullSceneSelection", spatialSelection, cullFunctor, RenderDetails::ITEM, spatialFilter);
const auto spatialSelection = task.addJob<FetchSpatialTree>("FetchSceneSelection", spatialFilter);
const auto culledSpatialSelection = task.addJob<CullSpatialSelection>("CullSceneSelection", spatialSelection, cullFunctor, RenderDetails::ITEM, spatialFilter);
// Overlays are not culled
const auto nonspatialSelection = addJob<FetchNonspatialItems>("FetchOverlaySelection");
const auto nonspatialSelection = task.addJob<FetchNonspatialItems>("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<NUM_SPATIAL_FILTERS>::ItemFilterArray spatialFilters = { {
MultiFilterItems<NUM_SPATIAL_FILTERS>::ItemFilterArray spatialFilters = { {
ItemFilter::Builder::opaqueShape(),
ItemFilter::Builder::transparentShape(),
ItemFilter::Builder::light(),
ItemFilter::Builder::meta()
} };
MultiFilterItem<NUM_NON_SPATIAL_FILTERS>::ItemFilterArray nonspatialFilters = { {
MultiFilterItems<NUM_NON_SPATIAL_FILTERS>::ItemFilterArray nonspatialFilters = { {
ItemFilter::Builder::opaqueShape(),
ItemFilter::Builder::transparentShape(),
ItemFilter::Builder::background()
} };
const auto filteredSpatialBuckets =
addJob<MultiFilterItem<NUM_SPATIAL_FILTERS>>("FilterSceneSelection", culledSpatialSelection, spatialFilters)
.get<MultiFilterItem<NUM_SPATIAL_FILTERS>::ItemBoundsArray>();
task.addJob<MultiFilterItems<NUM_SPATIAL_FILTERS>>("FilterSceneSelection", culledSpatialSelection, spatialFilters)
.get<MultiFilterItems<NUM_SPATIAL_FILTERS>::ItemBoundsArray>();
const auto filteredNonspatialBuckets =
addJob<MultiFilterItem<NUM_NON_SPATIAL_FILTERS>>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters)
.get<MultiFilterItem<NUM_NON_SPATIAL_FILTERS>::ItemBoundsArray>();
task.addJob<MultiFilterItems<NUM_NON_SPATIAL_FILTERS>>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters)
.get<MultiFilterItems<NUM_NON_SPATIAL_FILTERS>::ItemBoundsArray>();
// Extract opaques / transparents / lights / overlays
const auto opaques = addJob<DepthSortItems>("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]);
const auto transparents = addJob<DepthSortItems>("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false));
const auto opaques = task.addJob<DepthSortItems>("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]);
const auto transparents = task.addJob<DepthSortItems>("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false));
const auto lights = filteredSpatialBuckets[LIGHT_BUCKET];
const auto metas = filteredSpatialBuckets[META_BUCKET];
const auto overlayOpaques = addJob<DepthSortItems>("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]);
const auto overlayTransparents = addJob<DepthSortItems>("DepthSortOverlayTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false));
const auto overlayOpaques = task.addJob<DepthSortItems>("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]);
const auto overlayTransparents = task.addJob<DepthSortItems>("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 }});
}

View file

@ -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<render::Varying, Buckets::NUM_BUCKETS>;
using JobModel = ModelO<RenderFetchCullSortTask>;
using JobModel = render::Task::ModelO<RenderFetchCullSortTask, Output>;
RenderFetchCullSortTask(render::CullFunctor cullFunctor);
RenderFetchCullSortTask() {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor);
};
#endif // hifi_RenderFetchCullSortTask_h

View file

@ -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<std::mutex> 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<std::mutex> 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;
}
}
}

View file

@ -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<Transaction> 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;
};

View file

@ -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;
}

View file

@ -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<Selection>;
using SelectionMap = std::map<const Selection::Name, Selection>;
}
#endif

View file

@ -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();
}

View file

@ -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<QObject>;
using Persistent = PersistentConfig<TaskConfig>;
TaskConfig() = default ;
@ -428,12 +431,15 @@ public:
return findChild<typename T::Config*>(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 <class T, class C> void jobConfigure(T& data, const C& configuration) {
@ -458,73 +464,37 @@ template <class T, class I, class O> 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<GPUJobConfig>;
using Config = JobConfig;
using QConfigPointer = std::shared_ptr<QObject>;
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<GPUTaskConfig>;
protected:
void setCPURunTime(double mstime) { std::static_pointer_cast<Config>(_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<QObject>;
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>(_config)->setCPURunTime(mstime); }
QConfigPointer _config;
friend class Job;
};
using ConceptPointer = std::shared_ptr<Concept>;
template <class T, class C = Config, class I = None, class O = None> class Model : public Concept {
@ -541,11 +511,20 @@ public:
const Varying getOutput() const override { return _output; }
template <class... A>
Model(const Varying& input, A&&... args) :
Concept(std::make_shared<C>()), _data(Data(std::forward<A>(args)...)), _input(input), _output(Output()) {
Model(const Varying& input, QConfigPointer config, A&&... args) :
Concept(config),
_data(Data(std::forward<A>(args)...)),
_input(input),
_output(Output()) {
applyConfiguration();
}
template <class... A>
static std::shared_ptr<Model> create(const Varying& input, A&&... args) {
return std::make_shared<Model>(input, std::make_shared<C>(), std::forward<A>(args)...);
}
void applyConfiguration() override {
jobConfigure(_data, *std::static_pointer_cast<C>(_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<T>
class Task {
// It can be created on any type T by aliasing the type JobModel in the class T
// using JobModel = Task::Model<T>
// 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<Job>;
template <class T, class C = Config> 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 <class NT, class... NA> const Varying addJob(std::string name, const Varying& input, NA&&... args) {
_jobs.emplace_back(name, (NT::JobModel::create(input, std::forward<NA>(args)...)));
// Conect the child config to this task's config
std::static_pointer_cast<TaskConfig>(getConfiguration())->connectChildConfig(_jobs.back().getConfiguration(), name);
return _jobs.back().getOutput();
}
template <class NT, class... NA> const Varying addJob(std::string name, NA&&... args) {
const auto input = Varying(typename NT::JobModel::Input());
return addJob<NT>(name, input, std::forward<NA>(args)...);
}
};
template <class T, class C = Config, class I = None, class O = None> 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 <class... A>
Model(const Varying& input, A&&... args) :
Concept(nullptr), _data(Data(std::forward<A>(args)...)) {
static std::shared_ptr<TaskModel> create(const Varying& input, A&&... args) {
auto model = std::make_shared<TaskModel>(input, std::make_shared<C>());
// std::static_pointer_cast<C>(model->_config)->_task = model.get();
model->_data.build(*(model), model->_input, model->_output, std::forward<A>(args)...);
// Recreate the Config to use the templated type
_data.template createConfiguration<C>();
_config = _data.getConfiguration();
applyConfiguration();
model->createConfiguration();
model->applyConfiguration();
return model;
}
template <class... A>
static std::shared_ptr<TaskModel> create(A&&... args) {
const auto input = Varying(Input());
return create(input, std::forward<A>(args)...);
}
void createConfiguration() {
// A brand new config
auto config = std::make_shared<C>();
// Make sure we transfer the former children configs to the new config
config->transferChildrenConfigs(_config);
// swap
_config = config;
// Capture this
std::static_pointer_cast<C>(_config)->_task = this;
}
QConfigPointer& getConfiguration() override {
if (!_config) {
createConfiguration();
}
return _config;
}
void applyConfiguration() override {
jobConfigure(_data, *std::static_pointer_cast<C>(_config));
for (auto& job : _jobs) {
job.applyConfiguration();
}
}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) override {
auto config = std::static_pointer_cast<Config>(_config);
auto config = std::static_pointer_cast<C>(_config);
if (config->alwaysEnabled || config->enabled) {
for (auto job : _data._jobs) {
for (auto job : _jobs) {
job.run(sceneContext, renderContext);
}
}
}
};
template <class T, class C = Config> using ModelO = Model<T, C>;
template <class T, class C = Config> using Model = TaskModel<T, C, None, None>;
template <class T, class I, class C = Config> using ModelI = TaskModel<T, C, I, None>;
template <class T, class O, class C = Config> using ModelO = TaskModel<T, C, None, O>;
template <class T, class I, class O, class C = Config> using ModelIO = TaskModel<T, C, I, O>;
using Jobs = std::vector<Job>;
// 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 <class T, class... A> const Varying addJob(std::string name, const Varying& input, A&&... args) {
_jobs.emplace_back(name, std::make_shared<typename T::JobModel>(input, std::forward<A>(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<TaskConcept>( _concept)->addJob<T>(name, input, std::forward<A>(args)...);
}
template <class T, class... A> const Varying addJob(std::string name, A&&... args) {
const auto input = Varying(typename T::JobModel::Input());
return addJob<T>(name, input, std::forward<A>(args)...);
}
template <class O> void setOutput(O&& output) {
_output = Varying(output);
}
template <class C> void createConfiguration() {
auto config = std::make_shared<C>();
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>(_config)->_task = this;
return std::static_pointer_cast<TaskConcept>( _concept)->addJob<T>(name, input, std::forward<A>(args)...);
}
std::shared_ptr<Config> getConfiguration() {
if (!_config) {
createConfiguration<Config>();
}
return std::static_pointer_cast<Config>(_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<Config>(_concept->getConfiguration());
}
protected:
template <class T, class C> 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>;
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>;
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; }
};
}

View file

@ -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));
}

View file

@ -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(); });

View file

@ -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; }
}
}
}

View file

@ -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) {

View file

@ -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);

View file

@ -543,9 +543,9 @@ public:
assert(items.canCast<RenderFetchCullSortTask::Output>());
static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";
if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) {
_renderEngine->addJob<RenderForwardTask>("RenderForwardTask", items.get<RenderFetchCullSortTask::Output>());
_renderEngine->addJob<RenderForwardTask>("RenderForwardTask", items);
} else {
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", items.get<RenderFetchCullSortTask::Output>());
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", items);
}
_renderEngine->load();
_renderEngine->registerScene(_main3DScene);

View file

@ -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.
*/