diff options
14 files changed, 164 insertions, 30 deletions
diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index 14ea50f808a..96aae2d2d42 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -105,6 +105,11 @@ struct Material *BKE_id_material_pop(struct Main *bmain, /* index is an int because of RNA. */ int index); void BKE_id_material_clear(struct Main *bmain, struct ID *id); + +/* eval api */ +struct Material *BKE_object_material_get_eval(struct Object *ob, short act); +int BKE_object_material_count_eval(struct Object *ob); + /* rendering */ void ramp_blend(int type, float r_col[3], const float fac, const float col[3]); diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 37d47a984cc..73afea98163 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -77,6 +77,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "GPU_material.h" @@ -700,6 +701,73 @@ Material *BKE_object_material_get(Object *ob, short act) return ma_p ? *ma_p : NULL; } +static ID *get_evaluated_object_data_with_materials(Object *ob) +{ + ID *data = ob->data; + /* Meshes in edit mode need special handling. */ + if (ob->type == OB_MESH && ob->mode == OB_MODE_EDIT) { + Mesh *mesh = ob->data; + if (mesh->edit_mesh && mesh->edit_mesh->mesh_eval_final) { + data = &mesh->edit_mesh->mesh_eval_final->id; + } + } + return data; +} + +/** + * On evaluated objects the number of materials on an object and its data might go out of sync. + * This is because during evaluation materials can be added/removed on the object data. + * + * For rendering or exporting we generally use the materials on the object data. However, some + * material indices might be overwritten by the object. + */ +Material *BKE_object_material_get_eval(Object *ob, short act) +{ + BLI_assert(DEG_is_evaluated_object(ob)); + const int slot_index = act - 1; + + if (slot_index < 0) { + return NULL; + } + ID *data = get_evaluated_object_data_with_materials(ob); + const short *tot_slots_data_ptr = BKE_id_material_len_p(data); + const int tot_slots_data = tot_slots_data_ptr ? *tot_slots_data_ptr : 0; + if (slot_index >= tot_slots_data) { + return NULL; + } + const int tot_slots_object = ob->totcol; + + Material ***materials_data_ptr = BKE_id_material_array_p(data); + Material **materials_data = materials_data_ptr ? *materials_data_ptr : NULL; + Material **materials_object = ob->mat; + + /* Check if slot is overwritten by object. */ + if (slot_index < tot_slots_object) { + if (ob->matbits) { + if (ob->matbits[slot_index]) { + Material *material = materials_object[slot_index]; + if (material != NULL) { + return material; + } + } + } + } + /* Otherwise use data from object-data. */ + if (slot_index < tot_slots_data) { + Material *material = materials_data[slot_index]; + return material; + } + return NULL; +} + +int BKE_object_material_count_eval(Object *ob) +{ + BLI_assert(DEG_is_evaluated_object(ob)); + ID *id = get_evaluated_object_data_with_materials(ob); + const short *len_p = BKE_id_material_len_p(id); + return len_p ? *len_p : 0; +} + Material *BKE_gpencil_material(Object *ob, short act) { Material *ma = BKE_object_material_get(ob, act); diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index 70d48ea6040..7fe984b4397 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -265,8 +265,7 @@ void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata, Object *ob) { BLI_assert(ob->type == OB_HAIR); - Hair *hair = ob->data; - Material *material = hair->mat ? hair->mat[HAIR_MATERIAL_NR - 1] : NULL; + Material *material = BKE_object_material_get_eval(ob, HAIR_MATERIAL_NR); eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, NULL, NULL, material); } @@ -291,8 +290,7 @@ void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata, if (draw_as != PART_DRAW_PATH) { continue; } - Mesh *mesh = ob->data; - Material *material = part->omat - 1 < mesh->totcol ? NULL : mesh->mat[part->omat - 1]; + Material *material = BKE_object_material_get_eval(ob, part->omat); eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, psys, md, material); } } @@ -318,7 +316,7 @@ void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *s if (geom == NULL) { continue; } - Material *material = BKE_object_material_get(ob, i + 1); + Material *material = BKE_object_material_get_eval(ob, i + 1); DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create( vedata, sldata, ob, material, false); DRW_shgroup_call(grp, geom, ob); diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index a4325675ea9..d2e0c8308c5 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -723,7 +723,7 @@ BLI_INLINE Material *eevee_object_material_get(Object *ob, int slot, bool holdou if (holdout) { return BKE_material_default_holdout(); } - Material *ma = BKE_object_material_get(ob, slot + 1); + Material *ma = BKE_object_material_get_eval(ob, slot + 1); if (ma == NULL) { if (ob->type == OB_VOLUME) { ma = BKE_material_default_volume(); diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index 809d6010f10..eed36221fcb 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -501,7 +501,7 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, Scene *scene, Object *ob) { - Material *ma = BKE_object_material_get(ob, 1); + Material *ma = BKE_object_material_get_eval(ob, 1); if (ma == NULL) { if (ob->type == OB_VOLUME) { diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index ee51b751187..adb70f97585 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -60,7 +60,8 @@ GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob) /* Check if any material with holdout flag enabled. */ tgp_ob->do_mat_holdout = false; - for (int i = 0; i < ob->totcol; i++) { + const int tot_materials = BKE_object_material_count_eval(ob); + for (int i = 0; i < tot_materials; i++) { MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, i + 1); if (((gp_style != NULL) && (gp_style->flag & GP_MATERIAL_IS_STROKE_HOLDOUT)) || ((gp_style->flag & GP_MATERIAL_IS_FILL_HOLDOUT))) { diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c index 526f553329e..e3e84dd4c8c 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -186,7 +186,7 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje { GPENCIL_MaterialPool *matpool = pd->last_material_pool; - int mat_len = max_ii(1, ob->totcol); + int mat_len = max_ii(1, BKE_object_material_count_eval(ob)); bool reuse_matpool = matpool && ((matpool->used_count + mat_len) <= GP_MATERIAL_BUFFER_LEN); diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c index 891142fe0a2..aa26aa47faa 100644 --- a/source/blender/draw/engines/overlay/overlay_gpencil.c +++ b/source/blender/draw/engines/overlay/overlay_gpencil.c @@ -382,7 +382,7 @@ static void overlay_gpencil_draw_stroke_color_name(bGPDlayer *UNUSED(gpl), void *thunk) { Object *ob = (Object *)thunk; - Material *ma = BKE_object_material_get(ob, gps->mat_nr + 1); + Material *ma = BKE_object_material_get_eval(ob, gps->mat_nr + 1); if (ma == NULL) { return; } diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c index 89e724bcfcc..d52640ed174 100644 --- a/source/blender/draw/engines/overlay/overlay_paint.c +++ b/source/blender/draw/engines/overlay/overlay_paint.c @@ -48,7 +48,7 @@ static bool paint_object_is_rendered_transparent(View3D *v3d, Object *ob) v3d->shading.color_type == V3D_SHADING_MATERIAL_COLOR) { Mesh *me = ob->data; for (int i = 0; i < me->totcol; i++) { - Material *mat = me->mat[i]; + Material *mat = BKE_object_material_get_eval(ob, i + 1); if (mat && mat->a < 1.0f) { return true; } diff --git a/source/blender/draw/engines/overlay/overlay_particle.c b/source/blender/draw/engines/overlay/overlay_particle.c index 71064e7ff47..5fa74a8c3a6 100644 --- a/source/blender/draw/engines/overlay/overlay_particle.c +++ b/source/blender/draw/engines/overlay/overlay_particle.c @@ -186,7 +186,7 @@ void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob) /* TODO(fclem): Here would be a good place for preemptive culling. */ /* NOTE(fclem): Is color even useful in our modern context? */ - Material *ma = BKE_object_material_get(ob, part->omat); + Material *ma = BKE_object_material_get_eval(ob, part->omat); float color[4] = {0.6f, 0.6f, 0.6f, part->draw_size}; if (ma != NULL) { copy_v3_v3(color, &ma->r); diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index 6e9118bfe46..800d1085505 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -93,7 +93,7 @@ void workbench_material_ubo_data(WORKBENCH_PrivateData *wpd, /* Return correct material or empty default material if slot is empty. */ BLI_INLINE Material *workbench_object_material_get(Object *ob, int mat_nr) { - Material *ma = BKE_object_material_get(ob, mat_nr); + Material *ma = BKE_object_material_get_eval(ob, mat_nr); if (ma == NULL) { ma = BKE_material_default_empty(); } diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c index 525a81b5581..ddda6d7b58e 100644 --- a/source/blender/draw/engines/workbench/workbench_volume.c +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -202,7 +202,7 @@ static void workbench_volume_material_color(WORKBENCH_PrivateData *wpd, eV3DShadingColorType color_type, float color[3]) { - Material *ma = BKE_object_material_get(ob, VOLUME_MATERIAL_NR); + Material *ma = BKE_object_material_get_eval(ob, VOLUME_MATERIAL_NR); WORKBENCH_UBO_Material ubo_data; workbench_material_ubo_data(wpd, ob, ma, &ubo_data, color_type); copy_v3_v3(color, ubo_data.base_color); diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index c929fe7dfd3..bbb97fc09a3 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -88,6 +88,10 @@ typedef enum eMRExtractType { BLI_INLINE int mesh_render_mat_len_get(Mesh *me) { + /* In edit mode, the displayed mesh is stored in the edit-mesh. */ + if (me->edit_mesh && me->edit_mesh->mesh_eval_final) { + return MAX2(1, me->edit_mesh->mesh_eval_final->totcol); + } return MAX2(1, me->totcol); } diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 3edfd5c44f4..3126b45cb82 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -62,6 +62,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "DEG_depsgraph_query.h" + const EnumPropertyItem rna_enum_object_mode_items[] = { {OB_MODE_OBJECT, "OBJECT", ICON_OBJECT_DATAMODE, "Object Mode", ""}, {OB_MODE_EDIT, "EDIT", ICON_EDITMODE_HLT, "Edit Mode", ""}, @@ -1253,10 +1255,15 @@ static int rna_Object_rotation_4d_editable(PointerRNA *ptr, int index) return PROP_EDITABLE; } +static int rna_MaterialSlot_index(PointerRNA *ptr) +{ + return POINTER_AS_INT(ptr->data); +} + static int rna_MaterialSlot_material_editable(PointerRNA *ptr, const char **UNUSED(r_info)) { Object *ob = (Object *)ptr->owner_id; - const int index = (Material **)ptr->data - ob->mat; + const int index = rna_MaterialSlot_index(ptr); bool is_editable; if ((ob->matbits == NULL) || ob->matbits[index]) { @@ -1273,9 +1280,14 @@ static PointerRNA rna_MaterialSlot_material_get(PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; Material *ma; - const int index = (Material **)ptr->data - ob->mat; + const int index = rna_MaterialSlot_index(ptr); - ma = BKE_object_material_get(ob, index + 1); + if (DEG_is_evaluated_object(ob)) { + ma = BKE_object_material_get_eval(ob, index + 1); + } + else { + ma = BKE_object_material_get(ob, index + 1); + } return rna_pointer_inherit_refine(ptr, &RNA_Material, ma); } @@ -1284,7 +1296,7 @@ static void rna_MaterialSlot_material_set(PointerRNA *ptr, struct ReportList *UNUSED(reports)) { Object *ob = (Object *)ptr->owner_id; - int index = (Material **)ptr->data - ob->mat; + int index = rna_MaterialSlot_index(ptr); BLI_assert(BKE_id_is_in_global_main(&ob->id)); BLI_assert(BKE_id_is_in_global_main(value.data)); @@ -1309,15 +1321,17 @@ static bool rna_MaterialSlot_material_poll(PointerRNA *ptr, PointerRNA value) static int rna_MaterialSlot_link_get(PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; - int index = (Material **)ptr->data - ob->mat; - - return ob->matbits[index] != 0; + int index = rna_MaterialSlot_index(ptr); + if (index < ob->totcol) { + return ob->matbits[index] != 0; + } + return false; } static void rna_MaterialSlot_link_set(PointerRNA *ptr, int value) { Object *ob = (Object *)ptr->owner_id; - int index = (Material **)ptr->data - ob->mat; + int index = rna_MaterialSlot_index(ptr); if (value) { ob->matbits[index] = 1; @@ -1335,7 +1349,7 @@ static int rna_MaterialSlot_name_length(PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; Material *ma; - int index = (Material **)ptr->data - ob->mat; + int index = rna_MaterialSlot_index(ptr); ma = BKE_object_material_get(ob, index + 1); @@ -1350,7 +1364,7 @@ static void rna_MaterialSlot_name_get(PointerRNA *ptr, char *str) { Object *ob = (Object *)ptr->owner_id; Material *ma; - int index = (Material **)ptr->data - ob->mat; + int index = rna_MaterialSlot_index(ptr); ma = BKE_object_material_get(ob, index + 1); @@ -1373,10 +1387,48 @@ static void rna_MaterialSlot_update(Main *bmain, Scene *scene, PointerRNA *ptr) static char *rna_MaterialSlot_path(PointerRNA *ptr) { + int index = rna_MaterialSlot_index(ptr); + return BLI_sprintfN("material_slots[%d]", index); +} + +static int rna_Object_material_slots_length(PointerRNA *ptr) +{ Object *ob = (Object *)ptr->owner_id; - int index = (Material **)ptr->data - ob->mat; + if (DEG_is_evaluated_object(ob)) { + return BKE_object_material_count_eval(ob); + } + else { + return ob->totcol; + } +} - return BLI_sprintfN("material_slots[%d]", index); +static void rna_Object_material_slots_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + const int length = rna_Object_material_slots_length(ptr); + iter->internal.count.item = 0; + iter->internal.count.ptr = ptr->owner_id; + iter->valid = length > 0; +} + +static void rna_Object_material_slots_next(CollectionPropertyIterator *iter) +{ + const int length = rna_Object_material_slots_length(&iter->ptr); + iter->internal.count.item++; + iter->valid = iter->internal.count.item < length; +} + +static PointerRNA rna_Object_material_slots_get(CollectionPropertyIterator *iter) +{ + PointerRNA ptr; + RNA_pointer_create((ID *)iter->internal.count.ptr, + &RNA_MaterialSlot, + POINTER_FROM_INT(iter->internal.count.item), + &ptr); + return ptr; +} + +static void rna_Object_material_slots_end(CollectionPropertyIterator *UNUSED(iter)) +{ } static PointerRNA rna_Object_display_get(PointerRNA *ptr) @@ -2958,12 +3010,18 @@ static void rna_def_object(BlenderRNA *brna) /* materials */ prop = RNA_def_property(srna, "material_slots", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol"); RNA_def_property_struct_type(prop, "MaterialSlot"); RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_PROP_NAME); - /* don't dereference pointer! */ - RNA_def_property_collection_funcs( - prop, NULL, NULL, NULL, "rna_iterator_array_get", NULL, NULL, NULL, NULL); + /* Don't dereference the material slot pointer, it is the slot index encoded in a pointer. */ + RNA_def_property_collection_funcs(prop, + "rna_Object_material_slots_begin", + "rna_Object_material_slots_next", + "rna_Object_material_slots_end", + "rna_Object_material_slots_get", + "rna_Object_material_slots_length", + NULL, + NULL, + NULL); RNA_def_property_ui_text(prop, "Material Slots", "Material slots in the object"); prop = RNA_def_property(srna, "active_material", PROP_POINTER, PROP_NONE); |