]> git.tdb.fi Git - libs/gl.git/blob - source/render/instancearray.h
Add a loader to InstanceArray and make them loadable in scenes
[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 protected:
21         class Loader: public DataFile::ObjectLoader<InstanceArrayBase>
22         {
23         private:
24                 static ActionMap shared_actions;
25
26         public:
27                 Loader(InstanceArrayBase &);
28
29         private:
30                 virtual void init_actions();
31
32         protected:
33                 virtual void instance() = 0;
34         };
35
36 private:
37         struct Block
38         {
39                 char *begin = 0;
40                 char *end = 0;
41         };
42
43         struct Slot
44         {
45                 bool used = false;
46                 union
47                 {
48                         std::uint16_t array_index;
49                         std::uint16_t next_free;
50                 };
51                 std::uint16_t block_index;
52                 std::uint16_t index_in_block;
53         };
54
55         const Object &object;
56         VertexArray instance_data;
57         Buffer *instance_buffer = 0;
58         VertexSetup vtx_setup;
59         int matrix_location = -1;
60         unsigned matrix_offset = 0;
61         std::size_t instance_size;
62         std::size_t default_count;
63         std::vector<Block> storage;
64         std::vector<Slot> slots;
65         std::vector<int> array_order;
66         int first_free = -1;
67         int last_free = -1;
68         std::size_t instance_count = 0;
69
70 protected:
71         InstanceArrayBase(const Object &, std::size_t);
72         ~InstanceArrayBase();
73
74 private:
75         void add_block(std::size_t);
76         std::size_t allocate();
77         char *get_address(std::size_t) const;
78         std::size_t find_index(char *) const;
79         void release(std::size_t);
80 protected:
81         template<typename T, typename A>
82         T *create(A &);
83
84         template<typename T>
85         void destroy(T *);
86
87         template<typename T>
88         void destroy_all();
89
90         void update_instance_matrix(std::size_t, const Matrix &);
91
92 public:
93         std::size_t size() const { return instance_count; }
94
95         virtual void render(Renderer &, Tag) const;
96 };
97
98 template<typename T, typename A>
99 inline T *InstanceArrayBase::create(A &array)
100 {
101         size_t index = allocate();
102         return new(get_address(index)) T(object, array, index);
103 }
104
105 template<typename T>
106 inline void InstanceArrayBase::destroy(T *obj)
107 {
108         char *addr = reinterpret_cast<char *>(obj);
109         obj->~T();
110         release(find_index(addr));
111 }
112
113 template<typename T>
114 inline void InstanceArrayBase::destroy_all()
115 {
116         for(unsigned i=0; i<slots.size(); ++i)
117                 if(slots[i].used)
118                         reinterpret_cast<T *>(get_address(i))->~T();
119 }
120
121
122 /**
123 Stores and renders multiple instances of an Object in an efficient manner.
124
125 The instance specific transform is passed to the shader in an attribute with
126 the name instance_transform.  The attribute should have the type vec4[3].  Each
127 elements of the array corresponds to a row of the transform matrix.
128
129 If the Mesh or Technique of the Object is changed during the lifetime of the
130 InstanceArray, behaviour is undefined.
131
132 The instance type must have a constructor accepting a const Object &.  If it
133 has a virtual function with the signature void set_matrix(const Matrix &), it
134 will be used to update the instance matrix.  The original function is also
135 called.
136
137 Instance created by the array have stable addresses.  However after an instance
138 is removed, its address may later be reused for another instance.
139 */
140 template<typename T = ObjectInstance>
141 class InstanceArray: public InstanceArrayBase
142 {
143 public:
144         class Loader: public DataFile::DerivedObjectLoader<InstanceArray, InstanceArrayBase::Loader>
145         {
146         public:
147                 Loader(InstanceArray &a): DataFile::DerivedObjectLoader<InstanceArray, InstanceArrayBase::Loader>(a) { }
148
149         protected:
150                 virtual void instance();
151         };
152
153 private:
154         class Instance: public T
155         {
156         private:
157                 InstanceArray &array;
158                 unsigned index;
159
160         public:
161                 Instance(const Object &o, InstanceArray &a, unsigned i): T(o), array(a), index(i) { }
162
163                 virtual void set_matrix(const Matrix &);
164         };
165
166 public:
167         InstanceArray(const Object &o): InstanceArrayBase(o, sizeof(Instance)) { }
168         ~InstanceArray() { destroy_all<T>(); }
169
170         T &append() { return *create<Instance>(*this); }
171         void remove(T &obj) { destroy(&obj); }
172 };
173
174
175 template<typename T>
176 inline void InstanceArray<T>::Instance::set_matrix(const Matrix &m)
177 {
178         T::set_matrix(m);
179         array.update_instance_matrix(index, *this->get_matrix());
180 }
181
182
183 template<typename T>
184 void InstanceArray<T>::Loader::instance()
185 {
186         T &inst = this->obj.append();
187         this->load_sub(inst);
188 }
189
190 } // namespace GL
191 } // namespace Msp
192
193 #endif