#define MSP_GL_INSTANCEARRAY_H_
#include <vector>
+#include <msp/core/noncopyable.h>
#include "programdata.h"
#include "renderable.h"
#include "vertexarray.h"
class Object;
class ObjectInstance;
+class InstanceArrayBase: public Renderable, public NonCopyable
+{
+private:
+ struct Block
+ {
+ char *begin = 0;
+ char *end = 0;
+ };
+
+ struct Slot
+ {
+ bool used = false;
+ union
+ {
+ std::uint16_t array_index;
+ std::uint16_t next_free;
+ };
+ std::uint16_t block_index;
+ std::uint16_t index_in_block;
+ };
+
+ const Object &object;
+ VertexArray instance_data;
+ Buffer *instance_buffer = 0;
+ VertexSetup vtx_setup;
+ int matrix_location = -1;
+ unsigned matrix_offset = 0;
+ std::size_t instance_size;
+ std::size_t default_count;
+ std::vector<Block> storage;
+ std::vector<Slot> slots;
+ std::vector<int> array_order;
+ int first_free = -1;
+ int last_free = -1;
+ std::size_t instance_count = 0;
+
+protected:
+ InstanceArrayBase(const Object &, std::size_t);
+ ~InstanceArrayBase();
+
+private:
+ void add_block(std::size_t);
+ std::size_t allocate();
+ char *get_address(std::size_t) const;
+ std::size_t find_index(char *) const;
+ void release(std::size_t);
+protected:
+ template<typename T, typename A>
+ T *create(A &);
+
+ template<typename T>
+ void destroy(T *);
+
+ template<typename T>
+ void destroy_all();
+
+ void update_instance_matrix(std::size_t, const Matrix &);
+
+public:
+ std::size_t size() const { return instance_count; }
+
+ virtual void render(Renderer &, Tag) const;
+};
+
+template<typename T, typename A>
+inline T *InstanceArrayBase::create(A &array)
+{
+ size_t index = allocate();
+ return new(get_address(index)) T(object, array, index);
+}
+
+template<typename T>
+inline void InstanceArrayBase::destroy(T *obj)
+{
+ char *addr = reinterpret_cast<char *>(obj);
+ obj->~T();
+ release(find_index(addr));
+}
+
+template<typename T>
+inline void InstanceArrayBase::destroy_all()
+{
+ for(unsigned i=0; i<slots.size(); ++i)
+ if(slots[i].used)
+ reinterpret_cast<T *>(get_address(i))->~T();
+}
+
+
/**
-Renders multiple instances of an Object in an efficient manner. If instanced
-rendering is supported, only one draw call per Batch needs to be issued.
+Stores and renders multiple instances of an Object in an efficient manner.
+
+The instance specific transform is passed to the shader in an attribute with
+the name instance_transform. The attribute should have the type vec4[3]. Each
+elements of the array corresponds to a row of the transform matrix.
+
+If the Mesh or Technique of the Object is changed during the lifetime of the
+InstanceArray, behaviour is undefined.
+
+The instance type must have a constructor accepting a const Object &. If it
+has a virtual function with the signature void set_matrix(const Matrix &), it
+will be used to update the instance matrix. The original function is also
+called.
-Changing the Mesh of the Object while an InstanceArray exists is not supported.
+Instance created by the array have stable addresses. However after an instance
+is removed, its address may later be reused for another instance.
*/
-class InstanceArray: public Renderable
+template<typename T = ObjectInstance>
+class InstanceArray: public InstanceArrayBase
{
-public:
- template<typename T>
+private:
class Instance: public T
{
private:
virtual void set_matrix(const Matrix &);
};
-private:
- const Object &object;
- std::vector<ObjectInstance *> instances;
- VertexArray instance_data;
- Buffer *instance_buffer;
- VertexSetup vtx_setup;
- int matrix_location;
- unsigned matrix_offset;
-
public:
- InstanceArray(const Object &);
- ~InstanceArray();
-
- void set_matrix_attribute(const std::string &);
+ InstanceArray(const Object &o): InstanceArrayBase(o, sizeof(Instance)) { }
+ ~InstanceArray() { destroy_all<T>(); }
- template<typename T = ObjectInstance>
- T &append();
-private:
- void append(ObjectInstance *);
- void update_instance_matrix(unsigned);
-public:
- void remove(ObjectInstance &);
-
- virtual void render(Renderer &, Tag) const;
+ T &append() { return *create<Instance>(*this); }
+ void remove(T &obj) { destroy(&obj); }
};
-template<typename T>
-T &InstanceArray::append()
-{
- Instance<T> *inst = new Instance<T>(object, *this, instances.size());
- append(inst);
- return *inst;
-}
template<typename T>
-void InstanceArray::Instance<T>::set_matrix(const Matrix &m)
+inline void InstanceArray<T>::Instance::set_matrix(const Matrix &m)
{
T::set_matrix(m);
- array.update_instance_matrix(index);
+ array.update_instance_matrix(index, *this->get_matrix());
}
} // namespace GL