1683 lines
48 KiB
C++
1683 lines
48 KiB
C++
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*
|
|
* Based on original code by Drew Whitehouse / Houdini Ocean Toolkit
|
|
* OpenMP hints by Christian Schnellhammer
|
|
*/
|
|
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_modifier_types.h"
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "BLI_math_vector.h"
|
|
#include "BLI_path_util.h"
|
|
#include "BLI_rand.h"
|
|
#include "BLI_task.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BKE_image.h"
|
|
#include "BKE_image_format.h"
|
|
#include "BKE_ocean.h"
|
|
#include "ocean_intern.h"
|
|
|
|
#include "IMB_imbuf.h"
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
#include "RE_texture.h"
|
|
|
|
#include "BLI_hash.h"
|
|
|
|
#ifdef WITH_OCEANSIM
|
|
|
|
/* Ocean code */
|
|
|
|
static float nextfr(RNG *rng, float min, float max)
|
|
{
|
|
return BLI_rng_get_float(rng) * (min - max) + max;
|
|
}
|
|
|
|
static float gaussRand(RNG *rng)
|
|
{
|
|
/* NOTE: to avoid numerical problems with very small numbers, we make these variables
|
|
* single-precision floats, but later we call the double-precision log() and sqrt() functions
|
|
* instead of logf() and sqrtf(). */
|
|
float x;
|
|
float y;
|
|
float length2;
|
|
|
|
do {
|
|
x = float(nextfr(rng, -1, 1));
|
|
y = float(nextfr(rng, -1, 1));
|
|
length2 = x * x + y * y;
|
|
} while (length2 >= 1 || length2 == 0);
|
|
|
|
return x * sqrtf(-2.0f * logf(length2) / length2);
|
|
}
|
|
|
|
/**
|
|
* Some useful functions
|
|
*/
|
|
MINLINE float catrom(float p0, float p1, float p2, float p3, float f)
|
|
{
|
|
return 0.5f * ((2.0f * p1) + (-p0 + p2) * f + (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * f * f +
|
|
(-p0 + 3.0f * p1 - 3.0f * p2 + p3) * f * f * f);
|
|
}
|
|
|
|
MINLINE float omega(float k, float depth)
|
|
{
|
|
return sqrtf(GRAVITY * k * tanhf(k * depth));
|
|
}
|
|
|
|
/* modified Phillips spectrum */
|
|
static float Ph(Ocean *o, float kx, float kz)
|
|
{
|
|
float tmp;
|
|
float k2 = kx * kx + kz * kz;
|
|
|
|
if (k2 == 0.0f) {
|
|
return 0.0f; /* no DC component */
|
|
}
|
|
|
|
/* damp out the waves going in the direction opposite the wind */
|
|
tmp = (o->_wx * kx + o->_wz * kz) / sqrtf(k2);
|
|
if (tmp < 0) {
|
|
tmp *= o->_damp_reflections;
|
|
}
|
|
|
|
return o->_A * expf(-1.0f / (k2 * (o->_L * o->_L))) * expf(-k2 * (o->_l * o->_l)) *
|
|
powf(fabsf(tmp), o->_wind_alignment) / (k2 * k2);
|
|
}
|
|
|
|
static void compute_eigenstuff(OceanResult *ocr, float jxx, float jzz, float jxz)
|
|
{
|
|
float a, b, qplus, qminus;
|
|
a = jxx + jzz;
|
|
b = sqrt((jxx - jzz) * (jxx - jzz) + 4 * jxz * jxz);
|
|
|
|
ocr->Jminus = 0.5f * (a - b);
|
|
ocr->Jplus = 0.5f * (a + b);
|
|
|
|
qplus = (ocr->Jplus - jxx) / jxz;
|
|
qminus = (ocr->Jminus - jxx) / jxz;
|
|
|
|
a = sqrt(1 + qplus * qplus);
|
|
b = sqrt(1 + qminus * qminus);
|
|
|
|
ocr->Eplus[0] = 1.0f / a;
|
|
ocr->Eplus[1] = 0.0f;
|
|
ocr->Eplus[2] = qplus / a;
|
|
|
|
ocr->Eminus[0] = 1.0f / b;
|
|
ocr->Eminus[1] = 0.0f;
|
|
ocr->Eminus[2] = qminus / b;
|
|
}
|
|
|
|
/*
|
|
* instead of Complex.h
|
|
* in fftw.h "fftw_complex" typedefed as double[2]
|
|
* below you can see functions are needed to work with such complex numbers.
|
|
*/
|
|
static void init_complex(fftw_complex cmpl, float real, float image)
|
|
{
|
|
cmpl[0] = real;
|
|
cmpl[1] = image;
|
|
}
|
|
|
|
static void add_comlex_c(fftw_complex res, const fftw_complex cmpl1, const fftw_complex cmpl2)
|
|
{
|
|
res[0] = cmpl1[0] + cmpl2[0];
|
|
res[1] = cmpl1[1] + cmpl2[1];
|
|
}
|
|
|
|
static void mul_complex_f(fftw_complex res, const fftw_complex cmpl, float f)
|
|
{
|
|
res[0] = cmpl[0] * double(f);
|
|
res[1] = cmpl[1] * double(f);
|
|
}
|
|
|
|
static void mul_complex_c(fftw_complex res, const fftw_complex cmpl1, const fftw_complex cmpl2)
|
|
{
|
|
fftwf_complex temp;
|
|
temp[0] = cmpl1[0] * cmpl2[0] - cmpl1[1] * cmpl2[1];
|
|
temp[1] = cmpl1[0] * cmpl2[1] + cmpl1[1] * cmpl2[0];
|
|
res[0] = temp[0];
|
|
res[1] = temp[1];
|
|
}
|
|
|
|
static float real_c(fftw_complex cmpl)
|
|
{
|
|
return cmpl[0];
|
|
}
|
|
|
|
static float image_c(fftw_complex cmpl)
|
|
{
|
|
return cmpl[1];
|
|
}
|
|
|
|
static void conj_complex(fftw_complex res, const fftw_complex cmpl1)
|
|
{
|
|
res[0] = cmpl1[0];
|
|
res[1] = -cmpl1[1];
|
|
}
|
|
|
|
static void exp_complex(fftw_complex res, fftw_complex cmpl)
|
|
{
|
|
float r = expf(cmpl[0]);
|
|
|
|
res[0] = cosf(cmpl[1]) * r;
|
|
res[1] = sinf(cmpl[1]) * r;
|
|
}
|
|
|
|
float BKE_ocean_jminus_to_foam(float jminus, float coverage)
|
|
{
|
|
float foam = jminus * -0.005f + coverage;
|
|
CLAMP(foam, 0.0f, 1.0f);
|
|
return foam;
|
|
}
|
|
|
|
void BKE_ocean_eval_uv(Ocean *oc, OceanResult *ocr, float u, float v)
|
|
{
|
|
int i0, i1, j0, j1;
|
|
float frac_x, frac_z;
|
|
float uu, vv;
|
|
|
|
/* first wrap the texture so 0 <= (u, v) < 1 */
|
|
u = fmodf(u, 1.0f);
|
|
v = fmodf(v, 1.0f);
|
|
|
|
if (u < 0) {
|
|
u += 1.0f;
|
|
}
|
|
if (v < 0) {
|
|
v += 1.0f;
|
|
}
|
|
|
|
BLI_rw_mutex_lock(&oc->oceanmutex, THREAD_LOCK_READ);
|
|
|
|
uu = u * oc->_M;
|
|
vv = v * oc->_N;
|
|
|
|
i0 = int(floor(uu));
|
|
j0 = int(floor(vv));
|
|
|
|
i1 = (i0 + 1);
|
|
j1 = (j0 + 1);
|
|
|
|
frac_x = uu - i0;
|
|
frac_z = vv - j0;
|
|
|
|
i0 = i0 % oc->_M;
|
|
j0 = j0 % oc->_N;
|
|
|
|
i1 = i1 % oc->_M;
|
|
j1 = j1 % oc->_N;
|
|
|
|
# define BILERP(m) \
|
|
interpf(interpf(m[i1 * oc->_N + j1], m[i0 * oc->_N + j1], frac_x), \
|
|
interpf(m[i1 * oc->_N + j0], m[i0 * oc->_N + j0], frac_x), \
|
|
frac_z)
|
|
|
|
{
|
|
if (oc->_do_disp_y) {
|
|
ocr->disp[1] = BILERP(oc->_disp_y);
|
|
}
|
|
|
|
if (oc->_do_normals) {
|
|
ocr->normal[0] = BILERP(oc->_N_x);
|
|
ocr->normal[1] = oc->_N_y /* BILERP(oc->_N_y) (MEM01) */;
|
|
ocr->normal[2] = BILERP(oc->_N_z);
|
|
}
|
|
|
|
if (oc->_do_chop) {
|
|
ocr->disp[0] = BILERP(oc->_disp_x);
|
|
ocr->disp[2] = BILERP(oc->_disp_z);
|
|
}
|
|
else {
|
|
ocr->disp[0] = 0.0;
|
|
ocr->disp[2] = 0.0;
|
|
}
|
|
|
|
if (oc->_do_jacobian) {
|
|
compute_eigenstuff(ocr, BILERP(oc->_Jxx), BILERP(oc->_Jzz), BILERP(oc->_Jxz));
|
|
}
|
|
}
|
|
# undef BILERP
|
|
|
|
BLI_rw_mutex_unlock(&oc->oceanmutex);
|
|
}
|
|
|
|
void BKE_ocean_eval_uv_catrom(Ocean *oc, OceanResult *ocr, float u, float v)
|
|
{
|
|
int i0, i1, i2, i3, j0, j1, j2, j3;
|
|
float frac_x, frac_z;
|
|
float uu, vv;
|
|
|
|
/* first wrap the texture so 0 <= (u, v) < 1 */
|
|
u = fmod(u, 1.0f);
|
|
v = fmod(v, 1.0f);
|
|
|
|
if (u < 0) {
|
|
u += 1.0f;
|
|
}
|
|
if (v < 0) {
|
|
v += 1.0f;
|
|
}
|
|
|
|
BLI_rw_mutex_lock(&oc->oceanmutex, THREAD_LOCK_READ);
|
|
|
|
uu = u * oc->_M;
|
|
vv = v * oc->_N;
|
|
|
|
i1 = int(floor(uu));
|
|
j1 = int(floor(vv));
|
|
|
|
i2 = (i1 + 1);
|
|
j2 = (j1 + 1);
|
|
|
|
frac_x = uu - i1;
|
|
frac_z = vv - j1;
|
|
|
|
i1 = i1 % oc->_M;
|
|
j1 = j1 % oc->_N;
|
|
|
|
i2 = i2 % oc->_M;
|
|
j2 = j2 % oc->_N;
|
|
|
|
i0 = (i1 - 1);
|
|
i3 = (i2 + 1);
|
|
i0 = i0 < 0 ? i0 + oc->_M : i0;
|
|
i3 = i3 >= oc->_M ? i3 - oc->_M : i3;
|
|
|
|
j0 = (j1 - 1);
|
|
j3 = (j2 + 1);
|
|
j0 = j0 < 0 ? j0 + oc->_N : j0;
|
|
j3 = j3 >= oc->_N ? j3 - oc->_N : j3;
|
|
|
|
# define INTERP(m) \
|
|
catrom(catrom(m[i0 * oc->_N + j0], \
|
|
m[i1 * oc->_N + j0], \
|
|
m[i2 * oc->_N + j0], \
|
|
m[i3 * oc->_N + j0], \
|
|
frac_x), \
|
|
catrom(m[i0 * oc->_N + j1], \
|
|
m[i1 * oc->_N + j1], \
|
|
m[i2 * oc->_N + j1], \
|
|
m[i3 * oc->_N + j1], \
|
|
frac_x), \
|
|
catrom(m[i0 * oc->_N + j2], \
|
|
m[i1 * oc->_N + j2], \
|
|
m[i2 * oc->_N + j2], \
|
|
m[i3 * oc->_N + j2], \
|
|
frac_x), \
|
|
catrom(m[i0 * oc->_N + j3], \
|
|
m[i1 * oc->_N + j3], \
|
|
m[i2 * oc->_N + j3], \
|
|
m[i3 * oc->_N + j3], \
|
|
frac_x), \
|
|
frac_z)
|
|
|
|
{
|
|
if (oc->_do_disp_y) {
|
|
ocr->disp[1] = INTERP(oc->_disp_y);
|
|
}
|
|
if (oc->_do_normals) {
|
|
ocr->normal[0] = INTERP(oc->_N_x);
|
|
ocr->normal[1] = oc->_N_y /* INTERP(oc->_N_y) (MEM01) */;
|
|
ocr->normal[2] = INTERP(oc->_N_z);
|
|
}
|
|
if (oc->_do_chop) {
|
|
ocr->disp[0] = INTERP(oc->_disp_x);
|
|
ocr->disp[2] = INTERP(oc->_disp_z);
|
|
}
|
|
else {
|
|
ocr->disp[0] = 0.0;
|
|
ocr->disp[2] = 0.0;
|
|
}
|
|
|
|
if (oc->_do_jacobian) {
|
|
compute_eigenstuff(ocr, INTERP(oc->_Jxx), INTERP(oc->_Jzz), INTERP(oc->_Jxz));
|
|
}
|
|
}
|
|
# undef INTERP
|
|
|
|
BLI_rw_mutex_unlock(&oc->oceanmutex);
|
|
}
|
|
|
|
void BKE_ocean_eval_xz(Ocean *oc, OceanResult *ocr, float x, float z)
|
|
{
|
|
BKE_ocean_eval_uv(oc, ocr, x / oc->_Lx, z / oc->_Lz);
|
|
}
|
|
|
|
void BKE_ocean_eval_xz_catrom(Ocean *oc, OceanResult *ocr, float x, float z)
|
|
{
|
|
BKE_ocean_eval_uv_catrom(oc, ocr, x / oc->_Lx, z / oc->_Lz);
|
|
}
|
|
|
|
void BKE_ocean_eval_ij(Ocean *oc, OceanResult *ocr, int i, int j)
|
|
{
|
|
BLI_rw_mutex_lock(&oc->oceanmutex, THREAD_LOCK_READ);
|
|
|
|
i = abs(i) % oc->_M;
|
|
j = abs(j) % oc->_N;
|
|
|
|
ocr->disp[1] = oc->_do_disp_y ? float(oc->_disp_y[i * oc->_N + j]) : 0.0f;
|
|
|
|
if (oc->_do_chop) {
|
|
ocr->disp[0] = oc->_disp_x[i * oc->_N + j];
|
|
ocr->disp[2] = oc->_disp_z[i * oc->_N + j];
|
|
}
|
|
else {
|
|
ocr->disp[0] = 0.0f;
|
|
ocr->disp[2] = 0.0f;
|
|
}
|
|
|
|
if (oc->_do_normals) {
|
|
ocr->normal[0] = oc->_N_x[i * oc->_N + j];
|
|
ocr->normal[1] = oc->_N_y /* oc->_N_y[i * oc->_N + j] (MEM01) */;
|
|
ocr->normal[2] = oc->_N_z[i * oc->_N + j];
|
|
|
|
normalize_v3(ocr->normal);
|
|
}
|
|
|
|
if (oc->_do_jacobian) {
|
|
compute_eigenstuff(
|
|
ocr, oc->_Jxx[i * oc->_N + j], oc->_Jzz[i * oc->_N + j], oc->_Jxz[i * oc->_N + j]);
|
|
}
|
|
|
|
BLI_rw_mutex_unlock(&oc->oceanmutex);
|
|
}
|
|
|
|
struct OceanSimulateData {
|
|
Ocean *o;
|
|
float t;
|
|
float scale;
|
|
float chop_amount;
|
|
};
|
|
|
|
static void ocean_compute_htilda(void *__restrict userdata,
|
|
const int i,
|
|
const TaskParallelTLS *__restrict /*tls*/)
|
|
{
|
|
OceanSimulateData *osd = static_cast<OceanSimulateData *>(userdata);
|
|
const Ocean *o = osd->o;
|
|
const float scale = osd->scale;
|
|
const float t = osd->t;
|
|
|
|
int j;
|
|
|
|
/* Note the <= _N/2 here, see the FFTW documentation
|
|
* about the mechanics of the complex->real fft storage. */
|
|
for (j = 0; j <= o->_N / 2; j++) {
|
|
fftw_complex exp_param1;
|
|
fftw_complex exp_param2;
|
|
fftw_complex conj_param;
|
|
|
|
init_complex(exp_param1, 0.0, omega(o->_k[i * (1 + o->_N / 2) + j], o->_depth) * t);
|
|
init_complex(exp_param2, 0.0, -omega(o->_k[i * (1 + o->_N / 2) + j], o->_depth) * t);
|
|
exp_complex(exp_param1, exp_param1);
|
|
exp_complex(exp_param2, exp_param2);
|
|
conj_complex(conj_param, o->_h0_minus[i * o->_N + j]);
|
|
|
|
mul_complex_c(exp_param1, o->_h0[i * o->_N + j], exp_param1);
|
|
mul_complex_c(exp_param2, conj_param, exp_param2);
|
|
|
|
add_comlex_c(o->_htilda[i * (1 + o->_N / 2) + j], exp_param1, exp_param2);
|
|
mul_complex_f(o->_fft_in[i * (1 + o->_N / 2) + j], o->_htilda[i * (1 + o->_N / 2) + j], scale);
|
|
}
|
|
}
|
|
|
|
static void ocean_compute_displacement_y(TaskPool *__restrict pool, void * /*taskdata*/)
|
|
{
|
|
OceanSimulateData *osd = static_cast<OceanSimulateData *>(BLI_task_pool_user_data(pool));
|
|
const Ocean *o = osd->o;
|
|
|
|
fftw_execute(o->_disp_y_plan);
|
|
}
|
|
|
|
static void ocean_compute_displacement_x(TaskPool *__restrict pool, void * /*taskdata*/)
|
|
{
|
|
OceanSimulateData *osd = static_cast<OceanSimulateData *>(BLI_task_pool_user_data(pool));
|
|
const Ocean *o = osd->o;
|
|
const float scale = osd->scale;
|
|
const float chop_amount = osd->chop_amount;
|
|
int i, j;
|
|
|
|
for (i = 0; i < o->_M; i++) {
|
|
for (j = 0; j <= o->_N / 2; j++) {
|
|
fftw_complex mul_param;
|
|
fftw_complex minus_i;
|
|
|
|
init_complex(minus_i, 0.0, -1.0);
|
|
init_complex(mul_param, -scale, 0);
|
|
mul_complex_f(mul_param, mul_param, chop_amount);
|
|
mul_complex_c(mul_param, mul_param, minus_i);
|
|
mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
|
|
mul_complex_f(mul_param,
|
|
mul_param,
|
|
((o->_k[i * (1 + o->_N / 2) + j] == 0.0f) ?
|
|
0.0f :
|
|
o->_kx[i] / o->_k[i * (1 + o->_N / 2) + j]));
|
|
init_complex(o->_fft_in_x[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
|
|
}
|
|
}
|
|
fftw_execute(o->_disp_x_plan);
|
|
}
|
|
|
|
static void ocean_compute_displacement_z(TaskPool *__restrict pool, void * /*taskdata*/)
|
|
{
|
|
OceanSimulateData *osd = static_cast<OceanSimulateData *>(BLI_task_pool_user_data(pool));
|
|
const Ocean *o = osd->o;
|
|
const float scale = osd->scale;
|
|
const float chop_amount = osd->chop_amount;
|
|
int i, j;
|
|
|
|
for (i = 0; i < o->_M; i++) {
|
|
for (j = 0; j <= o->_N / 2; j++) {
|
|
fftw_complex mul_param;
|
|
fftw_complex minus_i;
|
|
|
|
init_complex(minus_i, 0.0, -1.0);
|
|
init_complex(mul_param, -scale, 0);
|
|
mul_complex_f(mul_param, mul_param, chop_amount);
|
|
mul_complex_c(mul_param, mul_param, minus_i);
|
|
mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
|
|
mul_complex_f(mul_param,
|
|
mul_param,
|
|
((o->_k[i * (1 + o->_N / 2) + j] == 0.0f) ?
|
|
0.0f :
|
|
o->_kz[j] / o->_k[i * (1 + o->_N / 2) + j]));
|
|
init_complex(o->_fft_in_z[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
|
|
}
|
|
}
|
|
fftw_execute(o->_disp_z_plan);
|
|
}
|
|
|
|
static void ocean_compute_jacobian_jxx(TaskPool *__restrict pool, void * /*taskdata*/)
|
|
{
|
|
OceanSimulateData *osd = static_cast<OceanSimulateData *>(BLI_task_pool_user_data(pool));
|
|
const Ocean *o = osd->o;
|
|
const float chop_amount = osd->chop_amount;
|
|
int i, j;
|
|
|
|
for (i = 0; i < o->_M; i++) {
|
|
for (j = 0; j <= o->_N / 2; j++) {
|
|
fftw_complex mul_param;
|
|
|
|
// init_complex(mul_param, -scale, 0);
|
|
init_complex(mul_param, -1, 0);
|
|
|
|
mul_complex_f(mul_param, mul_param, chop_amount);
|
|
mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
|
|
mul_complex_f(mul_param,
|
|
mul_param,
|
|
((o->_k[i * (1 + o->_N / 2) + j] == 0.0f) ?
|
|
0.0f :
|
|
o->_kx[i] * o->_kx[i] / o->_k[i * (1 + o->_N / 2) + j]));
|
|
init_complex(o->_fft_in_jxx[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
|
|
}
|
|
}
|
|
fftw_execute(o->_Jxx_plan);
|
|
|
|
for (i = 0; i < o->_M; i++) {
|
|
for (j = 0; j < o->_N; j++) {
|
|
o->_Jxx[i * o->_N + j] += 1.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ocean_compute_jacobian_jzz(TaskPool *__restrict pool, void * /*taskdata*/)
|
|
{
|
|
OceanSimulateData *osd = static_cast<OceanSimulateData *>(BLI_task_pool_user_data(pool));
|
|
const Ocean *o = osd->o;
|
|
const float chop_amount = osd->chop_amount;
|
|
int i, j;
|
|
|
|
for (i = 0; i < o->_M; i++) {
|
|
for (j = 0; j <= o->_N / 2; j++) {
|
|
fftw_complex mul_param;
|
|
|
|
// init_complex(mul_param, -scale, 0);
|
|
init_complex(mul_param, -1, 0);
|
|
|
|
mul_complex_f(mul_param, mul_param, chop_amount);
|
|
mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
|
|
mul_complex_f(mul_param,
|
|
mul_param,
|
|
((o->_k[i * (1 + o->_N / 2) + j] == 0.0f) ?
|
|
0.0f :
|
|
o->_kz[j] * o->_kz[j] / o->_k[i * (1 + o->_N / 2) + j]));
|
|
init_complex(o->_fft_in_jzz[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
|
|
}
|
|
}
|
|
fftw_execute(o->_Jzz_plan);
|
|
|
|
for (i = 0; i < o->_M; i++) {
|
|
for (j = 0; j < o->_N; j++) {
|
|
o->_Jzz[i * o->_N + j] += 1.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ocean_compute_jacobian_jxz(TaskPool *__restrict pool, void * /*taskdata*/)
|
|
{
|
|
OceanSimulateData *osd = static_cast<OceanSimulateData *>(BLI_task_pool_user_data(pool));
|
|
const Ocean *o = osd->o;
|
|
const float chop_amount = osd->chop_amount;
|
|
int i, j;
|
|
|
|
for (i = 0; i < o->_M; i++) {
|
|
for (j = 0; j <= o->_N / 2; j++) {
|
|
fftw_complex mul_param;
|
|
|
|
// init_complex(mul_param, -scale, 0);
|
|
init_complex(mul_param, -1, 0);
|
|
|
|
mul_complex_f(mul_param, mul_param, chop_amount);
|
|
mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
|
|
mul_complex_f(mul_param,
|
|
mul_param,
|
|
((o->_k[i * (1 + o->_N / 2) + j] == 0.0f) ?
|
|
0.0f :
|
|
o->_kx[i] * o->_kz[j] / o->_k[i * (1 + o->_N / 2) + j]));
|
|
init_complex(o->_fft_in_jxz[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
|
|
}
|
|
}
|
|
fftw_execute(o->_Jxz_plan);
|
|
}
|
|
|
|
static void ocean_compute_normal_x(TaskPool *__restrict pool, void * /*taskdata*/)
|
|
{
|
|
OceanSimulateData *osd = static_cast<OceanSimulateData *>(BLI_task_pool_user_data(pool));
|
|
const Ocean *o = osd->o;
|
|
int i, j;
|
|
|
|
for (i = 0; i < o->_M; i++) {
|
|
for (j = 0; j <= o->_N / 2; j++) {
|
|
fftw_complex mul_param;
|
|
|
|
init_complex(mul_param, 0.0, -1.0);
|
|
mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
|
|
mul_complex_f(mul_param, mul_param, o->_kx[i]);
|
|
init_complex(o->_fft_in_nx[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
|
|
}
|
|
}
|
|
fftw_execute(o->_N_x_plan);
|
|
}
|
|
|
|
static void ocean_compute_normal_z(TaskPool *__restrict pool, void * /*taskdata*/)
|
|
{
|
|
OceanSimulateData *osd = static_cast<OceanSimulateData *>(BLI_task_pool_user_data(pool));
|
|
const Ocean *o = osd->o;
|
|
int i, j;
|
|
|
|
for (i = 0; i < o->_M; i++) {
|
|
for (j = 0; j <= o->_N / 2; j++) {
|
|
fftw_complex mul_param;
|
|
|
|
init_complex(mul_param, 0.0, -1.0);
|
|
mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
|
|
mul_complex_f(mul_param, mul_param, o->_kz[i]);
|
|
init_complex(o->_fft_in_nz[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
|
|
}
|
|
}
|
|
fftw_execute(o->_N_z_plan);
|
|
}
|
|
|
|
bool BKE_ocean_is_valid(const Ocean *o)
|
|
{
|
|
return o->_k != nullptr;
|
|
}
|
|
|
|
void BKE_ocean_simulate(Ocean *o, float t, float scale, float chop_amount)
|
|
{
|
|
TaskPool *pool;
|
|
|
|
OceanSimulateData osd;
|
|
|
|
scale *= o->normalize_factor;
|
|
|
|
osd.o = o;
|
|
osd.t = t;
|
|
osd.scale = scale;
|
|
osd.chop_amount = chop_amount;
|
|
|
|
pool = BLI_task_pool_create(&osd, TASK_PRIORITY_HIGH);
|
|
|
|
BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE);
|
|
|
|
/* Note about multi-threading here: we have to run a first set of computations (htilda one)
|
|
* before we can run all others, since they all depend on it.
|
|
* So we make a first parallelized forloop run for htilda,
|
|
* and then pack all other computations into a set of parallel tasks.
|
|
* This is not optimal in all cases,
|
|
* but remains reasonably simple and should be OK most of the time. */
|
|
|
|
/* compute a new htilda */
|
|
TaskParallelSettings settings;
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
settings.use_threading = (o->_M > 16);
|
|
BLI_task_parallel_range(0, o->_M, &osd, ocean_compute_htilda, &settings);
|
|
|
|
if (o->_do_disp_y) {
|
|
BLI_task_pool_push(pool, ocean_compute_displacement_y, nullptr, false, nullptr);
|
|
}
|
|
|
|
if (o->_do_chop) {
|
|
BLI_task_pool_push(pool, ocean_compute_displacement_x, nullptr, false, nullptr);
|
|
BLI_task_pool_push(pool, ocean_compute_displacement_z, nullptr, false, nullptr);
|
|
}
|
|
|
|
if (o->_do_jacobian) {
|
|
BLI_task_pool_push(pool, ocean_compute_jacobian_jxx, nullptr, false, nullptr);
|
|
BLI_task_pool_push(pool, ocean_compute_jacobian_jzz, nullptr, false, nullptr);
|
|
BLI_task_pool_push(pool, ocean_compute_jacobian_jxz, nullptr, false, nullptr);
|
|
}
|
|
|
|
if (o->_do_normals) {
|
|
BLI_task_pool_push(pool, ocean_compute_normal_x, nullptr, false, nullptr);
|
|
BLI_task_pool_push(pool, ocean_compute_normal_z, nullptr, false, nullptr);
|
|
o->_N_y = 1.0f / scale;
|
|
}
|
|
|
|
BLI_task_pool_work_and_wait(pool);
|
|
|
|
BLI_rw_mutex_unlock(&o->oceanmutex);
|
|
|
|
BLI_task_pool_free(pool);
|
|
}
|
|
|
|
static void set_height_normalize_factor(Ocean *oc)
|
|
{
|
|
float res = 1.0;
|
|
float max_h = 0.0;
|
|
|
|
int i, j;
|
|
|
|
if (!oc->_do_disp_y) {
|
|
return;
|
|
}
|
|
|
|
oc->normalize_factor = 1.0;
|
|
|
|
BKE_ocean_simulate(oc, 0.0, 1.0, 0);
|
|
|
|
BLI_rw_mutex_lock(&oc->oceanmutex, THREAD_LOCK_READ);
|
|
|
|
for (i = 0; i < oc->_M; i++) {
|
|
for (j = 0; j < oc->_N; j++) {
|
|
if (max_h < fabs(oc->_disp_y[i * oc->_N + j])) {
|
|
max_h = fabs(oc->_disp_y[i * oc->_N + j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
BLI_rw_mutex_unlock(&oc->oceanmutex);
|
|
|
|
if (max_h == 0.0f) {
|
|
max_h = 0.00001f; /* just in case ... */
|
|
}
|
|
|
|
res = 1.0f / (max_h);
|
|
|
|
oc->normalize_factor = res;
|
|
}
|
|
|
|
Ocean *BKE_ocean_add()
|
|
{
|
|
Ocean *oc = static_cast<Ocean *>(MEM_callocN(sizeof(Ocean), "ocean sim data"));
|
|
|
|
BLI_rw_mutex_init(&oc->oceanmutex);
|
|
|
|
return oc;
|
|
}
|
|
|
|
bool BKE_ocean_ensure(OceanModifierData *omd, const int resolution)
|
|
{
|
|
if (omd->ocean) {
|
|
/* Check that the ocean has the same resolution than we want now. */
|
|
if (omd->ocean->_M == resolution * resolution) {
|
|
return false;
|
|
}
|
|
|
|
BKE_ocean_free(omd->ocean);
|
|
}
|
|
|
|
omd->ocean = BKE_ocean_add();
|
|
BKE_ocean_init_from_modifier(omd->ocean, omd, resolution);
|
|
return true;
|
|
}
|
|
|
|
bool BKE_ocean_init_from_modifier(Ocean *ocean, OceanModifierData const *omd, const int resolution)
|
|
{
|
|
short do_heightfield, do_chop, do_normals, do_jacobian, do_spray;
|
|
|
|
do_heightfield = true;
|
|
do_chop = (omd->chop_amount > 0);
|
|
do_normals = (omd->flag & MOD_OCEAN_GENERATE_NORMALS);
|
|
do_jacobian = (omd->flag & MOD_OCEAN_GENERATE_FOAM);
|
|
do_spray = do_jacobian && (omd->flag & MOD_OCEAN_GENERATE_SPRAY);
|
|
|
|
BKE_ocean_free_data(ocean);
|
|
|
|
return BKE_ocean_init(ocean,
|
|
resolution * resolution,
|
|
resolution * resolution,
|
|
omd->spatial_size,
|
|
omd->spatial_size,
|
|
omd->wind_velocity,
|
|
omd->smallest_wave,
|
|
1.0,
|
|
omd->wave_direction,
|
|
omd->damp,
|
|
omd->wave_alignment,
|
|
omd->depth,
|
|
omd->time,
|
|
omd->spectrum,
|
|
omd->fetch_jonswap,
|
|
omd->sharpen_peak_jonswap,
|
|
do_heightfield,
|
|
do_chop,
|
|
do_spray,
|
|
do_normals,
|
|
do_jacobian,
|
|
omd->seed);
|
|
}
|
|
|
|
bool BKE_ocean_init(Ocean *o,
|
|
int M,
|
|
int N,
|
|
float Lx,
|
|
float Lz,
|
|
float V,
|
|
float l,
|
|
float A,
|
|
float w,
|
|
float damp,
|
|
float alignment,
|
|
float depth,
|
|
float time,
|
|
int spectrum,
|
|
float fetch_jonswap,
|
|
float sharpen_peak_jonswap,
|
|
short do_height_field,
|
|
short do_chop,
|
|
short do_spray,
|
|
short do_normals,
|
|
short do_jacobian,
|
|
int seed)
|
|
{
|
|
int i, j, ii;
|
|
|
|
BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE);
|
|
|
|
o->_M = M;
|
|
o->_N = N;
|
|
o->_V = V;
|
|
o->_l = l;
|
|
o->_A = A;
|
|
o->_w = w;
|
|
o->_damp_reflections = 1.0f - damp;
|
|
o->_wind_alignment = alignment * 10.0f;
|
|
o->_depth = depth;
|
|
o->_Lx = Lx;
|
|
o->_Lz = Lz;
|
|
o->_wx = cos(w);
|
|
o->_wz = -sin(w); /* wave direction */
|
|
o->_L = V * V / GRAVITY; /* largest wave for a given velocity V */
|
|
o->time = time;
|
|
|
|
/* Spectrum to use. */
|
|
o->_spectrum = spectrum;
|
|
|
|
/* Common JONSWAP parameters. */
|
|
o->_fetch_jonswap = fetch_jonswap;
|
|
o->_sharpen_peak_jonswap = sharpen_peak_jonswap * 10.0f;
|
|
|
|
/* NOTE: most modifiers don't account for failure to allocate.
|
|
* In this case however a large resolution can easily perform large allocations that fail,
|
|
* support early exiting in this case. */
|
|
if ((o->_k = (float *)MEM_mallocN(sizeof(float) * size_t(M) * (1 + N / 2), "ocean_k")) &&
|
|
(o->_h0 = (fftw_complex *)MEM_mallocN(sizeof(fftw_complex) * size_t(M) * N, "ocean_h0")) &&
|
|
(o->_h0_minus = (fftw_complex *)MEM_mallocN(sizeof(fftw_complex) * size_t(M) * N,
|
|
"ocean_h0_minus")) &&
|
|
(o->_kx = (float *)MEM_mallocN(sizeof(float) * o->_M, "ocean_kx")) &&
|
|
(o->_kz = (float *)MEM_mallocN(sizeof(float) * o->_N, "ocean_kz")))
|
|
{
|
|
/* Success. */
|
|
}
|
|
else {
|
|
MEM_SAFE_FREE(o->_k);
|
|
MEM_SAFE_FREE(o->_h0);
|
|
MEM_SAFE_FREE(o->_h0_minus);
|
|
MEM_SAFE_FREE(o->_kx);
|
|
MEM_SAFE_FREE(o->_kz);
|
|
|
|
BLI_rw_mutex_unlock(&o->oceanmutex);
|
|
return false;
|
|
}
|
|
|
|
o->_do_disp_y = do_height_field;
|
|
o->_do_normals = do_normals;
|
|
o->_do_spray = do_spray;
|
|
o->_do_chop = do_chop;
|
|
o->_do_jacobian = do_jacobian;
|
|
|
|
/* make this robust in the face of erroneous usage */
|
|
if (o->_Lx == 0.0f) {
|
|
o->_Lx = 0.001f;
|
|
}
|
|
|
|
if (o->_Lz == 0.0f) {
|
|
o->_Lz = 0.001f;
|
|
}
|
|
|
|
/* The +VE components and DC. */
|
|
for (i = 0; i <= o->_M / 2; i++) {
|
|
o->_kx[i] = 2.0f * float(M_PI) * i / o->_Lx;
|
|
}
|
|
|
|
/* The -VE components. */
|
|
for (i = o->_M - 1, ii = 0; i > o->_M / 2; i--, ii++) {
|
|
o->_kx[i] = -2.0f * float(M_PI) * ii / o->_Lx;
|
|
}
|
|
|
|
/* The +VE components and DC. */
|
|
for (i = 0; i <= o->_N / 2; i++) {
|
|
o->_kz[i] = 2.0f * float(M_PI) * i / o->_Lz;
|
|
}
|
|
|
|
/* The -VE components. */
|
|
for (i = o->_N - 1, ii = 0; i > o->_N / 2; i--, ii++) {
|
|
o->_kz[i] = -2.0f * float(M_PI) * ii / o->_Lz;
|
|
}
|
|
|
|
/* pre-calculate the k matrix */
|
|
for (i = 0; i < o->_M; i++) {
|
|
for (j = 0; j <= o->_N / 2; j++) {
|
|
o->_k[size_t(i) * (1 + o->_N / 2) + j] = sqrt(o->_kx[i] * o->_kx[i] + o->_kz[j] * o->_kz[j]);
|
|
}
|
|
}
|
|
|
|
RNG *rng = BLI_rng_new(seed);
|
|
|
|
for (i = 0; i < o->_M; i++) {
|
|
for (j = 0; j < o->_N; j++) {
|
|
/* This ensures we get a value tied to the surface location, avoiding dramatic surface
|
|
* change with changing resolution.
|
|
* Explicitly cast to signed int first to ensure consistent behavior on all processors,
|
|
* since behavior of `float` to `uint` cast is undefined in C. */
|
|
const int hash_x = o->_kx[i] * 360.0f;
|
|
const int hash_z = o->_kz[j] * 360.0f;
|
|
int new_seed = seed + BLI_hash_int_2d(hash_x, hash_z);
|
|
|
|
BLI_rng_seed(rng, new_seed);
|
|
float r1 = gaussRand(rng);
|
|
float r2 = gaussRand(rng);
|
|
|
|
fftw_complex r1r2;
|
|
init_complex(r1r2, r1, r2);
|
|
switch (o->_spectrum) {
|
|
case MOD_OCEAN_SPECTRUM_JONSWAP:
|
|
mul_complex_f(o->_h0[i * o->_N + j],
|
|
r1r2,
|
|
float(sqrt(BLI_ocean_spectrum_jonswap(o, o->_kx[i], o->_kz[j]) / 2.0f)));
|
|
mul_complex_f(o->_h0_minus[i * o->_N + j],
|
|
r1r2,
|
|
float(sqrt(BLI_ocean_spectrum_jonswap(o, -o->_kx[i], -o->_kz[j]) / 2.0f)));
|
|
break;
|
|
case MOD_OCEAN_SPECTRUM_TEXEL_MARSEN_ARSLOE:
|
|
mul_complex_f(
|
|
o->_h0[i * o->_N + j],
|
|
r1r2,
|
|
float(sqrt(BLI_ocean_spectrum_texelmarsenarsloe(o, o->_kx[i], o->_kz[j]) / 2.0f)));
|
|
mul_complex_f(
|
|
o->_h0_minus[i * o->_N + j],
|
|
r1r2,
|
|
float(sqrt(BLI_ocean_spectrum_texelmarsenarsloe(o, -o->_kx[i], -o->_kz[j]) / 2.0f)));
|
|
break;
|
|
case MOD_OCEAN_SPECTRUM_PIERSON_MOSKOWITZ:
|
|
mul_complex_f(
|
|
o->_h0[i * o->_N + j],
|
|
r1r2,
|
|
float(sqrt(BLI_ocean_spectrum_piersonmoskowitz(o, o->_kx[i], o->_kz[j]) / 2.0f)));
|
|
mul_complex_f(
|
|
o->_h0_minus[i * o->_N + j],
|
|
r1r2,
|
|
float(sqrt(BLI_ocean_spectrum_piersonmoskowitz(o, -o->_kx[i], -o->_kz[j]) / 2.0f)));
|
|
break;
|
|
default:
|
|
mul_complex_f(
|
|
o->_h0[i * o->_N + j], r1r2, float(sqrt(Ph(o, o->_kx[i], o->_kz[j]) / 2.0f)));
|
|
mul_complex_f(o->_h0_minus[i * o->_N + j],
|
|
r1r2,
|
|
float(sqrt(Ph(o, -o->_kx[i], -o->_kz[j]) / 2.0f)));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
o->_fft_in = (fftw_complex *)MEM_mallocN(o->_M * (1 + o->_N / 2) * sizeof(fftw_complex),
|
|
"ocean_fft_in");
|
|
o->_htilda = (fftw_complex *)MEM_mallocN(o->_M * (1 + o->_N / 2) * sizeof(fftw_complex),
|
|
"ocean_htilda");
|
|
|
|
BLI_thread_lock(LOCK_FFTW);
|
|
|
|
if (o->_do_disp_y) {
|
|
o->_disp_y = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_disp_y");
|
|
o->_disp_y_plan = fftw_plan_dft_c2r_2d(o->_M, o->_N, o->_fft_in, o->_disp_y, FFTW_ESTIMATE);
|
|
}
|
|
|
|
if (o->_do_normals) {
|
|
o->_fft_in_nx = (fftw_complex *)MEM_mallocN(o->_M * (1 + o->_N / 2) * sizeof(fftw_complex),
|
|
"ocean_fft_in_nx");
|
|
o->_fft_in_nz = (fftw_complex *)MEM_mallocN(o->_M * (1 + o->_N / 2) * sizeof(fftw_complex),
|
|
"ocean_fft_in_nz");
|
|
|
|
o->_N_x = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_N_x");
|
|
// o->_N_y = (float *) fftwf_malloc(o->_M * o->_N * sizeof(float)); /* (MEM01) */
|
|
o->_N_z = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_N_z");
|
|
|
|
o->_N_x_plan = fftw_plan_dft_c2r_2d(o->_M, o->_N, o->_fft_in_nx, o->_N_x, FFTW_ESTIMATE);
|
|
o->_N_z_plan = fftw_plan_dft_c2r_2d(o->_M, o->_N, o->_fft_in_nz, o->_N_z, FFTW_ESTIMATE);
|
|
}
|
|
|
|
if (o->_do_chop) {
|
|
o->_fft_in_x = (fftw_complex *)MEM_mallocN(o->_M * (1 + o->_N / 2) * sizeof(fftw_complex),
|
|
"ocean_fft_in_x");
|
|
o->_fft_in_z = (fftw_complex *)MEM_mallocN(o->_M * (1 + o->_N / 2) * sizeof(fftw_complex),
|
|
"ocean_fft_in_z");
|
|
|
|
o->_disp_x = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_disp_x");
|
|
o->_disp_z = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_disp_z");
|
|
|
|
o->_disp_x_plan = fftw_plan_dft_c2r_2d(o->_M, o->_N, o->_fft_in_x, o->_disp_x, FFTW_ESTIMATE);
|
|
o->_disp_z_plan = fftw_plan_dft_c2r_2d(o->_M, o->_N, o->_fft_in_z, o->_disp_z, FFTW_ESTIMATE);
|
|
}
|
|
if (o->_do_jacobian) {
|
|
o->_fft_in_jxx = (fftw_complex *)MEM_mallocN(o->_M * (1 + o->_N / 2) * sizeof(fftw_complex),
|
|
"ocean_fft_in_jxx");
|
|
o->_fft_in_jzz = (fftw_complex *)MEM_mallocN(o->_M * (1 + o->_N / 2) * sizeof(fftw_complex),
|
|
"ocean_fft_in_jzz");
|
|
o->_fft_in_jxz = (fftw_complex *)MEM_mallocN(o->_M * (1 + o->_N / 2) * sizeof(fftw_complex),
|
|
"ocean_fft_in_jxz");
|
|
|
|
o->_Jxx = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_Jxx");
|
|
o->_Jzz = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_Jzz");
|
|
o->_Jxz = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_Jxz");
|
|
|
|
o->_Jxx_plan = fftw_plan_dft_c2r_2d(o->_M, o->_N, o->_fft_in_jxx, o->_Jxx, FFTW_ESTIMATE);
|
|
o->_Jzz_plan = fftw_plan_dft_c2r_2d(o->_M, o->_N, o->_fft_in_jzz, o->_Jzz, FFTW_ESTIMATE);
|
|
o->_Jxz_plan = fftw_plan_dft_c2r_2d(o->_M, o->_N, o->_fft_in_jxz, o->_Jxz, FFTW_ESTIMATE);
|
|
}
|
|
|
|
BLI_thread_unlock(LOCK_FFTW);
|
|
|
|
BLI_rw_mutex_unlock(&o->oceanmutex);
|
|
|
|
set_height_normalize_factor(o);
|
|
|
|
BLI_rng_free(rng);
|
|
|
|
return true;
|
|
}
|
|
|
|
void BKE_ocean_free_data(Ocean *oc)
|
|
{
|
|
if (!oc) {
|
|
return;
|
|
}
|
|
|
|
BLI_rw_mutex_lock(&oc->oceanmutex, THREAD_LOCK_WRITE);
|
|
|
|
BLI_thread_lock(LOCK_FFTW);
|
|
|
|
if (oc->_do_disp_y) {
|
|
fftw_destroy_plan(oc->_disp_y_plan);
|
|
MEM_freeN(oc->_disp_y);
|
|
}
|
|
|
|
if (oc->_do_normals) {
|
|
MEM_freeN(oc->_fft_in_nx);
|
|
MEM_freeN(oc->_fft_in_nz);
|
|
fftw_destroy_plan(oc->_N_x_plan);
|
|
fftw_destroy_plan(oc->_N_z_plan);
|
|
MEM_freeN(oc->_N_x);
|
|
// fftwf_free(oc->_N_y); /* (MEM01) */
|
|
MEM_freeN(oc->_N_z);
|
|
}
|
|
|
|
if (oc->_do_chop) {
|
|
MEM_freeN(oc->_fft_in_x);
|
|
MEM_freeN(oc->_fft_in_z);
|
|
fftw_destroy_plan(oc->_disp_x_plan);
|
|
fftw_destroy_plan(oc->_disp_z_plan);
|
|
MEM_freeN(oc->_disp_x);
|
|
MEM_freeN(oc->_disp_z);
|
|
}
|
|
|
|
if (oc->_do_jacobian) {
|
|
MEM_freeN(oc->_fft_in_jxx);
|
|
MEM_freeN(oc->_fft_in_jzz);
|
|
MEM_freeN(oc->_fft_in_jxz);
|
|
fftw_destroy_plan(oc->_Jxx_plan);
|
|
fftw_destroy_plan(oc->_Jzz_plan);
|
|
fftw_destroy_plan(oc->_Jxz_plan);
|
|
MEM_freeN(oc->_Jxx);
|
|
MEM_freeN(oc->_Jzz);
|
|
MEM_freeN(oc->_Jxz);
|
|
}
|
|
|
|
BLI_thread_unlock(LOCK_FFTW);
|
|
|
|
if (oc->_fft_in) {
|
|
MEM_freeN(oc->_fft_in);
|
|
}
|
|
|
|
/* check that ocean data has been initialized */
|
|
if (oc->_htilda) {
|
|
MEM_freeN(oc->_htilda);
|
|
MEM_freeN(oc->_k);
|
|
MEM_freeN(oc->_h0);
|
|
MEM_freeN(oc->_h0_minus);
|
|
MEM_freeN(oc->_kx);
|
|
MEM_freeN(oc->_kz);
|
|
}
|
|
|
|
BLI_rw_mutex_unlock(&oc->oceanmutex);
|
|
}
|
|
|
|
void BKE_ocean_free(Ocean *oc)
|
|
{
|
|
if (!oc) {
|
|
return;
|
|
}
|
|
|
|
BKE_ocean_free_data(oc);
|
|
BLI_rw_mutex_end(&oc->oceanmutex);
|
|
|
|
MEM_freeN(oc);
|
|
}
|
|
|
|
# undef GRAVITY
|
|
|
|
/* ********* Baking/Caching ********* */
|
|
|
|
# define CACHE_TYPE_DISPLACE 1
|
|
# define CACHE_TYPE_FOAM 2
|
|
# define CACHE_TYPE_NORMAL 3
|
|
# define CACHE_TYPE_SPRAY 4
|
|
# define CACHE_TYPE_SPRAY_INVERSE 5
|
|
|
|
static void cache_filepath(
|
|
char *filepath, const char *dirname, const char *relbase, int frame, int type)
|
|
{
|
|
char cachepath[FILE_MAX];
|
|
const char *filename;
|
|
|
|
switch (type) {
|
|
case CACHE_TYPE_FOAM:
|
|
filename = "foam_";
|
|
break;
|
|
case CACHE_TYPE_NORMAL:
|
|
filename = "normal_";
|
|
break;
|
|
case CACHE_TYPE_SPRAY:
|
|
filename = "spray_";
|
|
break;
|
|
case CACHE_TYPE_SPRAY_INVERSE:
|
|
filename = "spray_inverse_";
|
|
break;
|
|
case CACHE_TYPE_DISPLACE:
|
|
default:
|
|
filename = "disp_";
|
|
break;
|
|
}
|
|
|
|
BLI_path_join(cachepath, sizeof(cachepath), dirname, filename);
|
|
|
|
BKE_image_path_from_imtype(
|
|
filepath, cachepath, relbase, frame, R_IMF_IMTYPE_OPENEXR, true, true, "");
|
|
}
|
|
|
|
/* silly functions but useful to inline when the args do a lot of indirections */
|
|
MINLINE void rgb_to_rgba_unit_alpha(float r_rgba[4], const float rgb[3])
|
|
{
|
|
r_rgba[0] = rgb[0];
|
|
r_rgba[1] = rgb[1];
|
|
r_rgba[2] = rgb[2];
|
|
r_rgba[3] = 1.0f;
|
|
}
|
|
MINLINE void value_to_rgba_unit_alpha(float r_rgba[4], const float value)
|
|
{
|
|
r_rgba[0] = value;
|
|
r_rgba[1] = value;
|
|
r_rgba[2] = value;
|
|
r_rgba[3] = 1.0f;
|
|
}
|
|
|
|
void BKE_ocean_free_cache(OceanCache *och)
|
|
{
|
|
int i, f = 0;
|
|
|
|
if (!och) {
|
|
return;
|
|
}
|
|
|
|
if (och->ibufs_disp) {
|
|
for (i = och->start, f = 0; i <= och->end; i++, f++) {
|
|
if (och->ibufs_disp[f]) {
|
|
IMB_freeImBuf(och->ibufs_disp[f]);
|
|
}
|
|
}
|
|
MEM_freeN(och->ibufs_disp);
|
|
}
|
|
|
|
if (och->ibufs_foam) {
|
|
for (i = och->start, f = 0; i <= och->end; i++, f++) {
|
|
if (och->ibufs_foam[f]) {
|
|
IMB_freeImBuf(och->ibufs_foam[f]);
|
|
}
|
|
}
|
|
MEM_freeN(och->ibufs_foam);
|
|
}
|
|
|
|
if (och->ibufs_spray) {
|
|
for (i = och->start, f = 0; i <= och->end; i++, f++) {
|
|
if (och->ibufs_spray[f]) {
|
|
IMB_freeImBuf(och->ibufs_spray[f]);
|
|
}
|
|
}
|
|
MEM_freeN(och->ibufs_spray);
|
|
}
|
|
|
|
if (och->ibufs_spray_inverse) {
|
|
for (i = och->start, f = 0; i <= och->end; i++, f++) {
|
|
if (och->ibufs_spray_inverse[f]) {
|
|
IMB_freeImBuf(och->ibufs_spray_inverse[f]);
|
|
}
|
|
}
|
|
MEM_freeN(och->ibufs_spray_inverse);
|
|
}
|
|
|
|
if (och->ibufs_norm) {
|
|
for (i = och->start, f = 0; i <= och->end; i++, f++) {
|
|
if (och->ibufs_norm[f]) {
|
|
IMB_freeImBuf(och->ibufs_norm[f]);
|
|
}
|
|
}
|
|
MEM_freeN(och->ibufs_norm);
|
|
}
|
|
|
|
if (och->time) {
|
|
MEM_freeN(och->time);
|
|
}
|
|
MEM_freeN(och);
|
|
}
|
|
|
|
void BKE_ocean_cache_eval_uv(OceanCache *och, OceanResult *ocr, int f, float u, float v)
|
|
{
|
|
int res_x = och->resolution_x;
|
|
int res_y = och->resolution_y;
|
|
float result[4];
|
|
|
|
u = fmod(u, 1.0);
|
|
v = fmod(v, 1.0);
|
|
|
|
if (u < 0) {
|
|
u += 1.0f;
|
|
}
|
|
if (v < 0) {
|
|
v += 1.0f;
|
|
}
|
|
|
|
if (och->ibufs_disp[f]) {
|
|
ibuf_sample(och->ibufs_disp[f], u, v, (1.0f / float(res_x)), (1.0f / float(res_y)), result);
|
|
copy_v3_v3(ocr->disp, result);
|
|
}
|
|
|
|
if (och->ibufs_foam[f]) {
|
|
ibuf_sample(och->ibufs_foam[f], u, v, (1.0f / float(res_x)), (1.0f / float(res_y)), result);
|
|
ocr->foam = result[0];
|
|
}
|
|
|
|
if (och->ibufs_spray[f]) {
|
|
ibuf_sample(och->ibufs_spray[f], u, v, (1.0f / float(res_x)), (1.0f / float(res_y)), result);
|
|
copy_v3_v3(ocr->Eplus, result);
|
|
}
|
|
|
|
if (och->ibufs_spray_inverse[f]) {
|
|
ibuf_sample(
|
|
och->ibufs_spray_inverse[f], u, v, (1.0f / float(res_x)), (1.0f / float(res_y)), result);
|
|
copy_v3_v3(ocr->Eminus, result);
|
|
}
|
|
|
|
if (och->ibufs_norm[f]) {
|
|
ibuf_sample(och->ibufs_norm[f], u, v, (1.0f / float(res_x)), (1.0f / float(res_y)), result);
|
|
copy_v3_v3(ocr->normal, result);
|
|
}
|
|
}
|
|
|
|
void BKE_ocean_cache_eval_ij(OceanCache *och, OceanResult *ocr, int f, int i, int j)
|
|
{
|
|
const int res_x = och->resolution_x;
|
|
const int res_y = och->resolution_y;
|
|
|
|
if (i < 0) {
|
|
i = -i;
|
|
}
|
|
if (j < 0) {
|
|
j = -j;
|
|
}
|
|
|
|
i = i % res_x;
|
|
j = j % res_y;
|
|
|
|
if (och->ibufs_disp[f]) {
|
|
copy_v3_v3(ocr->disp, &och->ibufs_disp[f]->float_buffer.data[4 * (res_x * j + i)]);
|
|
}
|
|
|
|
if (och->ibufs_foam[f]) {
|
|
ocr->foam = och->ibufs_foam[f]->float_buffer.data[4 * (res_x * j + i)];
|
|
}
|
|
|
|
if (och->ibufs_spray[f]) {
|
|
copy_v3_v3(ocr->Eplus, &och->ibufs_spray[f]->float_buffer.data[4 * (res_x * j + i)]);
|
|
}
|
|
|
|
if (och->ibufs_spray_inverse[f]) {
|
|
copy_v3_v3(ocr->Eminus, &och->ibufs_spray_inverse[f]->float_buffer.data[4 * (res_x * j + i)]);
|
|
}
|
|
|
|
if (och->ibufs_norm[f]) {
|
|
copy_v3_v3(ocr->normal, &och->ibufs_norm[f]->float_buffer.data[4 * (res_x * j + i)]);
|
|
}
|
|
}
|
|
|
|
OceanCache *BKE_ocean_init_cache(const char *bakepath,
|
|
const char *relbase,
|
|
int start,
|
|
int end,
|
|
float wave_scale,
|
|
float chop_amount,
|
|
float foam_coverage,
|
|
float foam_fade,
|
|
int resolution)
|
|
{
|
|
OceanCache *och = static_cast<OceanCache *>(MEM_callocN(sizeof(OceanCache), "ocean cache data"));
|
|
|
|
och->bakepath = bakepath;
|
|
och->relbase = relbase;
|
|
|
|
och->start = start;
|
|
och->end = end;
|
|
och->duration = (end - start) + 1;
|
|
och->wave_scale = wave_scale;
|
|
och->chop_amount = chop_amount;
|
|
och->foam_coverage = foam_coverage;
|
|
och->foam_fade = foam_fade;
|
|
och->resolution_x = resolution * resolution;
|
|
och->resolution_y = resolution * resolution;
|
|
|
|
och->ibufs_disp = static_cast<ImBuf **>(
|
|
MEM_callocN(sizeof(ImBuf *) * och->duration, "displacement imbuf pointer array"));
|
|
och->ibufs_foam = static_cast<ImBuf **>(
|
|
MEM_callocN(sizeof(ImBuf *) * och->duration, "foam imbuf pointer array"));
|
|
och->ibufs_spray = static_cast<ImBuf **>(
|
|
MEM_callocN(sizeof(ImBuf *) * och->duration, "spray imbuf pointer array"));
|
|
och->ibufs_spray_inverse = static_cast<ImBuf **>(
|
|
MEM_callocN(sizeof(ImBuf *) * och->duration, "spray_inverse imbuf pointer array"));
|
|
och->ibufs_norm = static_cast<ImBuf **>(
|
|
MEM_callocN(sizeof(ImBuf *) * och->duration, "normal imbuf pointer array"));
|
|
|
|
och->time = nullptr;
|
|
|
|
return och;
|
|
}
|
|
|
|
void BKE_ocean_simulate_cache(OceanCache *och, int frame)
|
|
{
|
|
char filepath[FILE_MAX];
|
|
int f = frame;
|
|
|
|
/* ibufs array is zero based, but filenames are based on frame numbers */
|
|
/* still need to clamp frame numbers to valid range of images on disk though */
|
|
CLAMP(frame, och->start, och->end);
|
|
f = frame - och->start; /* shift to 0 based */
|
|
|
|
/* if image is already loaded in mem, return */
|
|
if (och->ibufs_disp[f] != nullptr) {
|
|
return;
|
|
}
|
|
|
|
/* Use default color spaces since we know for sure cache
|
|
* files were saved with default settings too. */
|
|
|
|
cache_filepath(filepath, och->bakepath, och->relbase, frame, CACHE_TYPE_DISPLACE);
|
|
och->ibufs_disp[f] = IMB_loadiffname(filepath, 0, nullptr);
|
|
|
|
cache_filepath(filepath, och->bakepath, och->relbase, frame, CACHE_TYPE_FOAM);
|
|
och->ibufs_foam[f] = IMB_loadiffname(filepath, 0, nullptr);
|
|
|
|
cache_filepath(filepath, och->bakepath, och->relbase, frame, CACHE_TYPE_SPRAY);
|
|
och->ibufs_spray[f] = IMB_loadiffname(filepath, 0, nullptr);
|
|
|
|
cache_filepath(filepath, och->bakepath, och->relbase, frame, CACHE_TYPE_SPRAY_INVERSE);
|
|
och->ibufs_spray_inverse[f] = IMB_loadiffname(filepath, 0, nullptr);
|
|
|
|
cache_filepath(filepath, och->bakepath, och->relbase, frame, CACHE_TYPE_NORMAL);
|
|
och->ibufs_norm[f] = IMB_loadiffname(filepath, 0, nullptr);
|
|
}
|
|
|
|
void BKE_ocean_bake(Ocean *o,
|
|
OceanCache *och,
|
|
void (*update_cb)(void *, float progress, int *cancel),
|
|
void *update_cb_data)
|
|
{
|
|
/* NOTE(@ideasman42): some of these values remain uninitialized unless certain options
|
|
* are enabled, take care that #BKE_ocean_eval_ij() initializes a member before use. */
|
|
OceanResult ocr;
|
|
|
|
ImageFormatData imf = {0};
|
|
|
|
int f, i = 0, x, y, cancel = 0;
|
|
float progress;
|
|
|
|
ImBuf *ibuf_foam, *ibuf_disp, *ibuf_normal, *ibuf_spray, *ibuf_spray_inverse;
|
|
float *prev_foam;
|
|
int res_x = och->resolution_x;
|
|
int res_y = och->resolution_y;
|
|
char filepath[FILE_MAX];
|
|
// RNG *rng;
|
|
|
|
if (!o) {
|
|
return;
|
|
}
|
|
|
|
if (o->_do_jacobian) {
|
|
prev_foam = static_cast<float *>(
|
|
MEM_callocN(res_x * res_y * sizeof(float), "previous frame foam bake data"));
|
|
}
|
|
else {
|
|
prev_foam = nullptr;
|
|
}
|
|
|
|
// rng = BLI_rng_new(0);
|
|
|
|
/* setup image format */
|
|
imf.imtype = R_IMF_IMTYPE_OPENEXR;
|
|
imf.depth = R_IMF_CHAN_DEPTH_16;
|
|
imf.exr_codec = R_IMF_EXR_CODEC_ZIP;
|
|
|
|
for (f = och->start, i = 0; f <= och->end; f++, i++) {
|
|
|
|
/* create a new imbuf to store image for this frame */
|
|
ibuf_foam = IMB_allocImBuf(res_x, res_y, 32, IB_rectfloat);
|
|
ibuf_disp = IMB_allocImBuf(res_x, res_y, 32, IB_rectfloat);
|
|
ibuf_normal = IMB_allocImBuf(res_x, res_y, 32, IB_rectfloat);
|
|
ibuf_spray = IMB_allocImBuf(res_x, res_y, 32, IB_rectfloat);
|
|
ibuf_spray_inverse = IMB_allocImBuf(res_x, res_y, 32, IB_rectfloat);
|
|
|
|
BKE_ocean_simulate(o, och->time[i], och->wave_scale, och->chop_amount);
|
|
|
|
/* add new foam */
|
|
for (y = 0; y < res_y; y++) {
|
|
for (x = 0; x < res_x; x++) {
|
|
|
|
BKE_ocean_eval_ij(o, &ocr, x, y);
|
|
|
|
/* add to the image */
|
|
rgb_to_rgba_unit_alpha(&ibuf_disp->float_buffer.data[4 * (res_x * y + x)], ocr.disp);
|
|
|
|
if (o->_do_jacobian) {
|
|
/* TODO(@ideasman42): cleanup unused code. */
|
|
|
|
float /* r, */ /* UNUSED */ pr = 0.0f, foam_result;
|
|
float neg_disp, neg_eplus;
|
|
|
|
ocr.foam = BKE_ocean_jminus_to_foam(ocr.Jminus, och->foam_coverage);
|
|
|
|
/* accumulate previous value for this cell */
|
|
if (i > 0) {
|
|
pr = prev_foam[res_x * y + x];
|
|
}
|
|
|
|
// r = BLI_rng_get_float(rng); /* UNUSED */ /* randomly reduce foam */
|
|
|
|
// pr = pr * och->foam_fade; /* overall fade */
|
|
|
|
/* Remember ocean coord system is Y up!
|
|
* break up the foam where height (Y) is low (wave valley),
|
|
* and X and Z displacement is greatest. */
|
|
|
|
neg_disp = ocr.disp[1] < 0.0f ? 1.0f + ocr.disp[1] : 1.0f;
|
|
neg_disp = neg_disp < 0.0f ? 0.0f : neg_disp;
|
|
|
|
/* foam, 'ocr.Eplus' only initialized with do_jacobian */
|
|
neg_eplus = ocr.Eplus[2] < 0.0f ? 1.0f + ocr.Eplus[2] : 1.0f;
|
|
neg_eplus = neg_eplus < 0.0f ? 0.0f : neg_eplus;
|
|
|
|
if (pr < 1.0f) {
|
|
pr *= pr;
|
|
}
|
|
|
|
pr *= och->foam_fade * (0.75f + neg_eplus * 0.25f);
|
|
|
|
/* A full clamping should not be needed! */
|
|
foam_result = min_ff(pr + ocr.foam, 1.0f);
|
|
|
|
prev_foam[res_x * y + x] = foam_result;
|
|
|
|
// foam_result = min_ff(foam_result, 1.0f);
|
|
|
|
value_to_rgba_unit_alpha(&ibuf_foam->float_buffer.data[4 * (res_x * y + x)],
|
|
foam_result);
|
|
|
|
/* spray map baking */
|
|
if (o->_do_spray) {
|
|
rgb_to_rgba_unit_alpha(&ibuf_spray->float_buffer.data[4 * (res_x * y + x)], ocr.Eplus);
|
|
rgb_to_rgba_unit_alpha(&ibuf_spray_inverse->float_buffer.data[4 * (res_x * y + x)],
|
|
ocr.Eminus);
|
|
}
|
|
}
|
|
|
|
if (o->_do_normals) {
|
|
rgb_to_rgba_unit_alpha(&ibuf_normal->float_buffer.data[4 * (res_x * y + x)], ocr.normal);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* write the images */
|
|
cache_filepath(filepath, och->bakepath, och->relbase, f, CACHE_TYPE_DISPLACE);
|
|
if (0 == BKE_imbuf_write(ibuf_disp, filepath, &imf)) {
|
|
printf("Cannot save Displacement File Output to %s\n", filepath);
|
|
}
|
|
|
|
if (o->_do_jacobian) {
|
|
cache_filepath(filepath, och->bakepath, och->relbase, f, CACHE_TYPE_FOAM);
|
|
if (0 == BKE_imbuf_write(ibuf_foam, filepath, &imf)) {
|
|
printf("Cannot save Foam File Output to %s\n", filepath);
|
|
}
|
|
|
|
if (o->_do_spray) {
|
|
cache_filepath(filepath, och->bakepath, och->relbase, f, CACHE_TYPE_SPRAY);
|
|
if (0 == BKE_imbuf_write(ibuf_spray, filepath, &imf)) {
|
|
printf("Cannot save Spray File Output to %s\n", filepath);
|
|
}
|
|
|
|
cache_filepath(filepath, och->bakepath, och->relbase, f, CACHE_TYPE_SPRAY_INVERSE);
|
|
if (0 == BKE_imbuf_write(ibuf_spray_inverse, filepath, &imf)) {
|
|
printf("Cannot save Spray Inverse File Output to %s\n", filepath);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (o->_do_normals) {
|
|
cache_filepath(filepath, och->bakepath, och->relbase, f, CACHE_TYPE_NORMAL);
|
|
if (0 == BKE_imbuf_write(ibuf_normal, filepath, &imf)) {
|
|
printf("Cannot save Normal File Output to %s\n", filepath);
|
|
}
|
|
}
|
|
|
|
IMB_freeImBuf(ibuf_disp);
|
|
IMB_freeImBuf(ibuf_foam);
|
|
IMB_freeImBuf(ibuf_normal);
|
|
IMB_freeImBuf(ibuf_spray);
|
|
IMB_freeImBuf(ibuf_spray_inverse);
|
|
|
|
progress = (f - och->start) / float(och->duration);
|
|
|
|
update_cb(update_cb_data, progress, &cancel);
|
|
|
|
if (cancel) {
|
|
if (prev_foam) {
|
|
MEM_freeN(prev_foam);
|
|
}
|
|
// BLI_rng_free(rng);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// BLI_rng_free(rng);
|
|
if (prev_foam) {
|
|
MEM_freeN(prev_foam);
|
|
}
|
|
och->baked = 1;
|
|
}
|
|
|
|
#else /* WITH_OCEANSIM */
|
|
|
|
float BKE_ocean_jminus_to_foam(float /*jminus*/, float /*coverage*/)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
void BKE_ocean_eval_uv(Ocean * /*oc*/, OceanResult * /*ocr*/, float /*u*/, float /*v*/) {}
|
|
|
|
/* use catmullrom interpolation rather than linear */
|
|
void BKE_ocean_eval_uv_catrom(Ocean * /*oc*/, OceanResult * /*ocr*/, float /*u*/, float /*v*/) {}
|
|
|
|
void BKE_ocean_eval_xz(Ocean * /*oc*/, OceanResult * /*ocr*/, float /*x*/, float /*z*/) {}
|
|
|
|
void BKE_ocean_eval_xz_catrom(Ocean * /*oc*/, OceanResult * /*ocr*/, float /*x*/, float /*z*/) {}
|
|
|
|
void BKE_ocean_eval_ij(Ocean * /*oc*/, OceanResult * /*ocr*/, int /*i*/, int /*j*/) {}
|
|
|
|
void BKE_ocean_simulate(Ocean * /*o*/, float /*t*/, float /*scale*/, float /*chop_amount*/) {}
|
|
|
|
Ocean *BKE_ocean_add()
|
|
{
|
|
Ocean *oc = static_cast<Ocean *>(MEM_callocN(sizeof(Ocean), "ocean sim data"));
|
|
|
|
return oc;
|
|
}
|
|
|
|
bool BKE_ocean_init(Ocean * /*o*/,
|
|
int /*M*/,
|
|
int /*N*/,
|
|
float /*Lx*/,
|
|
float /*Lz*/,
|
|
float /*V*/,
|
|
float /*l*/,
|
|
float /*A*/,
|
|
float /*w*/,
|
|
float /*damp*/,
|
|
float /*alignment*/,
|
|
float /*depth*/,
|
|
float /*time*/,
|
|
int /*spectrum*/,
|
|
float /*fetch_jonswap*/,
|
|
float /*sharpen_peak_jonswap*/,
|
|
short /*do_height_field*/,
|
|
short /*do_chop*/,
|
|
short /*do_spray*/,
|
|
short /*do_normals*/,
|
|
short /*do_jacobian*/,
|
|
int /*seed*/)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void BKE_ocean_free_data(Ocean * /*oc*/) {}
|
|
|
|
void BKE_ocean_free(Ocean *oc)
|
|
{
|
|
if (!oc) {
|
|
return;
|
|
}
|
|
MEM_freeN(oc);
|
|
}
|
|
|
|
/* ********* Baking/Caching ********* */
|
|
|
|
void BKE_ocean_free_cache(OceanCache *och)
|
|
{
|
|
if (!och) {
|
|
return;
|
|
}
|
|
|
|
MEM_freeN(och);
|
|
}
|
|
|
|
void BKE_ocean_cache_eval_uv(
|
|
OceanCache * /*och*/, OceanResult * /*ocr*/, int /*f*/, float /*u*/, float /*v*/)
|
|
{
|
|
}
|
|
|
|
void BKE_ocean_cache_eval_ij(
|
|
OceanCache * /*och*/, OceanResult * /*ocr*/, int /*f*/, int /*i*/, int /*j*/)
|
|
{
|
|
}
|
|
|
|
OceanCache *BKE_ocean_init_cache(const char * /*bakepath*/,
|
|
const char * /*relbase*/,
|
|
int /*start*/,
|
|
int /*end*/,
|
|
float /*wave_scale*/,
|
|
float /*chop_amount*/,
|
|
float /*foam_coverage*/,
|
|
float /*foam_fade*/,
|
|
int /*resolution*/)
|
|
{
|
|
OceanCache *och = static_cast<OceanCache *>(MEM_callocN(sizeof(OceanCache), "ocean cache data"));
|
|
|
|
return och;
|
|
}
|
|
|
|
void BKE_ocean_simulate_cache(OceanCache * /*och*/, int /*frame*/) {}
|
|
|
|
void BKE_ocean_bake(Ocean * /*o*/,
|
|
OceanCache * /*och*/,
|
|
void (*update_cb)(void *, float progress, int *cancel),
|
|
void * /*update_cb_data*/)
|
|
{
|
|
/* unused */
|
|
(void)update_cb;
|
|
}
|
|
|
|
bool BKE_ocean_init_from_modifier(Ocean * /*ocean*/,
|
|
OceanModifierData const * /*omd*/,
|
|
int /*resolution*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#endif /* WITH_OCEANSIM */
|
|
|
|
void BKE_ocean_free_modifier_cache(OceanModifierData *omd)
|
|
{
|
|
BKE_ocean_free_cache(omd->oceancache);
|
|
omd->oceancache = nullptr;
|
|
omd->cached = false;
|
|
}
|