]> git.tdb.fi Git - libs/gl.git/blobdiff - source/render/instancearray.h
Redesign InstanceArray
[libs/gl.git] / source / render / instancearray.h
index e6f8690c68cd5b2000d25f9bb7d770f063fcc1f4..f2a9b2c00be5930c281a8341851d838af949ba1f 100644 (file)
@@ -15,8 +15,96 @@ class Buffer;
 class Object;
 class ObjectInstance;
 
 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.
+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
 
 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
@@ -24,11 +112,19 @@ 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.
 
 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.
+
+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, public NonCopyable
+template<typename T = ObjectInstance>
+class InstanceArray: public InstanceArrayBase
 {
 {
-public:
-       template<typename T>
+private:
        class Instance: public T
        {
        private:
        class Instance: public T
        {
        private:
@@ -41,47 +137,20 @@ public:
                virtual void set_matrix(const Matrix &);
        };
 
                virtual void set_matrix(const Matrix &);
        };
 
-private:
-       const Object &object;
-       std::vector<ObjectInstance *> instances;
-       VertexArray instance_data;
-       Buffer *instance_buffer = 0;
-       VertexSetup vtx_setup;
-       int matrix_location = -1;
-       unsigned matrix_offset = 0;
-
 public:
 public:
-       InstanceArray(const Object &);
-       ~InstanceArray();
-
-       void set_matrix_attribute(const std::string &);
+       InstanceArray(const Object &o): InstanceArrayBase(o, sizeof(Instance)) { }
+       ~InstanceArray() { destroy_all<T>(); }
 
 
-       /** Adds a new instance to the array.  The instance class must have a
-       constructor taking a const reference to Object as its sole parameter. */
-       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>
 
 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);
 {
        T::set_matrix(m);
-       array.update_instance_matrix(index);
+       array.update_instance_matrix(index, *this->get_matrix());
 }
 
 } // namespace GL
 }
 
 } // namespace GL