Asset: Dropping Material assets on material slot under mouse cursor.

This patch allows dropping material assets from material slot under the mouse
cursor. Before this change the material slot had to be hand-picked from the
properties panel.

For consistency it is chosen to do this in any shading mode as the tooltip shows
what is exactly going to happen during release.

The feature also works for other object types than Meshes as it uses the drawn surface on the
GPU to detect the material slots. Performance of this patch has been tested with AMD GCN3.0
cards and are very responsive.

Reviewed By: fclem, Severin

Differential Revision: https://developer.blender.org/D12190
This commit is contained in:
Jeroen Bakker 2021-09-08 08:39:03 +02:00 committed by Jeroen Bakker
parent 8d40d61af0
commit 54f5c174a8
Notes: blender-bot 2023-02-13 22:20:49 +01:00
Referenced by issue #90576, As a user I want to apply an material to the material slot under the mouse cursor.
11 changed files with 166 additions and 44 deletions

View File

@ -463,6 +463,8 @@ void BKE_object_replace_data_on_shallow_copy(struct Object *ob, struct ID *new_d
struct PartEff;
struct PartEff *BKE_object_do_version_give_parteff_245(struct Object *ob);
bool BKE_object_supports_material_slots(struct Object *ob);
#ifdef __cplusplus
}
#endif

View File

@ -5754,3 +5754,9 @@ void BKE_object_replace_data_on_shallow_copy(Object *ob, ID *new_data)
}
ob->id.py_instance = NULL;
}
bool BKE_object_supports_material_slots(struct Object *ob)
{
return ELEM(
ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_HAIR, OB_POINTCLOUD, OB_VOLUME);
}

View File

@ -110,6 +110,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
bool use_obedit_skip,
bool draw_surface,
bool use_nearest,
const bool do_material_sub_selection,
const struct rcti *rect,
DRW_SelectPassFn select_pass_fn,
void *select_pass_user_data,

View File

@ -25,9 +25,12 @@
#include "DRW_render.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "BLI_alloca.h"
#include "DNA_particle_types.h"
#include "GPU_shader.h"
@ -80,6 +83,7 @@ typedef struct BASIC_PrivateData {
DRWShadingGroup *depth_shgrp[2];
DRWShadingGroup *depth_shgrp_cull[2];
DRWShadingGroup *depth_hair_shgrp[2];
bool use_material_slot_selection;
} BASIC_PrivateData; /* Transient data */
/* Functions */
@ -131,6 +135,8 @@ static void basic_cache_init(void *vedata)
stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
}
stl->g_data->use_material_slot_selection = DRW_state_is_material_select();
/* Twice for normal and in front objects. */
for (int i = 0; i < 2; i++) {
DRWState clip_state = (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) ? DRW_STATE_CLIP_PLANES : 0;
@ -155,6 +161,38 @@ static void basic_cache_init(void *vedata)
}
}
/* TODO(fclem): DRW_cache_object_surface_material_get needs a refactor to allow passing NULL
* instead of gpumat_array. Avoiding all this boilerplate code. */
static struct GPUBatch **basic_object_surface_material_get(Object *ob)
{
const int materials_len = DRW_cache_object_material_count_get(ob);
struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len);
return DRW_cache_object_surface_material_get(ob, gpumat_array, materials_len);
}
static void basic_cache_populate_particles(void *vedata, Object *ob)
{
const bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
BASIC_StorageList *stl = ((BASIC_Data *)vedata)->stl;
for (ParticleSystem *psys = ob->particlesystem.first; psys != NULL; psys = psys->next) {
if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) {
continue;
}
ParticleSettings *part = psys->part;
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
if (draw_as == PART_DRAW_PATH) {
struct GPUBatch *hairs = DRW_cache_particles_get_hair(ob, psys, NULL);
if (stl->g_data->use_material_slot_selection) {
const short material_slot = part->omat;
DRW_select_load_id(ob->runtime.select_id | (material_slot << 16));
}
DRW_shgroup_call(stl->g_data->depth_hair_shgrp[do_in_front], hairs, NULL);
}
}
}
static void basic_cache_populate(void *vedata, Object *ob)
{
BASIC_StorageList *stl = ((BASIC_Data *)vedata)->stl;
@ -165,24 +203,13 @@ static void basic_cache_populate(void *vedata, Object *ob)
return;
}
bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
const DRWContextState *draw_ctx = DRW_context_state_get();
if (ob != draw_ctx->object_edit) {
for (ParticleSystem *psys = ob->particlesystem.first; psys != NULL; psys = psys->next) {
if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) {
continue;
}
ParticleSettings *part = psys->part;
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
if (draw_as == PART_DRAW_PATH) {
struct GPUBatch *hairs = DRW_cache_particles_get_hair(ob, psys, NULL);
DRW_shgroup_call(stl->g_data->depth_hair_shgrp[do_in_front], hairs, NULL);
}
}
basic_cache_populate_particles(vedata, ob);
}
/* Make flat object selectable in ortho view if wireframe is enabled. */
const bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
if ((draw_ctx->v3d->overlay.flag & V3D_OVERLAY_WIREFRAMES) ||
(draw_ctx->v3d->shading.type == OB_WIRE) || (ob->dtx & OB_DRAWWIRE) || (ob->dt == OB_WIRE)) {
int flat_axis = 0;
@ -211,9 +238,25 @@ static void basic_cache_populate(void *vedata, Object *ob)
DRW_shgroup_call_sculpt(shgrp, ob, false, false);
}
else {
struct GPUBatch *geom = DRW_cache_object_surface_get(ob);
if (geom) {
DRW_shgroup_call(shgrp, geom, ob);
if (stl->g_data->use_material_slot_selection && BKE_object_supports_material_slots(ob)) {
struct GPUBatch **geoms = basic_object_surface_material_get(ob);
if (geoms) {
const int materials_len = DRW_cache_object_material_count_get(ob);
for (int i = 0; i < materials_len; i++) {
if (geoms[i] == NULL) {
continue;
}
const short material_slot_select_id = i + 1;
DRW_select_load_id(ob->runtime.select_id | (material_slot_select_id << 16));
DRW_shgroup_call(shgrp, geoms[i], ob);
}
}
}
else {
struct GPUBatch *geom = DRW_cache_object_surface_get(ob);
if (geom) {
DRW_shgroup_call(shgrp, geom, ob);
}
}
}
}

View File

@ -731,6 +731,7 @@ void DRW_select_load_id(uint id);
/* Draw State */
bool DRW_state_is_fbo(void);
bool DRW_state_is_select(void);
bool DRW_state_is_material_select(void);
bool DRW_state_is_depth(void);
bool DRW_state_is_image_render(void);
bool DRW_state_is_scene_render(void);

View File

@ -2232,6 +2232,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
bool use_obedit_skip,
bool draw_surface,
bool UNUSED(use_nearest),
const bool do_material_sub_selection,
const rcti *rect,
DRW_SelectPassFn select_pass_fn,
void *select_pass_user_data,
@ -2299,6 +2300,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
DST.viewport = viewport;
DST.options.is_select = true;
DST.options.is_material_select = do_material_sub_selection;
drw_task_graph_init();
/* Get list of enabled engines */
if (use_obedit) {
@ -2776,6 +2778,11 @@ bool DRW_state_is_select(void)
return DST.options.is_select;
}
bool DRW_state_is_material_select(void)
{
return DST.options.is_material_select;
}
bool DRW_state_is_depth(void)
{
return DST.options.is_depth;

View File

@ -553,6 +553,7 @@ typedef struct DRWManager {
struct {
uint is_select : 1;
uint is_material_select : 1;
uint is_depth : 1;
uint is_image_render : 1;
uint is_scene_render : 1;

View File

@ -566,6 +566,13 @@ eV3DSelectObjectFilter ED_view3d_select_filter_from_mode(const struct Scene *sce
void view3d_opengl_select_cache_begin(void);
void view3d_opengl_select_cache_end(void);
int view3d_opengl_select_ex(struct ViewContext *vc,
unsigned int *buffer,
unsigned int bufsize,
const struct rcti *input,
eV3DSelectMode select_mode,
eV3DSelectObjectFilter select_filter,
const bool do_material_slot_selection);
int view3d_opengl_select(struct ViewContext *vc,
unsigned int *buffer,
unsigned int bufsize,
@ -638,6 +645,9 @@ void ED_view3d_draw_setup_view(const struct wmWindowManager *wm,
struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]);
struct Object *ED_view3d_give_object_under_cursor(struct bContext *C, const int mval[2]);
struct Object *ED_view3d_give_material_slot_under_cursor(struct bContext *C,
const int mval[2],
int *r_material_slot);
bool ED_view3d_is_object_under_cursor(struct bContext *C, const int mval[2]);
void ED_view3d_quadview_update(struct ScrArea *area, struct ARegion *region, bool do_clip);
void ED_view3d_update_viewmat(struct Depsgraph *depsgraph,

View File

@ -2729,25 +2729,26 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C,
PointerRNA *properties,
const wmEvent *event)
{
Object *ob = ED_view3d_give_object_under_cursor(C, event->mval);
int mat_slot = 0;
Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot);
if (ob == NULL) {
return BLI_strdup("");
}
mat_slot = max_ii(mat_slot, 1);
char name[MAX_ID_NAME - 2];
RNA_string_get(properties, "name", name);
int active_mat_slot = max_ii(ob->actcol, 1);
Material *prev_mat = BKE_object_material_get(ob, active_mat_slot);
Material *prev_mat = BKE_object_material_get(ob, mat_slot);
char *result;
if (prev_mat) {
const char *tooltip = TIP_("Drop %s on %s (slot %d, replacing %s)");
result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot, prev_mat->id.name + 2);
result = BLI_sprintfN(tooltip, name, ob->id.name + 2, mat_slot, prev_mat->id.name + 2);
}
else {
const char *tooltip = TIP_("Drop %s on %s (slot %d)");
result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot);
result = BLI_sprintfN(tooltip, name, ob->id.name + 2, mat_slot);
}
return result;
}
@ -2755,7 +2756,10 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C,
static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_view3d_give_object_under_cursor(C, event->mval);
int mat_slot = 0;
Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot);
mat_slot = max_ii(mat_slot, 1);
Material *ma;
char name[MAX_ID_NAME - 2];
@ -2765,9 +2769,7 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_CANCELLED;
}
const short active_mat_slot = ob->actcol;
BKE_object_material_assign(CTX_data_main(C), ob, ma, active_mat_slot, BKE_MAT_ASSIGN_USERPREF);
BKE_object_material_assign(CTX_data_main(C), ob, ma, mat_slot, BKE_MAT_ASSIGN_USERPREF);
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);

View File

@ -1953,7 +1953,8 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc,
const int mval[2],
eV3DSelectObjectFilter select_filter,
bool do_nearest,
bool do_nearest_xray_if_supported)
bool do_nearest_xray_if_supported,
const bool do_material_slot_selection)
{
rcti rect;
int hits15, hits9 = 0, hits5 = 0;
@ -1972,7 +1973,8 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc,
view3d_opengl_select_cache_begin();
BLI_rcti_init_pt_radius(&rect, mval, 14);
hits15 = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter);
hits15 = view3d_opengl_select_ex(
vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter, do_material_slot_selection);
if (hits15 == 1) {
hits = selectbuffer_ret_hits_15(buffer, hits15);
goto finally;
@ -2071,7 +2073,8 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc,
do_nearest = do_nearest && !enumerate;
int hits = mixed_bones_object_selectbuffer(vc, buffer, mval, select_filter, do_nearest, true);
int hits = mixed_bones_object_selectbuffer(
vc, buffer, mval, select_filter, do_nearest, true, false);
return hits;
}
@ -2088,12 +2091,14 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
int hits,
Base *startbase,
bool has_bones,
bool do_nearest)
bool do_nearest,
int *r_sub_selection)
{
ViewLayer *view_layer = vc->view_layer;
View3D *v3d = vc->v3d;
Base *base, *basact = NULL;
int a;
int sub_selection_id = 0;
if (do_nearest) {
uint min = 0xFFFFFFFF;
@ -2105,6 +2110,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
if (min > buffer[4 * a + 1] && (buffer[4 * a + 3] & 0xFFFF0000)) {
min = buffer[4 * a + 1];
selcol = buffer[4 * a + 3] & 0xFFFF;
sub_selection_id = (buffer[4 * a + 3] & 0xFFFF0000) >> 16;
}
}
}
@ -2118,6 +2124,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
if (min > buffer[4 * a + 1] && notcol != (buffer[4 * a + 3] & 0xFFFF)) {
min = buffer[4 * a + 1];
selcol = buffer[4 * a + 3] & 0xFFFF;
sub_selection_id = (buffer[4 * a + 3] & 0xFFFF0000) >> 16;
}
}
}
@ -2184,11 +2191,16 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
}
}
if (basact && r_sub_selection) {
*r_sub_selection = sub_selection_id;
}
return basact;
}
/* mval comes from event->mval, only use within region handlers */
Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
static Base *ed_view3d_give_base_under_cursor_ex(bContext *C,
const int mval[2],
int *r_material_slot)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
@ -2202,18 +2214,30 @@ Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
ED_view3d_viewcontext_init(C, &vc, depsgraph);
const bool do_nearest = !XRAY_ACTIVE(vc.v3d);
const bool do_material_slot_selection = r_material_slot != NULL;
const int hits = mixed_bones_object_selectbuffer(
&vc, buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest, false);
&vc, buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest, false, do_material_slot_selection);
if (hits > 0) {
const bool has_bones = selectbuffer_has_bones(buffer, hits);
basact = mouse_select_eval_buffer(
&vc, buffer, hits, vc.view_layer->object_bases.first, has_bones, do_nearest);
const bool has_bones = (r_material_slot == NULL) && selectbuffer_has_bones(buffer, hits);
basact = mouse_select_eval_buffer(&vc,
buffer,
hits,
vc.view_layer->object_bases.first,
has_bones,
do_nearest,
r_material_slot);
}
return basact;
}
/* mval comes from event->mval, only use within region handlers */
Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
{
return ed_view3d_give_base_under_cursor_ex(C, mval, NULL);
}
Object *ED_view3d_give_object_under_cursor(bContext *C, const int mval[2])
{
Base *base = ED_view3d_give_base_under_cursor(C, mval);
@ -2223,6 +2247,17 @@ Object *ED_view3d_give_object_under_cursor(bContext *C, const int mval[2])
return NULL;
}
struct Object *ED_view3d_give_material_slot_under_cursor(struct bContext *C,
const int mval[2],
int *r_material_slot)
{
Base *base = ed_view3d_give_base_under_cursor_ex(C, mval, r_material_slot);
if (base) {
return base->object;
}
return NULL;
}
bool ED_view3d_is_object_under_cursor(bContext *C, const int mval[2])
{
return ED_view3d_give_object_under_cursor(C, mval) != NULL;
@ -2374,7 +2409,8 @@ static bool ed_object_select_pick(bContext *C,
}
}
else {
basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, has_bones, do_nearest);
basact = mouse_select_eval_buffer(
&vc, buffer, hits, startbase, has_bones, do_nearest, NULL);
}
if (has_bones && basact) {
@ -2436,7 +2472,7 @@ static bool ed_object_select_pick(bContext *C,
if (!changed) {
/* fallback to regular object selection if no new bundles were selected,
* allows to select object parented to reconstruction object */
basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest);
basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest, NULL);
}
}
}
@ -2677,7 +2713,7 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
uint buffer[MAXPICKBUF];
const int hits = mixed_bones_object_selectbuffer(
&vc, buffer, location, VIEW3D_SELECT_FILTER_NOP, false, true);
&vc, buffer, location, VIEW3D_SELECT_FILTER_NOP, false, true, false);
retval = bone_mouse_select_menu(C, buffer, hits, true, extend, deselect, toggle);
}
if (!retval) {

View File

@ -964,12 +964,13 @@ static bool drw_select_filter_object_mode_lock_for_weight_paint(Object *ob, void
*
* \note (vc->obedit == NULL) can be set to explicitly skip edit-object selection.
*/
int view3d_opengl_select(ViewContext *vc,
uint *buffer,
uint bufsize,
const rcti *input,
eV3DSelectMode select_mode,
eV3DSelectObjectFilter select_filter)
int view3d_opengl_select_ex(ViewContext *vc,
uint *buffer,
uint bufsize,
const rcti *input,
eV3DSelectMode select_mode,
eV3DSelectObjectFilter select_filter,
const bool do_material_slot_selection)
{
struct bThemeState theme_state;
const wmWindowManager *wm = CTX_wm_manager(vc->C);
@ -1119,6 +1120,7 @@ int view3d_opengl_select(ViewContext *vc,
use_obedit_skip,
draw_surface,
use_nearest,
do_material_slot_selection,
&rect,
drw_select_loop_pass,
&drw_select_loop_user_data,
@ -1149,6 +1151,7 @@ int view3d_opengl_select(ViewContext *vc,
use_obedit_skip,
draw_surface,
use_nearest,
do_material_slot_selection,
&rect,
drw_select_loop_pass,
&drw_select_loop_user_data,
@ -1178,6 +1181,16 @@ finally:
return hits;
}
int view3d_opengl_select(ViewContext *vc,
uint *buffer,
uint bufsize,
const rcti *input,
eV3DSelectMode select_mode,
eV3DSelectObjectFilter select_filter)
{
return view3d_opengl_select_ex(vc, buffer, bufsize, input, select_mode, select_filter, false);
}
int view3d_opengl_select_with_id_filter(ViewContext *vc,
uint *buffer,
uint bufsize,