Materials: support changing materials during evaluation
This commit allows that the evaluated geometry of an object has different materials from the original geometry. This is needed for geometry nodes. The main thing that changes for render engines and exporters is that the number of material slots on an object and its geometry might not match anymore. For original data, the slot counts are still equal, but not for evaluated data. Accessing material slots though rna stays the same. The behavior adapts automatically depending on whether the object is evaluated. When accessing materials of an object through `BKE_object_material_*` one has to use a new api for evaluated objects: `BKE_object_material_get_eval` and `BKE_object_material_count_eval`. In the future, the different behavior might be hidden behind a more general C api, but that would require quite a few more changes. The ground truth for the number of materials is the number of materials on the geometry now. This is important in the current design, because Eevee needs to know the number of materials just based on the mesh in `mesh_render_mat_len_get` and similar places. In a few places I had to add a special case for mesh edit mode to get it to work properly. This is unfortunate, but I don't see a way around that for now. Differential Revision: https://developer.blender.org/D11236
This commit is contained in:
@@ -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]);
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
|
@@ -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) {
|
||||
|
@@ -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))) {
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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,12 +1387,50 @@ static void rna_MaterialSlot_update(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
|
||||
static char *rna_MaterialSlot_path(PointerRNA *ptr)
|
||||
{
|
||||
Object *ob = (Object *)ptr->owner_id;
|
||||
int index = (Material **)ptr->data - ob->mat;
|
||||
|
||||
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;
|
||||
if (DEG_is_evaluated_object(ob)) {
|
||||
return BKE_object_material_count_eval(ob);
|
||||
}
|
||||
else {
|
||||
return ob->totcol;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return rna_pointer_inherit_refine(ptr, &RNA_ObjectDisplay, ptr->data);
|
||||
@@ -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);
|
||||
|
Reference in New Issue
Block a user