This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/blenkernel/intern/ocean.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1717 lines
49 KiB
C
Raw Normal View History

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* Based on original code by Drew Whitehouse / Houdini Ocean Toolkit
* OpenMP hints by Christian Schnellhammer
*/
/** \file
* \ingroup bke
2013-09-12 03:02:50 +00:00
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "DNA_modifier_types.h"
#include "DNA_scene_types.h"
2019-01-26 15:02:52 +01:00
#include "BLI_math.h"
#include "BLI_path_util.h"
#include "BLI_rand.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "BKE_image.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
* singe-precision floats, but later we call the double-precision log() and sqrt() functions
* instead of logf() and sqrtf(). */
float x;
float y;
float length2;
2012-05-12 19:18:02 +00:00
do {
2013-04-16 00:56:24 +00:00
x = (float)(nextfr(rng, -1, 1));
y = (float)(nextfr(rng, -1, 1));
length2 = x * x + y * y;
2012-05-12 19:18:02 +00:00
} while (length2 >= 1 || length2 == 0);
return x * sqrtf(-2.0f * logf(length2) / length2);
}
/**
2012-03-01 12:20:18 +00:00
* Some useful functions
*/
2012-04-29 15:47:02 +00:00
MINLINE float catrom(float p0, float p1, float p2, float p3, float f)
{
2012-05-12 19:18:02 +00:00
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 */
2012-05-12 19:18:02 +00:00
static float Ph(struct Ocean *o, float kx, float kz)
{
float tmp;
2012-05-12 19:18:02 +00:00
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 */
2012-06-23 23:22:19 +00:00
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);
}
2012-04-29 15:47:02 +00:00
static void compute_eigenstuff(struct OceanResult *ocr, float jxx, float jzz, float jxz)
{
2012-04-29 15:47:02 +00:00
float a, b, qplus, qminus;
a = jxx + jzz;
2012-05-12 19:18:02 +00:00
b = sqrt((jxx - jzz) * (jxx - jzz) + 4 * jxz * jxz);
2012-05-12 19:18:02 +00:00
ocr->Jminus = 0.5f * (a - b);
ocr->Jplus = 0.5f * (a + b);
2012-05-12 19:18:02 +00:00
qplus = (ocr->Jplus - jxx) / jxz;
qminus = (ocr->Jminus - jxx) / jxz;
2012-05-12 19:18:02 +00:00
a = sqrt(1 + qplus * qplus);
b = sqrt(1 + qminus * qminus);
2012-05-12 19:18:02 +00:00
ocr->Eplus[0] = 1.0f / a;
ocr->Eplus[1] = 0.0f;
2012-05-12 19:18:02 +00:00
ocr->Eplus[2] = qplus / a;
2012-05-12 19:18:02 +00:00
ocr->Eminus[0] = 1.0f / b;
ocr->Eminus[1] = 0.0f;
2012-05-12 19:18:02 +00:00
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;
2012-05-12 19:18:02 +00:00
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;
}
2012-04-29 15:47:02 +00:00
void BKE_ocean_eval_uv(struct Ocean *oc, struct OceanResult *ocr, float u, float v)
{
2012-04-29 15:47:02 +00:00
int i0, i1, j0, j1;
float frac_x, frac_z;
float uu, vv;
/* first wrap the texture so 0 <= (u, v) < 1 */
2012-04-29 15:47:02 +00:00
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))
2017-06-12 13:35:00 +10:00
{
if (oc->_do_disp_y) {
ocr->disp[1] = BILERP(oc->_disp_y);
}
if (oc->_do_normals) {
ocr->normal[0] = BILERP(oc->_N_x);
2012-05-12 19:18:02 +00:00
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) {
2012-04-29 15:47:02 +00:00
compute_eigenstuff(ocr, BILERP(oc->_Jxx), BILERP(oc->_Jzz), BILERP(oc->_Jxz));
}
}
# undef BILERP
BLI_rw_mutex_unlock(&oc->oceanmutex);
}
/* use catmullrom interpolation rather than linear */
2012-04-29 15:47:02 +00:00
void BKE_ocean_eval_uv_catrom(struct Ocean *oc, struct OceanResult *ocr, float u, float v)
{
2012-04-29 15:47:02 +00:00
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 */
2012-04-29 15:47:02 +00:00
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;
2012-05-12 19:18:02 +00:00
i0 = (i1 - 1);
i3 = (i2 + 1);
i0 = i0 < 0 ? i0 + oc->_M : i0;
i3 = i3 >= oc->_M ? i3 - oc->_M : i3;
2012-05-12 19:18:02 +00:00
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) {
2012-02-27 10:35:39 +00:00
ocr->disp[1] = INTERP(oc->_disp_y);
}
if (oc->_do_normals) {
ocr->normal[0] = INTERP(oc->_N_x);
2012-05-12 19:18:02 +00:00
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) {
2012-04-29 15:47:02 +00:00
compute_eigenstuff(ocr, INTERP(oc->_Jxx), INTERP(oc->_Jzz), INTERP(oc->_Jxz));
}
}
# undef INTERP
BLI_rw_mutex_unlock(&oc->oceanmutex);
}
2012-04-29 15:47:02 +00:00
void BKE_ocean_eval_xz(struct Ocean *oc, struct OceanResult *ocr, float x, float z)
{
2012-05-12 19:18:02 +00:00
BKE_ocean_eval_uv(oc, ocr, x / oc->_Lx, z / oc->_Lz);
}
2012-04-29 15:47:02 +00:00
void BKE_ocean_eval_xz_catrom(struct Ocean *oc, struct OceanResult *ocr, float x, float z)
{
2012-05-12 19:18:02 +00:00
BKE_ocean_eval_uv_catrom(oc, ocr, x / oc->_Lx, z / oc->_Lz);
}
/* note that this doesn't wrap properly for i, j < 0, but its not really meant for that being
* just a way to get the raw data out to save in some image format. */
2012-04-29 15:47:02 +00:00
void BKE_ocean_eval_ij(struct Ocean *oc, struct 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) {
2012-05-12 19:18:02 +00:00
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) {
2012-05-12 19:18:02 +00:00
ocr->normal[0] = oc->_N_x[i * oc->_N + j];
2012-10-27 10:42:28 +00:00
ocr->normal[1] = oc->_N_y /* oc->_N_y[i * oc->_N + j] (MEM01) */;
2012-05-12 19:18:02 +00:00
ocr->normal[2] = oc->_N_z[i * oc->_N + j];
normalize_v3(ocr->normal);
}
if (oc->_do_jacobian) {
2012-05-12 19:18:02 +00:00
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);
}
typedef struct OceanSimulateData {
Ocean *o;
float t;
float scale;
float chop_amount;
} OceanSimulateData;
static void ocean_compute_htilda(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict UNUSED(tls))
{
OceanSimulateData *osd = userdata;
const Ocean *o = osd->o;
const float scale = osd->scale;
const float t = osd->t;
int j;
2019-08-17 00:54:22 +10:00
/* 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 *UNUSED(taskdata))
{
OceanSimulateData *osd = 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 *UNUSED(taskdata))
{
OceanSimulateData *osd = 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 *UNUSED(taskdata))
{
OceanSimulateData *osd = 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 *UNUSED(taskdata))
{
OceanSimulateData *osd = 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 *UNUSED(taskdata))
{
OceanSimulateData *osd = 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 *UNUSED(taskdata))
{
OceanSimulateData *osd = 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 *UNUSED(taskdata))
{
OceanSimulateData *osd = 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 *UNUSED(taskdata))
{
OceanSimulateData *osd = 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);
}
void BKE_ocean_simulate(struct 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, TASK_ISOLATION_ON);
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, NULL, false, NULL);
}
if (o->_do_chop) {
BLI_task_pool_push(pool, ocean_compute_displacement_x, NULL, false, NULL);
BLI_task_pool_push(pool, ocean_compute_displacement_z, NULL, false, NULL);
}
if (o->_do_jacobian) {
BLI_task_pool_push(pool, ocean_compute_jacobian_jxx, NULL, false, NULL);
BLI_task_pool_push(pool, ocean_compute_jacobian_jzz, NULL, false, NULL);
BLI_task_pool_push(pool, ocean_compute_jacobian_jxz, NULL, false, NULL);
}
if (o->_do_normals) {
BLI_task_pool_push(pool, ocean_compute_normal_x, NULL, false, NULL);
BLI_task_pool_push(pool, ocean_compute_normal_z, NULL, false, NULL);
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(struct Ocean *oc)
{
float res = 1.0;
float max_h = 0.0;
2012-04-29 15:47:02 +00:00
int i, j;
if (!oc->_do_disp_y) {
return;
}
oc->normalize_factor = 1.0;
2015-04-04 15:18:52 +11:00
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++) {
2015-04-23 16:09:30 +10:00
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;
}
2015-04-04 15:18:52 +11:00
struct Ocean *BKE_ocean_add(void)
2012-05-17 23:21:11 +00:00
{
Ocean *oc = MEM_callocN(sizeof(Ocean), "ocean sim data");
BLI_rw_mutex_init(&oc->oceanmutex);
return oc;
}
bool BKE_ocean_ensure(struct 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;
}
void BKE_ocean_init_from_modifier(struct Ocean *ocean,
struct 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);
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);
}
2015-04-04 15:18:52 +11:00
void BKE_ocean_init(struct 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)
{
RNG *rng;
2012-04-29 15:47:02 +00:00
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;
2012-05-12 19:18:02 +00:00
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);
2012-05-12 19:18:02 +00:00
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;
2012-05-12 19:18:02 +00:00
o->_do_disp_y = do_height_field;
o->_do_normals = do_normals;
o->_do_spray = do_spray;
2012-05-12 19:18:02 +00:00
o->_do_chop = do_chop;
o->_do_jacobian = do_jacobian;
2012-05-12 19:18:02 +00:00
o->_k = (float *)MEM_mallocN(M * (1 + N / 2) * sizeof(float), "ocean_k");
o->_h0 = (fftw_complex *)MEM_mallocN(M * N * sizeof(fftw_complex), "ocean_h0");
o->_h0_minus = (fftw_complex *)MEM_mallocN(M * N * sizeof(fftw_complex), "ocean_h0_minus");
o->_kx = (float *)MEM_mallocN(o->_M * sizeof(float), "ocean_kx");
o->_kz = (float *)MEM_mallocN(o->_N * sizeof(float), "ocean_kz");
/* 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++) {
2012-05-12 19:18:02 +00:00
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++) {
2012-05-12 19:18:02 +00:00
o->_k[i * (1 + o->_N / 2) + j] = sqrt(o->_kx[i] * o->_kx[i] + o->_kz[j] * o->_kz[j]);
}
}
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 unsigned int 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");
2012-04-29 15:47:02 +00:00
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) {
2012-05-12 19:18:02 +00:00
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");
2012-07-29 00:20:28 +00:00
/* 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");
2012-04-29 15:47:02 +00:00
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");
2012-04-29 15:47:02 +00:00
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");
2012-04-29 15:47:02 +00:00
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);
}
2015-04-04 15:18:52 +11:00
void BKE_ocean_free_data(struct 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);
}
2012-03-18 07:38:51 +00:00
/* 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);
}
2015-04-04 15:18:52 +11:00
void BKE_ocean_free(struct Ocean *oc)
{
if (!oc) {
return;
}
2015-04-04 15:18:52 +11:00
BKE_ocean_free_data(oc);
BLI_rw_mutex_end(&oc->oceanmutex);
MEM_freeN(oc);
}
# undef GRAVITY
/* ********* Baking/Caching ********* */
2012-05-12 19:18:02 +00:00
# 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_filename(
char *string, const char *path, const char *relbase, int frame, int type)
{
char cachepath[FILE_MAX];
const char *fname;
switch (type) {
2012-05-12 19:18:02 +00:00
case CACHE_TYPE_FOAM:
fname = "foam_";
break;
case CACHE_TYPE_NORMAL:
fname = "normal_";
break;
case CACHE_TYPE_SPRAY:
fname = "spray_";
break;
case CACHE_TYPE_SPRAY_INVERSE:
fname = "spray_inverse_";
break;
2012-05-12 19:18:02 +00:00
case CACHE_TYPE_DISPLACE:
default:
fname = "disp_";
break;
}
BLI_join_dirfile(cachepath, sizeof(cachepath), path, fname);
Multi-View and Stereo 3D Official Documentation: http://www.blender.org/manual/render/workflows/multiview.html Implemented Features ==================== Builtin Stereo Camera * Convergence Mode * Interocular Distance * Convergence Distance * Pivot Mode Viewport * Cameras * Plane * Volume Compositor * View Switch Node * Image Node Multi-View OpenEXR support Sequencer * Image/Movie Strips 'Use Multiview' UV/Image Editor * Option to see Multi-View images in Stereo-3D or its individual images * Save/Open Multi-View (OpenEXR, Stereo3D, individual views) images I/O * Save/Open Multi-View (OpenEXR, Stereo3D, individual views) images Scene Render Views * Ability to have an arbitrary number of views in the scene Missing Bits ============ First rule of Multi-View bug report: If something is not working as it should *when Views is off* this is a severe bug, do mention this in the report. Second rule is, if something works *when Views is off* but doesn't (or crashes) when *Views is on*, this is a important bug. Do mention this in the report. Everything else is likely small todos, and may wait until we are sure none of the above is happening. Apart from that there are those known issues: * Compositor Image Node poorly working for Multi-View OpenEXR (this was working prefectly before the 'Use Multi-View' functionality) * Selecting camera from Multi-View when looking from camera is problematic * Animation Playback (ctrl+F11) doesn't support stereo formats * Wrong filepath when trying to play back animated scene * Viewport Rendering doesn't support Multi-View * Overscan Rendering * Fullscreen display modes need to warn the user * Object copy should be aware of views suffix Acknowledgments =============== * Francesco Siddi for the help with the original feature specs and design * Brecht Van Lommel for the original review of the code and design early on * Blender Foundation for the Development Fund to support the project wrap up Final patch reviewers: * Antony Riakiotakis (psy-fi) * Campbell Barton (ideasman42) * Julian Eisel (Severin) * Sergey Sharybin (nazgul) * Thomas Dinged (dingto) Code contributors of the original branch in github: * Alexey Akishin * Gabriel Caraballo
2015-04-06 10:40:12 -03:00
BKE_image_path_from_imtype(
string, 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])
{
2012-05-12 19:18:02 +00:00
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)
{
2012-05-12 19:18:02 +00:00
r_rgba[0] = value;
r_rgba[1] = value;
r_rgba[2] = value;
r_rgba[3] = 1.0f;
}
2015-04-04 15:18:52 +11:00
void BKE_ocean_free_cache(struct OceanCache *och)
{
2012-05-12 19:18:02 +00:00
int i, f = 0;
if (!och) {
return;
}
if (och->ibufs_disp) {
2012-05-12 19:18:02 +00:00
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) {
2012-05-12 19:18:02 +00:00
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) {
2012-05-12 19:18:02 +00:00
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(
struct OceanCache *och, struct 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]) {
2012-05-12 19:18:02 +00:00
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]) {
2012-05-12 19:18:02 +00:00
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]) {
2012-05-12 19:18:02 +00:00
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(struct OceanCache *och, struct 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) {
2012-05-12 19:18:02 +00:00
i = -i;
}
if (j < 0) {
2012-05-12 19:18:02 +00:00
j = -j;
}
i = i % res_x;
j = j % res_y;
if (och->ibufs_disp[f]) {
2012-05-12 19:18:02 +00:00
copy_v3_v3(ocr->disp, &och->ibufs_disp[f]->rect_float[4 * (res_x * j + i)]);
}
if (och->ibufs_foam[f]) {
2012-05-12 19:18:02 +00:00
ocr->foam = och->ibufs_foam[f]->rect_float[4 * (res_x * j + i)];
}
if (och->ibufs_spray[f]) {
copy_v3_v3(ocr->Eplus, &och->ibufs_spray[f]->rect_float[4 * (res_x * j + i)]);
}
if (och->ibufs_spray_inverse[f]) {
copy_v3_v3(ocr->Eminus, &och->ibufs_spray_inverse[f]->rect_float[4 * (res_x * j + i)]);
}
if (och->ibufs_norm[f]) {
2012-05-12 19:18:02 +00:00
copy_v3_v3(ocr->normal, &och->ibufs_norm[f]->rect_float[4 * (res_x * j + i)]);
}
}
2015-04-04 15:18:52 +11:00
struct OceanCache *BKE_ocean_init_cache(const char *bakepath,
const char *relbase,
int start,
int end,
float wave_scale,
2012-05-17 23:21:11 +00:00
float chop_amount,
float foam_coverage,
float foam_fade,
int resolution)
{
OceanCache *och = 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;
2012-05-12 19:18:02 +00:00
och->resolution_x = resolution * resolution;
och->resolution_y = resolution * resolution;
2012-05-12 19:18:02 +00:00
och->ibufs_disp = MEM_callocN(sizeof(ImBuf *) * och->duration,
"displacement imbuf pointer array");
och->ibufs_foam = MEM_callocN(sizeof(ImBuf *) * och->duration, "foam imbuf pointer array");
och->ibufs_spray = MEM_callocN(sizeof(ImBuf *) * och->duration, "spray imbuf pointer array");
och->ibufs_spray_inverse = MEM_callocN(sizeof(ImBuf *) * och->duration,
"spray_inverse imbuf pointer array");
2012-05-12 19:18:02 +00:00
och->ibufs_norm = MEM_callocN(sizeof(ImBuf *) * och->duration, "normal imbuf pointer array");
och->time = NULL;
return och;
}
2015-04-04 15:18:52 +11:00
void BKE_ocean_simulate_cache(struct OceanCache *och, int frame)
{
char string[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] != NULL) {
2012-05-12 19:18:02 +00:00
return;
}
/* Use default color spaces since we know for sure cache
* files were saved with default settings too. */
cache_filename(string, och->bakepath, och->relbase, frame, CACHE_TYPE_DISPLACE);
Color Management, Stage 2: Switch color pipeline to use OpenColorIO Replace old color pipeline which was supporting linear/sRGB color spaces only with OpenColorIO-based pipeline. This introduces two configurable color spaces: - Input color space for images and movie clips. This space is used to convert images/movies from color space in which file is saved to Blender's linear space (for float images, byte images are not internally converted, only input space is stored for such images and used later). This setting could be found in image/clip data block settings. - Display color space which defines space in which particular display is working. This settings could be found in scene's Color Management panel. When render result is being displayed on the screen, apart from converting image to display space, some additional conversions could happen. This conversions are: - View, which defines tone curve applying before display transformation. These are different ways to view the image on the same display device. For example it could be used to emulate film view on sRGB display. - Exposure affects on image exposure before tone map is applied. - Gamma is post-display gamma correction, could be used to match particular display gamma. - RGB curves are user-defined curves which are applying before display transformation, could be used for different purposes. All this settings by default are only applying on render result and does not affect on other images. If some particular image needs to be affected by this transformation, "View as Render" setting of image data block should be set to truth. Movie clips are always affected by all display transformations. This commit also introduces configurable color space in which sequencer is working. This setting could be found in scene's Color Management panel and it should be used if such stuff as grading needs to be done in color space different from sRGB (i.e. when Film view on sRGB display is use, using VD16 space as sequencer's internal space would make grading working in space which is close to the space using for display). Some technical notes: - Image buffer's float buffer is now always in linear space, even if it was created from 16bit byte images. - Space of byte buffer is stored in image buffer's rect_colorspace property. - Profile of image buffer was removed since it's not longer meaningful. - OpenGL and GLSL is supposed to always work in sRGB space. It is possible to support other spaces, but it's quite large project which isn't so much important. - Legacy Color Management option disabled is emulated by using None display. It could have some regressions, but there's no clear way to avoid them. - If OpenColorIO is disabled on build time, it should make blender behaving in the same way as previous release with color management enabled. More details could be found at this page (more details would be added soon): http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.64/Color_Management -- Thanks to Xavier Thomas, Lukas Toene for initial work on OpenColorIO integration and to Brecht van Lommel for some further development and code/ usecase review!
2012-09-15 10:05:07 +00:00
och->ibufs_disp[f] = IMB_loadiffname(string, 0, NULL);
cache_filename(string, och->bakepath, och->relbase, frame, CACHE_TYPE_FOAM);
Color Management, Stage 2: Switch color pipeline to use OpenColorIO Replace old color pipeline which was supporting linear/sRGB color spaces only with OpenColorIO-based pipeline. This introduces two configurable color spaces: - Input color space for images and movie clips. This space is used to convert images/movies from color space in which file is saved to Blender's linear space (for float images, byte images are not internally converted, only input space is stored for such images and used later). This setting could be found in image/clip data block settings. - Display color space which defines space in which particular display is working. This settings could be found in scene's Color Management panel. When render result is being displayed on the screen, apart from converting image to display space, some additional conversions could happen. This conversions are: - View, which defines tone curve applying before display transformation. These are different ways to view the image on the same display device. For example it could be used to emulate film view on sRGB display. - Exposure affects on image exposure before tone map is applied. - Gamma is post-display gamma correction, could be used to match particular display gamma. - RGB curves are user-defined curves which are applying before display transformation, could be used for different purposes. All this settings by default are only applying on render result and does not affect on other images. If some particular image needs to be affected by this transformation, "View as Render" setting of image data block should be set to truth. Movie clips are always affected by all display transformations. This commit also introduces configurable color space in which sequencer is working. This setting could be found in scene's Color Management panel and it should be used if such stuff as grading needs to be done in color space different from sRGB (i.e. when Film view on sRGB display is use, using VD16 space as sequencer's internal space would make grading working in space which is close to the space using for display). Some technical notes: - Image buffer's float buffer is now always in linear space, even if it was created from 16bit byte images. - Space of byte buffer is stored in image buffer's rect_colorspace property. - Profile of image buffer was removed since it's not longer meaningful. - OpenGL and GLSL is supposed to always work in sRGB space. It is possible to support other spaces, but it's quite large project which isn't so much important. - Legacy Color Management option disabled is emulated by using None display. It could have some regressions, but there's no clear way to avoid them. - If OpenColorIO is disabled on build time, it should make blender behaving in the same way as previous release with color management enabled. More details could be found at this page (more details would be added soon): http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.64/Color_Management -- Thanks to Xavier Thomas, Lukas Toene for initial work on OpenColorIO integration and to Brecht van Lommel for some further development and code/ usecase review!
2012-09-15 10:05:07 +00:00
och->ibufs_foam[f] = IMB_loadiffname(string, 0, NULL);
cache_filename(string, och->bakepath, och->relbase, frame, CACHE_TYPE_SPRAY);
och->ibufs_spray[f] = IMB_loadiffname(string, 0, NULL);
cache_filename(string, och->bakepath, och->relbase, frame, CACHE_TYPE_SPRAY_INVERSE);
och->ibufs_spray_inverse[f] = IMB_loadiffname(string, 0, NULL);
cache_filename(string, och->bakepath, och->relbase, frame, CACHE_TYPE_NORMAL);
Color Management, Stage 2: Switch color pipeline to use OpenColorIO Replace old color pipeline which was supporting linear/sRGB color spaces only with OpenColorIO-based pipeline. This introduces two configurable color spaces: - Input color space for images and movie clips. This space is used to convert images/movies from color space in which file is saved to Blender's linear space (for float images, byte images are not internally converted, only input space is stored for such images and used later). This setting could be found in image/clip data block settings. - Display color space which defines space in which particular display is working. This settings could be found in scene's Color Management panel. When render result is being displayed on the screen, apart from converting image to display space, some additional conversions could happen. This conversions are: - View, which defines tone curve applying before display transformation. These are different ways to view the image on the same display device. For example it could be used to emulate film view on sRGB display. - Exposure affects on image exposure before tone map is applied. - Gamma is post-display gamma correction, could be used to match particular display gamma. - RGB curves are user-defined curves which are applying before display transformation, could be used for different purposes. All this settings by default are only applying on render result and does not affect on other images. If some particular image needs to be affected by this transformation, "View as Render" setting of image data block should be set to truth. Movie clips are always affected by all display transformations. This commit also introduces configurable color space in which sequencer is working. This setting could be found in scene's Color Management panel and it should be used if such stuff as grading needs to be done in color space different from sRGB (i.e. when Film view on sRGB display is use, using VD16 space as sequencer's internal space would make grading working in space which is close to the space using for display). Some technical notes: - Image buffer's float buffer is now always in linear space, even if it was created from 16bit byte images. - Space of byte buffer is stored in image buffer's rect_colorspace property. - Profile of image buffer was removed since it's not longer meaningful. - OpenGL and GLSL is supposed to always work in sRGB space. It is possible to support other spaces, but it's quite large project which isn't so much important. - Legacy Color Management option disabled is emulated by using None display. It could have some regressions, but there's no clear way to avoid them. - If OpenColorIO is disabled on build time, it should make blender behaving in the same way as previous release with color management enabled. More details could be found at this page (more details would be added soon): http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.64/Color_Management -- Thanks to Xavier Thomas, Lukas Toene for initial work on OpenColorIO integration and to Brecht van Lommel for some further development and code/ usecase review!
2012-09-15 10:05:07 +00:00
och->ibufs_norm[f] = IMB_loadiffname(string, 0, NULL);
}
2015-04-04 15:18:52 +11:00
void BKE_ocean_bake(struct Ocean *o,
struct OceanCache *och,
void (*update_cb)(void *, float progress, int *cancel),
void *update_cb_data)
{
/* note: some of these values remain uninitialized unless certain options
* are enabled, take care that BKE_ocean_eval_ij() initializes a member
* before use - campbell */
OceanResult ocr;
2012-05-12 19:18:02 +00:00
ImageFormatData imf = {0};
2012-05-12 19:18:02 +00:00
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 string[FILE_MAX];
// RNG *rng;
if (!o) {
return;
}
if (o->_do_jacobian) {
2012-05-12 19:18:02 +00:00
prev_foam = MEM_callocN(res_x * res_y * sizeof(float), "previous frame foam bake data");
}
else {
2012-05-12 19:18:02 +00:00
prev_foam = NULL;
}
// rng = BLI_rng_new(0);
/* setup image format */
2012-05-12 19:18:02 +00:00
imf.imtype = R_IMF_IMTYPE_OPENEXR;
imf.depth = R_IMF_CHAN_DEPTH_16;
imf.exr_codec = R_IMF_EXR_CODEC_ZIP;
2012-05-12 19:18:02 +00:00
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);
2015-04-04 15:18:52 +11:00
BKE_ocean_simulate(o, och->time[i], och->wave_scale, och->chop_amount);
/* add new foam */
2012-05-12 19:18:02 +00:00
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 */
2012-05-12 19:18:02 +00:00
rgb_to_rgba_unit_alpha(&ibuf_disp->rect_float[4 * (res_x * y + x)], ocr.disp);
if (o->_do_jacobian) {
/* TODO, cleanup unused code - campbell */
2012-05-12 19:18:02 +00:00
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) {
2012-05-12 19:18:02 +00:00
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 sys is Y up!
* break up the foam where height (Y) is low (wave valley),
* and X and Z displacement is greatest. */
2012-05-12 19:18:02 +00:00
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 */
2012-05-12 19:18:02 +00:00
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);
2012-05-12 19:18:02 +00:00
prev_foam[res_x * y + x] = foam_result;
/*foam_result = min_ff(foam_result, 1.0f); */
2012-05-12 19:18:02 +00:00
value_to_rgba_unit_alpha(&ibuf_foam->rect_float[4 * (res_x * y + x)], foam_result);
/* spray map baking */
if (o->_do_spray) {
rgb_to_rgba_unit_alpha(&ibuf_spray->rect_float[4 * (res_x * y + x)], ocr.Eplus);
rgb_to_rgba_unit_alpha(&ibuf_spray_inverse->rect_float[4 * (res_x * y + x)],
ocr.Eminus);
}
}
if (o->_do_normals) {
2012-05-12 19:18:02 +00:00
rgb_to_rgba_unit_alpha(&ibuf_normal->rect_float[4 * (res_x * y + x)], ocr.normal);
}
}
}
/* write the images */
cache_filename(string, och->bakepath, och->relbase, f, CACHE_TYPE_DISPLACE);
if (0 == BKE_imbuf_write(ibuf_disp, string, &imf)) {
printf("Cannot save Displacement File Output to %s\n", string);
}
if (o->_do_jacobian) {
cache_filename(string, och->bakepath, och->relbase, f, CACHE_TYPE_FOAM);
if (0 == BKE_imbuf_write(ibuf_foam, string, &imf)) {
printf("Cannot save Foam File Output to %s\n", string);
}
if (o->_do_spray) {
cache_filename(string, och->bakepath, och->relbase, f, CACHE_TYPE_SPRAY);
if (0 == BKE_imbuf_write(ibuf_spray, string, &imf)) {
printf("Cannot save Spray File Output to %s\n", string);
}
cache_filename(string, och->bakepath, och->relbase, f, CACHE_TYPE_SPRAY_INVERSE);
if (0 == BKE_imbuf_write(ibuf_spray_inverse, string, &imf)) {
printf("Cannot save Spray Inverse File Output to %s\n", string);
}
}
}
if (o->_do_normals) {
cache_filename(string, och->bakepath, och->relbase, f, CACHE_TYPE_NORMAL);
if (0 == BKE_imbuf_write(ibuf_normal, string, &imf)) {
printf("Cannot save Normal File Output to %s\n", string);
}
}
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 UNUSED(jminus), float UNUSED(coverage))
{
return 0.0f;
}
2012-04-29 15:47:02 +00:00
void BKE_ocean_eval_uv(struct Ocean *UNUSED(oc),
struct OceanResult *UNUSED(ocr),
float UNUSED(u),
float UNUSED(v))
{
}
/* use catmullrom interpolation rather than linear */
void BKE_ocean_eval_uv_catrom(struct Ocean *UNUSED(oc),
struct OceanResult *UNUSED(ocr),
float UNUSED(u),
float UNUSED(v))
{
}
2012-04-29 15:47:02 +00:00
void BKE_ocean_eval_xz(struct Ocean *UNUSED(oc),
struct OceanResult *UNUSED(ocr),
float UNUSED(x),
float UNUSED(z))
{
}
void BKE_ocean_eval_xz_catrom(struct Ocean *UNUSED(oc),
struct OceanResult *UNUSED(ocr),
float UNUSED(x),
float UNUSED(z))
{
}
2012-04-29 15:47:02 +00:00
void BKE_ocean_eval_ij(struct Ocean *UNUSED(oc),
struct OceanResult *UNUSED(ocr),
int UNUSED(i),
int UNUSED(j))
{
}
2015-04-04 15:18:52 +11:00
void BKE_ocean_simulate(struct Ocean *UNUSED(o),
float UNUSED(t),
float UNUSED(scale),
float UNUSED(chop_amount))
{
}
2015-04-04 15:18:52 +11:00
struct Ocean *BKE_ocean_add(void)
{
Ocean *oc = MEM_callocN(sizeof(Ocean), "ocean sim data");
return oc;
}
2015-04-04 15:18:52 +11:00
void BKE_ocean_init(struct Ocean *UNUSED(o),
int UNUSED(M),
int UNUSED(N),
float UNUSED(Lx),
float UNUSED(Lz),
float UNUSED(V),
float UNUSED(l),
float UNUSED(A),
float UNUSED(w),
float UNUSED(damp),
float UNUSED(alignment),
float UNUSED(depth),
float UNUSED(time),
int UNUSED(spectrum),
float UNUSED(fetch_jonswap),
float UNUSED(sharpen_peak_jonswap),
short UNUSED(do_height_field),
short UNUSED(do_chop),
short UNUSED(do_spray),
short UNUSED(do_normals),
short UNUSED(do_jacobian),
int UNUSED(seed))
{
}
2015-04-04 15:18:52 +11:00
void BKE_ocean_free_data(struct Ocean *UNUSED(oc))
{
}
2015-04-04 15:18:52 +11:00
void BKE_ocean_free(struct Ocean *oc)
{
if (!oc) {
return;
}
MEM_freeN(oc);
}
/* ********* Baking/Caching ********* */
2015-04-04 15:18:52 +11:00
void BKE_ocean_free_cache(struct OceanCache *och)
{
if (!och) {
return;
}
MEM_freeN(och);
}
void BKE_ocean_cache_eval_uv(struct OceanCache *UNUSED(och),
struct OceanResult *UNUSED(ocr),
int UNUSED(f),
float UNUSED(u),
float UNUSED(v))
{
}
void BKE_ocean_cache_eval_ij(struct OceanCache *UNUSED(och),
struct OceanResult *UNUSED(ocr),
int UNUSED(f),
int UNUSED(i),
int UNUSED(j))
{
}
2015-04-04 15:18:52 +11:00
OceanCache *BKE_ocean_init_cache(const char *UNUSED(bakepath),
const char *UNUSED(relbase),
int UNUSED(start),
int UNUSED(end),
float UNUSED(wave_scale),
float UNUSED(chop_amount),
float UNUSED(foam_coverage),
float UNUSED(foam_fade),
int UNUSED(resolution))
2012-05-17 23:21:11 +00:00
{
OceanCache *och = MEM_callocN(sizeof(OceanCache), "ocean cache data");
return och;
}
2015-04-04 15:18:52 +11:00
void BKE_ocean_simulate_cache(struct OceanCache *UNUSED(och), int UNUSED(frame))
{
}
2015-04-04 15:18:52 +11:00
void BKE_ocean_bake(struct Ocean *UNUSED(o),
struct OceanCache *UNUSED(och),
void (*update_cb)(void *, float progress, int *cancel),
void *UNUSED(update_cb_data))
{
/* unused */
(void)update_cb;
}
void BKE_ocean_init_from_modifier(struct Ocean *UNUSED(ocean),
struct OceanModifierData const *UNUSED(omd),
int UNUSED(resolution))
{
}
#endif /* WITH_OCEANSIM */
void BKE_ocean_free_modifier_cache(struct OceanModifierData *omd)
{
BKE_ocean_free_cache(omd->oceancache);
omd->oceancache = NULL;
omd->cached = false;
}