Making the octree more useful and cleaning up the interaction with the Scene

This commit is contained in:
samcake 2016-01-26 18:46:57 -08:00
parent d1b29d561b
commit 1b1ffa494b
13 changed files with 728 additions and 654 deletions

View file

@ -215,7 +215,7 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend
}
};
void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) {
assert(renderContext->getArgs());
assert(renderContext->getArgs()->_viewFrustum);
@ -241,7 +241,7 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend
});
}
void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) {
assert(renderContext->getArgs());
assert(renderContext->getArgs()->_viewFrustum);
@ -294,7 +294,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered());
ItemIDsBounds inItems;
ItemBounds inItems;
inItems.reserve(items.size());
for (auto id : items) {
auto& item = scene->getItem(id);
@ -399,7 +399,7 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::background());
ItemIDsBounds inItems;
ItemBounds inItems;
inItems.reserve(items.size());
for (auto id : items) {
inItems.emplace_back(id);

View file

@ -52,9 +52,9 @@ public:
class DrawOpaqueDeferred {
public:
DrawOpaqueDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {}
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems);
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemBounds& inItems);
using JobModel = render::Task::Job::ModelI<DrawOpaqueDeferred, render::ItemIDsBounds>;
using JobModel = render::Task::Job::ModelI<DrawOpaqueDeferred, render::ItemBounds>;
protected:
render::ShapePlumberPointer _shapePlumber;
@ -63,9 +63,9 @@ protected:
class DrawTransparentDeferred {
public:
DrawTransparentDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {}
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems);
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemBounds& inItems);
using JobModel = render::Task::Job::ModelI<DrawTransparentDeferred, render::ItemIDsBounds>;
using JobModel = render::Task::Job::ModelI<DrawTransparentDeferred, render::ItemBounds>;
protected:
render::ShapePlumberPointer _shapePlumber;

View file

@ -72,53 +72,14 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext,
_octreeInfo = std::make_shared<gpu::Buffer>();;
}*/
const auto& inCells = scene->_spatialTree._cells;
const auto& inCells = scene->getSpatialTree()._cells;
_cells->resize(inCells.size() * sizeof(AABox));
AABox* cellAABox = reinterpret_cast<AABox*> (_cells->editData());
for (const auto& cell : inCells) {
(*cellAABox) = scene->_spatialTree.evalBound(cell.cellpos);
(*cellAABox) = scene->getSpatialTree().evalBound(cell.getlocation());
nbCells++;
cellAABox++;
}
/*
_cells->resize((inItems.size() * sizeof(AABox)));
_itemStatus->resize((inItems.size() * NUM_STATUS_VEC4_PER_ITEM * sizeof(glm::vec4)));
AABox* itemAABox = reinterpret_cast<AABox*> (_itemBounds->editData());
glm::ivec4* itemStatus = reinterpret_cast<glm::ivec4*> (_itemStatus->editData());
for (auto& item : inItems) {
if (!item.bounds.isInvalid()) {
if (!item.bounds.isNull()) {
(*itemAABox) = item.bounds;
} else {
(*itemAABox).setBox(item.bounds.getCorner(), 0.1f);
}
auto& itemScene = scene->getItem(item.id);
auto itemStatusPointer = itemScene.getStatus();
if (itemStatusPointer) {
// Query the current status values, this is where the statusGetter lambda get called
auto&& currentStatusValues = itemStatusPointer->getCurrentValues();
int valueNum = 0;
for (int vec4Num = 0; vec4Num < NUM_STATUS_VEC4_PER_ITEM; vec4Num++) {
(*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
for (int component = 0; component < VEC4_LENGTH; component++) {
valueNum = vec4Num * VEC4_LENGTH + component;
if (valueNum < (int)currentStatusValues.size()) {
(*itemStatus)[component] = currentStatusValues[valueNum].getPackedData();
}
}
itemStatus++;
}
} else {
(*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
itemStatus++;
(*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
itemStatus++;
}
}
}*/
}
if (nbCells == 0) {

View file

@ -96,7 +96,7 @@ const gpu::TexturePointer DrawStatus::getStatusIconMap() const {
void DrawStatus::run(const SceneContextPointer& sceneContext,
const RenderContextPointer& renderContext,
const ItemIDsBounds& inItems) {
const ItemBounds& inItems) {
assert(renderContext->getArgs());
assert(renderContext->getArgs()->_viewFrustum);
RenderArgs* args = renderContext->getArgs();
@ -119,11 +119,11 @@ void DrawStatus::run(const SceneContextPointer& sceneContext,
AABox* itemAABox = reinterpret_cast<AABox*> (_itemBounds->editData());
glm::ivec4* itemStatus = reinterpret_cast<glm::ivec4*> (_itemStatus->editData());
for (auto& item : inItems) {
if (!item.bounds.isInvalid()) {
if (!item.bounds.isNull()) {
(*itemAABox) = item.bounds;
if (!item.bound.isInvalid()) {
if (!item.bound.isNull()) {
(*itemAABox) = item.bound;
} else {
(*itemAABox).setBox(item.bounds.getCorner(), 0.1f);
(*itemAABox).setBox(item.bound.getCorner(), 0.1f);
}
auto& itemScene = scene->getItem(item.id);

View file

@ -36,9 +36,9 @@ namespace render {
DrawStatus() {}
DrawStatus(const gpu::TexturePointer statusIconMap) { setStatusIconMap(statusIconMap); }
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems);
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems);
using JobModel = Task::Job::ModelI<DrawStatus, ItemIDsBounds>;
using JobModel = Task::Job::ModelI<DrawStatus, ItemBounds>;
const gpu::PipelinePointer getDrawItemBoundsPipeline();
const gpu::PipelinePointer getDrawItemStatusPipeline();

View file

@ -21,7 +21,7 @@
using namespace render;
void render::cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details,
const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
const ItemBounds& inItems, ItemBounds& outItems) {
assert(renderContext->getArgs());
assert(renderContext->getArgs()->_viewFrustum);
@ -32,7 +32,7 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc
// Culling / LOD
for (auto item : inItems) {
if (item.bounds.isNull()) {
if (item.bound.isNull()) {
outItems.emplace_back(item); // One more Item to render
continue;
}
@ -42,13 +42,13 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc
bool outOfView;
{
PerformanceTimer perfTimer("boxInFrustum");
outOfView = frustum->boxInFrustum(item.bounds) == ViewFrustum::OUTSIDE;
outOfView = frustum->boxInFrustum(item.bound) == ViewFrustum::OUTSIDE;
}
if (!outOfView) {
bool bigEnoughToRender;
{
PerformanceTimer perfTimer("shouldRender");
bigEnoughToRender = cullFunctor(args, item.bounds);
bigEnoughToRender = cullFunctor(args, item.bound);
}
if (bigEnoughToRender) {
outItems.emplace_back(item); // One more Item to render
@ -62,30 +62,30 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc
details._rendered += outItems.size();
}
struct ItemBound {
struct ItemBoundSort {
float _centerDepth = 0.0f;
float _nearDepth = 0.0f;
float _farDepth = 0.0f;
ItemID _id = 0;
AABox _bounds;
ItemBound() {}
ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {}
ItemBoundSort() {}
ItemBoundSort(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {}
};
struct FrontToBackSort {
bool operator() (const ItemBound& left, const ItemBound& right) {
bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) {
return (left._centerDepth < right._centerDepth);
}
};
struct BackToFrontSort {
bool operator() (const ItemBound& left, const ItemBound& right) {
bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) {
return (left._centerDepth > right._centerDepth);
}
};
void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems) {
assert(renderContext->getArgs());
assert(renderContext->getArgs()->_viewFrustum);
@ -99,33 +99,33 @@ void render::depthSortItems(const SceneContextPointer& sceneContext, const Rende
// Make a local dataset of the center distance and closest point distance
std::vector<ItemBound> itemBounds;
itemBounds.reserve(outItems.size());
std::vector<ItemBoundSort> itemBoundSorts;
itemBoundSorts.reserve(outItems.size());
for (auto itemDetails : inItems) {
auto item = scene->getItem(itemDetails.id);
auto bound = itemDetails.bounds; // item.getBound();
auto bound = itemDetails.bound; // item.getBound();
float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter());
itemBounds.emplace_back(ItemBound(distance, distance, distance, itemDetails.id, bound));
itemBoundSorts.emplace_back(ItemBoundSort(distance, distance, distance, itemDetails.id, bound));
}
// sort against Z
if (frontToBack) {
FrontToBackSort frontToBackSort;
std::sort (itemBounds.begin(), itemBounds.end(), frontToBackSort);
std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), frontToBackSort);
} else {
BackToFrontSort backToFrontSort;
std::sort (itemBounds.begin(), itemBounds.end(), backToFrontSort);
std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), backToFrontSort);
}
// FInally once sorted result to a list of itemID
for (auto& itemBound : itemBounds) {
outItems.emplace_back(ItemIDAndBounds(itemBound._id, itemBound._bounds));
for (auto& item : itemBoundSorts) {
outItems.emplace_back(ItemBound(item._id, item._bounds));
}
}
void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) {
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->getArgs();
@ -151,7 +151,7 @@ void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, cons
}
void render::renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext,
const ShapePlumberPointer& shapeContext, const ItemIDsBounds& inItems, int maxDrawnItems) {
const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems) {
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->getArgs();
@ -162,7 +162,7 @@ void render::renderShapes(const SceneContextPointer& sceneContext, const RenderC
}
}
void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems) {
void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems) {
auto& scene = sceneContext->_scene;
outItems.clear();
@ -173,7 +173,7 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex
outItems.reserve(items->second.size());
for (auto& id : items->second) {
auto& item = scene->getItem(id);
outItems.emplace_back(ItemIDAndBounds(id, item.getBound()));
outItems.emplace_back(ItemBound(id, item.getBound()));
}
}
@ -182,7 +182,7 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex
}
}
void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) {
depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems);
}
@ -194,17 +194,17 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::light());
ItemIDsBounds inItems;
ItemBounds inItems;
inItems.reserve(items.size());
for (auto id : items) {
auto item = scene->getItem(id);
inItems.emplace_back(ItemIDAndBounds(id, item.getBound()));
inItems.emplace_back(ItemBound(id, item.getBound()));
}
RenderArgs* args = renderContext->getArgs();
auto& details = args->_details.edit(RenderDetails::OTHER_ITEM);
ItemIDsBounds culledItems;
ItemBounds culledItems;
culledItems.reserve(inItems.size());
cullItems(renderContext, _cullFunctor, details, inItems, culledItems);
@ -215,7 +215,7 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext
});
}
void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ShapesIDsBounds& outShapes) {
void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ShapesIDsBounds& outShapes) {
auto& scene = sceneContext->_scene;
outShapes.clear();
@ -223,7 +223,7 @@ void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const Rend
auto key = scene->getItem(item.id).getShapeKey();
auto outItems = outShapes.find(key);
if (outItems == outShapes.end()) {
outItems = outShapes.insert(std::make_pair(key, ItemIDsBounds{})).first;
outItems = outShapes.insert(std::make_pair(key, ItemBounds{})).first;
outItems->second.reserve(inItems.size());
}
@ -243,7 +243,7 @@ void DepthSortShapes::run(const SceneContextPointer& sceneContext, const RenderC
auto& inItems = pipeline.second;
auto outItems = outShapes.find(pipeline.first);
if (outItems == outShapes.end()) {
outItems = outShapes.insert(std::make_pair(pipeline.first, ItemIDsBounds{})).first;
outItems = outShapes.insert(std::make_pair(pipeline.first, ItemBounds{})).first;
}
depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems->second);

View file

@ -22,10 +22,10 @@ namespace render {
using CullFunctor = std::function<bool(const RenderArgs*, const AABox&)>;
void cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details,
const ItemIDsBounds& inItems, ItemIDsBounds& outItems);
void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outItems);
void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems);
void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemIDsBounds& inItems, int maxDrawnItems = -1);
const ItemBounds& inItems, ItemBounds& outItems);
void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems);
void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems);
void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1);
class FetchItems {
public:
@ -37,8 +37,8 @@ public:
ItemFilter _filter = ItemFilter::Builder::opaqueShape().withoutLayered();
ProbeNumItems _probeNumItems;
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems);
using JobModel = Task::Job::ModelO<FetchItems, ItemIDsBounds>;
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems);
using JobModel = Task::Job::ModelO<FetchItems, ItemBounds>;
};
template<RenderDetails::Type T = RenderDetails::Type::OTHER_ITEM>
@ -46,7 +46,7 @@ class CullItems {
public:
CullItems(CullFunctor cullFunctor) : _cullFunctor{ cullFunctor } {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) {
const auto& args = renderContext->getArgs();
auto& details = args->_details.edit(T);
outItems.clear();
@ -54,7 +54,7 @@ public:
render::cullItems(renderContext, _cullFunctor, details, inItems, outItems);
}
using JobModel = Task::Job::ModelIO<CullItems<T>, ItemIDsBounds, ItemIDsBounds>;
using JobModel = Task::Job::ModelIO<CullItems<T>, ItemBounds, ItemBounds>;
protected:
CullFunctor _cullFunctor;
@ -65,8 +65,8 @@ public:
bool _frontToBack;
DepthSortItems(bool frontToBack = true) : _frontToBack(frontToBack) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems);
using JobModel = Task::Job::ModelIO<DepthSortItems, ItemIDsBounds, ItemIDsBounds>;
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems);
using JobModel = Task::Job::ModelIO<DepthSortItems, ItemBounds, ItemBounds>;
};
class DrawLight {
@ -81,8 +81,8 @@ protected:
class PipelineSortShapes {
public:
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ShapesIDsBounds& outShapes);
using JobModel = Task::Job::ModelIO<PipelineSortShapes, ItemIDsBounds, ShapesIDsBounds>;
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ShapesIDsBounds& outShapes);
using JobModel = Task::Job::ModelIO<PipelineSortShapes, ItemBounds, ShapesIDsBounds>;
};
class DepthSortShapes {

View file

@ -0,0 +1,73 @@
//
// Item.cpp
// render/src/render
//
// Created by Sam Gateau on 1/26/16.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Item.h"
#include <numeric>
#include "gpu/Batch.h"
using namespace render;
const Item::Status::Value Item::Status::Value::INVALID = Item::Status::Value();
const float Item::Status::Value::RED = 0.0f;
const float Item::Status::Value::YELLOW = 60.0f;
const float Item::Status::Value::GREEN = 120.0f;
const float Item::Status::Value::CYAN = 180.0f;
const float Item::Status::Value::BLUE = 240.0f;
const float Item::Status::Value::MAGENTA = 300.0f;
void Item::Status::Value::setScale(float scale) {
_scale = (std::numeric_limits<unsigned short>::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f));
}
void Item::Status::Value::setColor(float hue) {
// Convert the HUe from range [0, 360] to signed normalized value
const float HUE_MAX = 360.0f;
_color = (std::numeric_limits<unsigned char>::max()) * (std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX);
}
void Item::Status::Value::setIcon(unsigned char icon) {
_icon = icon;
}
Item::Status::Values Item::Status::getCurrentValues() const {
Values currentValues(_values.size());
auto currentValue = currentValues.begin();
for (auto& getter : _values) {
(*currentValue) = getter();
currentValue++;
}
return currentValues;
}
void Item::PayloadInterface::addStatusGetter(const Status::Getter& getter) {
if (!_status) {
_status = std::make_shared<Status>();
}
_status->addGetter(getter);
}
void Item::PayloadInterface::addStatusGetters(const Status::Getters& getters) {
if (!_status) {
_status = std::make_shared<Status>();
}
for (auto& g : getters) {
_status->addGetter(g);
}
}
void Item::resetPayload(const PayloadPointer& payload) {
if (!payload) {
kill();
} else {
_payload = payload;
_key = _payload->getKey();
}
}

View file

@ -0,0 +1,444 @@
//
// Item.h
// render/src/render
//
// Created by Sam Gateau on 1/26/16.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_render_Item_h
#define hifi_render_Item_h
#include <atomic>
#include <bitset>
#include <map>
#include <memory>
#include <mutex>
#include <queue>
#include <set>
#include <vector>
#include <AABox.h>
#include <RenderArgs.h>
#include "model/Material.h"
#include "ShapePipeline.h"
namespace render {
class Context;
// Key is the KEY to filter Items and create specialized lists
class ItemKey {
public:
enum FlagBit {
TYPE_SHAPE = 0, // Item is a Shape
TYPE_LIGHT, // Item is a Light
TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work...
VIEW_SPACE, // Transformed in view space, and not in world space
DYNAMIC, // Dynamic and bound will change unlike static item
DEFORMED, // Deformed within bound, not solid
INVISIBLE, // Visible or not? could be just here to cast shadow
SHADOW_CASTER, // Item cast shadows
PICKABLE, // Item can be picked/selected
LAYERED, // Item belongs to one of the layers different from the default layer
NUM_FLAGS, // Not a valid flag
};
typedef std::bitset<NUM_FLAGS> Flags;
// The key is the Flags
Flags _flags;
ItemKey() : _flags(0) {}
ItemKey(const Flags& flags) : _flags(flags) {}
class Builder {
friend class ItemKey;
Flags _flags{ 0 };
public:
Builder() {}
ItemKey build() const { return ItemKey(_flags); }
Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); }
Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); }
Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); }
Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); }
Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); }
Builder& withDeformed() { _flags.set(DEFORMED); return (*this); }
Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }
Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); }
Builder& withPickable() { _flags.set(PICKABLE); return (*this); }
Builder& withLayered() { _flags.set(LAYERED); return (*this); }
// Convenient standard keys that we will keep on using all over the place
static Builder opaqueShape() { return Builder().withTypeShape(); }
static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); }
static Builder light() { return Builder().withTypeLight(); }
static Builder background() { return Builder().withViewSpace().withLayered(); }
};
ItemKey(const Builder& builder) : ItemKey(builder._flags) {}
bool isShape() const { return _flags[TYPE_SHAPE]; }
bool isLight() const { return _flags[TYPE_LIGHT]; }
bool isOpaque() const { return !_flags[TRANSLUCENT]; }
bool isTransparent() const { return _flags[TRANSLUCENT]; }
bool isWorldSpace() const { return !_flags[VIEW_SPACE]; }
bool isViewSpace() const { return _flags[VIEW_SPACE]; }
bool isStatic() const { return !_flags[DYNAMIC]; }
bool isDynamic() const { return _flags[DYNAMIC]; }
bool isRigid() const { return !_flags[DEFORMED]; }
bool isDeformed() const { return _flags[DEFORMED]; }
bool isVisible() const { return !_flags[INVISIBLE]; }
bool isInvisible() const { return _flags[INVISIBLE]; }
bool isShadowCaster() const { return _flags[SHADOW_CASTER]; }
bool isPickable() const { return _flags[PICKABLE]; }
bool isLayered() const { return _flags[LAYERED]; }
};
inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) {
debug << "[ItemKey: isOpaque:" << itemKey.isOpaque()
<< ", isStatic:" << itemKey.isStatic()
<< ", isWorldSpace:" << itemKey.isWorldSpace()
<< "]";
return debug;
}
class ItemFilter {
public:
ItemKey::Flags _value{ 0 };
ItemKey::Flags _mask{ 0 };
ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {}
class Builder {
friend class ItemFilter;
ItemKey::Flags _value{ 0 };
ItemKey::Flags _mask{ 0 };
public:
Builder() {}
ItemFilter build() const { return ItemFilter(_value, _mask); }
Builder& withTypeShape() { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); }
Builder& withTypeLight() { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); }
Builder& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
Builder& withWorldSpace() { _value.reset(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); }
Builder& withViewSpace() { _value.set(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); }
Builder& withStatic() { _value.reset(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); }
Builder& withDynamic() { _value.set(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); }
Builder& withRigid() { _value.reset(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); }
Builder& withDeformed() { _value.set(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); }
Builder& withVisible() { _value.reset(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); }
Builder& withInvisible() { _value.set(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); }
Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); }
Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); }
Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); }
Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
// Convenient standard keys that we will keep on using all over the place
static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); }
static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); }
static Builder light() { return Builder().withTypeLight(); }
static Builder background() { return Builder().withViewSpace().withLayered(); }
};
ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {}
// Item Filter operator testing if a key pass the filter
bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); }
class Less {
public:
bool operator() (const ItemFilter& left, const ItemFilter& right) const {
if (left._value.to_ulong() == right._value.to_ulong()) {
return left._mask.to_ulong() < right._mask.to_ulong();
} else {
return left._value.to_ulong() < right._value.to_ulong();
}
}
};
};
inline QDebug operator<<(QDebug debug, const ItemFilter& me) {
debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape().build())
<< "]";
return debug;
}
class Item {
public:
typedef std::vector<Item> Vector;
typedef unsigned int ID;
static const ID INVALID_ITEM_ID = 0;
// Bound is the AABBox fully containing this item
typedef AABox Bound;
// Status records the life history and performances of this item while performing at rendering and updating.
// This is Used for monitoring and dynamically adjust the quality
class Status {
public:
// Status::Value class is the data used to represent the transient information of a status as a square icon
// The "icon" is a square displayed in the 3D scene over the render::Item AABB center.
// It can be scaled in the range [0, 1] and the color hue in the range [0, 360] representing the color wheel hue
class Value {
unsigned short _scale = 0xFFFF;
unsigned char _color = 0xFF;
unsigned char _icon = 0xFF;
public:
const static Value INVALID; // Invalid value meanss the status won't show
Value() {}
Value(float scale, float hue, unsigned char icon = 0xFF) { setScale(scale); setColor(hue); setIcon(icon); }
// It can be scaled in the range [0, 1]
void setScale(float scale);
// the color hue in the range [0, 360] representing the color wheel hue
void setColor(float hue);
// the icon to display in the range [0, 255], where 0 means no icon, just filled quad and anything else would
// hopefully have an icon available to display (see DrawStatusJob)
void setIcon(unsigned char icon);
// Standard color Hue
static const float RED; // 0.0f;
static const float YELLOW; // 60.0f;
static const float GREEN; // 120.0f;
static const float CYAN; // 180.0f;
static const float BLUE; // 240.0f;
static const float MAGENTA; // 300.0f;
// Retreive the Value data tightely packed as an int
int getPackedData() const { return *((const int*) this); }
};
typedef std::function<Value()> Getter;
typedef std::vector<Getter> Getters;
Getters _values;
void addGetter(const Getter& getter) { _values.push_back(getter); }
size_t getNumValues() const { return _values.size(); }
using Values = std::vector <Value>;
Values getCurrentValues() const;
};
typedef std::shared_ptr<Status> StatusPointer;
// Update Functor
class UpdateFunctorInterface {
public:
virtual ~UpdateFunctorInterface() {}
};
typedef std::shared_ptr<UpdateFunctorInterface> UpdateFunctorPointer;
// Payload is whatever is in this Item and implement the Payload Interface
class PayloadInterface {
public:
virtual const ItemKey getKey() const = 0;
virtual const Bound getBound() const = 0;
virtual int getLayer() const = 0;
virtual void render(RenderArgs* args) = 0;
virtual const ShapeKey getShapeKey() const = 0;
~PayloadInterface() {}
// Status interface is local to the base class
const StatusPointer& getStatus() const { return _status; }
void addStatusGetter(const Status::Getter& getter);
void addStatusGetters(const Status::Getters& getters);
protected:
StatusPointer _status;
friend class Item;
virtual void update(const UpdateFunctorPointer& functor) = 0;
};
typedef std::shared_ptr<PayloadInterface> PayloadPointer;
Item() {}
~Item() {}
// Main scene / item managment interface Reset/Update/Kill
void resetPayload(const PayloadPointer& payload);
void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Communicate update to the payload
void kill() { _payload.reset(); _key._flags.reset(); } // Kill means forget the payload and key
// Check heuristic key
const ItemKey& getKey() const { return _key; }
// Payload Interface
// Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace())
const Bound getBound() const { return _payload->getBound(); }
// Get the layer where the item belongs. 0 by default meaning NOT LAYERED
int getLayer() const { return _payload->getLayer(); }
// Render call for the item
void render(RenderArgs* args) const { _payload->render(args); }
// Shape Type Interface
const ShapeKey getShapeKey() const { return _payload->getShapeKey(); }
// Access the status
const StatusPointer& getStatus() const { return _payload->getStatus(); }
protected:
PayloadPointer _payload;
ItemKey _key;
friend class Scene;
};
typedef Item::UpdateFunctorInterface UpdateFunctorInterface;
typedef Item::UpdateFunctorPointer UpdateFunctorPointer;
typedef std::vector<UpdateFunctorPointer> UpdateFunctors;
template <class T> class UpdateFunctor : public Item::UpdateFunctorInterface {
public:
typedef std::function<void(T&)> Func;
Func _func;
UpdateFunctor(Func func): _func(func) {}
~UpdateFunctor() {}
};
inline QDebug operator<<(QDebug debug, const Item& item) {
debug << "[Item: _key:" << item.getKey() << ", bounds:" << item.getBound() << "]";
return debug;
}
// THe Payload class is the real Payload to be used
// THis allow anything to be turned into a Payload as long as the required interface functions are available
// When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff"
// of the Payload interface
template <class T> const ItemKey payloadGetKey(const std::shared_ptr<T>& payloadData) { return ItemKey(); }
template <class T> const Item::Bound payloadGetBound(const std::shared_ptr<T>& payloadData) { return Item::Bound(); }
template <class T> int payloadGetLayer(const std::shared_ptr<T>& payloadData) { return 0; }
template <class T> void payloadRender(const std::shared_ptr<T>& payloadData, RenderArgs* args) { }
// Shape type interface
// This allows shapes to characterize their pipeline via a ShapeKey, to be picked with a subclass of Shape.
// When creating a new shape payload you need to create a specialized version, or the ShapeKey will be ownPipeline,
// implying that the shape will setup its own pipeline without the use of the ShapeKey.
template <class T> const ShapeKey shapeGetShapeKey(const std::shared_ptr<T>& payloadData) { return ShapeKey::Builder::ownPipeline(); }
template <class T> class Payload : public Item::PayloadInterface {
public:
typedef std::shared_ptr<T> DataPointer;
typedef UpdateFunctor<T> Updater;
Payload(const DataPointer& data) : _data(data) {}
// Payload general interface
virtual const ItemKey getKey() const { return payloadGetKey<T>(_data); }
virtual const Item::Bound getBound() const { return payloadGetBound<T>(_data); }
virtual int getLayer() const { return payloadGetLayer<T>(_data); }
virtual void render(RenderArgs* args) { payloadRender<T>(_data, args); }
// Shape Type interface
virtual const ShapeKey getShapeKey() const { return shapeGetShapeKey<T>(_data); }
protected:
DataPointer _data;
// Update mechanics
virtual void update(const UpdateFunctorPointer& functor) { std::static_pointer_cast<Updater>(functor)->_func((*_data)); }
friend class Item;
};
// Let's show how to make a simple FooPayload example:
/*
class Foo {
public:
mutable ItemKey _myownKey;
void makeMywnKey() const {
_myownKey = ItemKey::Builder().withTypeShape().build();
}
const Item::Bound evaluateMyBound() {
// Do stuff here to get your final Bound
return Item::Bound();
}
};
typedef Payload<Foo> FooPayload;
typedef std::shared_ptr<Foo> FooPointer;
// In a Source file, not a header, implement the Payload interface function specialized for Foo:
template <> const ItemKey payloadGetKey(const FooPointer& foo) {
// Foo's way of provinding its Key
foo->makeMyKey();
return foo->_myownKey;
}
template <> const Item::Bound payloadGetBound(const FooPointer& foo) {
// evaluate Foo's own bound
return foo->evaluateMyBound();
}
// In this example, do not specialize the payloadRender call which means the compiler will use the default version which does nothing
*/
// End of the example
typedef Item::PayloadPointer PayloadPointer;
typedef std::vector< PayloadPointer > Payloads;
// A few typedefs for standard containers of ItemIDs
using ItemID = Item::ID;
using ItemIDs = std::vector<ItemID>;
using ItemIDSet = std::set<ItemID>;
// Handy type to just pass the ID and the bound of an item
class ItemBound {
public:
ItemBound(ItemID id) : id(id) { }
ItemBound(ItemID id, const AABox& bound) : id(id), bound(bound) { }
ItemID id;
AABox bound;
};
// many Item Bounds in a vector
using ItemBounds = std::vector<ItemBound>;
// A map of items by ShapeKey to optimize rendering pipeline assignments
using ShapesIDsBounds = std::unordered_map<ShapeKey, ItemBounds, ShapeKey::Hash, ShapeKey::KeyEqual>;
}
#endif // hifi_render_Item_h

View file

@ -13,26 +13,54 @@
using namespace render;
const double Octree::INV_DEPTH_DIM[] = {
1.0,
1.0 / 2.0,
1.0 / 4.0,
1.0 / 8.0,
1.0 / 16.0,
1.0 / 32.0,
1.0 / 64.0,
1.0 / 128.0,
1.0 / 256.0,
1.0 / 512.0,
1.0 / 1024.0,
1.0 / 2048.0,
1.0 / 4096.0,
1.0 / 8192.0,
1.0 / 16384.0,
1.0 / 32768.0 };
Octree::Indices Octree::allocateCellPath(const CellPath& path) {
Indices cellPath;
Index currentIndex = 0;
Cell* currentCell = _cells.data();
int d = 0;
cellPath.push_back(currentIndex);
for (; d <= path.back().depth; d++) {
auto& cellPoint = path[d];
for (int d = 0; d <= path.back().depth; d++) {
auto& location = path[d];
auto currentIndex = currentCell->child(cellPoint.octant());
auto currentIndex = currentCell->child(location.octant());
if (currentIndex == INVALID) {
currentIndex = _cells.size();
currentCell->links[cellPoint.octant()] = currentIndex;
_cells.push_back(Cell(cellPath.back(), cellPoint));
currentCell->setChild(location.octant(), currentIndex);
_cells.push_back(Cell(cellPath.back(), location));
}
cellPath.push_back(currentIndex);
currentCell = _cells.data() + currentIndex;
}
return cellPath;
}
void ItemSpatialTree::insert(const ItemBounds& items) {
}
void ItemSpatialTree::erase(const ItemBounds& items) {
}

View file

@ -18,12 +18,10 @@
#include <array>
#include <glm/glm.hpp>
#include <glm/gtx/bit.hpp>
#include <AABox.h>
namespace render {
class Octree {
public:
@ -55,7 +53,7 @@ namespace render {
using Octant = Link;
// Depth, Width, Volume
// Depth, Dim, Volume
// {0, 1, 1}
// {1, 2, 8}
// {2, 4, 64}
@ -73,21 +71,30 @@ namespace render {
// {14, 16384, 4398046511104}
// {15, 32768, 35184372088832}
// {16, 65536, 281474976710656}
using Depth = int8_t; // Max depth is 15 => 32Km root down to 1m cells
using Coord = int16_t;// Need 16bits integer coordinates on each axes: 32768 cell positions
// Max depth is 15 => 32Km root down to 1m cells
using Depth = int8_t;
static const Depth MAX_DEPTH { 15 };
static const double INV_DEPTH_DIM[Octree::MAX_DEPTH + 1];
static int getDepthDimension(Depth depth) { return 2 << depth; }
static double getInvDepthDimension(Depth depth) { return INV_DEPTH_DIM[depth]; }
// Need 16bits integer coordinates on each axes: 32768 cell positions
using Coord = int16_t;
using Coord3 = glm::i16vec3;
using Coord4 = glm::i16vec4;
class CellPoint {
class Location {
void assertValid() {
assert((pos.x >= 0) && (pos.y >= 0) && (pos.z >= 0));
assert((pos.x < (2 << depth)) && (pos.y < (2 << depth)) && (pos.z < (2 << depth)));
}
public:
CellPoint() {}
CellPoint(const Coord3& xyz, Depth d) : pos(xyz), depth(d) { assertValid(); }
CellPoint(Depth d) : pos(0), depth(d) { assertValid(); }
Location() {}
Location(const Coord3& xyz, Depth d) : pos(xyz), depth(d) { assertValid(); }
Location(Depth d) : pos(0), depth(d) { assertValid(); }
Coord3 pos{ 0 };
uint8_t spare{ 0 };
@ -97,17 +104,17 @@ namespace render {
// Eval the octant of this cell relative to its parent
Octant octant() const { return Octant((pos.x & XAxis) | (pos.y & YAxis) | (pos.z & ZAxis)); }
// Get the Parent cell pos of this cell
CellPoint parent() const {
return CellPoint{ pos >> Coord3(1), Depth(depth <= 0 ? 0 : depth - 1) };
// Get the Parent cell Location of this cell
Location parent() const {
return Location{ pos >> Coord3(1), Depth(depth <= 0 ? 0 : depth - 1) };
}
CellPoint child(Link octant) const {
return CellPoint{ pos << Coord3(1) | Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)), Depth(depth + 1) };
Location child(Link octant) const {
return Location{ pos << Coord3(1) | Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)), Depth(depth + 1) };
}
using vector = std::vector< CellPoint >;
static vector rootTo(const CellPoint& dest) {
CellPoint current{ dest };
using vector = std::vector< Location >;
static vector rootTo(const Location& dest) {
Location current{ dest };
vector path(dest.depth + 1);
path[dest.depth] = dest;
while (current.depth > 0) {
@ -117,9 +124,9 @@ namespace render {
return path;
}
};
using CellPath = CellPoint::vector;
using CellPath = Location::vector;
class Range {
public:
Coord3 _min;
@ -129,66 +136,113 @@ namespace render {
// Cell or Brick Indexing
using Index = int32_t;
static const Index INVALID = -1;
static const Index ROOT = 0;
using Indices = std::vector<Index>;
// the cell description
class Cell {
public:
const Location& getlocation() const { return _location; }
CellPoint cellpos;
std::array<Index, NUM_LINKS> links;
Index parent() const { return links[Parent]; }
Index parent() const { return _links[Parent]; }
bool asParent() const { return parent() != INVALID; }
Index child(Link octant) const { return links[octant]; }
Index child(Link octant) const { return _links[octant]; }
bool asChild(Link octant) const { return child(octant) != INVALID; }
void setChild(Link octant, Index child) { _links[octant] = child; }
Cell() :
_links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID } })
{}
Cell(Index parent, Location loc) :
_location(loc),
_links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, parent } })
{}
Index brick{ INVALID };
Cell() :
links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID } })
{}
Cell(Index parent, CellPoint pos) :
cellpos(pos),
links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, parent } })
{}
private:
Location _location;
std::array<Index, NUM_LINKS> _links;
};
using Cells = std::vector< Cell >;
class Brick {
public:
};
using Bricks = std::vector< Brick >;
// Octree members
Cells _cells = Cells(1, Cell()); // start with only the Cell root
Bricks _bricks;
float _size = 320.0f;
Octree() {};
// allocatePath
Indices allocateCellPath(const CellPath& path);
AABox evalBound(const CellPoint& point) const {
float width = (float) (_size / double(1 << point.depth));
glm::vec3 corner = glm::vec3(-_size * 0.5f) + glm::vec3(point.pos) * width;
return AABox(corner, width);
// reach to Cell and allocate the cell path to it if needed
Index editCell(const Location& loc) {
auto cells = allocateCellPath(Location::rootTo(loc));
return cells.back();
}
};
}
// CLose the namespace here before including the Item in the picture, maybe Octre could stay pure of it
#include "Item.h"
namespace render {
// An octree of Items organizing them efficiently for culling
// The octree only cares about the bound & the key of an item to store it a the right cell location
class ItemSpatialTree : public Octree {
float _size{ 32000.0f };
double _invSize{ 1.0 / _size };
glm::vec3 _origin{ -_size };
public:
float getSize() const { _size; }
glm::vec3 getOrigin() const { _origin; }
float getCellWidth(Depth depth) const { return (float) _size * getInvDepthDimension(depth); }
float getInvCellWidth(Depth depth) const { return (float) getDepthDimension(depth) * _invSize; }
glm::vec3 evalPos(const Coord3& coord, Depth depth = Octree::MAX_DEPTH) const {
return getOrigin() + glm::vec3(coord) * getCellWidth(depth);
}
glm::vec3 evalPos(const Coord3& coord, float cellWidth) const {
return getOrigin() + glm::vec3(coord) * cellWidth;
}
Coord3 evalCoord(const glm::vec3& pos, Depth depth = Octree::MAX_DEPTH) const {
return Coord3((pos - getOrigin()) * getInvCellWidth(depth));
}
AABox evalBound(const Location& loc) const {
float cellWidth = getCellWidth(loc.depth);
return AABox(evalPos(loc.pos, cellWidth), cellWidth);
}
Location evalLocation(const AABox& bound) const {
auto minPos = evalCoord(bound.getMinimumPoint());
auto maxPos = evalCoord(bound.getMaximumPoint());
auto range = maxPos - minPos;
//range
return Location(minPos, 4);
}
ItemSpatialTree() {}
void insert(const ItemBounds& items);
void erase(const ItemBounds& items);
};
}
#endif // hifi_render_Octree_h

View file

@ -56,62 +56,6 @@ void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() {
(*this)[ItemFilter::Builder::transparentShape().withLayered()];
}
const Item::Status::Value Item::Status::Value::INVALID = Item::Status::Value();
const float Item::Status::Value::RED = 0.0f;
const float Item::Status::Value::YELLOW = 60.0f;
const float Item::Status::Value::GREEN = 120.0f;
const float Item::Status::Value::CYAN = 180.0f;
const float Item::Status::Value::BLUE = 240.0f;
const float Item::Status::Value::MAGENTA = 300.0f;
void Item::Status::Value::setScale(float scale) {
_scale = (std::numeric_limits<unsigned short>::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f));
}
void Item::Status::Value::setColor(float hue) {
// Convert the HUe from range [0, 360] to signed normalized value
const float HUE_MAX = 360.0f;
_color = (std::numeric_limits<unsigned char>::max()) * (std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX);
}
void Item::Status::Value::setIcon(unsigned char icon) {
_icon = icon;
}
Item::Status::Values Item::Status::getCurrentValues() const {
Values currentValues(_values.size());
auto currentValue = currentValues.begin();
for (auto& getter : _values) {
(*currentValue) = getter();
currentValue++;
}
return currentValues;
}
void Item::PayloadInterface::addStatusGetter(const Status::Getter& getter) {
if (!_status) {
_status = std::make_shared<Status>();
}
_status->addGetter(getter);
}
void Item::PayloadInterface::addStatusGetters(const Status::Getters& getters) {
if (!_status) {
_status = std::make_shared<Status>();
}
for (auto& g : getters) {
_status->addGetter(g);
}
}
void Item::resetPayload(const PayloadPointer& payload) {
if (!payload) {
kill();
} else {
_payload = payload;
_key = _payload->getKey();
}
}
void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) {
_resetItems.push_back(id);
@ -127,7 +71,6 @@ void PendingChanges::updateItem(ItemID id, const UpdateFunctorPointer& functor)
_updateFunctors.push_back(functor);
}
void PendingChanges::merge(PendingChanges& changes) {
_resetItems.insert(_resetItems.end(), changes._resetItems.begin(), changes._resetItems.end());
_resetPayloads.insert(_resetPayloads.end(), changes._resetPayloads.begin(), changes._resetPayloads.end());
@ -139,12 +82,6 @@ void PendingChanges::merge(PendingChanges& changes) {
Scene::Scene() {
_items.push_back(Item()); // add the itemID #0 to nothing
_masterBucketMap.allocateStandardOpaqueTranparentBuckets();
_spatialTree;
Octree::CellPoint point{ Octree::Coord3{ 2, 4, 3 }, 15};
auto path = Octree::CellPoint::rootTo(point);
auto indices = _spatialTree.allocateCellPath(path);
}
ItemID Scene::allocateID() {
@ -200,8 +137,9 @@ void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) {
item.resetPayload(*resetPayload);
_masterBucketMap.reset((*resetID), oldKey, item.getKey());
}
}
}
void Scene::removeItems(const ItemIDs& ids) {

View file

@ -12,436 +12,12 @@
#ifndef hifi_render_Scene_h
#define hifi_render_Scene_h
#include <atomic>
#include <bitset>
#include <map>
#include <memory>
#include <mutex>
#include <queue>
#include <set>
#include <vector>
#include <AABox.h>
#include <RenderArgs.h>
#include "model/Material.h"
#include "ShapePipeline.h"
#include "Item.h"
#include "Octree.h"
namespace render {
class Context;
// Key is the KEY to filter Items and create specialized lists
class ItemKey {
public:
enum FlagBit {
TYPE_SHAPE = 0, // Item is a Shape
TYPE_LIGHT, // Item is a Light
TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work...
VIEW_SPACE, // Transformed in view space, and not in world space
DYNAMIC, // Dynamic and bound will change unlike static item
DEFORMED, // Deformed within bound, not solid
INVISIBLE, // Visible or not? could be just here to cast shadow
SHADOW_CASTER, // Item cast shadows
PICKABLE, // Item can be picked/selected
LAYERED, // Item belongs to one of the layers different from the default layer
NUM_FLAGS, // Not a valid flag
};
typedef std::bitset<NUM_FLAGS> Flags;
// The key is the Flags
Flags _flags;
ItemKey() : _flags(0) {}
ItemKey(const Flags& flags) : _flags(flags) {}
class Builder {
friend class ItemKey;
Flags _flags{ 0 };
public:
Builder() {}
ItemKey build() const { return ItemKey(_flags); }
Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); }
Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); }
Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); }
Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); }
Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); }
Builder& withDeformed() { _flags.set(DEFORMED); return (*this); }
Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }
Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); }
Builder& withPickable() { _flags.set(PICKABLE); return (*this); }
Builder& withLayered() { _flags.set(LAYERED); return (*this); }
// Convenient standard keys that we will keep on using all over the place
static Builder opaqueShape() { return Builder().withTypeShape(); }
static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); }
static Builder light() { return Builder().withTypeLight(); }
static Builder background() { return Builder().withViewSpace().withLayered(); }
};
ItemKey(const Builder& builder) : ItemKey(builder._flags) {}
bool isShape() const { return _flags[TYPE_SHAPE]; }
bool isLight() const { return _flags[TYPE_LIGHT]; }
bool isOpaque() const { return !_flags[TRANSLUCENT]; }
bool isTransparent() const { return _flags[TRANSLUCENT]; }
bool isWorldSpace() const { return !_flags[VIEW_SPACE]; }
bool isViewSpace() const { return _flags[VIEW_SPACE]; }
bool isStatic() const { return !_flags[DYNAMIC]; }
bool isDynamic() const { return _flags[DYNAMIC]; }
bool isRigid() const { return !_flags[DEFORMED]; }
bool isDeformed() const { return _flags[DEFORMED]; }
bool isVisible() const { return !_flags[INVISIBLE]; }
bool isInvisible() const { return _flags[INVISIBLE]; }
bool isShadowCaster() const { return _flags[SHADOW_CASTER]; }
bool isPickable() const { return _flags[PICKABLE]; }
bool isLayered() const { return _flags[LAYERED]; }
};
inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) {
debug << "[ItemKey: isOpaque:" << itemKey.isOpaque()
<< ", isStatic:" << itemKey.isStatic()
<< ", isWorldSpace:" << itemKey.isWorldSpace()
<< "]";
return debug;
}
class ItemFilter {
public:
ItemKey::Flags _value{ 0 };
ItemKey::Flags _mask{ 0 };
ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {}
class Builder {
friend class ItemFilter;
ItemKey::Flags _value{ 0 };
ItemKey::Flags _mask{ 0 };
public:
Builder() {}
ItemFilter build() const { return ItemFilter(_value, _mask); }
Builder& withTypeShape() { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); }
Builder& withTypeLight() { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); }
Builder& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
Builder& withWorldSpace() { _value.reset(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); }
Builder& withViewSpace() { _value.set(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); }
Builder& withStatic() { _value.reset(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); }
Builder& withDynamic() { _value.set(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); }
Builder& withRigid() { _value.reset(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); }
Builder& withDeformed() { _value.set(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); }
Builder& withVisible() { _value.reset(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); }
Builder& withInvisible() { _value.set(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); }
Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); }
Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); }
Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); }
Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
// Convenient standard keys that we will keep on using all over the place
static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); }
static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); }
static Builder light() { return Builder().withTypeLight(); }
static Builder background() { return Builder().withViewSpace().withLayered(); }
};
ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {}
// Item Filter operator testing if a key pass the filter
bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); }
class Less {
public:
bool operator() (const ItemFilter& left, const ItemFilter& right) const {
if (left._value.to_ulong() == right._value.to_ulong()) {
return left._mask.to_ulong() < right._mask.to_ulong();
} else {
return left._value.to_ulong() < right._value.to_ulong();
}
}
};
};
inline QDebug operator<<(QDebug debug, const ItemFilter& me) {
debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape().build())
<< "]";
return debug;
}
class Item {
public:
typedef std::vector<Item> Vector;
typedef unsigned int ID;
static const ID INVALID_ITEM_ID = 0;
// Bound is the AABBox fully containing this item
typedef AABox Bound;
// Status records the life history and performances of this item while performing at rendering and updating.
// This is Used for monitoring and dynamically adjust the quality
class Status {
public:
// Status::Value class is the data used to represent the transient information of a status as a square icon
// The "icon" is a square displayed in the 3D scene over the render::Item AABB center.
// It can be scaled in the range [0, 1] and the color hue in the range [0, 360] representing the color wheel hue
class Value {
unsigned short _scale = 0xFFFF;
unsigned char _color = 0xFF;
unsigned char _icon = 0xFF;
public:
const static Value INVALID; // Invalid value meanss the status won't show
Value() {}
Value(float scale, float hue, unsigned char icon = 0xFF) { setScale(scale); setColor(hue); setIcon(icon); }
// It can be scaled in the range [0, 1]
void setScale(float scale);
// the color hue in the range [0, 360] representing the color wheel hue
void setColor(float hue);
// the icon to display in the range [0, 255], where 0 means no icon, just filled quad and anything else would
// hopefully have an icon available to display (see DrawStatusJob)
void setIcon(unsigned char icon);
// Standard color Hue
static const float RED; // 0.0f;
static const float YELLOW; // 60.0f;
static const float GREEN; // 120.0f;
static const float CYAN; // 180.0f;
static const float BLUE; // 240.0f;
static const float MAGENTA; // 300.0f;
// Retreive the Value data tightely packed as an int
int getPackedData() const { return *((const int*) this); }
};
typedef std::function<Value()> Getter;
typedef std::vector<Getter> Getters;
Getters _values;
void addGetter(const Getter& getter) { _values.push_back(getter); }
size_t getNumValues() const { return _values.size(); }
using Values = std::vector <Value>;
Values getCurrentValues() const;
};
typedef std::shared_ptr<Status> StatusPointer;
// Update Functor
class UpdateFunctorInterface {
public:
virtual ~UpdateFunctorInterface() {}
};
typedef std::shared_ptr<UpdateFunctorInterface> UpdateFunctorPointer;
// Payload is whatever is in this Item and implement the Payload Interface
class PayloadInterface {
public:
virtual const ItemKey getKey() const = 0;
virtual const Bound getBound() const = 0;
virtual int getLayer() const = 0;
virtual void render(RenderArgs* args) = 0;
virtual const ShapeKey getShapeKey() const = 0;
~PayloadInterface() {}
// Status interface is local to the base class
const StatusPointer& getStatus() const { return _status; }
void addStatusGetter(const Status::Getter& getter);
void addStatusGetters(const Status::Getters& getters);
protected:
StatusPointer _status;
friend class Item;
virtual void update(const UpdateFunctorPointer& functor) = 0;
};
typedef std::shared_ptr<PayloadInterface> PayloadPointer;
Item() {}
~Item() {}
// Main scene / item managment interface Reset/Update/Kill
void resetPayload(const PayloadPointer& payload);
void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Communicate update to the payload
void kill() { _payload.reset(); _key._flags.reset(); } // Kill means forget the payload and key
// Check heuristic key
const ItemKey& getKey() const { return _key; }
// Payload Interface
// Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace())
const Bound getBound() const { return _payload->getBound(); }
// Get the layer where the item belongs. 0 by default meaning NOT LAYERED
int getLayer() const { return _payload->getLayer(); }
// Render call for the item
void render(RenderArgs* args) const { _payload->render(args); }
// Shape Type Interface
const ShapeKey getShapeKey() const { return _payload->getShapeKey(); }
// Access the status
const StatusPointer& getStatus() const { return _payload->getStatus(); }
protected:
PayloadPointer _payload;
ItemKey _key;
friend class Scene;
};
typedef Item::UpdateFunctorInterface UpdateFunctorInterface;
typedef Item::UpdateFunctorPointer UpdateFunctorPointer;
typedef std::vector<UpdateFunctorPointer> UpdateFunctors;
template <class T> class UpdateFunctor : public Item::UpdateFunctorInterface {
public:
typedef std::function<void(T&)> Func;
Func _func;
UpdateFunctor(Func func): _func(func) {}
~UpdateFunctor() {}
};
inline QDebug operator<<(QDebug debug, const Item& item) {
debug << "[Item: _key:" << item.getKey() << ", bounds:" << item.getBound() << "]";
return debug;
}
// THe Payload class is the real Payload to be used
// THis allow anything to be turned into a Payload as long as the required interface functions are available
// When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff"
// of the Payload interface
template <class T> const ItemKey payloadGetKey(const std::shared_ptr<T>& payloadData) { return ItemKey(); }
template <class T> const Item::Bound payloadGetBound(const std::shared_ptr<T>& payloadData) { return Item::Bound(); }
template <class T> int payloadGetLayer(const std::shared_ptr<T>& payloadData) { return 0; }
template <class T> void payloadRender(const std::shared_ptr<T>& payloadData, RenderArgs* args) { }
// Shape type interface
// This allows shapes to characterize their pipeline via a ShapeKey, to be picked with a subclass of Shape.
// When creating a new shape payload you need to create a specialized version, or the ShapeKey will be ownPipeline,
// implying that the shape will setup its own pipeline without the use of the ShapeKey.
template <class T> const ShapeKey shapeGetShapeKey(const std::shared_ptr<T>& payloadData) { return ShapeKey::Builder::ownPipeline(); }
template <class T> class Payload : public Item::PayloadInterface {
public:
typedef std::shared_ptr<T> DataPointer;
typedef UpdateFunctor<T> Updater;
Payload(const DataPointer& data) : _data(data) {}
// Payload general interface
virtual const ItemKey getKey() const { return payloadGetKey<T>(_data); }
virtual const Item::Bound getBound() const { return payloadGetBound<T>(_data); }
virtual int getLayer() const { return payloadGetLayer<T>(_data); }
virtual void render(RenderArgs* args) { payloadRender<T>(_data, args); }
// Shape Type interface
virtual const ShapeKey getShapeKey() const { return shapeGetShapeKey<T>(_data); }
protected:
DataPointer _data;
// Update mechanics
virtual void update(const UpdateFunctorPointer& functor) { std::static_pointer_cast<Updater>(functor)->_func((*_data)); }
friend class Item;
};
// Let's show how to make a simple FooPayload example:
/*
class Foo {
public:
mutable ItemKey _myownKey;
void makeMywnKey() const {
_myownKey = ItemKey::Builder().withTypeShape().build();
}
const Item::Bound evaluateMyBound() {
// Do stuff here to get your final Bound
return Item::Bound();
}
};
typedef Payload<Foo> FooPayload;
typedef std::shared_ptr<Foo> FooPointer;
// In a Source file, not a header, implement the Payload interface function specialized for Foo:
template <> const ItemKey payloadGetKey(const FooPointer& foo) {
// Foo's way of provinding its Key
foo->makeMyKey();
return foo->_myownKey;
}
template <> const Item::Bound payloadGetBound(const FooPointer& foo) {
// evaluate Foo's own bound
return foo->evaluateMyBound();
}
// In this example, do not specialize the payloadRender call which means the compiler will use the default version which does nothing
*/
// End of the example
typedef Item::PayloadPointer PayloadPointer;
typedef std::vector< PayloadPointer > Payloads;
// A few typedefs for standard containers of ItemIDs
typedef Item::ID ItemID;
typedef std::vector<ItemID> ItemIDs;
typedef std::set<ItemID> ItemIDSet;
class ItemIDAndBounds {
public:
ItemIDAndBounds(ItemID id) : id(id) { }
ItemIDAndBounds(ItemID id, const AABox& bounds) : id(id), bounds(bounds) { }
ItemID id;
AABox bounds;
};
// A list of items to be passed between rendering jobs
using ItemIDsBounds = std::vector<ItemIDAndBounds>;
// A map of items by ShapeKey to optimize rendering pipeline assignments
using ShapesIDsBounds = std::unordered_map<ShapeKey, ItemIDsBounds, ShapeKey::Hash, ShapeKey::KeyEqual>;
// A map of ItemIDSets allowing to create bucket lists of items which are filtering correctly
// A map of ItemIDSets allowing to create bucket lists of items which are filtered according to their key
class ItemBucketMap : public std::map<ItemFilter, ItemIDSet, ItemFilter::Less> {
public:
@ -501,8 +77,8 @@ public:
/// Enqueue change batch to the scene
void enqueuePendingChanges(const PendingChanges& pendingChanges);
/// Access the main bucketmap of items
const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; }
// Process the penging changes equeued
void processPendingChangesQueue();
/// Access a particular item form its ID
/// WARNING, There is No check on the validity of the ID, so this could return a bad Item
@ -510,11 +86,11 @@ public:
size_t getNumItems() const { return _items.size(); }
/// Access the main bucketmap of items
const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; }
void processPendingChangesQueue();
Octree _spatialTree;
/// Access the item spatial tree
const ItemSpatialTree& getSpatialTree() const { return _masterSpatialTree; }
protected:
// Thread safe elements that can be accessed from anywhere
@ -527,6 +103,8 @@ protected:
std::mutex _itemsMutex;
Item::Vector _items;
ItemBucketMap _masterBucketMap;
ItemSpatialTree _masterSpatialTree;
void resetItems(const ItemIDs& ids, Payloads& payloads);
void removeItems(const ItemIDs& ids);
@ -535,8 +113,6 @@ protected:
friend class Engine;
};
typedef std::shared_ptr<Scene> ScenePointer;
typedef std::vector<ScenePointer> Scenes;