Files
test2/source/blender/draw/intern/draw_fluid.c
Sergey Sharybin a12a8a71bb Remove "All Rights Reserved" from Blender Foundation copyright code
The goal is to solve confusion of the "All rights reserved" for licensing
code under an open-source license.

The phrase "All rights reserved" comes from a historical convention that
required this phrase for the copyright protection to apply. This convention
is no longer relevant.

However, even though the phrase has no meaning in establishing the copyright
it has not lost meaning in terms of licensing.

This change makes it so code under the Blender Foundation copyright does
not use "all rights reserved". This is also how the GPL license itself
states how to apply it to the source code:

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software ...

This change does not change copyright notice in cases when the copyright
is dual (BF and an author), or just an author of the code. It also does
mot change copyright which is inherited from NaN Holding BV as it needs
some further investigation about what is the proper way to handle it.
2023-03-30 10:51:59 +02:00

578 lines
17 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2005 Blender Foundation */
/** \file
* \ingroup gpu
*
* GPU fluid drawing functions.
*/
#include <string.h>
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "DNA_fluid_types.h"
#include "DNA_modifier_types.h"
#include "MEM_guardedalloc.h"
#include "BKE_colorband.h"
#include "IMB_colormanagement.h"
#include "GPU_texture.h"
#include "draw_manager.h"
#include "draw_common.h" /* Own include. */
#ifdef WITH_FLUID
# include "manta_fluid_API.h"
#endif
/* -------------------------------------------------------------------- */
/** \name Private API
* \{ */
#ifdef WITH_FLUID
enum {
TFUNC_FLAME_SPECTRUM = 0,
TFUNC_COLOR_RAMP = 1,
};
# define TFUNC_WIDTH 256
static void create_flame_spectrum_texture(float *data)
{
# define FIRE_THRESH 7
# define MAX_FIRE_ALPHA 0.06f
# define FULL_ON_FIRE 100
float *spec_pixels = (float *)MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float),
"spec_pixels");
IMB_colormanagement_blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000);
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) {
for (int k = 0; k < TFUNC_WIDTH; k++) {
int index = (j * TFUNC_WIDTH * 16 + i * TFUNC_WIDTH + k) * 4;
if (k >= FIRE_THRESH) {
spec_pixels[index] = (data[k * 4]);
spec_pixels[index + 1] = (data[k * 4 + 1]);
spec_pixels[index + 2] = (data[k * 4 + 2]);
spec_pixels[index + 3] = MAX_FIRE_ALPHA *
((k > FULL_ON_FIRE) ?
1.0f :
(k - FIRE_THRESH) / ((float)FULL_ON_FIRE - FIRE_THRESH));
}
else {
zero_v4(&spec_pixels[index]);
}
}
}
}
memcpy(data, spec_pixels, sizeof(float) * 4 * TFUNC_WIDTH);
MEM_freeN(spec_pixels);
# undef FIRE_THRESH
# undef MAX_FIRE_ALPHA
# undef FULL_ON_FIRE
}
static void create_color_ramp(const struct ColorBand *coba, float *data)
{
for (int i = 0; i < TFUNC_WIDTH; i++) {
BKE_colorband_evaluate(coba, (float)i / TFUNC_WIDTH, &data[i * 4]);
straight_to_premul_v4(&data[i * 4]);
}
}
static GPUTexture *create_transfer_function(int type, const struct ColorBand *coba)
{
float *data = (float *)MEM_mallocN(sizeof(float[4]) * TFUNC_WIDTH, __func__);
switch (type) {
case TFUNC_FLAME_SPECTRUM:
create_flame_spectrum_texture(data);
break;
case TFUNC_COLOR_RAMP:
create_color_ramp(coba, data);
break;
}
GPUTexture *tex = GPU_texture_create_1d(
"transf_func", TFUNC_WIDTH, 1, GPU_SRGB8_A8, GPU_TEXTURE_USAGE_SHADER_READ, data);
MEM_freeN(data);
return tex;
}
static void swizzle_texture_channel_single(GPUTexture *tex)
{
/* Swizzle texture channels so that we get useful RGBA values when sampling
* a texture with fewer channels, e.g. when using density as color. */
GPU_texture_swizzle_set(tex, "rrr1");
}
static float *rescale_3d(const int dim[3],
const int final_dim[3],
int channels,
const float *fpixels)
{
const uint w = dim[0], h = dim[1], d = dim[2];
const uint fw = final_dim[0], fh = final_dim[1], fd = final_dim[2];
const uint xf = w / fw, yf = h / fh, zf = d / fd;
const uint pixel_count = fw * fh * fd;
float *nfpixels = (float *)MEM_mallocN(channels * sizeof(float) * pixel_count, __func__);
if (nfpixels) {
printf("Performance: You need to scale a 3D texture, feel the pain!\n");
for (uint k = 0; k < fd; k++) {
for (uint j = 0; j < fh; j++) {
for (uint i = 0; i < fw; i++) {
/* Obviously doing nearest filtering here,
* it's going to be slow in any case, let's not make it worse. */
float xb = i * xf;
float yb = j * yf;
float zb = k * zf;
uint offset = k * (fw * fh) + i * fh + j;
uint offset_orig = (zb) * (w * h) + (xb)*h + (yb);
if (channels == 4) {
nfpixels[offset * 4] = fpixels[offset_orig * 4];
nfpixels[offset * 4 + 1] = fpixels[offset_orig * 4 + 1];
nfpixels[offset * 4 + 2] = fpixels[offset_orig * 4 + 2];
nfpixels[offset * 4 + 3] = fpixels[offset_orig * 4 + 3];
}
else if (channels == 1) {
nfpixels[offset] = fpixels[offset_orig];
}
else {
BLI_assert(0);
}
}
}
}
}
return nfpixels;
}
/* Will resize input to fit GL system limits. */
static GPUTexture *create_volume_texture(const int dim[3],
eGPUTextureFormat texture_format,
eGPUDataFormat data_format,
const void *data)
{
GPUTexture *tex = NULL;
int final_dim[3] = {UNPACK3(dim)};
if (data == NULL) {
return NULL;
}
while (1) {
tex = GPU_texture_create_3d("volume",
UNPACK3(final_dim),
1,
texture_format,
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW,
NULL);
if (tex != NULL) {
break;
}
if (final_dim[0] == 1 && final_dim[1] == 1 && final_dim[2] == 1) {
break;
}
for (int i = 0; i < 3; i++) {
final_dim[i] = max_ii(1, final_dim[i] / 2);
}
}
if (tex == NULL) {
printf("Error: Could not create 3D texture.\n");
tex = GPU_texture_create_error(3, false);
}
else if (equals_v3v3_int(dim, final_dim)) {
/* No need to resize, just upload the data. */
GPU_texture_update_sub(tex, data_format, data, 0, 0, 0, UNPACK3(final_dim));
}
else if (data_format != GPU_DATA_FLOAT) {
printf("Error: Could not allocate 3D texture and not attempting to rescale non-float data.\n");
tex = GPU_texture_create_error(3, false);
}
else {
/* We need to resize the input. */
int channels = ELEM(texture_format, GPU_R8, GPU_R16F, GPU_R32F) ? 1 : 4;
float *rescaled_data = rescale_3d(dim, final_dim, channels, data);
if (rescaled_data) {
GPU_texture_update_sub(tex, GPU_DATA_FLOAT, rescaled_data, 0, 0, 0, UNPACK3(final_dim));
MEM_freeN(rescaled_data);
}
else {
printf("Error: Could not allocate rescaled 3d texture!\n");
GPU_texture_free(tex);
tex = GPU_texture_create_error(3, false);
}
}
return tex;
}
static GPUTexture *create_field_texture(FluidDomainSettings *fds, bool single_precision)
{
void *field = NULL;
eGPUDataFormat data_format = GPU_DATA_FLOAT;
eGPUTextureFormat texture_format = GPU_R8;
if (single_precision) {
texture_format = GPU_R32F;
}
switch (fds->coba_field) {
case FLUID_DOMAIN_FIELD_DENSITY:
field = manta_smoke_get_density(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_HEAT:
field = manta_smoke_get_heat(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_FUEL:
field = manta_smoke_get_fuel(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_REACT:
field = manta_smoke_get_react(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_FLAME:
field = manta_smoke_get_flame(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_VELOCITY_X:
field = manta_get_velocity_x(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_VELOCITY_Y:
field = manta_get_velocity_y(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_VELOCITY_Z:
field = manta_get_velocity_z(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_COLOR_R:
field = manta_smoke_get_color_r(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_COLOR_G:
field = manta_smoke_get_color_g(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_COLOR_B:
field = manta_smoke_get_color_b(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_FORCE_X:
field = manta_get_force_x(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_FORCE_Y:
field = manta_get_force_y(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_FORCE_Z:
field = manta_get_force_z(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_PHI:
field = manta_get_phi(fds->fluid);
texture_format = GPU_R16F;
break;
case FLUID_DOMAIN_FIELD_PHI_IN:
field = manta_get_phi_in(fds->fluid);
texture_format = GPU_R16F;
break;
case FLUID_DOMAIN_FIELD_PHI_OUT:
field = manta_get_phiout_in(fds->fluid);
texture_format = GPU_R16F;
break;
case FLUID_DOMAIN_FIELD_PHI_OBSTACLE:
field = manta_get_phiobs_in(fds->fluid);
texture_format = GPU_R16F;
break;
case FLUID_DOMAIN_FIELD_FLAGS:
field = manta_smoke_get_flags(fds->fluid);
data_format = GPU_DATA_INT;
texture_format = GPU_R8UI;
break;
case FLUID_DOMAIN_FIELD_PRESSURE:
field = manta_get_pressure(fds->fluid);
texture_format = GPU_R16F;
break;
default:
return NULL;
}
if (field == NULL) {
return NULL;
}
GPUTexture *tex = create_volume_texture(fds->res, texture_format, data_format, field);
swizzle_texture_channel_single(tex);
return tex;
}
static GPUTexture *create_density_texture(FluidDomainSettings *fds, int highres)
{
int *dim = (highres) ? fds->res_noise : fds->res;
float *data;
if (highres) {
data = manta_noise_get_density(fds->fluid);
}
else {
data = manta_smoke_get_density(fds->fluid);
}
if (data == NULL) {
return NULL;
}
GPUTexture *tex = create_volume_texture(dim, GPU_R8, GPU_DATA_FLOAT, data);
swizzle_texture_channel_single(tex);
return tex;
}
static GPUTexture *create_color_texture(FluidDomainSettings *fds, int highres)
{
const bool has_color = (highres) ? manta_noise_has_colors(fds->fluid) :
manta_smoke_has_colors(fds->fluid);
if (!has_color) {
return NULL;
}
int cell_count = (highres) ? manta_noise_get_cells(fds->fluid) : fds->total_cells;
int *dim = (highres) ? fds->res_noise : fds->res;
float *data = (float *)MEM_callocN(sizeof(float) * cell_count * 4, "smokeColorTexture");
if (data == NULL) {
return NULL;
}
if (highres) {
manta_noise_get_rgba(fds->fluid, data, 0);
}
else {
manta_smoke_get_rgba(fds->fluid, data, 0);
}
GPUTexture *tex = create_volume_texture(dim, GPU_RGBA8, GPU_DATA_FLOAT, data);
MEM_freeN(data);
return tex;
}
static GPUTexture *create_flame_texture(FluidDomainSettings *fds, int highres)
{
float *source = NULL;
const bool has_fuel = (highres) ? manta_noise_has_fuel(fds->fluid) :
manta_smoke_has_fuel(fds->fluid);
int *dim = (highres) ? fds->res_noise : fds->res;
if (!has_fuel) {
return NULL;
}
if (highres) {
source = manta_noise_get_flame(fds->fluid);
}
else {
source = manta_smoke_get_flame(fds->fluid);
}
GPUTexture *tex = create_volume_texture(dim, GPU_R8, GPU_DATA_FLOAT, source);
swizzle_texture_channel_single(tex);
return tex;
}
static bool get_smoke_velocity_field(FluidDomainSettings *fds,
float **r_velocity_x,
float **r_velocity_y,
float **r_velocity_z)
{
const char vector_field = fds->vector_field;
switch ((FLUID_DisplayVectorField)vector_field) {
case FLUID_DOMAIN_VECTOR_FIELD_VELOCITY:
*r_velocity_x = manta_get_velocity_x(fds->fluid);
*r_velocity_y = manta_get_velocity_y(fds->fluid);
*r_velocity_z = manta_get_velocity_z(fds->fluid);
break;
case FLUID_DOMAIN_VECTOR_FIELD_GUIDE_VELOCITY:
*r_velocity_x = manta_get_guide_velocity_x(fds->fluid);
*r_velocity_y = manta_get_guide_velocity_y(fds->fluid);
*r_velocity_z = manta_get_guide_velocity_z(fds->fluid);
break;
case FLUID_DOMAIN_VECTOR_FIELD_FORCE:
*r_velocity_x = manta_get_force_x(fds->fluid);
*r_velocity_y = manta_get_force_y(fds->fluid);
*r_velocity_z = manta_get_force_z(fds->fluid);
break;
}
return *r_velocity_x && *r_velocity_y && *r_velocity_z;
}
#endif /* WITH_FLUID */
/** \} */
/* -------------------------------------------------------------------- */
/** \name Public API
* \{ */
void DRW_smoke_ensure_coba_field(FluidModifierData *fmd)
{
#ifndef WITH_FLUID
UNUSED_VARS(fmd);
#else
if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
FluidDomainSettings *fds = fmd->domain;
if (!fds->tex_field) {
fds->tex_field = create_field_texture(fds, false);
BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_field));
}
if (!fds->tex_coba && !ELEM(fds->coba_field,
FLUID_DOMAIN_FIELD_PHI,
FLUID_DOMAIN_FIELD_PHI_IN,
FLUID_DOMAIN_FIELD_PHI_OUT,
FLUID_DOMAIN_FIELD_PHI_OBSTACLE,
FLUID_DOMAIN_FIELD_FLAGS,
FLUID_DOMAIN_FIELD_PRESSURE)) {
fds->tex_coba = create_transfer_function(TFUNC_COLOR_RAMP, fds->coba);
BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_coba));
}
}
#endif
}
void DRW_smoke_ensure(FluidModifierData *fmd, int highres)
{
#ifndef WITH_FLUID
UNUSED_VARS(fmd, highres);
#else
if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
FluidDomainSettings *fds = fmd->domain;
if (!fds->tex_density) {
fds->tex_density = create_density_texture(fds, highres);
BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_density));
}
if (!fds->tex_color) {
fds->tex_color = create_color_texture(fds, highres);
BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_color));
}
if (!fds->tex_flame) {
fds->tex_flame = create_flame_texture(fds, highres);
BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_flame));
}
if (!fds->tex_flame_coba && fds->tex_flame) {
fds->tex_flame_coba = create_transfer_function(TFUNC_FLAME_SPECTRUM, NULL);
BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_flame_coba));
}
if (!fds->tex_shadow) {
fds->tex_shadow = create_volume_texture(
fds->res, GPU_R8, GPU_DATA_FLOAT, manta_smoke_get_shadow(fds->fluid));
BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_shadow));
}
}
#endif /* WITH_FLUID */
}
void DRW_smoke_ensure_velocity(FluidModifierData *fmd)
{
#ifndef WITH_FLUID
UNUSED_VARS(fmd);
#else
if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
FluidDomainSettings *fds = fmd->domain;
float *vel_x = NULL, *vel_y = NULL, *vel_z = NULL;
if (!get_smoke_velocity_field(fds, &vel_x, &vel_y, &vel_z)) {
fds->vector_field = FLUID_DOMAIN_VECTOR_FIELD_VELOCITY;
get_smoke_velocity_field(fds, &vel_x, &vel_y, &vel_z);
}
if (ELEM(NULL, vel_x, vel_y, vel_z)) {
return;
}
if (!fds->tex_velocity_x) {
fds->tex_velocity_x = GPU_texture_create_3d(
"velx", UNPACK3(fds->res), 1, GPU_R16F, GPU_TEXTURE_USAGE_SHADER_READ, vel_x);
fds->tex_velocity_y = GPU_texture_create_3d(
"vely", UNPACK3(fds->res), 1, GPU_R16F, GPU_TEXTURE_USAGE_SHADER_READ, vel_y);
fds->tex_velocity_z = GPU_texture_create_3d(
"velz", UNPACK3(fds->res), 1, GPU_R16F, GPU_TEXTURE_USAGE_SHADER_READ, vel_z);
BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_velocity_x));
BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_velocity_y));
BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_velocity_z));
}
}
#endif /* WITH_FLUID */
}
void DRW_fluid_ensure_flags(FluidModifierData *fmd)
{
#ifndef WITH_FLUID
UNUSED_VARS(fmd);
#else
if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
FluidDomainSettings *fds = fmd->domain;
if (!fds->tex_flags) {
fds->tex_flags = create_volume_texture(
fds->res, GPU_R8UI, GPU_DATA_INT, manta_smoke_get_flags(fds->fluid));
BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_flags));
swizzle_texture_channel_single(fds->tex_flags);
}
}
#endif /* WITH_FLUID */
}
void DRW_fluid_ensure_range_field(FluidModifierData *fmd)
{
#ifndef WITH_FLUID
UNUSED_VARS(fmd);
#else
if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
FluidDomainSettings *fds = fmd->domain;
if (!fds->tex_range_field) {
fds->tex_range_field = create_field_texture(fds, true);
BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_range_field));
}
}
#endif /* WITH_FLUID */
}
void DRW_smoke_init(DRWData *drw_data)
{
BLI_listbase_clear(&drw_data->smoke_textures);
}
void DRW_smoke_exit(DRWData *drw_data)
{
/* Free Smoke Textures after rendering */
/* XXX This is a waste of processing and GPU bandwidth if nothing
* is updated. But the problem is since Textures are stored in the
* modifier we don't want them to take precious VRAM if the
* modifier is not used for display. We should share them for
* all viewport in a redraw at least. */
LISTBASE_FOREACH (LinkData *, link, &drw_data->smoke_textures) {
GPU_TEXTURE_FREE_SAFE(*(GPUTexture **)link->data);
}
BLI_freelistN(&drw_data->smoke_textures);
}
/** \} */