Files
test2/source/blender/draw/intern/draw_volume.cc
Campbell Barton e955c94ed3 License Headers: Set copyright to "Blender Authors", add AUTHORS
Listing the "Blender Foundation" as copyright holder implied the Blender
Foundation holds copyright to files which may include work from many
developers.

While keeping copyright on headers makes sense for isolated libraries,
Blender's own code may be refactored or moved between files in a way
that makes the per file copyright holders less meaningful.

Copyright references to the "Blender Foundation" have been replaced with
"Blender Authors", with the exception of `./extern/` since these this
contains libraries which are more isolated, any changed to license
headers there can be handled on a case-by-case basis.

Some directories in `./intern/` have also been excluded:

- `./intern/cycles/` it's own `AUTHORS` file is planned.
- `./intern/opensubdiv/`.

An "AUTHORS" file has been added, using the chromium projects authors
file as a template.

Design task: #110784

Ref !110783.
2023-08-16 00:20:26 +10:00

476 lines
15 KiB
C++

/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*
* \brief Contains Volume object GPU attributes configuration.
*/
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.h"
#include "DNA_fluid_types.h"
#include "DNA_volume_types.h"
#include "BKE_fluid.h"
#include "BKE_global.h"
#include "BKE_mesh.hh"
#include "BKE_modifier.h"
#include "BKE_volume.h"
#include "BKE_volume_render.h"
#include "GPU_material.h"
#include "draw_common.h"
#include "draw_manager.h"
#include "draw_common.hh"
using namespace blender;
using namespace blender::draw;
using VolumeInfosBuf = blender::draw::UniformBuffer<VolumeInfos>;
static struct {
GPUTexture *dummy_zero;
GPUTexture *dummy_one;
float dummy_grid_mat[4][4];
} g_data = {};
struct VolumeUniformBufPool {
Vector<VolumeInfosBuf *> ubos;
uint used = 0;
~VolumeUniformBufPool()
{
for (VolumeInfosBuf *ubo : ubos) {
delete ubo;
}
}
void reset()
{
used = 0;
}
VolumeInfosBuf *alloc()
{
if (used >= ubos.size()) {
VolumeInfosBuf *buf = new VolumeInfosBuf();
ubos.append(buf);
}
return ubos[used++];
}
};
void DRW_volume_ubos_pool_free(void *pool)
{
delete reinterpret_cast<VolumeUniformBufPool *>(pool);
}
static void drw_volume_globals_init()
{
const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f};
const float one[4] = {1.0f, 1.0f, 1.0f, 1.0f};
g_data.dummy_zero = GPU_texture_create_3d(
"dummy_zero", 1, 1, 1, 1, GPU_RGBA8, GPU_TEXTURE_USAGE_SHADER_READ, zero);
g_data.dummy_one = GPU_texture_create_3d(
"dummy_one", 1, 1, 1, 1, GPU_RGBA8, GPU_TEXTURE_USAGE_SHADER_READ, one);
GPU_texture_extend_mode(g_data.dummy_zero, GPU_SAMPLER_EXTEND_MODE_REPEAT);
GPU_texture_extend_mode(g_data.dummy_one, GPU_SAMPLER_EXTEND_MODE_REPEAT);
memset(g_data.dummy_grid_mat, 0, sizeof(g_data.dummy_grid_mat));
}
void DRW_volume_free()
{
GPU_TEXTURE_FREE_SAFE(g_data.dummy_zero);
GPU_TEXTURE_FREE_SAFE(g_data.dummy_one);
}
static GPUTexture *grid_default_texture(eGPUDefaultValue default_value)
{
if (g_data.dummy_one == nullptr) {
drw_volume_globals_init();
}
switch (default_value) {
case GPU_DEFAULT_0:
return g_data.dummy_zero;
case GPU_DEFAULT_1:
return g_data.dummy_one;
}
return g_data.dummy_zero;
}
void DRW_volume_init(DRWData *drw_data)
{
if (drw_data->volume_grids_ubos == nullptr) {
drw_data->volume_grids_ubos = new VolumeUniformBufPool();
}
VolumeUniformBufPool *pool = (VolumeUniformBufPool *)drw_data->volume_grids_ubos;
pool->reset();
if (g_data.dummy_one == nullptr) {
drw_volume_globals_init();
}
}
static DRWShadingGroup *drw_volume_object_grids_init(Object *ob,
ListBase *attrs,
DRWShadingGroup *grp)
{
VolumeUniformBufPool *pool = (VolumeUniformBufPool *)DST.vmempool->volume_grids_ubos;
VolumeInfosBuf &volume_infos = *pool->alloc();
Volume *volume = (Volume *)ob->data;
BKE_volume_load(volume, G.main);
grp = DRW_shgroup_create_sub(grp);
volume_infos.density_scale = BKE_volume_density_scale(volume, ob->object_to_world);
volume_infos.color_mul = float4(1.0f);
volume_infos.temperature_mul = 1.0f;
volume_infos.temperature_bias = 0.0f;
/* Bind volume grid textures. */
int grid_id = 0, grids_len = 0;
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attrs) {
const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, attr->name);
const DRWVolumeGrid *drw_grid = (volume_grid) ?
DRW_volume_batch_cache_get_grid(volume, volume_grid) :
nullptr;
/* Count number of valid attributes. */
grids_len += int(volume_grid != nullptr);
/* Handle 3 cases here:
* - Grid exists and texture was loaded -> use texture.
* - Grid exists but has zero size or failed to load -> use zero.
* - Grid does not exist -> use default value. */
const GPUTexture *grid_tex = (drw_grid) ? drw_grid->texture :
(volume_grid) ? g_data.dummy_zero :
grid_default_texture(attr->default_value);
DRW_shgroup_uniform_texture(grp, attr->input_name, grid_tex);
copy_m4_m4(volume_infos.grids_xform[grid_id++].ptr(),
(drw_grid) ? drw_grid->object_to_texture : g_data.dummy_grid_mat);
}
/* Render nothing if there is no attribute for the shader to render.
* This also avoids an assert caused by the bounding box being zero in size. */
if (grids_len == 0) {
return nullptr;
}
volume_infos.push_update();
DRW_shgroup_uniform_block(grp, "drw_volume", volume_infos);
return grp;
}
static DRWShadingGroup *drw_volume_object_mesh_init(Scene *scene,
Object *ob,
ListBase *attrs,
DRWShadingGroup *grp)
{
VolumeUniformBufPool *pool = (VolumeUniformBufPool *)DST.vmempool->volume_grids_ubos;
VolumeInfosBuf &volume_infos = *pool->alloc();
ModifierData *md = nullptr;
volume_infos.density_scale = 1.0f;
volume_infos.color_mul = float4(1.0f);
volume_infos.temperature_mul = 1.0f;
volume_infos.temperature_bias = 0.0f;
/* Smoke Simulation */
if ((md = BKE_modifiers_findby_type(ob, eModifierType_Fluid)) &&
BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime) &&
((FluidModifierData *)md)->domain != nullptr)
{
FluidModifierData *fmd = (FluidModifierData *)md;
FluidDomainSettings *fds = fmd->domain;
/* Don't try to show liquid domains here. */
if (!fds->fluid || !(fds->type == FLUID_DOMAIN_TYPE_GAS)) {
return nullptr;
}
if (fds->fluid && (fds->type == FLUID_DOMAIN_TYPE_GAS)) {
DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE);
}
grp = DRW_shgroup_create_sub(grp);
int grid_id = 0;
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attrs) {
if (STREQ(attr->name, "density")) {
DRW_shgroup_uniform_texture_ref(
grp, attr->input_name, fds->tex_density ? &fds->tex_density : &g_data.dummy_one);
}
else if (STREQ(attr->name, "color")) {
DRW_shgroup_uniform_texture_ref(
grp, attr->input_name, fds->tex_color ? &fds->tex_color : &g_data.dummy_one);
}
else if (STR_ELEM(attr->name, "flame", "temperature")) {
DRW_shgroup_uniform_texture_ref(
grp, attr->input_name, fds->tex_flame ? &fds->tex_flame : &g_data.dummy_zero);
}
else {
DRW_shgroup_uniform_texture(
grp, attr->input_name, grid_default_texture(attr->default_value));
}
copy_m4_m4(volume_infos.grids_xform[grid_id++].ptr(), g_data.dummy_grid_mat);
}
bool use_constant_color = ((fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 &&
(fds->active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0);
if (use_constant_color) {
volume_infos.color_mul = float4(UNPACK3(fds->active_color), 1.0f);
}
/* Output is such that 0..1 maps to 0..1000K */
volume_infos.temperature_mul = fds->flame_max_temp - fds->flame_ignition;
volume_infos.temperature_bias = fds->flame_ignition;
}
else {
grp = DRW_shgroup_create_sub(grp);
int grid_id = 0;
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attrs) {
DRW_shgroup_uniform_texture(
grp, attr->input_name, grid_default_texture(attr->default_value));
copy_m4_m4(volume_infos.grids_xform[grid_id++].ptr(), g_data.dummy_grid_mat);
}
}
volume_infos.push_update();
DRW_shgroup_uniform_block(grp, "drw_volume", volume_infos);
return grp;
}
static DRWShadingGroup *drw_volume_world_grids_init(ListBase *attrs, DRWShadingGroup *grp)
{
/* Bind default volume grid textures. */
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attrs) {
DRW_shgroup_uniform_texture(grp, attr->input_name, grid_default_texture(attr->default_value));
}
return grp;
}
DRWShadingGroup *DRW_shgroup_volume_create_sub(Scene *scene,
Object *ob,
DRWShadingGroup *shgrp,
GPUMaterial *gpu_material)
{
ListBase attrs = GPU_material_attributes(gpu_material);
if (ob == nullptr) {
return drw_volume_world_grids_init(&attrs, shgrp);
}
if (ob->type == OB_VOLUME) {
return drw_volume_object_grids_init(ob, &attrs, shgrp);
}
return drw_volume_object_mesh_init(scene, ob, &attrs, shgrp);
}
/* -------------------------------------------------------------------- */
/** \name New Draw Manager implementation
* \{ */
namespace blender::draw {
template<typename PassType>
PassType *volume_world_grids_init(PassType &ps, ListBaseWrapper<GPUMaterialAttribute> &attrs)
{
PassType *sub = &ps.sub("World Volume");
for (const GPUMaterialAttribute *attr : attrs) {
sub->bind_texture(attr->input_name, grid_default_texture(attr->default_value));
}
return sub;
}
template<typename PassType>
PassType *volume_object_grids_init(PassType &ps,
Object *ob,
ListBaseWrapper<GPUMaterialAttribute> &attrs)
{
VolumeUniformBufPool *pool = (VolumeUniformBufPool *)DST.vmempool->volume_grids_ubos;
VolumeInfosBuf &volume_infos = *pool->alloc();
Volume *volume = (Volume *)ob->data;
BKE_volume_load(volume, G.main);
volume_infos.density_scale = BKE_volume_density_scale(volume, ob->object_to_world);
volume_infos.color_mul = float4(1.0f);
volume_infos.temperature_mul = 1.0f;
volume_infos.temperature_bias = 0.0f;
bool has_grids = false;
for (const GPUMaterialAttribute *attr : attrs) {
if (BKE_volume_grid_find_for_read(volume, attr->name) != nullptr) {
has_grids = true;
break;
}
}
/* Render nothing if there is no attribute for the shader to render.
* This also avoids an assert caused by the bounding box being zero in size. */
if (!has_grids) {
return nullptr;
}
PassType *sub = &ps.sub("Volume Object SubPass");
/* Bind volume grid textures. */
int grid_id = 0;
for (const GPUMaterialAttribute *attr : attrs) {
const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, attr->name);
const DRWVolumeGrid *drw_grid = (volume_grid) ?
DRW_volume_batch_cache_get_grid(volume, volume_grid) :
nullptr;
/* Handle 3 cases here:
* - Grid exists and texture was loaded -> use texture.
* - Grid exists but has zero size or failed to load -> use zero.
* - Grid does not exist -> use default value. */
const GPUTexture *grid_tex = (drw_grid) ? drw_grid->texture :
(volume_grid) ? g_data.dummy_zero :
grid_default_texture(attr->default_value);
/* TODO(@pragma37): bind_texture const support ? */
sub->bind_texture(attr->input_name, (GPUTexture *)grid_tex);
volume_infos.grids_xform[grid_id++] = float4x4(drw_grid ? drw_grid->object_to_texture :
g_data.dummy_grid_mat);
}
volume_infos.push_update();
sub->bind_ubo("drw_volume", volume_infos);
return sub;
}
template<typename PassType>
PassType *drw_volume_object_mesh_init(PassType &ps,
Scene *scene,
Object *ob,
ListBaseWrapper<GPUMaterialAttribute> &attrs)
{
VolumeUniformBufPool *pool = (VolumeUniformBufPool *)DST.vmempool->volume_grids_ubos;
VolumeInfosBuf &volume_infos = *pool->alloc();
ModifierData *md = nullptr;
volume_infos.density_scale = 1.0f;
volume_infos.color_mul = float4(1.0f);
volume_infos.temperature_mul = 1.0f;
volume_infos.temperature_bias = 0.0f;
PassType *sub = nullptr;
/* Smoke Simulation. */
if ((md = BKE_modifiers_findby_type(ob, eModifierType_Fluid)) &&
BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime) &&
((FluidModifierData *)md)->domain != nullptr)
{
FluidModifierData *fmd = (FluidModifierData *)md;
FluidDomainSettings *fds = fmd->domain;
/* Don't try to show liquid domains here. */
if (!fds->fluid || !(fds->type == FLUID_DOMAIN_TYPE_GAS)) {
return nullptr;
}
if (fds->fluid && (fds->type == FLUID_DOMAIN_TYPE_GAS)) {
DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE);
}
sub = &ps.sub("Volume Modifier SubPass");
int grid_id = 0;
for (const GPUMaterialAttribute *attr : attrs) {
if (STREQ(attr->name, "density")) {
sub->bind_texture(attr->input_name,
fds->tex_density ? &fds->tex_density : &g_data.dummy_one);
}
else if (STREQ(attr->name, "color")) {
sub->bind_texture(attr->input_name, fds->tex_color ? &fds->tex_color : &g_data.dummy_one);
}
else if (STR_ELEM(attr->name, "flame", "temperature")) {
sub->bind_texture(attr->input_name, fds->tex_flame ? &fds->tex_flame : &g_data.dummy_zero);
}
else {
sub->bind_texture(attr->input_name, grid_default_texture(attr->default_value));
}
volume_infos.grids_xform[grid_id++] = float4x4(g_data.dummy_grid_mat);
}
bool use_constant_color = ((fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 &&
(fds->active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0);
if (use_constant_color) {
volume_infos.color_mul = float4(UNPACK3(fds->active_color), 1.0f);
}
/* Output is such that 0..1 maps to 0..1000K */
volume_infos.temperature_mul = fds->flame_max_temp - fds->flame_ignition;
volume_infos.temperature_bias = fds->flame_ignition;
}
else {
sub = &ps.sub("Volume Mesh SubPass");
int grid_id = 0;
for (const GPUMaterialAttribute *attr : attrs) {
sub->bind_texture(attr->input_name, grid_default_texture(attr->default_value));
volume_infos.grids_xform[grid_id++] = float4x4(g_data.dummy_grid_mat);
}
}
if (sub) {
volume_infos.push_update();
sub->bind_ubo("drw_volume", volume_infos);
}
return sub;
}
template<typename PassType>
PassType *volume_sub_pass_implementation(PassType &ps,
Scene *scene,
Object *ob,
GPUMaterial *gpu_material)
{
ListBase attr_list = GPU_material_attributes(gpu_material);
ListBaseWrapper<GPUMaterialAttribute> attrs(attr_list);
if (ob == nullptr) {
return volume_world_grids_init(ps, attrs);
}
else if (ob->type == OB_VOLUME) {
return volume_object_grids_init(ps, ob, attrs);
}
else {
return drw_volume_object_mesh_init(ps, scene, ob, attrs);
}
}
PassMain::Sub *volume_sub_pass(PassMain::Sub &ps,
Scene *scene,
Object *ob,
GPUMaterial *gpu_material)
{
return volume_sub_pass_implementation(ps, scene, ob, gpu_material);
}
PassSimple::Sub *volume_sub_pass(PassSimple::Sub &ps,
Scene *scene,
Object *ob,
GPUMaterial *gpu_material)
{
return volume_sub_pass_implementation(ps, scene, ob, gpu_material);
}
} // namespace blender::draw
/** \} */