GPencil: Convert from Mesh copying Vertex Groups

This patch adds the missing ability to keep the vertex groups when converting to a grease pencil object. This is increadible useful to create rigged grease pencil objects which move together with rigged meshes.

Differential Revision: https://developer.blender.org/D12249
This commit is contained in:
Henrik Dick 2021-08-17 20:04:27 +02:00 committed by Antonio Vazquez
parent 96d0cd57dc
commit 88dc274d05
4 changed files with 127 additions and 94 deletions

View File

@ -169,7 +169,8 @@ bool BKE_gpencil_convert_mesh(struct Main *bmain,
const float matrix[4][4],
const int frame_offset,
const bool use_seams,
const bool use_faces);
const bool use_faces,
const bool use_vgroups);
void BKE_gpencil_stroke_uniform_subdivide(struct bGPdata *gpd,
struct bGPDstroke *gps,

View File

@ -2269,7 +2269,8 @@ static void gpencil_generate_edgeloops(Object *ob,
const int thickness,
const float offset,
const float matrix[4][4],
const bool use_seams)
const bool use_seams,
const bool use_vgroups)
{
Mesh *me = (Mesh *)ob->data;
if (me->totedge == 0) {
@ -2278,9 +2279,9 @@ static void gpencil_generate_edgeloops(Object *ob,
/* Arrays for all edge vertices (forward and backward) that form a edge loop.
* This is reused for each edge-loop to create gpencil stroke. */
uint *stroke = (uint *)MEM_callocN(sizeof(uint) * me->totedge * 2, __func__);
uint *stroke_fw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__);
uint *stroke_bw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__);
uint *stroke = (uint *)MEM_mallocN(sizeof(uint) * me->totedge * 2, __func__);
uint *stroke_fw = (uint *)MEM_mallocN(sizeof(uint) * me->totedge, __func__);
uint *stroke_bw = (uint *)MEM_mallocN(sizeof(uint) * me->totedge, __func__);
/* Create array with all edges. */
GpEdge *gp_edges = (GpEdge *)MEM_callocN(sizeof(GpEdge) * me->totedge, __func__);
@ -2311,11 +2312,6 @@ static void gpencil_generate_edgeloops(Object *ob,
bool pending = true;
int e = 0;
while (pending) {
/* Clear arrays of stroke. */
memset(stroke_fw, 0, sizeof(uint) * me->totedge);
memset(stroke_bw, 0, sizeof(uint) * me->totedge);
memset(stroke, 0, sizeof(uint) * me->totedge * 2);
gped = &gp_edges[e];
/* Look first unused edge. */
if (gped->flag != 0) {
@ -2330,7 +2326,7 @@ static void gpencil_generate_edgeloops(Object *ob,
stroke_bw[0] = e;
gped->flag = 1;
/* Hash used to avoid loop over same vertice. */
/* Hash used to avoid loop over same vertices. */
GHash *v_table = BLI_ghash_int_new(__func__);
/* Look forward edges. */
int totedges = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_fw, e, angle, false);
@ -2354,38 +2350,41 @@ static void gpencil_generate_edgeloops(Object *ob,
bGPDstroke *gps_stroke = BKE_gpencil_stroke_add(
gpf_stroke, MAX2(stroke_mat_index, 0), array_len + 1, thickness * thickness, false);
/* Create dvert data. */
MDeformVert *me_dvert = me->dvert;
if (use_vgroups && me_dvert) {
gps_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * (array_len + 1),
"gp_stroke_dverts");
}
/* Create first segment. */
float fpt[3];
uint v = stroke[0];
gped = &gp_edges[v];
bGPDspoint *pt = &gps_stroke->points[0];
mul_v3_v3fl(fpt, gped->n1, offset);
add_v3_v3v3(&pt->x, gped->v1_co, fpt);
mul_m4_v3(matrix, &pt->x);
for (int i = 0; i < array_len + 1; i++) {
int vertex_index = i == 0 ? gp_edges[stroke[0]].v1 : gp_edges[stroke[i - 1]].v2;
MVert *mv = &me->mvert[vertex_index];
pt->pressure = 1.0f;
pt->strength = 1.0f;
pt = &gps_stroke->points[1];
mul_v3_v3fl(fpt, gped->n2, offset);
add_v3_v3v3(&pt->x, gped->v2_co, fpt);
mul_m4_v3(matrix, &pt->x);
pt->pressure = 1.0f;
pt->strength = 1.0f;
/* Add next segments. */
for (int i = 1; i < array_len; i++) {
v = stroke[i];
gped = &gp_edges[v];
pt = &gps_stroke->points[i + 1];
mul_v3_v3fl(fpt, gped->n2, offset);
add_v3_v3v3(&pt->x, gped->v2_co, fpt);
/* Add segment. */
bGPDspoint *pt = &gps_stroke->points[i];
normal_short_to_float_v3(fpt, mv->no);
mul_v3_v3fl(fpt, fpt, offset);
add_v3_v3v3(&pt->x, mv->co, fpt);
mul_m4_v3(matrix, &pt->x);
pt->pressure = 1.0f;
pt->strength = 1.0f;
/* Copy vertex groups from mesh. Assuming they already exist in the same order. */
if (use_vgroups && me_dvert) {
MDeformVert *dv = &gps_stroke->dvert[i];
MDeformVert *src_dv = &me_dvert[vertex_index];
dv->totweight = src_dv->totweight;
dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
"gp_stroke_dverts_dw");
for (int j = 0; j < dv->totweight; j++) {
dv->dw[j].weight = src_dv->dw[j].weight;
dv->dw[j].def_nr = src_dv->dw[j].def_nr;
}
}
}
BKE_gpencil_stroke_geometry_update(gpd, gps_stroke);
@ -2488,7 +2487,8 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
const float matrix[4][4],
const int frame_offset,
const bool use_seams,
const bool use_faces)
const bool use_faces,
const bool use_vgroups)
{
if (ELEM(nullptr, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == nullptr)) {
return false;
@ -2505,83 +2505,105 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
char element_name[200];
/* Need at least an edge. */
if (me_eval->totvert < 2) {
if (me_eval->totedge < 1) {
return false;
}
/* Create matching vertex groups. */
BKE_defgroup_copy_list(&gpd->vertex_group_names, &me_eval->vertex_group_names);
gpd->vertex_group_active_index = me_eval->vertex_group_active_index;
const float default_colors[2][4] = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.7f, 0.7f, 0.7f, 1.0f}};
/* Create stroke material. */
/* Lookup existing stroke material on gp object. */
make_element_name(ob_mesh->id.name + 2, "Stroke", 64, element_name);
int stroke_mat_index = gpencil_material_find_index_by_name(ob_gp, element_name);
if (stroke_mat_index == -1) {
/* Create new default stroke material as there is no existing material. */
gpencil_add_material(
bmain, ob_gp, element_name, default_colors[0], true, false, &stroke_mat_index);
}
/* Export faces as filled strokes. */
if (use_faces) {
if (use_faces && mpoly_len > 0) {
/* Read all polygons and create fill for each. */
if (mpoly_len > 0) {
make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name);
/* Create Layer and Frame. */
bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_fill == nullptr) {
gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name);
/* Create Layer and Frame. */
bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_fill == nullptr) {
gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
int i;
for (i = 0; i < mpoly_len; i++) {
const MPoly *mp = &mpoly[i];
/* Find material. */
int mat_idx = 0;
Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
make_element_name(
ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name);
mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
if (mat_idx == -1) {
float color[4];
if (ma != nullptr) {
copy_v3_v3(color, &ma->r);
color[3] = 1.0f;
}
else {
copy_v4_v4(color, default_colors[1]);
}
gpencil_add_material(bmain, ob_gp, element_name, color, false, true, &mat_idx);
}
bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
int i;
for (i = 0; i < mpoly_len; i++) {
const MPoly *mp = &mpoly[i];
/* Find material. */
int mat_idx = 0;
Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
make_element_name(
ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name);
mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
if (mat_idx == -1) {
float color[4];
if (ma != nullptr) {
copy_v3_v3(color, &ma->r);
color[3] = 1.0f;
}
else {
copy_v4_v4(color, default_colors[1]);
}
gpencil_add_material(bmain, ob_gp, element_name, color, false, true, &mat_idx);
}
bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, mp->totloop, 10, false);
gps_fill->flag |= GP_STROKE_CYCLIC;
bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, mp->totloop, 10, false);
gps_fill->flag |= GP_STROKE_CYCLIC;
/* Add points to strokes. */
for (int j = 0; j < mp->totloop; j++) {
const MLoop *ml = &mloop[mp->loopstart + j];
const MVert *mv = &me_eval->mvert[ml->v];
bGPDspoint *pt = &gps_fill->points[j];
copy_v3_v3(&pt->x, mv->co);
mul_m4_v3(matrix, &pt->x);
pt->pressure = 1.0f;
pt->strength = 1.0f;
}
/* If has only 3 points subdivide. */
if (mp->totloop == 3) {
BKE_gpencil_stroke_subdivide(gpd, gps_fill, 1, GP_SUBDIV_SIMPLE);
}
BKE_gpencil_stroke_geometry_update(gpd, gps_fill);
/* Create dvert data. */
MDeformVert *me_dvert = me_eval->dvert;
if (use_vgroups && me_dvert) {
gps_fill->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * mp->totloop,
"gp_fill_dverts");
}
/* Add points to strokes. */
for (int j = 0; j < mp->totloop; j++) {
const MLoop *ml = &mloop[mp->loopstart + j];
const MVert *mv = &me_eval->mvert[ml->v];
bGPDspoint *pt = &gps_fill->points[j];
copy_v3_v3(&pt->x, mv->co);
mul_m4_v3(matrix, &pt->x);
pt->pressure = 1.0f;
pt->strength = 1.0f;
/* Copy vertex groups from mesh. Assuming they already exist in the same order. */
if (use_vgroups && me_dvert) {
MDeformVert *dv = &gps_fill->dvert[j];
MDeformVert *src_dv = &me_dvert[ml->v];
dv->totweight = src_dv->totweight;
dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
"gp_fill_dverts_dw");
for (int k = 0; k < dv->totweight; k++) {
dv->dw[k].weight = src_dv->dw[k].weight;
dv->dw[k].def_nr = src_dv->dw[k].def_nr;
}
}
}
/* If has only 3 points subdivide. */
if (mp->totloop == 3) {
BKE_gpencil_stroke_subdivide(gpd, gps_fill, 1, GP_SUBDIV_SIMPLE);
}
BKE_gpencil_stroke_geometry_update(gpd, gps_fill);
}
}
/* Create stroke from edges. */
make_element_name(ob_mesh->id.name + 2, "Lines", 128, element_name);
/* Create Layer and Frame. */
make_element_name(ob_mesh->id.name + 2, "Lines", 128, element_name);
bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_stroke == nullptr) {
gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
@ -2589,8 +2611,16 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get(
gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
gpencil_generate_edgeloops(
ob_eval, gpd, gpf_stroke, stroke_mat_index, angle, thickness, offset, matrix, use_seams);
gpencil_generate_edgeloops(ob_eval,
gpd,
gpf_stroke,
stroke_mat_index,
angle,
thickness,
offset,
matrix,
use_seams,
use_vgroups);
/* Tag for recalculation */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);

View File

@ -316,7 +316,8 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op)
ob_eval->obmat,
frame_offset,
use_seams,
use_faces);
use_faces,
true);
/* Reproject all un-tagged created strokes. */
if (project_type != GP_REPROJECT_KEEP) {

View File

@ -2844,7 +2844,8 @@ static int object_convert_exec(bContext *C, wmOperator *op)
matrix,
0,
use_seams,
use_faces);
use_faces,
true);
/* Remove unused materials. */
int actcol = ob_gpencil->actcol;