1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2005 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup gpu
22 *
23 * GPU fluid drawing functions.
24 */
25
26 #include <string.h>
27
28 #include "BLI_math.h"
29 #include "BLI_utildefines.h"
30
31 #include "DNA_fluid_types.h"
32 #include "DNA_modifier_types.h"
33
34 #include "MEM_guardedalloc.h"
35
36 #include "BKE_colorband.h"
37
38 #include "GPU_texture.h"
39
40 #include "draw_common.h" /* Own include. */
41
42 #ifdef WITH_FLUID
43 # include "manta_fluid_API.h"
44 #endif
45
46 /* -------------------------------------------------------------------- */
47 /** \name Private API
48 * \{ */
49
50 #ifdef WITH_FLUID
51
52 enum {
53 TFUNC_FLAME_SPECTRUM = 0,
54 TFUNC_COLOR_RAMP = 1,
55 };
56
57 # define TFUNC_WIDTH 256
58
create_flame_spectrum_texture(float * data)59 static void create_flame_spectrum_texture(float *data)
60 {
61 # define FIRE_THRESH 7
62 # define MAX_FIRE_ALPHA 0.06f
63 # define FULL_ON_FIRE 100
64
65 float *spec_pixels = (float *)MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float),
66 "spec_pixels");
67
68 blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000);
69
70 for (int i = 0; i < 16; i++) {
71 for (int j = 0; j < 16; j++) {
72 for (int k = 0; k < TFUNC_WIDTH; k++) {
73 int index = (j * TFUNC_WIDTH * 16 + i * TFUNC_WIDTH + k) * 4;
74 if (k >= FIRE_THRESH) {
75 spec_pixels[index] = (data[k * 4]);
76 spec_pixels[index + 1] = (data[k * 4 + 1]);
77 spec_pixels[index + 2] = (data[k * 4 + 2]);
78 spec_pixels[index + 3] = MAX_FIRE_ALPHA *
79 ((k > FULL_ON_FIRE) ?
80 1.0f :
81 (k - FIRE_THRESH) / ((float)FULL_ON_FIRE - FIRE_THRESH));
82 }
83 else {
84 zero_v4(&spec_pixels[index]);
85 }
86 }
87 }
88 }
89
90 memcpy(data, spec_pixels, sizeof(float) * 4 * TFUNC_WIDTH);
91
92 MEM_freeN(spec_pixels);
93
94 # undef FIRE_THRESH
95 # undef MAX_FIRE_ALPHA
96 # undef FULL_ON_FIRE
97 }
98
create_color_ramp(const struct ColorBand * coba,float * data)99 static void create_color_ramp(const struct ColorBand *coba, float *data)
100 {
101 for (int i = 0; i < TFUNC_WIDTH; i++) {
102 BKE_colorband_evaluate(coba, (float)i / TFUNC_WIDTH, &data[i * 4]);
103 straight_to_premul_v4(&data[i * 4]);
104 }
105 }
106
create_transfer_function(int type,const struct ColorBand * coba)107 static GPUTexture *create_transfer_function(int type, const struct ColorBand *coba)
108 {
109 float *data = (float *)MEM_mallocN(sizeof(float[4]) * TFUNC_WIDTH, __func__);
110
111 switch (type) {
112 case TFUNC_FLAME_SPECTRUM:
113 create_flame_spectrum_texture(data);
114 break;
115 case TFUNC_COLOR_RAMP:
116 create_color_ramp(coba, data);
117 break;
118 }
119
120 GPUTexture *tex = GPU_texture_create_1d("transf_func", TFUNC_WIDTH, 1, GPU_SRGB8_A8, data);
121
122 MEM_freeN(data);
123
124 return tex;
125 }
126
swizzle_texture_channel_single(GPUTexture * tex)127 static void swizzle_texture_channel_single(GPUTexture *tex)
128 {
129 /* Swizzle texture channels so that we get useful RGBA values when sampling
130 * a texture with fewer channels, e.g. when using density as color. */
131 GPU_texture_swizzle_set(tex, "rrr1");
132 }
133
rescale_3d(const int dim[3],const int final_dim[3],int channels,const float * fpixels)134 static float *rescale_3d(const int dim[3],
135 const int final_dim[3],
136 int channels,
137 const float *fpixels)
138 {
139 const uint w = dim[0], h = dim[1], d = dim[2];
140 const uint fw = final_dim[0], fh = final_dim[1], fd = final_dim[2];
141 const uint xf = w / fw, yf = h / fh, zf = d / fd;
142 const uint pixel_count = fw * fh * fd;
143 float *nfpixels = (float *)MEM_mallocN(channels * sizeof(float) * pixel_count, __func__);
144
145 if (nfpixels) {
146 printf("Performance: You need to scale a 3D texture, feel the pain!\n");
147
148 for (uint k = 0; k < fd; k++) {
149 for (uint j = 0; j < fh; j++) {
150 for (uint i = 0; i < fw; i++) {
151 /* Obviously doing nearest filtering here,
152 * it's going to be slow in any case, let's not make it worse. */
153 float xb = i * xf;
154 float yb = j * yf;
155 float zb = k * zf;
156 uint offset = k * (fw * fh) + i * fh + j;
157 uint offset_orig = (zb) * (w * h) + (xb)*h + (yb);
158
159 if (channels == 4) {
160 nfpixels[offset * 4] = fpixels[offset_orig * 4];
161 nfpixels[offset * 4 + 1] = fpixels[offset_orig * 4 + 1];
162 nfpixels[offset * 4 + 2] = fpixels[offset_orig * 4 + 2];
163 nfpixels[offset * 4 + 3] = fpixels[offset_orig * 4 + 3];
164 }
165 else if (channels == 1) {
166 nfpixels[offset] = fpixels[offset_orig];
167 }
168 else {
169 BLI_assert(0);
170 }
171 }
172 }
173 }
174 }
175 return nfpixels;
176 }
177
178 /* Will resize input to fit GL system limits. */
create_volume_texture(const int dim[3],eGPUTextureFormat texture_format,eGPUDataFormat data_format,const void * data)179 static GPUTexture *create_volume_texture(const int dim[3],
180 eGPUTextureFormat texture_format,
181 eGPUDataFormat data_format,
182 const void *data)
183 {
184 GPUTexture *tex = NULL;
185 int final_dim[3] = {UNPACK3(dim)};
186
187 if (data == NULL) {
188 return NULL;
189 }
190
191 while (1) {
192 tex = GPU_texture_create_3d(
193 "volume", UNPACK3(final_dim), 1, texture_format, data_format, NULL);
194
195 if (tex != NULL) {
196 break;
197 }
198
199 if (final_dim[0] == 1 && final_dim[1] == 1 && final_dim[2] == 1) {
200 break;
201 }
202
203 for (int i = 0; i < 3; i++) {
204 final_dim[i] = max_ii(1, final_dim[i] / 2);
205 }
206 }
207
208 if (tex == NULL) {
209 printf("Error: Could not create 3D texture.\n");
210 tex = GPU_texture_create_error(3, false);
211 }
212 else if (equals_v3v3_int(dim, final_dim)) {
213 /* No need to resize, just upload the data. */
214 GPU_texture_update_sub(tex, data_format, data, 0, 0, 0, UNPACK3(final_dim));
215 }
216 else if (data_format != GPU_DATA_FLOAT) {
217 printf("Error: Could not allocate 3D texture and not attempting to rescale non-float data.\n");
218 tex = GPU_texture_create_error(3, false);
219 }
220 else {
221 /* We need to resize the input. */
222 int channels = (ELEM(texture_format, GPU_R8, GPU_R16F, GPU_R32F)) ? 1 : 4;
223 float *rescaled_data = rescale_3d(dim, final_dim, channels, data);
224 if (rescaled_data) {
225 GPU_texture_update_sub(tex, GPU_DATA_FLOAT, rescaled_data, 0, 0, 0, UNPACK3(final_dim));
226 MEM_freeN(rescaled_data);
227 }
228 else {
229 printf("Error: Could not allocate rescaled 3d texture!\n");
230 GPU_texture_free(tex);
231 tex = GPU_texture_create_error(3, false);
232 }
233 }
234 return tex;
235 }
236
create_field_texture(FluidDomainSettings * fds,bool single_precision)237 static GPUTexture *create_field_texture(FluidDomainSettings *fds, bool single_precision)
238 {
239 void *field = NULL;
240 eGPUDataFormat data_format = GPU_DATA_FLOAT;
241 eGPUTextureFormat texture_format = GPU_R8;
242
243 if (single_precision) {
244 texture_format = GPU_R32F;
245 }
246
247 switch (fds->coba_field) {
248 case FLUID_DOMAIN_FIELD_DENSITY:
249 field = manta_smoke_get_density(fds->fluid);
250 break;
251 case FLUID_DOMAIN_FIELD_HEAT:
252 field = manta_smoke_get_heat(fds->fluid);
253 break;
254 case FLUID_DOMAIN_FIELD_FUEL:
255 field = manta_smoke_get_fuel(fds->fluid);
256 break;
257 case FLUID_DOMAIN_FIELD_REACT:
258 field = manta_smoke_get_react(fds->fluid);
259 break;
260 case FLUID_DOMAIN_FIELD_FLAME:
261 field = manta_smoke_get_flame(fds->fluid);
262 break;
263 case FLUID_DOMAIN_FIELD_VELOCITY_X:
264 field = manta_get_velocity_x(fds->fluid);
265 break;
266 case FLUID_DOMAIN_FIELD_VELOCITY_Y:
267 field = manta_get_velocity_y(fds->fluid);
268 break;
269 case FLUID_DOMAIN_FIELD_VELOCITY_Z:
270 field = manta_get_velocity_z(fds->fluid);
271 break;
272 case FLUID_DOMAIN_FIELD_COLOR_R:
273 field = manta_smoke_get_color_r(fds->fluid);
274 break;
275 case FLUID_DOMAIN_FIELD_COLOR_G:
276 field = manta_smoke_get_color_g(fds->fluid);
277 break;
278 case FLUID_DOMAIN_FIELD_COLOR_B:
279 field = manta_smoke_get_color_b(fds->fluid);
280 break;
281 case FLUID_DOMAIN_FIELD_FORCE_X:
282 field = manta_get_force_x(fds->fluid);
283 break;
284 case FLUID_DOMAIN_FIELD_FORCE_Y:
285 field = manta_get_force_y(fds->fluid);
286 break;
287 case FLUID_DOMAIN_FIELD_FORCE_Z:
288 field = manta_get_force_z(fds->fluid);
289 break;
290 case FLUID_DOMAIN_FIELD_PHI:
291 field = manta_get_phi(fds->fluid);
292 texture_format = GPU_R16F;
293 break;
294 case FLUID_DOMAIN_FIELD_PHI_IN:
295 field = manta_get_phi_in(fds->fluid);
296 texture_format = GPU_R16F;
297 break;
298 case FLUID_DOMAIN_FIELD_PHI_OUT:
299 field = manta_get_phiout_in(fds->fluid);
300 texture_format = GPU_R16F;
301 break;
302 case FLUID_DOMAIN_FIELD_PHI_OBSTACLE:
303 field = manta_get_phiobs_in(fds->fluid);
304 texture_format = GPU_R16F;
305 break;
306 case FLUID_DOMAIN_FIELD_FLAGS:
307 field = manta_smoke_get_flags(fds->fluid);
308 data_format = GPU_DATA_INT;
309 texture_format = GPU_R8UI;
310 break;
311 case FLUID_DOMAIN_FIELD_PRESSURE:
312 field = manta_get_pressure(fds->fluid);
313 texture_format = GPU_R16F;
314 break;
315 default:
316 return NULL;
317 }
318
319 if (field == NULL) {
320 return NULL;
321 }
322
323 GPUTexture *tex = create_volume_texture(fds->res, texture_format, data_format, field);
324 swizzle_texture_channel_single(tex);
325 return tex;
326 }
327
create_density_texture(FluidDomainSettings * fds,int highres)328 static GPUTexture *create_density_texture(FluidDomainSettings *fds, int highres)
329 {
330 int *dim = (highres) ? fds->res_noise : fds->res;
331
332 float *data;
333 if (highres) {
334 data = manta_noise_get_density(fds->fluid);
335 }
336 else {
337 data = manta_smoke_get_density(fds->fluid);
338 }
339
340 if (data == NULL) {
341 return NULL;
342 }
343
344 GPUTexture *tex = create_volume_texture(dim, GPU_R8, GPU_DATA_FLOAT, data);
345 swizzle_texture_channel_single(tex);
346 return tex;
347 }
348
create_color_texture(FluidDomainSettings * fds,int highres)349 static GPUTexture *create_color_texture(FluidDomainSettings *fds, int highres)
350 {
351 const bool has_color = (highres) ? manta_noise_has_colors(fds->fluid) :
352 manta_smoke_has_colors(fds->fluid);
353
354 if (!has_color) {
355 return NULL;
356 }
357
358 int cell_count = (highres) ? manta_noise_get_cells(fds->fluid) : fds->total_cells;
359 int *dim = (highres) ? fds->res_noise : fds->res;
360 float *data = (float *)MEM_callocN(sizeof(float) * cell_count * 4, "smokeColorTexture");
361
362 if (data == NULL) {
363 return NULL;
364 }
365
366 if (highres) {
367 manta_noise_get_rgba(fds->fluid, data, 0);
368 }
369 else {
370 manta_smoke_get_rgba(fds->fluid, data, 0);
371 }
372
373 GPUTexture *tex = create_volume_texture(dim, GPU_RGBA8, GPU_DATA_FLOAT, data);
374
375 MEM_freeN(data);
376
377 return tex;
378 }
379
create_flame_texture(FluidDomainSettings * fds,int highres)380 static GPUTexture *create_flame_texture(FluidDomainSettings *fds, int highres)
381 {
382 float *source = NULL;
383 const bool has_fuel = (highres) ? manta_noise_has_fuel(fds->fluid) :
384 manta_smoke_has_fuel(fds->fluid);
385 int *dim = (highres) ? fds->res_noise : fds->res;
386
387 if (!has_fuel) {
388 return NULL;
389 }
390
391 if (highres) {
392 source = manta_noise_get_flame(fds->fluid);
393 }
394 else {
395 source = manta_smoke_get_flame(fds->fluid);
396 }
397
398 GPUTexture *tex = create_volume_texture(dim, GPU_R8, GPU_DATA_FLOAT, source);
399 swizzle_texture_channel_single(tex);
400 return tex;
401 }
402
get_smoke_velocity_field(FluidDomainSettings * fds,float ** r_velocity_x,float ** r_velocity_y,float ** r_velocity_z)403 static bool get_smoke_velocity_field(FluidDomainSettings *fds,
404 float **r_velocity_x,
405 float **r_velocity_y,
406 float **r_velocity_z)
407 {
408 const char vector_field = fds->vector_field;
409 switch ((FLUID_DisplayVectorField)vector_field) {
410 case FLUID_DOMAIN_VECTOR_FIELD_VELOCITY:
411 *r_velocity_x = manta_get_velocity_x(fds->fluid);
412 *r_velocity_y = manta_get_velocity_y(fds->fluid);
413 *r_velocity_z = manta_get_velocity_z(fds->fluid);
414 break;
415 case FLUID_DOMAIN_VECTOR_FIELD_GUIDE_VELOCITY:
416 *r_velocity_x = manta_get_guide_velocity_x(fds->fluid);
417 *r_velocity_y = manta_get_guide_velocity_y(fds->fluid);
418 *r_velocity_z = manta_get_guide_velocity_z(fds->fluid);
419 break;
420 case FLUID_DOMAIN_VECTOR_FIELD_FORCE:
421 *r_velocity_x = manta_get_force_x(fds->fluid);
422 *r_velocity_y = manta_get_force_y(fds->fluid);
423 *r_velocity_z = manta_get_force_z(fds->fluid);
424 break;
425 }
426
427 return *r_velocity_x && *r_velocity_y && *r_velocity_z;
428 }
429
430 #endif /* WITH_FLUID */
431
432 /** \} */
433
434 /* -------------------------------------------------------------------- */
435 /** \name Public API
436 * \{ */
437
DRW_smoke_free(FluidModifierData * fmd)438 void DRW_smoke_free(FluidModifierData *fmd)
439 {
440 if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) {
441 if (fmd->domain->tex_density) {
442 GPU_texture_free(fmd->domain->tex_density);
443 fmd->domain->tex_density = NULL;
444 }
445
446 if (fmd->domain->tex_color) {
447 GPU_texture_free(fmd->domain->tex_color);
448 fmd->domain->tex_color = NULL;
449 }
450
451 if (fmd->domain->tex_shadow) {
452 GPU_texture_free(fmd->domain->tex_shadow);
453 fmd->domain->tex_shadow = NULL;
454 }
455
456 if (fmd->domain->tex_flame) {
457 GPU_texture_free(fmd->domain->tex_flame);
458 fmd->domain->tex_flame = NULL;
459 }
460
461 if (fmd->domain->tex_flame_coba) {
462 GPU_texture_free(fmd->domain->tex_flame_coba);
463 fmd->domain->tex_flame_coba = NULL;
464 }
465
466 if (fmd->domain->tex_coba) {
467 GPU_texture_free(fmd->domain->tex_coba);
468 fmd->domain->tex_coba = NULL;
469 }
470
471 if (fmd->domain->tex_field) {
472 GPU_texture_free(fmd->domain->tex_field);
473 fmd->domain->tex_field = NULL;
474 }
475 }
476 }
477
DRW_smoke_ensure_coba_field(FluidModifierData * fmd)478 void DRW_smoke_ensure_coba_field(FluidModifierData *fmd)
479 {
480 #ifndef WITH_FLUID
481 UNUSED_VARS(fmd);
482 #else
483 if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
484 FluidDomainSettings *fds = fmd->domain;
485
486 if (!fds->tex_field) {
487 fds->tex_field = create_field_texture(fds, false);
488 }
489 if (!fds->tex_coba && !ELEM(fds->coba_field,
490 FLUID_DOMAIN_FIELD_PHI,
491 FLUID_DOMAIN_FIELD_PHI_IN,
492 FLUID_DOMAIN_FIELD_PHI_OUT,
493 FLUID_DOMAIN_FIELD_PHI_OBSTACLE,
494 FLUID_DOMAIN_FIELD_FLAGS,
495 FLUID_DOMAIN_FIELD_PRESSURE)) {
496 fds->tex_coba = create_transfer_function(TFUNC_COLOR_RAMP, fds->coba);
497 }
498 }
499 #endif
500 }
501
DRW_smoke_ensure(FluidModifierData * fmd,int highres)502 void DRW_smoke_ensure(FluidModifierData *fmd, int highres)
503 {
504 #ifndef WITH_FLUID
505 UNUSED_VARS(fmd, highres);
506 #else
507 if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
508 FluidDomainSettings *fds = fmd->domain;
509
510 if (!fds->tex_density) {
511 fds->tex_density = create_density_texture(fds, highres);
512 }
513 if (!fds->tex_color) {
514 fds->tex_color = create_color_texture(fds, highres);
515 }
516 if (!fds->tex_flame) {
517 fds->tex_flame = create_flame_texture(fds, highres);
518 }
519 if (!fds->tex_flame_coba && fds->tex_flame) {
520 fds->tex_flame_coba = create_transfer_function(TFUNC_FLAME_SPECTRUM, NULL);
521 }
522 if (!fds->tex_shadow) {
523 fds->tex_shadow = create_volume_texture(
524 fds->res, GPU_R8, GPU_DATA_FLOAT, manta_smoke_get_shadow(fds->fluid));
525 }
526 }
527 #endif /* WITH_FLUID */
528 }
529
DRW_smoke_ensure_velocity(FluidModifierData * fmd)530 void DRW_smoke_ensure_velocity(FluidModifierData *fmd)
531 {
532 #ifndef WITH_FLUID
533 UNUSED_VARS(fmd);
534 #else
535 if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
536 FluidDomainSettings *fds = fmd->domain;
537 float *vel_x = NULL, *vel_y = NULL, *vel_z = NULL;
538
539 if (!get_smoke_velocity_field(fds, &vel_x, &vel_y, &vel_z)) {
540 fds->vector_field = FLUID_DOMAIN_VECTOR_FIELD_VELOCITY;
541 get_smoke_velocity_field(fds, &vel_x, &vel_y, &vel_z);
542 }
543
544 if (ELEM(NULL, vel_x, vel_y, vel_z)) {
545 return;
546 }
547
548 if (!fds->tex_velocity_x) {
549 fds->tex_velocity_x = GPU_texture_create_3d(
550 "velx", UNPACK3(fds->res), 1, GPU_R16F, GPU_DATA_FLOAT, vel_x);
551 fds->tex_velocity_y = GPU_texture_create_3d(
552 "vely", UNPACK3(fds->res), 1, GPU_R16F, GPU_DATA_FLOAT, vel_y);
553 fds->tex_velocity_z = GPU_texture_create_3d(
554 "velz", UNPACK3(fds->res), 1, GPU_R16F, GPU_DATA_FLOAT, vel_z);
555 }
556 }
557 #endif /* WITH_FLUID */
558 }
559
DRW_fluid_ensure_flags(FluidModifierData * fmd)560 void DRW_fluid_ensure_flags(FluidModifierData *fmd)
561 {
562 #ifndef WITH_FLUID
563 UNUSED_VARS(fmd);
564 #else
565 if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
566 FluidDomainSettings *fds = fmd->domain;
567 if (!fds->tex_flags) {
568 fds->tex_flags = create_volume_texture(
569 fds->res, GPU_R8UI, GPU_DATA_INT, manta_smoke_get_flags(fds->fluid));
570
571 swizzle_texture_channel_single(fds->tex_flags);
572 }
573 }
574 #endif /* WITH_FLUID */
575 }
576
DRW_fluid_ensure_range_field(FluidModifierData * fmd)577 void DRW_fluid_ensure_range_field(FluidModifierData *fmd)
578 {
579 #ifndef WITH_FLUID
580 UNUSED_VARS(fmd);
581 #else
582 if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
583 FluidDomainSettings *fds = fmd->domain;
584
585 if (!fds->tex_range_field) {
586 fds->tex_range_field = create_field_texture(fds, true);
587 }
588 }
589 #endif /* WITH_FLUID */
590 }
591
592 /* TODO Unify with the other GPU_free_smoke. */
DRW_smoke_free_velocity(FluidModifierData * fmd)593 void DRW_smoke_free_velocity(FluidModifierData *fmd)
594 {
595 if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) {
596 if (fmd->domain->tex_velocity_x) {
597 GPU_texture_free(fmd->domain->tex_velocity_x);
598 }
599
600 if (fmd->domain->tex_velocity_y) {
601 GPU_texture_free(fmd->domain->tex_velocity_y);
602 }
603
604 if (fmd->domain->tex_velocity_z) {
605 GPU_texture_free(fmd->domain->tex_velocity_z);
606 }
607
608 if (fmd->domain->tex_flags) {
609 GPU_texture_free(fmd->domain->tex_flags);
610 }
611
612 if (fmd->domain->tex_range_field) {
613 GPU_texture_free(fmd->domain->tex_range_field);
614 }
615
616 fmd->domain->tex_velocity_x = NULL;
617 fmd->domain->tex_velocity_y = NULL;
618 fmd->domain->tex_velocity_z = NULL;
619 fmd->domain->tex_flags = NULL;
620 fmd->domain->tex_range_field = NULL;
621 }
622 }
623
624 /** \} */
625