diff options
author | YimingWu <xp8110@outlook.com> | 2020-02-01 05:25:32 +0300 |
---|---|---|
committer | YimingWu <xp8110@outlook.com> | 2020-02-01 05:25:32 +0300 |
commit | b47883a990ee68e659a8a8b44729be9b8e0d002f (patch) | |
tree | 24a7733807992fc84445d30b63deaedfe1ab40a1 /source/blender/editors | |
parent | b5abbc40a07041af91dca5d0a4acd8e5f1518c91 (diff) | |
parent | d9ec25844b4ac3143775615469fe69b27105c108 (diff) |
Merge remote-tracking branch 'origin/master' into temp-lanpr-review
Diffstat (limited to 'source/blender/editors')
58 files changed, 2588 insertions, 1803 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index a3d8695d186..2ec1634fa38 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -5057,72 +5057,76 @@ void ANIM_channel_draw_widgets(const bContext *C, } /* Special for Grease Pencil Layer. */ else if (ale->type == ANIMTYPE_GPLAYER) { - /* Add some offset to make it more pleasing to the eye. */ - offset += SLIDER_WIDTH / 2.1f; - - char *gp_rna_path = NULL; - bGPDlayer *gpl = (bGPDlayer *)ale->data; - const short width = SLIDER_WIDTH / 5; - - /* Create the RNA pointers. */ - RNA_pointer_create(ale->id, &RNA_GPencilLayer, ale->data, &ptr); - RNA_id_pointer_create(ale->id, &id_ptr); - int icon; - - /* Layer opacity. */ - UI_block_emboss_set(block, UI_EMBOSS); - prop = RNA_struct_find_property(&ptr, "opacity"); - gp_rna_path = RNA_path_from_ID_to_property(&ptr, prop); - if (RNA_path_resolve_property(&id_ptr, gp_rna_path, &ptr, &prop)) { - uiDefAutoButR(block, - &ptr, - prop, - array_index, - "", - ICON_NONE, - offset, - ymid, - width * 3, - channel_height); - } - MEM_freeN(gp_rna_path); - - /* Mask Layer. */ - UI_block_emboss_set(block, UI_EMBOSS_NONE); - prop = RNA_struct_find_property(&ptr, "mask_layer"); - gp_rna_path = RNA_path_from_ID_to_property(&ptr, prop); - if (RNA_path_resolve_property(&id_ptr, gp_rna_path, &ptr, &prop)) { - icon = (gpl->flag & GP_LAYER_USE_MASK) ? ICON_MOD_MASK : ICON_LAYER_ACTIVE; - uiDefAutoButR(block, - &ptr, - prop, - array_index, - "", - icon, - offset + (width * 3), - ymid, - width, - channel_height); - } - MEM_freeN(gp_rna_path); - - /* Layer onion skinning switch. */ - prop = RNA_struct_find_property(&ptr, "use_onion_skinning"); - gp_rna_path = RNA_path_from_ID_to_property(&ptr, prop); - if (RNA_path_resolve_property(&id_ptr, gp_rna_path, &ptr, &prop)) { - icon = (gpl->onion_flag & GP_LAYER_ONIONSKIN) ? ICON_ONIONSKIN_ON : ICON_ONIONSKIN_OFF; - uiDefAutoButR(block, - &ptr, - prop, - array_index, - "", - icon, - offset + (width * 4), - ymid, - width, - channel_height); + bGPdata *gpd = (bGPdata *)ale->id; + if ((gpd != NULL) && ((gpd->flag & GP_DATA_ANNOTATIONS) == 0)) { + /* Add some offset to make it more pleasing to the eye. */ + offset += SLIDER_WIDTH / 2.1f; + + char *gp_rna_path = NULL; + bGPDlayer *gpl = (bGPDlayer *)ale->data; + const short width = SLIDER_WIDTH / 5; + + /* Create the RNA pointers. */ + RNA_pointer_create(ale->id, &RNA_GPencilLayer, ale->data, &ptr); + RNA_id_pointer_create(ale->id, &id_ptr); + int icon; + + /* Layer opacity. */ + UI_block_emboss_set(block, UI_EMBOSS); + prop = RNA_struct_find_property(&ptr, "opacity"); + gp_rna_path = RNA_path_from_ID_to_property(&ptr, prop); + if (RNA_path_resolve_property(&id_ptr, gp_rna_path, &ptr, &prop)) { + uiDefAutoButR(block, + &ptr, + prop, + array_index, + "", + ICON_NONE, + offset, + ymid, + width * 3, + channel_height); + } + MEM_freeN(gp_rna_path); + + /* Mask Layer. */ + UI_block_emboss_set(block, UI_EMBOSS_NONE); + prop = RNA_struct_find_property(&ptr, "mask_layer"); + gp_rna_path = RNA_path_from_ID_to_property(&ptr, prop); + if (RNA_path_resolve_property(&id_ptr, gp_rna_path, &ptr, &prop)) { + icon = (gpl->flag & GP_LAYER_USE_MASK) ? ICON_MOD_MASK : ICON_LAYER_ACTIVE; + uiDefAutoButR(block, + &ptr, + prop, + array_index, + "", + icon, + offset + (width * 3), + ymid, + width, + channel_height); + } + MEM_freeN(gp_rna_path); + + /* Layer onion skinning switch. */ + prop = RNA_struct_find_property(&ptr, "use_onion_skinning"); + gp_rna_path = RNA_path_from_ID_to_property(&ptr, prop); + if (RNA_path_resolve_property(&id_ptr, gp_rna_path, &ptr, &prop)) { + icon = (gpl->onion_flag & GP_LAYER_ONIONSKIN) ? ICON_ONIONSKIN_ON : + ICON_ONIONSKIN_OFF; + uiDefAutoButR(block, + &ptr, + prop, + array_index, + "", + icon, + offset + (width * 4), + ymid, + width, + channel_height); + } + MEM_freeN(gp_rna_path); } - MEM_freeN(gp_rna_path); } /* Only if RNA-Path found. */ diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 55c9b661074..d366978ba2b 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -463,7 +463,7 @@ static int pose_visual_transform_apply_exec(bContext *C, wmOperator *UNUSED(op)) Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) { - /* loop over all selected pchans + /* Loop over all selected pchan's. * * TODO, loop over children before parents if multiple bones * at once are to be predictable*/ diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index af2d92533c3..e7803fdaafb 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -908,7 +908,7 @@ static bool curve_is_animated(Curve *cu) static void fcurve_path_rename(AnimData *adt, const char *orig_rna_path, - char *rna_path, + const char *rna_path, ListBase *orig_curves, ListBase *curves) { @@ -922,11 +922,15 @@ static void fcurve_path_rename(AnimData *adt, nfcu = copy_fcurve(fcu); spath = nfcu->rna_path; nfcu->rna_path = BLI_sprintfN("%s%s", rna_path, suffix); + + /* copy_fcurve() sets nfcu->grp to NULL. To maintain the groups, we need to keep the pointer. + * As a result, the group's 'channels' pointers will be wrong, which is fixed by calling + * `action_groups_reconstruct(action)` later, after all fcurves have been renamed. */ + nfcu->grp = fcu->grp; BLI_addtail(curves, nfcu); if (fcu->grp) { action_groups_remove_channel(adt->action, fcu); - action_groups_add_channel(adt->action, fcu->grp, nfcu); } else if ((adt->action) && (&adt->action->curves == orig_curves)) { BLI_remlink(&adt->action->curves, fcu); @@ -1077,6 +1081,9 @@ static void curve_rename_fcurves(Curve *cu, ListBase *orig_curves) } *orig_curves = curves; + if (adt != NULL) { + BKE_action_groups_reconstruct(adt->action); + } } /* return 0 if animation data wasn't changed, 1 otherwise */ diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index fd8fe103a2d..4cc0c865093 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -385,9 +385,8 @@ set(ICON_NAMES mod_mask mod_cloth mod_explode - mod_fluidsim mod_multires - mod_smoke + mod_fluid mod_solidify mod_screw mod_vertex_weight diff --git a/source/blender/editors/gizmo_library/gizmo_types/arrow2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/arrow2d_gizmo.c index 24571f67fdb..1c8c46a2bad 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/arrow2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/arrow2d_gizmo.c @@ -52,6 +52,7 @@ static void arrow2d_draw_geom(wmGizmo *gz, const float matrix[4][4], const float color[4]) { + const int draw_style = RNA_enum_get(gz->ptr, "draw_style"); const float size = 0.11f; const float size_breadth = size / 2.0f; const float size_length = size * 1.7f; @@ -74,11 +75,21 @@ static void arrow2d_draw_geom(wmGizmo *gz, const float matrix[4][4], const float immVertex2f(pos, 0.0f, arrow_length); immEnd(); - immBegin(GPU_PRIM_TRIS, 3); - immVertex2f(pos, size_breadth, arrow_length); - immVertex2f(pos, -size_breadth, arrow_length); - immVertex2f(pos, 0.0f, arrow_length + size_length); - immEnd(); + if (draw_style == ED_GIZMO_ARROW_STYLE_BOX) { + immBegin(GPU_PRIM_TRI_FAN, 4); + immVertex2f(pos, -size / 2, arrow_length); + immVertex2f(pos, size / 2, arrow_length); + immVertex2f(pos, size / 2, arrow_length + size); + immVertex2f(pos, -size / 2, arrow_length + size); + immEnd(); + } + else { + immBegin(GPU_PRIM_TRIS, 3); + immVertex2f(pos, size_breadth, arrow_length); + immVertex2f(pos, -size_breadth, arrow_length); + immVertex2f(pos, 0.0f, arrow_length + size_length); + immEnd(); + } immUnbindProgram(); @@ -197,6 +208,11 @@ static void GIZMO_GT_arrow_2d(wmGizmoType *gzt) gzt->struct_size = sizeof(wmGizmo); /* rna */ + static EnumPropertyItem rna_enum_draw_style_items[] = { + {ED_GIZMO_ARROW_STYLE_NORMAL, "NORMAL", 0, "Normal", ""}, + {ED_GIZMO_ARROW_STYLE_BOX, "BOX", 0, "Box", ""}, + {0, NULL, 0, NULL, NULL}, + }; RNA_def_float(gzt->srna, "length", 1.0f, 0.0f, FLT_MAX, "Arrow Line Length", "", 0.0f, FLT_MAX); RNA_def_float_rotation(gzt->srna, "angle", @@ -208,6 +224,12 @@ static void GIZMO_GT_arrow_2d(wmGizmoType *gzt) "", DEG2RADF(-360.0f), DEG2RADF(360.0f)); + RNA_def_enum(gzt->srna, + "draw_style", + rna_enum_draw_style_items, + ED_GIZMO_ARROW_STYLE_NORMAL, + "Draw Style", + ""); } void ED_gizmotypes_arrow_2d(void) diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c index 7e6db43dbb3..ef3014eabc2 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c @@ -286,7 +286,7 @@ static int gizmo_button2d_test_select(bContext *C, wmGizmo *gz, const int mval[2 else { copy_v2_v2(point_local, (float[2]){UNPACK2(mval)}); sub_v2_v2(point_local, gz->matrix_basis[3]); - mul_v2_fl(point_local, 1.0f / (gz->scale_basis * UI_DPI_FAC)); + mul_v2_fl(point_local, 1.0f / gz->scale_final); } /* The 'gz->scale_final' is already applied when projecting. */ if (len_squared_v2(point_local) < 1.0f) { diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 67ffc6adc9f..1331cc92d96 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -1797,6 +1797,11 @@ static int gpencil_vertex_group_invert_exec(bContext *C, wmOperator *op) } CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + /* Verify the strokes has something to change. */ + if ((gps->totpoints == 0) || (gps->dvert == NULL)) { + continue; + } + for (int i = 0; i < gps->totpoints; i++) { dvert = &gps->dvert[i]; MDeformWeight *dw = defvert_find_index(dvert, def_nr); @@ -1864,7 +1869,8 @@ static int gpencil_vertex_group_smooth_exec(bContext *C, wmOperator *op) MDeformVert *dverta, *dvertb; CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { - if (gps->dvert == NULL) { + /* Verify the strokes has something to change. */ + if ((gps->totpoints == 0) || (gps->dvert == NULL)) { continue; } @@ -1959,6 +1965,11 @@ static int gpencil_vertex_group_normalize_exec(bContext *C, wmOperator *op) } CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + /* Verify the strokes has something to change. */ + if ((gps->totpoints == 0) || (gps->dvert == NULL)) { + continue; + } + /* look for max value */ float maxvalue = 0.0f; for (int i = 0; i < gps->totpoints; i++) { @@ -2027,10 +2038,11 @@ static int gpencil_vertex_group_normalize_all_exec(bContext *C, wmOperator *op) } CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { - /* verify the strokes has something to change */ - if (gps->totpoints == 0) { + /* Verify the strokes has something to change. */ + if ((gps->totpoints == 0) || (gps->dvert == NULL)) { continue; } + /* look for tot value */ float *tot_values = MEM_callocN(gps->totpoints * sizeof(float), __func__); diff --git a/source/blender/editors/include/ED_mball.h b/source/blender/editors/include/ED_mball.h index 5afb645d9e7..8ffae0f2b66 100644 --- a/source/blender/editors/include/ED_mball.h +++ b/source/blender/editors/include/ED_mball.h @@ -25,7 +25,6 @@ #define __ED_MBALL_H__ struct Base; -struct MetaBall; struct Object; struct UndoType; struct bContext; diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index a5f56689c95..d27eeb270ac 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -421,6 +421,16 @@ void ED_object_data_xform_by_mat4(struct XFormObjectData *xod, const float mat[4 void ED_object_data_xform_restore(struct XFormObjectData *xod); void ED_object_data_xform_tag_update(struct XFormObjectData *xod); +/* Container helper API. */ +struct XFormObjectData_Container; +struct XFormObjectData_Container *ED_object_data_xform_container_create(void); +void ED_object_data_xform_container_destroy(struct XFormObjectData_Container *xds); +void ED_object_data_xform_container_update_all(struct XFormObjectData_Container *xds, + struct Main *bmain, + struct Depsgraph *depsgraph); +void ED_object_data_xform_container_item_ensure(struct XFormObjectData_Container *xds, + struct Object *ob); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index 0771f39f905..29bac9df93a 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -27,8 +27,6 @@ /* ******************* Registration Function ********************** */ struct Object; -struct SnapObjectContext; -struct SnapObjectParams; struct bContext; struct wmKeyConfig; struct wmOperatorType; @@ -107,7 +105,6 @@ bool calculateTransformCenter(struct bContext *C, struct Object; struct Scene; -struct TransInfo; struct wmGizmoGroup; struct wmGizmoGroupType; @@ -168,10 +165,28 @@ void VIEW3D_GGT_xform_shear(struct wmGizmoGroupType *gzgt); /* *** transform_gizmo_extrude_3d.c *** */ void VIEW3D_GGT_xform_extrude(struct wmGizmoGroupType *gzgt); -bool ED_widgetgroup_gizmo2d_poll(const struct bContext *C, struct wmGizmoGroupType *gzgt); -void ED_widgetgroup_gizmo2d_setup(const struct bContext *C, struct wmGizmoGroup *gzgroup); -void ED_widgetgroup_gizmo2d_refresh(const struct bContext *C, struct wmGizmoGroup *gzgroup); -void ED_widgetgroup_gizmo2d_draw_prepare(const struct bContext *C, struct wmGizmoGroup *gzgroup); +/* Transform: Axis/Cage */ +bool ED_widgetgroup_gizmo2d_xform_poll(const struct bContext *C, struct wmGizmoGroupType *gzgt); +void ED_widgetgroup_gizmo2d_xform_setup(const struct bContext *C, struct wmGizmoGroup *gzgroup); +void ED_widgetgroup_gizmo2d_xform_setup_no_cage(const struct bContext *C, + struct wmGizmoGroup *gzgroup); +void ED_widgetgroup_gizmo2d_xform_refresh(const struct bContext *C, struct wmGizmoGroup *gzgroup); +void ED_widgetgroup_gizmo2d_xform_draw_prepare(const struct bContext *C, + struct wmGizmoGroup *gzgroup); + +/* Resize: Axis */ +bool ED_widgetgroup_gizmo2d_resize_poll(const struct bContext *C, struct wmGizmoGroupType *gzgt); +void ED_widgetgroup_gizmo2d_resize_setup(const struct bContext *C, struct wmGizmoGroup *gzgroup); +void ED_widgetgroup_gizmo2d_resize_refresh(const struct bContext *C, struct wmGizmoGroup *gzgroup); +void ED_widgetgroup_gizmo2d_resize_draw_prepare(const struct bContext *C, + struct wmGizmoGroup *gzgroup); + +/* Rotate: Axis */ +bool ED_widgetgroup_gizmo2d_rotate_poll(const struct bContext *C, struct wmGizmoGroupType *gzgt); +void ED_widgetgroup_gizmo2d_rotate_setup(const struct bContext *C, struct wmGizmoGroup *gzgroup); +void ED_widgetgroup_gizmo2d_rotate_refresh(const struct bContext *C, struct wmGizmoGroup *gzgroup); +void ED_widgetgroup_gizmo2d_rotate_draw_prepare(const struct bContext *C, + struct wmGizmoGroup *gzgroup); #define SNAP_INCREMENTAL_ANGLE DEG2RAD(5.0) diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 2bf50d3b4b8..44c734e264a 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -560,7 +560,7 @@ DEF_ICON_MODIFIER(MOD_CLOTH) DEF_ICON_MODIFIER(MOD_EXPLODE) DEF_ICON_MODIFIER(MOD_FLUIDSIM) DEF_ICON_MODIFIER(MOD_MULTIRES) -DEF_ICON_MODIFIER(MOD_SMOKE) +DEF_ICON_MODIFIER(MOD_FLUID) DEF_ICON_MODIFIER(MOD_SOLIDIFY) DEF_ICON_MODIFIER(MOD_SCREW) DEF_ICON_MODIFIER(MOD_VERTEX_WEIGHT) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 17247736d3b..71fa28640e0 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -1819,11 +1819,13 @@ static int modifier_can_delete(ModifierData *md) { /* fluid particle modifier can't be deleted here */ if (md->type == eModifierType_ParticleSystem) { - if (((ParticleSystemModifierData *)md)->psys->part->type == PART_FLUID) { + short particle_type = ((ParticleSystemModifierData *)md)->psys->part->type; + if (particle_type == PART_FLUID || particle_type == PART_FLUID_FLIP || + particle_type == PART_FLUID_FOAM || particle_type == PART_FLUID_SPRAY || + particle_type == PART_FLUID_BUBBLE || particle_type == PART_FLUID_TRACER) { return 0; } } - return 1; } @@ -1836,7 +1838,7 @@ static int modifier_is_simulation(ModifierData *md) eModifierType_Cloth, eModifierType_Collision, eModifierType_Fluidsim, - eModifierType_Smoke, + eModifierType_Fluid, eModifierType_Softbody, eModifierType_Surface, eModifierType_DynamicPaint)) { @@ -2069,7 +2071,7 @@ static uiLayout *draw_modifier(uiLayout *layout, eModifierType_Softbody, eModifierType_ParticleSystem, eModifierType_Cloth, - eModifierType_Smoke)) { + eModifierType_Fluid)) { uiItemO(row, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy"), ICON_NONE, diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index 5a35b251d0c..5afe348158f 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -26,6 +26,7 @@ set(INC ../../depsgraph ../../makesdna ../../makesrna + ../../usd ../../windowmanager ../../../../intern/guardedalloc ) @@ -39,11 +40,13 @@ set(SRC io_cache.c io_collada.c io_ops.c + io_usd.c io_alembic.h io_cache.h io_collada.h io_ops.h + io_usd.h ) set(LIB @@ -69,6 +72,13 @@ if(WITH_ALEMBIC) endif() endif() +if(WITH_USD) + list(APPEND LIB + bf_usd + ) + add_definitions(-DWITH_USD) +endif() + if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c index e04fe4a20c0..acb511a414d 100644 --- a/source/blender/editors/io/io_ops.c +++ b/source/blender/editors/io/io_ops.c @@ -33,6 +33,10 @@ # include "io_alembic.h" #endif +#ifdef WITH_USD +# include "io_usd.h" +#endif + #include "io_cache.h" void ED_operatortypes_io(void) @@ -46,6 +50,9 @@ void ED_operatortypes_io(void) WM_operatortype_append(WM_OT_alembic_import); WM_operatortype_append(WM_OT_alembic_export); #endif +#ifdef WITH_USD + WM_operatortype_append(WM_OT_usd_export); +#endif WM_operatortype_append(CACHEFILE_OT_open); WM_operatortype_append(CACHEFILE_OT_reload); diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c new file mode 100644 index 00000000000..2818b134509 --- /dev/null +++ b/source/blender/editors/io/io_usd.c @@ -0,0 +1,240 @@ +/* + * 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) 2019 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup editor/io + */ + +#ifdef WITH_USD +# include "DNA_space_types.h" + +# include "BKE_context.h" +# include "BKE_main.h" +# include "BKE_report.h" + +# include "BLI_path_util.h" +# include "BLI_string.h" +# include "BLI_utildefines.h" + +# include "MEM_guardedalloc.h" + +# include "RNA_access.h" +# include "RNA_define.h" + +# include "UI_interface.h" +# include "UI_resources.h" + +# include "WM_api.h" +# include "WM_types.h" + +# include "DEG_depsgraph.h" + +# include "io_usd.h" +# include "usd.h" + +const EnumPropertyItem rna_enum_usd_export_evaluation_mode_items[] = { + {DAG_EVAL_RENDER, + "RENDER", + 0, + "Render", + "Use Render settings for object visibility, modifier settings, etc"}, + {DAG_EVAL_VIEWPORT, + "VIEWPORT", + 0, + "Viewport", + "Use Viewport settings for object visibility, modifier settings, etc"}, + {0, NULL, 0, NULL, NULL}, +}; + +/* Stored in the wmOperator's customdata field to indicate it should run as a background job. + * This is set when the operator is invoked, and not set when it is only executed. */ +enum { AS_BACKGROUND_JOB = 1 }; +typedef struct eUSDOperatorOptions { + bool as_background_job; +} eUSDOperatorOptions; + +static int wm_usd_export_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + eUSDOperatorOptions *options = MEM_callocN(sizeof(eUSDOperatorOptions), "eUSDOperatorOptions"); + options->as_background_job = true; + op->customdata = options; + + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + Main *bmain = CTX_data_main(C); + char filepath[FILE_MAX]; + const char *main_blendfile_path = BKE_main_blendfile_path(bmain); + + if (main_blendfile_path[0] == '\0') { + BLI_strncpy(filepath, "untitled", sizeof(filepath)); + } + else { + BLI_strncpy(filepath, main_blendfile_path, sizeof(filepath)); + } + + BLI_path_extension_replace(filepath, sizeof(filepath), ".usdc"); + RNA_string_set(op->ptr, "filepath", filepath); + } + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int wm_usd_export_exec(bContext *C, wmOperator *op) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + BKE_report(op->reports, RPT_ERROR, "No filename given"); + return OPERATOR_CANCELLED; + } + + char filename[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filename); + + eUSDOperatorOptions *options = (eUSDOperatorOptions *)op->customdata; + const bool as_background_job = (options != NULL && options->as_background_job); + MEM_SAFE_FREE(op->customdata); + + const bool selected_objects_only = RNA_boolean_get(op->ptr, "selected_objects_only"); + const bool visible_objects_only = RNA_boolean_get(op->ptr, "visible_objects_only"); + const bool export_animation = RNA_boolean_get(op->ptr, "export_animation"); + const bool export_hair = RNA_boolean_get(op->ptr, "export_hair"); + const bool export_uvmaps = RNA_boolean_get(op->ptr, "export_uvmaps"); + const bool export_normals = RNA_boolean_get(op->ptr, "export_normals"); + const bool export_materials = RNA_boolean_get(op->ptr, "export_materials"); + const bool use_instancing = RNA_boolean_get(op->ptr, "use_instancing"); + const bool evaluation_mode = RNA_enum_get(op->ptr, "evaluation_mode"); + + struct USDExportParams params = { + export_animation, + export_hair, + export_uvmaps, + export_normals, + export_materials, + selected_objects_only, + visible_objects_only, + use_instancing, + evaluation_mode, + }; + + bool ok = USD_export(C, filename, ¶ms, as_background_job); + + return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + +static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayout *col; + struct PointerRNA *ptr = op->ptr; + + uiLayoutSetPropSep(layout, true); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "selected_objects_only", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "visible_objects_only", 0, NULL, ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "export_animation", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "export_hair", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "export_uvmaps", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "export_normals", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "export_materials", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_instancing", 0, NULL, ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "evaluation_mode", 0, NULL, ICON_NONE); +} + +void WM_OT_usd_export(struct wmOperatorType *ot) +{ + ot->name = "Export USD"; + ot->description = "Export current scene in a USD archive"; + ot->idname = "WM_OT_usd_export"; + + ot->invoke = wm_usd_export_invoke; + ot->exec = wm_usd_export_exec; + ot->poll = WM_operator_winactive; + ot->ui = wm_usd_export_draw; + + WM_operator_properties_filesel(ot, + FILE_TYPE_FOLDER | FILE_TYPE_USD, + FILE_BLENDER, + FILE_SAVE, + WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, + FILE_DEFAULTDISPLAY, + FILE_SORT_ALPHA); + + RNA_def_boolean(ot->srna, + "selected_objects_only", + false, + "Only Export Selected Objects", + "Only selected objects are exported. Unselected parents of selected objects are " + "exported as empty transform"); + + RNA_def_boolean(ot->srna, + "visible_objects_only", + true, + "Only Export Visible Objects", + "Only visible objects are exported. Invisible parents of visible objects are " + "exported as empty transform"); + + RNA_def_boolean(ot->srna, + "export_animation", + false, + "Export Animation", + "When checked, the render frame range is exported. When false, only the current " + "frame is exported"); + RNA_def_boolean(ot->srna, + "export_hair", + false, + "Export Hair", + "When checked, hair is exported as USD curves"); + RNA_def_boolean(ot->srna, + "export_uvmaps", + true, + "Export UV Maps", + "When checked, all UV maps of exported meshes are included in the export"); + RNA_def_boolean(ot->srna, + "export_normals", + true, + "Export Normals", + "When checked, normals of exported meshes are included in the export"); + RNA_def_boolean(ot->srna, + "export_materials", + true, + "Export Materials", + "When checked, the viewport settings of materials are exported as USD preview " + "materials, and material assignments are exported as geometry subsets"); + + RNA_def_boolean(ot->srna, + "use_instancing", + false, + "Use Instancing (EXPERIMENTAL)", + "When true, dupli-objects are written as instances of the original in USD. " + "Experimental feature, not working perfectly"); + + RNA_def_enum(ot->srna, + "evaluation_mode", + rna_enum_usd_export_evaluation_mode_items, + DAG_EVAL_RENDER, + "Evaluation Mode", + "Determines visibility of objects and modifier settings"); +} + +#endif /* WITH_USD */ diff --git a/source/blender/editors/io/io_usd.h b/source/blender/editors/io/io_usd.h new file mode 100644 index 00000000000..4738e1c348d --- /dev/null +++ b/source/blender/editors/io/io_usd.h @@ -0,0 +1,31 @@ +/* + * 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) 2019 Blender Foundation. + * All rights reserved. + */ + +#ifndef __IO_USD_H__ +#define __IO_USD_H__ + +/** \file + * \ingroup editor/io + */ + +struct wmOperatorType; + +void WM_OT_usd_export(struct wmOperatorType *ot); + +#endif /* __IO_USD_H__ */ diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c index 51d8fb8fd72..29b0cb88935 100644 --- a/source/blender/editors/object/object_data_transform.c +++ b/source/blender/editors/object/object_data_transform.c @@ -49,10 +49,12 @@ #include "BKE_mesh.h" #include "BKE_armature.h" #include "BKE_lattice.h" +#include "BKE_scene.h" #include "bmesh.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "WM_types.h" @@ -575,3 +577,100 @@ void ED_object_data_xform_tag_update(struct XFormObjectData *xod_base) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Data Transform Container + * + * Use to implement 'Affect Only Origins' feature. + * + * \{ */ + +struct XFormObjectData_Container { + GHash *obdata_in_obmode_map; +}; + +struct XFormObjectData_Extra { + Object *ob; + float obmat_orig[4][4]; + struct XFormObjectData *xod; +}; + +void ED_object_data_xform_container_item_ensure(struct XFormObjectData_Container *xds, Object *ob) +{ + if (xds->obdata_in_obmode_map == NULL) { + xds->obdata_in_obmode_map = BLI_ghash_ptr_new(__func__); + } + + void **xf_p; + if (!BLI_ghash_ensure_p(xds->obdata_in_obmode_map, ob->data, &xf_p)) { + struct XFormObjectData_Extra *xf = MEM_mallocN(sizeof(*xf), __func__); + copy_m4_m4(xf->obmat_orig, ob->obmat); + xf->ob = ob; + /* Result may be NULL, that's OK. */ + xf->xod = ED_object_data_xform_create(ob->data); + *xf_p = xf; + } +} + +/** + * This may be called multiple times with the same data. + * Each time, the original transformations are re-applied, instead of accumulating the changes. + */ +void ED_object_data_xform_container_update_all(struct XFormObjectData_Container *xds, + struct Main *bmain, + Depsgraph *depsgraph) +{ + if (xds->obdata_in_obmode_map == NULL) { + return; + } + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + + GHashIterator gh_iter; + GHASH_ITER (gh_iter, xds->obdata_in_obmode_map) { + ID *id = BLI_ghashIterator_getKey(&gh_iter); + struct XFormObjectData_Extra *xf = BLI_ghashIterator_getValue(&gh_iter); + if (xf->xod == NULL) { + continue; + } + + Object *ob_eval = DEG_get_evaluated_object(depsgraph, xf->ob); + float imat[4][4], dmat[4][4]; + invert_m4_m4(imat, xf->obmat_orig); + mul_m4_m4m4(dmat, imat, ob_eval->obmat); + invert_m4(dmat); + + ED_object_data_xform_by_mat4(xf->xod, dmat); + if (xf->ob->type == OB_ARMATURE) { + /* TODO: none of the current flags properly update armatures, needs investigation. */ + DEG_id_tag_update(id, 0); + } + else { + DEG_id_tag_update(id, ID_RECALC_GEOMETRY); + } + } +} + +/** Callback for #GHash free. */ +static void trans_obdata_in_obmode_free_elem(void *xf_p) +{ + struct XFormObjectData_Extra *xf = xf_p; + if (xf->xod) { + ED_object_data_xform_destroy(xf->xod); + } + MEM_freeN(xf); +} + +struct XFormObjectData_Container *ED_object_data_xform_container_create(void) +{ + struct XFormObjectData_Container *xds = MEM_callocN(sizeof(*xds), __func__); + xds->obdata_in_obmode_map = BLI_ghash_ptr_new(__func__); + return xds; +} + +void ED_object_data_xform_container_destroy(struct XFormObjectData_Container *xds) +{ + BLI_ghash_free(xds->obdata_in_obmode_map, NULL, trans_obdata_in_obmode_free_elem); + MEM_freeN(xds); +} + +/** \} */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 41205bc8778..352ba744d92 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -129,7 +129,7 @@ void OBJECT_OT_hook_assign(struct wmOperatorType *ot); void OBJECT_OT_hook_reset(struct wmOperatorType *ot); void OBJECT_OT_hook_recenter(struct wmOperatorType *ot); -/* object_group.c */ +/* object_collection.c */ void COLLECTION_OT_create(struct wmOperatorType *ot); void COLLECTION_OT_objects_remove_all(struct wmOperatorType *ot); void COLLECTION_OT_objects_remove(struct wmOperatorType *ot); @@ -172,7 +172,7 @@ void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot); void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot); void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot); -/* grease pencil modifiers */ +/* object_gpencil_modifiers.c */ void OBJECT_OT_gpencil_modifier_add(struct wmOperatorType *ot); void OBJECT_OT_gpencil_modifier_remove(struct wmOperatorType *ot); void OBJECT_OT_gpencil_modifier_move_up(struct wmOperatorType *ot); @@ -180,7 +180,7 @@ void OBJECT_OT_gpencil_modifier_move_down(struct wmOperatorType *ot); void OBJECT_OT_gpencil_modifier_apply(struct wmOperatorType *ot); void OBJECT_OT_gpencil_modifier_copy(struct wmOperatorType *ot); -/* shader fx */ +/* object_shader_fx.c */ void OBJECT_OT_shaderfx_add(struct wmOperatorType *ot); void OBJECT_OT_shaderfx_remove(struct wmOperatorType *ot); void OBJECT_OT_shaderfx_move_up(struct wmOperatorType *ot); @@ -265,7 +265,7 @@ void OBJECT_OT_shape_key_retime(struct wmOperatorType *ot); void OBJECT_OT_shape_key_mirror(struct wmOperatorType *ot); void OBJECT_OT_shape_key_move(struct wmOperatorType *ot); -/* object_group.c */ +/* object_collection.c */ void OBJECT_OT_collection_add(struct wmOperatorType *ot); void OBJECT_OT_collection_link(struct wmOperatorType *ot); void OBJECT_OT_collection_remove(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index 826d1db2538..45b9c6306ac 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -72,6 +72,7 @@ #include "ED_screen.h" #include "ED_view3d.h" #include "ED_gpencil.h" +#include "ED_object.h" #include "MEM_guardedalloc.h" @@ -292,6 +293,8 @@ static int object_clear_transform_generic_exec(bContext *C, void (*clear_func)(Object *, const bool), const char default_ksName[]) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); KeyingSet *ks; const bool clear_delta = RNA_boolean_get(op->ptr, "clear_delta"); @@ -304,6 +307,16 @@ static int object_clear_transform_generic_exec(bContext *C, return OPERATOR_CANCELLED; } + /* Support transforming the object data. */ + const bool use_transform_data_origin = (scene->toolsettings->transform_flag & + SCE_XFORM_DATA_ORIGIN); + struct XFormObjectData_Container *xds = NULL; + + if (use_transform_data_origin) { + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + xds = ED_object_data_xform_container_create(); + } + /* get KeyingSet to use */ ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName); @@ -311,18 +324,29 @@ static int object_clear_transform_generic_exec(bContext *C, * (so that object-transform clearing won't be applied at same time as bone-clearing) */ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - if (!(ob->mode & OB_MODE_WEIGHT_PAINT)) { - /* run provided clearing function */ - clear_func(ob, clear_delta); - - ED_autokeyframe_object(C, scene, ob, ks); + if (ob->mode & OB_MODE_WEIGHT_PAINT) { + continue; + } - /* tag for updates */ - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + if (use_transform_data_origin) { + ED_object_data_xform_container_item_ensure(xds, ob); } + + /* run provided clearing function */ + clear_func(ob, clear_delta); + + ED_autokeyframe_object(C, scene, ob, ks); + + /* tag for updates */ + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); } CTX_DATA_END; + if (use_transform_data_origin) { + ED_object_data_xform_container_update_all(xds, bmain, depsgraph); + ED_object_data_xform_container_destroy(xds); + } + /* this is needed so children are also updated */ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); diff --git a/source/blender/editors/physics/CMakeLists.txt b/source/blender/editors/physics/CMakeLists.txt index 021c17a94c2..0998280c381 100644 --- a/source/blender/editors/physics/CMakeLists.txt +++ b/source/blender/editors/physics/CMakeLists.txt @@ -27,6 +27,7 @@ set(INC ../../windowmanager ../../../../intern/glew-mx ../../../../intern/guardedalloc + ../../../../intern/mantaflow/extern ) set(INC_SYS @@ -56,19 +57,10 @@ set(LIB ) if(WITH_MOD_FLUID) - list(APPEND INC - ../../../../intern/elbeem/extern - ) - list(APPEND LIB - bf_intern_elbeem - ) - add_definitions(-DWITH_MOD_FLUID) -endif() - -if(WITH_MOD_SMOKE) list(APPEND LIB - bf_intern_smoke + bf_intern_mantaflow ) + add_definitions(-DWITH_FLUID) endif() if(WITH_INTERNATIONAL) diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index f16a372cb3c..9d3388bd220 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -5324,6 +5324,7 @@ static int clear_edited_exec(bContext *C, wmOperator *UNUSED(op)) psys_reset(psys, PSYS_RESET_DEPSGRAPH); WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob); + BKE_particle_batch_cache_dirty_tag(psys, BKE_PARTICLE_BATCH_DIRTY_ALL); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } } diff --git a/source/blender/editors/physics/particle_edit_undo.c b/source/blender/editors/physics/particle_edit_undo.c index aee79523c87..e85b025e28e 100644 --- a/source/blender/editors/physics/particle_edit_undo.c +++ b/source/blender/editors/physics/particle_edit_undo.c @@ -262,6 +262,11 @@ static void particle_undosys_step_decode(struct bContext *C, PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); if (edit) { undoptcache_to_editcache(&us->data, edit); + ParticleEditSettings *pset = &scene->toolsettings->particle; + if ((pset->flag & PE_DRAW_PART) != 0) { + psys_free_path_cache(NULL, edit); + BKE_particle_batch_cache_dirty_tag(edit->psys, BKE_PARTICLE_BATCH_DIRTY_ALL); + } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } else { diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index cfb3a400f47..4df74434c6a 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -1065,7 +1065,7 @@ static void remove_particle_systems_from_object(Object *ob_to) if (ELEM(md->type, eModifierType_ParticleSystem, eModifierType_DynamicPaint, - eModifierType_Smoke)) { + eModifierType_Fluid)) { BLI_remlink(&ob_to->modifiers, md); modifier_free(md); } diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c index 44858e36fab..5414c2a44a2 100644 --- a/source/blender/editors/physics/physics_fluid.c +++ b/source/blender/editors/physics/physics_fluid.c @@ -18,7 +18,7 @@ */ /** \file - * \ingroup edphys + * \ingroup edphys */ #include <math.h> @@ -31,1219 +31,808 @@ /* types */ #include "DNA_action_types.h" #include "DNA_object_types.h" -#include "DNA_object_fluidsim_types.h" +#include "BLI_blenlib.h" +#include "BLI_path_util.h" +#include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "BKE_context.h" #include "BKE_customdata.h" -#include "BKE_fluidsim.h" +#include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_report.h" #include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_fluid.h" +#include "BKE_global.h" #include "DEG_depsgraph.h" #include "ED_screen.h" -#include "ED_object.h" +#include "PIL_time.h" #include "WM_types.h" #include "WM_api.h" #include "physics_intern.h" // own include +#include "manta_fluid_API.h" + +#include "DNA_scene_types.h" +#include "DNA_fluid_types.h" +#include "DNA_mesh_types.h" + +#define FLUID_JOB_BAKE_ALL "FLUID_OT_bake_all" +#define FLUID_JOB_BAKE_DATA "FLUID_OT_bake_data" +#define FLUID_JOB_BAKE_NOISE "FLUID_OT_bake_noise" +#define FLUID_JOB_BAKE_MESH "FLUID_OT_bake_mesh" +#define FLUID_JOB_BAKE_PARTICLES "FLUID_OT_bake_particles" +#define FLUID_JOB_BAKE_GUIDES "FLUID_OT_bake_guides" +#define FLUID_JOB_FREE_ALL "FLUID_OT_free_all" +#define FLUID_JOB_FREE_DATA "FLUID_OT_free_data" +#define FLUID_JOB_FREE_NOISE "FLUID_OT_free_noise" +#define FLUID_JOB_FREE_MESH "FLUID_OT_free_mesh" +#define FLUID_JOB_FREE_PARTICLES "FLUID_OT_free_particles" +#define FLUID_JOB_FREE_GUIDES "FLUID_OT_free_guides" +#define FLUID_JOB_BAKE_PAUSE "FLUID_OT_pause_bake" + +typedef struct FluidJob { + /* from wmJob */ + void *owner; + short *stop, *do_update; + float *progress; + const char *type; + const char *name; -/* enable/disable overall compilation */ -#ifdef WITH_MOD_FLUID - -# include "LBM_fluidsim.h" - -# include "BLI_blenlib.h" -# include "BLI_path_util.h" -# include "BLI_math.h" + struct Main *bmain; + Scene *scene; + Depsgraph *depsgraph; + Object *ob; -# include "BKE_global.h" -# include "BKE_main.h" + FluidModifierData *mmd; -# include "WM_api.h" + int success; + double start; -# include "DNA_scene_types.h" -# include "DNA_mesh_types.h" + int *pause_frame; +} FluidJob; -static float get_fluid_viscosity(FluidsimSettings *settings) +static inline bool fluid_is_bake_all(FluidJob *job) { - return (1.0f / powf(10.0f, settings->viscosityExponent)) * settings->viscosityValue; + return (STREQ(job->type, FLUID_JOB_BAKE_ALL)); } - -static float get_fluid_rate(FluidsimSettings *settings) +static inline bool fluid_is_bake_data(FluidJob *job) { - float rate = 1.0f; /* default rate if not animated... */ - - rate = settings->animRate; - - if (rate < 0.0f) { - rate = 0.0f; - } - - return rate; + return (STREQ(job->type, FLUID_JOB_BAKE_DATA)); } - -static void get_fluid_gravity(float *gravity, Scene *scene, FluidsimSettings *fss) +static inline bool fluid_is_bake_noise(FluidJob *job) { - if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { - copy_v3_v3(gravity, scene->physics_settings.gravity); - } - else { - copy_v3_v3(gravity, fss->grav); - } + return (STREQ(job->type, FLUID_JOB_BAKE_NOISE)); } - -static float get_fluid_size_m(Scene *scene, Object *domainob, FluidsimSettings *fss) +static inline bool fluid_is_bake_mesh(FluidJob *job) { - if (!scene->unit.system) { - return fss->realsize; - } - else { - float dim[3]; - float longest_axis; - - BKE_object_dimensions_get(domainob, dim); - longest_axis = max_fff(dim[0], dim[1], dim[2]); - - return longest_axis * scene->unit.scale_length; - } + return (STREQ(job->type, FLUID_JOB_BAKE_MESH)); } - -static bool fluid_is_animated_mesh(FluidsimSettings *fss) +static inline bool fluid_is_bake_particle(FluidJob *job) { - return ((fss->type == OB_FLUIDSIM_CONTROL) || fss->domainNovecgen); + return (STREQ(job->type, FLUID_JOB_BAKE_PARTICLES)); } - -/* ********************** fluid sim settings struct functions ********************** */ - -# if 0 -/* helper function */ -void fluidsimGetGeometryObjFilename(Object *ob, char *dst) //, char *srcname) +static inline bool fluid_is_bake_guiding(FluidJob *job) { - //BLI_snprintf(dst, FILE_MAXFILE, "%s_cfgdata_%s.bobj.gz", srcname, ob->id.name); - BLI_snprintf(dst, FILE_MAXFILE, "fluidcfgdata_%s.bobj.gz", ob->id.name); + return (STREQ(job->type, FLUID_JOB_BAKE_GUIDES)); } -# endif - -/* ********************** fluid sim channel helper functions ********************** */ - -typedef struct FluidAnimChannels { - int length; - - double aniFrameTime; - - float *timeAtFrame; - float *DomainTime; - float *DomainGravity; - float *DomainViscosity; -} FluidAnimChannels; - -typedef struct FluidObject { - struct FluidObject *next, *prev; - - struct Object *object; - - float *Translation; - float *Rotation; - float *Scale; - float *Active; - - float *InitialVelocity; - - float *AttractforceStrength; - float *AttractforceRadius; - float *VelocityforceStrength; - float *VelocityforceRadius; - - float *VertexCache; - int numVerts, numTris; -} FluidObject; - -// no. of entries for the two channel sizes -# define CHANNEL_FLOAT 1 -# define CHANNEL_VEC 3 - -// simplify channels before printing -// for API this is done anyway upon init -# if 0 -static void fluidsimPrintChannel(FILE *file, float *channel, int paramsize, char *str, int entries) +static inline bool fluid_is_free_all(FluidJob *job) { - int i, j; - int channelSize = paramsize; - - if (entries == 3) { - elbeemSimplifyChannelVec3(channel, &channelSize); - } - else if (entries == 1) { - elbeemSimplifyChannelFloat(channel, &channelSize); - } - else { - /* invalid, cant happen? */ - } - - fprintf(file, " CHANNEL %s =\n", str); - for (i = 0; i < channelSize; i++) { - fprintf(file, " "); - for (j = 0; j <= entries; j++) { // also print time value - fprintf(file, " %f ", channel[i * (entries + 1) + j]); - if (j == entries - 1) { - fprintf(file, " "); - } - } - fprintf(file, "\n"); - } - - fprintf(file, " ;\n"); + return (STREQ(job->type, FLUID_JOB_FREE_ALL)); } -# endif - -/* Note: fluid anim channel data layout - * ------------------------------------ - * CHANNEL_FLOAT: - * frame 1 |frame 2 - * [dataF][time][dataF][time] - * - * CHANNEL_VEC: - * frame 1 |frame 2 - * [dataX][dataY][dataZ][time][dataX][dataY][dataZ][time] - */ - -static void init_time(FluidsimSettings *domainSettings, FluidAnimChannels *channels) +static inline bool fluid_is_free_data(FluidJob *job) { - int i; - - channels->timeAtFrame = MEM_callocN((channels->length + 1) * sizeof(float), - "timeAtFrame channel"); - - channels->timeAtFrame[0] = channels->timeAtFrame[1] = - domainSettings->animStart; // start at index 1 - - for (i = 2; i <= channels->length; i++) { - channels->timeAtFrame[i] = channels->timeAtFrame[i - 1] + (float)channels->aniFrameTime; - } + return (STREQ(job->type, FLUID_JOB_FREE_DATA)); } - -/* if this is slow, can replace with faster, less readable code */ -static void set_channel(float *channel, float time, float *value, int i, int size) +static inline bool fluid_is_free_noise(FluidJob *job) { - if (size == CHANNEL_FLOAT) { - channel[(i * 2) + 0] = value[0]; - channel[(i * 2) + 1] = time; - } - else if (size == CHANNEL_VEC) { - channel[(i * 4) + 0] = value[0]; - channel[(i * 4) + 1] = value[1]; - channel[(i * 4) + 2] = value[2]; - channel[(i * 4) + 3] = time; - } + return (STREQ(job->type, FLUID_JOB_FREE_NOISE)); } - -static void set_vertex_channel(Depsgraph *depsgraph, - float *channel, - float time, - struct Scene *scene, - struct FluidObject *fobj, - int i) +static inline bool fluid_is_free_mesh(FluidJob *job) { - Object *ob = fobj->object; - FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType( - ob, eModifierType_Fluidsim); - float *verts; - int *tris = NULL, numVerts = 0, numTris = 0; - int modifierIndex = BLI_findindex(&ob->modifiers, fluidmd); - int framesize = (3 * fobj->numVerts) + 1; - int j; - - if (channel == NULL) { - return; - } + return (STREQ(job->type, FLUID_JOB_FREE_MESH)); +} +static inline bool fluid_is_free_particles(FluidJob *job) +{ + return (STREQ(job->type, FLUID_JOB_FREE_PARTICLES)); +} +static inline bool fluid_is_free_guiding(FluidJob *job) +{ + return (STREQ(job->type, FLUID_JOB_FREE_GUIDES)); +} - initElbeemMesh(depsgraph, scene, ob, &numVerts, &verts, &numTris, &tris, 1, modifierIndex); +static bool fluid_initjob( + bContext *C, FluidJob *job, wmOperator *op, char *error_msg, int error_size) +{ + FluidModifierData *mmd = NULL; + FluidDomainSettings *mds; + Object *ob = CTX_data_active_object(C); - /* don't allow mesh to change number of verts in anim sequence */ - if (numVerts != fobj->numVerts) { - MEM_freeN(channel); - channel = NULL; - return; + mmd = (FluidModifierData *)modifiers_findByType(ob, eModifierType_Fluid); + if (!mmd) { + BLI_strncpy(error_msg, N_("Bake failed: no Fluid modifier found"), error_size); + return false; } - - /* fill frame of channel with vertex locations */ - for (j = 0; j < (3 * numVerts); j++) { - channel[i * framesize + j] = verts[j]; + mds = mmd->domain; + if (!mds) { + BLI_strncpy(error_msg, N_("Bake failed: invalid domain"), error_size); + return false; } - channel[i * framesize + framesize - 1] = time; - MEM_freeN(verts); - MEM_freeN(tris); -} + job->bmain = CTX_data_main(C); + job->scene = CTX_data_scene(C); + job->depsgraph = CTX_data_depsgraph_pointer(C); + job->ob = CTX_data_active_object(C); + job->mmd = mmd; + job->type = op->type->idname; + job->name = op->type->name; -static void free_domain_channels(FluidAnimChannels *channels) -{ - if (!channels->timeAtFrame) { - return; - } - MEM_freeN(channels->timeAtFrame); - channels->timeAtFrame = NULL; - MEM_freeN(channels->DomainGravity); - channels->DomainGravity = NULL; - MEM_freeN(channels->DomainViscosity); - channels->DomainViscosity = NULL; - MEM_freeN(channels->DomainTime); - channels->DomainTime = NULL; + return true; } -static void free_all_fluidobject_channels(ListBase *fobjects) +static bool fluid_initpaths(FluidJob *job, ReportList *reports) { - FluidObject *fobj; - - for (fobj = fobjects->first; fobj; fobj = fobj->next) { - if (fobj->Translation) { - MEM_freeN(fobj->Translation); - fobj->Translation = NULL; - MEM_freeN(fobj->Rotation); - fobj->Rotation = NULL; - MEM_freeN(fobj->Scale); - fobj->Scale = NULL; - MEM_freeN(fobj->Active); - fobj->Active = NULL; - MEM_freeN(fobj->InitialVelocity); - fobj->InitialVelocity = NULL; - } - - if (fobj->AttractforceStrength) { - MEM_freeN(fobj->AttractforceStrength); - fobj->AttractforceStrength = NULL; - MEM_freeN(fobj->AttractforceRadius); - fobj->AttractforceRadius = NULL; - MEM_freeN(fobj->VelocityforceStrength); - fobj->VelocityforceStrength = NULL; - MEM_freeN(fobj->VelocityforceRadius); - fobj->VelocityforceRadius = NULL; - } + FluidDomainSettings *mds = job->mmd->domain; + char temp_dir[FILE_MAX]; + temp_dir[0] = '\0'; - if (fobj->VertexCache) { - MEM_freeN(fobj->VertexCache); - fobj->VertexCache = NULL; - } - } -} + const char *relbase = modifier_path_relbase(job->bmain, job->ob); -static void fluid_init_all_channels(bContext *C, - Depsgraph *depsgraph, - Object *UNUSED(fsDomain), - FluidsimSettings *domainSettings, - FluidAnimChannels *channels, - ListBase *fobjects) -{ - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Base *base; - int i; - int length = channels->length; - float eval_time; - - /* init time values (assuming that time moves at a constant speed; may be overridden later) */ - init_time(domainSettings, channels); - - /* allocate domain animation channels */ - channels->DomainGravity = MEM_callocN(length * (CHANNEL_VEC + 1) * sizeof(float), - "channel DomainGravity"); - channels->DomainViscosity = MEM_callocN(length * (CHANNEL_FLOAT + 1) * sizeof(float), - "channel DomainViscosity"); - channels->DomainTime = MEM_callocN(length * (CHANNEL_FLOAT + 1) * sizeof(float), - "channel DomainTime"); - - /* allocate fluid objects */ - for (base = FIRSTBASE(view_layer); base; base = base->next) { - Object *ob = base->object; - FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType( - ob, eModifierType_Fluidsim); - - if (fluidmd) { - FluidObject *fobj = MEM_callocN(sizeof(FluidObject), "Fluid Object"); - fobj->object = ob; - - if (ELEM(fluidmd->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE)) { - BLI_addtail(fobjects, fobj); - continue; - } - - fobj->Translation = MEM_callocN(length * (CHANNEL_VEC + 1) * sizeof(float), - "fluidobject Translation"); - fobj->Rotation = MEM_callocN(length * (CHANNEL_VEC + 1) * sizeof(float), - "fluidobject Rotation"); - fobj->Scale = MEM_callocN(length * (CHANNEL_VEC + 1) * sizeof(float), "fluidobject Scale"); - fobj->Active = MEM_callocN(length * (CHANNEL_FLOAT + 1) * sizeof(float), - "fluidobject Active"); - fobj->InitialVelocity = MEM_callocN(length * (CHANNEL_VEC + 1) * sizeof(float), - "fluidobject InitialVelocity"); - - if (fluidmd->fss->type == OB_FLUIDSIM_CONTROL) { - fobj->AttractforceStrength = MEM_callocN(length * (CHANNEL_FLOAT + 1) * sizeof(float), - "fluidobject AttractforceStrength"); - fobj->AttractforceRadius = MEM_callocN(length * (CHANNEL_FLOAT + 1) * sizeof(float), - "fluidobject AttractforceRadius"); - fobj->VelocityforceStrength = MEM_callocN(length * (CHANNEL_FLOAT + 1) * sizeof(float), - "fluidobject VelocityforceStrength"); - fobj->VelocityforceRadius = MEM_callocN(length * (CHANNEL_FLOAT + 1) * sizeof(float), - "fluidobject VelocityforceRadius"); - } - - if (fluid_is_animated_mesh(fluidmd->fss)) { - float *verts = NULL; - int *tris = NULL, modifierIndex = BLI_findindex(&ob->modifiers, (ModifierData *)fluidmd); - - initElbeemMesh(depsgraph, - scene, - ob, - &fobj->numVerts, - &verts, - &fobj->numTris, - &tris, - 0, - modifierIndex); - fobj->VertexCache = MEM_callocN(length * ((fobj->numVerts * CHANNEL_VEC) + 1) * - sizeof(float), - "fluidobject VertexCache"); - - MEM_freeN(verts); - MEM_freeN(tris); - } - - BLI_addtail(fobjects, fobj); - } + /* We do not accept empty paths, they can end in random places silently, see T51176. */ + if (mds->cache_directory[0] == '\0') { + modifier_path_init( + mds->cache_directory, sizeof(mds->cache_directory), FLUID_DOMAIN_DIR_DEFAULT); + BKE_reportf(reports, + RPT_WARNING, + "Fluid: Empty cache path, reset to default '%s'", + mds->cache_directory); } - /* now we loop over the frames and fill the allocated channels with data */ - for (i = 0; i < channels->length; i++) { - FluidObject *fobj; - float viscosity, gravity[3]; - float timeAtFrame, time; + BLI_strncpy(temp_dir, mds->cache_directory, FILE_MAXDIR); + BLI_path_abs(temp_dir, relbase); - eval_time = domainSettings->bakeStart + i; + /* Ensure whole path exists */ + const bool dir_exists = BLI_dir_create_recursive(temp_dir); - /* Modifying the global scene isn't nice, but we can do it in - * this part of the process before a threaded job is created */ - scene->r.cfra = (int)eval_time; - ED_update_for_newframe(CTX_data_main(C), depsgraph); + /* We change path to some presumably valid default value, but do not allow bake process to + * continue, this gives user chance to set manually another path. */ + if (!dir_exists) { + modifier_path_init( + mds->cache_directory, sizeof(mds->cache_directory), FLUID_DOMAIN_DIR_DEFAULT); - /* now scene data should be current according to animation system, so we fill the channels */ + BKE_reportf(reports, + RPT_ERROR, + "Fluid: Could not create cache directory '%s', reset to default '%s'", + temp_dir, + mds->cache_directory); - /* Domain time */ - /* TODO: have option for not running sim, time mangling, - * in which case second case comes in handy. */ - if (channels->DomainTime) { - time = get_fluid_rate(domainSettings) * (float)channels->aniFrameTime; - timeAtFrame = channels->timeAtFrame[i] + time; + BLI_strncpy(temp_dir, mds->cache_directory, FILE_MAXDIR); + BLI_path_abs(temp_dir, relbase); - channels->timeAtFrame[i + 1] = timeAtFrame; - set_channel(channels->DomainTime, i, &time, i, CHANNEL_FLOAT); - } - else { - timeAtFrame = channels->timeAtFrame[i + 1]; + /* Ensure whole path exists and is writable. */ + if (!BLI_dir_create_recursive(temp_dir)) { + BKE_reportf(reports, + RPT_ERROR, + "Fluid: Could not use default cache directory '%s', " + "please define a valid cache path manually", + temp_dir); } + /* Copy final dir back into domain settings */ + BLI_strncpy(mds->cache_directory, temp_dir, FILE_MAXDIR); - /* Domain properties - gravity/viscosity */ - get_fluid_gravity(gravity, scene, domainSettings); - set_channel(channels->DomainGravity, timeAtFrame, gravity, i, CHANNEL_VEC); - viscosity = get_fluid_viscosity(domainSettings); - set_channel(channels->DomainViscosity, timeAtFrame, &viscosity, i, CHANNEL_FLOAT); - - /* object movement */ - for (fobj = fobjects->first; fobj; fobj = fobj->next) { - Object *ob = fobj->object; - FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType( - ob, eModifierType_Fluidsim); - float active = (float)((fluidmd->fss->flag & OB_FLUIDSIM_ACTIVE) ? 1 : 0); - float rot_d[3] = {0.f, 0.f, 0.f}, old_rot[3] = {0.f, 0.f, 0.f}; - - if (ELEM(fluidmd->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE)) { - continue; - } - - /* init euler rotation values and convert to elbeem format */ - /* get the rotation from ob->obmat rather than ob->rot to account for parent animations */ - if (i) { - copy_v3_v3(old_rot, fobj->Rotation + 4 * (i - 1)); - mul_v3_fl(old_rot, (float)-M_PI / 180.f); - } - - mat4_to_compatible_eulO(rot_d, old_rot, 0, ob->obmat); - mul_v3_fl(rot_d, -180.0f / (float)M_PI); - - set_channel(fobj->Translation, timeAtFrame, ob->loc, i, CHANNEL_VEC); - set_channel(fobj->Rotation, timeAtFrame, rot_d, i, CHANNEL_VEC); - set_channel(fobj->Scale, timeAtFrame, ob->scale, i, CHANNEL_VEC); - set_channel(fobj->Active, timeAtFrame, &active, i, CHANNEL_FLOAT); - set_channel(fobj->InitialVelocity, timeAtFrame, &fluidmd->fss->iniVelx, i, CHANNEL_VEC); - - // printf("Active: %f, Frame: %f\n", active, timeAtFrame); - - if (fluidmd->fss->type == OB_FLUIDSIM_CONTROL) { - set_channel(fobj->AttractforceStrength, - timeAtFrame, - &fluidmd->fss->attractforceStrength, - i, - CHANNEL_FLOAT); - set_channel(fobj->AttractforceRadius, - timeAtFrame, - &fluidmd->fss->attractforceRadius, - i, - CHANNEL_FLOAT); - set_channel(fobj->VelocityforceStrength, - timeAtFrame, - &fluidmd->fss->velocityforceStrength, - i, - CHANNEL_FLOAT); - set_channel(fobj->VelocityforceRadius, - timeAtFrame, - &fluidmd->fss->velocityforceRadius, - i, - CHANNEL_FLOAT); - } - - if (fluid_is_animated_mesh(fluidmd->fss)) { - set_vertex_channel(depsgraph, fobj->VertexCache, timeAtFrame, scene, fobj, i); - } - } + return false; } + + /* Copy final dir back into domain settings */ + BLI_strncpy(mds->cache_directory, temp_dir, FILE_MAXDIR); + return true; } -static void export_fluid_objects(Depsgraph *depsgraph, - ListBase *fobjects, - Scene *scene, - int length) +static void fluid_bake_free(void *customdata) { - FluidObject *fobj; - - for (fobj = fobjects->first; fobj; fobj = fobj->next) { - Object *ob = fobj->object; - FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType( - ob, eModifierType_Fluidsim); - int modifierIndex = BLI_findindex(&ob->modifiers, fluidmd); + FluidJob *job = customdata; + MEM_freeN(job); +} - float *verts = NULL; - int *tris = NULL; - int numVerts = 0, numTris = 0; - bool deform = fluid_is_animated_mesh(fluidmd->fss); +static void fluid_bake_sequence(FluidJob *job) +{ + FluidDomainSettings *mds = job->mmd->domain; + Scene *scene = job->scene; + int frame = 1, orig_frame; + int frames; + int *pause_frame = NULL; + bool is_first_frame; - elbeemMesh fsmesh; + frames = mds->cache_frame_end - mds->cache_frame_start + 1; - if (ELEM(fluidmd->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE)) { - continue; - } + if (frames <= 0) { + BLI_strncpy(mds->error, N_("No frames to bake"), sizeof(mds->error)); + return; + } - elbeemResetMesh(&fsmesh); + /* Show progress bar. */ + if (job->do_update) { + *(job->do_update) = true; + } - fsmesh.type = fluidmd->fss->type; - fsmesh.name = ob->id.name; + /* Get current pause frame (pointer) - depending on bake type */ + pause_frame = job->pause_frame; - initElbeemMesh(depsgraph, scene, ob, &numVerts, &verts, &numTris, &tris, 0, modifierIndex); + /* Set frame to start point (depending on current pause frame value) */ + is_first_frame = ((*pause_frame) == 0); + frame = is_first_frame ? mds->cache_frame_start : (*pause_frame); - fsmesh.numVertices = numVerts; - fsmesh.numTriangles = numTris; - fsmesh.vertices = verts; - fsmesh.triangles = tris; + /* Save orig frame and update scene frame */ + orig_frame = CFRA; + CFRA = frame; - fsmesh.channelSizeTranslation = fsmesh.channelSizeRotation = fsmesh.channelSizeScale = - fsmesh.channelSizeInitialVel = fsmesh.channelSizeActive = length; + /* Loop through selected frames */ + for (; frame <= mds->cache_frame_end; frame++) { + const float progress = (frame - mds->cache_frame_start) / (float)frames; - fsmesh.channelTranslation = fobj->Translation; - fsmesh.channelRotation = fobj->Rotation; - fsmesh.channelScale = fobj->Scale; - fsmesh.channelActive = fobj->Active; + /* Keep track of pause frame - needed to init future loop */ + (*pause_frame) = frame; - if (ELEM(fsmesh.type, OB_FLUIDSIM_FLUID, OB_FLUIDSIM_INFLOW)) { - fsmesh.channelInitialVel = fobj->InitialVelocity; - fsmesh.localInivelCoords = ((fluidmd->fss->typeFlags & OB_FSINFLOW_LOCALCOORD) ? 1 : 0); + /* If user requested stop, quit baking */ + if (G.is_break) { + job->success = 0; + return; } - if (fluidmd->fss->typeFlags & OB_FSBND_NOSLIP) { - fsmesh.obstacleType = FLUIDSIM_OBSTACLE_NOSLIP; - } - else if (fluidmd->fss->typeFlags & OB_FSBND_PARTSLIP) { - fsmesh.obstacleType = FLUIDSIM_OBSTACLE_PARTSLIP; + /* Update progress bar */ + if (job->do_update) { + *(job->do_update) = true; } - else if (fluidmd->fss->typeFlags & OB_FSBND_FREESLIP) { - fsmesh.obstacleType = FLUIDSIM_OBSTACLE_FREESLIP; + if (job->progress) { + *(job->progress) = progress; } - fsmesh.obstaclePartslip = fluidmd->fss->partSlipValue; - fsmesh.volumeInitType = fluidmd->fss->volumeInitType; - fsmesh.obstacleImpactFactor = fluidmd->fss->surfaceSmoothing; // misused value - - if (fsmesh.type == OB_FLUIDSIM_CONTROL) { - fsmesh.cpsTimeStart = fluidmd->fss->cpsTimeStart; - fsmesh.cpsTimeEnd = fluidmd->fss->cpsTimeEnd; - fsmesh.cpsQuality = fluidmd->fss->cpsQuality; - fsmesh.obstacleType = (fluidmd->fss->flag & OB_FLUIDSIM_REVERSE); + CFRA = frame; - fsmesh.channelSizeAttractforceRadius = fsmesh.channelSizeVelocityforceStrength = - fsmesh.channelSizeVelocityforceRadius = fsmesh.channelSizeAttractforceStrength = length; - - fsmesh.channelAttractforceStrength = fobj->AttractforceStrength; - fsmesh.channelAttractforceRadius = fobj->AttractforceRadius; - fsmesh.channelVelocityforceStrength = fobj->VelocityforceStrength; - fsmesh.channelVelocityforceRadius = fobj->VelocityforceRadius; - } - else { - fsmesh.channelAttractforceStrength = fsmesh.channelAttractforceRadius = - fsmesh.channelVelocityforceStrength = fsmesh.channelVelocityforceRadius = NULL; - } - - /* animated meshes */ - if (deform) { - fsmesh.channelSizeVertices = length; - fsmesh.channelVertices = fobj->VertexCache; - - /* remove channels */ - fsmesh.channelTranslation = fsmesh.channelRotation = fsmesh.channelScale = NULL; - - /* Override user settings, only noslip is supported here! */ - if (fsmesh.type != OB_FLUIDSIM_CONTROL) { - fsmesh.obstacleType = FLUIDSIM_OBSTACLE_NOSLIP; - } - } + /* Update animation system */ + ED_update_for_newframe(job->bmain, job->depsgraph); + } - elbeemAddMesh(&fsmesh); + /* Restore frame position that we were on before bake */ + CFRA = orig_frame; +} - if (verts) { - MEM_freeN(verts); +static void fluid_bake_endjob(void *customdata) +{ + FluidJob *job = customdata; + FluidDomainSettings *mds = job->mmd->domain; + + if (fluid_is_bake_noise(job) || fluid_is_bake_all(job)) { + mds->cache_flag &= ~FLUID_DOMAIN_BAKING_NOISE; + mds->cache_flag |= FLUID_DOMAIN_BAKED_NOISE; + mds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_NOISE; + } + if (fluid_is_bake_mesh(job) || fluid_is_bake_all(job)) { + mds->cache_flag &= ~FLUID_DOMAIN_BAKING_MESH; + mds->cache_flag |= FLUID_DOMAIN_BAKED_MESH; + mds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_MESH; + } + if (fluid_is_bake_particle(job) || fluid_is_bake_all(job)) { + mds->cache_flag &= ~FLUID_DOMAIN_BAKING_PARTICLES; + mds->cache_flag |= FLUID_DOMAIN_BAKED_PARTICLES; + mds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_PARTICLES; + } + if (fluid_is_bake_guiding(job) || fluid_is_bake_all(job)) { + mds->cache_flag &= ~FLUID_DOMAIN_BAKING_GUIDE; + mds->cache_flag |= FLUID_DOMAIN_BAKED_GUIDE; + mds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_GUIDE; + } + if (fluid_is_bake_data(job) || fluid_is_bake_all(job)) { + mds->cache_flag &= ~FLUID_DOMAIN_BAKING_DATA; + mds->cache_flag |= FLUID_DOMAIN_BAKED_DATA; + mds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_DATA; + } + DEG_id_tag_update(&job->ob->id, ID_RECALC_GEOMETRY); + + G.is_rendering = false; + BKE_spacedata_draw_locks(false); + WM_set_locked_interface(G_MAIN->wm.first, false); + + /* Bake was successful: + * Report for ended bake and how long it took */ + if (job->success) { + /* Show bake info */ + WM_reportf( + RPT_INFO, "Fluid: %s complete! (%.2f)", job->name, PIL_check_seconds_timer() - job->start); + } + else { + if (mds->error != NULL && mds->error[0] != '\0') { + WM_reportf(RPT_ERROR, "Fluid: %s failed: %s", job->name, mds->error); } - if (tris) { - MEM_freeN(tris); + else { /* User canceled the bake */ + WM_reportf(RPT_WARNING, "Fluid: %s canceled!", job->name); } } } -static int fluid_validate_scene(ReportList *reports, ViewLayer *view_layer, Object *fsDomain) +static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, float *progress) { - Base *base; - Object *newdomain = NULL; - int channelObjCount = 0; - int fluidInputCount = 0; - - for (base = FIRSTBASE(view_layer); base; base = base->next) { - Object *ob = base->object; - FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType( - ob, eModifierType_Fluidsim); - - /* only find objects with fluid modifiers */ - if (!fluidmdtmp || ob->type != OB_MESH) { - continue; - } - - if (fluidmdtmp->fss->type == OB_FLUIDSIM_DOMAIN) { - /* if no initial domain object given, find another potential domain */ - if (!fsDomain) { - newdomain = ob; - } - /* if there's more than one domain, cancel */ - else if (fsDomain && ob != fsDomain) { - BKE_report(reports, RPT_ERROR, "There should be only one domain object"); - return 0; - } - } - - /* count number of objects needed for animation channels */ - if (!ELEM(fluidmdtmp->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE)) { - channelObjCount++; - } - - /* count number of fluid input objects */ - if (ELEM(fluidmdtmp->fss->type, OB_FLUIDSIM_FLUID, OB_FLUIDSIM_INFLOW)) { - fluidInputCount++; + FluidJob *job = customdata; + FluidDomainSettings *mds = job->mmd->domain; + + char temp_dir[FILE_MAX]; + + job->stop = stop; + job->do_update = do_update; + job->progress = progress; + job->start = PIL_check_seconds_timer(); + job->success = 1; + + G.is_break = false; + G.is_rendering = true; + BKE_spacedata_draw_locks(true); + + if (fluid_is_bake_noise(job) || fluid_is_bake_all(job)) { + BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_NOISE, NULL); + BLI_dir_create_recursive(temp_dir); /* Create 'noise' subdir if it does not exist already */ + mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_NOISE | FLUID_DOMAIN_OUTDATED_NOISE); + mds->cache_flag |= FLUID_DOMAIN_BAKING_NOISE; + job->pause_frame = &mds->cache_frame_pause_noise; + } + if (fluid_is_bake_mesh(job) || fluid_is_bake_all(job)) { + BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_MESH, NULL); + BLI_dir_create_recursive(temp_dir); /* Create 'mesh' subdir if it does not exist already */ + mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_MESH | FLUID_DOMAIN_OUTDATED_MESH); + mds->cache_flag |= FLUID_DOMAIN_BAKING_MESH; + job->pause_frame = &mds->cache_frame_pause_mesh; + } + if (fluid_is_bake_particle(job) || fluid_is_bake_all(job)) { + BLI_path_join( + temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES, NULL); + BLI_dir_create_recursive( + temp_dir); /* Create 'particles' subdir if it does not exist already */ + mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_PARTICLES | FLUID_DOMAIN_OUTDATED_PARTICLES); + mds->cache_flag |= FLUID_DOMAIN_BAKING_PARTICLES; + job->pause_frame = &mds->cache_frame_pause_particles; + } + if (fluid_is_bake_guiding(job) || fluid_is_bake_all(job)) { + BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_GUIDE, NULL); + BLI_dir_create_recursive(temp_dir); /* Create 'guiding' subdir if it does not exist already */ + mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_GUIDE | FLUID_DOMAIN_OUTDATED_GUIDE); + mds->cache_flag |= FLUID_DOMAIN_BAKING_GUIDE; + job->pause_frame = &mds->cache_frame_pause_guide; + } + if (fluid_is_bake_data(job) || fluid_is_bake_all(job)) { + BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_CONFIG, NULL); + BLI_dir_create_recursive(temp_dir); /* Create 'config' subdir if it does not exist already */ + + BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, NULL); + BLI_dir_create_recursive(temp_dir); /* Create 'data' subdir if it does not exist already */ + mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_DATA | FLUID_DOMAIN_OUTDATED_DATA); + mds->cache_flag |= FLUID_DOMAIN_BAKING_DATA; + job->pause_frame = &mds->cache_frame_pause_data; + + if (mds->flags & FLUID_DOMAIN_EXPORT_MANTA_SCRIPT) { + BLI_path_join( + temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, NULL); + BLI_dir_create_recursive(temp_dir); /* Create 'script' subdir if it does not exist already */ } } + DEG_id_tag_update(&job->ob->id, ID_RECALC_GEOMETRY); - if (newdomain) { - fsDomain = newdomain; - } + fluid_bake_sequence(job); - if (!fsDomain) { - BKE_report(reports, RPT_ERROR, "No domain object found"); - return 0; + if (do_update) { + *do_update = true; } - - if (channelObjCount >= 255) { - BKE_report(reports, RPT_ERROR, "Cannot bake with more than 256 objects"); - return 0; + if (stop) { + *stop = 0; } - - if (fluidInputCount == 0) { - BKE_report(reports, RPT_ERROR, "No fluid input objects in the scene"); - return 0; - } - - return 1; } -# define FLUID_SUFFIX_CONFIG "fluidsim.cfg" -# define FLUID_SUFFIX_CONFIG_TMP (FLUID_SUFFIX_CONFIG ".tmp") -# define FLUID_SUFFIX_SURFACE "fluidsurface" - -static bool fluid_init_filepaths(Main *bmain, - ReportList *reports, - FluidsimSettings *domainSettings, - Object *fsDomain, - char *targetDir, - char *targetFile) +static void fluid_free_endjob(void *customdata) { - const char *suffixConfigTmp = FLUID_SUFFIX_CONFIG_TMP; + FluidJob *job = customdata; + FluidDomainSettings *mds = job->mmd->domain; - /* prepare names... */ - const char *relbase = modifier_path_relbase(bmain, fsDomain); + G.is_rendering = false; + BKE_spacedata_draw_locks(false); + WM_set_locked_interface(G_MAIN->wm.first, false); - /* We do not accept empty paths, they can end in random places silently, see T51176. */ - if (domainSettings->surfdataPath[0] == '\0') { - modifier_path_init(domainSettings->surfdataPath, - sizeof(domainSettings->surfdataPath), - OB_FLUIDSIM_SURF_DIR_DEFAULT); - BKE_reportf(reports, - RPT_WARNING, - "Fluidsim: empty cache path, reset to default '%s'", - domainSettings->surfdataPath); + /* Free was successful: + * Report for ended free job and how long it took */ + if (job->success) { + /* Show free job info */ + WM_reportf( + RPT_INFO, "Fluid: %s complete! (%.2f)", job->name, PIL_check_seconds_timer() - job->start); } - - BLI_strncpy(targetDir, domainSettings->surfdataPath, FILE_MAXDIR); - BLI_path_abs(targetDir, relbase); - - /* .tmp: don't overwrite/delete original file */ - BLI_join_dirfile(targetFile, FILE_MAX, targetDir, suffixConfigTmp); - - /* Ensure whole path exists and is writeable. */ - const bool dir_exists = BLI_dir_create_recursive(targetDir); - const bool is_writable = BLI_file_is_writable(targetFile); - - /* We change path to some presumably valid default value, - * but do not allow bake process to continue, - * this gives user chance to set manually another path. */ - if (!dir_exists || !is_writable) { - modifier_path_init(domainSettings->surfdataPath, - sizeof(domainSettings->surfdataPath), - OB_FLUIDSIM_SURF_DIR_DEFAULT); - - if (!dir_exists) { - BKE_reportf(reports, - RPT_ERROR, - "Fluidsim: could not create cache directory '%s', reset to default '%s'", - targetDir, - domainSettings->surfdataPath); + else { + if (mds->error != NULL && mds->error[0] != '\0') { + WM_reportf(RPT_ERROR, "Fluid: %s failed: %s", job->name, mds->error); } - else { - BKE_reportf(reports, - RPT_ERROR, - "Fluidsim: cache directory '%s' is not writable, reset to default '%s'", - targetDir, - domainSettings->surfdataPath); + else { /* User canceled the free job */ + WM_reportf(RPT_WARNING, "Fluid: %s canceled!", job->name); } - - BLI_strncpy(targetDir, domainSettings->surfdataPath, FILE_MAXDIR); - BLI_path_abs(targetDir, relbase); - - /* .tmp: don't overwrite/delete original file */ - BLI_join_dirfile(targetFile, FILE_MAX, targetDir, suffixConfigTmp); - - /* Ensure whole path exists and is writeable. */ - if (!BLI_dir_create_recursive(targetDir) || !BLI_file_is_writable(targetFile)) { - BKE_reportf(reports, - RPT_ERROR, - "Fluidsim: could not use default cache directory '%s', " - "please define a valid cache path manually", - targetDir); - } - return false; } - - return true; } -/* ******************************************************************************** */ -/* ********************** write fluidsim config to file ************************* */ -/* ******************************************************************************** */ +static void fluid_free_startjob(void *customdata, short *stop, short *do_update, float *progress) +{ + FluidJob *job = customdata; + FluidDomainSettings *mds = job->mmd->domain; + Scene *scene = job->scene; -typedef struct FluidBakeJob { - /* from wmJob */ - void *owner; - short *stop, *do_update; - float *progress; - int current_frame; - elbeemSimulationSettings *settings; -} FluidBakeJob; + job->stop = stop; + job->do_update = do_update; + job->progress = progress; + job->start = PIL_check_seconds_timer(); + job->success = 1; -static void fluidbake_free(void *customdata) -{ - FluidBakeJob *fb = (FluidBakeJob *)customdata; - MEM_freeN(fb); -} + G.is_break = false; + G.is_rendering = true; + BKE_spacedata_draw_locks(true); -/* called by fluidbake, only to check job 'stop' value */ -static int fluidbake_breakjob(void *customdata) -{ - FluidBakeJob *fb = (FluidBakeJob *)customdata; + int cache_map = 0; - if (fb->stop && *(fb->stop)) { - return 1; + if (fluid_is_free_data(job) || fluid_is_free_all(job)) { + cache_map |= (FLUID_DOMAIN_OUTDATED_DATA | FLUID_DOMAIN_OUTDATED_NOISE | + FLUID_DOMAIN_OUTDATED_MESH | FLUID_DOMAIN_OUTDATED_PARTICLES); + } + if (fluid_is_free_noise(job) || fluid_is_free_all(job)) { + cache_map |= FLUID_DOMAIN_OUTDATED_NOISE; + } + if (fluid_is_free_mesh(job) || fluid_is_free_all(job)) { + cache_map |= FLUID_DOMAIN_OUTDATED_MESH; + } + if (fluid_is_free_particles(job) || fluid_is_free_all(job)) { + cache_map |= FLUID_DOMAIN_OUTDATED_PARTICLES; + } + if (fluid_is_free_guiding(job) || fluid_is_free_all(job)) { + cache_map |= FLUID_DOMAIN_OUTDATED_GUIDE; } +#ifdef WITH_FLUID + BKE_fluid_cache_free(mds, job->ob, cache_map); +#endif - /* this is not nice yet, need to make the jobs list template better - * for identifying/acting upon various different jobs */ - /* but for now we'll reuse the render break... */ - return (G.is_break); -} + *do_update = true; + *stop = 0; -/* called by fluidbake, wmJob sends notifier */ -static void fluidbake_updatejob(void *customdata, float progress) -{ - FluidBakeJob *fb = (FluidBakeJob *)customdata; + /* Reset scene frame to cache frame start */ + CFRA = mds->cache_frame_start; - *(fb->do_update) = true; - *(fb->progress) = progress; + /* Update scene so that viewport shows freed up scene */ + ED_update_for_newframe(job->bmain, job->depsgraph); } -static void fluidbake_startjob(void *customdata, short *stop, short *do_update, float *progress) -{ - FluidBakeJob *fb = (FluidBakeJob *)customdata; +/***************************** Operators ******************************/ - fb->stop = stop; - fb->do_update = do_update; - fb->progress = progress; +static int fluid_bake_exec(struct bContext *C, struct wmOperator *op) +{ + FluidJob *job = MEM_mallocN(sizeof(FluidJob), "FluidJob"); + char error_msg[256] = "\0"; - G.is_break = false; /* XXX shared with render - replace with job 'stop' switch */ + if (!fluid_initjob(C, job, op, error_msg, sizeof(error_msg))) { + if (error_msg[0]) { + BKE_report(op->reports, RPT_ERROR, error_msg); + } + fluid_bake_free(job); + return OPERATOR_CANCELLED; + } + if (!fluid_initpaths(job, op->reports)) { + return OPERATOR_CANCELLED; + } + fluid_bake_startjob(job, NULL, NULL, NULL); + fluid_bake_endjob(job); + fluid_bake_free(job); - elbeemSimulate(); - *do_update = true; - *stop = 0; + return OPERATOR_FINISHED; } -static void fluidbake_endjob(void *customdata) +static int fluid_bake_invoke(struct bContext *C, + struct wmOperator *op, + const wmEvent *UNUSED(_event)) { - FluidBakeJob *fb = (FluidBakeJob *)customdata; + Scene *scene = CTX_data_scene(C); + FluidJob *job = MEM_mallocN(sizeof(FluidJob), "FluidJob"); + char error_msg[256] = "\0"; - if (fb->settings) { - MEM_freeN(fb->settings); - fb->settings = NULL; + if (!fluid_initjob(C, job, op, error_msg, sizeof(error_msg))) { + if (error_msg[0]) { + BKE_report(op->reports, RPT_ERROR, error_msg); + } + fluid_bake_free(job); + return OPERATOR_CANCELLED; } -} -static int runSimulationCallback(void *data, int status, int frame) -{ - FluidBakeJob *fb = (FluidBakeJob *)data; - elbeemSimulationSettings *settings = fb->settings; - - if (status == FLUIDSIM_CBSTATUS_NEWFRAME) { - fluidbake_updatejob(fb, frame / (float)settings->noOfFrames); -# if 0 - printf("elbeem blender cb s%d, f%d, domainid:%d noOfFrames: %d\n", - status, - frame, - settings->domainId, - settings->noOfFrames); // DEBUG -# endif + if (!fluid_initpaths(job, op->reports)) { + return OPERATOR_CANCELLED; } - if (fluidbake_breakjob(fb)) { - return FLUIDSIM_CBRET_ABORT; - } + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + scene, + "Fluid Bake", + WM_JOB_PROGRESS, + WM_JOB_TYPE_OBJECT_SIM_FLUID); + + WM_jobs_customdata_set(wm_job, job, fluid_bake_free); + WM_jobs_timer(wm_job, 0.01, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER); + WM_jobs_callbacks(wm_job, fluid_bake_startjob, NULL, NULL, fluid_bake_endjob); + + WM_set_locked_interface(CTX_wm_manager(C), true); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + WM_event_add_modal_handler(C, op); - return FLUIDSIM_CBRET_CONTINUE; + return OPERATOR_RUNNING_MODAL; } -static void fluidbake_free_data(FluidAnimChannels *channels, - ListBase *fobjects, - elbeemSimulationSettings *fsset, - FluidBakeJob *fb) +static int fluid_bake_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { - free_domain_channels(channels); - MEM_freeN(channels); - channels = NULL; - - free_all_fluidobject_channels(fobjects); - BLI_freelistN(fobjects); - MEM_freeN(fobjects); - fobjects = NULL; - - if (fsset) { - MEM_freeN(fsset); - fsset = NULL; + /* no running blender, remove handler and pass through */ + if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_OBJECT_SIM_FLUID)) { + return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; } - if (fb) { - MEM_freeN(fb); - fb = NULL; + switch (event->type) { + case ESCKEY: + return OPERATOR_RUNNING_MODAL; } + return OPERATOR_PASS_THROUGH; } -/* copied from rna_fluidsim.c: fluidsim_find_lastframe() */ -static void fluidsim_delete_until_lastframe(FluidsimSettings *fss, const char *relbase) +static int fluid_free_exec(struct bContext *C, struct wmOperator *op) { - char targetDir[FILE_MAX], targetFile[FILE_MAX]; - char targetDirVel[FILE_MAX], targetFileVel[FILE_MAX]; - char previewDir[FILE_MAX], previewFile[FILE_MAX]; - int curFrame = 1, exists = 0; - - BLI_join_dirfile( - targetDir, sizeof(targetDir), fss->surfdataPath, OB_FLUIDSIM_SURF_FINAL_OBJ_FNAME); - BLI_join_dirfile( - targetDirVel, sizeof(targetDirVel), fss->surfdataPath, OB_FLUIDSIM_SURF_FINAL_VEL_FNAME); - BLI_join_dirfile( - previewDir, sizeof(previewDir), fss->surfdataPath, OB_FLUIDSIM_SURF_PREVIEW_OBJ_FNAME); - - BLI_path_abs(targetDir, relbase); - BLI_path_abs(targetDirVel, relbase); - BLI_path_abs(previewDir, relbase); - - do { - BLI_strncpy(targetFile, targetDir, sizeof(targetFile)); - BLI_strncpy(targetFileVel, targetDirVel, sizeof(targetFileVel)); - BLI_strncpy(previewFile, previewDir, sizeof(previewFile)); - - BLI_path_frame(targetFile, curFrame, 0); - BLI_path_frame(targetFileVel, curFrame, 0); - BLI_path_frame(previewFile, curFrame, 0); - - curFrame++; - - if ((exists = BLI_exists(targetFile))) { - BLI_delete(targetFile, false, false); - BLI_delete(targetFileVel, false, false); - BLI_delete(previewFile, false, false); - } - } while (exists); + FluidModifierData *mmd = NULL; + FluidDomainSettings *mds; + Object *ob = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); - return; -} + /* + * Get modifier data + */ + mmd = (FluidModifierData *)modifiers_findByType(ob, eModifierType_Fluid); + if (!mmd) { + BKE_report(op->reports, RPT_ERROR, "Bake free failed: no Fluid modifier found"); + return OPERATOR_CANCELLED; + } + mds = mmd->domain; + if (!mds) { + BKE_report(op->reports, RPT_ERROR, "Bake free failed: invalid domain"); + return OPERATOR_CANCELLED; + } -static int fluidsimBake(bContext *C, ReportList *reports, Object *fsDomain, short do_job) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - int i; - FluidsimSettings *domainSettings; - - char debugStrBuffer[256]; - - int gridlevels = 0; - const char *relbase = modifier_path_relbase(bmain, fsDomain); - const char *strEnvName = "BLENDER_ELBEEMDEBUG"; // from blendercall.cpp - const char *suffixConfigTmp = FLUID_SUFFIX_CONFIG_TMP; - const char *suffixSurface = FLUID_SUFFIX_SURFACE; - - char targetDir[FILE_MAX]; // store & modify output settings - char targetFile[FILE_MAX]; // temp. store filename from targetDir for access - - float domainMat[4][4]; - float invDomMat[4][4]; - - int noFrames; - int origFrame = scene->r.cfra; - - FluidAnimChannels *channels = MEM_callocN(sizeof(FluidAnimChannels), - "fluid domain animation channels"); - ListBase *fobjects = MEM_callocN(sizeof(ListBase), "fluid objects"); - FluidsimModifierData *fluidmd = NULL; - Mesh *mesh = NULL; - - FluidBakeJob *fb; - elbeemSimulationSettings *fsset = MEM_callocN(sizeof(elbeemSimulationSettings), - "Fluid sim settings"); - - fb = MEM_callocN(sizeof(FluidBakeJob), "fluid bake job"); - - if (BLI_getenv(strEnvName)) { - int dlevel = atoi(BLI_getenv(strEnvName)); - elbeemSetDebugLevel(dlevel); - BLI_snprintf(debugStrBuffer, - sizeof(debugStrBuffer), - "fluidsimBake::msg: Debug messages activated due to envvar '%s'\n", - strEnvName); - elbeemDebugOut(debugStrBuffer); + /* Cannot free data if other bakes currently working */ + if (mmd->domain->cache_flag & (FLUID_DOMAIN_BAKING_DATA | FLUID_DOMAIN_BAKING_NOISE | + FLUID_DOMAIN_BAKING_MESH | FLUID_DOMAIN_BAKING_PARTICLES)) { + BKE_report(op->reports, RPT_ERROR, "Bake free failed: pending bake jobs found"); + return OPERATOR_CANCELLED; } - /* Make sure it corresponds to startFrame setting - * (old: noFrames = scene->r.efra - scene->r.sfra +1). */ + FluidJob *job = MEM_mallocN(sizeof(FluidJob), "FluidJob"); + job->bmain = CTX_data_main(C); + job->scene = scene; + job->depsgraph = CTX_data_depsgraph_pointer(C); + job->ob = ob; + job->mmd = mmd; + job->type = op->type->idname; + job->name = op->type->name; - noFrames = scene->r.efra - 0; - if (noFrames <= 0) { - BKE_report(reports, RPT_ERROR, "No frames to export (check your animation range settings)"); - fluidbake_free_data(channels, fobjects, fsset, fb); - return 0; + if (!fluid_initpaths(job, op->reports)) { + return OPERATOR_CANCELLED; } - /* check scene for sane object/modifier settings */ - if (!fluid_validate_scene(reports, view_layer, fsDomain)) { - fluidbake_free_data(channels, fobjects, fsset, fb); - return 0; - } + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + scene, + "Fluid Free", + WM_JOB_PROGRESS, + WM_JOB_TYPE_OBJECT_SIM_FLUID); - /* these both have to be valid, otherwise we wouldn't be here */ - fluidmd = (FluidsimModifierData *)modifiers_findByType(fsDomain, eModifierType_Fluidsim); - domainSettings = fluidmd->fss; - mesh = fsDomain->data; - - domainSettings->bakeStart = 1; - domainSettings->bakeEnd = scene->r.efra; - - // calculate bounding box - fluid_get_bb(mesh->mvert, - mesh->totvert, - fsDomain->obmat, - domainSettings->bbStart, - domainSettings->bbSize); - - // reset last valid frame - domainSettings->lastgoodframe = -1; - - /* delete old baked files */ - fluidsim_delete_until_lastframe(domainSettings, relbase); - - /* rough check of settings... */ - if (domainSettings->previewresxyz > domainSettings->resolutionxyz) { - BLI_snprintf(debugStrBuffer, - sizeof(debugStrBuffer), - "fluidsimBake::warning - Preview (%d) >= Resolution (%d)... setting equal.\n", - domainSettings->previewresxyz, - domainSettings->resolutionxyz); - elbeemDebugOut(debugStrBuffer); - domainSettings->previewresxyz = domainSettings->resolutionxyz; - } - // set adaptive coarsening according to resolutionxyz - // this should do as an approximation, with in/outflow - // doing this more accurate would be overkill - // perhaps add manual setting? - if (domainSettings->maxRefine < 0) { - if (domainSettings->resolutionxyz > 128) { - gridlevels = 2; - } - else if (domainSettings->resolutionxyz > 64) { - gridlevels = 1; - } - else { - gridlevels = 0; - } - } - else { - gridlevels = domainSettings->maxRefine; - } - BLI_snprintf(debugStrBuffer, - sizeof(debugStrBuffer), - "fluidsimBake::msg: Baking %s, refine: %d\n", - fsDomain->id.name, - gridlevels); - elbeemDebugOut(debugStrBuffer); - - /* ******** prepare output file paths ******** */ - if (!fluid_init_filepaths(bmain, reports, domainSettings, fsDomain, targetDir, targetFile)) { - fluidbake_free_data(channels, fobjects, fsset, fb); - return false; - } + WM_jobs_customdata_set(wm_job, job, fluid_bake_free); + WM_jobs_timer(wm_job, 0.01, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER); + WM_jobs_callbacks(wm_job, fluid_free_startjob, NULL, NULL, fluid_free_endjob); - /* DG TODO: why using endframe and not "noFrames" here? - * because "noFrames" is buggy too? (not using sfra) */ - channels->length = scene->r.efra; - channels->aniFrameTime = (double)((double)domainSettings->animEnd - - (double)domainSettings->animStart) / - (double)noFrames; - - /* ******** initialize and allocate animation channels ******** */ - fluid_init_all_channels(C, depsgraph, fsDomain, domainSettings, channels, fobjects); - - /* reset to original current frame */ - scene->r.cfra = origFrame; - ED_update_for_newframe(CTX_data_main(C), CTX_data_depsgraph_pointer(C)); - - /* ******** init domain object's matrix ******** */ - copy_m4_m4(domainMat, fsDomain->obmat); - if (!invert_m4_m4(invDomMat, domainMat)) { - BLI_snprintf( - debugStrBuffer, sizeof(debugStrBuffer), "fluidsimBake::error - Invalid obj matrix?\n"); - elbeemDebugOut(debugStrBuffer); - BKE_report(reports, RPT_ERROR, "Invalid object matrix"); - - fluidbake_free_data(channels, fobjects, fsset, fb); - return 0; - } + WM_set_locked_interface(CTX_wm_manager(C), true); - /* ******** start writing / exporting ******** */ - // use .tmp, don't overwrite/delete original file - BLI_join_dirfile(targetFile, sizeof(targetFile), targetDir, suffixConfigTmp); - - /* ******** export domain to elbeem ******** */ - elbeemResetSettings(fsset); - fsset->version = 1; - fsset->threads = (domainSettings->threads == 0) ? BKE_scene_num_threads(scene) : - domainSettings->threads; - // setup global settings - copy_v3_v3(fsset->geoStart, domainSettings->bbStart); - copy_v3_v3(fsset->geoSize, domainSettings->bbSize); - - // simulate with 50^3 - fsset->resolutionxyz = (int)domainSettings->resolutionxyz; - fsset->previewresxyz = (int)domainSettings->previewresxyz; - - fsset->realsize = get_fluid_size_m(scene, fsDomain, domainSettings); - fsset->viscosity = get_fluid_viscosity(domainSettings); - get_fluid_gravity(fsset->gravity, scene, domainSettings); - - // simulate 5 frames, each 0.03 seconds, output to ./apitest_XXX.bobj.gz - fsset->animStart = domainSettings->animStart; - fsset->aniFrameTime = channels->aniFrameTime; - fsset->noOfFrames = noFrames; // is otherwise subtracted in parser - - BLI_join_dirfile(targetFile, sizeof(targetFile), targetDir, suffixSurface); - - // defaults for compressibility and adaptive grids - fsset->gstar = domainSettings->gstar; - fsset->maxRefine = domainSettings->maxRefine; // check <-> gridlevels - fsset->generateParticles = domainSettings->generateParticles; - fsset->numTracerParticles = domainSettings->generateTracers; - fsset->surfaceSmoothing = domainSettings->surfaceSmoothing; - fsset->surfaceSubdivs = domainSettings->surfaceSubdivs; - fsset->farFieldSize = domainSettings->farFieldSize; - BLI_strncpy(fsset->outputPath, targetFile, sizeof(fsset->outputPath)); - - // domain channels - fsset->channelSizeFrameTime = fsset->channelSizeViscosity = fsset->channelSizeGravity = - channels->length; - fsset->channelFrameTime = channels->DomainTime; - fsset->channelViscosity = channels->DomainViscosity; - fsset->channelGravity = channels->DomainGravity; - - fsset->runsimCallback = &runSimulationCallback; - fsset->runsimUserData = fb; - - if (domainSettings->typeFlags & OB_FSBND_NOSLIP) { - fsset->domainobsType = FLUIDSIM_OBSTACLE_NOSLIP; - } - else if (domainSettings->typeFlags & OB_FSBND_PARTSLIP) { - fsset->domainobsType = FLUIDSIM_OBSTACLE_PARTSLIP; - } - else if (domainSettings->typeFlags & OB_FSBND_FREESLIP) { - fsset->domainobsType = FLUIDSIM_OBSTACLE_FREESLIP; - } - fsset->domainobsPartslip = domainSettings->partSlipValue; + /* Free Fluid Geometry */ + WM_jobs_start(CTX_wm_manager(C), wm_job); + + return OPERATOR_FINISHED; +} - /* use domainobsType also for surface generation flag (bit: >=64) */ - if (domainSettings->typeFlags & OB_FSSG_NOOBS) { - fsset->mFsSurfGenSetting = FLUIDSIM_FSSG_NOOBS; +static int fluid_pause_exec(struct bContext *C, struct wmOperator *op) +{ + FluidModifierData *mmd = NULL; + FluidDomainSettings *mds; + Object *ob = CTX_data_active_object(C); + + /* + * Get modifier data + */ + mmd = (FluidModifierData *)modifiers_findByType(ob, eModifierType_Fluid); + if (!mmd) { + BKE_report(op->reports, RPT_ERROR, "Bake free failed: no Fluid modifier found"); + return OPERATOR_CANCELLED; } - else { - fsset->mFsSurfGenSetting = 0; // "normal" mode + mds = mmd->domain; + if (!mds) { + BKE_report(op->reports, RPT_ERROR, "Bake free failed: invalid domain"); + return OPERATOR_CANCELLED; } - fsset->generateVertexVectors = (domainSettings->domainNovecgen == 0); + G.is_break = true; - // init blender domain transform matrix - { - int j; - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) { - fsset->surfaceTrafo[i * 4 + j] = invDomMat[j][i]; - } - } - } + return OPERATOR_FINISHED; +} - /* ******** init solver with settings ******** */ - elbeemInit(); - elbeemAddDomain(fsset); +void FLUID_OT_bake_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Bake All"; + ot->description = "Bake Entire Fluid Simulation"; + ot->idname = FLUID_JOB_BAKE_ALL; - /* ******** export all fluid objects to elbeem ******** */ - export_fluid_objects(depsgraph, fobjects, scene, channels->length); + /* api callbacks */ + ot->exec = fluid_bake_exec; + ot->invoke = fluid_bake_invoke; + ot->modal = fluid_bake_modal; + ot->poll = ED_operator_object_active_editable; +} - /* custom data for fluid bake job */ - fb->settings = fsset; +void FLUID_OT_free_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Free All"; + ot->description = "Free Entire Fluid Simulation"; + ot->idname = FLUID_JOB_FREE_ALL; - if (do_job) { - wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - scene, - "Fluid Simulation", - WM_JOB_PROGRESS, - WM_JOB_TYPE_OBJECT_SIM_FLUID); + /* api callbacks */ + ot->exec = fluid_free_exec; + ot->poll = ED_operator_object_active_editable; +} - /* setup job */ - WM_jobs_customdata_set(wm_job, fb, fluidbake_free); - WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); - WM_jobs_callbacks(wm_job, fluidbake_startjob, NULL, NULL, fluidbake_endjob); +void FLUID_OT_bake_data(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Bake Data"; + ot->description = "Bake Fluid Data"; + ot->idname = FLUID_JOB_BAKE_DATA; - WM_jobs_start(CTX_wm_manager(C), wm_job); - } - else { - short dummy_stop = 0, dummy_do_update = 0; - float dummy_progress = 0.0f; + /* api callbacks */ + ot->exec = fluid_bake_exec; + ot->invoke = fluid_bake_invoke; + ot->modal = fluid_bake_modal; + ot->poll = ED_operator_object_active_editable; +} - /* blocking, use with exec() */ - fluidbake_startjob((void *)fb, &dummy_stop, &dummy_do_update, &dummy_progress); - fluidbake_endjob((void *)fb); - fluidbake_free((void *)fb); - } +void FLUID_OT_free_data(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Free Data"; + ot->description = "Free Fluid Data"; + ot->idname = FLUID_JOB_FREE_DATA; + + /* api callbacks */ + ot->exec = fluid_free_exec; + ot->poll = ED_operator_object_active_editable; +} - /* ******** free stored animation data ******** */ - fluidbake_free_data(channels, fobjects, NULL, NULL); +void FLUID_OT_bake_noise(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Bake Noise"; + ot->description = "Bake Fluid Noise"; + ot->idname = FLUID_JOB_BAKE_NOISE; - // elbeemFree(); - return 1; + /* api callbacks */ + ot->exec = fluid_bake_exec; + ot->invoke = fluid_bake_invoke; + ot->modal = fluid_bake_modal; + ot->poll = ED_operator_object_active_editable; } -static void UNUSED_FUNCTION(fluidsimFreeBake)(Object *UNUSED(ob)) +void FLUID_OT_free_noise(wmOperatorType *ot) { - /* not implemented yet */ + /* identifiers */ + ot->name = "Free Noise"; + ot->description = "Free Fluid Noise"; + ot->idname = FLUID_JOB_FREE_NOISE; + + /* api callbacks */ + ot->exec = fluid_free_exec; + ot->poll = ED_operator_object_active_editable; } -#else /* WITH_MOD_FLUID */ +void FLUID_OT_bake_mesh(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Bake Mesh"; + ot->description = "Bake Fluid Mesh"; + ot->idname = FLUID_JOB_BAKE_MESH; + + /* api callbacks */ + ot->exec = fluid_bake_exec; + ot->invoke = fluid_bake_invoke; + ot->modal = fluid_bake_modal; + ot->poll = ED_operator_object_active_editable; +} -/* only compile dummy functions */ -static int fluidsimBake(bContext *UNUSED(C), - ReportList *UNUSED(reports), - Object *UNUSED(ob), - short UNUSED(do_job)) +void FLUID_OT_free_mesh(wmOperatorType *ot) { - return 0; + /* identifiers */ + ot->name = "Free Mesh"; + ot->description = "Free Fluid Mesh"; + ot->idname = FLUID_JOB_FREE_MESH; + + /* api callbacks */ + ot->exec = fluid_free_exec; + ot->poll = ED_operator_object_active_editable; } -#endif /* WITH_MOD_FLUID */ +void FLUID_OT_bake_particles(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Bake Particles"; + ot->description = "Bake Fluid Particles"; + ot->idname = FLUID_JOB_BAKE_PARTICLES; -/***************************** Operators ******************************/ + /* api callbacks */ + ot->exec = fluid_bake_exec; + ot->invoke = fluid_bake_invoke; + ot->modal = fluid_bake_modal; + ot->poll = ED_operator_object_active_editable; +} -static int fluid_bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +void FLUID_OT_free_particles(wmOperatorType *ot) { - /* only one bake job at a time */ - if (WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_OBJECT_SIM_FLUID)) { - return OPERATOR_CANCELLED; - } + /* identifiers */ + ot->name = "Free Particles"; + ot->description = "Free Fluid Particles"; + ot->idname = FLUID_JOB_FREE_PARTICLES; - if (!fluidsimBake(C, op->reports, ED_object_context(C), true)) { - return OPERATOR_CANCELLED; - } + /* api callbacks */ + ot->exec = fluid_free_exec; + ot->poll = ED_operator_object_active_editable; +} - return OPERATOR_FINISHED; +void FLUID_OT_bake_guides(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Bake Guides"; + ot->description = "Bake Fluid Guiding"; + ot->idname = FLUID_JOB_BAKE_GUIDES; + + /* api callbacks */ + ot->exec = fluid_bake_exec; + ot->invoke = fluid_bake_invoke; + ot->modal = fluid_bake_modal; + ot->poll = ED_operator_object_active_editable; } -static int fluid_bake_exec(bContext *C, wmOperator *op) +void FLUID_OT_free_guides(wmOperatorType *ot) { - if (!fluidsimBake(C, op->reports, CTX_data_active_object(C), false)) { - return OPERATOR_CANCELLED; - } + /* identifiers */ + ot->name = "Free Guides"; + ot->description = "Free Fluid Guiding"; + ot->idname = FLUID_JOB_FREE_GUIDES; - return OPERATOR_FINISHED; + /* api callbacks */ + ot->exec = fluid_free_exec; + ot->poll = ED_operator_object_active_editable; } -void FLUID_OT_bake(wmOperatorType *ot) +void FLUID_OT_pause_bake(wmOperatorType *ot) { /* identifiers */ - ot->name = "Fluid Simulation Bake"; - ot->description = "Bake fluid simulation"; - ot->idname = "FLUID_OT_bake"; + ot->name = "Pause Bake"; + ot->description = "Pause Bake"; + ot->idname = FLUID_JOB_BAKE_PAUSE; /* api callbacks */ - ot->invoke = fluid_bake_invoke; - ot->exec = fluid_bake_exec; + ot->exec = fluid_pause_exec; ot->poll = ED_operator_object_active_editable; } diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h index fc2f3d21bb6..3af818b3a9d 100644 --- a/source/blender/editors/physics/physics_intern.h +++ b/source/blender/editors/physics/physics_intern.h @@ -103,7 +103,19 @@ void BOID_OT_state_move_up(struct wmOperatorType *ot); void BOID_OT_state_move_down(struct wmOperatorType *ot); /* physics_fluid.c */ -void FLUID_OT_bake(struct wmOperatorType *ot); +void FLUID_OT_bake_all(struct wmOperatorType *ot); +void FLUID_OT_free_all(struct wmOperatorType *ot); +void FLUID_OT_bake_data(struct wmOperatorType *ot); +void FLUID_OT_free_data(struct wmOperatorType *ot); +void FLUID_OT_bake_noise(struct wmOperatorType *ot); +void FLUID_OT_free_noise(struct wmOperatorType *ot); +void FLUID_OT_bake_mesh(struct wmOperatorType *ot); +void FLUID_OT_free_mesh(struct wmOperatorType *ot); +void FLUID_OT_bake_particles(struct wmOperatorType *ot); +void FLUID_OT_free_particles(struct wmOperatorType *ot); +void FLUID_OT_bake_guides(struct wmOperatorType *ot); +void FLUID_OT_free_guides(struct wmOperatorType *ot); +void FLUID_OT_pause_bake(struct wmOperatorType *ot); /* dynamicpaint.c */ void DPAINT_OT_bake(struct wmOperatorType *ot); diff --git a/source/blender/editors/physics/physics_ops.c b/source/blender/editors/physics/physics_ops.c index b1b3927d05e..d1536733b9b 100644 --- a/source/blender/editors/physics/physics_ops.c +++ b/source/blender/editors/physics/physics_ops.c @@ -126,7 +126,19 @@ static void operatortypes_boids(void) static void operatortypes_fluid(void) { - WM_operatortype_append(FLUID_OT_bake); + WM_operatortype_append(FLUID_OT_bake_all); + WM_operatortype_append(FLUID_OT_free_all); + WM_operatortype_append(FLUID_OT_bake_data); + WM_operatortype_append(FLUID_OT_free_data); + WM_operatortype_append(FLUID_OT_bake_noise); + WM_operatortype_append(FLUID_OT_free_noise); + WM_operatortype_append(FLUID_OT_bake_mesh); + WM_operatortype_append(FLUID_OT_free_mesh); + WM_operatortype_append(FLUID_OT_bake_particles); + WM_operatortype_append(FLUID_OT_free_particles); + WM_operatortype_append(FLUID_OT_bake_guides); + WM_operatortype_append(FLUID_OT_free_guides); + WM_operatortype_append(FLUID_OT_pause_bake); } /**************************** point cache **********************************/ diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 10244cfa3fd..3a77e1e1565 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -28,6 +28,7 @@ #include "MEM_guardedalloc.h" #include "DNA_camera_types.h" +#include "BLI_bitmap.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" #include "BLI_blenlib.h" @@ -35,13 +36,18 @@ #include "BLI_threads.h" #include "BLI_task.h" +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_curve_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" #include "DNA_gpencil_types.h" +#include "BKE_animsys.h" #include "BKE_camera.h" #include "BKE_context.h" #include "BKE_customdata.h" +#include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" @@ -123,6 +129,9 @@ typedef struct OGLRender { int totvideos; + /* For only rendering frames that have a key in animation data. */ + BLI_bitmap *render_frames; + /* quick lookup */ int view_id; @@ -517,6 +526,71 @@ static void screen_opengl_render_apply(const bContext *C, OGLRender *oglrender) } } +static void gather_frames_to_render_for_adt(OGLRender *oglrender, + int frame_start, + int frame_end, + const AnimData *adt) +{ + if (adt == NULL || adt->action == NULL) { + return; + } + + LISTBASE_FOREACH (FCurve *, fcu, &adt->action->curves) { + if (fcu->driver != NULL || fcu->fpt != NULL) { + /* Drivers have values for any point in time, so to get "the keyed frames" they are + * useless. Same for baked FCurves, they also have keys for every frame, which is not + * useful for rendering the keyed subset of the frames. */ + continue; + } + + bool found = false; /* Not interesting, we just want a starting point for the for-loop.*/ + int key_index = binarysearch_bezt_index(fcu->bezt, frame_start, fcu->totvert, &found); + for (; key_index < fcu->totvert; key_index++) { + BezTriple *bezt = &fcu->bezt[key_index]; + /* The frame range to render uses integer frame numbers, and the frame + * step is also an integer, so we always render on the frame. */ + int frame_nr = round_fl_to_int(bezt->vec[1][0]); + + /* (frame_nr < frame_start) cannot happen because of the binary search above. */ + BLI_assert(frame_nr >= frame_start); + if (frame_nr > frame_end) { + break; + } + BLI_BITMAP_ENABLE(oglrender->render_frames, frame_nr - frame_start); + } + } +} + +/** + * Collect the frame numbers for which selected objects have keys in the animation data. + * The frames ares stored in #OGLRender.render_frames. + */ +static void gather_frames_to_render(bContext *C, OGLRender *oglrender) +{ + Scene *scene = CTX_data_scene(C); + int frame_start = PSFRA; + int frame_end = PEFRA; + + /* Will be freed in screen_opengl_render_end(). */ + oglrender->render_frames = BLI_BITMAP_NEW(frame_end - frame_start + 1, + "OGLRender::render_frames"); + + /* The first frame should always be rendered, otherwise there is nothing to write to file. */ + BLI_BITMAP_ENABLE(oglrender->render_frames, 0); + + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if (ob->adt != NULL) { + gather_frames_to_render_for_adt(oglrender, frame_start, frame_end, ob->adt); + } + + AnimData *adt = BKE_animdata_from_id(ob->data); + if (adt != NULL) { + gather_frames_to_render_for_adt(oglrender, frame_start, frame_end, adt); + } + } + CTX_DATA_END; +} + static bool screen_opengl_render_init(bContext *C, wmOperator *op) { /* new render clears all callbacks */ @@ -532,6 +606,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) int sizex, sizey; bool is_view_context = RNA_boolean_get(op->ptr, "view_context"); const bool is_animation = RNA_boolean_get(op->ptr, "animation"); + const bool is_render_keyed_only = RNA_boolean_get(op->ptr, "render_keyed_only"); const bool is_sequencer = RNA_boolean_get(op->ptr, "sequencer"); const bool is_write_still = RNA_boolean_get(op->ptr, "write_still"); const eImageFormatDepth color_depth = (is_animation) ? scene->r.im_format.depth : @@ -665,6 +740,10 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) oglrender->movie_ctx_arr = NULL; if (is_animation) { + if (is_render_keyed_only) { + gather_frames_to_render(C, oglrender); + } + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { task_scheduler = BLI_task_scheduler_create(1); @@ -731,6 +810,8 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender) printf("Total render time: %f\n", PIL_check_seconds_timer() - oglrender->time_start); #endif + MEM_SAFE_FREE(oglrender->render_frames); + if (oglrender->mh) { if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { for (i = 0; i < oglrender->totvideos; i++) { @@ -995,8 +1076,11 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) BKE_scene_camera_switch_update(scene); } - /* render into offscreen buffer */ - screen_opengl_render_apply(C, oglrender); + if (oglrender->render_frames == NULL || + BLI_BITMAP_TEST_BOOL(oglrender->render_frames, CFRA - PSFRA)) { + /* render into offscreen buffer */ + screen_opengl_render_apply(C, oglrender); + } /* save to disk */ rr = RE_AcquireResultRead(oglrender->re); @@ -1124,6 +1208,23 @@ static int screen_opengl_render_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static char *screen_opengl_render_description(struct bContext *UNUSED(C), + struct wmOperatorType *UNUSED(ot), + struct PointerRNA *ptr) +{ + if (!RNA_boolean_get(ptr, "animation")) { + return NULL; + } + + if (RNA_boolean_get(ptr, "render_keyed_only")) { + return BLI_strdup( + "Render the viewport for the animation range of this scene, but only render keyframes of " + "selected objects"); + } + + return BLI_strdup("Render the viewport for the animation range of this scene"); +} + void RENDER_OT_opengl(wmOperatorType *ot) { PropertyRNA *prop; @@ -1134,6 +1235,7 @@ void RENDER_OT_opengl(wmOperatorType *ot) ot->idname = "RENDER_OT_opengl"; /* api callbacks */ + ot->get_description = screen_opengl_render_description; ot->invoke = screen_opengl_render_invoke; ot->exec = screen_opengl_render_exec; /* blocking */ ot->modal = screen_opengl_render_modal; @@ -1147,6 +1249,15 @@ void RENDER_OT_opengl(wmOperatorType *ot) "Animation", "Render files from the animation range of this scene"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, + "render_keyed_only", + 0, + "Render Keyframes Only", + "Render only those frames where selected objects have a key in their " + "animation data. Only used when rendering animation"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean( ot->srna, "sequencer", 0, "Sequencer", "Render using the sequencer's OpenGL display"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index e3070903ccc..98bee156090 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1595,6 +1595,8 @@ static void ed_default_handlers( } } if (flag & ED_KEYMAP_TOOL) { + WM_event_add_keymap_handler_dynamic( + &ar->handlers, WM_event_get_keymap_from_toolsystem_fallback, sa); WM_event_add_keymap_handler_dynamic(&ar->handlers, WM_event_get_keymap_from_toolsystem, sa); } if (flag & ED_KEYMAP_VIEW2D) { diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 5b8fd33a4e9..501c36286d0 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1389,13 +1389,13 @@ ScrArea *ED_screen_temp_space_open(bContext *C, case USER_TEMP_SPACE_DISPLAY_FULLSCREEN: { ScrArea *ctx_sa = CTX_wm_area(C); - if (ctx_sa->full) { + if (ctx_sa != NULL && ctx_sa->full) { sa = ctx_sa; ED_area_newspace(C, ctx_sa, space_type, true); sa->flag |= AREA_FLAG_STACKED_FULLSCREEN; ((SpaceLink *)sa->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY; } - else if (ctx_sa->spacetype == space_type) { + else if (ctx_sa != NULL && ctx_sa->spacetype == space_type) { sa = ED_screen_state_toggle(C, CTX_wm_window(C), ctx_sa, SCREENMAXIMIZED); } else { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 9975c49dc54..7c47f7439ab 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4229,19 +4229,6 @@ static void SCREEN_OT_region_context_menu(wmOperatorType *ot) * Animation Step. * \{ */ -static int match_area_with_refresh(int spacetype, int refresh) -{ - switch (spacetype) { - case SPACE_TIME: - if (refresh & SPACE_TIME) { - return 1; - } - break; - } - - return 0; -} - static int match_region_with_redraws(int spacetype, int regiontype, int redraws, @@ -4524,10 +4511,6 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv } } } - - if (match_area_with_refresh(sa->spacetype, sad->refresh)) { - ED_area_tag_refresh(sa); - } } } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 06d79b8a49d..7f71110b360 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -1560,9 +1560,9 @@ static void paint_2d_canvas_free(ImagePaintState *s) } } -static void paint_2d_transform_mouse(ImagePaintState *s, const float in[2], float out[2]) +static void paint_2d_transform_mouse(View2D *v2d, const float in[2], float out[2]) { - UI_view2d_region_to_view(s->v2d, in[0], in[1], &out[0], &out[1]); + UI_view2d_region_to_view(v2d, in[0], in[1], &out[0], &out[1]); } static bool is_inside_tile(const int size[2], const float pos[2], const float brush[2]) @@ -1656,8 +1656,9 @@ void paint_2d_stroke(void *ps, brush_painter_2d_refresh_cache(s, painter, tile, new_coord, mval, pressure, distance, size); - if (paint_2d_op(s, tile, old_coord, new_coord)) + if (paint_2d_op(s, tile, old_coord, new_coord)) { tile->need_redraw = true; + } } painter->firsttouch = 0; @@ -1899,14 +1900,24 @@ void paint_2d_bucket_fill(const bContext *C, return; } + View2D *v2d = s ? s->v2d : &CTX_wm_region(C)->v2d; float uv_origin[2]; float image_init[2]; - paint_2d_transform_mouse(s, mouse_init, image_init); + paint_2d_transform_mouse(v2d, mouse_init, image_init); int tile_number = BKE_image_get_tile_from_pos(ima, image_init, image_init, uv_origin); - ImageUser *iuser = paint_2d_get_tile_iuser(s, tile_number); - if (!iuser) { - return; + + ImageUser local_iuser, *iuser; + if (s != NULL) { + iuser = paint_2d_get_tile_iuser(s, tile_number); + if (iuser == NULL) { + return; + } + } + else { + iuser = &local_iuser; + BKE_imageuser_default(iuser); + iuser->tile = tile_number; } ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); @@ -2125,8 +2136,8 @@ void paint_2d_gradient_fill( return; } - paint_2d_transform_mouse(s, mouse_final, image_final); - paint_2d_transform_mouse(s, mouse_init, image_init); + paint_2d_transform_mouse(s->v2d, mouse_final, image_final); + paint_2d_transform_mouse(s->v2d, mouse_init, image_init); sub_v2_v2(image_init, uv_origin); sub_v2_v2(image_final, uv_origin); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 6a67c469955..d649f5017eb 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -748,11 +748,7 @@ static bool project_paint_PickColor(const ProjPaintState *ps, iuser.tile = tile_number; ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); if (ibuf == NULL) { - iuser.tile = 0; - ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); - if (ibuf == NULL) { - return 0; - } + return false; } if (interp) { @@ -3554,16 +3550,7 @@ static void project_bucket_init(const ProjPaintState *ps, break; } } - if (ibuf == NULL) { - /* Failed to find the specific tile, fall back to the primary tile. */ - for (image_index = 0; image_index < ps->image_tot; image_index++) { - ProjPaintImage *projIma = &ps->projImages[image_index]; - if ((projIma->ima == tpage) && (projIma->iuser.tile == 0)) { - ibuf = projIma->ibuf; - break; - } - } - } + BLI_assert(ibuf != NULL); } /* context switching done */ @@ -4454,11 +4441,19 @@ static void project_paint_prepare_all_faces(ProjPaintState *ps, } if (image_index == ps->image_tot) { - PrepareImageEntry *e = MEM_callocN(sizeof(PrepareImageEntry), "PrepareImageEntry"); - e->ima = tpage; - e->tile = tile; - BLI_addtail(&used_images, e); - ps->image_tot++; + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile; + if (BKE_image_has_ibuf(tpage, &iuser)) { + PrepareImageEntry *e = MEM_callocN(sizeof(PrepareImageEntry), "PrepareImageEntry"); + e->ima = tpage; + e->tile = tile; + BLI_addtail(&used_images, e); + ps->image_tot++; + } + else { + image_index = -1; + } } tpage_last = tpage; diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 536d93cac29..cafdd72c7cd 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -2339,7 +2339,9 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* calculate pivot for rotation around seletion if needed */ /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, ss->cache->true_location); + float loc_world[3]; + mul_v3_m4v3(loc_world, ob->obmat, ss->cache->true_location); + paint_last_stroke_update(scene, loc_world); BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); @@ -3314,7 +3316,9 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* calculate pivot for rotation around seletion if needed */ /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, ss->cache->true_location); + float loc_world[3]; + mul_v3_m4v3(loc_world, ob->obmat, ss->cache->true_location); + paint_last_stroke_update(scene, loc_world); ED_region_tag_redraw(vc->ar); diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index fdf96917b56..fb095a57835 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -813,7 +813,6 @@ const char *buttons_context_dir[] = { "cloth", "soft_body", "fluid", - "smoke", "collision", "brush", "dynamic_paint", @@ -1061,24 +1060,14 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r return 1; } } - else if (CTX_data_equals(member, "fluid")) { - PointerRNA *ptr = get_pointer_type(path, &RNA_Object); - if (ptr && ptr->data) { - Object *ob = ptr->data; - ModifierData *md = modifiers_findByType(ob, eModifierType_Fluidsim); - CTX_data_pointer_set(result, &ob->id, &RNA_FluidSimulationModifier, md); - return 1; - } - } - - else if (CTX_data_equals(member, "smoke")) { + else if (CTX_data_equals(member, "fluid")) { PointerRNA *ptr = get_pointer_type(path, &RNA_Object); if (ptr && ptr->data) { Object *ob = ptr->data; - ModifierData *md = modifiers_findByType(ob, eModifierType_Smoke); - CTX_data_pointer_set(result, &ob->id, &RNA_SmokeModifier, md); + ModifierData *md = modifiers_findByType(ob, eModifierType_Fluid); + CTX_data_pointer_set(result, &ob->id, &RNA_FluidModifier, md); return 1; } } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 1ea7d81f9da..a567aeed826 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1001,6 +1001,9 @@ static int filelist_geticon_ex(const int typeflag, else if (typeflag & FILE_TYPE_ALEMBIC) { return ICON_FILE_3D; } + else if (typeflag & FILE_TYPE_USD) { + return ICON_FILE_3D; + } else if (typeflag & FILE_TYPE_OBJECT_IO) { return ICON_FILE_3D; } @@ -2130,6 +2133,9 @@ int ED_path_extension_type(const char *path) else if (BLI_path_extension_check(path, ".abc")) { return FILE_TYPE_ALEMBIC; } + else if (BLI_path_extension_check_n(path, ".usd", ".usda", ".usdc", NULL)) { + return FILE_TYPE_USD; + } else if (BLI_path_extension_check(path, ".zip")) { return FILE_TYPE_ARCHIVE; } diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 99e4fc62980..02fb98aa7d7 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -215,6 +215,9 @@ short ED_fileselect_set_params(SpaceFile *sfile) if ((prop = RNA_struct_find_property(op->ptr, "filter_alembic"))) { params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_ALEMBIC : 0; } + if ((prop = RNA_struct_find_property(op->ptr, "filter_usd"))) { + params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_USD : 0; + } if ((prop = RNA_struct_find_property(op->ptr, "filter_glob"))) { /* Protection against pyscripts not setting proper size limit... */ char *tmp = RNA_property_string_get_alloc( diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 4d2772aabee..03df93e4c8a 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -1409,6 +1409,8 @@ static void decimate_exit(bContext *C, wmOperator *op) if (dgo == NULL) { return; } + + ScrArea *sa = dgo->sa; LinkData *link; for (link = dgo->bezt_arr_list.first; link != NULL; link = link->next) { @@ -1422,7 +1424,7 @@ static void decimate_exit(bContext *C, wmOperator *op) /* Return to normal cursor and header status. */ WM_cursor_modal_restore(win); - ED_area_status_text(dgo->sa, NULL); + ED_area_status_text(sa, NULL); /* cleanup */ op->customdata = NULL; diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 9a633427d82..3f563fe9033 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -549,12 +549,15 @@ static void draw_udim_label(ARegion *ar, float fx, float fy, const char *label) int textwidth = BLF_width(blf_mono_font, label, strlen(label)) + 10; float stepx = BLI_rcti_size_x(&ar->v2d.mask) / BLI_rctf_size_x(&ar->v2d.cur); float opacity; - if (textwidth < 0.5f * (stepx - 10)) + if (textwidth < 0.5f * (stepx - 10)) { opacity = 1.0f; - else if (textwidth < (stepx - 10)) + } + else if (textwidth < (stepx - 10)) { opacity = 2.0f - 2.0f * (textwidth / (stepx - 10)); - else + } + else { opacity = 0.0f; + } BLF_color4ub(blf_mono_font, 220, 220, 220, 150 * opacity); BLF_position(blf_mono_font, (int)(x + 10), (int)(y + 10), 0); BLF_draw_ascii(blf_mono_font, label, strlen(label)); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 4404f904891..9b091979f1e 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1267,49 +1267,61 @@ static int image_cmp_frame(const void *a, const void *b) return 0; } -static int image_get_udim(const char *filepath, LinkNodePair *udim_tiles) +/* Checks whether the given filepath refers to a UDIM texture. + * If yes, the range from 1001 to the highest tile is returned, otherwise 0. + * + * If the result is positive, the filepath will be overwritten with that of + * the 1001 tile. + * udim_tiles may get filled even if the result ultimately is false! */ +static int image_get_udim(char *filepath, LinkNodePair *udim_tiles) { char filename[FILE_MAX], dirname[FILE_MAXDIR]; BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename)); - if (strstr(filename, "1001") == NULL) { + unsigned short digits; + char base_head[FILE_MAX], base_tail[FILE_MAX]; + int id = BLI_stringdec(filename, base_head, base_tail, &digits); + + if (id < 1001 || id >= IMA_UDIM_MAX) { return 0; } bool is_udim = true; + bool has_primary = false; int max_udim = 0; - unsigned short digits; - char base_head[FILE_MAX], base_tail[FILE_MAX]; - int id = BLI_stringdec(filename, base_head, base_tail, &digits); - if (id == 1001) { - struct direntry *dir; - uint totfile = BLI_filelist_dir_contents(dirname, &dir); - for (int i = 0; i < totfile; i++) { - if (!(dir[i].type & S_IFREG)) { - continue; - } - char head[FILE_MAX], tail[FILE_MAX]; - id = BLI_stringdec(dir[i].relname, head, tail, &digits); - - if (digits > 4 || !(STREQLEN(base_head, head, FILE_MAX)) || - !(STREQLEN(base_tail, tail, FILE_MAX))) { - continue; - } + struct direntry *dir; + uint totfile = BLI_filelist_dir_contents(dirname, &dir); + for (int i = 0; i < totfile; i++) { + if (!(dir[i].type & S_IFREG)) { + continue; + } + char head[FILE_MAX], tail[FILE_MAX]; + id = BLI_stringdec(dir[i].relname, head, tail, &digits); - if (id < 1001 || id >= 2000) { - is_udim = false; - break; - } + if (digits > 4 || !(STREQLEN(base_head, head, FILE_MAX)) || + !(STREQLEN(base_tail, tail, FILE_MAX))) { + continue; + } - BLI_linklist_append(udim_tiles, POINTER_FROM_INT(id)); - max_udim = max_ii(max_udim, id); + if (id < 1001 || id >= IMA_UDIM_MAX) { + is_udim = false; + break; + } + if (id == 1001) { + has_primary = true; } - BLI_filelist_free(dir, totfile); + BLI_linklist_append(udim_tiles, POINTER_FROM_INT(id)); + max_udim = max_ii(max_udim, id); } + BLI_filelist_free(dir, totfile); - return is_udim ? (max_udim - 1001) : 0; + if (is_udim && has_primary) { + BLI_stringenc_path(filepath, dirname, base_head, base_tail, digits, 1001); + return max_udim - 1000; + } + return 0; } /** @@ -1320,27 +1332,34 @@ static int image_get_udim(const char *filepath, LinkNodePair *udim_tiles) * \param ofs: [out] offset the first frame number in the sequence. * \return the number of contiguous frames in the sequence */ -static int image_sequence_get_len(ImageFrameRange *frame_range, int *ofs, LinkNodePair *udim_tiles) +static int image_sequence_get_len(ImageFrameRange *frame_range, + int *ofs, + char *filepath_range, + LinkNodePair *udim_tiles) { ImageFrame *frame; BLI_listbase_sort(&frame_range->frames, image_cmp_frame); + BLI_strncpy(filepath_range, frame_range->filepath, FILE_MAX); frame = frame_range->frames.first; if (frame != NULL) { int frame_curr = frame->framenr; (*ofs) = frame_curr; - if (udim_tiles != NULL && (frame_curr == 1001)) { - return 1 + image_get_udim(frame_range->filepath, udim_tiles); - } - else { - while (frame != NULL && (frame->framenr == frame_curr)) { - frame_curr++; - frame = frame->next; + if (udim_tiles != NULL) { + int len_udim = image_get_udim(filepath_range, udim_tiles); + if (len_udim > 0) { + *ofs = 1001; + return len_udim; } - return frame_curr - (*ofs); } + + while (frame != NULL && (frame->framenr == frame_curr)) { + frame_curr++; + frame = frame->next; + } + return frame_curr - (*ofs); } *ofs = 0; return 0; @@ -1422,7 +1441,6 @@ static int image_open_exec(bContext *C, wmOperator *op) char filepath[FILE_MAX]; int frame_seq_len = 0; int frame_ofs = 1; - LinkNodePair udim_tiles = {NULL}; const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path"); const bool use_multiview = RNA_boolean_get(op->ptr, "use_multiview"); @@ -1445,13 +1463,13 @@ static int image_open_exec(bContext *C, wmOperator *op) frame_range = frame_range->next) { int frame_range_ofs; + LinkNodePair udim_tiles = {NULL}; LinkNodePair *udim_tiles_ptr = use_udim ? (&udim_tiles) : NULL; - int frame_range_seq_len = image_sequence_get_len( - frame_range, &frame_range_ofs, udim_tiles_ptr); - BLI_freelistN(&frame_range->frames); char filepath_range[FILE_MAX]; - BLI_strncpy(filepath_range, frame_range->filepath, sizeof(filepath_range)); + int frame_range_seq_len = image_sequence_get_len( + frame_range, &frame_range_ofs, filepath_range, udim_tiles_ptr); + BLI_freelistN(&frame_range->frames); if (was_relative) { BLI_path_rel(filepath_range, BKE_main_blendfile_path(bmain)); @@ -1473,35 +1491,42 @@ static int image_open_exec(bContext *C, wmOperator *op) frame_seq_len = frame_range_seq_len; frame_ofs = frame_range_ofs; } + + BLI_linklist_free(udim_tiles.list, NULL); } BLI_freelistN(&frame_ranges_all); } else { /* for drag & drop etc. */ + + LinkNodePair udim_tiles = {NULL}; frame_seq_len = 1; + char filepath_range[FILE_MAX]; + BLI_strncpy(filepath_range, filepath, FILE_MAX); - if (use_udim) { + if (use_udim > 0) { /* Try to find UDIM tiles corresponding to the image */ - frame_seq_len = 1 + image_get_udim(filepath, &udim_tiles); + int udim_len = image_get_udim(filepath_range, &udim_tiles); /* If we found something, mark the image as tiled. */ - if (frame_seq_len > 1) { + if (udim_len) { + frame_seq_len = udim_len; frame_ofs = 1001; } } ima = image_open_single(bmain, op, - filepath, + filepath_range, BKE_main_blendfile_path(bmain), is_relative_path, use_multiview, frame_seq_len, frame_ofs, &udim_tiles); - } - BLI_linklist_free(udim_tiles.list, NULL); + BLI_linklist_free(udim_tiles.list, NULL); + } if (ima == NULL) { return OPERATOR_CANCELLED; @@ -4212,32 +4237,108 @@ void IMAGE_OT_clear_render_border(wmOperatorType *ot) /* ********************* Add tile operator ****************** */ -static bool tile_poll(bContext *C) +static bool do_fill_tile(PointerRNA *ptr, Image *ima, ImageTile *tile) +{ + float color[4]; + RNA_float_get_array(ptr, "color", color); + int gen_type = RNA_enum_get(ptr, "generated_type"); + int width = RNA_int_get(ptr, "width"); + int height = RNA_int_get(ptr, "height"); + bool is_float = RNA_boolean_get(ptr, "float"); + int planes = RNA_boolean_get(ptr, "alpha") ? 32 : 24; + + return BKE_image_fill_tile(ima, tile, width, height, color, gen_type, planes, is_float); +} + +static void draw_fill_tile(PointerRNA *ptr, uiLayout *layout) +{ + uiLayout *split, *col[2]; + + split = uiLayoutSplit(layout, 0.5f, false); + col[0] = uiLayoutColumn(split, false); + col[1] = uiLayoutColumn(split, false); + + uiItemL(col[0], IFACE_("Color"), ICON_NONE); + uiItemR(col[1], ptr, "color", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Width"), ICON_NONE); + uiItemR(col[1], ptr, "width", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Height"), ICON_NONE); + uiItemR(col[1], ptr, "height", 0, "", ICON_NONE); + + uiItemL(col[0], "", ICON_NONE); + uiItemR(col[1], ptr, "alpha", 0, NULL, ICON_NONE); + + uiItemL(col[0], IFACE_("Generated Type"), ICON_NONE); + uiItemR(col[1], ptr, "generated_type", 0, "", ICON_NONE); + + uiItemL(col[0], "", ICON_NONE); + uiItemR(col[1], ptr, "float", 0, NULL, ICON_NONE); +} + +static void def_fill_tile(StructOrFunctionRNA *srna) +{ + PropertyRNA *prop; + static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + prop = RNA_def_float_color( + srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); + RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); + RNA_def_property_float_array_default(prop, default_color); + RNA_def_enum(srna, + "generated_type", + rna_enum_image_generated_type_items, + IMA_GENTYPE_BLANK, + "Generated Type", + "Fill the image with a grid for UV map testing"); + prop = RNA_def_int(srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + prop = RNA_def_int(srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + + /* Only needed when filling the first tile. */ + RNA_def_boolean( + srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth"); + RNA_def_boolean(srna, "alpha", 1, "Alpha", "Create an image with an alpha channel"); +} + +static bool tile_add_poll(bContext *C) { Image *ima = CTX_data_edit_image(C); - return (ima != NULL && ima->source == IMA_SRC_TILED); + return (ima != NULL && ima->source == IMA_SRC_TILED && BKE_image_has_ibuf(ima, NULL)); } static int tile_add_exec(bContext *C, wmOperator *op) { Image *ima = CTX_data_edit_image(C); - int tile_number = RNA_int_get(op->ptr, "number"); - + int start_tile = RNA_int_get(op->ptr, "number"); + int end_tile = min_ii(start_tile + RNA_int_get(op->ptr, "count"), IMA_UDIM_MAX); + bool fill_tile = RNA_boolean_get(op->ptr, "fill"); char *label = RNA_string_get_alloc(op->ptr, "label", NULL, 0); - ImageTile *tile = BKE_image_add_tile(ima, tile_number, label); + bool created_tile = false; + for (int tile_number = start_tile; tile_number < end_tile; tile_number++) { + ImageTile *tile = BKE_image_add_tile(ima, tile_number, label); + + if (tile != NULL) { + ima->active_tile_index = BLI_findindex(&ima->tiles, tile); + + if (fill_tile) { + do_fill_tile(op->ptr, ima, tile); + } + + created_tile = true; + } + } MEM_freeN(label); - if (tile == NULL) { + if (!created_tile) { return OPERATOR_CANCELLED; } - ima->active_tile_index = BLI_findindex(&ima->tiles, tile); - WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); - return OPERATOR_FINISHED; } @@ -4256,9 +4357,10 @@ static int tile_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev } RNA_int_set(op->ptr, "number", next_number); + RNA_int_set(op->ptr, "count", 1); RNA_string_set(op->ptr, "label", ""); - return WM_operator_props_dialog_popup(C, op, 5 * UI_UNIT_X, 5 * UI_UNIT_Y); + return WM_operator_props_dialog_popup(C, op, 10 * UI_UNIT_X, 5 * UI_UNIT_Y); } static void tile_add_draw(bContext *UNUSED(C), wmOperator *op) @@ -4276,8 +4378,17 @@ static void tile_add_draw(bContext *UNUSED(C), wmOperator *op) uiItemL(col[0], IFACE_("Number"), ICON_NONE); uiItemR(col[1], &ptr, "number", 0, "", ICON_NONE); + uiItemL(col[0], IFACE_("Count"), ICON_NONE); + uiItemR(col[1], &ptr, "count", 0, "", ICON_NONE); + uiItemL(col[0], IFACE_("Label"), ICON_NONE); uiItemR(col[1], &ptr, "label", 0, "", ICON_NONE); + + uiItemR(layout, &ptr, "fill", 0, NULL, ICON_NONE); + + if (RNA_boolean_get(&ptr, "fill")) { + draw_fill_tile(&ptr, layout); + } } void IMAGE_OT_tile_add(wmOperatorType *ot) @@ -4288,7 +4399,7 @@ void IMAGE_OT_tile_add(wmOperatorType *ot) ot->idname = "IMAGE_OT_tile_add"; /* api callbacks */ - ot->poll = tile_poll; + ot->poll = tile_add_poll; ot->exec = tile_add_exec; ot->invoke = tile_add_invoke; ot->ui = tile_add_draw; @@ -4298,7 +4409,10 @@ void IMAGE_OT_tile_add(wmOperatorType *ot) RNA_def_int( ot->srna, "number", 1002, 1001, INT_MAX, "Number", "UDIM number of the tile", 1001, 1099); + RNA_def_int(ot->srna, "count", 1, 1, INT_MAX, "Count", "How many tiles to add", 1, 1000); RNA_def_string(ot->srna, "label", NULL, 0, "Label", "Optional tile label"); + RNA_def_boolean(ot->srna, "fill", true, "Fill", "Fill new tile with a generated image"); + def_fill_tile(ot->srna); } /* ********************* Remove tile operator ****************** */ @@ -4344,20 +4458,23 @@ void IMAGE_OT_tile_remove(wmOperatorType *ot) /* ********************* Fill tile operator ****************** */ -static int tile_fill_exec(bContext *C, wmOperator *op) +static bool tile_fill_poll(bContext *C) { Image *ima = CTX_data_edit_image(C); - float color[4]; - RNA_float_get_array(op->ptr, "color", color); - int gen_type = RNA_enum_get(op->ptr, "generated_type"); - int width = RNA_int_get(op->ptr, "width"); - int height = RNA_int_get(op->ptr, "height"); - bool is_float = RNA_boolean_get(op->ptr, "float"); - int planes = RNA_boolean_get(op->ptr, "alpha") ? 32 : 24; + if (ima != NULL && ima->source == IMA_SRC_TILED) { + /* Filling secondary tiles is only allowed if the primary tile exists. */ + return (ima->active_tile_index == 0) || BKE_image_has_ibuf(ima, NULL); + } + return false; +} + +static int tile_fill_exec(bContext *C, wmOperator *op) +{ + Image *ima = CTX_data_edit_image(C); ImageTile *tile = BLI_findlink(&ima->tiles, ima->active_tile_index); - if (!BKE_image_fill_tile(ima, tile, width, height, color, gen_type, planes, is_float)) { + if (!do_fill_tile(op->ptr, ima, tile)) { return OPERATOR_CANCELLED; } @@ -4385,46 +4502,21 @@ static int tile_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e static void tile_fill_draw(bContext *UNUSED(C), wmOperator *op) { - uiLayout *split, *col[2]; - uiLayout *layout = op->layout; PointerRNA ptr; - RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); - /* copy of WM_operator_props_dialog_popup() layout */ - - split = uiLayoutSplit(layout, 0.5f, false); - col[0] = uiLayoutColumn(split, false); - col[1] = uiLayoutColumn(split, false); - - uiItemL(col[0], IFACE_("Color"), ICON_NONE); - uiItemR(col[1], &ptr, "color", 0, "", ICON_NONE); - - uiItemL(col[0], IFACE_("Width"), ICON_NONE); - uiItemR(col[1], &ptr, "width", 0, "", ICON_NONE); - - uiItemL(col[0], IFACE_("Height"), ICON_NONE); - uiItemR(col[1], &ptr, "height", 0, "", ICON_NONE); - - uiItemL(col[0], "", ICON_NONE); - uiItemR(col[1], &ptr, "alpha", 0, NULL, ICON_NONE); - - uiItemL(col[0], IFACE_("Generated Type"), ICON_NONE); - uiItemR(col[1], &ptr, "generated_type", 0, "", ICON_NONE); - - uiItemL(col[0], "", ICON_NONE); - uiItemR(col[1], &ptr, "float", 0, NULL, ICON_NONE); + draw_fill_tile(&ptr, op->layout); } void IMAGE_OT_tile_fill(wmOperatorType *ot) { /* identifiers */ - ot->name = "Fill tile"; + ot->name = "Fill Tile"; ot->description = "Fill the current tile with a generated image"; ot->idname = "IMAGE_OT_tile_fill"; /* api callbacks */ - ot->poll = tile_poll; + ot->poll = tile_fill_poll; ot->exec = tile_fill_exec; ot->invoke = tile_fill_invoke; ot->ui = tile_fill_draw; @@ -4432,25 +4524,5 @@ void IMAGE_OT_tile_fill(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - PropertyRNA *prop; - static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - prop = RNA_def_float_color( - ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); - RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); - RNA_def_property_float_array_default(prop, default_color); - RNA_def_enum(ot->srna, - "generated_type", - rna_enum_image_generated_type_items, - IMA_GENTYPE_BLANK, - "Generated Type", - "Fill the image with a grid for UV map testing"); - prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); - prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); - - /* Only needed when filling the first tile. */ - RNA_def_boolean( - ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth"); - RNA_def_boolean(ot->srna, "alpha", 1, "Alpha", "Create an image with an alpha channel"); + def_fill_tile(ot->srna); } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index f30c0e97cab..2f93be8ae38 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -464,14 +464,71 @@ static void IMAGE_GGT_gizmo2d(wmGizmoGroupType *gzgt) gzgt->name = "UV Transform Gizmo"; gzgt->idname = "IMAGE_GGT_gizmo2d"; + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_IMAGE; + gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; + + gzgt->poll = ED_widgetgroup_gizmo2d_xform_poll; + gzgt->setup = ED_widgetgroup_gizmo2d_xform_setup; + gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; + gzgt->refresh = ED_widgetgroup_gizmo2d_xform_refresh; + gzgt->draw_prepare = ED_widgetgroup_gizmo2d_xform_draw_prepare; +} + +static void IMAGE_GGT_gizmo2d_translate(wmGizmoGroupType *gzgt) +{ + gzgt->name = "UV Translate Gizmo"; + gzgt->idname = "IMAGE_GGT_gizmo2d_translate"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_IMAGE; + gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; + + gzgt->poll = ED_widgetgroup_gizmo2d_xform_poll; + gzgt->setup = ED_widgetgroup_gizmo2d_xform_setup_no_cage; + gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; + gzgt->refresh = ED_widgetgroup_gizmo2d_xform_refresh; + gzgt->draw_prepare = ED_widgetgroup_gizmo2d_xform_draw_prepare; +} + +static void IMAGE_GGT_gizmo2d_resize(wmGizmoGroupType *gzgt) +{ + gzgt->name = "UV Transform Gizmo Resize"; + gzgt->idname = "IMAGE_GGT_gizmo2d_resize"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_IMAGE; + gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; + + gzgt->poll = ED_widgetgroup_gizmo2d_resize_poll; + gzgt->setup = ED_widgetgroup_gizmo2d_resize_setup; + gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; + gzgt->refresh = ED_widgetgroup_gizmo2d_resize_refresh; + gzgt->draw_prepare = ED_widgetgroup_gizmo2d_resize_draw_prepare; +} + +static void IMAGE_GGT_gizmo2d_rotate(wmGizmoGroupType *gzgt) +{ + gzgt->name = "UV Transform Gizmo Resize"; + gzgt->idname = "IMAGE_GGT_gizmo2d_rotate"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + gzgt->gzmap_params.spaceid = SPACE_IMAGE; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; - gzgt->poll = ED_widgetgroup_gizmo2d_poll; - gzgt->setup = ED_widgetgroup_gizmo2d_setup; + gzgt->poll = ED_widgetgroup_gizmo2d_rotate_poll; + gzgt->setup = ED_widgetgroup_gizmo2d_rotate_setup; gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; - gzgt->refresh = ED_widgetgroup_gizmo2d_refresh; - gzgt->draw_prepare = ED_widgetgroup_gizmo2d_draw_prepare; + gzgt->refresh = ED_widgetgroup_gizmo2d_rotate_refresh; + gzgt->draw_prepare = ED_widgetgroup_gizmo2d_rotate_draw_prepare; } static void IMAGE_GGT_navigate(wmGizmoGroupType *gzgt) @@ -485,6 +542,9 @@ static void image_widgets(void) &(const struct wmGizmoMapType_Params){SPACE_IMAGE, RGN_TYPE_WINDOW}); WM_gizmogrouptype_append(IMAGE_GGT_gizmo2d); + WM_gizmogrouptype_append(IMAGE_GGT_gizmo2d_translate); + WM_gizmogrouptype_append(IMAGE_GGT_gizmo2d_resize); + WM_gizmogrouptype_append(IMAGE_GGT_gizmo2d_rotate); WM_gizmogrouptype_append_and_link(gzmap_type, IMAGE_GGT_navigate); } diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 486be952b2f..2c2989a284d 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -2125,14 +2125,14 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case eModifierType_Surface: data.icon = ICON_MOD_PHYSICS; break; - case eModifierType_Fluidsim: + case eModifierType_Fluidsim: /* deprecated, old fluid modifier */ data.icon = ICON_MOD_FLUIDSIM; break; case eModifierType_Multires: data.icon = ICON_MOD_MULTIRES; break; - case eModifierType_Smoke: - data.icon = ICON_MOD_SMOKE; + case eModifierType_Fluid: + data.icon = ICON_MOD_FLUID; break; case eModifierType_Solidify: data.icon = ICON_MOD_SOLIDIFY; diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 34bbf3a2a30..7b59b056264 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -1311,6 +1311,10 @@ static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op)) outliner_show_active(so, ar, te, id); } + /* Also open back from the active_element (only done for the first found occurrence of ID + * though). */ + outliner_show_active(so, ar, active_element, id); + /* Center view on first element found */ int size_y = BLI_rcti_size_y(&v2d->mask) + 1; int ytop = (active_element->ys + (size_y / 2)); @@ -2173,9 +2177,15 @@ void OUTLINER_OT_keyingset_remove_selected(wmOperatorType *ot) static bool ed_operator_outliner_id_orphans_active(bContext *C) { ScrArea *sa = CTX_wm_area(C); - if ((sa) && (sa->spacetype == SPACE_OUTLINER)) { - SpaceOutliner *so = CTX_wm_space_outliner(C); - return (so->outlinevis == SO_ID_ORPHANS); + if (sa != NULL) { + if (sa->spacetype == SPACE_TOPBAR) { + return true; + } + + if (sa->spacetype == SPACE_OUTLINER) { + SpaceOutliner *so = CTX_wm_space_outliner(C); + return (so->outlinevis == SO_ID_ORPHANS); + } } return 0; } @@ -2242,6 +2252,7 @@ static int outliner_orphans_purge_invoke(bContext *C, wmOperator *op, const wmEv static int outliner_orphans_purge_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); + ScrArea *sa = CTX_wm_area(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); int num_tagged[INDEX_ID_MAX] = {0}; @@ -2268,7 +2279,9 @@ static int outliner_orphans_purge_exec(bContext *C, wmOperator *op) * outliner several mouse events can be handled in one cycle without * handling notifiers/redraw which leads to deleting the same object twice. * cleanup tree here to prevent such cases. */ - outliner_cleanup_tree(soops); + if ((sa != NULL) && (sa->spacetype == SPACE_OUTLINER)) { + outliner_cleanup_tree(soops); + } DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL); diff --git a/source/blender/editors/space_userpref/space_userpref.c b/source/blender/editors/space_userpref/space_userpref.c index a42ab048907..731c8a3028e 100644 --- a/source/blender/editors/space_userpref/space_userpref.c +++ b/source/blender/editors/space_userpref/space_userpref.c @@ -35,6 +35,9 @@ #include "ED_screen.h" #include "ED_space_api.h" +#include "RNA_access.h" +#include "RNA_enum_types.h" + #include "WM_api.h" #include "WM_types.h" @@ -119,9 +122,27 @@ static void userpref_main_region_init(wmWindowManager *wm, ARegion *ar) ED_region_panels_init(wm, ar); } -static void userpref_main_region_draw(const bContext *C, ARegion *ar) +static void userpref_main_region_layout(const bContext *C, ARegion *ar) { - ED_region_panels_ex(C, ar, NULL, U.space_data.section_active, true); + char id_lower[64]; + const char *contexts[2] = {id_lower, NULL}; + + /* Avoid duplicating identifiers, use existing RNA enum. */ + { + const EnumPropertyItem *items = rna_enum_preference_section_items; + int i = RNA_enum_from_value(items, U.space_data.section_active); + /* File is from the future. */ + if (i == -1) { + i = 0; + } + const char *id = items[i].identifier; + BLI_assert(strlen(id) < sizeof(id_lower)); + STRNCPY(id_lower, id); + BLI_str_tolower_ascii(id_lower, strlen(id_lower)); + } + + ED_region_panels_layout_ex( + C, ar, &ar->type->paneltypes, contexts, U.space_data.section_active, true, NULL); } static void userpref_operatortypes(void) @@ -225,7 +246,8 @@ void ED_spacetype_userpref(void) art = MEM_callocN(sizeof(ARegionType), "spacetype userpref region"); art->regionid = RGN_TYPE_WINDOW; art->init = userpref_main_region_init; - art->draw = userpref_main_region_draw; + art->layout = userpref_main_region_layout; + art->draw = ED_region_panels_draw; art->listener = userpref_main_region_listener; art->keymapflag = ED_KEYMAP_UI; diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index 4542845f90c..193be7567bc 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -32,7 +32,7 @@ set(INC ../../windowmanager ../../../../intern/glew-mx ../../../../intern/guardedalloc - ../../../../intern/smoke/extern + ../../../../intern/mantaflow/extern # dna_type_offsets.h ${CMAKE_CURRENT_BINARY_DIR}/../../makesdna/intern @@ -99,8 +99,8 @@ if(WITH_LANPR) add_definitions(-DWITH_LANPR) endif() -if(WITH_MOD_SMOKE) - add_definitions(-DWITH_SMOKE) +if(WITH_MOD_FLUID) + add_definitions(-DWITH_FLUID) endif() blender_add_lib(bf_editor_space_view3d "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c index d6d3a3dc563..504b10888e8 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c @@ -466,7 +466,7 @@ static int gizmo_axis_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mv { float point_local[2] = {UNPACK2(mval)}; sub_v2_v2(point_local, gz->matrix_basis[3]); - mul_v2_fl(point_local, 1.0f / (gz->scale_basis * UI_DPI_FAC)); + mul_v2_fl(point_local, 1.0f / gz->scale_final); const float len_sq = len_squared_v2(point_local); if (len_sq > 1.0) { diff --git a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c index 848accb8534..8cd5ed7a478 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c @@ -70,19 +70,17 @@ static bool WIDGETGROUP_tool_generic_poll(const bContext *C, wmGizmoGroupType *g static wmGizmo *tool_generic_create_gizmo(const bContext *C, wmGizmoGroup *gzgroup) { - wmGizmo *gz; + wmGizmo *gz = WM_gizmo_new("GIZMO_GT_button_2d", gzgroup, NULL); + gz->flag |= WM_GIZMO_OPERATOR_TOOL_INIT; - if (gzgroup->type->idname == handle_normal_id) { - gz = WM_gizmo_new("GIZMO_GT_button_2d", gzgroup, NULL); - - UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, gz->color); - UI_GetThemeColor3fv(TH_GIZMO_HI, gz->color_hi); + UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, gz->color); + UI_GetThemeColor3fv(TH_GIZMO_HI, gz->color_hi); - unit_m4(gz->matrix_offset); + unit_m4(gz->matrix_offset); - PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon"); - RNA_property_enum_set(gz->ptr, prop, ICON_NONE); + RNA_enum_set(gz->ptr, "icon", ICON_NONE); + if (gzgroup->type->idname == handle_normal_id) { gz->scale_basis = 0.12f; gz->matrix_offset[3][2] -= 12.0; RNA_enum_set(gz->ptr, @@ -91,17 +89,8 @@ static wmGizmo *tool_generic_create_gizmo(const bContext *C, wmGizmoGroup *gzgro ED_GIZMO_BUTTON_SHOW_OUTLINE)); } else { - gz = WM_gizmo_new("GIZMO_GT_button_2d", gzgroup, NULL); - - UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, gz->color); - UI_GetThemeColor3fv(TH_GIZMO_HI, gz->color_hi); - - unit_m4(gz->matrix_offset); gz->scale_basis = 0.16f * 3; - PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon"); - RNA_property_enum_set(gz->ptr, prop, ICON_NONE); - RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_BUTTON_SHOW_BACKDROP); /* Make the center low alpha. */ @@ -206,7 +195,8 @@ void VIEW3D_GGT_tool_generic_handle_normal(wmGizmoGroupType *gzgt) gzgt->name = "Generic Tool Widget Normal"; gzgt->idname = handle_normal_id; - gzgt->flag |= (WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP); + gzgt->flag |= (WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; @@ -222,6 +212,8 @@ void VIEW3D_GGT_tool_generic_handle_free(wmGizmoGroupType *gzgt) gzgt->name = "Generic Tool Widget Free"; gzgt->idname = handle_free_id; + /* Don't use 'WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK' here since this style of gizmo + * is better suited to being activated immediately. */ gzgt->flag |= (WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP); gzgt->gzmap_params.spaceid = SPACE_VIEW3D; diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index 5853574dced..6f39a8c3b9c 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC transform_convert_sculpt.c transform_convert_sequencer.c transform_convert_tracking.c + transform_draw_cursors.c transform_generics.c transform_gizmo_2d.c transform_gizmo_3d.c @@ -71,6 +72,7 @@ set(SRC transform.h transform_convert.h + transform_draw_cursors.h transform_snap.h ) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 42adf1ee456..7112444655b 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -95,6 +95,7 @@ #include "transform.h" #include "transform_convert.h" +#include "transform_draw_cursors.h" #include "transform_snap.h" /* Disabling, since when you type you know what you are doing, @@ -1684,285 +1685,6 @@ bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], floa return success; } -typedef enum { - UP, - DOWN, - LEFT, - RIGHT, -} ArrowDirection; - -#define POS_INDEX 0 -/* NOTE: this --^ is a bit hackish, but simplifies GPUVertFormat usage among functions - * private to this file - merwin - */ - -static void drawArrow(ArrowDirection d, short offset, short length, short size) -{ - immBegin(GPU_PRIM_LINES, 6); - - switch (d) { - case LEFT: - offset = -offset; - length = -length; - size = -size; - ATTR_FALLTHROUGH; - case RIGHT: - immVertex2f(POS_INDEX, offset, 0); - immVertex2f(POS_INDEX, offset + length, 0); - immVertex2f(POS_INDEX, offset + length, 0); - immVertex2f(POS_INDEX, offset + length - size, -size); - immVertex2f(POS_INDEX, offset + length, 0); - immVertex2f(POS_INDEX, offset + length - size, size); - break; - - case DOWN: - offset = -offset; - length = -length; - size = -size; - ATTR_FALLTHROUGH; - case UP: - immVertex2f(POS_INDEX, 0, offset); - immVertex2f(POS_INDEX, 0, offset + length); - immVertex2f(POS_INDEX, 0, offset + length); - immVertex2f(POS_INDEX, -size, offset + length - size); - immVertex2f(POS_INDEX, 0, offset + length); - immVertex2f(POS_INDEX, size, offset + length - size); - break; - } - - immEnd(); -} - -static void drawArrowHead(ArrowDirection d, short size) -{ - immBegin(GPU_PRIM_LINES, 4); - - switch (d) { - case LEFT: - size = -size; - ATTR_FALLTHROUGH; - case RIGHT: - immVertex2f(POS_INDEX, 0, 0); - immVertex2f(POS_INDEX, -size, -size); - immVertex2f(POS_INDEX, 0, 0); - immVertex2f(POS_INDEX, -size, size); - break; - - case DOWN: - size = -size; - ATTR_FALLTHROUGH; - case UP: - immVertex2f(POS_INDEX, 0, 0); - immVertex2f(POS_INDEX, -size, -size); - immVertex2f(POS_INDEX, 0, 0); - immVertex2f(POS_INDEX, size, -size); - break; - } - - immEnd(); -} - -static void drawArc(float size, float angle_start, float angle_end, int segments) -{ - float delta = (angle_end - angle_start) / segments; - float angle; - int a; - - immBegin(GPU_PRIM_LINE_STRIP, segments + 1); - - for (angle = angle_start, a = 0; a < segments; angle += delta, a++) { - immVertex2f(POS_INDEX, cosf(angle) * size, sinf(angle) * size); - } - immVertex2f(POS_INDEX, cosf(angle_end) * size, sinf(angle_end) * size); - - immEnd(); -} - -static bool helpline_poll(bContext *C) -{ - ARegion *ar = CTX_wm_region(C); - - if (ar && ar->regiontype == RGN_TYPE_WINDOW) { - return 1; - } - return 0; -} - -static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata) -{ - TransInfo *t = (TransInfo *)customdata; - - if (t->helpline != HLP_NONE) { - float cent[2]; - const float mval[3] = { - x, - y, - 0.0f, - }; - float tmval[2] = { - (float)t->mval[0], - (float)t->mval[1], - }; - - projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO); - /* Offset the values for the area region. */ - const float offset[2] = { - t->ar->winrct.xmin, - t->ar->winrct.ymin, - }; - - for (int i = 0; i < 2; i++) { - cent[i] += offset[i]; - tmval[i] += offset[i]; - } - - GPU_matrix_push(); - - /* Dashed lines first. */ - if (ELEM(t->helpline, HLP_SPRING, HLP_ANGLE)) { - const uint shdr_pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - UNUSED_VARS_NDEBUG(shdr_pos); /* silence warning */ - BLI_assert(shdr_pos == POS_INDEX); - - GPU_line_width(1.0f); - - immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); - - float viewport_size[4]; - GPU_viewport_size_get_f(viewport_size); - immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); - - immUniform1i("colors_len", 0); /* "simple" mode */ - immUniformThemeColor(TH_VIEW_OVERLAY); - immUniform1f("dash_width", 6.0f); - immUniform1f("dash_factor", 0.5f); - - immBegin(GPU_PRIM_LINES, 2); - immVertex2fv(POS_INDEX, cent); - immVertex2f(POS_INDEX, tmval[0], tmval[1]); - immEnd(); - - immUnbindProgram(); - } - - /* And now, solid lines. */ - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - UNUSED_VARS_NDEBUG(pos); /* silence warning */ - BLI_assert(pos == POS_INDEX); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - switch (t->helpline) { - case HLP_SPRING: - immUniformThemeColor(TH_VIEW_OVERLAY); - - GPU_matrix_translate_3fv(mval); - GPU_matrix_rotate_axis(-RAD2DEGF(atan2f(cent[0] - tmval[0], cent[1] - tmval[1])), 'Z'); - - GPU_line_width(3.0f); - drawArrow(UP, 5, 10, 5); - drawArrow(DOWN, 5, 10, 5); - break; - case HLP_HARROW: - immUniformThemeColor(TH_VIEW_OVERLAY); - GPU_matrix_translate_3fv(mval); - - GPU_line_width(3.0f); - drawArrow(RIGHT, 5, 10, 5); - drawArrow(LEFT, 5, 10, 5); - break; - case HLP_VARROW: - immUniformThemeColor(TH_VIEW_OVERLAY); - - GPU_matrix_translate_3fv(mval); - - GPU_line_width(3.0f); - drawArrow(UP, 5, 10, 5); - drawArrow(DOWN, 5, 10, 5); - break; - case HLP_CARROW: { - /* Draw arrow based on direction defined by custom-points. */ - immUniformThemeColor(TH_VIEW_OVERLAY); - - GPU_matrix_translate_3fv(mval); - - GPU_line_width(3.0f); - - const int *data = t->mouse.data; - const float dx = data[2] - data[0], dy = data[3] - data[1]; - const float angle = -atan2f(dx, dy); - - GPU_matrix_push(); - - GPU_matrix_rotate_axis(RAD2DEGF(angle), 'Z'); - - drawArrow(UP, 5, 10, 5); - drawArrow(DOWN, 5, 10, 5); - - GPU_matrix_pop(); - break; - } - case HLP_ANGLE: { - float dx = tmval[0] - cent[0], dy = tmval[1] - cent[1]; - float angle = atan2f(dy, dx); - float dist = hypotf(dx, dy); - float delta_angle = min_ff(15.0f / dist, (float)M_PI / 4.0f); - float spacing_angle = min_ff(5.0f / dist, (float)M_PI / 12.0f); - - immUniformThemeColor(TH_VIEW_OVERLAY); - - GPU_matrix_translate_3f(cent[0] - tmval[0] + mval[0], cent[1] - tmval[1] + mval[1], 0); - - GPU_line_width(3.0f); - drawArc(dist, angle - delta_angle, angle - spacing_angle, 10); - drawArc(dist, angle + spacing_angle, angle + delta_angle, 10); - - GPU_matrix_push(); - - GPU_matrix_translate_3f( - cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0); - GPU_matrix_rotate_axis(RAD2DEGF(angle - delta_angle), 'Z'); - - drawArrowHead(DOWN, 5); - - GPU_matrix_pop(); - - GPU_matrix_translate_3f( - cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0); - GPU_matrix_rotate_axis(RAD2DEGF(angle + delta_angle), 'Z'); - - drawArrowHead(UP, 5); - break; - } - case HLP_TRACKBALL: { - unsigned char col[3], col2[3]; - UI_GetThemeColor3ubv(TH_GRID, col); - - GPU_matrix_translate_3fv(mval); - - GPU_line_width(3.0f); - - UI_make_axis_color(col, col2, 'X'); - immUniformColor3ubv(col2); - - drawArrow(RIGHT, 5, 10, 5); - drawArrow(LEFT, 5, 10, 5); - - UI_make_axis_color(col, col2, 'Y'); - immUniformColor3ubv(col2); - - drawArrow(UP, 5, 10, 5); - drawArrow(DOWN, 5, 10, 5); - break; - } - } - - immUnbindProgram(); - GPU_matrix_pop(); - } -} - static bool transinfo_show_overlay(const struct bContext *C, TransInfo *t, ARegion *ar) { /* Don't show overlays when not the active view and when overlay is disabled: T57139 */ @@ -2380,38 +2102,62 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW); t->draw_handle_pixel = ED_region_draw_cb_activate( t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL); - t->draw_handle_cursor = WM_paint_cursor_activate( - CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t); + t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), + SPACE_TYPE_ANY, + RGN_TYPE_ANY, + transform_draw_cursor_poll, + transform_draw_cursor_draw, + t); } else if (t->spacetype == SPACE_IMAGE) { t->draw_handle_view = ED_region_draw_cb_activate( t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW); - t->draw_handle_cursor = WM_paint_cursor_activate( - CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t); + t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), + SPACE_TYPE_ANY, + RGN_TYPE_ANY, + transform_draw_cursor_poll, + transform_draw_cursor_draw, + t); } else if (t->spacetype == SPACE_CLIP) { t->draw_handle_view = ED_region_draw_cb_activate( t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW); - t->draw_handle_cursor = WM_paint_cursor_activate( - CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t); + t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), + SPACE_TYPE_ANY, + RGN_TYPE_ANY, + transform_draw_cursor_poll, + transform_draw_cursor_draw, + t); } else if (t->spacetype == SPACE_NODE) { t->draw_handle_view = ED_region_draw_cb_activate( t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW); - t->draw_handle_cursor = WM_paint_cursor_activate( - CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t); + t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), + SPACE_TYPE_ANY, + RGN_TYPE_ANY, + transform_draw_cursor_poll, + transform_draw_cursor_draw, + t); } else if (t->spacetype == SPACE_GRAPH) { t->draw_handle_view = ED_region_draw_cb_activate( t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW); - t->draw_handle_cursor = WM_paint_cursor_activate( - CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t); + t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), + SPACE_TYPE_ANY, + RGN_TYPE_ANY, + transform_draw_cursor_poll, + transform_draw_cursor_draw, + t); } else if (t->spacetype == SPACE_ACTION) { t->draw_handle_view = ED_region_draw_cb_activate( t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW); - t->draw_handle_cursor = WM_paint_cursor_activate( - CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t); + t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), + SPACE_TYPE_ANY, + RGN_TYPE_ANY, + transform_draw_cursor_poll, + transform_draw_cursor_draw, + t); } createTransData(C, t); // make TransData structs from selection diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 4b529765ab0..95506918d98 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -649,7 +649,7 @@ typedef struct TransInfo { short index; short *types[2]; /* this gets used when custom_orientation is V3D_ORIENT_CUSTOM */ - TransformOrientation *custom; + struct TransformOrientation *custom; } orientation; /** backup from view3d, to restore on end. */ short gizmo_flag; diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index 331a3eea59f..16dfdd35c32 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -55,10 +55,8 @@ typedef struct TransDataObject { * Object to object data transform table. * Don't add these to transform data because we may want to include child objects * which aren't being transformed. - * - The key is object data #ID. - * - The value is #XFormObjectData_Extra. */ - struct GHash *obdata_in_obmode_map; + struct XFormObjectData_Container *xds; /** * Transform @@ -69,7 +67,6 @@ typedef struct TransDataObject { } TransDataObject; -static void trans_obdata_in_obmode_free_all(TransDataObject *tdo); static void trans_obchild_in_obmode_free_all(TransDataObject *tdo); static void freeTransObjectCustomData(TransInfo *t, @@ -80,7 +77,7 @@ static void freeTransObjectCustomData(TransInfo *t, custom_data->data = NULL; if (t->options & CTX_OBMODE_XFORM_OBDATA) { - trans_obdata_in_obmode_free_all(tdo); + ED_object_data_xform_container_destroy(tdo->xds); } if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) { @@ -98,81 +95,18 @@ static void freeTransObjectCustomData(TransInfo *t, * We need this to be detached from transform data because, * unlike transforming regular objects, we need to transform the children. * + * Nearly all of the logic here is in the 'ED_object_data_xform_container_*' API. * \{ */ -struct XFormObjectData_Extra { - Object *ob; - float obmat_orig[4][4]; - struct XFormObjectData *xod; -}; - -static void trans_obdata_in_obmode_ensure_object(TransDataObject *tdo, Object *ob) -{ - if (tdo->obdata_in_obmode_map == NULL) { - tdo->obdata_in_obmode_map = BLI_ghash_ptr_new(__func__); - } - - void **xf_p; - if (!BLI_ghash_ensure_p(tdo->obdata_in_obmode_map, ob->data, &xf_p)) { - struct XFormObjectData_Extra *xf = MEM_mallocN(sizeof(*xf), __func__); - copy_m4_m4(xf->obmat_orig, ob->obmat); - xf->ob = ob; - /* Result may be NULL, that's OK. */ - xf->xod = ED_object_data_xform_create(ob->data); - *xf_p = xf; - } -} - void trans_obdata_in_obmode_update_all(TransInfo *t) { TransDataObject *tdo = t->custom.type.data; - if (tdo->obdata_in_obmode_map == NULL) { + if (tdo->xds == NULL) { return; } struct Main *bmain = CTX_data_main(t->context); - BKE_scene_graph_evaluated_ensure(t->depsgraph, bmain); - - GHashIterator gh_iter; - GHASH_ITER (gh_iter, tdo->obdata_in_obmode_map) { - ID *id = BLI_ghashIterator_getKey(&gh_iter); - struct XFormObjectData_Extra *xf = BLI_ghashIterator_getValue(&gh_iter); - if (xf->xod == NULL) { - continue; - } - - Object *ob_eval = DEG_get_evaluated_object(t->depsgraph, xf->ob); - float imat[4][4], dmat[4][4]; - invert_m4_m4(imat, xf->obmat_orig); - mul_m4_m4m4(dmat, imat, ob_eval->obmat); - invert_m4(dmat); - - ED_object_data_xform_by_mat4(xf->xod, dmat); - if (xf->ob->type == OB_ARMATURE) { - /* TODO: none of the current flags properly update armatures, needs investigation. */ - DEG_id_tag_update(id, 0); - } - else { - DEG_id_tag_update(id, ID_RECALC_GEOMETRY); - } - } -} - -/** Callback for #GHash free. */ -static void trans_obdata_in_obmode_free_elem(void *xf_p) -{ - struct XFormObjectData_Extra *xf = xf_p; - if (xf->xod) { - ED_object_data_xform_destroy(xf->xod); - } - MEM_freeN(xf); -} - -static void trans_obdata_in_obmode_free_all(TransDataObject *tdo) -{ - if (tdo->obdata_in_obmode_map != NULL) { - BLI_ghash_free(tdo->obdata_in_obmode_map, NULL, trans_obdata_in_obmode_free_elem); - } + ED_object_data_xform_container_update_all(tdo->xds, bmain, t->depsgraph); } /** \} */ @@ -697,6 +631,10 @@ void createTransObject(bContext *C, TransInfo *t) t->custom.type.data = tdo; t->custom.type.free_cb = freeTransObjectCustomData; + if (t->options & CTX_OBMODE_XFORM_OBDATA) { + tdo->xds = ED_object_data_xform_container_create(); + } + CTX_DATA_BEGIN (C, Base *, base, selected_bases) { Object *ob = base->object; @@ -729,7 +667,7 @@ void createTransObject(bContext *C, TransInfo *t) if (t->options & CTX_OBMODE_XFORM_OBDATA) { if ((td->flag & TD_SKIP) == 0) { - trans_obdata_in_obmode_ensure_object(tdo, ob); + ED_object_data_xform_container_item_ensure(tdo->xds, ob); } } @@ -797,7 +735,7 @@ void createTransObject(bContext *C, TransInfo *t) ob_parent = ob_parent->parent; } if (parent_in_transdata) { - trans_obdata_in_obmode_ensure_object(tdo, ob); + ED_object_data_xform_container_item_ensure(tdo->xds, ob); } } } diff --git a/source/blender/editors/transform/transform_draw.c b/source/blender/editors/transform/transform_draw.c new file mode 100644 index 00000000000..e44442b7e49 --- /dev/null +++ b/source/blender/editors/transform/transform_draw.c @@ -0,0 +1,114 @@ + +/* -------------------------------------------------------------------- */ +/** \name Auto-Key (Pixel Space) + * \{ */ + +/* just draw a little warning message in the top-right corner of the viewport + * to warn that autokeying is enabled */ +static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar) +{ + const char *printable = IFACE_("Auto Keying On"); + float printable_size[2]; + int xco, yco; + + const rcti *rect = ED_region_visible_rect(ar); + + const int font_id = BLF_default(); + BLF_width_and_height( + font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); + + xco = (rect->xmax - U.widget_unit) - (int)printable_size[0]; + yco = (rect->ymax - U.widget_unit); + + /* warning text (to clarify meaning of overlays) + * - original color was red to match the icon, but that clashes badly with a less nasty border + */ + unsigned char color[3]; + UI_GetThemeColorShade3ubv(TH_TEXT_HI, -50, color); + BLF_color3ubv(font_id, color); +#ifdef WITH_INTERNATIONAL + BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); +#else + BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); +#endif + + /* autokey recording icon... */ + GPU_blend_set_func_separate( + GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(true); + + xco -= U.widget_unit; + yco -= (int)printable_size[1] / 2; + + UI_icon_draw(xco, yco, ICON_REC); + + GPU_blend(false); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Constraints (View Space) + * \{ */ + +/* called from drawview.c, as an extra per-window draw option */ +void drawPropCircle(const struct bContext *C, TransInfo *t) +{ + if (t->flag & T_PROP_EDIT) { + RegionView3D *rv3d = CTX_wm_region_view3d(C); + float tmat[4][4], imat[4][4]; + int depth_test_enabled; + + if (t->spacetype == SPACE_VIEW3D && rv3d != NULL) { + copy_m4_m4(tmat, rv3d->viewmat); + invert_m4_m4(imat, tmat); + } + else { + unit_m4(tmat); + unit_m4(imat); + } + + GPU_matrix_push(); + + if (t->spacetype == SPACE_VIEW3D) { + /* pass */ + } + else if (t->spacetype == SPACE_IMAGE) { + GPU_matrix_scale_2f(1.0f / t->aspect[0], 1.0f / t->aspect[1]); + } + else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_ACTION)) { + /* only scale y */ + rcti *mask = &t->ar->v2d.mask; + rctf *datamask = &t->ar->v2d.cur; + float xsize = BLI_rctf_size_x(datamask); + float ysize = BLI_rctf_size_y(datamask); + float xmask = BLI_rcti_size_x(mask); + float ymask = BLI_rcti_size_y(mask); + GPU_matrix_scale_2f(1.0f, (ysize / xsize) * (xmask / ymask)); + } + + depth_test_enabled = GPU_depth_test_enabled(); + if (depth_test_enabled) { + GPU_depth_test(false); + } + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformThemeColor(TH_GRID); + + GPU_logic_op_invert_set(true); + imm_drawcircball(t->center_global, t->prop_size, imat, pos); + GPU_logic_op_invert_set(false); + + immUnbindProgram(); + + if (depth_test_enabled) { + GPU_depth_test(true); + } + + GPU_matrix_pop(); + } +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_draw_cursors.c b/source/blender/editors/transform/transform_draw_cursors.c new file mode 100644 index 00000000000..dc2ebdca56a --- /dev/null +++ b/source/blender/editors/transform/transform_draw_cursors.c @@ -0,0 +1,344 @@ +/* + * 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. + */ + +/** \file + * \ingroup edtransform + */ + +#include "BLI_math.h" + +#include "GPU_immediate.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + +#include "BKE_context.h" + +#include "DNA_screen_types.h" +#include "DNA_userdef_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "transform.h" +#include "transform_draw_cursors.h" /* Own include. */ + +enum eArrowDirection { + UP, + DOWN, + LEFT, + RIGHT, +}; + +struct ArrowDims { + int offset; + int length; + int size; +}; + +#define POS_INDEX 0 +/* NOTE: this --^ is a bit hackish, but simplifies GPUVertFormat usage among functions + * private to this file - merwin + */ + +static void drawArrow(enum eArrowDirection dir, const struct ArrowDims *arrow_dims) +{ + int offset = arrow_dims->offset; + int length = arrow_dims->length; + int size = arrow_dims->size; + + immBegin(GPU_PRIM_LINES, 6); + + switch (dir) { + case LEFT: + offset = -offset; + length = -length; + size = -size; + ATTR_FALLTHROUGH; + case RIGHT: + immVertex2f(POS_INDEX, offset, 0); + immVertex2f(POS_INDEX, offset + length, 0); + immVertex2f(POS_INDEX, offset + length, 0); + immVertex2f(POS_INDEX, offset + length - size, -size); + immVertex2f(POS_INDEX, offset + length, 0); + immVertex2f(POS_INDEX, offset + length - size, size); + break; + + case DOWN: + offset = -offset; + length = -length; + size = -size; + ATTR_FALLTHROUGH; + case UP: + immVertex2f(POS_INDEX, 0, offset); + immVertex2f(POS_INDEX, 0, offset + length); + immVertex2f(POS_INDEX, 0, offset + length); + immVertex2f(POS_INDEX, -size, offset + length - size); + immVertex2f(POS_INDEX, 0, offset + length); + immVertex2f(POS_INDEX, size, offset + length - size); + break; + } + + immEnd(); +} + +static void drawArrowHead(enum eArrowDirection dir, int size) +{ + immBegin(GPU_PRIM_LINES, 4); + + switch (dir) { + case LEFT: + size = -size; + ATTR_FALLTHROUGH; + case RIGHT: + immVertex2f(POS_INDEX, 0, 0); + immVertex2f(POS_INDEX, -size, -size); + immVertex2f(POS_INDEX, 0, 0); + immVertex2f(POS_INDEX, -size, size); + break; + + case DOWN: + size = -size; + ATTR_FALLTHROUGH; + case UP: + immVertex2f(POS_INDEX, 0, 0); + immVertex2f(POS_INDEX, -size, -size); + immVertex2f(POS_INDEX, 0, 0); + immVertex2f(POS_INDEX, size, -size); + break; + } + + immEnd(); +} + +static void drawArc(float angle_start, float angle_end, int segments, float size) +{ + float delta = (angle_end - angle_start) / segments; + float angle; + int a; + + immBegin(GPU_PRIM_LINE_STRIP, segments + 1); + + for (angle = angle_start, a = 0; a < segments; angle += delta, a++) { + immVertex2f(POS_INDEX, cosf(angle) * size, sinf(angle) * size); + } + immVertex2f(POS_INDEX, cosf(angle_end) * size, sinf(angle_end) * size); + + immEnd(); +} + +/** + * Poll callback for cursor drawing: + * #WM_paint_cursor_activate + */ +bool transform_draw_cursor_poll(bContext *C) +{ + ARegion *ar = CTX_wm_region(C); + + if (ar && ar->regiontype == RGN_TYPE_WINDOW) { + return 1; + } + return 0; +} + +/** + * Cursor and help-line drawing, callback for: + * #WM_paint_cursor_activate + */ +void transform_draw_cursor_draw(bContext *UNUSED(C), int x, int y, void *customdata) +{ + TransInfo *t = (TransInfo *)customdata; + + if (t->helpline != HLP_NONE) { + struct ArrowDims arrow_dims = { + .offset = 5 * UI_DPI_FAC, + .length = 10 * UI_DPI_FAC, + .size = 5 * UI_DPI_FAC, + }; + + float cent[2]; + const float mval[3] = {x, y, 0.0f}; + float tmval[2] = { + (float)t->mval[0], + (float)t->mval[1], + }; + + projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO); + /* Offset the values for the area region. */ + const float offset[2] = { + t->ar->winrct.xmin, + t->ar->winrct.ymin, + }; + + for (int i = 0; i < 2; i++) { + cent[i] += offset[i]; + tmval[i] += offset[i]; + } + + GPU_line_smooth(true); + GPU_blend(true); + + GPU_matrix_push(); + + /* Dashed lines first. */ + if (ELEM(t->helpline, HLP_SPRING, HLP_ANGLE)) { + const uint shdr_pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + UNUSED_VARS_NDEBUG(shdr_pos); /* silence warning */ + BLI_assert(shdr_pos == POS_INDEX); + + GPU_line_width(1.0f); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("colors_len", 0); /* "simple" mode */ + immUniformThemeColor3(TH_VIEW_OVERLAY); + immUniform1f("dash_width", 6.0f * UI_DPI_FAC); + immUniform1f("dash_factor", 0.5f); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2fv(POS_INDEX, cent); + immVertex2f(POS_INDEX, tmval[0], tmval[1]); + immEnd(); + + immUnbindProgram(); + } + + /* And now, solid lines. */ + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + UNUSED_VARS_NDEBUG(pos); /* silence warning */ + BLI_assert(pos == POS_INDEX); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + switch (t->helpline) { + case HLP_SPRING: + immUniformThemeColor3(TH_VIEW_OVERLAY); + + GPU_matrix_translate_3fv(mval); + GPU_matrix_rotate_axis(-RAD2DEGF(atan2f(cent[0] - tmval[0], cent[1] - tmval[1])), 'Z'); + + GPU_line_width(3.0f); + drawArrow(UP, &arrow_dims); + drawArrow(DOWN, &arrow_dims); + break; + case HLP_HARROW: + immUniformThemeColor3(TH_VIEW_OVERLAY); + GPU_matrix_translate_3fv(mval); + + GPU_line_width(3.0f); + drawArrow(RIGHT, &arrow_dims); + drawArrow(LEFT, &arrow_dims); + break; + case HLP_VARROW: + immUniformThemeColor3(TH_VIEW_OVERLAY); + + GPU_matrix_translate_3fv(mval); + + GPU_line_width(3.0f); + drawArrow(UP, &arrow_dims); + drawArrow(DOWN, &arrow_dims); + break; + case HLP_CARROW: { + /* Draw arrow based on direction defined by custom-points. */ + immUniformThemeColor3(TH_VIEW_OVERLAY); + + GPU_matrix_translate_3fv(mval); + + GPU_line_width(3.0f); + + const int *data = t->mouse.data; + const float dx = data[2] - data[0], dy = data[3] - data[1]; + const float angle = -atan2f(dx, dy); + + GPU_matrix_push(); + + GPU_matrix_rotate_axis(RAD2DEGF(angle), 'Z'); + + drawArrow(UP, &arrow_dims); + drawArrow(DOWN, &arrow_dims); + + GPU_matrix_pop(); + break; + } + case HLP_ANGLE: { + float dx = tmval[0] - cent[0], dy = tmval[1] - cent[1]; + float angle = atan2f(dy, dx); + float dist = hypotf(dx, dy); + float delta_angle = min_ff(15.0f / (dist / UI_DPI_FAC), (float)M_PI / 4.0f); + float spacing_angle = min_ff(5.0f / (dist / UI_DPI_FAC), (float)M_PI / 12.0f); + + immUniformThemeColor3(TH_VIEW_OVERLAY); + + GPU_matrix_translate_3f(cent[0] - tmval[0] + mval[0], cent[1] - tmval[1] + mval[1], 0); + + GPU_line_width(3.0f); + drawArc(angle - delta_angle, angle - spacing_angle, 10, dist); + drawArc(angle + spacing_angle, angle + delta_angle, 10, dist); + + GPU_matrix_push(); + + GPU_matrix_translate_3f( + cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0); + GPU_matrix_rotate_axis(RAD2DEGF(angle - delta_angle), 'Z'); + + drawArrowHead(DOWN, arrow_dims.size); + + GPU_matrix_pop(); + + GPU_matrix_translate_3f( + cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0); + GPU_matrix_rotate_axis(RAD2DEGF(angle + delta_angle), 'Z'); + + drawArrowHead(UP, arrow_dims.size); + break; + } + case HLP_TRACKBALL: { + unsigned char col[3], col2[3]; + UI_GetThemeColor3ubv(TH_GRID, col); + + GPU_matrix_translate_3fv(mval); + + GPU_line_width(3.0f); + + UI_make_axis_color(col, col2, 'X'); + immUniformColor3ubv(col2); + + drawArrow(RIGHT, &arrow_dims); + drawArrow(LEFT, &arrow_dims); + + UI_make_axis_color(col, col2, 'Y'); + immUniformColor3ubv(col2); + + drawArrow(UP, &arrow_dims); + drawArrow(DOWN, &arrow_dims); + break; + } + } + + immUnbindProgram(); + GPU_matrix_pop(); + + GPU_line_smooth(false); + GPU_blend(false); + } +} diff --git a/source/blender/editors/transform/transform_draw_cursors.h b/source/blender/editors/transform/transform_draw_cursors.h new file mode 100644 index 00000000000..e7696bad5a7 --- /dev/null +++ b/source/blender/editors/transform/transform_draw_cursors.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +/** \file + * \ingroup edtransform + */ + +#ifndef __TRANSFORM_DRAW_CURSORS_H__ +#define __TRANSFORM_DRAW_CURSORS_H__ + +/* Callbacks for #WM_paint_cursor_activate */ +bool transform_draw_cursor_poll(struct bContext *C); +void transform_draw_cursor_draw(struct bContext *C, int x, int y, void *customdata); + +#endif /* __TRANSFORM_DRAW_CURSORS_H__ */ diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index 4ae64c7ca5f..3f0032cc7cc 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -52,6 +52,18 @@ #include "transform.h" /* own include */ +/* -------------------------------------------------------------------- */ +/** \name Arrow / Cage Gizmo Group + * + * Defines public functions, not the gizmo it's self: + * + * - #ED_widgetgroup_gizmo2d_xform_setup + * - #ED_widgetgroup_gizmo2d_xform_refresh + * - #ED_widgetgroup_gizmo2d_xform_draw_prepare + * - #ED_widgetgroup_gizmo2d_xform_poll + * + * \{ */ + /* axes as index */ enum { MAN2D_AXIS_TRANS_X = 0, @@ -61,8 +73,7 @@ enum { }; typedef struct GizmoGroup2D { - wmGizmo *translate_x, *translate_y; - + wmGizmo *translate_xy[3]; wmGizmo *cage; /* Current origin in view space, used to update widget origin for possible view changes */ @@ -70,37 +81,12 @@ typedef struct GizmoGroup2D { float min[2]; float max[2]; + bool no_cage; + } GizmoGroup2D; /* **************** Utilities **************** */ -/* loop over axes */ -#define MAN2D_ITER_AXES_BEGIN(axis, axis_idx) \ - { \ - wmGizmo *axis; \ - int axis_idx; \ - for (axis_idx = 0; axis_idx < MAN2D_AXIS_LAST; axis_idx++) { \ - axis = gizmo2d_get_axis_from_index(ggd, axis_idx); - -#define MAN2D_ITER_AXES_END \ - } \ - } \ - ((void)0) - -static wmGizmo *gizmo2d_get_axis_from_index(const GizmoGroup2D *ggd, const short axis_idx) -{ - BLI_assert(IN_RANGE_INCL(axis_idx, (float)MAN2D_AXIS_TRANS_X, (float)MAN2D_AXIS_TRANS_Y)); - - switch (axis_idx) { - case MAN2D_AXIS_TRANS_X: - return ggd->translate_x; - case MAN2D_AXIS_TRANS_Y: - return ggd->translate_y; - } - - return NULL; -} - static void gizmo2d_get_axis_color(const int axis_idx, float *r_col, float *r_col_hi) { const float alpha = 0.6f; @@ -131,11 +117,13 @@ static GizmoGroup2D *gizmogroup2d_init(wmGizmoGroup *gzgroup) { const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_2d", true); const wmGizmoType *gzt_cage = WM_gizmotype_find("GIZMO_GT_cage_2d", true); + const wmGizmoType *gzt_button = WM_gizmotype_find("GIZMO_GT_button_2d", true); GizmoGroup2D *ggd = MEM_callocN(sizeof(GizmoGroup2D), __func__); - ggd->translate_x = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); - ggd->translate_y = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); + ggd->translate_xy[0] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); + ggd->translate_xy[1] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); + ggd->translate_xy[2] = WM_gizmo_new_ptr(gzt_button, gzgroup, NULL); ggd->cage = WM_gizmo_new_ptr(gzt_cage, gzgroup, NULL); RNA_enum_set(ggd->cage->ptr, @@ -208,39 +196,54 @@ static int gizmo2d_modal(bContext *C, return OPERATOR_RUNNING_MODAL; } -void ED_widgetgroup_gizmo2d_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) +void ED_widgetgroup_gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) { wmOperatorType *ot_translate = WM_operatortype_find("TRANSFORM_OT_translate", true); GizmoGroup2D *ggd = gizmogroup2d_init(gzgroup); gzgroup->customdata = ggd; - MAN2D_ITER_AXES_BEGIN (axis, axis_idx) { + for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { + wmGizmo *gz = ggd->translate_xy[i]; const float offset[3] = {0.0f, 0.2f}; - float color[4], color_hi[4]; - gizmo2d_get_axis_color(axis_idx, color, color_hi); - /* custom handler! */ - WM_gizmo_set_fn_custom_modal(axis, gizmo2d_modal); - /* set up widget data */ - RNA_float_set(axis->ptr, "angle", -M_PI_2 * axis_idx); - RNA_float_set(axis->ptr, "length", 0.8f); - WM_gizmo_set_matrix_offset_location(axis, offset); - WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH); - WM_gizmo_set_scale(axis, U.gizmo_size); - WM_gizmo_set_color(axis, color); - WM_gizmo_set_color_highlight(axis, color_hi); + WM_gizmo_set_fn_custom_modal(gz, gizmo2d_modal); + WM_gizmo_set_scale(gz, U.gizmo_size); + + if (i < 2) { + float color[4], color_hi[4]; + gizmo2d_get_axis_color(i, color, color_hi); + + /* set up widget data */ + RNA_float_set(gz->ptr, "angle", -M_PI_2 * i); + RNA_float_set(gz->ptr, "length", 0.8f); + WM_gizmo_set_matrix_offset_location(gz, offset); + WM_gizmo_set_line_width(gz, GIZMO_AXIS_LINE_WIDTH); + WM_gizmo_set_color(gz, color); + WM_gizmo_set_color_highlight(gz, color_hi); + } + else { + PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon"); + RNA_property_enum_set(gz->ptr, prop, ICON_NONE); + + RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_BUTTON_SHOW_BACKDROP); + /* Make the center low alpha. */ + WM_gizmo_set_line_width(gz, 2.0f); + RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.0); + } - /* assign operator */ - PointerRNA *ptr = WM_gizmo_operator_set(axis, 0, ot_translate, NULL); - bool constraint[3] = {0}; - constraint[(axis_idx + 1) % 2] = 1; - if (RNA_struct_find_property(ptr, "constraint_axis")) { - RNA_boolean_set_array(ptr, "constraint_axis", constraint); + /* Assign operator. */ + PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ot_translate, NULL); + if (i < 2) { + bool constraint[3] = {0}; + constraint[(i + 1) % 2] = 1; + if (RNA_struct_find_property(ptr, "constraint_axis")) { + RNA_boolean_set_array(ptr, "constraint_axis", constraint); + } } + RNA_boolean_set(ptr, "release_confirm", 1); } - MAN2D_ITER_AXES_END; { wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true); @@ -286,23 +289,44 @@ void ED_widgetgroup_gizmo2d_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgro } } -void ED_widgetgroup_gizmo2d_refresh(const bContext *C, wmGizmoGroup *gzgroup) +void ED_widgetgroup_gizmo2d_xform_setup_no_cage(const bContext *C, wmGizmoGroup *gzgroup) +{ + ED_widgetgroup_gizmo2d_xform_setup(C, gzgroup); + GizmoGroup2D *ggd = gzgroup->customdata; + ggd->no_cage = true; +} + +void ED_widgetgroup_gizmo2d_xform_refresh(const bContext *C, wmGizmoGroup *gzgroup) { GizmoGroup2D *ggd = gzgroup->customdata; float origin[3]; gizmo2d_calc_bounds(C, origin, ggd->min, ggd->max); copy_v2_v2(ggd->origin, origin); - bool show_cage = !equals_v2v2(ggd->min, ggd->max); + bool show_cage = !ggd->no_cage && !equals_v2v2(ggd->min, ggd->max); + + if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { + Scene *scene = CTX_data_scene(C); + if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { + gzgroup->use_fallback_keymap = true; + } + else { + gzgroup->use_fallback_keymap = false; + } + } if (show_cage) { ggd->cage->flag &= ~WM_GIZMO_HIDDEN; - ggd->translate_x->flag |= WM_GIZMO_HIDDEN; - ggd->translate_y->flag |= WM_GIZMO_HIDDEN; + for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { + wmGizmo *gz = ggd->translate_xy[i]; + gz->flag |= WM_GIZMO_HIDDEN; + } } else { ggd->cage->flag |= WM_GIZMO_HIDDEN; - ggd->translate_x->flag &= ~WM_GIZMO_HIDDEN; - ggd->translate_y->flag &= ~WM_GIZMO_HIDDEN; + for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { + wmGizmo *gz = ggd->translate_xy[i]; + gz->flag &= ~WM_GIZMO_HIDDEN; + } } if (show_cage) { @@ -345,7 +369,7 @@ void ED_widgetgroup_gizmo2d_refresh(const bContext *C, wmGizmoGroup *gzgroup) } } -void ED_widgetgroup_gizmo2d_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) +void ED_widgetgroup_gizmo2d_xform_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) { ARegion *ar = CTX_wm_region(C); GizmoGroup2D *ggd = gzgroup->customdata; @@ -354,10 +378,10 @@ void ED_widgetgroup_gizmo2d_draw_prepare(const bContext *C, wmGizmoGroup *gzgrou gizmo2d_origin_to_region(ar, origin); - MAN2D_ITER_AXES_BEGIN (axis, axis_idx) { - WM_gizmo_set_matrix_location(axis, origin); + for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { + wmGizmo *gz = ggd->translate_xy[i]; + WM_gizmo_set_matrix_location(gz, origin); } - MAN2D_ITER_AXES_END; UI_view2d_view_to_region_m4(&ar->v2d, ggd->cage->matrix_space); WM_gizmo_set_matrix_offset_location(ggd->cage, origin_aa); @@ -369,7 +393,7 @@ void ED_widgetgroup_gizmo2d_draw_prepare(const bContext *C, wmGizmoGroup *gzgrou * - Called on every redraw, better to do a more simple poll and check for selection in _refresh * - UV editing only, could be expanded for other things. */ -bool ED_widgetgroup_gizmo2d_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt)) +bool ED_widgetgroup_gizmo2d_xform_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt)) { if ((U.gizmo_flag & USER_GIZMO_DRAW) == 0) { return false; @@ -404,3 +428,223 @@ bool ED_widgetgroup_gizmo2d_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzg return false; } + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Scale Handles + * + * Defines public functions, not the gizmo it's self: + * + * - #ED_widgetgroup_gizmo2d_resize_setup + * - #ED_widgetgroup_gizmo2d_resize_refresh + * - #ED_widgetgroup_gizmo2d_resize_draw_prepare + * - #ED_widgetgroup_gizmo2d_resize_poll + * + * \{ */ + +typedef struct GizmoGroup_Resize2D { + wmGizmo *gizmo_xy[3]; + float origin[2]; +} GizmoGroup_Resize2D; + +static GizmoGroup_Resize2D *gizmogroup2d_resize_init(wmGizmoGroup *gzgroup) +{ + const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_2d", true); + const wmGizmoType *gzt_button = WM_gizmotype_find("GIZMO_GT_button_2d", true); + + GizmoGroup_Resize2D *ggd = MEM_callocN(sizeof(GizmoGroup_Resize2D), __func__); + + ggd->gizmo_xy[0] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); + ggd->gizmo_xy[1] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); + ggd->gizmo_xy[2] = WM_gizmo_new_ptr(gzt_button, gzgroup, NULL); + + return ggd; +} + +void ED_widgetgroup_gizmo2d_resize_refresh(const bContext *C, wmGizmoGroup *gzgroup) +{ + GizmoGroup_Resize2D *ggd = gzgroup->customdata; + float origin[3]; + gizmo2d_calc_bounds(C, origin, NULL, NULL); + copy_v2_v2(ggd->origin, origin); +} + +void ED_widgetgroup_gizmo2d_resize_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) +{ + ARegion *ar = CTX_wm_region(C); + GizmoGroup_Resize2D *ggd = gzgroup->customdata; + float origin[3] = {UNPACK2(ggd->origin), 0.0f}; + + if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { + Scene *scene = CTX_data_scene(C); + if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { + gzgroup->use_fallback_keymap = true; + } + else { + gzgroup->use_fallback_keymap = false; + } + } + + gizmo2d_origin_to_region(ar, origin); + + for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) { + wmGizmo *gz = ggd->gizmo_xy[i]; + WM_gizmo_set_matrix_location(gz, origin); + } +} + +bool ED_widgetgroup_gizmo2d_resize_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt)) +{ + return ED_widgetgroup_gizmo2d_xform_poll(C, NULL); +} + +void ED_widgetgroup_gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) +{ + + wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true); + GizmoGroup_Resize2D *ggd = gizmogroup2d_resize_init(gzgroup); + gzgroup->customdata = ggd; + + for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) { + wmGizmo *gz = ggd->gizmo_xy[i]; + + /* custom handler! */ + WM_gizmo_set_fn_custom_modal(gz, gizmo2d_modal); + WM_gizmo_set_scale(gz, U.gizmo_size); + + if (i < 2) { + const float offset[3] = {0.0f, 0.2f}; + float color[4], color_hi[4]; + gizmo2d_get_axis_color(i, color, color_hi); + + /* set up widget data */ + RNA_float_set(gz->ptr, "angle", -M_PI_2 * i); + RNA_float_set(gz->ptr, "length", 0.8f); + RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_BOX); + + WM_gizmo_set_matrix_offset_location(gz, offset); + WM_gizmo_set_line_width(gz, GIZMO_AXIS_LINE_WIDTH); + WM_gizmo_set_color(gz, color); + WM_gizmo_set_color_highlight(gz, color_hi); + } + else { + PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon"); + RNA_property_enum_set(gz->ptr, prop, ICON_NONE); + + RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_BUTTON_SHOW_BACKDROP); + /* Make the center low alpha. */ + WM_gizmo_set_line_width(gz, 2.0f); + RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.0); + } + + /* Assign operator. */ + PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ot_resize, NULL); + if (i < 2) { + bool constraint[3] = {0}; + constraint[(i + 1) % 2] = 1; + if (RNA_struct_find_property(ptr, "constraint_axis")) { + RNA_boolean_set_array(ptr, "constraint_axis", constraint); + } + } + RNA_boolean_set(ptr, "release_confirm", true); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Rotate Handles + * + * Defines public functions, not the gizmo it's self: + * + * - #ED_widgetgroup_gizmo2d_rotate_setup + * - #ED_widgetgroup_gizmo2d_rotate_refresh + * - #ED_widgetgroup_gizmo2d_rotate_draw_prepare + * - #ED_widgetgroup_gizmo2d_rotate_poll + * + * \{ */ + +typedef struct GizmoGroup_Rotate2D { + wmGizmo *gizmo; + float origin[2]; +} GizmoGroup_Rotate2D; + +static GizmoGroup_Rotate2D *gizmogroup2d_rotate_init(wmGizmoGroup *gzgroup) +{ + const wmGizmoType *gzt_button = WM_gizmotype_find("GIZMO_GT_button_2d", true); + + GizmoGroup_Rotate2D *ggd = MEM_callocN(sizeof(GizmoGroup_Rotate2D), __func__); + + ggd->gizmo = WM_gizmo_new_ptr(gzt_button, gzgroup, NULL); + + return ggd; +} + +void ED_widgetgroup_gizmo2d_rotate_refresh(const bContext *C, wmGizmoGroup *gzgroup) +{ + GizmoGroup_Rotate2D *ggd = gzgroup->customdata; + float origin[3]; + gizmo2d_calc_bounds(C, origin, NULL, NULL); + copy_v2_v2(ggd->origin, origin); +} + +void ED_widgetgroup_gizmo2d_rotate_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) +{ + ARegion *ar = CTX_wm_region(C); + GizmoGroup_Rotate2D *ggd = gzgroup->customdata; + float origin[3] = {UNPACK2(ggd->origin), 0.0f}; + + if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { + Scene *scene = CTX_data_scene(C); + if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { + gzgroup->use_fallback_keymap = true; + } + else { + gzgroup->use_fallback_keymap = false; + } + } + + gizmo2d_origin_to_region(ar, origin); + + wmGizmo *gz = ggd->gizmo; + WM_gizmo_set_matrix_location(gz, origin); +} + +bool ED_widgetgroup_gizmo2d_rotate_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt)) +{ + return ED_widgetgroup_gizmo2d_xform_poll(C, NULL); +} + +void ED_widgetgroup_gizmo2d_rotate_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) +{ + + wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_rotate", true); + GizmoGroup_Rotate2D *ggd = gizmogroup2d_rotate_init(gzgroup); + gzgroup->customdata = ggd; + + /* Other setup functions iterate over axis. */ + { + wmGizmo *gz = ggd->gizmo; + + /* custom handler! */ + WM_gizmo_set_fn_custom_modal(gz, gizmo2d_modal); + WM_gizmo_set_scale(gz, U.gizmo_size); + + { + PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon"); + RNA_property_enum_set(gz->ptr, prop, ICON_NONE); + + RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_BUTTON_SHOW_BACKDROP); + /* Make the center low alpha. */ + WM_gizmo_set_line_width(gz, 2.0f); + RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.0); + } + + /* Assign operator. */ + PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ot_resize, NULL); + RNA_boolean_set(ptr, "release_confirm", true); + } +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index f19147baa89..b3c1fbd3aad 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -2046,7 +2046,8 @@ void VIEW3D_GGT_xform_gizmo(wmGizmoGroupType *gzgt) gzgt->name = "3D View: Transform Gizmo"; gzgt->idname = "VIEW3D_GGT_xform_gizmo"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP; + gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; @@ -2081,7 +2082,7 @@ void VIEW3D_GGT_xform_gizmo_context(wmGizmoGroupType *gzgt) gzgt->idname = "VIEW3D_GGT_xform_gizmo_context"; gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_PERSISTENT | - WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP; + WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; gzgt->poll = WIDGETGROUP_gizmo_poll_context; gzgt->setup = WIDGETGROUP_gizmo_setup; @@ -2289,7 +2290,8 @@ void VIEW3D_GGT_xform_cage(wmGizmoGroupType *gzgt) gzgt->name = "Transform Cage"; gzgt->idname = "VIEW3D_GGT_xform_cage"; - gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP; + gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; @@ -2530,7 +2532,8 @@ void VIEW3D_GGT_xform_shear(wmGizmoGroupType *gzgt) gzgt->name = "Transform Shear"; gzgt->idname = "VIEW3D_GGT_xform_shear"; - gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP; + gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c index da6b0285a5c..fb33471cf3f 100644 --- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c +++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c @@ -514,7 +514,8 @@ void VIEW3D_GGT_xform_extrude(struct wmGizmoGroupType *gzgt) gzgt->name = "3D View Extrude"; gzgt->idname = "VIEW3D_GGT_xform_extrude"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP; + gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h index f9f6a513f63..a188e2eb829 100644 --- a/source/blender/editors/transform/transform_snap.h +++ b/source/blender/editors/transform/transform_snap.h @@ -26,6 +26,8 @@ /* For enum. */ #include "DNA_space_types.h" +struct SnapObjectParams; + bool peelObjectsTransform(struct TransInfo *t, const float mval[2], const bool use_peel_object, diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index eb3d47ba1b5..1de4d05a721 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -1286,7 +1286,7 @@ static void uv_select_linked_multi(Scene *scene, vmap = BM_uv_vert_map_create(em->bm, limit, !select_faces, false); if (vmap == NULL) { - return; + continue; } stack = MEM_mallocN(sizeof(*stack) * (em->bm->totface + 1), "UvLinkStack"); |