Implement particle velocity and acceleration visualization
[blender.git] / source / blender / draw / intern / draw_cache_impl_particles.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2017 by Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation, Mike Erwin, Dalai Felinto
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file draw_cache_impl_particles.c
27  *  \ingroup draw
28  *
29  * \brief Particle API for render engines
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_utildefines.h"
35 #include "BLI_math_vector.h"
36
37 #include "DNA_particle_types.h"
38
39 #include "BKE_particle.h"
40
41 #include "GPU_batch.h"
42
43 #include "draw_cache_impl.h"  /* own include */
44
45 static void particle_batch_cache_clear(ParticleSystem *psys);
46
47 /* ---------------------------------------------------------------------- */
48 /* Particle Batch Cache */
49
50 typedef struct ParticleBatchCache {
51         VertexBuffer *pos;
52         ElementList *segments;
53
54         Batch *hairs;
55
56         int segment_count;
57         int point_count;
58
59         /* settings to determine if cache is invalid */
60         bool is_dirty;
61 } ParticleBatchCache;
62
63 /* Batch cache management. */
64
65 static bool particle_batch_cache_valid(ParticleSystem *psys)
66 {
67         ParticleBatchCache *cache = psys->batch_cache;
68
69         if (cache == NULL) {
70                 return false;
71         }
72
73         if (cache->is_dirty == false) {
74                 return true;
75         }
76         else {
77                 return false;
78         }
79
80         return true;
81 }
82
83 static void particle_batch_cache_init(ParticleSystem *psys)
84 {
85         ParticleBatchCache *cache = psys->batch_cache;
86
87         if (!cache) {
88                 cache = psys->batch_cache = MEM_callocN(sizeof(*cache), __func__);
89         }
90         else {
91                 memset(cache, 0, sizeof(*cache));
92         }
93
94         cache->is_dirty = false;
95 }
96
97 static ParticleBatchCache *particle_batch_cache_get(ParticleSystem *psys)
98 {
99         if (!particle_batch_cache_valid(psys)) {
100                 particle_batch_cache_clear(psys);
101                 particle_batch_cache_init(psys);
102         }
103         return psys->batch_cache;
104 }
105
106 void DRW_particle_batch_cache_dirty(ParticleSystem *psys, int mode)
107 {
108         ParticleBatchCache *cache = psys->batch_cache;
109         if (cache == NULL) {
110                 return;
111         }
112         switch (mode) {
113                 case BKE_PARTICLE_BATCH_DIRTY_ALL:
114                         cache->is_dirty = true;
115                         break;
116                 default:
117                         BLI_assert(0);
118         }
119 }
120
121 static void particle_batch_cache_clear(ParticleSystem *psys)
122 {
123         ParticleBatchCache *cache = psys->batch_cache;
124         if (!cache) {
125                 return;
126         }
127
128         BATCH_DISCARD_SAFE(cache->hairs);
129
130         VERTEXBUFFER_DISCARD_SAFE(cache->pos);
131         ELEMENTLIST_DISCARD_SAFE(cache->segments);
132 }
133
134 void DRW_particle_batch_cache_free(ParticleSystem *psys)
135 {
136         particle_batch_cache_clear(psys);
137         MEM_SAFE_FREE(psys->batch_cache);
138 }
139
140 static void ensure_seg_pt_count(ParticleSystem *psys, ParticleBatchCache *cache)
141 {
142         if (cache->pos == NULL || cache->segments == NULL) {
143                 cache->segment_count = 0;
144                 cache->point_count = 0;
145
146                 if (psys->pathcache && (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) {
147                         for (int i = 0; i < psys->totpart; i++) {
148                                 ParticleCacheKey *path = psys->pathcache[i];
149
150                                 if (path->segments > 0) {
151                                         cache->segment_count += path->segments;
152                                         cache->point_count += path->segments + 1;
153                                 }
154                         }
155                 }
156
157                 if (psys->childcache) {
158                         int child_count = psys->totchild * psys->part->disp / 100;
159
160                         for (int i = 0; i < child_count; i++) {
161                                 ParticleCacheKey *path = psys->childcache[i];
162
163                                 if (path->segments > 0) {
164                                         cache->segment_count += path->segments;
165                                         cache->point_count += path->segments + 1;
166                                 }
167                         }
168                 }
169         }
170 }
171
172 /* Batch cache usage. */
173 static void particle_batch_cache_ensure_pos_and_seg(ParticleSystem *psys, ParticleBatchCache *cache)
174 {
175         if (cache->pos == NULL || cache->segments == NULL) {
176                 int curr_point = 0;
177
178                 VERTEXBUFFER_DISCARD_SAFE(cache->pos);
179                 ELEMENTLIST_DISCARD_SAFE(cache->segments);
180
181                 static VertexFormat format = { 0 };
182                 static struct { uint pos, tan, ind; } attr_id;
183                 if (format.attrib_ct == 0) {
184                         /* initialize vertex format */
185                         attr_id.pos = VertexFormat_add_attrib(&format, "pos", COMP_F32, 3, KEEP_FLOAT);
186                         attr_id.tan = VertexFormat_add_attrib(&format, "tang", COMP_F32, 3, KEEP_FLOAT);
187                         attr_id.ind = VertexFormat_add_attrib(&format, "ind", COMP_I32, 1, KEEP_INT);
188                 }
189
190                 cache->pos = VertexBuffer_create_with_format(&format);
191                 VertexBuffer_allocate_data(cache->pos, cache->point_count);
192
193                 ElementListBuilder elb;
194                 ElementListBuilder_init(&elb, PRIM_LINES, cache->segment_count, cache->point_count);
195
196                 if (psys->pathcache && (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) {
197                         for (int i = 0; i < psys->totpart; i++) {
198                                 ParticleCacheKey *path = psys->pathcache[i];
199
200                                 if (path->segments > 0) {
201                                         float tangent[3];
202
203                                         for (int j = 0; j < path->segments; j++) {
204                                                 if (j == 0) {
205                                                         sub_v3_v3v3(tangent, path[j + 1].co, path[j].co);
206                                                 }
207                                                 else {
208                                                         sub_v3_v3v3(tangent, path[j + 1].co, path[j - 1].co);
209                                                 }
210
211                                                 VertexBuffer_set_attrib(cache->pos, attr_id.pos, curr_point, path[j].co);
212                                                 VertexBuffer_set_attrib(cache->pos, attr_id.tan, curr_point, tangent);
213                                                 VertexBuffer_set_attrib(cache->pos, attr_id.ind, curr_point, &i);
214
215                                                 add_line_vertices(&elb, curr_point, curr_point + 1);
216
217                                                 curr_point++;
218                                         }
219
220                                         sub_v3_v3v3(tangent, path[path->segments].co, path[path->segments - 1].co);
221
222                                         VertexBuffer_set_attrib(cache->pos, attr_id.pos, curr_point, path[path->segments].co);
223                                         VertexBuffer_set_attrib(cache->pos, attr_id.tan, curr_point, tangent);
224                                         VertexBuffer_set_attrib(cache->pos, attr_id.ind, curr_point, &i);
225
226                                         curr_point++;
227                                 }
228                         }
229                 }
230
231                 if (psys->childcache) {
232                         int child_count = psys->totchild * psys->part->disp / 100;
233
234                         for (int i = 0, x = psys->totpart; i < child_count; i++, x++) {
235                                 ParticleCacheKey *path = psys->childcache[i];
236                                 float tangent[3];
237
238                                 if (path->segments > 0) {
239                                         for (int j = 0; j < path->segments; j++) {
240                                                 if (j == 0) {
241                                                         sub_v3_v3v3(tangent, path[j + 1].co, path[j].co);
242                                                 }
243                                                 else {
244                                                         sub_v3_v3v3(tangent, path[j + 1].co, path[j - 1].co);
245                                                 }
246
247                                                 VertexBuffer_set_attrib(cache->pos, attr_id.pos, curr_point, path[j].co);
248                                                 VertexBuffer_set_attrib(cache->pos, attr_id.tan, curr_point, tangent);
249                                                 VertexBuffer_set_attrib(cache->pos, attr_id.ind, curr_point, &x);
250
251                                                 add_line_vertices(&elb, curr_point, curr_point + 1);
252
253                                                 curr_point++;
254                                         }
255
256                                         sub_v3_v3v3(tangent, path[path->segments].co, path[path->segments - 1].co);
257
258                                         VertexBuffer_set_attrib(cache->pos, attr_id.pos, curr_point, path[path->segments].co);
259                                         VertexBuffer_set_attrib(cache->pos, attr_id.tan, curr_point, tangent);
260                                         VertexBuffer_set_attrib(cache->pos, attr_id.ind, curr_point, &x);
261
262                                         curr_point++;
263                                 }
264                         }
265                 }
266
267                 cache->segments = ElementList_build(&elb);
268         }
269 }
270
271 static void particle_batch_cache_ensure_pos(ParticleSystem *psys, ParticleBatchCache *cache)
272 {
273         if (cache->pos == NULL) {
274                 static VertexFormat format = { 0 };
275                 static unsigned pos_id, rot_id, val_id;
276                 int i, curr_point;
277                 ParticleData *pa;
278
279                 VERTEXBUFFER_DISCARD_SAFE(cache->pos);
280                 ELEMENTLIST_DISCARD_SAFE(cache->segments);
281
282                 if (format.attrib_ct == 0) {
283                         /* initialize vertex format */
284                         pos_id = VertexFormat_add_attrib(&format, "pos", COMP_F32, 3, KEEP_FLOAT);
285                         rot_id = VertexFormat_add_attrib(&format, "rot", COMP_F32, 4, KEEP_FLOAT);
286                         val_id = VertexFormat_add_attrib(&format, "val", COMP_F32, 1, KEEP_FLOAT);
287                 }
288
289                 cache->pos = VertexBuffer_create_with_format(&format);
290                 VertexBuffer_allocate_data(cache->pos, psys->totpart);
291
292                 for (curr_point = 0, i = 0, pa = psys->particles; i < psys->totpart; i++, pa++) {
293                         if (pa->state.time >= pa->time && pa->state.time < pa->dietime &&
294                             !(pa->flag & (PARS_NO_DISP | PARS_UNEXIST)))
295                         {
296                                 float val;
297
298                                 VertexBuffer_set_attrib(cache->pos, pos_id, curr_point, pa->state.co);
299                                 VertexBuffer_set_attrib(cache->pos, rot_id, curr_point, pa->state.rot);
300
301                                 switch (psys->part->draw_col) {
302                                         case PART_DRAW_COL_VEL:
303                                                 val = len_v3(pa->state.vel) / psys->part->color_vec_max;
304                                                 break;
305                                         case PART_DRAW_COL_ACC:
306                                                 val = len_v3v3(pa->state.vel, pa->prev_state.vel) / ((pa->state.time - pa->prev_state.time) * psys->part->color_vec_max);
307                                                 break;
308                                         default:
309                                                 val = -1.0f;
310                                                 break;
311                                 }
312
313                                 VertexBuffer_set_attrib(cache->pos, val_id, curr_point, &val);
314
315                                 curr_point++;
316                         }
317                 }
318
319                 if (curr_point != psys->totpart) {
320                         VertexBuffer_resize_data(cache->pos, curr_point);
321                 }
322         }
323 }
324
325 Batch *DRW_particles_batch_cache_get_hair(ParticleSystem *psys)
326 {
327         ParticleBatchCache *cache = particle_batch_cache_get(psys);
328
329         if (cache->hairs == NULL) {
330                 ensure_seg_pt_count(psys, cache);
331                 particle_batch_cache_ensure_pos_and_seg(psys, cache);
332                 cache->hairs = Batch_create(PRIM_LINES, cache->pos, cache->segments);
333         }
334
335         return cache->hairs;
336 }
337
338 Batch *DRW_particles_batch_cache_get_dots(ParticleSystem *psys)
339 {
340         ParticleBatchCache *cache = particle_batch_cache_get(psys);
341
342         if (cache->hairs == NULL) {
343                 particle_batch_cache_ensure_pos(psys, cache);
344                 cache->hairs = Batch_create(PRIM_POINTS, cache->pos, NULL);
345         }
346
347         return cache->hairs;
348 }