diff options
Diffstat (limited to 'source/blender/modifiers/intern/MOD_wave.cc')
-rw-r--r-- | source/blender/modifiers/intern/MOD_wave.cc | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/source/blender/modifiers/intern/MOD_wave.cc b/source/blender/modifiers/intern/MOD_wave.cc new file mode 100644 index 00000000000..44002b2d719 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_wave.cc @@ -0,0 +1,497 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2005 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup modifiers + */ + +#include "BLI_utildefines.h" + +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "DNA_defaults.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_editmesh.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_mesh.h" +#include "BKE_mesh_wrapper.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_texture.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "MEM_guardedalloc.h" + +#include "RE_texture.h" + +#include "MOD_modifiertypes.h" +#include "MOD_ui_common.h" +#include "MOD_util.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +static void initData(ModifierData *md) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wmd, modifier)); + + MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WaveModifierData), modifier); +} + +static bool dependsOnTime(Scene *UNUSED(scene), ModifierData *UNUSED(md)) +{ + return true; +} + +static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + + walk(userData, ob, (ID **)&wmd->texture, IDWALK_CB_USER); + walk(userData, ob, (ID **)&wmd->objectcenter, IDWALK_CB_NOP); + walk(userData, ob, (ID **)&wmd->map_object, IDWALK_CB_NOP); +} + +static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData) +{ + walk(userData, ob, md, "texture"); +} + +static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + bool need_transform_relation = false; + + if (wmd->objectcenter != nullptr) { + DEG_add_object_relation(ctx->node, wmd->objectcenter, DEG_OB_COMP_TRANSFORM, "Wave Modifier"); + need_transform_relation = true; + } + + if (wmd->texture != nullptr) { + DEG_add_generic_id_relation(ctx->node, &wmd->texture->id, "Wave Modifier"); + + if ((wmd->texmapping == MOD_DISP_MAP_OBJECT) && wmd->map_object != nullptr) { + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->map_object, wmd->map_bone, "Wave Modifier"); + need_transform_relation = true; + } + else if (wmd->texmapping == MOD_DISP_MAP_GLOBAL) { + need_transform_relation = true; + } + } + + if (need_transform_relation) { + DEG_add_depends_on_transform_relation(ctx->node, "Wave Modifier"); + } +} + +static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + + /* ask for UV coordinates if we need them */ + if (wmd->texture && wmd->texmapping == MOD_DISP_MAP_UV) { + r_cddata_masks->fmask |= CD_MASK_MTFACE; + } + + /* ask for vertexgroups if we need them */ + if (wmd->defgrp_name[0] != '\0') { + r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT; + } +} + +static bool dependsOnNormals(ModifierData *md) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + + return (wmd->flag & MOD_WAVE_NORM) != 0; +} + +static void waveModifier_do(WaveModifierData *md, + const ModifierEvalContext *ctx, + Object *ob, + Mesh *mesh, + float (*vertexCos)[3], + int verts_num) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + const MDeformVert *dvert; + int defgrp_index; + float ctime = DEG_get_ctime(ctx->depsgraph); + float minfac = (float)(1.0 / exp(wmd->width * wmd->narrow * wmd->width * wmd->narrow)); + float lifefac = wmd->height; + float(*tex_co)[3] = nullptr; + const int wmd_axis = wmd->flag & (MOD_WAVE_X | MOD_WAVE_Y); + const float falloff = wmd->falloff; + float falloff_fac = 1.0f; /* when falloff == 0.0f this stays at 1.0f */ + const bool invert_group = (wmd->flag & MOD_WAVE_INVERT_VGROUP) != 0; + + const float(*vert_normals)[3] = nullptr; + if ((wmd->flag & MOD_WAVE_NORM) && (mesh != nullptr)) { + vert_normals = BKE_mesh_vertex_normals_ensure(mesh); + } + + if (wmd->objectcenter != nullptr) { + float mat[4][4]; + /* get the control object's location in local coordinates */ + invert_m4_m4(ob->imat, ob->obmat); + mul_m4_m4m4(mat, ob->imat, wmd->objectcenter->obmat); + + wmd->startx = mat[3][0]; + wmd->starty = mat[3][1]; + } + + /* get the index of the deform group */ + MOD_get_vgroup(ob, mesh, wmd->defgrp_name, &dvert, &defgrp_index); + + if (wmd->damp == 0.0f) { + wmd->damp = 10.0f; + } + + if (wmd->lifetime != 0.0f) { + float x = ctime - wmd->timeoffs; + + if (x > wmd->lifetime) { + lifefac = x - wmd->lifetime; + + if (lifefac > wmd->damp) { + lifefac = 0.0; + } + else { + lifefac = (float)(wmd->height * (1.0f - sqrtf(lifefac / wmd->damp))); + } + } + } + + Tex *tex_target = wmd->texture; + if (mesh != nullptr && tex_target != nullptr) { + tex_co = static_cast<float(*)[3]>(MEM_malloc_arrayN(verts_num, sizeof(*tex_co), __func__)); + MOD_get_texture_coords((MappingInfoModifierData *)wmd, ctx, ob, mesh, vertexCos, tex_co); + + MOD_init_texture((MappingInfoModifierData *)wmd, ctx); + } + + if (lifefac != 0.0f) { + /* avoid divide by zero checks within the loop */ + float falloff_inv = falloff != 0.0f ? 1.0f / falloff : 1.0f; + int i; + + for (i = 0; i < verts_num; i++) { + float *co = vertexCos[i]; + float x = co[0] - wmd->startx; + float y = co[1] - wmd->starty; + float amplit = 0.0f; + float def_weight = 1.0f; + + /* get weights */ + if (dvert) { + def_weight = invert_group ? 1.0f - BKE_defvert_find_weight(&dvert[i], defgrp_index) : + BKE_defvert_find_weight(&dvert[i], defgrp_index); + + /* if this vert isn't in the vgroup, don't deform it */ + if (def_weight == 0.0f) { + continue; + } + } + + switch (wmd_axis) { + case MOD_WAVE_X | MOD_WAVE_Y: + amplit = sqrtf(x * x + y * y); + break; + case MOD_WAVE_X: + amplit = x; + break; + case MOD_WAVE_Y: + amplit = y; + break; + } + + /* this way it makes nice circles */ + amplit -= (ctime - wmd->timeoffs) * wmd->speed; + + if (wmd->flag & MOD_WAVE_CYCL) { + amplit = (float)fmodf(amplit - wmd->width, 2.0f * wmd->width) + wmd->width; + } + + if (falloff != 0.0f) { + float dist = 0.0f; + + switch (wmd_axis) { + case MOD_WAVE_X | MOD_WAVE_Y: + dist = sqrtf(x * x + y * y); + break; + case MOD_WAVE_X: + dist = fabsf(x); + break; + case MOD_WAVE_Y: + dist = fabsf(y); + break; + } + + falloff_fac = (1.0f - (dist * falloff_inv)); + CLAMP(falloff_fac, 0.0f, 1.0f); + } + + /* GAUSSIAN */ + if ((falloff_fac != 0.0f) && (amplit > -wmd->width) && (amplit < wmd->width)) { + amplit = amplit * wmd->narrow; + amplit = (float)(1.0f / expf(amplit * amplit) - minfac); + + /* Apply texture. */ + if (tex_co) { + Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); + TexResult texres; + BKE_texture_get_value(scene, tex_target, tex_co[i], &texres, false); + amplit *= texres.tin; + } + + /* Apply weight & falloff. */ + amplit *= def_weight * falloff_fac; + + if (vert_normals) { + /* move along normals */ + if (wmd->flag & MOD_WAVE_NORM_X) { + co[0] += (lifefac * amplit) * vert_normals[i][0]; + } + if (wmd->flag & MOD_WAVE_NORM_Y) { + co[1] += (lifefac * amplit) * vert_normals[i][1]; + } + if (wmd->flag & MOD_WAVE_NORM_Z) { + co[2] += (lifefac * amplit) * vert_normals[i][2]; + } + } + else { + /* move along local z axis */ + co[2] += lifefac * amplit; + } + } + } + } + + MEM_SAFE_FREE(tex_co); +} + +static void deformVerts(ModifierData *md, + const ModifierEvalContext *ctx, + Mesh *mesh, + float (*vertexCos)[3], + int verts_num) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + Mesh *mesh_src = nullptr; + + if (wmd->flag & MOD_WAVE_NORM) { + mesh_src = MOD_deform_mesh_eval_get(ctx->object, nullptr, mesh, vertexCos, verts_num, false); + } + else if (wmd->texture != nullptr || wmd->defgrp_name[0] != '\0') { + mesh_src = MOD_deform_mesh_eval_get(ctx->object, nullptr, mesh, nullptr, verts_num, false); + } + + waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num); + + if (!ELEM(mesh_src, nullptr, mesh)) { + BKE_id_free(nullptr, mesh_src); + } +} + +static void deformVertsEM(ModifierData *md, + const ModifierEvalContext *ctx, + BMEditMesh *editData, + Mesh *mesh, + float (*vertexCos)[3], + int verts_num) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + Mesh *mesh_src = nullptr; + + if (wmd->flag & MOD_WAVE_NORM) { + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, vertexCos, verts_num, false); + } + else if (wmd->texture != nullptr || wmd->defgrp_name[0] != '\0') { + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, nullptr, verts_num, false); + } + + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ + if (mesh_src != nullptr) { + BKE_mesh_wrapper_ensure_mdata(mesh_src); + } + + waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num); + + if (!ELEM(mesh_src, nullptr, mesh)) { + /* Important not to free `vertexCos` owned by the caller. */ + EditMeshData *edit_data = mesh_src->runtime.edit_data; + if (edit_data->vertexCos == vertexCos) { + edit_data->vertexCos = nullptr; + } + + BKE_id_free(nullptr, mesh_src); + } +} + +static void panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *sub, *row, *col; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetPropSep(layout, true); + + row = uiLayoutRowWithHeading(layout, true, IFACE_("Motion")); + uiItemR( + row, ptr, "use_x", UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE, nullptr, ICON_NONE); + uiItemR( + row, ptr, "use_y", UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "use_cyclic", 0, nullptr, ICON_NONE); + + row = uiLayoutRowWithHeading(layout, true, IFACE_("Along Normals")); + uiItemR(row, ptr, "use_normal", 0, "", ICON_NONE); + sub = uiLayoutRow(row, true); + uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_normal")); + uiItemR(sub, ptr, "use_normal_x", UI_ITEM_R_TOGGLE, "X", ICON_NONE); + uiItemR(sub, ptr, "use_normal_y", UI_ITEM_R_TOGGLE, "Y", ICON_NONE); + uiItemR(sub, ptr, "use_normal_z", UI_ITEM_R_TOGGLE, "Z", ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "falloff_radius", 0, IFACE_("Falloff"), ICON_NONE); + uiItemR(col, ptr, "height", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "width", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "narrowness", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", nullptr); + + modifier_panel_end(layout, ptr); +} + +static void position_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "start_position_object", 0, IFACE_("Object"), ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "start_position_x", 0, IFACE_("Start Position X"), ICON_NONE); + uiItemR(col, ptr, "start_position_y", 0, "Y", ICON_NONE); +} + +static void time_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + + uiLayoutSetPropSep(layout, true); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "time_offset", 0, IFACE_("Offset"), ICON_NONE); + uiItemR(col, ptr, "lifetime", 0, IFACE_("Life"), ICON_NONE); + uiItemR(col, ptr, "damping_time", 0, IFACE_("Damping"), ICON_NONE); + uiItemR(col, ptr, "speed", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +static void texture_panel_draw(const bContext *C, Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + int texture_coords = RNA_enum_get(ptr, "texture_coords"); + + uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr); + + uiLayoutSetPropSep(layout, true); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "texture_coords", 0, IFACE_("Coordinates"), ICON_NONE); + if (texture_coords == MOD_DISP_MAP_OBJECT) { + uiItemR(col, ptr, "texture_coords_object", 0, IFACE_("Object"), ICON_NONE); + PointerRNA texture_coords_obj_ptr = RNA_pointer_get(ptr, "texture_coords_object"); + if (!RNA_pointer_is_null(&texture_coords_obj_ptr) && + (RNA_enum_get(&texture_coords_obj_ptr, "type") == OB_ARMATURE)) { + PointerRNA texture_coords_obj_data_ptr = RNA_pointer_get(&texture_coords_obj_ptr, "data"); + uiItemPointerR(col, + ptr, + "texture_coords_bone", + &texture_coords_obj_data_ptr, + "bones", + IFACE_("Bone"), + ICON_NONE); + } + } + else if (texture_coords == MOD_DISP_MAP_UV && RNA_enum_get(&ob_ptr, "type") == OB_MESH) { + PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data"); + uiItemPointerR(col, ptr, "uv_layer", &obj_data_ptr, "uv_layers", nullptr, ICON_NONE); + } +} + +static void panelRegister(ARegionType *region_type) +{ + PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Wave, panel_draw); + modifier_subpanel_register( + region_type, "position", "Start Position", nullptr, position_panel_draw, panel_type); + modifier_subpanel_register(region_type, "time", "Time", nullptr, time_panel_draw, panel_type); + modifier_subpanel_register( + region_type, "texture", "Texture", nullptr, texture_panel_draw, panel_type); +} + +ModifierTypeInfo modifierType_Wave = { + /* name */ N_("Wave"), + /* structName */ "WaveModifierData", + /* structSize */ sizeof(WaveModifierData), + /* srna */ &RNA_WaveModifier, + /* type */ eModifierTypeType_OnlyDeform, + /* flags */ eModifierTypeFlag_AcceptsCVs | eModifierTypeFlag_AcceptsVertexCosOnly | + eModifierTypeFlag_SupportsEditmode, + /* icon */ ICON_MOD_WAVE, + + /* copyData */ BKE_modifier_copydata_generic, + + /* deformVerts */ deformVerts, + /* deformMatrices */ nullptr, + /* deformVertsEM */ deformVertsEM, + /* deformMatricesEM */ nullptr, + /* modifyMesh */ nullptr, + /* modifyGeometrySet */ nullptr, + + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ nullptr, + /* isDisabled */ nullptr, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ dependsOnTime, + /* dependsOnNormals */ dependsOnNormals, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ foreachTexLink, + /* freeRuntimeData */ nullptr, + /* panelRegister */ panelRegister, + /* blendWrite */ nullptr, + /* blendRead */ nullptr, +}; |