diff options
64 files changed, 1162 insertions, 315 deletions
diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index 31d0eb8e923..fbdb226ab6d 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -229,6 +229,8 @@ const UserDef U_default = { .collection_instance_empty_size = 1.0f, + .statusbar_flag = STATUSBAR_SHOW_VERSION, + .runtime = { .is_dirty = 0, diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 87802c90068..7a0350510b7 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -3176,7 +3176,7 @@ def km_grease_pencil_stroke_edit_mode(params): ("gpencil.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), ("gpencil.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), # Snap - op_menu("GPENCIL_MT_snap", {"type": 'S', "value": 'PRESS', "shift": True}), + op_menu_pie("GPENCIL_MT_snap_pie", {"type": 'S', "value": 'PRESS', "shift": True}), # Show/hide ("gpencil.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), ("gpencil.hide", {"type": 'H', "value": 'PRESS'}, diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index f275d892abe..eedf07935c8 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -2316,7 +2316,7 @@ def km_grease_pencil_stroke_edit_mode(params): ("gpencil.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), ("gpencil.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), # Snap - op_menu("GPENCIL_MT_snap", {"type": 'X', "value": 'PRESS', "shift": True}), + op_menu_pie("GPENCIL_MT_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}), # Show/hide ("gpencil.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), ("gpencil.hide", {"type": 'H', "value": 'PRESS', "ctrl": True}, diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 07c9fc363b5..55b6f76632f 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -279,6 +279,29 @@ class GPENCIL_MT_snap(Menu): layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid") +class GPENCIL_MT_snap_pie(Menu): + bl_label = "Snap" + + def draw(self, _context): + layout = self.layout + pie = layout.menu_pie() + + pie.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid", icon='CURSOR') + pie.operator("gpencil.snap_to_grid", text="Selection to Grid", icon='RESTRICT_SELECT_OFF') + pie.operator("gpencil.snap_cursor_to_selected", text="Cursor to Selected", icon='CURSOR') + pie.operator( + "gpencil.snap_to_cursor", + text="Selection to Cursor", + icon='RESTRICT_SELECT_OFF' + ).use_offset = False + pie.operator( + "gpencil.snap_to_cursor", + text="Selection to Cursor (Keep Offset)", + icon='RESTRICT_SELECT_OFF' + ).use_offset = True + pie.operator("view3d.snap_cursor_to_center", text="Cursor to World Origin", icon='CURSOR') + + class GPENCIL_MT_move_to_layer(Menu): bl_label = "Move to Layer" @@ -901,6 +924,7 @@ class GreasePencilFlipTintColors(Operator): classes = ( GPENCIL_MT_snap, + GPENCIL_MT_snap_pie, GPENCIL_MT_cleanup, GPENCIL_MT_move_to_layer, GPENCIL_MT_layer_active, diff --git a/release/scripts/startup/bl_ui/space_statusbar.py b/release/scripts/startup/bl_ui/space_statusbar.py index 4984372eed3..cbf72a7bc59 100644 --- a/release/scripts/startup/bl_ui/space_statusbar.py +++ b/release/scripts/startup/bl_ui/space_statusbar.py @@ -31,17 +31,20 @@ class STATUSBAR_HT_header(Header): layout.separator_spacer() - # messages - layout.template_reports_banner() - layout.template_running_jobs() - + # Nothing in the center. layout.separator_spacer() - # stats - scene = context.scene - view_layer = context.view_layer + row = layout.row() + row.alignment = 'RIGHT' + + # Stats & Info + row.label(text=context.screen.statusbar_info(), translate=False) + + # Messages + row.template_reports_banner() - layout.label(text=scene.statistics(view_layer), translate=False) + # Progress Bar + row.template_running_jobs() classes = ( diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 66c98b81e0e..03f85578b6e 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -283,6 +283,22 @@ class USERPREF_PT_interface_temporary_windows(InterfacePanel, CenterAlignMixIn, col.prop(view, "filebrowser_display_type", text="File Browser") +class USERPREF_PT_interface_statusbar(InterfacePanel, CenterAlignMixIn, Panel): + bl_label = "Status Bar" + bl_parent_id = "USERPREF_PT_interface_editors" + bl_options = {'DEFAULT_CLOSED'} + + def draw_centered(self, context, layout): + prefs = context.preferences + view = prefs.view + + col = layout.column(heading="Show") + col.prop(view, "show_statusbar_stats", text="Scene Statistics") + col.prop(view, "show_statusbar_memory", text="System Memory") + col.prop(view, "show_statusbar_vram", text="Video Memory") + col.prop(view, "show_statusbar_version", text="Blender Version") + + class USERPREF_PT_interface_menus(InterfacePanel, Panel): bl_label = "Menus" bl_options = {'DEFAULT_CLOSED'} @@ -2189,6 +2205,7 @@ classes = ( USERPREF_PT_interface_display, USERPREF_PT_interface_editors, USERPREF_PT_interface_temporary_windows, + USERPREF_PT_interface_statusbar, USERPREF_PT_interface_translation, USERPREF_PT_interface_text, USERPREF_PT_interface_menus, diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 749e92bdcf7..077b53559aa 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -7034,7 +7034,7 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu): col.separator() col.menu("VIEW3D_MT_mirror", text="Mirror Points") - col.menu("VIEW3D_MT_snap", text="Snap Points") + col.menu("GPENCIL_MT_snap", text="Snap Points") col.separator() diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 57f22a8d709..556c1c961dc 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -40,7 +40,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 6 +#define BLENDER_FILE_SUBVERSION 7 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index a246265c3f2..9365ee040c2 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -64,7 +64,7 @@ #include "BLI_mempool.h" #include "BLI_system.h" #include "BLI_threads.h" -#include "BLI_timecode.h" /* for stamp timecode format */ +#include "BLI_timecode.h" /* For stamp time-code format. */ #include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index f51486c5e7b..64b0dcccda1 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -671,6 +671,13 @@ void projmat_dimensions(const float projmat[4][4], float *r_top, float *r_near, float *r_far); +void projmat_dimensions_db(const float projmat[4][4], + double *r_left, + double *r_right, + double *r_bottom, + double *r_top, + double *r_near, + double *r_far); void projmat_from_subregion(const float projmat[4][4], const int win_size[2], diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index d3dc4729617..2891279bb63 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -4880,6 +4880,37 @@ void projmat_dimensions(const float projmat[4][4], } } +void projmat_dimensions_db(const float projmat_fl[4][4], + double *r_left, + double *r_right, + double *r_bottom, + double *r_top, + double *r_near, + double *r_far) +{ + double projmat[4][4]; + copy_m4d_m4(projmat, projmat_fl); + + bool is_persp = projmat[3][3] == 0.0f; + + if (is_persp) { + *r_left = (projmat[2][0] - 1.0) / projmat[0][0]; + *r_right = (projmat[2][0] + 1.0) / projmat[0][0]; + *r_bottom = (projmat[2][1] - 1.0) / projmat[1][1]; + *r_top = (projmat[2][1] + 1.0) / projmat[1][1]; + *r_near = projmat[3][2] / (projmat[2][2] - 1.0); + *r_far = projmat[3][2] / (projmat[2][2] + 1.0); + } + else { + *r_left = (-projmat[3][0] - 1.0) / projmat[0][0]; + *r_right = (-projmat[3][0] + 1.0) / projmat[0][0]; + *r_bottom = (-projmat[3][1] - 1.0) / projmat[1][1]; + *r_top = (-projmat[3][1] + 1.0) / projmat[1][1]; + *r_near = (projmat[3][2] + 1.0) / projmat[2][2]; + *r_far = (projmat[3][2] - 1.0) / projmat[2][2]; + } +} + /** * Creates a projection matrix for a small region of the viewport. * diff --git a/source/blender/blenlib/intern/timecode.c b/source/blender/blenlib/intern/timecode.c index 510b9651961..9586da941a4 100644 --- a/source/blender/blenlib/intern/timecode.c +++ b/source/blender/blenlib/intern/timecode.c @@ -36,7 +36,7 @@ #include "BLI_strict_flags.h" /** - * Generate timecode/frame number string and store in \a str + * Generate time-code/frame number string and store in \a str * * \param str: destination string * \param maxncpy: maximum number of characters to copy ``sizeof(str)`` @@ -44,7 +44,7 @@ * used to specify how detailed we need to be * \param time_seconds: time total time in seconds * \param fps: frames per second, typically from the #FPS macro - * \param timecode_style: enum from eTimecodeStyles + * \param timecode_style: enum from #eTimecodeStyles * \return length of \a str */ diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index ce472a97337..8c00c20c822 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -579,7 +579,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) } if (bmain->versionfile <= 109) { - /* new variable: gridlines */ + /* New variable: `gridlines`. */ bScreen *screen = bmain->screens.first; while (screen) { ScrArea *area = screen->areabase.first; diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 1b0e41ec54a..50e3b375166 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -753,6 +753,10 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef) userdef->transopts &= ~USER_DOTRANSLATE_DEPRECATED; } + if (!USER_VERSION_ATLEAST(290, 7)) { + userdef->statusbar_flag = STATUSBAR_SHOW_VERSION; + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 3a8f6075621..988bc2b97ac 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -3857,6 +3857,10 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const layers, CD_MASK_ALL); + if (layers != NULL && layers != layers_buff) { + MEM_freeN(layers); + } + write_pointcaches(writer, &particle_state->ptcaches); break; } diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c index daf83e11e17..0144fac76ab 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.c +++ b/source/blender/draw/engines/overlay/overlay_armature.c @@ -178,6 +178,7 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata) #define BUF_INSTANCE DRW_shgroup_call_buffer_instance #define BUF_LINE(grp, format) DRW_shgroup_call_buffer(grp, format, GPU_PRIM_LINES) +#define BUF_POINT(grp, format) DRW_shgroup_call_buffer(grp, format, GPU_PRIM_POINTS) { format = formats->instance_bone; diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index f096c9657c7..d4169014385 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -199,6 +199,9 @@ void OVERLAY_extra_cache_init(OVERLAY_Data *vedata) cb->extra_loose_points = grp = DRW_shgroup_create(sh, extra_ps); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + + /* Buffer access for drawing isolated points, matching `extra_lines`. */ + cb->extra_points = BUF_POINT(grp, formats->point_extra); } { format = formats->pos; @@ -230,6 +233,11 @@ void OVERLAY_extra_cache_init(OVERLAY_Data *vedata) } } +void OVERLAY_extra_point(OVERLAY_ExtraCallBuffers *cb, const float point[3], const float color[4]) +{ + DRW_buffer_add_entry(cb->extra_points, point, color); +} + void OVERLAY_extra_line_dashed(OVERLAY_ExtraCallBuffers *cb, const float start[3], const float end[3], @@ -1276,6 +1284,19 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb, OVERLAY_extra_line_dashed(cb, parent_pos, ob->obmat[3], relation_color); } + /* Drawing the hook lines. */ + for (ModifierData *md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Hook) { + HookModifierData *hmd = (HookModifierData *)md; + float center[3]; + mul_v3_m4v3(center, ob->obmat, hmd->cent); + if (hmd->object) { + OVERLAY_extra_line_dashed(cb, hmd->object->obmat[3], center, relation_color); + } + OVERLAY_extra_point(cb, center, relation_color); + } + } + if (ob->rigidbody_constraint) { Object *rbc_ob1 = ob->rigidbody_constraint->ob1; Object *rbc_ob2 = ob->rigidbody_constraint->ob2; diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 465a14a57d7..21d92112d53 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -150,6 +150,7 @@ typedef struct OVERLAY_ExtraCallBuffers { DRWCallBuffer *extra_dashed_lines; DRWCallBuffer *extra_lines; + DRWCallBuffer *extra_points; DRWCallBuffer *field_curve; DRWCallBuffer *field_force; @@ -391,6 +392,7 @@ typedef struct OVERLAY_InstanceFormats { struct GPUVertFormat *pos; struct GPUVertFormat *pos_color; struct GPUVertFormat *wire_extra; + struct GPUVertFormat *point_extra; } OVERLAY_InstanceFormats; /* Pack data into the last row of the 4x4 matrix. It will be decoded by the vertex shader. */ @@ -484,6 +486,7 @@ void OVERLAY_lightprobe_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_speaker_cache_populate(OVERLAY_Data *vedata, Object *ob); OVERLAY_ExtraCallBuffers *OVERLAY_extra_call_buffer_get(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_extra_point(OVERLAY_ExtraCallBuffers *cb, const float point[3], const float color[4]); void OVERLAY_extra_line_dashed(OVERLAY_ExtraCallBuffers *cb, const float start[3], const float end[3], diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c index 87f4642809b..e3cb052890b 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.c +++ b/source/blender/draw/engines/overlay/overlay_shader.c @@ -1476,6 +1476,11 @@ OVERLAY_InstanceFormats *OVERLAY_shader_instance_formats_get(void) {"pos", DRW_ATTR_FLOAT, 3}, {"colorid", DRW_ATTR_INT, 1}, }); + DRW_shgroup_instance_format(g_formats.point_extra, + { + {"pos", DRW_ATTR_FLOAT, 3}, + {"colorid", DRW_ATTR_INT, 1}, + }); DRW_shgroup_instance_format(g_formats.instance_bone, { {"inst_obmat", DRW_ATTR_FLOAT, 16}, diff --git a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl index 265d19f336f..c529f23265b 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl @@ -48,7 +48,7 @@ float workbench_float_pair_encode(float v1, float v2) { // const uint v1_mask = ~(0xFFFFFFFFu << ROUGHNESS_BITS); // const uint v2_mask = ~(0xFFFFFFFFu << METALLIC_BITS); - /* Same as above because some compiler are dumb af. and think we use mediump int. */ + /* Same as above because some compiler are very dumb and think we use medium int. */ const int v1_mask = 0x1F; const int v2_mask = 0x7; int iv1 = int(v1 * float(v1_mask)); @@ -60,7 +60,7 @@ void workbench_float_pair_decode(float data, out float v1, out float v2) { // const uint v1_mask = ~(0xFFFFFFFFu << ROUGHNESS_BITS); // const uint v2_mask = ~(0xFFFFFFFFu << METALLIC_BITS); - /* Same as above because some compiler are dumb af. and think we use mediump int. */ + /* Same as above because some compiler are very dumb and think we use medium int. */ const int v1_mask = 0x1F; const int v2_mask = 0x7; int idata = int(data); diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 3e428eaffc2..91a8ea0fa3a 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -4085,7 +4085,7 @@ static int curve_normals_make_consistent_exec(bContext *C, wmOperator *op) void CURVE_OT_normals_make_consistent(wmOperatorType *ot) { /* identifiers */ - ot->name = "Recalc Normals"; + ot->name = "Recalculate Handles"; ot->description = "Recalculate the direction of selected handles"; ot->idname = "CURVE_OT_normals_make_consistent"; diff --git a/source/blender/editors/include/ED_info.h b/source/blender/editors/include/ED_info.h index 1146c49bef2..e97fd424742 100644 --- a/source/blender/editors/include/ED_info.h +++ b/source/blender/editors/include/ED_info.h @@ -31,9 +31,11 @@ struct Main; /* info_stats.c */ void ED_info_stats_clear(struct ViewLayer *view_layer); -const char *ED_info_footer_string(struct ViewLayer *view_layer); +const char *ED_info_statusbar_string(struct Main *bmain, + struct bScreen *screen, + struct bContext *C); void ED_info_draw_stats( - Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height); + struct Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height); #ifdef __cplusplus } diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index bac86adbabb..586f5e07997 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -881,7 +881,7 @@ void UI_icons_reload_internal_textures(void) } if (need_icons_with_border && icongltex.tex[1] == NULL) { - icongltex.tex[0] = GPU_texture_create_nD(b32buf_border->x, + icongltex.tex[1] = GPU_texture_create_nD(b32buf_border->x, b32buf_border->y, 0, 2, @@ -891,7 +891,7 @@ void UI_icons_reload_internal_textures(void) 0, false, NULL); - GPU_texture_add_mipmap(icongltex.tex[0], GPU_DATA_UNSIGNED_BYTE, 1, b16buf_border->rect); + GPU_texture_add_mipmap(icongltex.tex[1], GPU_DATA_UNSIGNED_BYTE, 1, b16buf_border->rect); } } diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c index d953a8f9d42..51f3a94efde 100644 --- a/source/blender/editors/mask/mask_ops.c +++ b/source/blender/editors/mask/mask_ops.c @@ -1589,12 +1589,12 @@ static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op) return OPERATOR_CANCELLED; } -/* named to match mesh recalc normals */ +/* Named to match mesh recalculate normals. */ void MASK_OT_normals_make_consistent(wmOperatorType *ot) { /* identifiers */ - ot->name = "Recalc Normals"; - ot->description = "Re-calculate the direction of selected handles"; + ot->name = "Recalculate Handles"; + ot->description = "Recalculate the direction of selected handles"; ot->idname = "MASK_OT_normals_make_consistent"; /* api callbacks */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index b034fb186d2..fde1498bc5e 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -89,6 +89,8 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "GPU_extensions.h" + #include "screen_intern.h" /* own module include */ #define KM_MODAL_CANCEL 1 @@ -4127,12 +4129,6 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot) /** \name Region Context Menu Operator (Header/Footer/Navbar) * \{ */ -static bool screen_region_context_menu_poll(bContext *C) -{ - ScrArea *area = CTX_wm_area(C); - return (area && area->spacetype != SPACE_STATUSBAR); -} - void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg)) { ScrArea *area = CTX_wm_area(C); @@ -4221,15 +4217,35 @@ void ED_screens_navigation_bar_tools_menu_create(bContext *C, uiLayout *layout, uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip"); } +static void ed_screens_statusbar_menu_create(uiLayout *layout, void *UNUSED(arg)) +{ + PointerRNA ptr; + + RNA_pointer_create(NULL, &RNA_PreferencesView, &U, &ptr); + uiItemR(layout, &ptr, "show_statusbar_stats", 0, IFACE_("Scene Statistics"), ICON_NONE); + uiItemR(layout, &ptr, "show_statusbar_memory", 0, IFACE_("System Memory"), ICON_NONE); + if (GPU_mem_stats_supported()) { + uiItemR(layout, &ptr, "show_statusbar_vram", 0, IFACE_("Video Memory"), ICON_NONE); + } + uiItemR(layout, &ptr, "show_statusbar_version", 0, IFACE_("Blender Version"), ICON_NONE); +} + static int screen_context_menu_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) { uiPopupMenu *pup; uiLayout *layout; + const ScrArea *area = CTX_wm_area(C); const ARegion *region = CTX_wm_region(C); - if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) { + if (area && area->spacetype == SPACE_STATUSBAR) { + pup = UI_popup_menu_begin(C, IFACE_("Status Bar"), ICON_NONE); + layout = UI_popup_menu_layout(pup); + ed_screens_statusbar_menu_create(layout, NULL); + UI_popup_menu_end(C, pup); + } + else if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) { pup = UI_popup_menu_begin(C, IFACE_("Header"), ICON_NONE); layout = UI_popup_menu_layout(pup); ED_screens_header_tools_menu_create(C, layout, NULL); @@ -4259,7 +4275,6 @@ static void SCREEN_OT_region_context_menu(wmOperatorType *ot) ot->idname = "SCREEN_OT_region_context_menu"; /* api callbacks */ - ot->poll = screen_region_context_menu_poll; ot->invoke = screen_context_menu_invoke; } diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index 9da75ab7e3c..8532d8420f9 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -1515,7 +1515,7 @@ static void proxy_endjob(void *pjv) } if (pj->clip->source == MCLIP_SRC_MOVIE) { - /* Timecode might have changed, so do a full reload to deal with this. */ + /* Time-code might have changed, so do a full reload to deal with this. */ DEG_id_tag_update(&pj->clip->id, ID_RECALC_SOURCE); } else { diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index e1937dffb37..4e91da01cc9 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -43,6 +43,7 @@ #include "BLT_translation.h" #include "BKE_blender_version.h" +#include "BKE_context.h" #include "BKE_curve.h" #include "BKE_displist.h" #include "BKE_editmesh.h" @@ -405,46 +406,6 @@ static void stats_update(Depsgraph *depsgraph, ViewLayer *view_layer) *(view_layer->stats) = stats; } -static const char *footer_string(ViewLayer *view_layer) -{ -#define MAX_INFO_MEM_LEN 64 - char memstr[MAX_INFO_MEM_LEN]; - char gpumemstr[MAX_INFO_MEM_LEN] = ""; - char formatted_mem[15]; - size_t ofs = 0; - - uintptr_t mem_in_use = MEM_get_memory_in_use(); - - /* get memory statistics */ - BLI_str_format_byte_unit(formatted_mem, mem_in_use, false); - ofs = BLI_snprintf(memstr, MAX_INFO_MEM_LEN, TIP_("Mem: %s"), formatted_mem); - - if (GPU_mem_stats_supported()) { - int gpu_free_mem, gpu_tot_memory; - - GPU_mem_stats_get(&gpu_tot_memory, &gpu_free_mem); - - BLI_str_format_byte_unit(formatted_mem, gpu_free_mem, false); - ofs = BLI_snprintf(gpumemstr, MAX_INFO_MEM_LEN, TIP_(" | Free GPU Mem: %s"), formatted_mem); - - if (gpu_tot_memory) { - BLI_str_format_byte_unit(formatted_mem, gpu_tot_memory, false); - BLI_snprintf(gpumemstr + ofs, MAX_INFO_MEM_LEN - ofs, TIP_("/%s"), formatted_mem); - } - } - - BLI_snprintf(view_layer->footer_str, - sizeof(view_layer->footer_str), - "%s%s | %s", - memstr, - gpumemstr, - BKE_blender_version_string()); - - return view_layer->footer_str; - -#undef MAX_INFO_MEM_LEN -} - void ED_info_stats_clear(ViewLayer *view_layer) { if (view_layer->stats) { @@ -453,45 +414,26 @@ void ED_info_stats_clear(ViewLayer *view_layer) } } -const char *ED_info_footer_string(ViewLayer *view_layer) -{ - return footer_string(view_layer); -} - -static void stats_row(int col1, - const char *key, - int col2, - const char *value1, - const char *value2, - int *y, - int height) -{ - *y -= height; - BLF_draw_default(col1, *y, 0.0f, key, 128); - char values[128]; - BLI_snprintf(values, sizeof(values), (value2) ? "%s / %s" : "%s", value1, value2); - BLF_draw_default(col2, *y, 0.0f, values, sizeof(values)); -} - -void ED_info_draw_stats( - Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height) +static bool format_stats(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + SceneStatsFmt *stats_fmt) { /* Create stats if they don't already exist. */ if (!view_layer->stats) { /* Do not not access dependency graph if interface is marked as locked. */ wmWindowManager *wm = bmain->wm.first; if (wm->is_interface_locked) { - return; + return false; } Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); stats_update(depsgraph, view_layer); } SceneStats *stats = view_layer->stats; - SceneStatsFmt stats_fmt; /* Generate formatted numbers. */ -#define SCENE_STATS_FMT_INT(_id) BLI_str_format_uint64_grouped(stats_fmt._id, stats->_id) +#define SCENE_STATS_FMT_INT(_id) BLI_str_format_uint64_grouped(stats_fmt->_id, stats->_id) SCENE_STATS_FMT_INT(totvert); SCENE_STATS_FMT_INT(totvertsel); @@ -519,6 +461,176 @@ void ED_info_draw_stats( SCENE_STATS_FMT_INT(totgppoint); #undef SCENE_STATS_FMT_INT + return true; +} + +static void get_stats_string( + char *info, int len, size_t *ofs, ViewLayer *view_layer, SceneStatsFmt *stats_fmt) +{ + Object *ob = OBACT(view_layer); + Object *obedit = OBEDIT_FROM_OBACT(ob); + eObjectMode object_mode = ob ? ob->mode : OB_MODE_OBJECT; + LayerCollection *layer_collection = view_layer->active_collection; + + if (object_mode == OB_MODE_OBJECT) { + *ofs += BLI_snprintf(info + *ofs, + len - *ofs, + "%s | ", + BKE_collection_ui_name_get(layer_collection->collection)); + } + + if (ob) { + *ofs += BLI_snprintf(info + *ofs, len - *ofs, "%s | ", ob->id.name + 2); + } + + if (obedit) { + if (BKE_keyblock_from_object(obedit)) { + *ofs += BLI_strncpy_rlen(info + *ofs, TIP_("(Key) "), len - *ofs); + } + + if (obedit->type == OB_MESH) { + *ofs += BLI_snprintf(info + *ofs, + len - *ofs, + TIP_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"), + stats_fmt->totvertsel, + stats_fmt->totvert, + stats_fmt->totedgesel, + stats_fmt->totedge, + stats_fmt->totfacesel, + stats_fmt->totface, + stats_fmt->tottri); + } + else if (obedit->type == OB_ARMATURE) { + *ofs += BLI_snprintf(info + *ofs, + len - *ofs, + TIP_("Verts:%s/%s | Bones:%s/%s"), + stats_fmt->totvertsel, + stats_fmt->totvert, + stats_fmt->totbonesel, + stats_fmt->totbone); + } + else { + *ofs += BLI_snprintf( + info + *ofs, len - *ofs, TIP_("Verts:%s/%s"), stats_fmt->totvertsel, stats_fmt->totvert); + } + } + else if (ob && (object_mode & OB_MODE_POSE)) { + *ofs += BLI_snprintf( + info + *ofs, len - *ofs, TIP_("Bones:%s/%s"), stats_fmt->totbonesel, stats_fmt->totbone); + } + else if ((ob) && (ob->type == OB_GPENCIL)) { + *ofs += BLI_snprintf(info + *ofs, + len - *ofs, + TIP_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"), + stats_fmt->totgplayer, + stats_fmt->totgpframe, + stats_fmt->totgpstroke, + stats_fmt->totgppoint); + } + else if (stats_is_object_dynamic_topology_sculpt(ob, object_mode)) { + *ofs += BLI_snprintf(info + *ofs, + len - *ofs, + TIP_("Verts:%s | Tris:%s"), + stats_fmt->totvert, + stats_fmt->tottri); + } + else { + *ofs += BLI_snprintf(info + *ofs, + len - *ofs, + TIP_("Verts:%s | Faces:%s | Tris:%s"), + stats_fmt->totvert, + stats_fmt->totface, + stats_fmt->tottri); + } + + *ofs += BLI_snprintf( + info + *ofs, len - *ofs, TIP_(" | Objects:%s/%s"), stats_fmt->totobjsel, stats_fmt->totobj); +} + +const char *ED_info_statusbar_string(Main *bmain, bScreen *screen, bContext *C) +{ + char formatted_mem[15]; + size_t ofs = 0; + char *info = screen->statusbar_info; + int len = sizeof(screen->statusbar_info); + + info[0] = '\0'; + + /* Scene statistics. */ + if (U.statusbar_flag & STATUSBAR_SHOW_STATS) { + ViewLayer *view_layer = CTX_data_view_layer(C); + Scene *scene = CTX_data_scene(C); + SceneStatsFmt stats_fmt; + if (format_stats(bmain, scene, view_layer, &stats_fmt)) { + get_stats_string(info + ofs, len, &ofs, view_layer, &stats_fmt); + } + } + + /* Memory status. */ + if (U.statusbar_flag & STATUSBAR_SHOW_MEMORY) { + if (info[0]) { + ofs += BLI_snprintf(info + ofs, len - ofs, " | "); + } + uintptr_t mem_in_use = MEM_get_memory_in_use(); + BLI_str_format_byte_unit(formatted_mem, mem_in_use, false); + ofs += BLI_snprintf(info + ofs, len, TIP_("Memory: %s"), formatted_mem); + } + + /* GPU VRAM status. */ + if ((U.statusbar_flag & STATUSBAR_SHOW_VRAM) && (GPU_mem_stats_supported())) { + int gpu_free_mem_kb, gpu_tot_mem_kb; + GPU_mem_stats_get(&gpu_tot_mem_kb, &gpu_free_mem_kb); + float gpu_total_gb = gpu_tot_mem_kb / 1048576.0f; + float gpu_free_gb = gpu_free_mem_kb / 1048576.0f; + if (info[0]) { + ofs += BLI_snprintf(info + ofs, len - ofs, " | "); + } + if (gpu_free_mem_kb && gpu_tot_mem_kb) { + ofs += BLI_snprintf(info + ofs, + len - ofs, + TIP_("VRAM: %.1f/%.1f GiB"), + gpu_total_gb - gpu_free_gb, + gpu_total_gb); + } + else { + /* Can only show amount of GPU VRAM available. */ + ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("VRAM: %.1f GiB Free"), gpu_free_gb); + } + } + + /* Blender version. */ + if (U.statusbar_flag & STATUSBAR_SHOW_VERSION) { + if (info[0]) { + ofs += BLI_snprintf(info + ofs, len - ofs, " | "); + } + ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("%s"), BKE_blender_version_string()); + } + + return info; +} + +static void stats_row(int col1, + const char *key, + int col2, + const char *value1, + const char *value2, + int *y, + int height) +{ + *y -= height; + BLF_draw_default(col1, *y, 0.0f, key, 128); + char values[128]; + BLI_snprintf(values, sizeof(values), (value2) ? "%s / %s" : "%s", value1, value2); + BLF_draw_default(col2, *y, 0.0f, values, sizeof(values)); +} + +void ED_info_draw_stats( + Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height) +{ + SceneStatsFmt stats_fmt; + if (!format_stats(bmain, scene, view_layer, &stats_fmt)) { + return; + } Object *ob = OBACT(view_layer); Object *obedit = OBEDIT_FROM_OBACT(ob); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index b4098f17212..cc5f7deb418 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1699,6 +1699,11 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph, /* set flags */ G.f |= G_FLAG_RENDER_VIEWPORT; + /* There are too many functions inside the draw manager that check the shading type, + * so use a temporary override instead. */ + const eDrawType drawtype_orig = v3d->shading.type; + v3d->shading.type = drawtype; + { /* free images which can have changed on frame-change * warning! can be slow so only free animated images - campbell */ @@ -1739,6 +1744,7 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph, UI_Theme_Restore(&theme_state); + v3d->shading.type = drawtype_orig; G.f &= ~G_FLAG_RENDER_VIEWPORT; } diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 43341eadb97..50e0bb1f1c2 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -98,6 +98,14 @@ void ED_undo_push(bContext *C, const char *str) if (steps <= 0) { return; } + if (G.background) { + /* Python developers may have explicitly created the undo stack in background mode, + * otherwise allow it to be NULL, see: T60934. + * Otherwise it must never be NULL, even when undo is disabled. */ + if (wm->undo_stack == NULL) { + return; + } + } /* Only apply limit if this is the last undo step. */ if (wm->undo_stack->step_active && (wm->undo_stack->step_active->next == NULL)) { diff --git a/source/blender/functions/FN_multi_function_network.hh b/source/blender/functions/FN_multi_function_network.hh index e47c8260057..91eb5bb65dc 100644 --- a/source/blender/functions/FN_multi_function_network.hh +++ b/source/blender/functions/FN_multi_function_network.hh @@ -233,6 +233,8 @@ class MFNetwork : NonCopyable, NonMovable { VectorSet<const MFOutputSocket *> &r_dummy_sockets, VectorSet<const MFInputSocket *> &r_unlinked_inputs) const; + bool have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const; + std::string to_dot(Span<const MFNode *> marked_nodes = {}) const; }; diff --git a/source/blender/functions/intern/multi_function_network.cc b/source/blender/functions/intern/multi_function_network.cc index 11c9c065f51..1d3d3a8b5f2 100644 --- a/source/blender/functions/intern/multi_function_network.cc +++ b/source/blender/functions/intern/multi_function_network.cc @@ -269,6 +269,14 @@ void MFNetwork::find_dependencies(Span<const MFInputSocket *> sockets, } } +bool MFNetwork::have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const +{ + VectorSet<const MFOutputSocket *> dummy_sockets; + VectorSet<const MFInputSocket *> unlinked_inputs; + this->find_dependencies(sockets, dummy_sockets, unlinked_inputs); + return dummy_sockets.size() + unlinked_inputs.size() > 0; +} + std::string MFNetwork::to_dot(Span<const MFNode *> marked_nodes) const { dot::DirectedGraph digraph; diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h index 56d640ea0e4..eabfb5d2dc3 100644 --- a/source/blender/gpu/GPU_matrix.h +++ b/source/blender/gpu/GPU_matrix.h @@ -102,11 +102,15 @@ struct GPUMatrixUnproject_Precalc { float model_inverted[4][4]; float view[4]; bool is_persp; - /** Result of 'projmat_dimensions'. */ + /** + * Result of #projmat_dimensions_db. + * Using double precision here is important as far clipping ranges + * can cause divide-by-zero when using float, see: T66937. + */ struct { - float xmin, xmax; - float ymin, ymax; - float zmin, zmax; + double xmin, xmax; + double ymin, ymax; + double zmin, zmax; } dims; }; diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c index 1c533c80ab7..77ad16eeb72 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.c @@ -409,7 +409,7 @@ void gpu_extensions_exit(void) bool GPU_mem_stats_supported(void) { #ifndef GPU_STANDALONE - return (GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo) && (G.debug & G_DEBUG_GPU_MEM); + return (GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo); #else return false; #endif diff --git a/source/blender/gpu/intern/gpu_matrix.c b/source/blender/gpu/intern/gpu_matrix.c index d0f7eab32a3..669bf56b726 100644 --- a/source/blender/gpu/intern/gpu_matrix.c +++ b/source/blender/gpu/intern/gpu_matrix.c @@ -536,13 +536,13 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc, const int view[4]) { precalc->is_persp = proj[3][3] == 0.0f; - projmat_dimensions(proj, - &precalc->dims.xmin, - &precalc->dims.xmax, - &precalc->dims.ymin, - &precalc->dims.ymax, - &precalc->dims.zmin, - &precalc->dims.zmax); + projmat_dimensions_db(proj, + &precalc->dims.xmin, + &precalc->dims.xmax, + &precalc->dims.ymin, + &precalc->dims.ymax, + &precalc->dims.zmin, + &precalc->dims.zmax); if (isinf(precalc->dims.zmax)) { /* We cannot retrieve the actual value of the clip_end. * Use `FLT_MAX` to avoid nans. */ diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index 3067be1c485..bf59b720cff 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -116,7 +116,7 @@ typedef struct GPUInput { struct GPUInput *next, *prev; GPUNode *node; - eGPUType type; /* datatype */ + eGPUType type; /* data-type. */ GPUNodeLink *link; int id; /* unique id as created by code generator */ diff --git a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl index cc12e3f78a5..d85884e0a25 100644 --- a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl @@ -136,4 +136,5 @@ void main() } fragColor.a *= color_flat.a; + fragColor = blender_srgb_to_framebuffer_space(fragColor); } diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index fc7e03c3073..478297e61b2 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -291,7 +291,7 @@ void IMB_rectblend_threaded(struct ImBuf *dbuf, */ typedef enum IMB_Timecode_Type { - /** Don't use timecode files at all. */ + /** Don't use time-code files at all. */ IMB_TC_NONE = 0, /** use images in the order as they are recorded * (currently, this is the only one implemented @@ -318,7 +318,7 @@ typedef enum IMB_Proxy_Size { IMB_PROXY_MAX_SLOT = 4, } IMB_Proxy_Size; -/* defaults to BL_proxy within the directory of the animation */ +/* Defaults to BL_proxy within the directory of the animation. */ void IMB_anim_set_index_dir(struct anim *anim, const char *dir); void IMB_anim_get_fname(struct anim *anim, char *file, int size); @@ -328,7 +328,7 @@ IMB_Proxy_Size IMB_anim_proxy_get_existing(struct anim *anim); struct IndexBuildContext; -/* prepare context for proxies/imecodes builder */ +/* Prepare context for proxies/time-codes builder. */ struct IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Timecode_Type tcs_in_use, IMB_Proxy_Size proxy_sizes_in_use, @@ -336,13 +336,13 @@ struct IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, const bool overwrite, struct GSet *file_list); -/* will rebuild all used indices and proxies at once */ +/* Will rebuild all used indices and proxies at once. */ void IMB_anim_index_rebuild(struct IndexBuildContext *context, short *stop, short *do_update, float *progress); -/* finish rebuilding proxises/timecodes and free temporary contexts used */ +/* Finish rebuilding proxies/time-codes and free temporary contexts used. */ void IMB_anim_index_rebuild_finish(struct IndexBuildContext *context, short stop); /** @@ -391,7 +391,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, /** * * \attention Defined in anim_movie.c - * fetches a define previewframe, usually half way into the movie + * fetches a define preview-frame, usually half way into the movie. */ struct ImBuf *IMB_anim_previewframe(struct anim *anim); diff --git a/source/blender/imbuf/intern/IMB_indexer.h b/source/blender/imbuf/intern/IMB_indexer.h index 61bb50aff38..446aaa0655e 100644 --- a/source/blender/imbuf/intern/IMB_indexer.h +++ b/source/blender/imbuf/intern/IMB_indexer.h @@ -31,9 +31,9 @@ /* * separate animation index files to solve the following problems: * - * a) different timecodes within one file (like DTS/PTS, Timecode-Track, - * "implicit" timecodes within DV-files and HDV-files etc.) - * b) seeking difficulties within ffmpeg for files with timestamp holes + * a) different time-codes within one file (like DTS/PTS, Time-code-Track, + * "implicit" time-codes within DV-files and HDV-files etc.) + * b) seeking difficulties within FFMPEG for files with timestamp holes * c) broken files that miss several frames / have varying framerates * d) use proxies accordingly * diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 2a5532a0902..887225f0dc6 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -1209,7 +1209,7 @@ void IMB_exr_read_channels(void *handle) ExrHandle *data = (ExrHandle *)handle; int numparts = data->ifile->parts(); - /* check if exr was saved with previous versions of blender which flipped images */ + /* Check if EXR was saved with previous versions of blender which flipped images. */ const StringAttribute *ta = data->ifile->header(0).findTypedAttribute<StringAttribute>( "BlenderMultiChannel"); @@ -1803,12 +1803,12 @@ static void imb_exr_type_by_channels(ChannelList &channels, } if (!layerNames.empty()) { - /* if layerNames is not empty, it means at least one layer is non-empty, + /* If `layerNames` is not empty, it means at least one layer is non-empty, * but it also could be layers without names in the file and such case - * shall be considered a multilayer exr + * shall be considered a multi-layer EXR. * - * that's what we do here: test whether there're empty layer names together - * with non-empty ones in the file + * That's what we do here: test whether there are empty layer names together + * with non-empty ones in the file. */ for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); i++) { for (std::set<string>::iterator i = layerNames.begin(); i != layerNames.end(); i++) { diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc index f42349d99e8..d0db1284c77 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc @@ -73,7 +73,7 @@ std::string USDHierarchyIterator::make_valid_name(const std::string &name) const void USDHierarchyIterator::set_export_frame(float frame_nr) { - // The USD stage is already set up to have FPS timecodes per frame. + /* The USD stage is already set up to have FPS time-codes per frame. */ export_time_ = pxr::UsdTimeCode(frame_nr); } diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index 6348dc5f03d..cb604fd6681 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -115,7 +115,6 @@ typedef struct ViewLayer { ListBase object_bases; /** Default allocated now. */ struct SceneStats *stats; - char footer_str[128]; struct Base *basact; /** A view layer has one top level layer collection, because a scene has only one top level diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 72421ecb79d..bf5c097322f 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -69,6 +69,8 @@ typedef struct bScreen { /** User-setting for which editors get redrawn during anim playback. */ short redraws_flag; + char statusbar_info[256]; + /** Temp screen in a temp window, don't save (like user prefs). */ char temp; /** Temp screen for image render display or fileselect. */ diff --git a/source/blender/makesdna/DNA_simulation_types.h b/source/blender/makesdna/DNA_simulation_types.h index a2b81b731d3..c4ff51a107e 100644 --- a/source/blender/makesdna/DNA_simulation_types.h +++ b/source/blender/makesdna/DNA_simulation_types.h @@ -32,6 +32,8 @@ typedef struct Simulation { int flag; float current_frame; + float current_simulation_time; + char _pad[4]; /** List containing SimulationState objects. */ struct ListBase states; @@ -53,7 +55,7 @@ typedef struct ParticleSimulationState { /** Contains the state of the particles at time Simulation->current_frame. */ int tot_particles; - int _pad; + int next_particle_id; struct CustomData attributes; /** Caches the state of the particles over time. The cache only exists on the original data diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 8ea4d3b6476..e6f6ce1e208 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -661,9 +661,9 @@ typedef struct UserDef { char anim_player[1024]; int anim_player_preset; - /** Minimum spacing between gridlines in View2D grids. */ + /** Minimum spacing between grid-lines in View2D grids. */ short v2d_min_gridsize; - /** #eTimecodeStyles, style of timecode display. */ + /** #eTimecodeStyles, style of time-code display. */ short timecode_style; short versions; @@ -694,7 +694,7 @@ typedef struct UserDef { float ui_scale; /** Setting for UI line width. */ int ui_line_width; - /** Runtime, full DPI divided by pixelsize. */ + /** Runtime, full DPI divided by `pixelsize`. */ int dpi; /** Runtime, multiplier to scale UI elements based on DPI. */ float dpi_fac; @@ -704,7 +704,7 @@ typedef struct UserDef { /** Deprecated, for forward compatibility. */ int virtual_pixel; - /** Console scrollback limit. */ + /** Console scroll-back limit. */ int scrollback; /** Node insert offset (aka auto-offset) margin, but might be useful for later stuff as well. */ char node_margin; @@ -880,7 +880,9 @@ typedef struct UserDef { char _pad5[2]; float collection_instance_empty_size; - char _pad10[4]; + char _pad10[3]; + + char statusbar_flag; /* eUserpref_StatusBar_Flag */ struct WalkNavigation walk_navigation; @@ -1079,6 +1081,14 @@ typedef enum eUserpref_APP_Flag { USER_APP_LOCK_UI_LAYOUT = (1 << 0), } eUserpref_APP_Flag; +/** #UserDef.statusbar_flag */ +typedef enum eUserpref_StatusBar_Flag { + STATUSBAR_SHOW_MEMORY = (1 << 0), + STATUSBAR_SHOW_VRAM = (1 << 1), + STATUSBAR_SHOW_STATS = (1 << 2), + STATUSBAR_SHOW_VERSION = (1 << 3), +} eUserpref_StatusBar_Flag; + /** * Auto-Keying mode. * #UserDef.autokey_mode diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c index f3c73e75434..b94221ae936 100644 --- a/source/blender/makesrna/intern/rna_movieclip.c +++ b/source/blender/makesrna/intern/rna_movieclip.c @@ -218,7 +218,7 @@ static void rna_def_movieclip_proxy(BlenderRNA *brna) RNA_def_property_ui_text( prop, "100%", "Build proxy resolution 100% of the original undistorted footage dimension"); - /* build timecodes */ + /* Build time-codes. */ prop = RNA_def_property(srna, "build_record_run", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "build_tc_flag", IMB_TC_RECORD_RUN); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 9b98be61cbf..a6247474c69 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -924,13 +924,6 @@ static void rna_Scene_volume_update(Main *UNUSED(bmain), Scene *UNUSED(scene), P DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_VOLUME | ID_RECALC_SEQUENCER_STRIPS); } -static const char *rna_Scene_statistics_string_get(Scene *UNUSED(scene), - Main *UNUSED(bmain), - ViewLayer *view_layer) -{ - return ED_info_footer_string(view_layer); -} - static void rna_Scene_framelen_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) { scene->r.framelen = (float)scene->r.framapto / (float)scene->r.images; @@ -3456,7 +3449,7 @@ static void rna_def_tool_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "use_edge_path_live_unwrap", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "edge_mode_live_unwrap", 1); - RNA_def_property_ui_text(prop, "Live Unwrap", "Changing edges seam re-calculates UV unwrap"); + RNA_def_property_ui_text(prop, "Live Unwrap", "Changing edges seam recalculates UV unwrap"); prop = RNA_def_property(srna, "normal_vector", PROP_FLOAT, PROP_XYZ); RNA_def_property_ui_text(prop, "Normal Vector", "Normal Vector used to copy, add or multiply"); @@ -4814,7 +4807,7 @@ void rna_def_freestyle_settings(BlenderRNA *brna) RNA_def_property_ui_text( prop, "View Map Cache", - "Keep the computed view map and avoid re-calculating it if mesh geometry is unchanged"); + "Keep the computed view map and avoid recalculating it if mesh geometry is unchanged"); RNA_def_property_update( prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_use_view_map_cache_update"); @@ -7275,9 +7268,6 @@ void RNA_def_scene(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - FunctionRNA *func; - PropertyRNA *parm; - static const EnumPropertyItem audio_distance_model_items[] = { {0, "NONE", 0, "None", "No distance attenuation"}, {1, "INVERSE", 0, "Inverse", "Inverse distance model"}, @@ -7669,14 +7659,6 @@ void RNA_def_scene(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE, NULL); RNA_def_property_update(prop, NC_SCENE, "rna_Scene_volume_update"); - /* Statistics */ - func = RNA_def_function(srna, "statistics", "rna_Scene_statistics_string_get"); - RNA_def_function_flag(func, FUNC_USE_MAIN); - parm = RNA_def_pointer(func, "view_layer", "ViewLayer", "", "Active layer"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_string(func, "statistics", NULL, 0, "Statistics", ""); - RNA_def_function_return(func, parm); - /* Grease Pencil */ prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "gpd"); diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index ea6421c8d11..2b65bf4922c 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -30,6 +30,8 @@ #include "DNA_screen_types.h" #include "DNA_workspace_types.h" +#include "ED_info.h" + const EnumPropertyItem rna_enum_region_type_items[] = { {RGN_TYPE_WINDOW, "WINDOW", 0, "Window", ""}, {RGN_TYPE_HEADER, "HEADER", 0, "Header", ""}, @@ -286,6 +288,11 @@ static void rna_View2D_view_to_region( } } +static const char *rna_Screen_statusbar_info_get(struct bScreen *screen, Main *bmain, bContext *C) +{ + return ED_info_statusbar_string(bmain, screen, C); +} + #else /* Area.spaces */ @@ -536,6 +543,9 @@ static void rna_def_screen(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; + FunctionRNA *func; + PropertyRNA *parm; + srna = RNA_def_struct(brna, "Screen", "ID"); RNA_def_struct_sdna(srna, "Screen"); /* it is actually bScreen but for 2.5 the dna is patched! */ RNA_def_struct_ui_text( @@ -570,11 +580,18 @@ static void rna_def_screen(BlenderRNA *brna) RNA_def_property_boolean_funcs(prop, "rna_Screen_fullscreen_get", NULL); RNA_def_property_ui_text(prop, "Maximize", "An area is maximized, filling this screen"); + /* Status Bar. */ + prop = RNA_def_property(srna, "show_statusbar", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SCREEN_COLLAPSE_STATUSBAR); - RNA_def_property_ui_text(prop, "Show Status Bar", "Show status bar"); + RNA_def_property_ui_text(prop, "Show Status Bar", "Show Status Bar"); RNA_def_property_update(prop, 0, "rna_Screen_bar_update"); + func = RNA_def_function(srna, "statusbar_info", "rna_Screen_statusbar_info_get"); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_CONTEXT); + parm = RNA_def_string(func, "statusbar_info", NULL, 0, "Status Bar Info", ""); + RNA_def_function_return(func, parm); + /* Define Anim Playback Areas */ prop = RNA_def_property(srna, "use_play_top_left_3d_editor", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "redraws_flag", TIME_REGION); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index de225e3c685..6589ae4b8da 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2145,7 +2145,7 @@ static void rna_def_proxy(StructRNA *srna) prop = RNA_def_property(srna, "use_proxy", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_PROXY); RNA_def_property_ui_text( - prop, "Use Proxy / Timecode", "Use a preview proxy and/or timecode index for this strip"); + prop, "Use Proxy / Timecode", "Use a preview proxy and/or time-code index for this strip"); RNA_def_property_boolean_funcs(prop, NULL, "rna_Sequence_use_proxy_set"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update"); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 956fb65054b..4884b536258 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -188,6 +188,7 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = { # include "DEG_depsgraph.h" # include "GPU_draw.h" +# include "GPU_extensions.h" # include "GPU_select.h" # include "BLF_api.h" @@ -439,13 +440,12 @@ static void rna_userdef_timecode_style_set(PointerRNA *ptr, int value) UserDef *userdef = (UserDef *)ptr->data; int required_size = userdef->v2d_min_gridsize; - /* set the timecode style */ + /* Set the time-code style. */ userdef->timecode_style = value; - /* adjust the v2d gridsize if needed so that timecodes don't overlap + /* Adjust the v2d grid-size if needed so that time-codes don't overlap * NOTE: most of these have been hand-picked to avoid overlaps while still keeping - * things from getting too blown out - */ + * things from getting too blown out. */ switch (value) { case USER_TIMECODE_MINIMAL: case USER_TIMECODE_SECONDS_ONLY: @@ -1066,6 +1066,11 @@ static void rna_UserDef_studiolight_light_ambient_get(PointerRNA *ptr, float *va copy_v3_v3(values, sl->light_ambient); } +int rna_show_statusbar_vram_editable(struct PointerRNA *UNUSED(ptr), const char **UNUSED(r_info)) +{ + return GPU_mem_stats_supported() ? PROP_EDITABLE : 0; +} + #else # define USERDEF_TAG_DIRTY_PROPERTY_UPDATE_ENABLE \ @@ -4771,6 +4776,29 @@ static void rna_def_userdef_view(BlenderRNA *brna) "Translate New Names", "Translate the names of new data-blocks (objects, materials...)"); RNA_def_property_update(prop, 0, "rna_userdef_update"); + + /* Statusbar. */ + + prop = RNA_def_property(srna, "show_statusbar_memory", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "statusbar_flag", STATUSBAR_SHOW_MEMORY); + RNA_def_property_ui_text(prop, "Show Memory", "Show Blender memory usage"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_INFO, "rna_userdef_update"); + + prop = RNA_def_property(srna, "show_statusbar_vram", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "statusbar_flag", STATUSBAR_SHOW_VRAM); + RNA_def_property_ui_text(prop, "Show VRAM", "Show GPU video memory usage"); + RNA_def_property_editable_func(prop, "rna_show_statusbar_vram_editable"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_INFO, "rna_userdef_update"); + + prop = RNA_def_property(srna, "show_statusbar_version", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "statusbar_flag", STATUSBAR_SHOW_VERSION); + RNA_def_property_ui_text(prop, "Show Version", "Show Blender version string"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_INFO, "rna_userdef_update"); + + prop = RNA_def_property(srna, "show_statusbar_stats", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "statusbar_flag", STATUSBAR_SHOW_STATS); + RNA_def_property_ui_text(prop, "Show Statistics", "Show scene statistics"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_INFO, "rna_userdef_update"); } static void rna_def_userdef_edit(BlenderRNA *brna) diff --git a/source/blender/modifiers/intern/MOD_simulation.cc b/source/blender/modifiers/intern/MOD_simulation.cc index 38dc1546763..819ed320255 100644 --- a/source/blender/modifiers/intern/MOD_simulation.cc +++ b/source/blender/modifiers/intern/MOD_simulation.cc @@ -129,7 +129,7 @@ static PointCloud *modifyPointCloud(ModifierData *md, memcpy(pointcloud->co, positions, sizeof(float3) * state->tot_particles); for (int i = 0; i < state->tot_particles; i++) { - pointcloud->radius[i] = 0.1f; + pointcloud->radius[i] = 0.03f; } return pointcloud; diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index be5a92309f2..ed5e505176c 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -268,11 +268,11 @@ void BPY_python_start(int argc, const char **argv) /* allow to use our own included python */ PyC_SetHomePath(py_path_bundle); - /* without this the sys.stdout may be set to 'ascii' + /* Without this the `sys.stdout` may be set to 'ascii' * (it is on my system at least), where printing unicode values will raise - * an error, this is highly annoying, another stumbling block for devs, + * an error, this is highly annoying, another stumbling block for developers, * so use a more relaxed error handler and enforce utf-8 since the rest of - * blender is utf-8 too - campbell */ + * Blender is utf-8 too - campbell */ Py_SetStandardStreamEncoding("utf-8", "surrogateescape"); /* Suppress error messages when calculating the module search path. diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index ebbbc8f3140..893832b61b6 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -7660,8 +7660,8 @@ void BPY_update_rna_module(void) } #if 0 -/* This is a way we can access docstrings for RNA types - * without having the datatypes in blender */ +/* This is a way we can access doc-strings for RNA types + * without having the data-types in Blender. */ PyObject *BPY_rna_doc(void) { PointerRNA ptr; diff --git a/source/blender/python/intern/bpy_traceback.c b/source/blender/python/intern/bpy_traceback.c index dd7e6b03e8b..e2c894e90f8 100644 --- a/source/blender/python/intern/bpy_traceback.c +++ b/source/blender/python/intern/bpy_traceback.c @@ -140,8 +140,8 @@ void python_script_error_jump(const char *filepath, int *lineno, int *offset) PyErr_Fetch(&exception, &value, (PyObject **)&tb); if (exception && PyErr_GivenExceptionMatches(exception, PyExc_SyntaxError)) { - /* no traceback available when SyntaxError. - * python has no api's to this. reference parse_syntax_error() from pythonrun.c */ + /* no trace-back available when `SyntaxError`. + * python has no API's to this. reference #parse_syntax_error() from pythonrun.c */ PyErr_NormalizeException(&exception, &value, (PyObject **)&tb); if (value) { /* should always be true */ diff --git a/source/blender/simulation/CMakeLists.txt b/source/blender/simulation/CMakeLists.txt index a19e96e1a91..243b056db74 100644 --- a/source/blender/simulation/CMakeLists.txt +++ b/source/blender/simulation/CMakeLists.txt @@ -41,6 +41,7 @@ set(SRC intern/hair_volume.cpp intern/implicit_blender.c intern/implicit_eigen.cpp + intern/particle_allocator.cc intern/particle_function.cc intern/simulation_collect_influences.cc intern/simulation_solver.cc @@ -49,11 +50,13 @@ set(SRC intern/ConstrainedConjugateGradient.h intern/eigen_utils.h intern/implicit.h + intern/particle_allocator.hh + intern/particle_function.hh intern/simulation_collect_influences.hh intern/simulation_solver.hh + intern/time_interval.hh SIM_mass_spring.h - SIM_particle_function.hh SIM_simulation_update.hh ) diff --git a/source/blender/simulation/intern/particle_allocator.cc b/source/blender/simulation/intern/particle_allocator.cc new file mode 100644 index 00000000000..b65c0197c76 --- /dev/null +++ b/source/blender/simulation/intern/particle_allocator.cc @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#include "particle_allocator.hh" + +namespace blender::sim { + +AttributesAllocator::~AttributesAllocator() +{ + for (std::unique_ptr<AttributesBlock> &block : allocated_blocks_) { + for (uint i : attributes_info_.index_range()) { + const fn::CPPType &type = attributes_info_.type_of(i); + type.destruct_n(block->buffers[i], block->size); + MEM_freeN(block->buffers[i]); + } + } +} + +fn::MutableAttributesRef AttributesAllocator::allocate_uninitialized(uint size) +{ + std::unique_ptr<AttributesBlock> block = std::make_unique<AttributesBlock>(); + block->buffers = Array<void *>(attributes_info_.size(), nullptr); + block->size = size; + + for (uint i : attributes_info_.index_range()) { + const fn::CPPType &type = attributes_info_.type_of(i); + void *buffer = MEM_mallocN_aligned(size * type.size(), type.alignment(), AT); + block->buffers[i] = buffer; + } + + fn::MutableAttributesRef attributes{attributes_info_, block->buffers, size}; + + { + std::lock_guard lock{mutex_}; + allocated_blocks_.append(std::move(block)); + allocated_attributes_.append(attributes); + total_allocated_ += size; + } + + return attributes; +} + +fn::MutableAttributesRef ParticleAllocator::allocate(uint size) +{ + const fn::AttributesInfo &info = attributes_allocator_.attributes_info(); + fn::MutableAttributesRef attributes = attributes_allocator_.allocate_uninitialized(size); + for (uint i : info.index_range()) { + const fn::CPPType &type = info.type_of(i); + StringRef name = info.name_of(i); + if (name == "ID") { + uint start_id = next_id_.fetch_add(size); + MutableSpan<int> ids = attributes.get<int>("ID"); + for (uint pindex : IndexRange(size)) { + ids[pindex] = start_id + pindex; + } + } + else { + type.fill_uninitialized(info.default_of(i), attributes.get(i).buffer(), size); + } + } + return attributes; +} + +} // namespace blender::sim diff --git a/source/blender/simulation/intern/particle_allocator.hh b/source/blender/simulation/intern/particle_allocator.hh new file mode 100644 index 00000000000..f854413c9aa --- /dev/null +++ b/source/blender/simulation/intern/particle_allocator.hh @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#ifndef __SIM_PARTICLE_ALLOCATOR_HH__ +#define __SIM_PARTICLE_ALLOCATOR_HH__ + +#include "BLI_array.hh" +#include "BLI_vector.hh" + +#include "FN_attributes_ref.hh" + +#include <atomic> +#include <mutex> + +namespace blender::sim { + +class AttributesAllocator : NonCopyable, NonMovable { + private: + struct AttributesBlock { + Array<void *> buffers; + uint size; + }; + + const fn::AttributesInfo &attributes_info_; + Vector<std::unique_ptr<AttributesBlock>> allocated_blocks_; + Vector<fn::MutableAttributesRef> allocated_attributes_; + uint total_allocated_ = 0; + std::mutex mutex_; + + public: + AttributesAllocator(const fn::AttributesInfo &attributes_info) + : attributes_info_(attributes_info) + { + } + + ~AttributesAllocator(); + + Span<fn::MutableAttributesRef> get_allocations() const + { + return allocated_attributes_; + } + + uint total_allocated() const + { + return total_allocated_; + } + + const fn::AttributesInfo &attributes_info() const + { + return attributes_info_; + } + + fn::MutableAttributesRef allocate_uninitialized(uint size); +}; + +class ParticleAllocator : NonCopyable, NonMovable { + private: + AttributesAllocator attributes_allocator_; + std::atomic<uint> next_id_; + + public: + ParticleAllocator(const fn::AttributesInfo &attributes_info, uint next_id) + : attributes_allocator_(attributes_info), next_id_(next_id) + { + } + + Span<fn::MutableAttributesRef> get_allocations() const + { + return attributes_allocator_.get_allocations(); + } + + uint total_allocated() const + { + return attributes_allocator_.total_allocated(); + } + + fn::MutableAttributesRef allocate(uint size); +}; + +} // namespace blender::sim + +#endif /* __SIM_PARTICLE_ALLOCATOR_HH__ */ diff --git a/source/blender/simulation/intern/particle_function.cc b/source/blender/simulation/intern/particle_function.cc index 7a0c9ccdb13..3788fd17e36 100644 --- a/source/blender/simulation/intern/particle_function.cc +++ b/source/blender/simulation/intern/particle_function.cc @@ -14,7 +14,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "SIM_particle_function.hh" +#include "particle_function.hh" namespace blender::sim { @@ -49,12 +49,11 @@ ParticleFunction::ParticleFunction(const fn::MultiFunction *global_fn, } } -ParticleFunctionEvaluator::ParticleFunctionEvaluator(const ParticleFunction &particle_fn, - IndexMask mask, - fn::AttributesRef particle_attributes) +ParticleFunctionEvaluator::ParticleFunctionEvaluator( + const ParticleFunction &particle_fn, const ParticleChunkContext &particle_chunk_context) : particle_fn_(particle_fn), - mask_(mask), - particle_attributes_(particle_attributes), + particle_chunk_context_(particle_chunk_context), + mask_(particle_chunk_context_.index_mask()), outputs_(particle_fn_.output_types_.size(), nullptr) { } @@ -112,7 +111,7 @@ void ParticleFunctionEvaluator::compute_globals() /* Add input parameters. */ for (const ParticleFunctionInput *input : particle_fn_.global_inputs_) { - input->add_input(particle_attributes_, params, resources_); + input->add_input(particle_chunk_context_.attributes(), params, resources_); } /* Add output parameters. */ @@ -139,7 +138,7 @@ void ParticleFunctionEvaluator::compute_per_particle() /* Add input parameters. */ for (const ParticleFunctionInput *input : particle_fn_.per_particle_inputs_) { - input->add_input(particle_attributes_, params, resources_); + input->add_input(particle_chunk_context_.attributes(), params, resources_); } /* Add output parameters. */ diff --git a/source/blender/simulation/SIM_particle_function.hh b/source/blender/simulation/intern/particle_function.hh index eae61b1ae11..abed9063bae 100644 --- a/source/blender/simulation/SIM_particle_function.hh +++ b/source/blender/simulation/intern/particle_function.hh @@ -22,6 +22,8 @@ #include "BLI_resource_collector.hh" +#include "simulation_solver.hh" + namespace blender::sim { class ParticleFunctionInput { @@ -58,17 +60,16 @@ class ParticleFunctionEvaluator { private: ResourceCollector resources_; const ParticleFunction &particle_fn_; + const ParticleChunkContext &particle_chunk_context_; IndexMask mask_; fn::MFContextBuilder global_context_; fn::MFContextBuilder per_particle_context_; - fn::AttributesRef particle_attributes_; Vector<void *> outputs_; bool is_computed_ = false; public: ParticleFunctionEvaluator(const ParticleFunction &particle_fn, - IndexMask mask, - fn::AttributesRef particle_attributes); + const ParticleChunkContext &particle_chunk_context); ~ParticleFunctionEvaluator(); void compute(); diff --git a/source/blender/simulation/intern/simulation_collect_influences.cc b/source/blender/simulation/intern/simulation_collect_influences.cc index 3feb0ccce5b..98b055802c9 100644 --- a/source/blender/simulation/intern/simulation_collect_influences.cc +++ b/source/blender/simulation/intern/simulation_collect_influences.cc @@ -15,7 +15,7 @@ */ #include "simulation_collect_influences.hh" -#include "SIM_particle_function.hh" +#include "particle_function.hh" #include "FN_attributes_ref.hh" #include "FN_multi_function_network_evaluation.hh" @@ -23,49 +23,88 @@ #include "NOD_node_tree_multi_function.hh" +#include "BLI_rand.hh" + namespace blender::sim { +struct DummyDataSources { + Map<const fn::MFOutputSocket *, std::string> particle_attributes; +}; + extern "C" { void WM_clipboard_text_set(const char *buf, bool selection); } -static Map<const fn::MFOutputSocket *, std::string> deduplicate_attribute_nodes( - fn::MFNetwork &network, - nodes::MFNetworkTreeMap &network_map, - const nodes::DerivedNodeTree &tree) +static std::string dnode_to_path(const nodes::DNode &dnode) { - Span<const nodes::DNode *> attribute_dnodes = tree.nodes_by_type( - "SimulationNodeParticleAttribute"); - uint amount = attribute_dnodes.size(); + std::string path; + for (const nodes::DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) { + path = parent->node_ref().name() + "/" + path; + } + path = path + dnode.name(); + return path; +} + +static Span<const nodes::DNode *> get_particle_simulation_nodes(const nodes::DerivedNodeTree &tree) +{ + return tree.nodes_by_type("SimulationNodeParticleSimulation"); +} + +static std::optional<Array<std::string>> compute_global_string_inputs( + nodes::MFNetworkTreeMap &network_map, Span<const fn::MFInputSocket *> sockets) +{ + uint amount = sockets.size(); if (amount == 0) { - return {}; + return Array<std::string>(); } - Vector<fn::MFInputSocket *> name_sockets; - for (const nodes::DNode *dnode : attribute_dnodes) { - fn::MFInputSocket &name_socket = network_map.lookup_dummy(dnode->input(0)); - name_sockets.append(&name_socket); + if (network_map.network().have_dummy_or_unlinked_dependencies(sockets)) { + return {}; } - fn::MFNetworkEvaluator network_fn{{}, name_sockets.as_span()}; + fn::MFNetworkEvaluator network_fn{{}, sockets}; fn::MFParamsBuilder params{network_fn, 1}; - Array<std::string> attribute_names{amount, NoInitialization()}; + Array<std::string> strings(amount, NoInitialization()); for (uint i : IndexRange(amount)) { params.add_uninitialized_single_output( - fn::GMutableSpan(fn::CPPType::get<std::string>(), attribute_names.data() + i, 1)); + fn::GMutableSpan(fn::CPPType::get<std::string>(), strings.data() + i, 1)); } fn::MFContextBuilder context; - /* Todo: Check that the names don't depend on dummy nodes. */ network_fn.call({0}, params, context); + return strings; +} + +static void find_and_deduplicate_particle_attribute_nodes(nodes::MFNetworkTreeMap &network_map, + DummyDataSources &r_data_sources) +{ + fn::MFNetwork &network = network_map.network(); + const nodes::DerivedNodeTree &tree = network_map.tree(); + + Span<const nodes::DNode *> attribute_dnodes = tree.nodes_by_type( + "SimulationNodeParticleAttribute"); + + Vector<fn::MFInputSocket *> name_sockets; + for (const nodes::DNode *dnode : attribute_dnodes) { + fn::MFInputSocket &name_socket = network_map.lookup_dummy(dnode->input(0)); + name_sockets.append(&name_socket); + } + + std::optional<Array<std::string>> attribute_names = compute_global_string_inputs(network_map, + name_sockets); + if (!attribute_names.has_value()) { + return; + } + Map<std::pair<std::string, fn::MFDataType>, Vector<fn::MFNode *>> attribute_nodes_by_name_and_type; - for (uint i : IndexRange(amount)) { + for (uint i : attribute_names->index_range()) { attribute_nodes_by_name_and_type - .lookup_or_add_default({attribute_names[i], name_sockets[i]->node().output(0).data_type()}) + .lookup_or_add_default( + {(*attribute_names)[i], name_sockets[i]->node().output(0).data_type()}) .append(&name_sockets[i]->node()); } @@ -82,10 +121,8 @@ static Map<const fn::MFOutputSocket *, std::string> deduplicate_attribute_nodes( } network.remove(nodes); - attribute_inputs.add_new(&new_attribute_socket, attribute_name); + r_data_sources.particle_attributes.add_new(&new_attribute_socket, attribute_name); } - - return attribute_inputs; } class ParticleAttributeInput : public ParticleFunctionInput { @@ -116,7 +153,7 @@ class ParticleAttributeInput : public ParticleFunctionInput { static const ParticleFunction *create_particle_function_for_inputs( Span<const fn::MFInputSocket *> sockets_to_compute, ResourceCollector &resources, - const Map<const fn::MFOutputSocket *, std::string> &attribute_inputs) + DummyDataSources &data_sources) { BLI_assert(sockets_to_compute.size() >= 1); const fn::MFNetwork &network = sockets_to_compute[0]->node().network(); @@ -128,7 +165,7 @@ static const ParticleFunction *create_particle_function_for_inputs( Vector<const ParticleFunctionInput *> per_particle_inputs; for (const fn::MFOutputSocket *socket : dummy_deps) { - const std::string *attribute_name = attribute_inputs.lookup_ptr(socket); + const std::string *attribute_name = data_sources.particle_attributes.lookup_ptr(socket); if (attribute_name == nullptr) { return nullptr; } @@ -161,12 +198,15 @@ class ParticleFunctionForce : public ParticleForce { { } - void add_force(fn::AttributesRef attributes, MutableSpan<float3> r_combined_force) const override + void add_force(ParticleForceContext &context) const override { - IndexMask mask = IndexRange(attributes.size()); - ParticleFunctionEvaluator evaluator{particle_fn_, mask, attributes}; + IndexMask mask = context.particle_chunk().index_mask(); + MutableSpan<float3> r_combined_force = context.force_dst(); + + ParticleFunctionEvaluator evaluator{particle_fn_, context.particle_chunk()}; evaluator.compute(); fn::VSpan<float3> forces = evaluator.get<float3>(0, "Force"); + for (uint i : mask) { r_combined_force[i] += forces[i]; } @@ -177,7 +217,7 @@ static Vector<const ParticleForce *> create_forces_for_particle_simulation( const nodes::DNode &simulation_node, nodes::MFNetworkTreeMap &network_map, ResourceCollector &resources, - const Map<const fn::MFOutputSocket *, std::string> &attribute_inputs) + DummyDataSources &data_sources) { Vector<const ParticleForce *> forces; for (const nodes::DOutputSocket *origin_socket : @@ -191,7 +231,7 @@ static Vector<const ParticleForce *> create_forces_for_particle_simulation( origin_node.input(0, "Force")); const ParticleFunction *particle_fn = create_particle_function_for_inputs( - {&force_socket}, resources, attribute_inputs); + {&force_socket}, resources, data_sources); if (particle_fn == nullptr) { continue; @@ -205,32 +245,99 @@ static Vector<const ParticleForce *> create_forces_for_particle_simulation( static void collect_forces(nodes::MFNetworkTreeMap &network_map, ResourceCollector &resources, - const Map<const fn::MFOutputSocket *, std::string> &attribute_inputs, + DummyDataSources &data_sources, SimulationInfluences &r_influences) { - for (const nodes::DNode *dnode : - network_map.tree().nodes_by_type("SimulationNodeParticleSimulation")) { + for (const nodes::DNode *dnode : get_particle_simulation_nodes(network_map.tree())) { std::string name = dnode_to_path(*dnode); Vector<const ParticleForce *> forces = create_forces_for_particle_simulation( - *dnode, network_map, resources, attribute_inputs); + *dnode, network_map, resources, data_sources); r_influences.particle_forces.add_new(std::move(name), std::move(forces)); } } -void collect_simulation_influences(const nodes::DerivedNodeTree &tree, +class MyBasicEmitter : public ParticleEmitter { + private: + std::string name_; + + public: + MyBasicEmitter(std::string name) : name_(std::move(name)) + { + } + + void emit(ParticleEmitterContext &context) const override + { + ParticleAllocator *allocator = context.try_get_particle_allocator(name_); + if (allocator == nullptr) { + return; + } + + fn::MutableAttributesRef attributes = allocator->allocate(10); + RandomNumberGenerator rng{(uint)context.simulation_time_interval().start() ^ + DefaultHash<std::string>{}(name_)}; + + MutableSpan<float3> positions = attributes.get<float3>("Position"); + MutableSpan<float3> velocities = attributes.get<float3>("Velocity"); + + for (uint i : IndexRange(attributes.size())) { + positions[i] = rng.get_unit_float3(); + velocities[i] = rng.get_unit_float3(); + } + } +}; + +static void collect_emitters(nodes::MFNetworkTreeMap &network_map, + ResourceCollector &resources, + SimulationInfluences &r_influences) +{ + for (const nodes::DNode *dnode : get_particle_simulation_nodes(network_map.tree())) { + std::string name = dnode_to_path(*dnode); + ParticleEmitter &emitter = resources.construct<MyBasicEmitter>(AT, name); + r_influences.particle_emitters.append(&emitter); + } +} + +static void prepare_particle_attribute_builders(nodes::MFNetworkTreeMap &network_map, + ResourceCollector &resources, + SimulationInfluences &r_influences) +{ + for (const nodes::DNode *dnode : get_particle_simulation_nodes(network_map.tree())) { + std::string name = dnode_to_path(*dnode); + fn::AttributesInfoBuilder &builder = resources.construct<fn::AttributesInfoBuilder>(AT); + builder.add<float3>("Position", {0, 0, 0}); + builder.add<float3>("Velocity", {0, 0, 0}); + builder.add<int>("ID", 0); + r_influences.particle_attributes_builder.add_new(std::move(name), &builder); + } +} + +void collect_simulation_influences(Simulation &simulation, ResourceCollector &resources, - SimulationInfluences &r_influences) + SimulationInfluences &r_influences, + SimulationStatesInfo &r_states_info) { + nodes::NodeTreeRefMap tree_refs; + const nodes::DerivedNodeTree tree{simulation.nodetree, tree_refs}; + fn::MFNetwork &network = resources.construct<fn::MFNetwork>(AT); nodes::MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources); - Map<const fn::MFOutputSocket *, std::string> attribute_inputs = deduplicate_attribute_nodes( - network, network_map, tree); + + prepare_particle_attribute_builders(network_map, resources, r_influences); + + DummyDataSources data_sources; + find_and_deduplicate_particle_attribute_nodes(network_map, data_sources); + fn::mf_network_optimization::constant_folding(network, resources); fn::mf_network_optimization::common_subnetwork_elimination(network); fn::mf_network_optimization::dead_node_removal(network); // WM_clipboard_text_set(network.to_dot().c_str(), false); - collect_forces(network_map, resources, attribute_inputs, r_influences); + collect_forces(network_map, resources, data_sources, r_influences); + collect_emitters(network_map, resources, r_influences); + + for (const nodes::DNode *dnode : get_particle_simulation_nodes(tree)) { + r_states_info.particle_simulation_names.add(dnode_to_path(*dnode)); + } } } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_collect_influences.hh b/source/blender/simulation/intern/simulation_collect_influences.hh index a02a6320419..cca396ffa66 100644 --- a/source/blender/simulation/intern/simulation_collect_influences.hh +++ b/source/blender/simulation/intern/simulation_collect_influences.hh @@ -25,20 +25,14 @@ namespace blender::sim { -void collect_simulation_influences(const nodes::DerivedNodeTree &tree, +struct SimulationStatesInfo { + VectorSet<std::string> particle_simulation_names; +}; + +void collect_simulation_influences(Simulation &simulation, ResourceCollector &resources, - SimulationInfluences &r_influences); - -/* TODO: Move this to a better place. */ -inline std::string dnode_to_path(const nodes::DNode &dnode) -{ - std::string path; - for (const nodes::DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) { - path = parent->node_ref().name() + "/" + path; - } - path = path + dnode.name(); - return path; -} + SimulationInfluences &r_influences, + SimulationStatesInfo &r_states_info); } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver.cc b/source/blender/simulation/intern/simulation_solver.cc index c51e8e1a37f..310261f6c95 100644 --- a/source/blender/simulation/intern/simulation_solver.cc +++ b/source/blender/simulation/intern/simulation_solver.cc @@ -26,57 +26,103 @@ ParticleForce::~ParticleForce() { } +ParticleEmitter::~ParticleEmitter() +{ +} + +static CustomDataType cpp_to_custom_data_type(const fn::CPPType &type) +{ + if (type.is<float3>()) { + return CD_PROP_FLOAT3; + } + if (type.is<float>()) { + return CD_PROP_FLOAT; + } + if (type.is<int32_t>()) { + return CD_PROP_INT32; + } + BLI_assert(false); + return CD_PROP_FLOAT; +} + +static const fn::CPPType &custom_to_cpp_data_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_FLOAT3: + return fn::CPPType::get<float3>(); + case CD_PROP_FLOAT: + return fn::CPPType::get<float>(); + case CD_PROP_INT32: + return fn::CPPType::get<int32_t>(); + default: + BLI_assert(false); + return fn::CPPType::get<float>(); + } +} + class CustomDataAttributesRef { private: - Vector<void *> buffers_; + Array<void *> buffers_; uint size_; - std::unique_ptr<fn::AttributesInfo> info_; + const fn::AttributesInfo &info_; public: - CustomDataAttributesRef(CustomData &custom_data, uint size) + CustomDataAttributesRef(CustomData &custom_data, uint size, const fn::AttributesInfo &info) + : buffers_(info.size(), nullptr), size_(size), info_(info) { - fn::AttributesInfoBuilder builder; - for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) { - buffers_.append(layer.data); - switch (layer.type) { - case CD_PROP_INT32: { - builder.add<int32_t>(layer.name, 0); - break; - } - case CD_PROP_FLOAT3: { - builder.add<float3>(layer.name, {0, 0, 0}); - break; - } - } + for (uint attribute_index : info.index_range()) { + StringRefNull name = info.name_of(attribute_index); + const fn::CPPType &cpp_type = info.type_of(attribute_index); + CustomDataType custom_type = cpp_to_custom_data_type(cpp_type); + void *data = CustomData_get_layer_named(&custom_data, custom_type, name.c_str()); + buffers_[attribute_index] = data; } - info_ = std::make_unique<fn::AttributesInfo>(builder); - size_ = size; } operator fn::MutableAttributesRef() { - return fn::MutableAttributesRef(*info_, buffers_, size_); + return fn::MutableAttributesRef(info_, buffers_, size_); } operator fn::AttributesRef() const { - return fn::AttributesRef(*info_, buffers_, size_); + return fn::AttributesRef(info_, buffers_, size_); } }; -static void ensure_attributes_exist(ParticleSimulationState *state) +static void ensure_attributes_exist(ParticleSimulationState *state, const fn::AttributesInfo &info) { - if (CustomData_get_layer_named(&state->attributes, CD_PROP_FLOAT3, "Position") == nullptr) { - CustomData_add_layer_named( - &state->attributes, CD_PROP_FLOAT3, CD_CALLOC, nullptr, state->tot_particles, "Position"); - } - if (CustomData_get_layer_named(&state->attributes, CD_PROP_FLOAT3, "Velocity") == nullptr) { - CustomData_add_layer_named( - &state->attributes, CD_PROP_FLOAT3, CD_CALLOC, nullptr, state->tot_particles, "Velocity"); - } - if (CustomData_get_layer_named(&state->attributes, CD_PROP_INT32, "ID") == nullptr) { - CustomData_add_layer_named( - &state->attributes, CD_PROP_INT32, CD_CALLOC, nullptr, state->tot_particles, "ID"); + bool found_layer_to_remove; + do { + found_layer_to_remove = false; + for (int layer_index = 0; layer_index < state->attributes.totlayer; layer_index++) { + CustomDataLayer *layer = &state->attributes.layers[layer_index]; + BLI_assert(layer->name != nullptr); + const fn::CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer->type); + StringRefNull name = layer->name; + if (!info.has_attribute(name, cpp_type)) { + found_layer_to_remove = true; + CustomData_free_layer(&state->attributes, layer->type, state->tot_particles, layer_index); + break; + } + } + } while (found_layer_to_remove); + + for (uint attribute_index : info.index_range()) { + StringRefNull attribute_name = info.name_of(attribute_index); + const fn::CPPType &cpp_type = info.type_of(attribute_index); + CustomDataType custom_type = cpp_to_custom_data_type(cpp_type); + if (CustomData_get_layer_named(&state->attributes, custom_type, attribute_name.c_str()) == + nullptr) { + void *data = CustomData_add_layer_named(&state->attributes, + custom_type, + CD_CALLOC, + nullptr, + state->tot_particles, + attribute_name.c_str()); + cpp_type.fill_uninitialized( + info.default_of(attribute_index), data, (uint)state->tot_particles); + } } } @@ -84,48 +130,52 @@ void initialize_simulation_states(Simulation &simulation, Depsgraph &UNUSED(depsgraph), const SimulationInfluences &UNUSED(influences)) { - RandomNumberGenerator rng; - - LISTBASE_FOREACH (ParticleSimulationState *, state, &simulation.states) { - state->tot_particles = 1000; - CustomData_realloc(&state->attributes, state->tot_particles); - ensure_attributes_exist(state); - - CustomDataAttributesRef custom_data_attributes{state->attributes, (uint)state->tot_particles}; - - fn::MutableAttributesRef attributes = custom_data_attributes; - MutableSpan<float3> positions = attributes.get<float3>("Position"); - MutableSpan<float3> velocities = attributes.get<float3>("Velocity"); - MutableSpan<int32_t> ids = attributes.get<int32_t>("ID"); - - for (uint i : positions.index_range()) { - positions[i] = {i / 100.0f, 0, 0}; - velocities[i] = {0, rng.get_float() - 0.5f, rng.get_float() - 0.5f}; - ids[i] = i; - } - } + simulation.current_simulation_time = 0.0f; } void solve_simulation_time_step(Simulation &simulation, - Depsgraph &UNUSED(depsgraph), + Depsgraph &depsgraph, const SimulationInfluences &influences, float time_step) { + SimulationSolveContext solve_context{simulation, depsgraph, influences}; + TimeInterval simulation_time_interval{simulation.current_simulation_time, time_step}; + + Map<std::string, std::unique_ptr<fn::AttributesInfo>> attribute_infos; + Map<std::string, std::unique_ptr<ParticleAllocator>> particle_allocators; LISTBASE_FOREACH (ParticleSimulationState *, state, &simulation.states) { - ensure_attributes_exist(state); + const fn::AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as( + state->head.name); + auto info = std::make_unique<fn::AttributesInfo>(builder); - CustomDataAttributesRef custom_data_attributes{state->attributes, (uint)state->tot_particles}; + ensure_attributes_exist(state, *info); + particle_allocators.add_new( + state->head.name, std::make_unique<ParticleAllocator>(*info, state->next_particle_id)); + attribute_infos.add_new(state->head.name, std::move(info)); + } + + LISTBASE_FOREACH (ParticleSimulationState *, state, &simulation.states) { + const fn::AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name); + CustomDataAttributesRef custom_data_attributes{ + state->attributes, (uint)state->tot_particles, attributes_info}; fn::MutableAttributesRef attributes = custom_data_attributes; + MutableSpan<float3> positions = attributes.get<float3>("Position"); MutableSpan<float3> velocities = attributes.get<float3>("Velocity"); Array<float3> force_vectors{(uint)state->tot_particles, {0, 0, 0}}; const Vector<const ParticleForce *> *forces = influences.particle_forces.lookup_ptr( state->head.name); + if (forces != nullptr) { + ParticleChunkContext particle_chunk_context{IndexMask((uint)state->tot_particles), + attributes}; + ParticleForceContext particle_force_context{ + solve_context, particle_chunk_context, force_vectors}; + for (const ParticleForce *force : *forces) { - force->add_force(attributes, force_vectors); + force->add_force(particle_force_context); } } @@ -134,6 +184,46 @@ void solve_simulation_time_step(Simulation &simulation, positions[i] += velocities[i] * time_step; } } + + for (const ParticleEmitter *emitter : influences.particle_emitters) { + ParticleEmitterContext emitter_context{ + solve_context, particle_allocators, simulation_time_interval}; + emitter->emit(emitter_context); + } + + LISTBASE_FOREACH (ParticleSimulationState *, state, &simulation.states) { + const fn::AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name); + ParticleAllocator &allocator = *particle_allocators.lookup_as(state->head.name); + + const uint emitted_particle_amount = allocator.total_allocated(); + const uint old_particle_amount = state->tot_particles; + const uint new_particle_amount = old_particle_amount + emitted_particle_amount; + + CustomData_realloc(&state->attributes, new_particle_amount); + + CustomDataAttributesRef custom_data_attributes{ + state->attributes, new_particle_amount, attributes_info}; + fn::MutableAttributesRef attributes = custom_data_attributes; + + uint offset = old_particle_amount; + for (fn::MutableAttributesRef emitted_attributes : allocator.get_allocations()) { + fn::MutableAttributesRef dst_attributes = attributes.slice( + IndexRange(offset, emitted_attributes.size())); + for (uint attribute_index : attributes.info().index_range()) { + fn::GMutableSpan emitted_data = emitted_attributes.get(attribute_index); + fn::GMutableSpan dst = dst_attributes.get(attribute_index); + const fn::CPPType &type = dst.type(); + type.copy_to_uninitialized_n( + emitted_data.buffer(), dst.buffer(), emitted_attributes.size()); + } + offset += emitted_attributes.size(); + } + + state->tot_particles = new_particle_amount; + state->next_particle_id += emitted_particle_amount; + } + + simulation.current_simulation_time = simulation_time_interval.end(); } } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver.hh b/source/blender/simulation/intern/simulation_solver.hh index 818bcf067a3..7b37ed0583d 100644 --- a/source/blender/simulation/intern/simulation_solver.hh +++ b/source/blender/simulation/intern/simulation_solver.hh @@ -24,19 +24,134 @@ #include "FN_attributes_ref.hh" +#include "particle_allocator.hh" +#include "time_interval.hh" + struct Depsgraph; namespace blender::sim { +class ParticleEmitterContext; +class ParticleForceContext; + +class ParticleEmitter { + public: + virtual ~ParticleEmitter(); + virtual void emit(ParticleEmitterContext &context) const = 0; +}; + class ParticleForce { public: virtual ~ParticleForce(); - virtual void add_force(fn::AttributesRef attributes, - MutableSpan<float3> r_combined_force) const = 0; + virtual void add_force(ParticleForceContext &context) const = 0; }; struct SimulationInfluences { Map<std::string, Vector<const ParticleForce *>> particle_forces; + Map<std::string, fn::AttributesInfoBuilder *> particle_attributes_builder; + Vector<const ParticleEmitter *> particle_emitters; +}; + +class SimulationSolveContext { + private: + Simulation &simulation_; + Depsgraph &depsgraph_; + const SimulationInfluences &influences_; + + public: + SimulationSolveContext(Simulation &simulation, + Depsgraph &depsgraph, + const SimulationInfluences &influences) + : simulation_(simulation), depsgraph_(depsgraph), influences_(influences) + { + } +}; + +class ParticleChunkContext { + private: + IndexMask index_mask_; + fn::MutableAttributesRef attributes_; + + public: + ParticleChunkContext(IndexMask index_mask, fn::MutableAttributesRef attributes) + : index_mask_(index_mask), attributes_(attributes) + { + } + + IndexMask index_mask() const + { + return index_mask_; + } + + fn::MutableAttributesRef attributes() + { + return attributes_; + } + + fn::AttributesRef attributes() const + { + return attributes_; + } +}; + +class ParticleEmitterContext { + private: + SimulationSolveContext &solve_context_; + Map<std::string, std::unique_ptr<ParticleAllocator>> &particle_allocators_; + TimeInterval simulation_time_interval_; + + public: + ParticleEmitterContext(SimulationSolveContext &solve_context, + Map<std::string, std::unique_ptr<ParticleAllocator>> &particle_allocators, + TimeInterval simulation_time_interval) + : solve_context_(solve_context), + particle_allocators_(particle_allocators), + simulation_time_interval_(simulation_time_interval) + { + } + + ParticleAllocator *try_get_particle_allocator(StringRef particle_simulation_name) + { + auto *ptr = particle_allocators_.lookup_ptr_as(particle_simulation_name); + if (ptr != nullptr) { + return ptr->get(); + } + else { + return nullptr; + } + } + + TimeInterval simulation_time_interval() const + { + return simulation_time_interval_; + } +}; + +class ParticleForceContext { + private: + SimulationSolveContext &solve_context_; + const ParticleChunkContext &particle_chunk_context_; + MutableSpan<float3> force_dst_; + + public: + ParticleForceContext(SimulationSolveContext &solve_context, + const ParticleChunkContext &particle_chunk_context, + MutableSpan<float3> force_dst) + : solve_context_(solve_context), + particle_chunk_context_(particle_chunk_context), + force_dst_(force_dst) + { + } + + const ParticleChunkContext &particle_chunk() const + { + return particle_chunk_context_; + } + + MutableSpan<float3> force_dst() + { + return force_dst_; + } }; void initialize_simulation_states(Simulation &simulation, diff --git a/source/blender/simulation/intern/simulation_update.cc b/source/blender/simulation/intern/simulation_update.cc index a3fddbbe18f..5328304863b 100644 --- a/source/blender/simulation/intern/simulation_update.cc +++ b/source/blender/simulation/intern/simulation_update.cc @@ -14,7 +14,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "SIM_particle_function.hh" #include "SIM_simulation_update.hh" #include "BKE_customdata.h" @@ -32,6 +31,7 @@ #include "BLI_rand.h" #include "BLI_vector.hh" +#include "particle_function.hh" #include "simulation_collect_influences.hh" #include "simulation_solver.hh" @@ -107,28 +107,18 @@ static void add_missing_particle_states(Simulation *simulation, Span<std::string } static void reinitialize_empty_simulation_states(Simulation *simulation, - const nodes::DerivedNodeTree &tree) + const SimulationStatesInfo &states_info) { - VectorSet<std::string> state_names; - for (const nodes::DNode *dnode : tree.nodes_by_type("SimulationNodeParticleSimulation")) { - state_names.add(dnode_to_path(*dnode)); - } - - remove_unused_states(simulation, state_names); + remove_unused_states(simulation, states_info.particle_simulation_names); reset_states(simulation); - add_missing_particle_states(simulation, state_names); + add_missing_particle_states(simulation, states_info.particle_simulation_names); } static void update_simulation_state_list(Simulation *simulation, - const nodes::DerivedNodeTree &tree) + const SimulationStatesInfo &states_info) { - VectorSet<std::string> state_names; - for (const nodes::DNode *dnode : tree.nodes_by_type("SimulationNodeParticleSimulation")) { - state_names.add(dnode_to_path(*dnode)); - } - - remove_unused_states(simulation, state_names); - add_missing_particle_states(simulation, state_names); + remove_unused_states(simulation, states_info.particle_simulation_names); + add_missing_particle_states(simulation, states_info.particle_simulation_names); } void update_simulation_in_depsgraph(Depsgraph *depsgraph, @@ -147,16 +137,15 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, Simulation *simulation_orig = (Simulation *)DEG_get_original_id(&simulation_cow->id); - nodes::NodeTreeRefMap tree_refs; - /* TODO: Use simulation_cow, but need to add depsgraph relations before that. */ - const nodes::DerivedNodeTree tree{simulation_orig->nodetree, tree_refs}; - ResourceCollector resources; SimulationInfluences influences; - collect_simulation_influences(tree, resources, influences); + SimulationStatesInfo states_info; + + /* TODO: Use simulation_cow, but need to add depsgraph relations before that. */ + collect_simulation_influences(*simulation_orig, resources, influences, states_info); if (current_frame == 1) { - reinitialize_empty_simulation_states(simulation_orig, tree); + reinitialize_empty_simulation_states(simulation_orig, states_info); initialize_simulation_states(*simulation_orig, *depsgraph, influences); simulation_orig->current_frame = 1; @@ -164,7 +153,7 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, copy_states_to_cow(simulation_orig, simulation_cow); } else if (current_frame == simulation_orig->current_frame + 1) { - update_simulation_state_list(simulation_orig, tree); + update_simulation_state_list(simulation_orig, states_info); float time_step = 1.0f / 24.0f; solve_simulation_time_step(*simulation_orig, *depsgraph, influences, time_step); diff --git a/source/blender/simulation/intern/time_interval.hh b/source/blender/simulation/intern/time_interval.hh new file mode 100644 index 00000000000..49600dd10de --- /dev/null +++ b/source/blender/simulation/intern/time_interval.hh @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef __SIM_TIME_INTERVAL_HH__ +#define __SIM_TIME_INTERVAL_HH__ + +#include "BLI_utildefines.h" + +namespace blender::sim { + +/** + * The start time is inclusive and the end time is exclusive. The duration is zero, the interval + * describes a single point in time. + */ +class TimeInterval { + private: + float start_; + float duration_; + + public: + TimeInterval(float start, float duration) : start_(start), duration_(duration) + { + BLI_assert(duration_ >= 0.0f); + } + + float start() const + { + return start_; + } + + float end() const + { + return start_ + duration_; + } + + float duration() const + { + return duration_; + } +}; + +} // namespace blender::sim + +#endif /* __SIM_TIME_INTERVAL_HH__ */ |