]> git.tdb.fi Git - libs/gl.git/blobdiff - source/render/instancearray.h
Check the flat qualifier from the correct member
[libs/gl.git] / source / render / instancearray.h
index 6a598b5854f3183e290896eb052708f32c0f98a6..6f4048ece6ea580dfcdf30f78de4d8388c82d3a5 100644 (file)
@@ -2,8 +2,11 @@
 #define MSP_GL_INSTANCEARRAY_H_
 
 #include <vector>
+#include <msp/core/noncopyable.h>
 #include "programdata.h"
 #include "renderable.h"
+#include "vertexarray.h"
+#include "vertexsetup.h"
 
 namespace Msp {
 namespace GL {
@@ -11,19 +14,143 @@ namespace GL {
 class Buffer;
 class Object;
 class ObjectInstance;
-class VertexArray;
-class VertexSetup;
+
+class InstanceArrayBase: public Renderable, public NonCopyable
+{
+protected:
+       class Loader: public DataFile::ObjectLoader<InstanceArrayBase>
+       {
+       private:
+               static ActionMap shared_actions;
+
+       public:
+               Loader(InstanceArrayBase &);
+
+       private:
+               virtual void init_actions();
+
+       protected:
+               virtual void instance() = 0;
+       };
+
+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.
 
-Changing the Mesh of the Object while an InstanceArray exists is not supported.
+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
+template<typename T = ObjectInstance>
+class InstanceArray: public InstanceArrayBase
 {
 public:
-       template<typename T>
+       class Loader: public DataFile::DerivedObjectLoader<InstanceArray, InstanceArrayBase::Loader>
+       {
+       public:
+               Loader(InstanceArray &a): DataFile::DerivedObjectLoader<InstanceArray, InstanceArrayBase::Loader>(a) { }
+
+       protected:
+               virtual void instance();
+       };
+
+private:
        class Instance: public T
        {
        private:
@@ -36,45 +163,28 @@ public:
                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 &);
-
-       template<typename T = ObjectInstance>
-       T &append();
-private:
-       void append(ObjectInstance *);
-       void update_instance_matrix(unsigned);
 public:
-       void remove(ObjectInstance &);
+       InstanceArray(const Object &o): InstanceArrayBase(o, sizeof(Instance)) { }
+       ~InstanceArray() { destroy_all<T>(); }
 
-       virtual void render(Renderer &, Tag) const;
+       T &append() { return *create<Instance>(*this); }
+       void remove(T &obj) { destroy(&obj); }
 };
 
+
 template<typename T>
-T &InstanceArray::append()
+inline void InstanceArray<T>::Instance::set_matrix(const Matrix &m)
 {
-       Instance<T> *inst = new Instance<T>(object, *this, instances.size());
-       append(inst);
-       return *inst;
+       T::set_matrix(m);
+       array.update_instance_matrix(index, *this->get_matrix());
 }
 
+
 template<typename T>
-void InstanceArray::Instance<T>::set_matrix(const Matrix &m)
+void InstanceArray<T>::Loader::instance()
 {
-       T::set_matrix(m);
-       array.update_instance_matrix(index);
+       T &inst = this->obj.append();
+       this->load_sub(inst);
 }
 
 } // namespace GL