Fix #133035: Grease Pencil: Convert mesh materials to fill materials

When converting mesh to grease pencil, different face materials should
also be converted to grease pencil ones. This now works by converting
viewport display color diffuse_color from the mesh material to grease
pencil material fill color. Notes:

- If there are empty material slots in the mesh, faces assgined to these
  material slots will be assgined to a new fallback white material in
  the converted grease pencil fills.
- If original mesh does not have any material slots, a white material
  will be created in the grease pencil object for the fills.
- If original mesh has multiple material slots of the same material,
  they would be reduced to one material slot in the converted fills.

Pull Request: https://projects.blender.org/blender/blender/pulls/133087
This commit is contained in:
YimingWu 2025-01-16 15:12:50 +01:00 committed by YimingWu
parent f105366d73
commit 43492a8278

View file

@ -3114,12 +3114,62 @@ static int mesh_to_grease_pencil_add_material(Main &bmain,
return index;
}
class FillColorRecord {
public:
float4 color;
StringRefNull name;
bool operator==(const FillColorRecord &other) const
{
return other.color == color && other.name == name;
}
uint64_t hash() const
{
return hash_string(name);
}
};
static VectorSet<FillColorRecord> mesh_to_grease_pencil_get_material_list(
Object &ob_mesh, const Mesh &mesh, Array<int> &material_remap)
{
const short num_materials = mesh.totcol;
const FillColorRecord empty_fill = {float4(1.0f), DATA_("Empty Fill")};
/* This function will only be called when we want to create fills out of mesh faces, so always
* ensure that fills would have at least one material to be assigned to. */
if (num_materials == 0) {
VectorSet<FillColorRecord> fill_colors;
fill_colors.add(empty_fill);
material_remap.reinitialize(1);
material_remap[0] = 0;
return fill_colors;
}
VectorSet<FillColorRecord> fill_colors;
material_remap.reinitialize(num_materials);
for (const int material_i : IndexRange(num_materials)) {
const Material *mesh_material = BKE_object_material_get(&ob_mesh, material_i + 1);
if (!mesh_material) {
material_remap[material_i] = fill_colors.index_of_or_add(empty_fill);
continue;
}
const float4 fill_color = float4(
mesh_material->r, mesh_material->g, mesh_material->b, mesh_material->a);
const StringRefNull material_name = BKE_id_name(mesh_material->id);
const FillColorRecord record = {fill_color, material_name};
material_remap[material_i] = fill_colors.index_of_or_add(record);
}
return fill_colors;
}
static void mesh_data_to_grease_pencil(const Mesh &mesh_eval,
GreasePencil &grease_pencil,
const int current_frame,
const bool generate_faces,
const float stroke_radius,
const float offset)
const float offset,
const Array<int> &material_remap)
{
grease_pencil.flag |= GREASE_PENCIL_STROKE_ORDER_3D;
@ -3130,7 +3180,6 @@ static void mesh_data_to_grease_pencil(const Mesh &mesh_eval,
bke::greasepencil::Layer &layer_line = grease_pencil.add_layer(DATA_("Lines"));
bke::greasepencil::Drawing *drawing_line = grease_pencil.insert_frame(layer_line, current_frame);
constexpr int face_mat_index = 1;
const Span<float3> mesh_positions = mesh_eval.vert_positions();
const Span<float3> vert_normals = mesh_eval.vert_normals();
const Span<int2> edges = mesh_eval.edges();
@ -3153,12 +3202,20 @@ static void mesh_data_to_grease_pencil(const Mesh &mesh_eval,
bke::SpanAttributeWriter<int> stroke_materials_fill =
curves_fill.attributes_for_write().lookup_or_add_for_write_span<int>(
"material_index", bke::AttrDomain::Curve);
curves_fill.fill_curve_types(CURVE_TYPE_POLY);
bke::AttributeAccessor mesh_attributes = mesh_eval.attributes();
VArray<int> mesh_materials = *mesh_attributes.lookup_or_default(
"material_index", bke::AttrDomain::Face, 0);
curves_fill.fill_curve_types(CURVE_TYPE_POLY);
array_utils::gather(mesh_positions, corner_verts, positions_fill);
array_utils::copy(faces_span, offsets_fill);
cyclic_fill.fill(true);
stroke_materials_fill.span.fill(face_mat_index);
MutableSpan<int> material_span = stroke_materials_fill.span;
for (const int face_i : material_span.index_range()) {
/* Increase material index by 1 to accommodate the stroke material. */
material_span[face_i] = material_remap[mesh_materials[face_i]] + 1;
}
stroke_materials_fill.finish();
}
@ -3197,11 +3254,20 @@ static Object *convert_mesh_to_grease_pencil(Base &base,
const float offset = RNA_float_get(info.op_props, "offset");
/* To be compatible with the thickness value prior to Grease Pencil v3. */
const float stroke_radius = float(thickness) / 1000.0f;
const float stroke_radius = float(thickness) / 2 *
bke::greasepencil::LEGACY_RADIUS_CONVERSION_FACTOR;
const Object *ob_eval = DEG_get_evaluated_object(info.depsgraph, ob);
Object *ob_eval = DEG_get_evaluated_object(info.depsgraph, ob);
const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
VectorSet<FillColorRecord> fill_colors;
Array<int> material_remap;
if (generate_faces) {
fill_colors = mesh_to_grease_pencil_get_material_list(*ob_eval, *mesh_eval, material_remap);
}
Mesh *newob_mesh = static_cast<Mesh *>(newob->data);
BKE_id_material_clear(info.bmain, &newob_mesh->id);
BKE_object_free_derived_caches(newob);
BKE_object_free_modifiers(newob, 0);
@ -3213,18 +3279,26 @@ static Object *convert_mesh_to_grease_pencil(Base &base,
* functions still depend on this value being coherent (The same value as
* `GreasePencil::material_array_num`).
*/
short *totcol = BKE_object_material_len_p(newob);
newob->totcol = *totcol;
newob->actcol = *totcol;
newob->totcol = 0;
newob->actcol = 0;
mesh_to_grease_pencil_add_material(
*info.bmain, *newob, DATA_("Stroke"), float4(0.0f, 0.0f, 0.0f, 1.0f), {});
if (generate_faces) {
mesh_to_grease_pencil_add_material(*info.bmain, *newob, DATA_("Fill"), {}, float4(1.0f));
for (const int fill_i : fill_colors.index_range()) {
const FillColorRecord &record = fill_colors[fill_i];
mesh_to_grease_pencil_add_material(*info.bmain, *newob, record.name, {}, record.color);
}
}
mesh_data_to_grease_pencil(
*mesh_eval, *grease_pencil, info.scene->r.cfra, generate_faces, stroke_radius, offset);
mesh_data_to_grease_pencil(*mesh_eval,
*grease_pencil,
info.scene->r.cfra,
generate_faces,
stroke_radius,
offset,
material_remap);
return newob;
}