Merge branch 'team-teaching' of https://github.com/highfidelity/hifi into simple_program

This commit is contained in:
Atlante45 2015-05-22 17:21:50 +02:00
commit 9561086d0d
2 changed files with 211 additions and 69 deletions

View file

@ -12,25 +12,58 @@
using namespace render;
void Scene::PendingChanges::resetItem(ID id, PayloadPointer& payload) {
void ItemBucketMap::insert(const ItemID& id, const ItemKey& key) {
// Insert the itemID in every bucket where it filters true
for (auto& bucket : (*this)) {
if (key.filterTest(bucket.first)) {
bucket.second.insert(id);
}
}
}
void ItemBucketMap::erase(const ItemID& id, const ItemKey& key) {
// Remove the itemID in every bucket where it filters true
for (auto& bucket : (*this)) {
if (key.filterTest(bucket.first)) {
bucket.second.erase(id);
}
}
}
void ItemBucketMap::reset(const ItemID& id, const ItemKey& oldKey, const ItemKey& newKey) {
// Reset the itemID in every bucket,
// Remove from the buckets where oldKey filters true AND newKey filters false
// Insert into the buckets where newKey filters true
for (auto& bucket : (*this)) {
if (oldKey.filterTest(bucket.first)) {
if (!newKey.filterTest(bucket.first)) {
bucket.second.erase(id);
}
} else if (newKey.filterTest(bucket.first)) {
bucket.second.insert(id);
}
}
}
void Scene::PendingChanges::resetItem(ItemID id, PayloadPointer& payload) {
_resetItems.push_back(id);
_resetPayloads.push_back(payload);
}
void Scene::PendingChanges::removeItem(ID id) {
void Scene::PendingChanges::removeItem(ItemID id) {
_removedItems.push_back(id);
}
void Scene::PendingChanges::moveItem(ID id) {
void Scene::PendingChanges::moveItem(ItemID id) {
_movedItems.push_back(id);
}
void Scene::PendingChanges::mergeBatch(PendingChanges& newBatch) {
_resetItems.insert(_resetItems.end(), newBatch._resetItems.begin(), newBatch._resetItems.end());
_resetPayloads.insert(_resetPayloads.end(), newBatch._resetPayloads.begin(), newBatch._resetPayloads.end());
_removedItems.insert(_removedItems.end(), newBatch._removedItems.begin(), newBatch._removedItems.end());
_movedItems.insert(_movedItems.end(), newBatch._movedItems.begin(), newBatch._movedItems.end());
void Scene::PendingChanges::merge(PendingChanges& changes) {
_resetItems.insert(_resetItems.end(), changes._resetItems.begin(), changes._resetItems.end());
_resetPayloads.insert(_resetPayloads.end(), changes._resetPayloads.begin(), changes._resetPayloads.end());
_removedItems.insert(_removedItems.end(), changes._removedItems.begin(), changes._removedItems.end());
_movedItems.insert(_movedItems.end(), changes._movedItems.begin(), changes._movedItems.end());
}
Scene::Scene() :
@ -38,7 +71,7 @@ Scene::Scene() :
{
}
Item::ID Scene::allocateID() {
ItemID Scene::allocateID() {
// Just increment and return the proevious value initialized at 0
return _IDAllocator.fetch_add(1);
}
@ -53,7 +86,7 @@ void Scene::enqueuePendingChanges(const PendingChanges& pendingChanges) {
void consolidateChangeQueue(Scene::PendingChangesQueue& queue, Scene::PendingChanges& singleBatch) {
while (!queue.empty()) {
auto pendingChanges = queue.front();
singleBatch.mergeBatch(pendingChanges);
singleBatch.merge(pendingChanges);
queue.pop();
};
}
@ -65,9 +98,9 @@ void Scene::processPendingChangesQueue() {
_changeQueueMutex.unlock();
_itemsMutex.lock();
// Here we should be able to check the value of last ID allocated
// Here we should be able to check the value of last ItemID allocated
// and allocate new items accordingly
ID maxID = _IDAllocator.load();
ItemID maxID = _IDAllocator.load();
if (maxID > _items.size()) {
_items.resize(maxID + 100); // allocate the maxId and more
}
@ -85,12 +118,18 @@ void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) {
auto resetID = ids.begin();
auto resetPayload = payloads.begin();
for (;resetID != ids.end(); resetID++, resetPayload++) {
_items[(*resetID)].resetPayload(*resetPayload);
auto item = _items[(*resetID)];
auto oldKey = item.getKey();
item.resetPayload(*resetPayload);
_buckets.reset((*resetID), oldKey, item.getKey());
}
}
void Scene::removeItems(const ItemIDs& ids) {
for (auto removedID :ids) {
_buckets.erase(removedID, _items[removedID].getKey());
_items[removedID].kill();
}
}

View file

@ -15,6 +15,7 @@
#include <bitset>
#include <memory>
#include <vector>
#include <set>
#include <map>
#include <AABox.h>
#include <atomic>
@ -25,16 +26,9 @@ namespace render {
class Context;
class Item {
// Key is the KEY to filter Items and create specialized lists
class ItemKey {
public:
typedef std::vector<Item> Vector;
typedef unsigned int ID;
// State is the KEY to filter Items and create specialized lists
enum FlagBit {
TYPE_SHAPE = 0, // Item is a Shape
TYPE_LIGHT, // Item is a Light
@ -46,9 +40,77 @@ public:
SHADOW_CASTER, // Item cast shadows
PICKABLE, // Item can be picked/selected
INVERT_FLAG, // If true then the meaning of the other flags is inverted
NUM_FLAGS, // Not a valid flag
};
typedef std::bitset<NUM_FLAGS> State;
typedef std::bitset<NUM_FLAGS> Flags;
// THe key is the Flags
Flags _flags;
ItemKey() : _flags(0) {}
ItemKey(const Flags& flags) : _flags(flags) {}
class Builder {
Flags _flags;
public:
Builder() {}
const ItemKey& build() const { return ItemKey(_flags); }
Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); }
Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); }
Builder& withTranslucent() { _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); }
};
bool isOpaque() const { return !_flags[TRANSLUCENT]; }
bool isTranslucent() 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 isUnvisible() const { return _flags[INVISIBLE]; }
bool isShadowCaster() const { return _flags[SHADOW_CASTER]; }
bool isPickable() const { return _flags[PICKABLE]; }
// Item Key operator testing if a key pass a filter test
// the filter can have several flags on and the test is true if
// (key AND filter) == filter
// IF the filter has the INVERT_FLAGS On then we need to use the
// !key value instead of key
bool filterTest(const ItemKey& filter) const {
if (filter._flags[INVERT_FLAG]) {
return (filter._flags & ~_flags) == filter._flags;
} else {
return (filter._flags & _flags) == filter._flags;
}
}
};
class RenderArgs;
class Item {
public:
typedef std::vector<Item> Vector;
typedef unsigned int ID;
// Bound is the AABBox fully containing this item
typedef AABox Bound;
@ -63,9 +125,9 @@ public:
// Payload is whatever is in this Item and implement the Payload Interface
class PayloadInterface {
public:
virtual const State getState() const = 0;
virtual const ItemKey getKey() const = 0;
virtual const Bound getBound() const = 0;
virtual void render(Context& context) = 0;
virtual void render(RenderArgs* args) = 0;
~PayloadInterface() {}
protected:
@ -83,66 +145,106 @@ public:
void kill();
void move();
// Check heuristic flags of the state
const State& getState() const { return _state; }
// Check heuristic key
const ItemKey& getKey() const { return _key; }
bool isOpaque() const { return !_state[TRANSLUCENT]; }
bool isTranslucent() const { return _state[TRANSLUCENT]; }
bool isWorldSpace() const { return !_state[VIEW_SPACE]; }
bool isViewSpace() const { return _state[VIEW_SPACE]; }
bool isStatic() const { return !_state[DYNAMIC]; }
bool isDynamic() const { return _state[DYNAMIC]; }
bool isDeformed() const { return _state[DEFORMED]; }
bool isVisible() const { return !_state[INVISIBLE]; }
bool isUnvisible() const { return _state[INVISIBLE]; }
bool isShadowCaster() const { return _state[SHADOW_CASTER]; }
bool isPickable() const { return _state[PICKABLE]; }
// Payload Interface
const Bound getBound() const { return _payload->getBound(); }
void render(Context& context) { _payload->render(context); }
void render(RenderArgs* args) { _payload->render(args); }
protected:
PayloadPointer _payload;
State _state = 0;
ItemKey _key;
friend class Scene;
};
template <class T> const Item::State payloadGetState(const T* payload) { return Item::State(); }
template <class T> const Item::Bound payloadGetBound(const T* payload) { return Item::Bound(); }
template <class T> void payloadRender(const T* payload) { }
// 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> void payloadRender(const std::shared_ptr<T>& payloadData, RenderArgs* args) { }
template <class T> class ItemPayload : public Item::PayloadInterface {
template <class T> class Payload : public Item::PayloadInterface {
public:
virtual const Item::State getState() const { return payloadGetState<T>(_pointee); }
virtual const Item::Bound getBound() const { return payloadGetBound<T>(_pointee); }
virtual void render(Context& context) { payloadRender<T>(_pointee, context); }
typedef std::shared_ptr<T> DataPointer;
virtual const ItemKey getKey() const { return payloadGetKey<T>(_data); }
virtual const Item::Bound getBound() const { return payloadGetBound<T>(_data); }
virtual void render(RenderArgs* args) { payloadRender<T>(_data, args); }
ItemPayload(std::shared_ptr<T>& pointee) : _pointee(pointee) {}
Payload(DataPointer& data) : _data(data) {}
protected:
std::shared_ptr<T> _pointee;
DataPointer _data;
};
typedef Item::PayloadInterface Payload;
// 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;
// A map of ItemIDSets allowing to create bucket lists of items which are filtering correctly
class ItemBucketMap : public std::map<ItemKey, ItemIDSet> {
public:
ItemBucketMap() {}
void insert(const ItemID& id, const ItemKey& key);
void erase(const ItemID& id, const ItemKey& key);
void reset(const ItemID& id, const ItemKey& oldKey, const ItemKey& newKey);
};
class Engine;
class Observer;
// Scene is a container for Items
// Items are introduced, modified or erased in the scene through PendingChanges
// Once per Frame, the PendingChanges are all flushed
// During the flush the standard buckets are updated
// Items are notified accordingly on any update message happening
class Scene {
public:
typedef Item::Vector Items;
typedef Item::ID ID;
typedef std::vector<Item::ID> ItemIDs;
typedef std::map<Item::State, ItemIDs> ItemLists;
class Observer {
public:
Observer(Scene* scene) {
@ -177,11 +279,11 @@ public:
PendingChanges() {}
~PendingChanges();
void resetItem(ID id, PayloadPointer& payload);
void removeItem(ID id);
void moveItem(ID id);
void resetItem(ItemID id, PayloadPointer& payload);
void removeItem(ItemID id);
void moveItem(ItemID id);
void mergeBatch(PendingChanges& newBatch);
void merge(PendingChanges& changes);
Payloads _resetPayloads;
ItemIDs _resetItems;
@ -196,7 +298,7 @@ public:
~Scene() {}
/// This call is thread safe, can be called from anywhere to allocate a new ID
ID allocateID();
ItemID allocateID();
/// Enqueue change batch to the scene
void enqueuePendingChanges(const PendingChanges& pendingChanges);
@ -214,14 +316,15 @@ protected:
// The actual database
// database of items is protected for editing by a mutex
std::mutex _itemsMutex;
Items _items;
ItemLists _buckets;
Item::Vector _items;
ItemBucketMap _buckets;
void processPendingChangesQueue();
void resetItems(const ItemIDs& ids, Payloads& payloads);
void removeItems(const ItemIDs& ids);
void moveItems(const ItemIDs& ids);
// The scene context listening for any change to the database
Observers _observers;