]> git.tdb.fi Git - libs/gl.git/blob - source/render/instancearray.h
Redesign InstanceArray
[libs/gl.git] / source / render / instancearray.h
1 #ifndef MSP_GL_INSTANCEARRAY_H_
2 #define MSP_GL_INSTANCEARRAY_H_
3
4 #include <vector>
5 #include <msp/core/noncopyable.h>
6 #include "programdata.h"
7 #include "renderable.h"
8 #include "vertexarray.h"
9 #include "vertexsetup.h"
10
11 namespace Msp {
12 namespace GL {
13
14 class Buffer;
15 class Object;
16 class ObjectInstance;
17
18 class InstanceArrayBase: public Renderable, public NonCopyable
19 {
20 private:
21         struct Block
22         {
23                 char *begin = 0;
24                 char *end = 0;
25         };
26
27         struct Slot
28         {
29                 bool used = false;
30                 union
31                 {
32                         std::uint16_t array_index;
33                         std::uint16_t next_free;
34                 };
35                 std::uint16_t block_index;
36                 std::uint16_t index_in_block;
37         };
38
39         const Object &object;
40         VertexArray instance_data;
41         Buffer *instance_buffer = 0;
42         VertexSetup vtx_setup;
43         int matrix_location = -1;
44         unsigned matrix_offset = 0;
45         std::size_t instance_size;
46         std::size_t default_count;
47         std::vector<Block> storage;
48         std::vector<Slot> slots;
49         std::vector<int> array_order;
50         int first_free = -1;
51         int last_free = -1;
52         std::size_t instance_count = 0;
53
54 protected:
55         InstanceArrayBase(const Object &, std::size_t);
56         ~InstanceArrayBase();
57
58 private:
59         void add_block(std::size_t);
60         std::size_t allocate();
61         char *get_address(std::size_t) const;
62         std::size_t find_index(char *) const;
63         void release(std::size_t);
64 protected:
65         template<typename T, typename A>
66         T *create(A &);
67
68         template<typename T>
69         void destroy(T *);
70
71         template<typename T>
72         void destroy_all();
73
74         void update_instance_matrix(std::size_t, const Matrix &);
75
76 public:
77         std::size_t size() const { return instance_count; }
78
79         virtual void render(Renderer &, Tag) const;
80 };
81
82 template<typename T, typename A>
83 inline T *InstanceArrayBase::create(A &array)
84 {
85         size_t index = allocate();
86         return new(get_address(index)) T(object, array, index);
87 }
88
89 template<typename T>
90 inline void InstanceArrayBase::destroy(T *obj)
91 {
92         char *addr = reinterpret_cast<char *>(obj);
93         obj->~T();
94         release(find_index(addr));
95 }
96
97 template<typename T>
98 inline void InstanceArrayBase::destroy_all()
99 {
100         for(unsigned i=0; i<slots.size(); ++i)
101                 if(slots[i].used)
102                         reinterpret_cast<T *>(get_address(i))->~T();
103 }
104
105
106 /**
107 Stores and renders multiple instances of an Object in an efficient manner.
108
109 The instance specific transform is passed to the shader in an attribute with
110 the name instance_transform.  The attribute should have the type vec4[3].  Each
111 elements of the array corresponds to a row of the transform matrix.
112
113 If the Mesh or Technique of the Object is changed during the lifetime of the
114 InstanceArray, behaviour is undefined.
115
116 The instance type must have a constructor accepting a const Object &.  If it
117 has a virtual function with the signature void set_matrix(const Matrix &), it
118 will be used to update the instance matrix.  The original function is also
119 called.
120
121 Instance created by the array have stable addresses.  However after an instance
122 is removed, its address may later be reused for another instance.
123 */
124 template<typename T = ObjectInstance>
125 class InstanceArray: public InstanceArrayBase
126 {
127 private:
128         class Instance: public T
129         {
130         private:
131                 InstanceArray &array;
132                 unsigned index;
133
134         public:
135                 Instance(const Object &o, InstanceArray &a, unsigned i): T(o), array(a), index(i) { }
136
137                 virtual void set_matrix(const Matrix &);
138         };
139
140 public:
141         InstanceArray(const Object &o): InstanceArrayBase(o, sizeof(Instance)) { }
142         ~InstanceArray() { destroy_all<T>(); }
143
144         T &append() { return *create<Instance>(*this); }
145         void remove(T &obj) { destroy(&obj); }
146 };
147
148
149 template<typename T>
150 inline void InstanceArray<T>::Instance::set_matrix(const Matrix &m)
151 {
152         T::set_matrix(m);
153         array.update_instance_matrix(index, *this->get_matrix());
154 }
155
156 } // namespace GL
157 } // namespace Msp
158
159 #endif