Disable depth buffer writes while rendering smoke in the viewport.
[blender.git] / source / blender / editors / space_view3d / drawvolume.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) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * Contributor(s): Daniel Genrich
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/space_view3d/drawvolume.c
27  *  \ingroup spview3d
28  */
29
30 #include <string.h>
31 #include <math.h>
32
33 #include "MEM_guardedalloc.h"
34
35 #include "DNA_scene_types.h"
36 #include "DNA_screen_types.h"
37 #include "DNA_smoke_types.h"
38 #include "DNA_view3d_types.h"
39
40 #include "BLI_utildefines.h"
41 #include "BLI_math.h"
42
43 #include "BKE_particle.h"
44
45 #include "smoke_API.h"
46
47 #include "BIF_gl.h"
48
49 #include "GPU_debug.h"
50 #include "GPU_shader.h"
51 #include "GPU_texture.h"
52
53 #include "view3d_intern.h"  // own include
54
55 struct GPUTexture;
56
57 // #define DEBUG_DRAW_TIME
58
59 #ifdef DEBUG_DRAW_TIME
60 #  include "PIL_time.h"
61 #  include "PIL_time_utildefines.h"
62 #endif
63
64 static GPUTexture *create_flame_spectrum_texture(void)
65 {
66 #define SPEC_WIDTH 256
67 #define FIRE_THRESH 7
68 #define MAX_FIRE_ALPHA 0.06f
69 #define FULL_ON_FIRE 100
70
71         GPUTexture *tex;
72         int i, j, k;
73         float *spec_data = MEM_mallocN(SPEC_WIDTH * 4 * sizeof(float), "spec_data");
74         float *spec_pixels = MEM_mallocN(SPEC_WIDTH * 4 * 16 * 16 * sizeof(float), "spec_pixels");
75
76         blackbody_temperature_to_rgb_table(spec_data, SPEC_WIDTH, 1500, 3000);
77
78         for (i = 0; i < 16; i++) {
79                 for (j = 0; j < 16; j++) {
80                         for (k = 0; k < SPEC_WIDTH; k++) {
81                                 int index = (j * SPEC_WIDTH * 16 + i * SPEC_WIDTH + k) * 4;
82                                 if (k >= FIRE_THRESH) {
83                                         spec_pixels[index] = (spec_data[k * 4]);
84                                         spec_pixels[index + 1] = (spec_data[k * 4 + 1]);
85                                         spec_pixels[index + 2] = (spec_data[k * 4 + 2]);
86                                         spec_pixels[index + 3] = MAX_FIRE_ALPHA * (
87                                                 (k > FULL_ON_FIRE) ? 1.0f : (k - FIRE_THRESH) / ((float)FULL_ON_FIRE - FIRE_THRESH));
88                                 }
89                                 else {
90                                         zero_v4(&spec_pixels[index]);
91                                 }
92                         }
93                 }
94         }
95
96         tex = GPU_texture_create_1D(SPEC_WIDTH, spec_pixels, NULL);
97
98         MEM_freeN(spec_data);
99         MEM_freeN(spec_pixels);
100
101 #undef SPEC_WIDTH
102 #undef FIRE_THRESH
103 #undef MAX_FIRE_ALPHA
104 #undef FULL_ON_FIRE
105
106         return tex;
107 }
108
109 typedef struct VolumeSlicer {
110         float size[3];
111         float min[3];
112         float max[3];
113         float (*verts)[3];
114 } VolumeSlicer;
115
116 /* *************************** View Aligned Slicing ************************** */
117
118 /* Code adapted from:
119  * "GPU-based Volume Rendering, Real-time Volume Graphics", AK Peters/CRC Press
120  */
121 static int create_view_aligned_slices(VolumeSlicer *slicer,
122                                       const int num_slices,
123                                       const float view_dir[3])
124 {
125         const int indices[] = { 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5 };
126
127         const float vertices[8][3] = {
128             { slicer->min[0], slicer->min[1], slicer->min[2] },
129             { slicer->max[0], slicer->min[1], slicer->min[2] },
130             { slicer->max[0], slicer->max[1], slicer->min[2] },
131             { slicer->min[0], slicer->max[1], slicer->min[2] },
132             { slicer->min[0], slicer->min[1], slicer->max[2] },
133             { slicer->max[0], slicer->min[1], slicer->max[2] },
134             { slicer->max[0], slicer->max[1], slicer->max[2] },
135             { slicer->min[0], slicer->max[1], slicer->max[2] }
136         };
137
138         const int edges[12][2] = {
139             { 0, 1 }, { 1, 2 }, { 2, 3 },
140             { 3, 0 }, { 0, 4 }, { 1, 5 },
141             { 2, 6 }, { 3, 7 }, { 4, 5 },
142             { 5, 6 }, { 6, 7 }, { 7, 4 }
143         };
144
145         const int edge_list[8][12] = {
146             { 0, 1, 5, 6, 4, 8, 11, 9, 3, 7, 2, 10 },
147             { 0, 4, 3, 11, 1, 2, 6, 7, 5, 9, 8, 10 },
148             { 1, 5, 0, 8, 2, 3, 7, 4, 6, 10, 9, 11 },
149             { 7, 11, 10, 8, 2, 6, 1, 9, 3, 0, 4, 5 },
150             { 8, 5, 9, 1, 11, 10, 7, 6, 4, 3, 0, 2 },
151             { 9, 6, 10, 2, 8, 11, 4, 7, 5, 0, 1, 3 },
152             { 9, 8, 5, 4, 6, 1, 2, 0, 10, 7, 11, 3 },
153             { 10, 9, 6, 5, 7, 2, 3, 1, 11, 4, 8, 0 }
154         };
155
156         /* find vertex that is the furthest from the view plane */
157         int max_index = 0;
158         float max_dist, min_dist;
159         min_dist = max_dist = dot_v3v3(view_dir, vertices[0]);
160
161         for (int i = 1; i < 8; i++) {
162                 float dist = dot_v3v3(view_dir, vertices[i]);
163
164                 if (dist > max_dist) {
165                         max_dist = dist;
166                         max_index = i;
167                 }
168
169                 if (dist < min_dist) {
170                         min_dist = dist;
171                 }
172         }
173
174         max_dist -= FLT_EPSILON;
175         min_dist += FLT_EPSILON;
176
177         /* start and direction vectors */
178         float vec_start[12][3], vec_dir[12][3];
179         /* lambda intersection values */
180         float lambda[12], lambda_inc[12];
181         float denom = 0.0f;
182
183         float plane_dist = min_dist;
184         float plane_dist_inc = (max_dist - min_dist) / (float)num_slices;
185
186         /* for all egdes */
187         for (int i = 0; i < 12; i++) {
188                 copy_v3_v3(vec_start[i], vertices[edges[edge_list[max_index][i]][0]]);
189                 copy_v3_v3(vec_dir[i],   vertices[edges[edge_list[max_index][i]][1]]);
190                 sub_v3_v3(vec_dir[i], vec_start[i]);
191
192                 denom = dot_v3v3(vec_dir[i], view_dir);
193
194                 if (1.0f + denom != 1.0f) {
195                         lambda_inc[i] = plane_dist_inc / denom;
196                         lambda[i] = (plane_dist - dot_v3v3(vec_start[i], view_dir)) / denom;
197                 }
198                 else {
199                         lambda[i] = -1.0f;
200                         lambda_inc[i] = 0.0f;
201                 }
202         }
203
204         float intersections[6][3];
205         float dL[12];
206         int num_points = 0;
207         /* find intersections for each slice, process them in back to front order */
208         for (int i = 0; i < num_slices; i++) {
209                 for (int e = 0; e < 12; e++) {
210                         dL[e] = lambda[e] + i * lambda_inc[e];
211                 }
212
213                 if ((dL[0] >= 0.0f) && (dL[0] < 1.0f)) {
214                         madd_v3_v3v3fl(intersections[0], vec_start[0], vec_dir[0], dL[0]);
215                 }
216                 else if ((dL[1] >= 0.0f) && (dL[1] < 1.0f)) {
217                         madd_v3_v3v3fl(intersections[0], vec_start[1], vec_dir[1], dL[1]);
218                 }
219                 else if ((dL[3] >= 0.0f) && (dL[3] < 1.0f)) {
220                         madd_v3_v3v3fl(intersections[0], vec_start[3], vec_dir[3], dL[3]);
221                 }
222                 else continue;
223
224                 if ((dL[2] >= 0.0f) && (dL[2] < 1.0f)) {
225                         madd_v3_v3v3fl(intersections[1], vec_start[2], vec_dir[2], dL[2]);
226                 }
227                 else if ((dL[0] >= 0.0f) && (dL[0] < 1.0f)) {
228                         madd_v3_v3v3fl(intersections[1], vec_start[0], vec_dir[0], dL[0]);
229                 }
230                 else if ((dL[1] >= 0.0f) && (dL[1] < 1.0f)) {
231                         madd_v3_v3v3fl(intersections[1], vec_start[1], vec_dir[1], dL[1]);
232                 }
233                 else {
234                         madd_v3_v3v3fl(intersections[1], vec_start[3], vec_dir[3], dL[3]);
235                 }
236
237                 if ((dL[4] >= 0.0f) && (dL[4] < 1.0f)) {
238                         madd_v3_v3v3fl(intersections[2], vec_start[4], vec_dir[4], dL[4]);
239                 }
240                 else if ((dL[5] >= 0.0f) && (dL[5] < 1.0f)) {
241                         madd_v3_v3v3fl(intersections[2], vec_start[5], vec_dir[5], dL[5]);
242                 }
243                 else {
244                         madd_v3_v3v3fl(intersections[2], vec_start[7], vec_dir[7], dL[7]);
245                 }
246
247                 if ((dL[6] >= 0.0f) && (dL[6] < 1.0f)) {
248                         madd_v3_v3v3fl(intersections[3], vec_start[6], vec_dir[6], dL[6]);
249                 }
250                 else if ((dL[4] >= 0.0f) && (dL[4] < 1.0f)) {
251                         madd_v3_v3v3fl(intersections[3], vec_start[4], vec_dir[4], dL[4]);
252                 }
253                 else if ((dL[5] >= 0.0f) && (dL[5] < 1.0f)) {
254                         madd_v3_v3v3fl(intersections[3], vec_start[5], vec_dir[5], dL[5]);
255                 }
256                 else {
257                         madd_v3_v3v3fl(intersections[3], vec_start[7], vec_dir[7], dL[7]);
258                 }
259
260                 if ((dL[8] >= 0.0f) && (dL[8] < 1.0f)) {
261                         madd_v3_v3v3fl(intersections[4], vec_start[8], vec_dir[8], dL[8]);
262                 }
263                 else if ((dL[9] >= 0.0f) && (dL[9] < 1.0f)) {
264                         madd_v3_v3v3fl(intersections[4], vec_start[9], vec_dir[9], dL[9]);
265                 }
266                 else {
267                         madd_v3_v3v3fl(intersections[4], vec_start[11], vec_dir[11], dL[11]);
268                 }
269
270                 if ((dL[10] >= 0.0f) && (dL[10] < 1.0f)) {
271                         madd_v3_v3v3fl(intersections[5], vec_start[10], vec_dir[10], dL[10]);
272                 }
273                 else if ((dL[8] >= 0.0f) && (dL[8] < 1.0f)) {
274                         madd_v3_v3v3fl(intersections[5], vec_start[8], vec_dir[8], dL[8]);
275                 }
276                 else if ((dL[9] >= 0.0f) && (dL[9] < 1.0f)) {
277                         madd_v3_v3v3fl(intersections[5], vec_start[9], vec_dir[9], dL[9]);
278                 }
279                 else {
280                         madd_v3_v3v3fl(intersections[5], vec_start[11], vec_dir[11], dL[11]);
281                 }
282
283                 for (int e = 0; e < 12; e++) {
284                         copy_v3_v3(slicer->verts[num_points++], intersections[indices[e]]);
285                 }
286         }
287
288         return num_points;
289 }
290
291 void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
292                        const float min[3], const float max[3],
293                         const float viewnormal[3])
294 {
295         if (!sds->tex || !sds->tex_shadow) {
296                 fprintf(stderr, "Could not allocate 3D texture for volume rendering!\n");
297                 return;
298         }
299
300         const bool use_fire = (sds->active_fields & SM_ACTIVE_FIRE) && sds->tex_flame;
301
302         GPUShader *shader = GPU_shader_get_builtin_shader(
303                                 (use_fire) ? GPU_SHADER_SMOKE_FIRE : GPU_SHADER_SMOKE);
304
305         if (!shader) {
306                 fprintf(stderr, "Unable to create GLSL smoke shader.\n");
307                 return;
308         }
309
310         const float ob_sizei[3] = {
311             1.0f / fabsf(ob->size[0]),
312             1.0f / fabsf(ob->size[1]),
313             1.0f / fabsf(ob->size[2])
314         };
315
316         const float size[3] = { max[0] - min[0], max[1] - min[1], max[2] - min[2] };
317         const float invsize[3] = { 1.0f / size[0], 1.0f / size[1], 1.0f / size[2] };
318
319 #ifdef DEBUG_DRAW_TIME
320         TIMEIT_START(draw);
321 #endif
322
323         /* setup smoke shader */
324
325         int soot_location = GPU_shader_get_uniform(shader, "soot_texture");
326         int spec_location = GPU_shader_get_uniform(shader, "spectrum_texture");
327         int shadow_location = GPU_shader_get_uniform(shader, "shadow_texture");
328         int flame_location = GPU_shader_get_uniform(shader, "flame_texture");
329         int actcol_location = GPU_shader_get_uniform(shader, "active_color");
330         int stepsize_location = GPU_shader_get_uniform(shader, "step_size");
331         int densityscale_location = GPU_shader_get_uniform(shader, "density_scale");
332         int invsize_location = GPU_shader_get_uniform(shader, "invsize");
333         int ob_sizei_location = GPU_shader_get_uniform(shader, "ob_sizei");
334         int min_location = GPU_shader_get_uniform(shader, "min_location");
335
336         GPU_shader_bind(shader);
337
338         GPU_texture_bind(sds->tex, 0);
339         GPU_shader_uniform_texture(shader, soot_location, sds->tex);
340
341         GPU_texture_bind(sds->tex_shadow, 1);
342         GPU_shader_uniform_texture(shader, shadow_location, sds->tex_shadow);
343
344         GPUTexture *tex_spec = NULL;
345
346         if (use_fire) {
347                 GPU_texture_bind(sds->tex_flame, 2);
348                 GPU_shader_uniform_texture(shader, flame_location, sds->tex_flame);
349
350                 tex_spec = create_flame_spectrum_texture();
351                 GPU_texture_bind(tex_spec, 3);
352                 GPU_shader_uniform_texture(shader, spec_location, tex_spec);
353         }
354
355         float active_color[3] = { 0.9, 0.9, 0.9 };
356         float density_scale = 10.0f;
357         if ((sds->active_fields & SM_ACTIVE_COLORS) == 0)
358                 mul_v3_v3(active_color, sds->active_color);
359
360         GPU_shader_uniform_vector(shader, actcol_location, 3, 1, active_color);
361         GPU_shader_uniform_vector(shader, stepsize_location, 1, 1, &sds->dx);
362         GPU_shader_uniform_vector(shader, densityscale_location, 1, 1, &density_scale);
363         GPU_shader_uniform_vector(shader, min_location, 3, 1, min);
364         GPU_shader_uniform_vector(shader, ob_sizei_location, 3, 1, ob_sizei);
365         GPU_shader_uniform_vector(shader, invsize_location, 3, 1, invsize);
366
367         /* setup slicing information */
368
369         const int max_slices = 256;
370         const int max_points = max_slices * 12;
371
372         VolumeSlicer slicer;
373         copy_v3_v3(slicer.min, min);
374         copy_v3_v3(slicer.max, max);
375         copy_v3_v3(slicer.size, size);
376         slicer.verts = MEM_mallocN(sizeof(float) * 3 * max_points, "smoke_slice_vertices");
377
378         const int num_points = create_view_aligned_slices(&slicer, max_slices, viewnormal);
379
380         /* setup buffer and draw */
381
382         int gl_depth = 0, gl_blend = 0, gl_depth_write = 0;
383         glGetBooleanv(GL_BLEND, (GLboolean *)&gl_blend);
384         glGetBooleanv(GL_DEPTH_TEST, (GLboolean *)&gl_depth);
385         glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&gl_depth_write);
386
387         glEnable(GL_DEPTH_TEST);
388         glDepthMask(GL_FALSE);
389         glEnable(GL_BLEND);
390         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
391
392         GLuint vertex_buffer;
393         glGenBuffers(1, &vertex_buffer);
394         glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
395         glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * num_points, &slicer.verts[0][0], GL_STATIC_DRAW);
396
397         glEnableClientState(GL_VERTEX_ARRAY);
398         glVertexPointer(3, GL_FLOAT, 0, NULL);
399
400         glDrawArrays(GL_TRIANGLES, 0, num_points);
401
402         glDisableClientState(GL_VERTEX_ARRAY);
403
404 #ifdef DEBUG_DRAW_TIME
405         printf("Draw Time: %f\n", (float)TIMEIT_VALUE(draw));
406         TIMEIT_END(draw);
407 #endif
408
409         /* cleanup */
410
411         glBindBuffer(GL_ARRAY_BUFFER, 0);
412         glDeleteBuffers(1, &vertex_buffer);
413
414         GPU_texture_unbind(sds->tex);
415         GPU_texture_unbind(sds->tex_shadow);
416
417         if (use_fire) {
418                 GPU_texture_unbind(sds->tex_flame);
419                 GPU_texture_unbind(tex_spec);
420                 GPU_texture_free(tex_spec);
421         }
422
423         MEM_freeN(slicer.verts);
424
425         GPU_shader_unbind();
426
427         glDepthMask(gl_depth_write);
428
429         if (!gl_blend) {
430                 glDisable(GL_BLEND);
431         }
432
433         if (gl_depth) {
434                 glEnable(GL_DEPTH_TEST);
435         }
436 }
437
438 #ifdef SMOKE_DEBUG_VELOCITY
439 void draw_smoke_velocity(SmokeDomainSettings *domain, Object *ob)
440 {
441         float x, y, z;
442         float x0, y0, z0;
443         int *base_res = domain->base_res;
444         int *res = domain->res;
445         int *res_min = domain->res_min;
446         int *res_max = domain->res_max;
447         float *vel_x = smoke_get_velocity_x(domain->fluid);
448         float *vel_y = smoke_get_velocity_y(domain->fluid);
449         float *vel_z = smoke_get_velocity_z(domain->fluid);
450
451         float min[3];
452         float *cell_size = domain->cell_size;
453         float step_size = ((float)max_iii(base_res[0], base_res[1], base_res[2])) / 16.f;
454         float vf = domain->scale / 16.f * 2.f; /* velocity factor */
455
456         glLineWidth(1.0f);
457
458         /* set first position so that it doesn't jump when domain moves */
459         x0 = res_min[0] + fmod(-(float)domain->shift[0] + res_min[0], step_size);
460         y0 = res_min[1] + fmod(-(float)domain->shift[1] + res_min[1], step_size);
461         z0 = res_min[2] + fmod(-(float)domain->shift[2] + res_min[2], step_size);
462         if (x0 < res_min[0]) x0 += step_size;
463         if (y0 < res_min[1]) y0 += step_size;
464         if (z0 < res_min[2]) z0 += step_size;
465         add_v3_v3v3(min, domain->p0, domain->obj_shift_f);
466
467         for (x = floor(x0); x < res_max[0]; x += step_size)
468                 for (y = floor(y0); y < res_max[1]; y += step_size)
469                         for (z = floor(z0); z < res_max[2]; z += step_size) {
470                                 int index = (floor(x) - res_min[0]) + (floor(y) - res_min[1]) * res[0] + (floor(z) - res_min[2]) * res[0] * res[1];
471
472                                 float pos[3] = {min[0] + ((float)x + 0.5f) * cell_size[0], min[1] + ((float)y + 0.5f) * cell_size[1], min[2] + ((float)z + 0.5f) * cell_size[2]};
473                                 float vel = sqrtf(vel_x[index] * vel_x[index] + vel_y[index] * vel_y[index] + vel_z[index] * vel_z[index]);
474
475                                 /* draw heat as scaled "arrows" */
476                                 if (vel >= 0.01f) {
477                                         float col_g = 1.0f - vel;
478                                         CLAMP(col_g, 0.0f, 1.0f);
479                                         glColor3f(1.0f, col_g, 0.0f);
480                                         glPointSize(10.0f * vel);
481
482                                         glBegin(GL_LINES);
483                                         glVertex3f(pos[0], pos[1], pos[2]);
484                                         glVertex3f(pos[0] + vel_x[index] * vf, pos[1] + vel_y[index] * vf, pos[2] + vel_z[index] * vf);
485                                         glEnd();
486                                         glBegin(GL_POINTS);
487                                         glVertex3f(pos[0] + vel_x[index] * vf, pos[1] + vel_y[index] * vf, pos[2] + vel_z[index] * vf);
488                                         glEnd();
489                                 }
490                         }
491 }
492 #endif
493
494 #ifdef SMOKE_DEBUG_HEAT
495 void draw_smoke_heat(SmokeDomainSettings *domain, Object *ob)
496 {
497         float x, y, z;
498         float x0, y0, z0;
499         int *base_res = domain->base_res;
500         int *res = domain->res;
501         int *res_min = domain->res_min;
502         int *res_max = domain->res_max;
503         float *heat = smoke_get_heat(domain->fluid);
504
505         float min[3];
506         float *cell_size = domain->cell_size;
507         float step_size = ((float)max_iii(base_res[0], base_res[1], base_res[2])) / 16.f;
508         float vf = domain->scale / 16.f * 2.f; /* velocity factor */
509
510         /* set first position so that it doesn't jump when domain moves */
511         x0 = res_min[0] + fmod(-(float)domain->shift[0] + res_min[0], step_size);
512         y0 = res_min[1] + fmod(-(float)domain->shift[1] + res_min[1], step_size);
513         z0 = res_min[2] + fmod(-(float)domain->shift[2] + res_min[2], step_size);
514         if (x0 < res_min[0]) x0 += step_size;
515         if (y0 < res_min[1]) y0 += step_size;
516         if (z0 < res_min[2]) z0 += step_size;
517         add_v3_v3v3(min, domain->p0, domain->obj_shift_f);
518
519         for (x = floor(x0); x < res_max[0]; x += step_size)
520                 for (y = floor(y0); y < res_max[1]; y += step_size)
521                         for (z = floor(z0); z < res_max[2]; z += step_size) {
522                                 int index = (floor(x) - res_min[0]) + (floor(y) - res_min[1]) * res[0] + (floor(z) - res_min[2]) * res[0] * res[1];
523
524                                 float pos[3] = {min[0] + ((float)x + 0.5f) * cell_size[0], min[1] + ((float)y + 0.5f) * cell_size[1], min[2] + ((float)z + 0.5f) * cell_size[2]};
525
526                                 /* draw heat as different sized points */
527                                 if (heat[index] >= 0.01f) {
528                                         float col_gb = 1.0f - heat[index];
529                                         CLAMP(col_gb, 0.0f, 1.0f);
530                                         glColor3f(1.0f, col_gb, col_gb);
531                                         glPointSize(24.0f * heat[index]);
532
533                                         glBegin(GL_POINTS);
534                                         glVertex3f(pos[0], pos[1], pos[2]);
535                                         glEnd();
536                                 }
537                         }
538 }
539 #endif