Blender V4.3
draw_volume.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include "DRW_gpu_wrapper.hh"
12#include "DRW_render.hh"
13
14#include "DNA_fluid_types.h"
15#include "DNA_volume_types.h"
16
17#include "BKE_fluid.h"
18#include "BKE_global.hh"
19#include "BKE_mesh.hh"
20#include "BKE_modifier.hh"
21#include "BKE_volume.hh"
22#include "BKE_volume_render.hh"
23
24#include "GPU_material.hh"
25
26#include "draw_common_c.hh"
27#include "draw_manager_c.hh"
28
29#include "draw_common.hh"
30
31using namespace blender;
32using namespace blender::draw;
34
35static struct {
36 GPUTexture *dummy_zero;
37 GPUTexture *dummy_one;
38} g_data = {};
39
42 uint used = 0;
43
45 {
46 for (VolumeInfosBuf *ubo : ubos) {
47 delete ubo;
48 }
49 }
50
51 void reset()
52 {
53 used = 0;
54 }
55
57 {
58 if (used >= ubos.size()) {
59 VolumeInfosBuf *buf = new VolumeInfosBuf();
60 ubos.append(buf);
61 }
62 return ubos[used++];
63 }
64};
65
67{
68 delete reinterpret_cast<VolumeUniformBufPool *>(pool);
69}
70
72{
73 const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f};
74 const float one[4] = {1.0f, 1.0f, 1.0f, 1.0f};
75 g_data.dummy_zero = GPU_texture_create_3d(
76 "dummy_zero", 1, 1, 1, 1, GPU_RGBA8, GPU_TEXTURE_USAGE_SHADER_READ, zero);
77 g_data.dummy_one = GPU_texture_create_3d(
78 "dummy_one", 1, 1, 1, 1, GPU_RGBA8, GPU_TEXTURE_USAGE_SHADER_READ, one);
81}
82
84{
85 GPU_TEXTURE_FREE_SAFE(g_data.dummy_zero);
87}
88
89static GPUTexture *grid_default_texture(eGPUDefaultValue default_value)
90{
91 if (g_data.dummy_one == nullptr) {
93 }
94
95 switch (default_value) {
96 case GPU_DEFAULT_0:
97 return g_data.dummy_zero;
98 case GPU_DEFAULT_1:
99 return g_data.dummy_one;
100 }
101 return g_data.dummy_zero;
102}
103
105{
106 if (drw_data->volume_grids_ubos == nullptr) {
107 drw_data->volume_grids_ubos = new VolumeUniformBufPool();
108 }
110 pool->reset();
111
112 if (g_data.dummy_one == nullptr) {
114 }
115}
116
118 ListBase *attrs,
119 DRWShadingGroup *grp)
120{
122 VolumeInfosBuf &volume_infos = *pool->alloc();
123
124 Volume *volume = (Volume *)ob->data;
125 BKE_volume_load(volume, G.main);
126
127 grp = DRW_shgroup_create_sub(grp);
128
129 volume_infos.density_scale = BKE_volume_density_scale(volume, ob->object_to_world().ptr());
130 volume_infos.color_mul = float4(1.0f);
131 volume_infos.temperature_mul = 1.0f;
132 volume_infos.temperature_bias = 0.0f;
133
134 /* Bind volume grid textures. */
135 int grid_id = 0, grids_len = 0;
136 LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attrs) {
137 const blender::bke::VolumeGridData *volume_grid = BKE_volume_grid_find(volume, attr->name);
138 const DRWVolumeGrid *drw_grid = (volume_grid) ?
139 DRW_volume_batch_cache_get_grid(volume, volume_grid) :
140 nullptr;
141 /* Count number of valid attributes. */
142 grids_len += int(volume_grid != nullptr);
143
144 /* Handle 3 cases here:
145 * - Grid exists and texture was loaded -> use texture.
146 * - Grid exists but has zero size or failed to load -> use zero.
147 * - Grid does not exist -> use default value. */
148 const GPUTexture *grid_tex = (drw_grid) ? drw_grid->texture :
149 (volume_grid) ? g_data.dummy_zero :
150 grid_default_texture(attr->default_value);
151 DRW_shgroup_uniform_texture(grp, attr->input_name, grid_tex);
152
153 volume_infos.grids_xform[grid_id++] = (drw_grid) ? float4x4(drw_grid->object_to_texture) :
155 }
156 /* Render nothing if there is no attribute for the shader to render.
157 * This also avoids an assert caused by the bounding box being zero in size. */
158 if (grids_len == 0) {
159 return nullptr;
160 }
161
162 volume_infos.push_update();
163
164 DRW_shgroup_uniform_block(grp, "drw_volume", volume_infos);
165
166 return grp;
167}
168
170 Object *ob,
171 ListBase *attrs,
172 DRWShadingGroup *grp)
173{
175 VolumeInfosBuf &volume_infos = *pool->alloc();
176
177 ModifierData *md = nullptr;
178
179 volume_infos.density_scale = 1.0f;
180 volume_infos.color_mul = float4(1.0f);
181 volume_infos.temperature_mul = 1.0f;
182 volume_infos.temperature_bias = 0.0f;
183
184 /* Smoke Simulation */
187 ((FluidModifierData *)md)->domain != nullptr)
188 {
190 FluidDomainSettings *fds = fmd->domain;
191
192 /* Don't try to show liquid domains here. */
193 if (!fds->fluid || !(fds->type == FLUID_DOMAIN_TYPE_GAS)) {
194 return nullptr;
195 }
196
197 if (fds->type == FLUID_DOMAIN_TYPE_GAS) {
199 }
200
201 grp = DRW_shgroup_create_sub(grp);
202
203 int grid_id = 0;
204 LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attrs) {
205 if (STREQ(attr->name, "density")) {
207 grp, attr->input_name, fds->tex_density ? &fds->tex_density : &g_data.dummy_one);
208 }
209 else if (STREQ(attr->name, "color")) {
211 grp, attr->input_name, fds->tex_color ? &fds->tex_color : &g_data.dummy_one);
212 }
213 else if (STR_ELEM(attr->name, "flame", "temperature")) {
215 grp, attr->input_name, fds->tex_flame ? &fds->tex_flame : &g_data.dummy_zero);
216 }
217 else {
219 grp, attr->input_name, grid_default_texture(attr->default_value));
220 }
221 volume_infos.grids_xform[grid_id++] = float4x4::identity();
222 }
223
224 bool use_constant_color = ((fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 &&
226 if (use_constant_color) {
227 volume_infos.color_mul = float4(UNPACK3(fds->active_color), 1.0f);
228 }
229
230 /* Output is such that 0..1 maps to 0..1000K */
231 volume_infos.temperature_mul = fds->flame_max_temp - fds->flame_ignition;
232 volume_infos.temperature_bias = fds->flame_ignition;
233 }
234 else {
235 grp = DRW_shgroup_create_sub(grp);
236
237 int grid_id = 0;
238 LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attrs) {
240 grp, attr->input_name, grid_default_texture(attr->default_value));
241 volume_infos.grids_xform[grid_id++] = float4x4::identity();
242 }
243 }
244
245 volume_infos.push_update();
246
247 DRW_shgroup_uniform_block(grp, "drw_volume", volume_infos);
248
249 return grp;
250}
251
253{
254 /* Bind default volume grid textures. */
255 LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attrs) {
256 DRW_shgroup_uniform_texture(grp, attr->input_name, grid_default_texture(attr->default_value));
257 }
258 return grp;
259}
260
262 Object *ob,
263 DRWShadingGroup *shgrp,
264 GPUMaterial *gpu_material)
265{
266 ListBase attrs = GPU_material_attributes(gpu_material);
267
268 if (ob == nullptr) {
269 return drw_volume_world_grids_init(&attrs, shgrp);
270 }
271 if (ob->type == OB_VOLUME) {
272 return drw_volume_object_grids_init(ob, &attrs, shgrp);
273 }
274 return drw_volume_object_mesh_init(scene, ob, &attrs, shgrp);
275}
276
277/* -------------------------------------------------------------------- */
281namespace blender::draw {
282
283template<typename PassType>
285{
286 PassType *sub = &ps.sub("World Volume");
287 for (const GPUMaterialAttribute *attr : attrs) {
288 sub->bind_texture(attr->input_name, grid_default_texture(attr->default_value));
289 }
290
291 return sub;
292}
293
294template<typename PassType>
296 Object *ob,
298{
299 Volume *volume = (Volume *)ob->data;
300 BKE_volume_load(volume, G.main);
301
302 /* Render nothing if there is no attribute. */
303 if (BKE_volume_num_grids(volume) == 0) {
304 return nullptr;
305 }
306
308 VolumeInfosBuf &volume_infos = *pool->alloc();
309
310 volume_infos.density_scale = BKE_volume_density_scale(volume, ob->object_to_world().ptr());
311 volume_infos.color_mul = float4(1.0f);
312 volume_infos.temperature_mul = 1.0f;
313 volume_infos.temperature_bias = 0.0f;
314
315 PassType *sub = &ps.sub("Volume Object SubPass");
316
317 /* Bind volume grid textures. */
318 int grid_id = 0;
319 for (const GPUMaterialAttribute *attr : attrs) {
320 const blender::bke::VolumeGridData *volume_grid = BKE_volume_grid_find(volume, attr->name);
321 const DRWVolumeGrid *drw_grid = (volume_grid) ?
322 DRW_volume_batch_cache_get_grid(volume, volume_grid) :
323 nullptr;
324 /* Handle 3 cases here:
325 * - Grid exists and texture was loaded -> use texture.
326 * - Grid exists but has zero size or failed to load -> use zero.
327 * - Grid does not exist -> use default value. */
328 const GPUTexture *grid_tex = (drw_grid) ? drw_grid->texture :
329 (volume_grid) ? g_data.dummy_zero :
330 grid_default_texture(attr->default_value);
331 /* TODO(@pragma37): bind_texture const support ? */
332 sub->bind_texture(attr->input_name, (GPUTexture *)grid_tex);
333
334 volume_infos.grids_xform[grid_id++] = drw_grid ? float4x4(drw_grid->object_to_texture) :
336 }
337
338 volume_infos.push_update();
339
340 sub->bind_ubo("drw_volume", volume_infos);
341
342 return sub;
343}
344
345template<typename PassType>
347 Scene *scene,
348 Object *ob,
350{
352 VolumeInfosBuf &volume_infos = *pool->alloc();
353
354 ModifierData *md = nullptr;
355
356 volume_infos.density_scale = 1.0f;
357 volume_infos.color_mul = float4(1.0f);
358 volume_infos.temperature_mul = 1.0f;
359 volume_infos.temperature_bias = 0.0f;
360
361 bool has_fluid_modifier = (md = BKE_modifiers_findby_type(ob, eModifierType_Fluid)) &&
363 ((FluidModifierData *)md)->domain != nullptr;
364 FluidModifierData *fmd = has_fluid_modifier ? (FluidModifierData *)md : nullptr;
365 FluidDomainSettings *fds = has_fluid_modifier ? fmd->domain : nullptr;
366
367 PassType *sub = nullptr;
368
369 if (!has_fluid_modifier || (fds->type != FLUID_DOMAIN_TYPE_GAS)) {
370 /* No volume attributes or fluid domain. */
371 sub = &ps.sub("Volume Mesh SubPass");
372 int grid_id = 0;
373 for (const GPUMaterialAttribute *attr : attrs) {
374 sub->bind_texture(attr->input_name, grid_default_texture(attr->default_value));
375 volume_infos.grids_xform[grid_id++] = float4x4::identity();
376 }
377 }
378 else if (fds->fluid) {
379 /* Smoke Simulation. */
381
382 sub = &ps.sub("Volume Modifier SubPass");
383
384 float3 location, scale;
385 BKE_mesh_texspace_get(static_cast<Mesh *>(ob->data), location, scale);
386 float3 orco_mul = math::safe_rcp(scale * 2.0);
387 float3 orco_add = (location - scale) * -orco_mul;
388 /* Replace OrcoTexCoFactors with a matrix multiplication. */
389 float4x4 orco_mat = math::from_scale<float4x4>(orco_mul);
390 orco_mat.location() = orco_add;
391
392 int grid_id = 0;
393 for (const GPUMaterialAttribute *attr : attrs) {
394 if (STREQ(attr->name, "density")) {
395 sub->bind_texture(attr->input_name,
396 fds->tex_density ? &fds->tex_density : &g_data.dummy_one);
397 }
398 else if (STREQ(attr->name, "color")) {
399 sub->bind_texture(attr->input_name, fds->tex_color ? &fds->tex_color : &g_data.dummy_one);
400 }
401 else if (STR_ELEM(attr->name, "flame", "temperature")) {
402 sub->bind_texture(attr->input_name, fds->tex_flame ? &fds->tex_flame : &g_data.dummy_zero);
403 }
404 else {
405 sub->bind_texture(attr->input_name, grid_default_texture(attr->default_value));
406 }
407 volume_infos.grids_xform[grid_id++] = orco_mat;
408 }
409
410 bool use_constant_color = ((fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 &&
412 if (use_constant_color) {
413 volume_infos.color_mul = float4(UNPACK3(fds->active_color), 1.0f);
414 }
415
416 /* Output is such that 0..1 maps to 0..1000K. */
417 volume_infos.temperature_mul = fds->flame_max_temp - fds->flame_ignition;
418 volume_infos.temperature_bias = fds->flame_ignition;
419 }
420
421 if (sub) {
422 volume_infos.push_update();
423 sub->bind_ubo("drw_volume", volume_infos);
424 }
425
426 return sub;
427}
428
429template<typename PassType>
431 Scene *scene,
432 Object *ob,
433 GPUMaterial *gpu_material)
434{
435 ListBase attr_list = GPU_material_attributes(gpu_material);
437 if (ob == nullptr) {
438 return volume_world_grids_init(ps, attrs);
439 }
440 else if (ob->type == OB_VOLUME) {
441 return volume_object_grids_init(ps, ob, attrs);
442 }
443 else {
444 return drw_volume_object_mesh_init(ps, scene, ob, attrs);
445 }
446}
447
449 Scene *scene,
450 Object *ob,
451 GPUMaterial *gpu_material)
452{
453 return volume_sub_pass_implementation(ps, scene, ob, gpu_material);
454}
455
457 Scene *scene,
458 Object *ob,
459 GPUMaterial *gpu_material)
460{
461 return volume_sub_pass_implementation(ps, scene, ob, gpu_material);
462}
463
464} // namespace blender::draw
465
void BKE_mesh_texspace_get(Mesh *mesh, float r_texspace_location[3], float r_texspace_size[3])
bool BKE_modifier_is_enabled(const Scene *scene, ModifierData *md, int required_mode)
ModifierData * BKE_modifiers_findby_type(const Object *ob, ModifierType type)
Volume data-block.
int BKE_volume_num_grids(const Volume *volume)
bool BKE_volume_load(const Volume *volume, const Main *bmain)
const blender::bke::VolumeGridData * BKE_volume_grid_find(const Volume *volume, const char *name)
Volume data-block rendering and viewport drawing utilities.
float BKE_volume_density_scale(const Volume *volume, const float matrix[4][4])
#define LISTBASE_FOREACH(type, var, list)
#define STR_ELEM(...)
Definition BLI_string.h:653
unsigned int uint
#define UNPACK3(a)
#define STREQ(a, b)
@ FLUID_DOMAIN_TYPE_GAS
@ FLUID_DOMAIN_ACTIVE_COLORS
@ FLUID_DOMAIN_ACTIVE_COLOR_SET
@ FLUID_DOMAIN_USE_NOISE
@ eModifierMode_Realtime
@ eModifierType_Fluid
@ OB_VOLUME
#define DRW_shgroup_uniform_block(shgroup, name, ubo)
eGPUDefaultValue
@ GPU_DEFAULT_1
@ GPU_DEFAULT_0
ListBase GPU_material_attributes(const GPUMaterial *material)
void GPU_texture_extend_mode(GPUTexture *texture, GPUSamplerExtendMode extend_mode)
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_SAMPLER_EXTEND_MODE_REPEAT
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)
void DRW_smoke_ensure(FluidModifierData *fmd, int highres)
DRWManager DST
void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, const char *name, const GPUTexture *tex)
DRWShadingGroup * DRW_shgroup_create_sub(DRWShadingGroup *shgroup)
void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, const char *name, GPUTexture **tex)
void DRW_volume_ubos_pool_free(void *pool)
static DRWShadingGroup * drw_volume_object_grids_init(Object *ob, ListBase *attrs, DRWShadingGroup *grp)
static GPUTexture * grid_default_texture(eGPUDefaultValue default_value)
DRWShadingGroup * DRW_shgroup_volume_create_sub(Scene *scene, Object *ob, DRWShadingGroup *shgrp, GPUMaterial *gpu_material)
void DRW_volume_free()
static struct @301 g_data
void DRW_volume_init(DRWData *drw_data)
static DRWShadingGroup * drw_volume_object_mesh_init(Scene *scene, Object *ob, ListBase *attrs, DRWShadingGroup *grp)
static void drw_volume_globals_init()
GPUTexture * dummy_zero
GPUTexture * dummy_one
static DRWShadingGroup * drw_volume_world_grids_init(ListBase *attrs, DRWShadingGroup *grp)
blender::draw::UniformBuffer< VolumeInfos > VolumeInfosBuf
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
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
PassType
#define G(x, y, z)
PassType * volume_object_grids_init(PassType &ps, Object *ob, ListBaseWrapper< GPUMaterialAttribute > &attrs)
PassType * volume_world_grids_init(PassType &ps, ListBaseWrapper< GPUMaterialAttribute > &attrs)
PassType * volume_sub_pass_implementation(PassType &ps, Scene *scene, Object *ob, GPUMaterial *gpu_material)
PassType * drw_volume_object_mesh_init(PassType &ps, Scene *scene, Object *ob, ListBaseWrapper< GPUMaterialAttribute > &attrs)
PassMain::Sub * volume_sub_pass(PassMain::Sub &ps, Scene *scene, Object *ob, GPUMaterial *gpu_material)
DRWVolumeGrid * DRW_volume_batch_cache_get_grid(Volume *volume, const bke::VolumeGridData *volume_grid)
T safe_rcp(const T &a)
MatBase< T, NumCol, NumRow > scale(const MatBase< T, NumCol, NumRow > &mat, const VectorT &scale)
MatT from_scale(const VecBase< typename MatT::base_type, ScaleDim > &scale)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
void * volume_grids_ubos
DRWData * vmempool
blender::float4x4 object_to_texture
GPUTexture * texture
struct GPUTexture * tex_density
struct GPUTexture * tex_color
struct GPUTexture * tex_flame
struct FluidDomainSettings * domain
Vector< VolumeInfosBuf * > ubos
VolumeInfosBuf * alloc()