diff options
Diffstat (limited to 'source/blender')
32 files changed, 606 insertions, 236 deletions
diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h index 7d612f293ab..f8534371b17 100644 --- a/source/blender/blenkernel/BKE_subdiv_ccg.h +++ b/source/blender/blenkernel/BKE_subdiv_ccg.h @@ -216,7 +216,10 @@ typedef struct SubdivCCG { } dirty; } SubdivCCG; -/* Create real hi-res CCG from subdivision. +/* Create CCG representation of subdivision surface. + * + * NOTE: CCG stores dense verticies in a grid-like storage. There is no edges or + * polygons informations for the high-poly surface. * * NOTE: Subdiv is expected to be refined and ready for evaluation. * NOTE: CCG becomes an owner of subdiv. diff --git a/source/blender/blenkernel/BKE_subdiv_deform.h b/source/blender/blenkernel/BKE_subdiv_deform.h index 72d2b252cf7..11ef225de27 100644 --- a/source/blender/blenkernel/BKE_subdiv_deform.h +++ b/source/blender/blenkernel/BKE_subdiv_deform.h @@ -34,6 +34,10 @@ struct Mesh; struct Subdiv; /* Special version of subdivision surface which calculates final positions for coarse vertices. + * Effectively is pushsing the coarse positions to the limit surface. + * + * One of the usage examples is calculation of crazy space of subdivision modifier, allowing to + * paint on a deformed mesh with subsurf on it. * * vertex_cos are supposed to hold coordinates of the coarse mesh. */ void BKE_subdiv_deform_coarse_vertices(struct Subdiv *subdiv, diff --git a/source/blender/blenkernel/BKE_subdiv_eval.h b/source/blender/blenkernel/BKE_subdiv_eval.h index 095d3c17d63..3f682716928 100644 --- a/source/blender/blenkernel/BKE_subdiv_eval.h +++ b/source/blender/blenkernel/BKE_subdiv_eval.h @@ -50,6 +50,8 @@ void BKE_subdiv_eval_init_displacement(struct Subdiv *subdiv); /* Single point queries. */ +/* Evaluate point at a limit surface, with optional derivatives and normal. */ + void BKE_subdiv_eval_limit_point( struct Subdiv *subdiv, const int ptex_face_index, const float u, const float v, float r_P[3]); void BKE_subdiv_eval_limit_point_and_derivatives(struct Subdiv *subdiv, @@ -72,6 +74,7 @@ void BKE_subdiv_eval_limit_point_and_short_normal(struct Subdiv *subdiv, float r_P[3], short r_N[3]); +/* Evaluate face-varying layer (such as UV). */ void BKE_subdiv_eval_face_varying(struct Subdiv *subdiv, const int face_varying_channel, const int ptex_face_index, @@ -93,6 +96,7 @@ void BKE_subdiv_eval_displacement(struct Subdiv *subdiv, const float dPdv[3], float r_D[3]); +/* Evaluate point on a limit surface with displacement applied to it. */ void BKE_subdiv_eval_final_point( struct Subdiv *subdiv, const int ptex_face_index, const float u, const float v, float r_P[3]); diff --git a/source/blender/blenkernel/BKE_subdiv_foreach.h b/source/blender/blenkernel/BKE_subdiv_foreach.h index f1d4adda37c..bef141b5ac5 100644 --- a/source/blender/blenkernel/BKE_subdiv_foreach.h +++ b/source/blender/blenkernel/BKE_subdiv_foreach.h @@ -160,6 +160,10 @@ typedef struct SubdivForeachContext { /* Invokes callbacks in the order and with values which corresponds to creation * of final subdivided mesh. * + * Main goal is to abstract all the traversal routines to give geometry element + * indices (for vertices, edges, loops, polygons) in the same way as subdivision + * modifier will do for a dense mesh. + * * Returns truth if the whole topology was traversed, without any early exits. * * TODO(sergey): Need to either get rid of subdiv or of coarse_mesh. diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c index 99eb4dc9122..ab382d0e8ff 100644 --- a/source/blender/blenkernel/intern/blender_undo.c +++ b/source/blender/blenkernel/intern/blender_undo.c @@ -124,6 +124,9 @@ MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev) } else { MemFile *prevfile = (mfu_prev) ? &(mfu_prev->memfile) : NULL; + if (prevfile) { + BLO_memfile_clear_future(prevfile); + } /* success = */ /* UNUSED */ BLO_write_file_mem(bmain, prevfile, &mfu->memfile, G.fileflags); mfu->undo_size = mfu->memfile.size; } diff --git a/source/blender/blenkernel/intern/subdiv_foreach.c b/source/blender/blenkernel/intern/subdiv_foreach.c index 533279f4425..6e39f9f302b 100644 --- a/source/blender/blenkernel/intern/subdiv_foreach.c +++ b/source/blender/blenkernel/intern/subdiv_foreach.c @@ -1875,6 +1875,13 @@ bool BKE_subdiv_foreach_subdiv_geometry(Subdiv *subdiv, if (context->user_data_tls_free != NULL) { parallel_range_settings.func_finalize = subdiv_foreach_finalize; } + + /* TODO(sergey): Possible optimization is to have a single pool and push all + * the tasks into it. + * NOTE: Watch out for callbacks which needs to run for loose geometry as they + * currently are relying on the fact that face/grid callbacks will tag non- + * loose geomtry. */ + BLI_task_parallel_range( 0, coarse_mesh->totpoly, &ctx, subdiv_foreach_task, ¶llel_range_settings); if (context->vertex_loose != NULL) { diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index 6d484a7b702..b111dee80ad 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -464,12 +464,12 @@ UndoStep *BKE_undosys_step_push_init_with_type(UndoStack *ustack, } UndoStep *us = MEM_callocN(ut->step_size, __func__); - CLOG_INFO(&LOG, 1, "addr=%p, name='%s', type='%s'", us, name, ut->name); if (name != NULL) { BLI_strncpy(us->name, name, sizeof(us->name)); } us->type = ut; ustack->step_init = us; + CLOG_INFO(&LOG, 1, "addr=%p, name='%s', type='%s'", us, us->name, us->type->name); ut->step_encode_init(C, us); undosys_stack_validate(ustack, false); return us; @@ -552,6 +552,8 @@ bool BKE_undosys_step_push_with_type(UndoStack *ustack, us->type = ut; /* Initialized, not added yet. */ + CLOG_INFO(&LOG, 1, "addr=%p, name='%s', type='%s'", us, us->name, us->type->name); + if (!undosys_step_encode(C, G_MAIN, ustack, us)) { MEM_freeN(us); undosys_stack_validate(ustack, true); diff --git a/source/blender/blenloader/BLO_undofile.h b/source/blender/blenloader/BLO_undofile.h index 5f1142cc20e..f280b8f3b9c 100644 --- a/source/blender/blenloader/BLO_undofile.h +++ b/source/blender/blenloader/BLO_undofile.h @@ -60,6 +60,7 @@ extern void memfile_chunk_add(MemFile *memfile, /* exports */ extern void BLO_memfile_free(MemFile *memfile); extern void BLO_memfile_merge(MemFile *first, MemFile *second); +extern void BLO_memfile_clear_future(MemFile *memfile); /* utilities */ extern struct Main *BLO_memfile_main_get(struct MemFile *memfile, diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.c index 06469a0c087..69c4ba2b1f2 100644 --- a/source/blender/blenloader/intern/undofile.c +++ b/source/blender/blenloader/intern/undofile.c @@ -92,6 +92,14 @@ void BLO_memfile_merge(MemFile *first, MemFile *second) BLO_memfile_free(first); } +/* Clear is_identical_future before adding next memfile. */ +void BLO_memfile_clear_future(MemFile *memfile) +{ + for (MemFileChunk *chunk = memfile->chunks.first; chunk; chunk = chunk->next) { + chunk->is_identical_future = false; + } +} + void memfile_chunk_add(MemFile *memfile, const char *buf, uint size, MemFileChunk **compchunk_step) { MemFileChunk *curchunk = MEM_mallocN(sizeof(MemFileChunk), "MemFileChunk"); diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index bdab4fc8219..8b6ddd99749 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -734,11 +734,6 @@ static void write_iddata(WriteData *wd, ID *id) } } } - - /* Clear the accumulated recalc flags in case of undo step saving. */ - if (wd->use_memfile) { - id->recalc_undo_accumulated = 0; - } } static void write_previews(WriteData *wd, const PreviewImage *prv_orig) @@ -2244,6 +2239,7 @@ static void write_mesh(WriteData *wd, Mesh *mesh) mesh->mface = NULL; mesh->totface = 0; memset(&mesh->fdata, 0, sizeof(mesh->fdata)); + memset(&mesh->runtime, 0, sizeof(mesh->runtime)); /* Reduce xdata layers, fill xlayers with layers to be written. * This makes xdata invalid for Blender, which is why we made a @@ -4173,6 +4169,9 @@ static bool write_file_handle(Main *mainvar, /* Very important to do it after every ID write now, otherwise we cannot know whether a * specific ID changed or not. */ mywrite_flush(wd); + + /* Clear the accumulated recalc flags in case of undo step saving. */ + id->recalc_undo_accumulated = 0; } } diff --git a/source/blender/draw/engines/eevee/eevee_bloom.c b/source/blender/draw/engines/eevee/eevee_bloom.c index 6545a1bca84..8fd953478d5 100644 --- a/source/blender/draw/engines/eevee/eevee_bloom.c +++ b/source/blender/draw/engines/eevee/eevee_bloom.c @@ -323,8 +323,8 @@ void EEVEE_bloom_draw(EEVEE_Data *vedata) /* Upsample and accumulate */ for (int i = effects->bloom_iteration_len - 2; i >= 0; i--) { copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[i]); - effects->unf_source_buffer = effects->bloom_downsample[i]; - effects->unf_base_buffer = last; + effects->unf_source_buffer = last; + effects->unf_base_buffer = effects->bloom_downsample[i]; GPU_framebuffer_bind(fbl->bloom_accum_fb[i]); DRW_draw_pass(psl->bloom_upsample); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index e2470b4fa76..264f301e52c 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -600,6 +600,7 @@ typedef struct EEVEE_EffectsInfo { int taa_total_sample; float taa_alpha; bool prev_drw_support; + bool prev_is_navigating; float prev_drw_persmat[4][4]; struct DRWView *taa_view; /* Ambient Occlusion */ diff --git a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c index 1f44b815a42..b70d872c4af 100644 --- a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c +++ b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c @@ -244,8 +244,10 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data copy_m4_m4(effects->prev_drw_persmat, persmat); /* Prevent ghosting from probe data. */ - view_is_valid = view_is_valid && (effects->prev_drw_support == DRW_state_draw_support()); + view_is_valid = view_is_valid && (effects->prev_drw_support == DRW_state_draw_support()) && + (effects->prev_is_navigating == DRW_state_is_navigating()); effects->prev_drw_support = DRW_state_draw_support(); + effects->prev_is_navigating = DRW_state_is_navigating(); if (((effects->taa_total_sample == 0) || (effects->taa_current_sample < effects->taa_total_sample)) || diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index a5f67b7831e..f8f55843a29 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -314,12 +314,12 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, break; case eGplBlendMode_Multiply: case eGplBlendMode_Divide: - case eGplBlendMode_Overlay: + case eGplBlendMode_HardLight: state |= DRW_STATE_BLEND_MUL; break; } - if (ELEM(gpl->blend_mode, eGplBlendMode_Subtract, eGplBlendMode_Overlay)) { + if (ELEM(gpl->blend_mode, eGplBlendMode_Subtract, eGplBlendMode_HardLight)) { /* For these effect to propagate, we need a signed floating point buffer. */ pd->use_signed_fb = true; } @@ -336,7 +336,7 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, DRW_shgroup_stencil_mask(grp, 0xFF); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - if (gpl->blend_mode == eGplBlendMode_Overlay) { + if (gpl->blend_mode == eGplBlendMode_HardLight) { /* We cannot do custom blending on MultiTarget framebuffers. * Workaround by doing 2 passes. */ grp = DRW_shgroup_create(sh, tgp_layer->blend_ps); diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl index cb34515a960..54b2369b32b 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -77,12 +77,12 @@ layout(std140) uniform gpLightBlock /* Must match eGPLayerBlendModes */ #define MODE_REGULAR 0 -#define MODE_OVERLAY 1 +#define MODE_HARDLIGHT 1 #define MODE_ADD 2 #define MODE_SUB 3 #define MODE_MULTIPLY 4 #define MODE_DIVIDE 5 -#define MODE_OVERLAY_SECOND_PASS 999 +#define MODE_HARDLIGHT_SECOND_PASS 999 void blend_mode_output( int blend_mode, vec4 color, float opacity, out vec4 frag_color, out vec4 frag_revealage) @@ -104,7 +104,7 @@ void blend_mode_output( color.a *= opacity; frag_revealage = frag_color = clamp(1.0 / max(vec4(1e-6), 1.0 - color * color.a), 0.0, 1e18); break; - case MODE_OVERLAY: + case MODE_HARDLIGHT: /* Reminder: Blending func is multiply blend (dst.rgba * src.rgba).*/ /** * We need to separate the overlay equation into 2 term (one mul and one add). @@ -120,11 +120,13 @@ void blend_mode_output( color = mix(vec4(0.5), color, color.a * opacity); vec4 s = step(-0.5, -color); frag_revealage = frag_color = 2.0 * s + 2.0 * color * (1.0 - s * 2.0); + frag_revealage = max(vec4(0.0), frag_revealage); break; - case MODE_OVERLAY_SECOND_PASS: + case MODE_HARDLIGHT_SECOND_PASS: /* Reminder: Blending func is additive blend (dst.rgba + src.rgba).*/ color = mix(vec4(0.5), color, color.a * opacity); frag_revealage = frag_color = (-1.0 + 2.0 * color) * step(-0.5, -color); + frag_revealage = max(vec4(0.0), frag_revealage); break; case MODE_SUB: case MODE_ADD: @@ -572,6 +574,7 @@ void fill_vertex() finalUvs = rot_scale * uv1.xy + loc; # endif + strokeHardeness = 1.0; strokeThickness = 1e18; strokeAspect = vec2(1.0); strokePt1 = strokePt2 = vec2(0.0); diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl index 8833490e818..fd37bc8c534 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl @@ -40,7 +40,7 @@ void main() } else { nor = norAndFlag.xyz; - if (all(equal(nor, vec3(0.0)))) { + if (all(equal(nor, vec3(0)))) { finalColor = vec4(0.0); return; } diff --git a/source/blender/draw/engines/workbench/workbench_data.c b/source/blender/draw/engines/workbench/workbench_data.c index fc047eadf55..6ef9cbfab57 100644 --- a/source/blender/draw/engines/workbench/workbench_data.c +++ b/source/blender/draw/engines/workbench/workbench_data.c @@ -182,7 +182,7 @@ void workbench_private_data_init(WORKBENCH_PrivateData *wpd) WORKBENCH_ViewLayerData *vldata = workbench_view_layer_data_ensure_ex(draw_ctx->view_layer); wpd->is_playback = DRW_state_is_playback(); - wpd->is_navigating = rv3d && (rv3d->rflag & (RV3D_NAVIGATING | RV3D_PAINTING)); + wpd->is_navigating = DRW_state_is_navigating(); wpd->ctx_mode = CTX_data_mode_enum_ex( draw_ctx->object_edit, draw_ctx->obact, draw_ctx->object_mode); diff --git a/source/blender/draw/engines/workbench/workbench_effect_antialiasing.c b/source/blender/draw/engines/workbench/workbench_effect_antialiasing.c index a0db09e9273..094d13fb84c 100644 --- a/source/blender/draw/engines/workbench/workbench_effect_antialiasing.c +++ b/source/blender/draw/engines/workbench/workbench_effect_antialiasing.c @@ -168,10 +168,17 @@ void workbench_antialiasing_engine_init(WORKBENCH_Data *vedata) wpd->view = NULL; - /* reset complete drawing when navigating or during viewport playback. */ + /* Reset complete drawing when navigating or during viewport playback or when + * leaving one of those states. In case of multires modifier the navigation + * mesh differs from the viewport mesh, so we need to be sure to restart. */ if (wpd->taa_sample != 0) { if (wpd->is_navigating || wpd->is_playback) { wpd->taa_sample = 0; + wpd->reset_next_sample = true; + } + else if (wpd->reset_next_sample) { + wpd->taa_sample = 0; + wpd->reset_next_sample = false; } } diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index 87b1c82ce94..a5e80f417d3 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -333,6 +333,7 @@ typedef struct WORKBENCH_PrivateData { bool dof_enabled; bool is_playback; bool is_navigating; + bool reset_next_sample; } WORKBENCH_PrivateData; /* Transient data */ typedef struct WORKBENCH_ObjectData { diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 382e7313f21..2d6d6c7d9d0 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -670,6 +670,7 @@ bool DRW_state_do_color_management(void); bool DRW_state_is_scene_render(void); bool DRW_state_is_opengl_render(void); bool DRW_state_is_playback(void); +bool DRW_state_is_navigating(void); bool DRW_state_show_text(void); bool DRW_state_draw_support(void); bool DRW_state_draw_background(void); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index 54e745102f0..f54fef63c82 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -191,7 +191,17 @@ static MeshRenderData *mesh_render_data_create(Mesh *me, else { mr->me = me; mr->edit_bmesh = NULL; - mr->extract_type = MR_EXTRACT_MESH; + + bool use_mapped = mr->me && !mr->me->runtime.is_original; + if (use_mapped) { + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + + use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); + } + + mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH; } if (mr->extract_type != MR_EXTRACT_BMESH) { @@ -924,10 +934,14 @@ static void extract_lines_paint_mask_loop_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_LinePaintMask_Data *data = (MeshExtract_LinePaintMask_Data *)_data; - if (!(mr->use_hide && (mpoly->flag & ME_HIDE))) { + const int edge_idx = mloop->e; + const MEdge *medge = &mr->medge[edge_idx]; + if (!((mr->use_hide && (medge->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && + (mr->e_origindex[edge_idx] == ORIGINDEX_NONE)))) { + int loopend = mpoly->totloop + mpoly->loopstart - 1; int other_loop = (l == loopend) ? mpoly->loopstart : (l + 1); - int edge_idx = mloop->e; if (mpoly->flag & ME_FACE_SEL) { if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, edge_idx)) { /* Hide edge as it has more than 2 selected loop. */ @@ -945,6 +959,9 @@ static void extract_lines_paint_mask_loop_mesh(const MeshRenderData *mr, } } } + else { + GPU_indexbuf_set_line_restart(&data->elb, edge_idx); + } } static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), void *ibo, @@ -1500,7 +1517,8 @@ static void extract_pos_nor_loop_mesh(const MeshRenderData *mr, copy_v3_v3(vert->pos, mvert->co); vert->nor = data->packed_nor[mloop->v]; /* Flag for paint mode overlay. */ - if (mpoly->flag & ME_HIDE || mvert->flag & ME_HIDE) { + if (mpoly->flag & ME_HIDE || mvert->flag & ME_HIDE || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex[mloop->v] == ORIGINDEX_NONE))) { vert->nor.w = -1; } else if (mvert->flag & SELECT) { @@ -1617,24 +1635,27 @@ static void extract_lnor_hq_loop_bmesh(const MeshRenderData *mr, int l, BMLoop * static void extract_lnor_hq_loop_mesh( const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *data) { + gpuHQNor *lnor_data = &((gpuHQNor *)data)[l]; if (mr->loop_normals) { - normal_float_to_short_v3(&((gpuHQNor *)data)[l].x, mr->loop_normals[l]); + normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[l]); } else if (mpoly->flag & ME_SMOOTH) { - copy_v3_v3_short(&((gpuHQNor *)data)[l].x, mr->mvert[mloop->v].no); + copy_v3_v3_short(&lnor_data->x, mr->mvert[mloop->v].no); } else { - normal_float_to_short_v3(&((gpuHQNor *)data)[l].x, mr->poly_normals[p]); + normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[p]); } + /* Flag for paint mode overlay. */ - if (mpoly->flag & ME_HIDE) { - ((gpuHQNor *)data)[l].w = -1; + if (mpoly->flag & ME_HIDE || + (mr->extract_type == MR_EXTRACT_MAPPED && mr->v_origindex[mloop->v] == ORIGINDEX_NONE)) { + lnor_data->w = -1; } else if (mpoly->flag & ME_FACE_SEL) { - ((gpuHQNor *)data)[l].w = 1; + lnor_data->w = 1; } else { - ((gpuHQNor *)data)[l].w = 0; + lnor_data->w = 0; } } @@ -1690,24 +1711,27 @@ static void extract_lnor_loop_bmesh(const MeshRenderData *mr, int l, BMLoop *loo static void extract_lnor_loop_mesh( const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *data) { + GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[l]; if (mr->loop_normals) { - ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(mr->loop_normals[l]); + *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[l]); } else if (mpoly->flag & ME_SMOOTH) { - ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_s3(mr->mvert[mloop->v].no); + *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[mloop->v].no); } else { - ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(mr->poly_normals[p]); + *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[p]); } + /* Flag for paint mode overlay. */ - if (mpoly->flag & ME_HIDE) { - ((GPUPackedNormal *)data)[l].w = -1; + if (mpoly->flag & ME_HIDE || + (mr->extract_type == MR_EXTRACT_MAPPED && mr->v_origindex[mloop->v] == ORIGINDEX_NONE)) { + lnor_data->w = -1; } else if (mpoly->flag & ME_FACE_SEL) { - ((GPUPackedNormal *)data)[l].w = 1; + lnor_data->w = 1; } else { - ((GPUPackedNormal *)data)[l].w = 0; + lnor_data->w = 0; } } @@ -3890,7 +3914,8 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr, void *buf, void * for (int f = 0; f < mr->poly_len; f++) { efa = BM_face_at_index(mr->bm, f); const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN); - if (is_face_hidden) { + if (is_face_hidden || + (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex[f] == ORIGINDEX_NONE)) { nor[f] = GPU_normal_convert_i10_v3(invalid_normal); nor[f].w = NOR_AND_FLAG_HIDDEN; } @@ -3906,7 +3931,9 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr, void *buf, void * else { for (int f = 0; f < mr->poly_len; f++) { efa = bm_original_face_get(mr, f); - if (!efa || BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN); + if (is_face_hidden || + (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex[f] == ORIGINDEX_NONE)) { nor[f] = GPU_normal_convert_i10_v3(invalid_normal); nor[f].w = NOR_AND_FLAG_HIDDEN; } diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index 4a474768226..3cbcdc1ede6 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -62,8 +62,6 @@ typedef struct GpencilBatchCache { /** Cache is dirty */ bool is_dirty; - /** Edit mode flag */ - bool is_editmode; /** Last cache frame */ int cache_frame; } GpencilBatchCache; @@ -71,21 +69,17 @@ typedef struct GpencilBatchCache { static bool gpencil_batch_cache_valid(GpencilBatchCache *cache, bGPdata *gpd, int cfra) { bool valid = true; + if (cache == NULL) { return false; } - cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd); if (cfra != cache->cache_frame) { valid = false; } else if (gpd->flag & GP_DATA_CACHE_IS_DIRTY) { valid = false; } - else if (gpd->flag & GP_DATA_PYTHON_UPDATED) { - gpd->flag &= ~GP_DATA_PYTHON_UPDATED; - valid = false; - } else if (cache->is_dirty) { valid = false; } @@ -106,7 +100,6 @@ static GpencilBatchCache *gpencil_batch_cache_init(Object *ob, int cfra) memset(cache, 0, sizeof(*cache)); } - cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd); cache->is_dirty = true; cache->cache_frame = cfra; return cache; diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index cc618c76ccd..b265035f449 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2575,6 +2575,15 @@ bool DRW_state_is_playback(void) } /** + * Is the user navigating the region. + */ +bool DRW_state_is_navigating(void) +{ + const RegionView3D *rv3d = DST.draw_ctx.rv3d; + return (rv3d) && (rv3d->rflag & (RV3D_NAVIGATING | RV3D_PAINTING)); +} + +/** * Should text draw in this mode? */ bool DRW_state_show_text(void) diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index ba914080d98..37f0c7197a9 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -59,6 +59,7 @@ void ARMATURE_OT_select_mirror(struct wmOperatorType *ot); void ARMATURE_OT_select_more(struct wmOperatorType *ot); void ARMATURE_OT_select_less(struct wmOperatorType *ot); void ARMATURE_OT_select_hierarchy(struct wmOperatorType *ot); +void ARMATURE_OT_select_linked_pick(struct wmOperatorType *ot); void ARMATURE_OT_select_linked(struct wmOperatorType *ot); void ARMATURE_OT_select_similar(struct wmOperatorType *ot); void ARMATURE_OT_shortest_path_pick(struct wmOperatorType *ot); diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index 097d384a609..b304ce92a54 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -56,6 +56,7 @@ void ED_operatortypes_armature(void) WM_operatortype_append(ARMATURE_OT_select_less); WM_operatortype_append(ARMATURE_OT_select_hierarchy); WM_operatortype_append(ARMATURE_OT_select_linked); + WM_operatortype_append(ARMATURE_OT_select_linked_pick); WM_operatortype_append(ARMATURE_OT_select_similar); WM_operatortype_append(ARMATURE_OT_shortest_path_pick); diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index e927765304f..99dcd5bb42b 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -59,7 +59,9 @@ #define EBONE_PREV_FLAG_GET(ebone) ((void)0, (ebone)->temp.i) #define EBONE_PREV_FLAG_SET(ebone, val) ((ebone)->temp.i = val) -/* **************** PoseMode & EditMode Selection Buffer Queries *************************** */ +/* -------------------------------------------------------------------- */ +/** \name Select Buffer Queries for PoseMode & EditMode + * \{ */ Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases, uint bases_len, @@ -293,25 +295,29 @@ void *get_nearest_bone(bContext *C, const int xy[2], bool findunsel, Base **r_ba return NULL; } -/* **************** EditMode stuff ********************** */ - -static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - const bool select = !RNA_boolean_get(op->ptr, "deselect"); - /* true: select all connected bones traveling up & down forks. - * false: select all parents and all children, but not the children of the root bone. */ - const bool all_forks = RNA_boolean_get(op->ptr, "all_forks"); +/** \} */ - view3d_operator_needs_opengl(C); - BKE_object_update_select_id(CTX_data_main(C)); +/* -------------------------------------------------------------------- */ +/** \name Select Linked Implementation + * + * Shared logic for select linked all/pick. + * + * Use #BONE_DONE flag to select linked. + * \{ */ - Base *base = NULL; - EditBone *ebone_active = get_nearest_bone(C, event->mval, true, &base); - bArmature *arm = base->object->data; +/** + * \param all_forks: Control how chains are stepped over. + * true: select all connected bones traveling up & down forks. + * false: select all parents and all children, but not the children of the root bone. + */ +static bool armature_select_linked_impl(Object *ob, const bool select, const bool all_forks) +{ + bool changed = false; + bArmature *arm = ob->data; - if (ebone_active == NULL || !EBONE_SELECTABLE(arm, ebone_active)) { - return OPERATOR_CANCELLED; - } + /* Implementation note, this flood-fills selected bones with the 'TOUCH' flag, + * even though this is a loop-within a loop, walking up the parent chain only touches new bones. + * Bones that have been touched are skipped, so the complexity is OK. */ enum { /* Bone has been walked over, it's LINK value can be read. */ @@ -320,36 +326,44 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv LINK = (1 << 1), }; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - ebone->temp.i = 0; - } - #define CHECK_PARENT(ebone) \ (((ebone)->flag & BONE_CONNECTED) && \ ((ebone)->parent ? EBONE_SELECTABLE(arm, (ebone)->parent) : false)) + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + ebone->temp.i = 0; + } + /* Select parents. */ - for (EditBone *ebone = ebone_active; ebone; ebone = CHECK_PARENT(ebone) ? ebone->parent : NULL) { - if ((ebone->flag & BONE_UNSELECTABLE) == 0) { + for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + if (ebone_iter->temp.i & TOUCH) { + continue; + } + if ((ebone_iter->flag & BONE_DONE) == 0) { + continue; + } + + ebone_iter->temp.i |= TOUCH | LINK; + + /* We have an un-touched link. */ + for (EditBone *ebone = ebone_iter; ebone; ebone = CHECK_PARENT(ebone) ? ebone->parent : NULL) { ED_armature_ebone_select_set(ebone, select); + changed = true; + if (all_forks) { - ebone->temp.i = (TOUCH | LINK); + ebone->temp.i |= (TOUCH | LINK); } else { - ebone->temp.i = TOUCH; + ebone->temp.i |= TOUCH; + } + /* Don't walk onto links (messes up 'all_forks' logic). */ + if (ebone->parent && ebone->parent->temp.i & LINK) { + break; } } } - if (all_forks == false) { - ebone_active->temp.i = LINK; - } - - /* Select children. - * - * Implementation note, this flood-fills selected bones with the 'TOUCH' flag, - * even though this is a loop-within a loop, walking up the parent chain only touches new bones. - * Bones that have been touched are skipped, so the complexity is OK. */ + /* Select children. */ for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { /* No need to 'touch' this bone as it won't be walked over when scanning up the chain. */ if (!CHECK_PARENT(ebone_iter)) { @@ -374,6 +388,7 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv if ((ebone->temp.i & LINK) == 0) { ebone->temp.i |= LINK; ED_armature_ebone_select_set(ebone, select); + changed = true; } } } @@ -381,32 +396,129 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv #undef CHECK_PARENT - ED_outliner_select_sync_from_edit_bone_tag(C); + if (changed) { + ED_armature_edit_sync_selection(arm->edbo); + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, ob); + } - ED_armature_edit_sync_selection(arm->edbo); + return changed; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Linked Operator + * \{ */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); +static int armature_select_linked_exec(bContext *C, wmOperator *op) +{ + const bool all_forks = RNA_boolean_get(op->ptr, "all_forks"); + bool changed_multi = false; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + view_layer, CTX_wm_view3d(C), &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + bArmature *arm = ob->data; + + bool found = false; + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone) && + (ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL))) { + ebone->flag |= BONE_DONE; + found = true; + } + else { + ebone->flag &= ~BONE_DONE; + } + } + + if (found) { + if (armature_select_linked_impl(ob, true, all_forks)) { + changed_multi = true; + } + } + } + MEM_freeN(objects); + + if (changed_multi) { + ED_outliner_select_sync_from_edit_bone_tag(C); + } return OPERATOR_FINISHED; } -static bool armature_select_linked_poll(bContext *C) +void ARMATURE_OT_select_linked(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Linked All"; + ot->idname = "ARMATURE_OT_select_linked"; + ot->description = "Select all bones linked by parent/child connections to the current selection"; + + /* api callbacks */ + ot->exec = armature_select_linked_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Leave disabled by default as this matches pose mode. */ + RNA_def_boolean(ot->srna, "all_forks", 0, "All Forks", "Follow forks in the parents chain"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Linked (Cursor Pick) Operator + * \{ */ + +static int armature_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const bool select = !RNA_boolean_get(op->ptr, "deselect"); + const bool all_forks = RNA_boolean_get(op->ptr, "all_forks"); + + view3d_operator_needs_opengl(C); + BKE_object_update_select_id(CTX_data_main(C)); + + Base *base = NULL; + EditBone *ebone_active = get_nearest_bone(C, event->mval, true, &base); + bArmature *arm = base->object->data; + + if (ebone_active == NULL || !EBONE_SELECTABLE(arm, ebone_active)) { + return OPERATOR_CANCELLED; + } + + /* Initialize flags. */ + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + ebone->flag &= ~BONE_DONE; + } + ebone_active->flag |= BONE_DONE; + + if (armature_select_linked_impl(base->object, select, all_forks)) { + ED_outliner_select_sync_from_edit_bone_tag(C); + } + + return OPERATOR_FINISHED; +} + +static bool armature_select_linked_pick_poll(bContext *C) { return (ED_operator_view3d_active(C) && ED_operator_editarmature(C)); } -void ARMATURE_OT_select_linked(wmOperatorType *ot) +void ARMATURE_OT_select_linked_pick(wmOperatorType *ot) { /* identifiers */ - ot->name = "Select Connected"; - ot->idname = "ARMATURE_OT_select_linked"; - ot->description = "Select bones related to selected ones by parent/child relationships"; + ot->name = "Select Linked"; + ot->idname = "ARMATURE_OT_select_linked_pick"; + ot->description = "(De)select bones linked by parent/child connections under the mouse cursor"; /* api callbacks */ /* leave 'exec' unset */ - ot->invoke = armature_select_linked_invoke; - ot->poll = armature_select_linked_poll; + ot->invoke = armature_select_linked_pick_invoke; + ot->poll = armature_select_linked_pick_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -416,6 +528,12 @@ void ARMATURE_OT_select_linked(wmOperatorType *ot) RNA_def_boolean(ot->srna, "all_forks", 0, "All Forks", "Follow forks in the parents chain"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Buffer Queries EditMode + * \{ */ + /* utility function for get_nearest_editbonepoint */ static int selectbuffer_ret_hits_12(unsigned int *UNUSED(buffer), const int hits12) { @@ -620,6 +738,12 @@ cache_end: return NULL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Utility Functions + * \{ */ + bool ED_armature_edit_deselect_all(Object *obedit) { bArmature *arm = obedit->data; @@ -686,6 +810,12 @@ bool ED_armature_edit_deselect_all_visible_multi(bContext *C) return changed_multi; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cursor Picking API + * \{ */ + /* accounts for connected parents */ static int ebone_select_flag(EditBone *ebone) { @@ -825,6 +955,8 @@ bool ED_armature_edit_select_pick( return false; } +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Select Op From Tagged * @@ -1009,7 +1141,9 @@ bool ED_armature_edit_select_op_from_tagged(bArmature *arm, const int sel_op) /** \} */ -/* **************** Selections ******************/ +/* -------------------------------------------------------------------- */ +/** \name (De)Select All Operator + * \{ */ static int armature_de_select_all_exec(bContext *C, wmOperator *op) { @@ -1088,7 +1222,11 @@ void ARMATURE_OT_select_all(wmOperatorType *ot) WM_operator_properties_select_all(ot); } -/**************** Select more/less **************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select More/Less Implementation + * \{ */ static void armature_select_more(bArmature *arm, EditBone *ebone) { @@ -1175,6 +1313,12 @@ static void armature_select_more_less(Object *ob, bool more) ED_armature_edit_sync_selection(arm->edbo); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select More Operator + * \{ */ + static int armature_de_select_more_exec(bContext *C, wmOperator *UNUSED(op)) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1208,6 +1352,12 @@ void ARMATURE_OT_select_more(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Less Operator + * \{ */ + static int armature_de_select_less_exec(bContext *C, wmOperator *UNUSED(op)) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1241,6 +1391,12 @@ void ARMATURE_OT_select_less(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Similar + * \{ */ + enum { SIMEDBONE_CHILDREN = 1, SIMEDBONE_CHILDREN_IMMEDIATE, @@ -1656,7 +1812,11 @@ void ARMATURE_OT_select_similar(wmOperatorType *ot) RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f); } -/* ********************* select hierarchy operator ************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Hierarchy Operator + * \{ */ /* No need to convert to multi-objects. Just like we keep the non-active bones * selected we then keep the non-active objects untouched (selected/unselected). */ @@ -1762,7 +1922,11 @@ void ARMATURE_OT_select_hierarchy(wmOperatorType *ot) RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); } -/****************** Mirror Select ****************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Mirror Operator + * \{ */ /** * \note clone of #pose_select_mirror_exec keep in sync @@ -1847,7 +2011,11 @@ void ARMATURE_OT_select_mirror(wmOperatorType *ot) RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); } -/****************** Select Path ****************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Path Operator + * \{ */ static bool armature_shortest_path_select( bArmature *arm, EditBone *ebone_parent, EditBone *ebone_child, bool use_parent, bool is_test) @@ -1973,3 +2141,5 @@ void ARMATURE_OT_shortest_path_pick(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +/** \} */ diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 6b5a7215709..1a25e582859 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -1771,8 +1771,93 @@ void MESH_OT_face_make_planar(wmOperatorType *ot) /** \name Split Edge Operator * \{ */ +static bool edbm_edge_split_selected_edges(wmOperator *op, Object *obedit, BMEditMesh *em) +{ + BMesh *bm = em->bm; + if (bm->totedgesel == 0) { + return false; + } + if (!EDBM_op_call_and_selectf( + em, op, "edges.out", false, "split_edges edges=%he", BM_ELEM_SELECT)) { + return false; + } + + EDBM_select_flush(em); + EDBM_update_generic(obedit->data, true, true); + + return true; +} + +static bool edbm_edge_split_selected_verts(wmOperator *op, Object *obedit, BMEditMesh *em) +{ + BMesh *bm = em->bm; + + /* Note that tracking vertices through the 'split_edges' operator is complicated. + * Instead, tag loops for selection. */ + if (bm->totvertsel == 0) { + return false; + } + + /* Flush from vertices to edges. */ + BMIter iter; + BMEdge *eed; + BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { + BM_elem_flag_disable(eed, BM_ELEM_TAG); + if (eed->l != NULL) { + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && + (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) || + BM_elem_flag_test(eed->v2, BM_ELEM_SELECT))) { + BM_elem_flag_enable(eed, BM_ELEM_TAG); + } + /* Store selection in loop tags. */ + BMLoop *l_iter = eed->l; + do { + BM_elem_flag_set(l_iter, BM_ELEM_TAG, BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)); + } while ((l_iter = l_iter->radial_next) != eed->l); + } + } + + if (!EDBM_op_callf(em, + op, + "split_edges edges=%he verts=%hv use_verts=%b", + BM_ELEM_TAG, + BM_ELEM_SELECT, + true)) { + return false; + } + + BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { + if (eed->l != NULL) { + BMLoop *l_iter = eed->l; + do { + if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { + BM_vert_select_set(em->bm, l_iter->v, true); + } + } while ((l_iter = l_iter->radial_next) != eed->l); + } + else { + /* Split out wire. */ + for (int i = 0; i < 2; i++) { + BMVert *v = *(&eed->v1 + i); + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + if (eed != BM_DISK_EDGE_NEXT(eed, v)) { + BM_vert_separate(bm, v, &eed, 1, true, NULL, NULL); + } + } + } + } + } + + EDBM_select_flush(em); + EDBM_update_generic(obedit->data, true, true); + + return true; +} + static int edbm_edge_split_exec(bContext *C, wmOperator *op) { + const int type = RNA_enum_get(op->ptr, "type"); + ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( @@ -1780,20 +1865,21 @@ static int edbm_edge_split_exec(bContext *C, wmOperator *op) for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (em->bm->totedgesel == 0) { - continue; - } - if (!EDBM_op_call_and_selectf( - em, op, "edges.out", false, "split_edges edges=%he", BM_ELEM_SELECT)) { - continue; - } - - if (em->selectmode == SCE_SELECT_FACE) { - EDBM_select_flush(em); + switch (type) { + case BM_VERT: + if (!edbm_edge_split_selected_verts(op, obedit, em)) { + continue; + } + break; + case BM_EDGE: + if (!edbm_edge_split_selected_edges(op, obedit, em)) { + continue; + } + break; + default: + BLI_assert(0); } - - EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -1813,6 +1899,20 @@ void MESH_OT_edge_split(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + static const EnumPropertyItem merge_type_items[] = { + {BM_EDGE, "EDGE", 0, "Faces by Edges", "Split faces along selected edges"}, + {BM_VERT, + "VERT", + 0, + "Faces & Edges by Vertices", + "Split faces & edges connected to selected vertices"}, + {0, NULL, 0, NULL, NULL}, + }; + + ot->prop = RNA_def_enum( + ot->srna, "type", merge_type_items, BM_EDGE, "Type", "Method to use for splitting"); } /** \} */ diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index c3949899dff..1049db74a75 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -459,15 +459,20 @@ int count_set_pose_transflags(Object *ob, for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { bone = pchan->bone; - bone->flag &= ~(BONE_TRANSFORM | BONE_TRANSFORM_MIRROR); if (PBONE_VISIBLE(arm, bone)) { if ((bone->flag & BONE_SELECTED)) { bone->flag |= BONE_TRANSFORM; } + else { + bone->flag &= ~BONE_TRANSFORM; + } bone->flag &= ~BONE_HINGE_CHILD_TRANSFORM; bone->flag &= ~BONE_TRANSFORM_CHILD; } + else { + bone->flag &= ~BONE_TRANSFORM; + } } /* make sure no bone can be transformed when a parent is transformed */ @@ -1581,145 +1586,148 @@ void autokeyframe_pose(bContext *C, Scene *scene, Object *ob, int tmode, short t FCurve *fcu; // TODO: this should probably be done per channel instead... - if (autokeyframe_cfra_can_key(scene, id)) { - ReportList *reports = CTX_wm_reports(C); - ToolSettings *ts = scene->toolsettings; - KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene); - ListBase nla_cache = {NULL, NULL}; - float cfra = (float)CFRA; - eInsertKeyFlags flag = 0; + if (!autokeyframe_cfra_can_key(scene, id)) { + /* tag channels that should have unkeyed data */ + for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->bone->flag & BONE_TRANSFORM) { + /* tag this channel */ + pchan->bone->flag |= BONE_UNKEYED; + } + } + return; + } - /* flag is initialized from UserPref keyframing settings - * - special exception for targetless IK - INSERTKEY_MATRIX keyframes should get - * visual keyframes even if flag not set, as it's not that useful otherwise - * (for quick animation recording) - */ - flag = ANIM_get_keyframing_flags(scene, true); + ReportList *reports = CTX_wm_reports(C); + ToolSettings *ts = scene->toolsettings; + KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene); + ListBase nla_cache = {NULL, NULL}; + float cfra = (float)CFRA; + eInsertKeyFlags flag = 0; + + /* flag is initialized from UserPref keyframing settings + * - special exception for targetless IK - INSERTKEY_MATRIX keyframes should get + * visual keyframes even if flag not set, as it's not that useful otherwise + * (for quick animation recording) + */ + flag = ANIM_get_keyframing_flags(scene, true); + + if (targetless_ik) { + flag |= INSERTKEY_MATRIX; + } - if (targetless_ik) { - flag |= INSERTKEY_MATRIX; + for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { + if ((pchan->bone->flag & BONE_TRANSFORM) == 0 && + !((pose->flag & POSE_MIRROR_EDIT) && (pchan->bone->flag & BONE_TRANSFORM_MIRROR))) { + continue; } - for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { - if ((pchan->bone->flag & BONE_TRANSFORM) || - ((pose->flag & POSE_MIRROR_EDIT) && (pchan->bone->flag & BONE_TRANSFORM_MIRROR))) { - ListBase dsources = {NULL, NULL}; - - /* clear any 'unkeyed' flag it may have */ - pchan->bone->flag &= ~BONE_UNKEYED; - - /* add datasource override for the camera object */ - ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan); - - /* only insert into active keyingset? */ - if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) { - /* run the active Keying Set on the current datasource */ - ANIM_apply_keyingset(C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, cfra); - } - /* only insert into available channels? */ - else if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) { - if (act) { - for (fcu = act->curves.first; fcu; fcu = fcu->next) { - /* only insert keyframes for this F-Curve if it affects the current bone */ - if (strstr(fcu->rna_path, "bones")) { - char *pchanName = BLI_str_quoted_substrN(fcu->rna_path, "bones["); - - /* only if bone name matches too... - * NOTE: this will do constraints too, but those are ok to do here too? - */ - if (pchanName && STREQ(pchanName, pchan->name)) { - insert_keyframe(bmain, - reports, - id, - act, - ((fcu->grp) ? (fcu->grp->name) : (NULL)), - fcu->rna_path, - fcu->array_index, - cfra, - ts->keyframe_type, - &nla_cache, - flag); - } - - if (pchanName) { - MEM_freeN(pchanName); - } - } - } - } - } - /* only insert keyframe if needed? */ - else if (IS_AUTOKEY_FLAG(scene, INSERTNEEDED)) { - bool do_loc = false, do_rot = false, do_scale = false; + ListBase dsources = {NULL, NULL}; - /* Filter the conditions when this happens - * (assume that 'curarea->spacetype == SPACE_VIEW3D'). */ - if (tmode == TFM_TRANSLATION) { - if (targetless_ik) { - do_rot = true; - } - else { - do_loc = true; - } - } - else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) { - if (ELEM(scene->toolsettings->transform_pivot_point, - V3D_AROUND_CURSOR, - V3D_AROUND_ACTIVE)) { - do_loc = true; - } + /* clear any 'unkeyed' flag it may have */ + pchan->bone->flag &= ~BONE_UNKEYED; - if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) { - do_rot = true; - } - } - else if (tmode == TFM_RESIZE) { - if (ELEM(scene->toolsettings->transform_pivot_point, - V3D_AROUND_CURSOR, - V3D_AROUND_ACTIVE)) { - do_loc = true; - } + /* add datasource override for the camera object */ + ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan); - if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) { - do_scale = true; - } + /* only insert into active keyingset? */ + if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) { + /* run the active Keying Set on the current datasource */ + ANIM_apply_keyingset(C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, cfra); + } + /* only insert into available channels? */ + else if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) { + if (act) { + for (fcu = act->curves.first; fcu; fcu = fcu->next) { + /* only insert keyframes for this F-Curve if it affects the current bone */ + if (strstr(fcu->rna_path, "bones") == NULL) { + continue; } + char *pchanName = BLI_str_quoted_substrN(fcu->rna_path, "bones["); - if (do_loc) { - KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID); - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra); - } - if (do_rot) { - KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_ROTATION_ID); - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra); + /* only if bone name matches too... + * NOTE: this will do constraints too, but those are ok to do here too? + */ + if (pchanName && STREQ(pchanName, pchan->name)) { + insert_keyframe(bmain, + reports, + id, + act, + ((fcu->grp) ? (fcu->grp->name) : (NULL)), + fcu->rna_path, + fcu->array_index, + cfra, + ts->keyframe_type, + &nla_cache, + flag); } - if (do_scale) { - KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_SCALING_ID); - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra); + + if (pchanName) { + MEM_freeN(pchanName); } } - /* insert keyframe in all (transform) channels */ + } + } + /* only insert keyframe if needed? */ + else if (IS_AUTOKEY_FLAG(scene, INSERTNEEDED)) { + bool do_loc = false, do_rot = false, do_scale = false; + + /* Filter the conditions when this happens + * (assume that 'curarea->spacetype == SPACE_VIEW3D'). */ + if (tmode == TFM_TRANSLATION) { + if (targetless_ik) { + do_rot = true; + } else { - KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID); - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra); + do_loc = true; + } + } + else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) { + if (ELEM(scene->toolsettings->transform_pivot_point, + V3D_AROUND_CURSOR, + V3D_AROUND_ACTIVE)) { + do_loc = true; } - /* free temp info */ - BLI_freelistN(&dsources); + if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) { + do_rot = true; + } } - } + else if (tmode == TFM_RESIZE) { + if (ELEM(scene->toolsettings->transform_pivot_point, + V3D_AROUND_CURSOR, + V3D_AROUND_ACTIVE)) { + do_loc = true; + } - BKE_animsys_free_nla_keyframing_context_cache(&nla_cache); - } - else { - /* tag channels that should have unkeyed data */ - for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->bone->flag & BONE_TRANSFORM) { - /* tag this channel */ - pchan->bone->flag |= BONE_UNKEYED; + if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) { + do_scale = true; + } + } + + if (do_loc) { + KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra); + } + if (do_rot) { + KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_ROTATION_ID); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra); + } + if (do_scale) { + KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_SCALING_ID); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra); } } + /* insert keyframe in all (transform) channels */ + else { + KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra); + } + + /* free temp info */ + BLI_freelistN(&dsources); } + + BKE_animsys_free_nla_keyframing_context_cache(&nla_cache); } /** \} */ diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 4e8e0cc2369..53dd54d4b53 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -530,6 +530,12 @@ void pose_transform_mirror_update(TransInfo *t, TransDataContainer *tc, Object * unit_m4(flip_mtx); flip_mtx[0][0] = -1; + for (bPoseChannel *pchan_orig = ob->pose->chanbase.first; pchan_orig; + pchan_orig = pchan_orig->next) { + /* Clear the MIRROR flag from previous runs. */ + pchan_orig->bone->flag &= ~BONE_TRANSFORM_MIRROR; + } + bPose *pose = ob->pose; PoseInitData_Mirror *pid = NULL; if ((t->mode != TFM_BONESIZE) && (pose->flag & POSE_MIRROR_RELATIVE)) { @@ -565,6 +571,9 @@ void pose_transform_mirror_update(TransInfo *t, TransDataContainer *tc, Object * } BKE_pchan_apply_mat4(pchan, pchan_mtx_final, false); + /* Set flag to let autokeyframe know to keyframe the mirrred bone. */ + pchan->bone->flag |= BONE_TRANSFORM_MIRROR; + /* In this case we can do target-less IK grabbing. */ if (t->mode == TFM_TRANSLATION) { bKinematicConstraint *data = has_targetless_ik(pchan); diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index a90935bfe62..a3b1f315f04 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -468,7 +468,7 @@ typedef enum eGPDlayer_OnionFlag { /* layer blend_mode */ typedef enum eGPLayerBlendModes { eGplBlendMode_Regular = 0, - eGplBlendMode_Overlay = 1, + eGplBlendMode_HardLight = 1, eGplBlendMode_Add = 2, eGplBlendMode_Subtract = 3, eGplBlendMode_Multiply = 4, @@ -661,8 +661,6 @@ typedef enum eGPdata_Flag { /* Autolock not active layers */ GP_DATA_AUTOLOCK_LAYERS = (1 << 20), - /* Internal flag for python update */ - GP_DATA_PYTHON_UPDATED = (1 << 21), } eGPdata_Flag; /* gpd->onion_flag */ diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index deacd8e1cfc..4afa467d2e3 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -114,7 +114,7 @@ static const EnumPropertyItem rna_enum_gplayer_move_type_items[] = { static const EnumPropertyItem rna_enum_layer_blend_modes_items[] = { {eGplBlendMode_Regular, "REGULAR", 0, "Regular", ""}, - {eGplBlendMode_Overlay, "OVERLAY", 0, "Overlay", ""}, + {eGplBlendMode_HardLight, "HARDLIGHT", 0, "Hard Light", ""}, {eGplBlendMode_Add, "ADD", 0, "Add", ""}, {eGplBlendMode_Subtract, "SUBTRACT", 0, "Subtract", ""}, {eGplBlendMode_Multiply, "MULTIPLY", 0, "Multiply", ""}, @@ -642,7 +642,6 @@ static void rna_GPencil_stroke_point_add( /* Calc geometry data. */ BKE_gpencil_stroke_geometry_update(stroke); - gpd->flag |= GP_DATA_PYTHON_UPDATED; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); @@ -704,7 +703,6 @@ static void rna_GPencil_stroke_point_pop(ID *id, /* Calc geometry data. */ BKE_gpencil_stroke_geometry_update(stroke); - gpd->flag |= GP_DATA_PYTHON_UPDATED; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.c index b4b1c1d3698..bfef9341913 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c @@ -154,6 +154,12 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, /* equivalent to normal_world_to_object */ GPU_link(mat, "normal_transform_transposed_m4v3", vnor, ob_mat, &norm); + { + /* See SHD_PROJ_FLAT for explanation. */ + GPU_link(mat, "set_rgb", *texco, texco); + GPU_link(mat, "set_rgb", *texco, &input_coords); + in[0].link = input_coords; + } GPU_link( mat, gpu_node_name, *texco, norm, GPU_image(mat, ima, iuser), &col1, &col2, &col3); GPU_stack_link( |