2018-08-30 12:22:55 +02:00
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2018, Blender Foundation
|
|
|
|
* This is a new part of Blender
|
|
|
|
*/
|
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
* \ingroup edgpencil
|
2019-06-12 09:04:10 +10:00
|
|
|
*
|
|
|
|
* Operators for dealing with armatures and GP data-blocks.
|
2018-08-30 12:22:55 +02:00
|
|
|
*/
|
|
|
|
|
2020-03-19 09:33:03 +01:00
|
|
|
#include <math.h>
|
|
|
|
#include <stddef.h>
|
2018-08-30 12:22:55 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2020-03-19 09:33:03 +01:00
|
|
|
#include <string.h>
|
2018-08-30 12:22:55 +02:00
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "BLI_math.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_utildefines.h"
|
2018-08-30 12:22:55 +02:00
|
|
|
|
|
|
|
#include "DNA_armature_types.h"
|
|
|
|
#include "DNA_gpencil_types.h"
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
|
|
|
|
#include "BKE_action.h"
|
|
|
|
#include "BKE_armature.h"
|
|
|
|
#include "BKE_context.h"
|
|
|
|
#include "BKE_deform.h"
|
|
|
|
#include "BKE_gpencil.h"
|
|
|
|
#include "BKE_gpencil_modifier.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BKE_main.h"
|
2018-08-30 12:22:55 +02:00
|
|
|
#include "BKE_object_deform.h"
|
|
|
|
#include "BKE_report.h"
|
|
|
|
|
|
|
|
#include "WM_api.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
|
|
|
#include "RNA_access.h"
|
|
|
|
#include "RNA_define.h"
|
|
|
|
#include "RNA_enum_types.h"
|
|
|
|
|
|
|
|
#include "ED_gpencil.h"
|
|
|
|
#include "ED_mesh.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "ED_object.h"
|
2018-08-30 12:22:55 +02:00
|
|
|
|
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
#include "DEG_depsgraph_query.h"
|
|
|
|
|
|
|
|
#include "gpencil_intern.h"
|
|
|
|
|
|
|
|
enum {
|
|
|
|
GP_ARMATURE_NAME = 0,
|
2019-01-15 23:57:49 +11:00
|
|
|
GP_ARMATURE_AUTO = 1,
|
2018-08-30 12:22:55 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
#define DEFAULT_RATIO 0.10f
|
|
|
|
#define DEFAULT_DECAY 0.8f
|
|
|
|
|
|
|
|
static int gpencil_bone_looper(Object *ob,
|
2018-08-30 20:47:46 +10:00
|
|
|
Bone *bone,
|
|
|
|
void *data,
|
|
|
|
int (*bone_func)(Object *, Bone *, void *))
|
2018-08-30 12:22:55 +02:00
|
|
|
{
|
|
|
|
/* We want to apply the function bone_func to every bone
|
|
|
|
* in an armature -- feed bone_looper the first bone and
|
|
|
|
* a pointer to the bone_func and watch it go!. The int count
|
|
|
|
* can be useful for counting bones with a certain property
|
|
|
|
* (e.g. skinnable)
|
|
|
|
*/
|
|
|
|
int count = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
if (bone) {
|
|
|
|
/* only do bone_func if the bone is non null */
|
|
|
|
count += bone_func(ob, bone, data);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* try to execute bone_func for the first child */
|
|
|
|
count += gpencil_bone_looper(ob, bone->childbase.first, data, bone_func);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* try to execute bone_func for the next bone at this
|
|
|
|
* depth of the recursion.
|
|
|
|
*/
|
|
|
|
count += gpencil_bone_looper(ob, bone->next, data, bone_func);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gpencil_bone_skinnable_cb(Object *UNUSED(ob), Bone *bone, void *datap)
|
|
|
|
{
|
|
|
|
/* Bones that are deforming
|
|
|
|
* are regarded to be "skinnable" and are eligible for
|
|
|
|
* auto-skinning.
|
|
|
|
*
|
|
|
|
* This function performs 2 functions:
|
|
|
|
*
|
|
|
|
* a) It returns 1 if the bone is skinnable.
|
|
|
|
* If we loop over all bones with this
|
|
|
|
* function, we can count the number of
|
|
|
|
* skinnable bones.
|
|
|
|
* b) If the pointer data is non null,
|
|
|
|
* it is treated like a handle to a
|
|
|
|
* bone pointer -- the bone pointer
|
|
|
|
* is set to point at this bone, and
|
|
|
|
* the pointer the handle points to
|
|
|
|
* is incremented to point to the
|
|
|
|
* next member of an array of pointers
|
|
|
|
* to bones. This way we can loop using
|
|
|
|
* this function to construct an array of
|
|
|
|
* pointers to bones that point to all
|
|
|
|
* skinnable bones.
|
|
|
|
*/
|
|
|
|
Bone ***hbone;
|
|
|
|
int a, segments;
|
|
|
|
struct {
|
|
|
|
Object *armob;
|
|
|
|
void *list;
|
|
|
|
int heat;
|
|
|
|
} *data = datap;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
if (!(bone->flag & BONE_HIDDEN_P)) {
|
|
|
|
if (!(bone->flag & BONE_NO_DEFORM)) {
|
|
|
|
if (data->heat && data->armob->pose &&
|
2018-09-02 16:45:00 +10:00
|
|
|
BKE_pose_channel_find_name(data->armob->pose, bone->name)) {
|
2018-08-30 12:22:55 +02:00
|
|
|
segments = bone->segments;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
segments = 1;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
if (data->list != NULL) {
|
|
|
|
hbone = (Bone ***)&data->list;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
for (a = 0; a < segments; a++) {
|
|
|
|
**hbone = bone;
|
2018-09-02 16:45:00 +10:00
|
|
|
(*hbone)++;
|
2018-08-30 12:22:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return segments;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vgroup_add_unique_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr))
|
|
|
|
{
|
|
|
|
/* This group creates a vertex group to ob that has the
|
|
|
|
* same name as bone (provided the bone is skinnable).
|
|
|
|
* If such a vertex group already exist the routine exits.
|
|
|
|
*/
|
|
|
|
if (!(bone->flag & BONE_NO_DEFORM)) {
|
2020-03-06 12:50:56 +11:00
|
|
|
if (!BKE_object_defgroup_find_name(ob, bone->name)) {
|
2018-08-30 12:22:55 +02:00
|
|
|
BKE_object_defgroup_add_name(ob, bone->name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dgroup_skinnable_cb(Object *ob, Bone *bone, void *datap)
|
|
|
|
{
|
|
|
|
/* Bones that are deforming
|
|
|
|
* are regarded to be "skinnable" and are eligible for
|
|
|
|
* auto-skinning.
|
|
|
|
*
|
|
|
|
* This function performs 2 functions:
|
|
|
|
*
|
|
|
|
* a) If the bone is skinnable, it creates
|
|
|
|
* a vertex group for ob that has
|
|
|
|
* the name of the skinnable bone
|
|
|
|
* (if one doesn't exist already).
|
|
|
|
* b) If the pointer data is non null,
|
|
|
|
* it is treated like a handle to a
|
|
|
|
* bDeformGroup pointer -- the
|
|
|
|
* bDeformGroup pointer is set to point
|
|
|
|
* to the deform group with the bone's
|
|
|
|
* name, and the pointer the handle
|
|
|
|
* points to is incremented to point to the
|
|
|
|
* next member of an array of pointers
|
|
|
|
* to bDeformGroups. This way we can loop using
|
|
|
|
* this function to construct an array of
|
|
|
|
* pointers to bDeformGroups, all with names
|
|
|
|
* of skinnable bones.
|
|
|
|
*/
|
|
|
|
bDeformGroup ***hgroup, *defgroup = NULL;
|
|
|
|
int a, segments;
|
|
|
|
struct {
|
|
|
|
Object *armob;
|
|
|
|
void *list;
|
|
|
|
int heat;
|
|
|
|
} *data = datap;
|
|
|
|
bArmature *arm = data->armob->data;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
if (!(bone->flag & BONE_HIDDEN_P)) {
|
|
|
|
if (!(bone->flag & BONE_NO_DEFORM)) {
|
|
|
|
if (data->heat && data->armob->pose &&
|
2018-09-02 16:45:00 +10:00
|
|
|
BKE_pose_channel_find_name(data->armob->pose, bone->name)) {
|
2018-08-30 12:22:55 +02:00
|
|
|
segments = bone->segments;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
segments = 1;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
if (arm->layer & bone->layer) {
|
2020-03-06 12:50:56 +11:00
|
|
|
if (!(defgroup = BKE_object_defgroup_find_name(ob, bone->name))) {
|
2018-08-30 12:22:55 +02:00
|
|
|
defgroup = BKE_object_defgroup_add_name(ob, bone->name);
|
|
|
|
}
|
|
|
|
else if (defgroup->flag & DG_LOCK_WEIGHT) {
|
|
|
|
/* In case vgroup already exists and is locked, do not modify it here. See T43814. */
|
|
|
|
defgroup = NULL;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
if (data->list != NULL) {
|
|
|
|
hgroup = (bDeformGroup ***)&data->list;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
for (a = 0; a < segments; a++) {
|
|
|
|
**hgroup = defgroup;
|
2018-09-02 16:45:00 +10:00
|
|
|
(*hgroup)++;
|
2018-08-30 12:22:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return segments;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get weight value depending of distance and decay value */
|
|
|
|
static float get_weight(float dist, float decay_rad, float dif_rad)
|
|
|
|
{
|
|
|
|
float weight = 1.0f;
|
|
|
|
if (dist < decay_rad) {
|
|
|
|
weight = 1.0f;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
weight = interpf(0.0f, 0.9f, (dist - decay_rad) / dif_rad);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
return weight;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This functions implements the automatic computation of vertex group weights */
|
|
|
|
static void gpencil_add_verts_to_dgroups(
|
2018-08-30 20:41:40 +10:00
|
|
|
const bContext *C, Object *ob, Object *ob_arm, const float ratio, const float decay)
|
2018-08-30 12:22:55 +02:00
|
|
|
{
|
|
|
|
bArmature *arm = ob_arm->data;
|
|
|
|
Bone **bonelist, *bone;
|
|
|
|
bDeformGroup **dgrouplist;
|
|
|
|
bPoseChannel *pchan;
|
|
|
|
bGPdata *gpd = (bGPdata *)ob->data;
|
2018-09-25 20:23:55 +02:00
|
|
|
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
Mat4 bbone_array[MAX_BBONE_SUBDIV], *bbone = NULL;
|
|
|
|
float(*root)[3], (*tip)[3], (*verts)[3];
|
|
|
|
float *radsqr;
|
|
|
|
int *selected;
|
|
|
|
float weight;
|
|
|
|
int numbones, i, j, segments = 0;
|
|
|
|
struct {
|
|
|
|
Object *armob;
|
|
|
|
void *list;
|
|
|
|
int heat;
|
|
|
|
} looper_data;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
looper_data.armob = ob_arm;
|
|
|
|
looper_data.heat = true;
|
|
|
|
looper_data.list = NULL;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* count the number of skinnable bones */
|
|
|
|
numbones = gpencil_bone_looper(ob, arm->bonebase.first, &looper_data, gpencil_bone_skinnable_cb);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (numbones == 0) {
|
2018-08-30 12:22:55 +02:00
|
|
|
return;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* create an array of pointer to bones that are skinnable
|
|
|
|
* and fill it with all of the skinnable bones */
|
|
|
|
bonelist = MEM_callocN(numbones * sizeof(Bone *), "bonelist");
|
|
|
|
looper_data.list = bonelist;
|
|
|
|
gpencil_bone_looper(ob, arm->bonebase.first, &looper_data, gpencil_bone_skinnable_cb);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* create an array of pointers to the deform groups that
|
|
|
|
* correspond to the skinnable bones (creating them
|
|
|
|
* as necessary. */
|
|
|
|
dgrouplist = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgrouplist");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
looper_data.list = dgrouplist;
|
|
|
|
gpencil_bone_looper(ob, arm->bonebase.first, &looper_data, dgroup_skinnable_cb);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* create an array of root and tip positions transformed into
|
|
|
|
* global coords */
|
2020-08-08 13:29:21 +10:00
|
|
|
root = MEM_callocN(sizeof(float[3]) * numbones, "root");
|
|
|
|
tip = MEM_callocN(sizeof(float[3]) * numbones, "tip");
|
|
|
|
selected = MEM_callocN(sizeof(int) * numbones, "selected");
|
|
|
|
radsqr = MEM_callocN(sizeof(float) * numbones, "radsqr");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
for (j = 0; j < numbones; j++) {
|
|
|
|
bone = bonelist[j];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* handle bbone */
|
|
|
|
if (segments == 0) {
|
|
|
|
segments = 1;
|
|
|
|
bbone = NULL;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-09-02 16:45:00 +10:00
|
|
|
if ((ob_arm->pose) && (pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name))) {
|
2018-08-30 12:22:55 +02:00
|
|
|
if (bone->segments > 1) {
|
|
|
|
segments = bone->segments;
|
2019-04-05 18:40:59 +03:00
|
|
|
BKE_pchan_bbone_spline_setup(pchan, true, false, bbone_array);
|
2018-08-30 12:22:55 +02:00
|
|
|
bbone = bbone_array;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
segments--;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* compute root and tip */
|
|
|
|
if (bbone) {
|
|
|
|
mul_v3_m4v3(root[j], bone->arm_mat, bbone[segments].mat[3]);
|
|
|
|
if ((segments + 1) < bone->segments) {
|
|
|
|
mul_v3_m4v3(tip[j], bone->arm_mat, bbone[segments + 1].mat[3]);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
copy_v3_v3(tip[j], bone->arm_tail);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2018-08-30 12:22:55 +02:00
|
|
|
else {
|
|
|
|
copy_v3_v3(root[j], bone->arm_head);
|
|
|
|
copy_v3_v3(tip[j], bone->arm_tail);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
mul_m4_v3(ob_arm->obmat, root[j]);
|
|
|
|
mul_m4_v3(ob_arm->obmat, tip[j]);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
selected[j] = 1;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* calculate radius squared */
|
|
|
|
radsqr[j] = len_squared_v3v3(root[j], tip[j]) * ratio;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* loop all strokes */
|
2020-03-09 16:27:24 +01:00
|
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
2019-06-07 16:07:32 +02:00
|
|
|
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
|
2018-08-30 12:22:55 +02:00
|
|
|
bGPDspoint *pt = NULL;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
|
2018-09-02 16:45:00 +10:00
|
|
|
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (gpf == NULL) {
|
2018-08-30 12:22:55 +02:00
|
|
|
continue;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-09 16:27:24 +01:00
|
|
|
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
|
2018-08-30 12:22:55 +02:00
|
|
|
/* skip strokes that are invalid for current view */
|
2019-04-22 09:19:45 +10:00
|
|
|
if (ED_gpencil_stroke_can_use(C, gps) == false) {
|
2018-08-30 12:22:55 +02:00
|
|
|
continue;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
BKE_gpencil_dvert_ensure(gps);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* create verts array */
|
|
|
|
verts = MEM_callocN(gps->totpoints * sizeof(*verts), __func__);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* transform stroke points to global space */
|
|
|
|
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
|
|
|
copy_v3_v3(verts[i], &pt->x);
|
|
|
|
mul_m4_v3(ob->obmat, verts[i]);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* loop groups and assign weight */
|
|
|
|
for (j = 0; j < numbones; j++) {
|
|
|
|
int def_nr = BLI_findindex(&ob->defbase, dgrouplist[j]);
|
|
|
|
if (def_nr < 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
float decay_rad = radsqr[j] - (radsqr[j] * decay);
|
|
|
|
float dif_rad = radsqr[j] - decay_rad;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
|
|
|
MDeformVert *dvert = &gps->dvert[i];
|
|
|
|
float dist = dist_squared_to_line_segment_v3(verts[i], root[j], tip[j]);
|
|
|
|
if (dist > radsqr[j]) {
|
|
|
|
/* if not in cylinder, check if inside extreme spheres */
|
|
|
|
weight = 0.0f;
|
|
|
|
dist = len_squared_v3v3(root[j], verts[i]);
|
|
|
|
if (dist < radsqr[j]) {
|
|
|
|
weight = get_weight(dist, decay_rad, dif_rad);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
dist = len_squared_v3v3(tip[j], verts[i]);
|
|
|
|
if (dist < radsqr[j]) {
|
|
|
|
weight = get_weight(dist, decay_rad, dif_rad);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* inside bone cylinder */
|
|
|
|
weight = get_weight(dist, decay_rad, dif_rad);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* assign weight */
|
2020-03-06 12:50:56 +11:00
|
|
|
MDeformWeight *dw = BKE_defvert_ensure_index(dvert, def_nr);
|
2018-08-30 12:22:55 +02:00
|
|
|
if (dw) {
|
|
|
|
dw->weight = weight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
MEM_SAFE_FREE(verts);
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* if not multiedit, exit loop*/
|
|
|
|
if (!is_multiedit) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* free the memory allocated */
|
|
|
|
MEM_SAFE_FREE(bonelist);
|
|
|
|
MEM_SAFE_FREE(dgrouplist);
|
|
|
|
MEM_SAFE_FREE(root);
|
|
|
|
MEM_SAFE_FREE(tip);
|
|
|
|
MEM_SAFE_FREE(radsqr);
|
|
|
|
MEM_SAFE_FREE(selected);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gpencil_object_vgroup_calc_from_armature(const bContext *C,
|
2018-08-30 20:47:46 +10:00
|
|
|
Object *ob,
|
|
|
|
Object *ob_arm,
|
|
|
|
const int mode,
|
|
|
|
const float ratio,
|
|
|
|
const float decay)
|
2018-08-30 12:22:55 +02:00
|
|
|
{
|
|
|
|
/* Lets try to create some vertex groups
|
|
|
|
* based on the bones of the parent armature.
|
|
|
|
*/
|
|
|
|
bArmature *arm = ob_arm->data;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* always create groups */
|
|
|
|
const int defbase_tot = BLI_listbase_count(&ob->defbase);
|
|
|
|
int defbase_add;
|
|
|
|
/* Traverse the bone list, trying to create empty vertex
|
2018-09-02 18:28:27 +10:00
|
|
|
* groups corresponding to the bone.
|
|
|
|
*/
|
2018-09-02 16:45:00 +10:00
|
|
|
defbase_add = gpencil_bone_looper(ob, arm->bonebase.first, NULL, vgroup_add_unique_bone_cb);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
if (defbase_add) {
|
|
|
|
/* its possible there are DWeight's outside the range of the current
|
2018-09-02 18:28:27 +10:00
|
|
|
* objects deform groups, in this case the new groups wont be empty */
|
2018-08-30 12:22:55 +02:00
|
|
|
ED_vgroup_data_clamp_range(ob->data, defbase_tot);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
if (mode == GP_ARMATURE_AUTO) {
|
|
|
|
/* Traverse the bone list, trying to fill vertex groups
|
|
|
|
* with the corresponding vertice weights for which the
|
|
|
|
* bone is closest.
|
|
|
|
*/
|
2018-08-30 20:47:46 +10:00
|
|
|
gpencil_add_verts_to_dgroups(C, ob, ob_arm, ratio, decay);
|
2018-08-30 12:22:55 +02:00
|
|
|
}
|
2019-04-24 16:24:53 +03:00
|
|
|
|
|
|
|
DEG_relations_tag_update(CTX_data_main(C));
|
2018-08-30 12:22:55 +02:00
|
|
|
}
|
|
|
|
|
2020-05-05 13:53:47 +02:00
|
|
|
bool ED_gpencil_add_armature(const bContext *C, ReportList *reports, Object *ob, Object *ob_arm)
|
2018-08-30 12:22:55 +02:00
|
|
|
{
|
|
|
|
Main *bmain = CTX_data_main(C);
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-12-15 09:36:08 +01:00
|
|
|
if (ob == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* if no armature modifier, add a new one */
|
2020-05-08 10:32:30 +02:00
|
|
|
GpencilModifierData *md = BKE_gpencil_modifiers_findby_type(ob, eGpencilModifierType_Armature);
|
2018-08-30 12:22:55 +02:00
|
|
|
if (md == NULL) {
|
2018-09-02 16:45:00 +10:00
|
|
|
md = ED_object_gpencil_modifier_add(
|
|
|
|
reports, bmain, scene, ob, "Armature", eGpencilModifierType_Armature);
|
2018-08-30 12:22:55 +02:00
|
|
|
if (md == NULL) {
|
2018-09-02 16:45:00 +10:00
|
|
|
BKE_report(reports, RPT_ERROR, "Unable to add a new Armature modifier to object");
|
2018-08-30 12:22:55 +02:00
|
|
|
return false;
|
|
|
|
}
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
|
2018-08-30 12:22:55 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* verify armature */
|
|
|
|
ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)md;
|
|
|
|
if (mmd->object == NULL) {
|
|
|
|
mmd->object = ob_arm;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (ob_arm != mmd->object) {
|
|
|
|
BKE_report(reports,
|
|
|
|
RPT_ERROR,
|
2018-09-02 16:45:00 +10:00
|
|
|
"The existing Armature modifier is already using a different Armature object");
|
2018-08-30 12:22:55 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-05-05 13:53:47 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ED_gpencil_add_armature_weights(
|
|
|
|
const bContext *C, ReportList *reports, Object *ob, Object *ob_arm, int mode)
|
|
|
|
{
|
|
|
|
if (ob == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool success = ED_gpencil_add_armature(C, reports, ob, ob_arm);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* add weights */
|
2020-05-05 13:53:47 +02:00
|
|
|
if (success) {
|
|
|
|
gpencil_object_vgroup_calc_from_armature(C, ob, ob_arm, mode, DEFAULT_RATIO, DEFAULT_DECAY);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-05-05 13:53:47 +02:00
|
|
|
return success;
|
2018-08-30 12:22:55 +02:00
|
|
|
}
|
|
|
|
/* ***************** Generate armature weights ************************** */
|
2018-08-30 20:47:46 +10:00
|
|
|
static bool gpencil_generate_weights_poll(bContext *C)
|
2018-08-30 12:22:55 +02:00
|
|
|
{
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
2018-09-03 16:05:57 +02:00
|
|
|
|
2018-12-14 20:35:55 -02:00
|
|
|
if (ob == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-09-03 16:05:57 +02:00
|
|
|
if (ob->type != OB_GPENCIL) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
|
|
bGPdata *gpd = (bGPdata *)ob->data;
|
|
|
|
|
|
|
|
if (BLI_listbase_count(&gpd->layers) == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* need some armature in the view layer */
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
|
2018-08-30 12:22:55 +02:00
|
|
|
if (base->object->type == OB_ARMATURE) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gpencil_generate_weights_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
2019-07-25 16:36:22 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
2018-08-30 12:22:55 +02:00
|
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
|
|
|
|
bGPdata *gpd = (bGPdata *)ob->data;
|
|
|
|
Object *ob_arm = NULL;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
const int mode = RNA_enum_get(op->ptr, "mode");
|
|
|
|
const float ratio = RNA_float_get(op->ptr, "ratio");
|
|
|
|
const float decay = RNA_float_get(op->ptr, "decay");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* sanity checks */
|
2019-04-22 09:19:45 +10:00
|
|
|
if (ELEM(NULL, ob, gpd)) {
|
2018-08-30 12:22:55 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* get armature */
|
|
|
|
const int arm_idx = RNA_enum_get(op->ptr, "armature");
|
|
|
|
if (arm_idx > 0) {
|
|
|
|
Base *base = BLI_findlink(&view_layer->object_bases, arm_idx - 1);
|
|
|
|
ob_arm = base->object;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* get armature from modifier */
|
2020-05-08 10:32:30 +02:00
|
|
|
GpencilModifierData *md = BKE_gpencil_modifiers_findby_type(ob_eval,
|
2020-05-08 19:02:03 +10:00
|
|
|
eGpencilModifierType_Armature);
|
2018-08-30 12:22:55 +02:00
|
|
|
if (md == NULL) {
|
2019-03-15 08:53:22 +11:00
|
|
|
BKE_report(op->reports, RPT_ERROR, "The grease pencil object need an Armature modifier");
|
2018-08-30 12:22:55 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)md;
|
|
|
|
if (mmd->object == NULL) {
|
2019-03-15 08:53:22 +11:00
|
|
|
BKE_report(op->reports, RPT_ERROR, "Armature modifier is not valid or wrong defined");
|
2018-08-30 12:22:55 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
ob_arm = mmd->object;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
if (ob_arm == NULL) {
|
2018-09-02 16:45:00 +10:00
|
|
|
BKE_report(op->reports, RPT_ERROR, "No Armature object in the view layer");
|
2018-08-30 12:22:55 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 20:41:40 +10:00
|
|
|
gpencil_object_vgroup_calc_from_armature(C, ob, ob_arm, mode, ratio, decay);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* notifiers */
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
|
2018-08-30 12:22:55 +02:00
|
|
|
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dynamically populate an enum of Armatures */
|
2018-08-30 20:47:46 +10:00
|
|
|
static const EnumPropertyItem *gpencil_armatures_enum_itemf(bContext *C,
|
|
|
|
PointerRNA *UNUSED(ptr),
|
|
|
|
PropertyRNA *UNUSED(prop),
|
|
|
|
bool *r_free)
|
2018-08-30 12:22:55 +02:00
|
|
|
{
|
|
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
|
|
EnumPropertyItem *item = NULL, item_tmp = {0};
|
|
|
|
int totitem = 0;
|
|
|
|
int i = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
if (C == NULL) {
|
|
|
|
return DummyRNA_DEFAULT_items;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* add default */
|
|
|
|
item_tmp.identifier = "DEFAULT";
|
|
|
|
item_tmp.name = "Default";
|
|
|
|
item_tmp.value = 0;
|
|
|
|
RNA_enum_item_add(&item, &totitem, &item_tmp);
|
|
|
|
i++;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
|
2018-08-30 12:22:55 +02:00
|
|
|
Object *ob = base->object;
|
|
|
|
if (ob->type == OB_ARMATURE) {
|
|
|
|
item_tmp.identifier = item_tmp.name = ob->id.name + 2;
|
|
|
|
item_tmp.value = i;
|
|
|
|
RNA_enum_item_add(&item, &totitem, &item_tmp);
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
RNA_enum_item_end(&item, &totitem);
|
|
|
|
*r_free = true;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPENCIL_OT_generate_weights(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
static const EnumPropertyItem mode_type[] = {
|
2018-08-30 20:41:40 +10:00
|
|
|
{GP_ARMATURE_NAME, "NAME", 0, "Empty Groups", ""},
|
|
|
|
{GP_ARMATURE_AUTO, "AUTO", 0, "Automatic Weights", ""},
|
2019-02-03 14:01:45 +11:00
|
|
|
{0, NULL, 0, NULL, NULL},
|
2018-08-30 12:22:55 +02:00
|
|
|
};
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
PropertyRNA *prop;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* identifiers */
|
|
|
|
ot->name = "Generate Automatic Weights";
|
|
|
|
ot->idname = "GPENCIL_OT_generate_weights";
|
|
|
|
ot->description = "Generate automatic weights for armatures (requires armature modifier)";
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* callbacks */
|
|
|
|
ot->exec = gpencil_generate_weights_exec;
|
|
|
|
ot->poll = gpencil_generate_weights_poll;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
ot->prop = RNA_def_enum(ot->srna, "mode", mode_type, 0, "Mode", "");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
prop = RNA_def_enum(
|
|
|
|
ot->srna, "armature", DummyRNA_DEFAULT_items, 0, "Armature", "Armature to use");
|
|
|
|
RNA_def_enum_funcs(prop, gpencil_armatures_enum_itemf);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 20:41:40 +10:00
|
|
|
RNA_def_float(ot->srna,
|
|
|
|
"ratio",
|
|
|
|
DEFAULT_RATIO,
|
|
|
|
0.0f,
|
|
|
|
2.0f,
|
|
|
|
"Ratio",
|
|
|
|
"Ratio between bone length and influence radius",
|
|
|
|
0.001f,
|
|
|
|
1.0f);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-30 20:41:40 +10:00
|
|
|
RNA_def_float(ot->srna,
|
|
|
|
"decay",
|
|
|
|
DEFAULT_DECAY,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
"Decay",
|
|
|
|
"Factor to reduce influence depending of distance to bone axis",
|
|
|
|
0.0f,
|
|
|
|
1.0f);
|
2018-08-30 12:22:55 +02:00
|
|
|
}
|