Blender V4.3
draw_fluid.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include <cstring>
12
13#include "BLI_listbase.h"
14#include "BLI_utildefines.h"
15
16#include "DNA_fluid_types.h"
17#include "DNA_modifier_types.h"
18
19#include "MEM_guardedalloc.h"
20
21#include "BKE_colorband.hh"
22
24
25#include "GPU_texture.hh"
26
27#include "draw_manager_c.hh"
28
29#include "draw_common_c.hh" /* Own include. */
30
31#ifdef WITH_FLUID
32# include "manta_fluid_API.h"
33#endif
34
35/* -------------------------------------------------------------------- */
39#ifdef WITH_FLUID
40
41enum {
42 TFUNC_FLAME_SPECTRUM = 0,
43 TFUNC_COLOR_RAMP = 1,
44};
45
46# define TFUNC_WIDTH 256
47
48static void create_flame_spectrum_texture(float *data)
49{
50# define FIRE_THRESH 7
51# define MAX_FIRE_ALPHA 0.06f
52# define FULL_ON_FIRE 100
53
54 float *spec_pixels = (float *)MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float),
55 "spec_pixels");
56
57 IMB_colormanagement_blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000);
58
59 for (int i = 0; i < 16; i++) {
60 for (int j = 0; j < 16; j++) {
61 for (int k = 0; k < TFUNC_WIDTH; k++) {
62 int index = (j * TFUNC_WIDTH * 16 + i * TFUNC_WIDTH + k) * 4;
63 if (k >= FIRE_THRESH) {
64 spec_pixels[index] = (data[k * 4]);
65 spec_pixels[index + 1] = (data[k * 4 + 1]);
66 spec_pixels[index + 2] = (data[k * 4 + 2]);
67 spec_pixels[index + 3] = MAX_FIRE_ALPHA *
68 ((k > FULL_ON_FIRE) ?
69 1.0f :
70 (k - FIRE_THRESH) / (float(FULL_ON_FIRE) - FIRE_THRESH));
71 }
72 else {
73 zero_v4(&spec_pixels[index]);
74 }
75 }
76 }
77 }
78
79 memcpy(data, spec_pixels, sizeof(float) * 4 * TFUNC_WIDTH);
80
81 MEM_freeN(spec_pixels);
82
83# undef FIRE_THRESH
84# undef MAX_FIRE_ALPHA
85# undef FULL_ON_FIRE
86}
87
88static void create_color_ramp(const ColorBand *coba, float *data)
89{
90 for (int i = 0; i < TFUNC_WIDTH; i++) {
91 BKE_colorband_evaluate(coba, float(i) / TFUNC_WIDTH, &data[i * 4]);
92 straight_to_premul_v4(&data[i * 4]);
93 }
94}
95
96static GPUTexture *create_transfer_function(int type, const ColorBand *coba)
97{
98 float *data = (float *)MEM_mallocN(sizeof(float[4]) * TFUNC_WIDTH, __func__);
99
100 switch (type) {
101 case TFUNC_FLAME_SPECTRUM:
102 create_flame_spectrum_texture(data);
103 break;
104 case TFUNC_COLOR_RAMP:
105 create_color_ramp(coba, data);
106 break;
107 }
108
109 GPUTexture *tex = GPU_texture_create_1d(
110 "transf_func", TFUNC_WIDTH, 1, GPU_SRGB8_A8, GPU_TEXTURE_USAGE_SHADER_READ, data);
111
112 MEM_freeN(data);
113
114 return tex;
115}
116
117static void swizzle_texture_channel_single(GPUTexture *tex)
118{
119 /* Swizzle texture channels so that we get useful RGBA values when sampling
120 * a texture with fewer channels, e.g. when using density as color. */
122}
123
124static float *rescale_3d(const int dim[3],
125 const int final_dim[3],
126 int channels,
127 const float *fpixels)
128{
129 const uint w = dim[0], h = dim[1], d = dim[2];
130 const uint fw = final_dim[0], fh = final_dim[1], fd = final_dim[2];
131 const uint xf = w / fw, yf = h / fh, zf = d / fd;
132 const uint pixel_count = fw * fh * fd;
133 float *nfpixels = (float *)MEM_mallocN(channels * sizeof(float) * pixel_count, __func__);
134
135 if (nfpixels) {
136 printf("Performance: You need to scale a 3D texture, feel the pain!\n");
137
138 for (uint k = 0; k < fd; k++) {
139 for (uint j = 0; j < fh; j++) {
140 for (uint i = 0; i < fw; i++) {
141 /* Obviously doing nearest filtering here,
142 * it's going to be slow in any case, let's not make it worse. */
143 float xb = i * xf;
144 float yb = j * yf;
145 float zb = k * zf;
146 uint offset = k * (fw * fh) + i * fh + j;
147 uint offset_orig = (zb) * (w * h) + (xb)*h + (yb);
148
149 if (channels == 4) {
150 nfpixels[offset * 4] = fpixels[offset_orig * 4];
151 nfpixels[offset * 4 + 1] = fpixels[offset_orig * 4 + 1];
152 nfpixels[offset * 4 + 2] = fpixels[offset_orig * 4 + 2];
153 nfpixels[offset * 4 + 3] = fpixels[offset_orig * 4 + 3];
154 }
155 else if (channels == 1) {
156 nfpixels[offset] = fpixels[offset_orig];
157 }
158 else {
159 BLI_assert(0);
160 }
161 }
162 }
163 }
164 }
165 return nfpixels;
166}
167
168/* Will resize input to fit GL system limits. */
169static GPUTexture *create_volume_texture(const int dim[3],
170 eGPUTextureFormat texture_format,
171 eGPUDataFormat data_format,
172 const void *data)
173{
174 GPUTexture *tex = nullptr;
175 int final_dim[3] = {UNPACK3(dim)};
176
177 if (data == nullptr) {
178 return nullptr;
179 }
180
181 while (true) {
183 "volume", UNPACK3(final_dim), 1, texture_format, GPU_TEXTURE_USAGE_SHADER_READ, nullptr);
184
185 if (tex != nullptr) {
186 break;
187 }
188
189 if (final_dim[0] == 1 && final_dim[1] == 1 && final_dim[2] == 1) {
190 break;
191 }
192
193 for (int i = 0; i < 3; i++) {
194 final_dim[i] = max_ii(1, final_dim[i] / 2);
195 }
196 }
197
198 if (tex == nullptr) {
199 printf("Error: Could not create 3D texture.\n");
200 tex = GPU_texture_create_error(3, false);
201 }
202 else if (equals_v3v3_int(dim, final_dim)) {
203 /* No need to resize, just upload the data. */
204 GPU_texture_update_sub(tex, data_format, data, 0, 0, 0, UNPACK3(final_dim));
205 }
206 else if (data_format != GPU_DATA_FLOAT) {
207 printf("Error: Could not allocate 3D texture and not attempting to rescale non-float data.\n");
208 tex = GPU_texture_create_error(3, false);
209 }
210 else {
211 /* We need to resize the input. */
212 int channels = ELEM(texture_format, GPU_R8, GPU_R16F, GPU_R32F) ? 1 : 4;
213 float *rescaled_data = rescale_3d(dim, final_dim, channels, static_cast<const float *>(data));
214 if (rescaled_data) {
215 GPU_texture_update_sub(tex, GPU_DATA_FLOAT, rescaled_data, 0, 0, 0, UNPACK3(final_dim));
216 MEM_freeN(rescaled_data);
217 }
218 else {
219 printf("Error: Could not allocate rescaled 3d texture!\n");
221 tex = GPU_texture_create_error(3, false);
222 }
223 }
224 return tex;
225}
226
227static GPUTexture *create_field_texture(FluidDomainSettings *fds, bool single_precision)
228{
229 void *field = nullptr;
230 eGPUDataFormat data_format = GPU_DATA_FLOAT;
231 eGPUTextureFormat texture_format = GPU_R8;
232
233 if (single_precision) {
234 texture_format = GPU_R32F;
235 }
236
237 switch (fds->coba_field) {
239 field = manta_smoke_get_density(fds->fluid);
240 break;
242 field = manta_smoke_get_heat(fds->fluid);
243 break;
245 field = manta_smoke_get_fuel(fds->fluid);
246 break;
248 field = manta_smoke_get_react(fds->fluid);
249 break;
251 field = manta_smoke_get_flame(fds->fluid);
252 break;
254 field = manta_get_velocity_x(fds->fluid);
255 break;
257 field = manta_get_velocity_y(fds->fluid);
258 break;
260 field = manta_get_velocity_z(fds->fluid);
261 break;
263 field = manta_smoke_get_color_r(fds->fluid);
264 break;
266 field = manta_smoke_get_color_g(fds->fluid);
267 break;
269 field = manta_smoke_get_color_b(fds->fluid);
270 break;
272 field = manta_get_force_x(fds->fluid);
273 break;
275 field = manta_get_force_y(fds->fluid);
276 break;
278 field = manta_get_force_z(fds->fluid);
279 break;
281 field = manta_get_phi(fds->fluid);
282 texture_format = GPU_R16F;
283 break;
285 field = manta_get_phi_in(fds->fluid);
286 texture_format = GPU_R16F;
287 break;
289 field = manta_get_phiout_in(fds->fluid);
290 texture_format = GPU_R16F;
291 break;
293 field = manta_get_phiobs_in(fds->fluid);
294 texture_format = GPU_R16F;
295 break;
297 field = manta_smoke_get_flags(fds->fluid);
298 data_format = GPU_DATA_INT;
299 texture_format = GPU_R8UI;
300 break;
302 field = manta_get_pressure(fds->fluid);
303 texture_format = GPU_R16F;
304 break;
305 default:
306 return nullptr;
307 }
308
309 if (field == nullptr) {
310 return nullptr;
311 }
312
313 GPUTexture *tex = create_volume_texture(fds->res, texture_format, data_format, field);
314 swizzle_texture_channel_single(tex);
315 return tex;
316}
317
318static GPUTexture *create_density_texture(FluidDomainSettings *fds, int highres)
319{
320 int *dim = (highres) ? fds->res_noise : fds->res;
321
322 float *data;
323 if (highres) {
324 data = manta_noise_get_density(fds->fluid);
325 }
326 else {
327 data = manta_smoke_get_density(fds->fluid);
328 }
329
330 if (data == nullptr) {
331 return nullptr;
332 }
333
334 GPUTexture *tex = create_volume_texture(dim, GPU_R8, GPU_DATA_FLOAT, data);
335 swizzle_texture_channel_single(tex);
336 return tex;
337}
338
339static GPUTexture *create_color_texture(FluidDomainSettings *fds, int highres)
340{
341 const bool has_color = (highres) ? manta_noise_has_colors(fds->fluid) :
343
344 if (!has_color) {
345 return nullptr;
346 }
347
348 int cell_count = (highres) ? manta_noise_get_cells(fds->fluid) : fds->total_cells;
349 int *dim = (highres) ? fds->res_noise : fds->res;
350 float *data = (float *)MEM_callocN(sizeof(float) * cell_count * 4, "smokeColorTexture");
351
352 if (data == nullptr) {
353 return nullptr;
354 }
355
356 if (highres) {
357 manta_noise_get_rgba(fds->fluid, data, 0);
358 }
359 else {
360 manta_smoke_get_rgba(fds->fluid, data, 0);
361 }
362
363 GPUTexture *tex = create_volume_texture(dim, GPU_RGBA8, GPU_DATA_FLOAT, data);
364
365 MEM_freeN(data);
366
367 return tex;
368}
369
370static GPUTexture *create_flame_texture(FluidDomainSettings *fds, int highres)
371{
372 float *source = nullptr;
373 const bool has_fuel = (highres) ? manta_noise_has_fuel(fds->fluid) :
375 int *dim = (highres) ? fds->res_noise : fds->res;
376
377 if (!has_fuel) {
378 return nullptr;
379 }
380
381 if (highres) {
382 source = manta_noise_get_flame(fds->fluid);
383 }
384 else {
385 source = manta_smoke_get_flame(fds->fluid);
386 }
387
388 GPUTexture *tex = create_volume_texture(dim, GPU_R8, GPU_DATA_FLOAT, source);
389 swizzle_texture_channel_single(tex);
390 return tex;
391}
392
393static bool get_smoke_velocity_field(FluidDomainSettings *fds,
394 float **r_velocity_x,
395 float **r_velocity_y,
396 float **r_velocity_z)
397{
398 const char vector_field = fds->vector_field;
399 switch ((FLUID_DisplayVectorField)vector_field) {
401 *r_velocity_x = manta_get_velocity_x(fds->fluid);
402 *r_velocity_y = manta_get_velocity_y(fds->fluid);
403 *r_velocity_z = manta_get_velocity_z(fds->fluid);
404 break;
406 *r_velocity_x = manta_get_guide_velocity_x(fds->fluid);
407 *r_velocity_y = manta_get_guide_velocity_y(fds->fluid);
408 *r_velocity_z = manta_get_guide_velocity_z(fds->fluid);
409 break;
411 *r_velocity_x = manta_get_force_x(fds->fluid);
412 *r_velocity_y = manta_get_force_y(fds->fluid);
413 *r_velocity_z = manta_get_force_z(fds->fluid);
414 break;
415 }
416
417 return *r_velocity_x && *r_velocity_y && *r_velocity_z;
418}
419
420#endif /* WITH_FLUID */
421
424/* -------------------------------------------------------------------- */
429{
430#ifndef WITH_FLUID
431 UNUSED_VARS(fmd);
432#else
433 if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
434 FluidDomainSettings *fds = fmd->domain;
435
436 if (!fds->tex_field) {
437 fds->tex_field = create_field_texture(fds, false);
439 }
440 if (!fds->tex_coba && !ELEM(fds->coba_field,
447 {
448 fds->tex_coba = create_transfer_function(TFUNC_COLOR_RAMP, fds->coba);
450 }
451 }
452#endif
453}
454
455void DRW_smoke_ensure(FluidModifierData *fmd, int highres)
456{
457#ifndef WITH_FLUID
458 UNUSED_VARS(fmd, highres);
459#else
460 if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
461 FluidDomainSettings *fds = fmd->domain;
462
463 if (!fds->tex_density) {
464 fds->tex_density = create_density_texture(fds, highres);
466 }
467 if (!fds->tex_color) {
468 fds->tex_color = create_color_texture(fds, highres);
470 }
471 if (!fds->tex_flame) {
472 fds->tex_flame = create_flame_texture(fds, highres);
474 }
475 if (!fds->tex_flame_coba && fds->tex_flame) {
476 fds->tex_flame_coba = create_transfer_function(TFUNC_FLAME_SPECTRUM, nullptr);
478 }
479 if (!fds->tex_shadow) {
480 fds->tex_shadow = create_volume_texture(
483 }
484 }
485#endif /* WITH_FLUID */
486}
487
489{
490#ifndef WITH_FLUID
491 UNUSED_VARS(fmd);
492#else
493 if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
494 FluidDomainSettings *fds = fmd->domain;
495 float *vel_x = nullptr, *vel_y = nullptr, *vel_z = nullptr;
496
497 if (!get_smoke_velocity_field(fds, &vel_x, &vel_y, &vel_z)) {
499 get_smoke_velocity_field(fds, &vel_x, &vel_y, &vel_z);
500 }
501
502 if (ELEM(nullptr, vel_x, vel_y, vel_z)) {
503 return;
504 }
505
506 if (!fds->tex_velocity_x) {
508 "velx", UNPACK3(fds->res), 1, GPU_R16F, GPU_TEXTURE_USAGE_SHADER_READ, vel_x);
510 "vely", UNPACK3(fds->res), 1, GPU_R16F, GPU_TEXTURE_USAGE_SHADER_READ, vel_y);
512 "velz", UNPACK3(fds->res), 1, GPU_R16F, GPU_TEXTURE_USAGE_SHADER_READ, vel_z);
516 }
517 }
518#endif /* WITH_FLUID */
519}
520
522{
523#ifndef WITH_FLUID
524 UNUSED_VARS(fmd);
525#else
526 if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
527 FluidDomainSettings *fds = fmd->domain;
528 if (!fds->tex_flags) {
529 fds->tex_flags = create_volume_texture(
532
533 swizzle_texture_channel_single(fds->tex_flags);
534 }
535 }
536#endif /* WITH_FLUID */
537}
538
540{
541#ifndef WITH_FLUID
542 UNUSED_VARS(fmd);
543#else
544 if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
545 FluidDomainSettings *fds = fmd->domain;
546
547 if (!fds->tex_range_field) {
548 fds->tex_range_field = create_field_texture(fds, true);
550 }
551 }
552#endif /* WITH_FLUID */
553}
554
555void DRW_smoke_init(DRWData *drw_data)
556{
558}
559
560void DRW_smoke_exit(DRWData *drw_data)
561{
562 /* Free Smoke Textures after rendering */
563 /* XXX This is a waste of processing and GPU bandwidth if nothing
564 * is updated. But the problem is since Textures are stored in the
565 * modifier we don't want them to take precious VRAM if the
566 * modifier is not used for display. We should share them for
567 * all viewport in a redraw at least. */
568 LISTBASE_FOREACH (LinkData *, link, &drw_data->smoke_textures) {
569 GPU_TEXTURE_FREE_SAFE(*(GPUTexture **)link->data);
570 }
571 BLI_freelistN(&drw_data->smoke_textures);
572}
573
bool BKE_colorband_evaluate(const ColorBand *coba, float in, float out[4])
Definition colorband.cc:396
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
struct LinkData * BLI_genericNodeN(void *data)
Definition listbase.cc:909
MINLINE int max_ii(int a, int b)
MINLINE void straight_to_premul_v4(float color[4])
MINLINE bool equals_v3v3_int(const int v1[3], const int v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v4(float r[4])
unsigned int uint
#define UNUSED_VARS(...)
#define UNPACK3(a)
#define ELEM(...)
@ FLUID_DOMAIN_FIELD_COLOR_B
@ FLUID_DOMAIN_FIELD_FLAME
@ FLUID_DOMAIN_FIELD_REACT
@ FLUID_DOMAIN_FIELD_PHI_OUT
@ FLUID_DOMAIN_FIELD_FORCE_Z
@ FLUID_DOMAIN_FIELD_PHI_OBSTACLE
@ FLUID_DOMAIN_FIELD_FLAGS
@ FLUID_DOMAIN_FIELD_VELOCITY_Z
@ FLUID_DOMAIN_FIELD_FORCE_Y
@ FLUID_DOMAIN_FIELD_PHI
@ FLUID_DOMAIN_FIELD_PRESSURE
@ FLUID_DOMAIN_FIELD_VELOCITY_X
@ FLUID_DOMAIN_FIELD_DENSITY
@ FLUID_DOMAIN_FIELD_VELOCITY_Y
@ FLUID_DOMAIN_FIELD_PHI_IN
@ FLUID_DOMAIN_FIELD_HEAT
@ FLUID_DOMAIN_FIELD_COLOR_G
@ FLUID_DOMAIN_FIELD_FORCE_X
@ FLUID_DOMAIN_FIELD_FUEL
@ FLUID_DOMAIN_FIELD_COLOR_R
FLUID_DisplayVectorField
@ FLUID_DOMAIN_VECTOR_FIELD_FORCE
@ FLUID_DOMAIN_VECTOR_FIELD_VELOCITY
@ FLUID_DOMAIN_VECTOR_FIELD_GUIDE_VELOCITY
@ MOD_FLUID_TYPE_DOMAIN
GPUTexture * GPU_texture_create_1d(const char *name, int width, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_texture_free(GPUTexture *texture)
GPUTexture * GPU_texture_create_error(int dimension, bool array)
eGPUDataFormat
@ GPU_DATA_INT
@ GPU_DATA_FLOAT
@ GPU_TEXTURE_USAGE_SHADER_READ
void GPU_texture_update_sub(GPUTexture *texture, eGPUDataFormat data_format, const void *pixels, int offset_x, int offset_y, int offset_z, int width, int height, int depth)
GPUTexture * GPU_texture_create_3d(const char *name, int width, int height, int depth, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const void *data)
#define GPU_TEXTURE_FREE_SAFE(texture)
eGPUTextureFormat
@ GPU_SRGB8_A8
@ GPU_R8UI
@ GPU_R8
void GPU_texture_swizzle_set(GPUTexture *texture, const char swizzle[4])
void IMB_colormanagement_blackbody_temperature_to_rgb_table(float *r_table, int width, float min, float max)
Read Guarded memory(de)allocation.
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
#define printf
void DRW_fluid_ensure_flags(FluidModifierData *fmd)
void DRW_smoke_ensure_coba_field(FluidModifierData *fmd)
void DRW_fluid_ensure_range_field(FluidModifierData *fmd)
void DRW_smoke_ensure(FluidModifierData *fmd, int highres)
void DRW_smoke_ensure_velocity(FluidModifierData *fmd)
void DRW_smoke_exit(DRWData *drw_data)
void DRW_smoke_init(DRWData *drw_data)
DRWManager DST
draw_view in_light_buf[] float
RAYTRACE_GROUP_SIZE additional_info("eevee_shared", "eevee_gbuffer_data", "eevee_global_ubo", "eevee_sampling_data", "eevee_utility_texture", "eevee_hiz_data", "draw_view") .specialization_constant(Type RAYTRACE_GROUP_SIZE in_sh_0_tx in_sh_2_tx screen_normal_tx GPU_RGBA8
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
float * manta_noise_get_density(struct MANTA *smoke)
float * manta_smoke_get_color_r(struct MANTA *smoke)
float * manta_get_force_z(struct MANTA *fluid)
bool manta_noise_has_colors(struct MANTA *smoke)
float * manta_get_force_y(struct MANTA *fluid)
float * manta_get_guide_velocity_y(struct MANTA *fluid)
float * manta_smoke_get_react(struct MANTA *smoke)
float * manta_smoke_get_shadow(struct MANTA *fluid)
float * manta_get_guide_velocity_z(struct MANTA *fluid)
float * manta_get_guide_velocity_x(struct MANTA *fluid)
void manta_noise_get_rgba(struct MANTA *smoke, float *data, int sequential)
float * manta_get_velocity_y(struct MANTA *fluid)
bool manta_noise_has_fuel(struct MANTA *smoke)
float * manta_get_force_x(struct MANTA *fluid)
float * manta_smoke_get_density(struct MANTA *smoke)
float * manta_get_phi(struct MANTA *fluid)
float * manta_get_phi_in(struct MANTA *fluid)
float * manta_get_velocity_z(struct MANTA *fluid)
bool manta_smoke_has_colors(struct MANTA *smoke)
float * manta_get_phiout_in(struct MANTA *fluid)
float * manta_get_phiobs_in(struct MANTA *fluid)
int * manta_smoke_get_flags(struct MANTA *smoke)
float * manta_get_velocity_x(struct MANTA *fluid)
bool manta_smoke_has_fuel(struct MANTA *smoke)
float * manta_get_pressure(struct MANTA *fluid)
float * manta_smoke_get_color_g(struct MANTA *smoke)
int manta_noise_get_cells(struct MANTA *smoke)
float * manta_smoke_get_heat(struct MANTA *smoke)
float * manta_smoke_get_color_b(struct MANTA *smoke)
void manta_smoke_get_rgba(struct MANTA *smoke, float *data, int sequential)
float * manta_smoke_get_fuel(struct MANTA *smoke)
float * manta_smoke_get_flame(struct MANTA *smoke)
float * manta_noise_get_flame(struct MANTA *smoke)
ListBase smoke_textures
DRWData * vmempool
struct GPUTexture * tex_density
struct GPUTexture * tex_range_field
struct GPUTexture * tex_velocity_x
struct GPUTexture * tex_color
struct GPUTexture * tex_velocity_y
struct GPUTexture * tex_field
struct GPUTexture * tex_velocity_z
struct GPUTexture * tex_shadow
struct ColorBand * coba
struct GPUTexture * tex_flags
struct GPUTexture * tex_coba
struct GPUTexture * tex_flame
struct GPUTexture * tex_flame_coba
struct FluidDomainSettings * domain