517 lines
13 KiB
C
517 lines
13 KiB
C
/*
|
|
* 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) Blender Foundation
|
|
* All rights reserved.
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup modifiers
|
|
*/
|
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_math_inline.h"
|
|
#include "BLI_task.h"
|
|
|
|
#include "DNA_customdata_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_modifier_types.h"
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "BKE_library.h"
|
|
#include "BKE_mesh.h"
|
|
#include "BKE_modifier.h"
|
|
#include "BKE_ocean.h"
|
|
|
|
#include "DEG_depsgraph_query.h"
|
|
|
|
#include "MOD_modifiertypes.h"
|
|
|
|
#ifdef WITH_OCEANSIM
|
|
static void init_cache_data(Object *ob, struct OceanModifierData *omd)
|
|
{
|
|
const char *relbase = modifier_path_relbase_from_global(ob);
|
|
|
|
omd->oceancache = BKE_ocean_init_cache(omd->cachepath, relbase,
|
|
omd->bakestart, omd->bakeend, omd->wave_scale,
|
|
omd->chop_amount, omd->foam_coverage, omd->foam_fade, omd->resolution);
|
|
}
|
|
|
|
static void simulate_ocean_modifier(struct OceanModifierData *omd)
|
|
{
|
|
BKE_ocean_simulate(omd->ocean, omd->time, omd->wave_scale, omd->chop_amount);
|
|
}
|
|
#endif /* WITH_OCEANSIM */
|
|
|
|
|
|
|
|
/* Modifier Code */
|
|
|
|
static void initData(ModifierData *md)
|
|
{
|
|
#ifdef WITH_OCEANSIM
|
|
OceanModifierData *omd = (OceanModifierData *) md;
|
|
|
|
omd->resolution = 7;
|
|
omd->spatial_size = 50;
|
|
|
|
omd->wave_alignment = 0.0;
|
|
omd->wind_velocity = 30.0;
|
|
|
|
omd->damp = 0.5;
|
|
omd->smallest_wave = 0.01;
|
|
omd->wave_direction = 0.0;
|
|
omd->depth = 200.0;
|
|
|
|
omd->wave_scale = 1.0;
|
|
|
|
omd->chop_amount = 1.0;
|
|
|
|
omd->foam_coverage = 0.0;
|
|
|
|
omd->seed = 0;
|
|
omd->time = 1.0;
|
|
|
|
omd->size = 1.0;
|
|
omd->repeat_x = 1;
|
|
omd->repeat_y = 1;
|
|
|
|
modifier_path_init(omd->cachepath, sizeof(omd->cachepath), "cache_ocean");
|
|
|
|
omd->cached = 0;
|
|
omd->bakestart = 1;
|
|
omd->bakeend = 250;
|
|
omd->oceancache = NULL;
|
|
omd->foam_fade = 0.98;
|
|
omd->foamlayername[0] = '\0'; /* layer name empty by default */
|
|
|
|
omd->ocean = BKE_ocean_add();
|
|
BKE_ocean_init_from_modifier(omd->ocean, omd);
|
|
simulate_ocean_modifier(omd);
|
|
#else /* WITH_OCEANSIM */
|
|
/* unused */
|
|
(void)md;
|
|
#endif /* WITH_OCEANSIM */
|
|
}
|
|
|
|
static void freeData(ModifierData *md)
|
|
{
|
|
#ifdef WITH_OCEANSIM
|
|
OceanModifierData *omd = (OceanModifierData *) md;
|
|
|
|
BKE_ocean_free(omd->ocean);
|
|
if (omd->oceancache)
|
|
BKE_ocean_free_cache(omd->oceancache);
|
|
#else /* WITH_OCEANSIM */
|
|
/* unused */
|
|
(void)md;
|
|
#endif /* WITH_OCEANSIM */
|
|
}
|
|
|
|
static void copyData(const ModifierData *md, ModifierData *target, const int flag)
|
|
{
|
|
#ifdef WITH_OCEANSIM
|
|
#if 0
|
|
const OceanModifierData *omd = (const OceanModifierData *) md;
|
|
#endif
|
|
OceanModifierData *tomd = (OceanModifierData *) target;
|
|
|
|
modifier_copyData_generic(md, target, flag);
|
|
|
|
/* The oceancache object will be recreated for this copy
|
|
* automatically when cached=true */
|
|
tomd->oceancache = NULL;
|
|
|
|
tomd->ocean = BKE_ocean_add();
|
|
BKE_ocean_init_from_modifier(tomd->ocean, tomd);
|
|
simulate_ocean_modifier(tomd);
|
|
#else /* WITH_OCEANSIM */
|
|
/* unused */
|
|
(void)md;
|
|
(void)target;
|
|
(void)flag;
|
|
#endif /* WITH_OCEANSIM */
|
|
}
|
|
|
|
#ifdef WITH_OCEANSIM
|
|
static void requiredDataMask(Object *UNUSED(ob), ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
|
|
{
|
|
OceanModifierData *omd = (OceanModifierData *)md;
|
|
|
|
if (omd->flag & MOD_OCEAN_GENERATE_FOAM) {
|
|
r_cddata_masks->fmask |= CD_MASK_MCOL; /* XXX Should be loop cddata I guess? */
|
|
}
|
|
}
|
|
#else /* WITH_OCEANSIM */
|
|
static void requiredDataMask(Object *UNUSED(ob), ModifierData *UNUSED(md), CustomData_MeshMasks *UNUSED(r_cddata_masks))
|
|
{
|
|
}
|
|
#endif /* WITH_OCEANSIM */
|
|
|
|
static bool dependsOnNormals(ModifierData *md)
|
|
{
|
|
OceanModifierData *omd = (OceanModifierData *)md;
|
|
return (omd->geometry_mode != MOD_OCEAN_GEOM_GENERATE);
|
|
}
|
|
|
|
#ifdef WITH_OCEANSIM
|
|
|
|
typedef struct GenerateOceanGeometryData {
|
|
MVert *mverts;
|
|
MPoly *mpolys;
|
|
MLoop *mloops;
|
|
MLoopUV *mloopuvs;
|
|
|
|
int res_x, res_y;
|
|
int rx, ry;
|
|
float ox, oy;
|
|
float sx, sy;
|
|
float ix, iy;
|
|
} GenerateOceanGeometryData;
|
|
|
|
static void generate_ocean_geometry_vertices(
|
|
void *__restrict userdata,
|
|
const int y,
|
|
const ParallelRangeTLS *__restrict UNUSED(tls))
|
|
{
|
|
GenerateOceanGeometryData *gogd = userdata;
|
|
int x;
|
|
|
|
for (x = 0; x <= gogd->res_x; x++) {
|
|
const int i = y * (gogd->res_x + 1) + x;
|
|
float *co = gogd->mverts[i].co;
|
|
co[0] = gogd->ox + (x * gogd->sx);
|
|
co[1] = gogd->oy + (y * gogd->sy);
|
|
co[2] = 0.0f;
|
|
}
|
|
}
|
|
|
|
static void generate_ocean_geometry_polygons(
|
|
void *__restrict userdata,
|
|
const int y,
|
|
const ParallelRangeTLS *__restrict UNUSED(tls))
|
|
{
|
|
GenerateOceanGeometryData *gogd = userdata;
|
|
int x;
|
|
|
|
for (x = 0; x < gogd->res_x; x++) {
|
|
const int fi = y * gogd->res_x + x;
|
|
const int vi = y * (gogd->res_x + 1) + x;
|
|
MPoly *mp = &gogd->mpolys[fi];
|
|
MLoop *ml = &gogd->mloops[fi * 4];
|
|
|
|
ml->v = vi;
|
|
ml++;
|
|
ml->v = vi + 1;
|
|
ml++;
|
|
ml->v = vi + 1 + gogd->res_x + 1;
|
|
ml++;
|
|
ml->v = vi + gogd->res_x + 1;
|
|
ml++;
|
|
|
|
mp->loopstart = fi * 4;
|
|
mp->totloop = 4;
|
|
|
|
mp->flag |= ME_SMOOTH;
|
|
}
|
|
}
|
|
|
|
static void generate_ocean_geometry_uvs(
|
|
void *__restrict userdata,
|
|
const int y,
|
|
const ParallelRangeTLS *__restrict UNUSED(tls))
|
|
{
|
|
GenerateOceanGeometryData *gogd = userdata;
|
|
int x;
|
|
|
|
for (x = 0; x < gogd->res_x; x++) {
|
|
const int i = y * gogd->res_x + x;
|
|
MLoopUV *luv = &gogd->mloopuvs[i * 4];
|
|
|
|
luv->uv[0] = x * gogd->ix;
|
|
luv->uv[1] = y * gogd->iy;
|
|
luv++;
|
|
|
|
luv->uv[0] = (x + 1) * gogd->ix;
|
|
luv->uv[1] = y * gogd->iy;
|
|
luv++;
|
|
|
|
luv->uv[0] = (x + 1) * gogd->ix;
|
|
luv->uv[1] = (y + 1) * gogd->iy;
|
|
luv++;
|
|
|
|
luv->uv[0] = x * gogd->ix;
|
|
luv->uv[1] = (y + 1) * gogd->iy;
|
|
luv++;
|
|
}
|
|
}
|
|
|
|
static Mesh *generate_ocean_geometry(OceanModifierData *omd)
|
|
{
|
|
Mesh *result;
|
|
|
|
GenerateOceanGeometryData gogd;
|
|
|
|
int num_verts;
|
|
int num_polys;
|
|
|
|
const bool use_threading = omd->resolution > 4;
|
|
|
|
gogd.rx = omd->resolution * omd->resolution;
|
|
gogd.ry = omd->resolution * omd->resolution;
|
|
gogd.res_x = gogd.rx * omd->repeat_x;
|
|
gogd.res_y = gogd.ry * omd->repeat_y;
|
|
|
|
num_verts = (gogd.res_x + 1) * (gogd.res_y + 1);
|
|
num_polys = gogd.res_x * gogd.res_y;
|
|
|
|
gogd.sx = omd->size * omd->spatial_size;
|
|
gogd.sy = omd->size * omd->spatial_size;
|
|
gogd.ox = -gogd.sx / 2.0f;
|
|
gogd.oy = -gogd.sy / 2.0f;
|
|
|
|
gogd.sx /= gogd.rx;
|
|
gogd.sy /= gogd.ry;
|
|
|
|
result = BKE_mesh_new_nomain(num_verts, 0, 0, num_polys * 4, num_polys);
|
|
|
|
gogd.mverts = result->mvert;
|
|
gogd.mpolys = result->mpoly;
|
|
gogd.mloops = result->mloop;
|
|
|
|
ParallelRangeSettings settings;
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
settings.use_threading = use_threading;
|
|
|
|
/* create vertices */
|
|
BLI_task_parallel_range(0, gogd.res_y + 1, &gogd, generate_ocean_geometry_vertices, &settings);
|
|
|
|
/* create faces */
|
|
BLI_task_parallel_range(0, gogd.res_y, &gogd, generate_ocean_geometry_polygons, &settings);
|
|
|
|
BKE_mesh_calc_edges(result, false, false);
|
|
|
|
/* add uvs */
|
|
if (CustomData_number_of_layers(&result->ldata, CD_MLOOPUV) < MAX_MTFACE) {
|
|
gogd.mloopuvs = CustomData_add_layer(&result->ldata, CD_MLOOPUV, CD_CALLOC, NULL, num_polys * 4);
|
|
|
|
if (gogd.mloopuvs) { /* unlikely to fail */
|
|
gogd.ix = 1.0 / gogd.rx;
|
|
gogd.iy = 1.0 / gogd.ry;
|
|
|
|
BLI_task_parallel_range(0, gogd.res_y, &gogd, generate_ocean_geometry_uvs, &settings);
|
|
}
|
|
}
|
|
|
|
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
|
|
|
return result;
|
|
}
|
|
|
|
static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
|
|
{
|
|
OceanModifierData *omd = (OceanModifierData *) md;
|
|
int cfra_scene = (int)DEG_get_ctime(ctx->depsgraph);
|
|
Object *ob = ctx->object;
|
|
bool allocated_ocean = false;
|
|
|
|
Mesh *result = NULL;
|
|
OceanResult ocr;
|
|
|
|
MVert *mverts;
|
|
|
|
int cfra_for_cache;
|
|
int i, j;
|
|
|
|
/* use cached & inverted value for speed
|
|
* expanded this would read...
|
|
*
|
|
* (axis / (omd->size * omd->spatial_size)) + 0.5f) */
|
|
#define OCEAN_CO(_size_co_inv, _v) ((_v * _size_co_inv) + 0.5f)
|
|
|
|
const float size_co_inv = 1.0f / (omd->size * omd->spatial_size);
|
|
|
|
/* can happen in when size is small, avoid bad array lookups later and quit now */
|
|
if (!isfinite(size_co_inv)) {
|
|
return mesh;
|
|
}
|
|
|
|
/* do ocean simulation */
|
|
if (omd->cached == true) {
|
|
if (!omd->oceancache) {
|
|
init_cache_data(ob, omd);
|
|
}
|
|
BKE_ocean_simulate_cache(omd->oceancache, cfra_scene);
|
|
}
|
|
else {
|
|
/* omd->ocean is NULL on an original object (in contrast to an evaluated one).
|
|
* We can create a new one, but we have to free it as well once we're done.
|
|
* This function is only called on an original object when applying the modifier
|
|
* using the 'Apply Modifier' button, and thus it is not called frequently for
|
|
* simulation. */
|
|
allocated_ocean |= BKE_ocean_ensure(omd);
|
|
simulate_ocean_modifier(omd);
|
|
}
|
|
|
|
if (omd->geometry_mode == MOD_OCEAN_GEOM_GENERATE) {
|
|
result = generate_ocean_geometry(omd);
|
|
BKE_mesh_ensure_normals(result);
|
|
}
|
|
else if (omd->geometry_mode == MOD_OCEAN_GEOM_DISPLACE) {
|
|
BKE_id_copy_ex(NULL, &mesh->id, (ID **)&result, LIB_ID_COPY_LOCALIZE);
|
|
}
|
|
|
|
cfra_for_cache = cfra_scene;
|
|
CLAMP(cfra_for_cache, omd->bakestart, omd->bakeend);
|
|
cfra_for_cache -= omd->bakestart; /* shift to 0 based */
|
|
|
|
mverts = result->mvert;
|
|
|
|
/* add vcols before displacement - allows lookup based on position */
|
|
|
|
if (omd->flag & MOD_OCEAN_GENERATE_FOAM) {
|
|
if (CustomData_number_of_layers(&result->ldata, CD_MLOOPCOL) < MAX_MCOL) {
|
|
const int num_polys = result->totpoly;
|
|
const int num_loops = result->totloop;
|
|
MLoop *mloops = result->mloop;
|
|
MLoopCol *mloopcols = CustomData_add_layer_named(
|
|
&result->ldata, CD_MLOOPCOL, CD_CALLOC, NULL, num_loops, omd->foamlayername);
|
|
|
|
if (mloopcols) { /* unlikely to fail */
|
|
MPoly *mpolys = result->mpoly;
|
|
MPoly *mp;
|
|
|
|
for (i = 0, mp = mpolys; i < num_polys; i++, mp++) {
|
|
MLoop *ml = &mloops[mp->loopstart];
|
|
MLoopCol *mlcol = &mloopcols[mp->loopstart];
|
|
|
|
for (j = mp->totloop; j--; ml++, mlcol++) {
|
|
const float *vco = mverts[ml->v].co;
|
|
const float u = OCEAN_CO(size_co_inv, vco[0]);
|
|
const float v = OCEAN_CO(size_co_inv, vco[1]);
|
|
float foam;
|
|
|
|
if (omd->oceancache && omd->cached == true) {
|
|
BKE_ocean_cache_eval_uv(omd->oceancache, &ocr, cfra_for_cache, u, v);
|
|
foam = ocr.foam;
|
|
CLAMP(foam, 0.0f, 1.0f);
|
|
}
|
|
else {
|
|
BKE_ocean_eval_uv(omd->ocean, &ocr, u, v);
|
|
foam = BKE_ocean_jminus_to_foam(ocr.Jminus, omd->foam_coverage);
|
|
}
|
|
|
|
mlcol->r = mlcol->g = mlcol->b = (char)(foam * 255);
|
|
/* This needs to be set (render engine uses) */
|
|
mlcol->a = 255;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* displace the geometry */
|
|
|
|
/* Note: tried to parallelized that one and previous foam loop, but gives 20% slower results... odd. */
|
|
{
|
|
const int num_verts = result->totvert;
|
|
|
|
for (i = 0; i < num_verts; i++) {
|
|
float *vco = mverts[i].co;
|
|
const float u = OCEAN_CO(size_co_inv, vco[0]);
|
|
const float v = OCEAN_CO(size_co_inv, vco[1]);
|
|
|
|
if (omd->oceancache && omd->cached == true) {
|
|
BKE_ocean_cache_eval_uv(omd->oceancache, &ocr, cfra_for_cache, u, v);
|
|
}
|
|
else {
|
|
BKE_ocean_eval_uv(omd->ocean, &ocr, u, v);
|
|
}
|
|
|
|
vco[2] += ocr.disp[1];
|
|
|
|
if (omd->chop_amount > 0.0f) {
|
|
vco[0] += ocr.disp[0];
|
|
vco[1] += ocr.disp[2];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (allocated_ocean) {
|
|
BKE_ocean_free(omd->ocean);
|
|
omd->ocean = NULL;
|
|
}
|
|
|
|
#undef OCEAN_CO
|
|
|
|
return result;
|
|
}
|
|
#else /* WITH_OCEANSIM */
|
|
static Mesh *doOcean(ModifierData *UNUSED(md), const ModifierEvalContext *UNUSED(ctx), Mesh *mesh)
|
|
{
|
|
return mesh;
|
|
}
|
|
#endif /* WITH_OCEANSIM */
|
|
|
|
static Mesh *applyModifier(
|
|
ModifierData *md, const ModifierEvalContext *ctx,
|
|
Mesh *mesh)
|
|
{
|
|
Mesh *result;
|
|
|
|
result = doOcean(md, ctx, mesh);
|
|
|
|
if (result != mesh)
|
|
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
ModifierTypeInfo modifierType_Ocean = {
|
|
/* name */ "Ocean",
|
|
/* structName */ "OceanModifierData",
|
|
/* structSize */ sizeof(OceanModifierData),
|
|
/* type */ eModifierTypeType_Constructive,
|
|
/* flags */ eModifierTypeFlag_AcceptsMesh |
|
|
eModifierTypeFlag_SupportsEditmode |
|
|
eModifierTypeFlag_EnableInEditmode,
|
|
|
|
/* copyData */ copyData,
|
|
/* deformMatrices_DM */ NULL,
|
|
|
|
/* deformMatrices */ NULL,
|
|
/* deformVertsEM */ NULL,
|
|
/* deformMatricesEM */ NULL,
|
|
/* applyModifier */ applyModifier,
|
|
|
|
/* initData */ initData,
|
|
/* requiredDataMask */ requiredDataMask,
|
|
/* freeData */ freeData,
|
|
/* isDisabled */ NULL,
|
|
/* updateDepsgraph */ NULL,
|
|
/* dependsOnTime */ NULL,
|
|
/* dependsOnNormals */ dependsOnNormals,
|
|
/* foreachObjectLink */ NULL,
|
|
/* foreachIDLink */ NULL,
|
|
/* foreachTexLink */ NULL,
|
|
/* freeRuntimeData */ NULL,
|
|
};
|