mirror of
https://github.com/JulianGro/overte.git
synced 2025-08-06 08:42:13 +02:00
635 lines
27 KiB
C++
635 lines
27 KiB
C++
//
|
|
// 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 "Args.h"
|
|
|
|
#include <graphics/Material.h>
|
|
#include "ShapePipeline.h"
|
|
|
|
namespace render {
|
|
|
|
typedef int32_t Index;
|
|
const Index INVALID_INDEX{ -1 };
|
|
|
|
class Context;
|
|
|
|
// Key is the KEY to filter Items and create specialized lists
|
|
class ItemKey {
|
|
public:
|
|
// 8 tags are available to organize the items and filter them against as fields of the ItemKey.
|
|
// TAG & TAG_BITS are defined from several bits in the Key.
|
|
// An Item can be tagged and filtering can rely on the tags to keep or exclude items
|
|
// ItemKey are not taged by default
|
|
enum Tag : uint8_t {
|
|
TAG_0 = 0, // 8 Tags
|
|
TAG_1,
|
|
TAG_2,
|
|
TAG_3,
|
|
TAG_4,
|
|
TAG_5,
|
|
TAG_6,
|
|
TAG_7,
|
|
|
|
NUM_TAGS,
|
|
|
|
// Tag bits are derived from the Tag enum
|
|
TAG_BITS_ALL = 0xFF,
|
|
TAG_BITS_NONE = 0x00,
|
|
TAG_BITS_0 = 0x01,
|
|
TAG_BITS_1 = 0x02,
|
|
TAG_BITS_2 = 0x04,
|
|
TAG_BITS_3 = 0x08,
|
|
TAG_BITS_4 = 0x10,
|
|
TAG_BITS_5 = 0x20,
|
|
TAG_BITS_6 = 0x40,
|
|
TAG_BITS_7 = 0x80,
|
|
};
|
|
|
|
// Items are organized in layers, an item belongs to one of the 8 Layers available.
|
|
// By default an item is in the 'LAYER_DEFAULT' meaning that it is NOT layered.
|
|
// THere is NO ordering relationship between layers.
|
|
enum Layer : uint8_t {
|
|
LAYER_DEFAULT = 0, // layer 0 aka Default is a 'NOT' layer, items are not considered layered, this is the default value
|
|
LAYER_1,
|
|
LAYER_2,
|
|
LAYER_3,
|
|
LAYER_4,
|
|
LAYER_5,
|
|
LAYER_6,
|
|
LAYER_BACKGROUND, // Last Layer is the background by convention
|
|
|
|
NUM_LAYERS,
|
|
|
|
// Layer bits are derived from the Layer enum, the number of bits needed to represent integer 0 to NUM_LAYERS
|
|
NUM_LAYER_BITS = 3,
|
|
LAYER_BITS_ALL = 0x07,
|
|
};
|
|
|
|
enum FlagBit : uint32_t {
|
|
TYPE_SHAPE = 0, // Item is a Shape: Implements the Shape Interface that draw a Geometry rendered with a Material
|
|
TYPE_LIGHT, // Item is a Light: Implements the Light Interface that
|
|
TYPE_CAMERA, // Item is a Camera: Implements the Camera Interface
|
|
TYPE_META, // Item is a Meta: meanning it s used to represent a higher level object, potentially represented by other render items
|
|
|
|
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 in the scene?
|
|
SHADOW_CASTER, // Item cast shadows
|
|
META_CULL_GROUP, // As a meta item, the culling of my sub items is based solely on my bounding box and my visibility in the view
|
|
SUB_META_CULLED, // As a sub item of a meta render item set as cull group, need to be set to my culling to the meta render it
|
|
|
|
FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against
|
|
LAST_TAG_BIT = FIRST_TAG_BIT + NUM_TAGS,
|
|
|
|
FIRST_LAYER_BIT, // 8 Exclusive Layers (encoded in 3 bits) available to organize the items in layers, an item can only belong to ONE layer
|
|
LAST_LAYER_BIT = FIRST_LAYER_BIT + NUM_LAYER_BITS,
|
|
|
|
__SMALLER, // Reserved bit for spatialized item to indicate that it is smaller than expected in the cell in which it belongs (probably because it overlaps over several smaller cells)
|
|
|
|
NUM_FLAGS, // Not a valid flag
|
|
};
|
|
typedef std::bitset<NUM_FLAGS> Flags;
|
|
|
|
// All the bits touching tag bits sets to true
|
|
const static uint32_t KEY_TAG_BITS_MASK;
|
|
static uint32_t evalTagBitsWithKeyBits(uint8_t tagBits, const uint32_t keyBits) {
|
|
return (keyBits & ~KEY_TAG_BITS_MASK) | (((uint32_t)tagBits) << FIRST_TAG_BIT);
|
|
}
|
|
|
|
// All the bits touching layer bits sets to true
|
|
const static uint32_t KEY_LAYER_BITS_MASK;
|
|
static uint32_t evalLayerBitsWithKeyBits(uint8_t layer, const uint32_t keyBits) {
|
|
return (keyBits & ~KEY_LAYER_BITS_MASK) | (((uint32_t)layer & LAYER_BITS_ALL) << FIRST_LAYER_BIT);
|
|
}
|
|
|
|
// The key is the Flags
|
|
Flags _flags;
|
|
|
|
ItemKey() : _flags(0) {}
|
|
ItemKey(const Flags& flags) : _flags(flags) {}
|
|
|
|
bool operator== (const ItemKey& rhs) const { return _flags == rhs._flags; }
|
|
bool operator!= (const ItemKey& rhs) const { return _flags != rhs._flags; }
|
|
|
|
class Builder {
|
|
friend class ItemKey;
|
|
Flags _flags{ 0 };
|
|
public:
|
|
Builder() {}
|
|
Builder(const ItemKey& key) : _flags{ key._flags } {}
|
|
|
|
ItemKey build() const { return ItemKey(_flags); }
|
|
|
|
Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); }
|
|
Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); }
|
|
Builder& withTypeMeta() { _flags.set(TYPE_META); return (*this); }
|
|
Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); }
|
|
Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); }
|
|
Builder& withoutViewSpace() { _flags.reset(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& withVisible() { _flags.reset(INVISIBLE); return (*this); }
|
|
Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); }
|
|
Builder& withoutShadowCaster() { _flags.reset(SHADOW_CASTER); return (*this); }
|
|
Builder& withMetaCullGroup() { _flags.set(META_CULL_GROUP); return (*this); }
|
|
Builder& withoutMetaCullGroup() { _flags.reset(META_CULL_GROUP); return (*this); }
|
|
Builder& withSubMetaCulled() { _flags.set(SUB_META_CULLED); return (*this); }
|
|
Builder& withoutSubMetaCulled() { _flags.reset(SUB_META_CULLED); return (*this); }
|
|
|
|
Builder& withTag(Tag tag) { _flags.set(FIRST_TAG_BIT + tag); return (*this); }
|
|
// Set ALL the tags in one call using the Tag bits
|
|
Builder& withTagBits(uint8_t tagBits) { _flags = evalTagBitsWithKeyBits(tagBits, _flags.to_ulong()); return (*this); }
|
|
|
|
Builder& withLayer(uint8_t layer) { _flags = evalLayerBitsWithKeyBits(layer, _flags.to_ulong()); return (*this); }
|
|
Builder& withoutLayer() { return withLayer(LAYER_DEFAULT); }
|
|
|
|
// 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().withLayer(LAYER_BACKGROUND); }
|
|
};
|
|
ItemKey(const Builder& builder) : ItemKey(builder._flags) {}
|
|
|
|
bool isShape() const { return _flags[TYPE_SHAPE]; }
|
|
bool isLight() const { return _flags[TYPE_LIGHT]; }
|
|
bool isMeta() const { return _flags[TYPE_META]; }
|
|
|
|
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 isMetaCullGroup() const { return _flags[META_CULL_GROUP]; }
|
|
void setMetaCullGroup(bool cullGroup) { (cullGroup ? _flags.set(META_CULL_GROUP) : _flags.reset(META_CULL_GROUP)); }
|
|
|
|
bool isSubMetaCulled() const { return _flags[SUB_META_CULLED]; }
|
|
void setSubMetaCulled(bool metaCulled) { (metaCulled ? _flags.set(SUB_META_CULLED) : _flags.reset(SUB_META_CULLED)); }
|
|
|
|
bool isTag(Tag tag) const { return _flags[FIRST_TAG_BIT + tag]; }
|
|
uint8_t getTagBits() const { return ((_flags.to_ulong() & KEY_TAG_BITS_MASK) >> FIRST_TAG_BIT); }
|
|
|
|
uint8_t getLayer() const { return ((_flags.to_ulong() & KEY_LAYER_BITS_MASK) >> FIRST_LAYER_BIT); }
|
|
bool isLayer(uint8_t layer) const { return getLayer() == layer; }
|
|
bool isLayered() const { return getLayer() != LAYER_DEFAULT; }
|
|
bool isSpatial() const { return !isLayered(); }
|
|
|
|
// Probably not public, flags used by the scene
|
|
bool isSmall() const { return _flags[__SMALLER]; }
|
|
void setSmaller(bool smaller) { (smaller ? _flags.set(__SMALLER) : _flags.reset(__SMALLER)); }
|
|
|
|
bool operator==(const ItemKey& key) { return (_flags == key._flags); }
|
|
bool operator!=(const ItemKey& key) { return (_flags != key._flags); }
|
|
};
|
|
using ItemKeys = std::vector<ItemKey>;
|
|
|
|
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() {}
|
|
Builder(const ItemFilter& srcFilter) : _value(srcFilter._value), _mask(srcFilter._mask) {}
|
|
|
|
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& withTypeMeta() { _value.set(ItemKey::TYPE_META); _mask.set(ItemKey::TYPE_META); 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& withoutMetaCullGroup() { _value.reset(ItemKey::META_CULL_GROUP); _mask.set(ItemKey::META_CULL_GROUP); return (*this); }
|
|
Builder& withMetaCullGroup() { _value.set(ItemKey::META_CULL_GROUP); _mask.set(ItemKey::META_CULL_GROUP); return (*this); }
|
|
|
|
Builder& withoutSubMetaCulled() { _value.reset(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); return (*this); }
|
|
Builder& withSubMetaCulled() { _value.set(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); return (*this); }
|
|
|
|
Builder& withoutTag(ItemKey::Tag tagIndex) { _value.reset(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); }
|
|
Builder& withTag(ItemKey::Tag tagIndex) { _value.set(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); }
|
|
// Set ALL the tags in one call using the Tag bits and the Tag bits touched
|
|
Builder& withTagBits(uint8_t tagBits, uint8_t tagMask) { _value = ItemKey::evalTagBitsWithKeyBits(tagBits, _value.to_ulong()); _mask = ItemKey::evalTagBitsWithKeyBits(tagMask, _mask.to_ulong()); return (*this); }
|
|
|
|
Builder& withoutLayered() { _value = ItemKey::evalLayerBitsWithKeyBits(ItemKey::LAYER_DEFAULT, _value.to_ulong()); _mask |= ItemKey::KEY_LAYER_BITS_MASK; return (*this); }
|
|
Builder& withLayer(uint8_t layer) { _value = ItemKey::evalLayerBitsWithKeyBits(layer, _value.to_ulong()); _mask |= ItemKey::KEY_LAYER_BITS_MASK; return (*this); }
|
|
|
|
Builder& withNothing() { _value.reset(); _mask.reset(); return (*this); }
|
|
|
|
// Convenient standard keys that we will keep on using all over the place
|
|
static Builder visibleWorldItems() { return Builder().withVisible().withWorldSpace(); }
|
|
static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); }
|
|
static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); }
|
|
static Builder light() { return Builder().withTypeLight(); }
|
|
static Builder meta() { return Builder().withTypeMeta(); }
|
|
static Builder background() { return Builder().withViewSpace().withLayer(ItemKey::LAYER_BACKGROUND); }
|
|
static Builder nothing() { return Builder().withNothing(); }
|
|
};
|
|
|
|
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); }
|
|
bool selectsNothing() const { return !_mask.any(); }
|
|
|
|
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;
|
|
}
|
|
|
|
// Handy type to just pass the ID and the bound of an item
|
|
class ItemBound {
|
|
public:
|
|
ItemBound() {}
|
|
ItemBound(ItemID id) : id(id) { }
|
|
ItemBound(ItemID id, const AABox& bound) : id(id), bound(bound) { }
|
|
|
|
ItemID id;
|
|
AABox bound;
|
|
uint32_t padding;
|
|
};
|
|
|
|
// many Item Bounds in a vector
|
|
using ItemBounds = std::vector<ItemBound>;
|
|
|
|
// Item is the proxy to a bounded "object" in the scene
|
|
// An item is described by its Key
|
|
class Item {
|
|
public:
|
|
typedef std::vector<Item> Vector;
|
|
typedef ItemID ID;
|
|
|
|
static const ID INVALID_ITEM_ID;
|
|
static const ItemCell INVALID_CELL;
|
|
|
|
// Convenient function to clear an ID or check it s valid
|
|
static void clearID(ID& id) { id = INVALID_ITEM_ID; }
|
|
static bool isValidID(const ID id) { return id != INVALID_ITEM_ID; }
|
|
|
|
// 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:
|
|
|
|
enum class Icon {
|
|
ACTIVE_IN_BULLET = 0,
|
|
PACKET_SENT = 1,
|
|
PACKET_RECEIVED = 2,
|
|
SIMULATION_OWNER = 3,
|
|
HAS_ACTIONS = 4,
|
|
OTHER_SIMULATION_OWNER = 5,
|
|
ENTITY_HOST_TYPE = 6,
|
|
GENERIC_TRANSITION = 7,
|
|
GENERIC_TRANSITION_OUT = 8,
|
|
GENERIC_TRANSITION_IN = 9,
|
|
USER_TRANSITION_OUT = 10,
|
|
USER_TRANSITION_IN = 11,
|
|
NONE = 255
|
|
};
|
|
|
|
// 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 void render(RenderArgs* args) = 0;
|
|
|
|
virtual const ShapeKey getShapeKey() const = 0;
|
|
|
|
virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) 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() {}
|
|
|
|
// Item exists if it has a valid payload
|
|
bool exist() const { return (bool)(_payload); }
|
|
|
|
// Main scene / item managment interface reset/update/kill
|
|
void resetPayload(const PayloadPointer& payload);
|
|
void resetCell(ItemCell cell = INVALID_CELL, bool _small = false) { _cell = cell; _key.setSmaller(_small); }
|
|
void update(const UpdateFunctorPointer& updateFunctor); // communicate update to payload
|
|
void kill() { _payload.reset(); resetCell(); _key._flags.reset(); } // forget the payload, key, cell
|
|
|
|
// Check heuristic key
|
|
const ItemKey& getKey() const { return _key; }
|
|
|
|
// Check spatial cell
|
|
const ItemCell& getCell() const { return _cell; }
|
|
|
|
// 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, simply reflecting the key.
|
|
int getLayer() const { return _key.getLayer(); }
|
|
|
|
// Render call for the item
|
|
void render(RenderArgs* args) const { _payload->render(args); }
|
|
|
|
// Shape Type Interface
|
|
const ShapeKey getShapeKey() const;
|
|
|
|
// Meta Type Interface
|
|
uint32_t fetchMetaSubItems(ItemIDs& subItems) const { return _payload->fetchMetaSubItems(subItems); }
|
|
uint32_t fetchMetaSubItemBounds(ItemBounds& subItemBounds, Scene& scene) const;
|
|
|
|
// Access the status
|
|
const StatusPointer& getStatus() const { return _payload->getStatus(); }
|
|
|
|
void setTransitionId(Index id) { _transitionId = id; }
|
|
Index getTransitionId() const { return _transitionId; }
|
|
|
|
protected:
|
|
PayloadPointer _payload;
|
|
ItemKey _key;
|
|
ItemCell _cell { INVALID_CELL };
|
|
Index _transitionId { INVALID_INDEX };
|
|
|
|
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;
|
|
}
|
|
|
|
// Item shared interface supported by the payload
|
|
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> 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(); }
|
|
|
|
// Meta Type Interface
|
|
// Meta items act as the grouping object for several sub items (typically shapes).
|
|
template <class T> uint32_t metaFetchMetaSubItems(const std::shared_ptr<T>& payloadData, ItemIDs& subItems) { return 0; }
|
|
|
|
// 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> class Payload : public Item::PayloadInterface {
|
|
public:
|
|
typedef std::shared_ptr<T> DataPointer;
|
|
typedef UpdateFunctor<T> Updater;
|
|
|
|
Payload(const DataPointer& data) : _data(data) {}
|
|
virtual ~Payload() = default;
|
|
|
|
// Payload general interface
|
|
virtual const ItemKey getKey() const override { return payloadGetKey<T>(_data); }
|
|
virtual const Item::Bound getBound() const override { return payloadGetBound<T>(_data); }
|
|
|
|
virtual void render(RenderArgs* args) override { payloadRender<T>(_data, args); }
|
|
|
|
// Shape Type interface
|
|
virtual const ShapeKey getShapeKey() const override { return shapeGetShapeKey<T>(_data); }
|
|
|
|
// Meta Type Interface
|
|
virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const override { return metaFetchMetaSubItems<T>(_data, subItems); }
|
|
|
|
protected:
|
|
DataPointer _data;
|
|
|
|
// Update mechanics
|
|
virtual void update(const UpdateFunctorPointer& functor) override {
|
|
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
|
|
|
|
class PayloadProxyInterface {
|
|
public:
|
|
using ProxyPayload = Payload<PayloadProxyInterface>;
|
|
using Pointer = ProxyPayload::DataPointer;
|
|
|
|
virtual ItemKey getKey() = 0;
|
|
virtual ShapeKey getShapeKey() = 0;
|
|
virtual Item::Bound getBound() = 0;
|
|
virtual void render(RenderArgs* args) = 0;
|
|
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const = 0;
|
|
};
|
|
|
|
template <> const ItemKey payloadGetKey(const PayloadProxyInterface::Pointer& payload);
|
|
template <> const Item::Bound payloadGetBound(const PayloadProxyInterface::Pointer& payload);
|
|
template <> void payloadRender(const PayloadProxyInterface::Pointer& payload, RenderArgs* args);
|
|
template <> uint32_t metaFetchMetaSubItems(const PayloadProxyInterface::Pointer& payload, ItemIDs& subItems);
|
|
template <> const ShapeKey shapeGetShapeKey(const PayloadProxyInterface::Pointer& payload);
|
|
|
|
|
|
typedef Item::PayloadPointer PayloadPointer;
|
|
typedef std::vector<PayloadPointer> Payloads;
|
|
|
|
// A map of items by ShapeKey to optimize rendering pipeline assignments
|
|
using ShapeBounds = std::unordered_map<ShapeKey, ItemBounds, ShapeKey::Hash, ShapeKey::KeyEqual>;
|
|
|
|
}
|
|
|
|
#endif // hifi_render_Item_h
|