For some reason the Armature modifier in the Multi-Modifier mode interpreted the vertex group in a way essentially opposite to the regular mode. Moreover, this depended not on the Multi-Modifier checkbox, but on whether the mode was actually active. This fixes the flip and adds versioning code to patch old files. One difficulty is that whether the Multi-Modifier flag is valid can be different between the viewport and render. The versioning code assumes any modifier enabled in either to be active. This change is not forward compatible, so min version is also bumped. Differential Revision: https://developer.blender.org/D16787
691 lines
21 KiB
C
691 lines
21 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*
|
|
* Deform coordinates by a armature object (used by modifier).
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <float.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_task.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "DNA_armature_types.h"
|
|
#include "DNA_gpencil_types.h"
|
|
#include "DNA_lattice_types.h"
|
|
#include "DNA_listBase.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_object_types.h"
|
|
|
|
#include "BKE_action.h"
|
|
#include "BKE_armature.h"
|
|
#include "BKE_customdata.h"
|
|
#include "BKE_deform.h"
|
|
#include "BKE_editmesh.h"
|
|
#include "BKE_lattice.h"
|
|
#include "BKE_mesh.h"
|
|
|
|
#include "DEG_depsgraph_build.h"
|
|
|
|
#include "CLG_log.h"
|
|
|
|
static CLG_LogRef LOG = {"bke.armature_deform"};
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Armature Deform Internal Utilities
|
|
* \{ */
|
|
|
|
/* Add the effect of one bone or B-Bone segment to the accumulated result. */
|
|
static void pchan_deform_accumulate(const DualQuat *deform_dq,
|
|
const float deform_mat[4][4],
|
|
const float co_in[3],
|
|
float weight,
|
|
float co_accum[3],
|
|
DualQuat *dq_accum,
|
|
float mat_accum[3][3])
|
|
{
|
|
if (weight == 0.0f) {
|
|
return;
|
|
}
|
|
|
|
if (dq_accum) {
|
|
BLI_assert(!co_accum);
|
|
|
|
add_weighted_dq_dq(dq_accum, deform_dq, weight);
|
|
}
|
|
else {
|
|
float tmp[3];
|
|
mul_v3_m4v3(tmp, deform_mat, co_in);
|
|
|
|
sub_v3_v3(tmp, co_in);
|
|
madd_v3_v3fl(co_accum, tmp, weight);
|
|
|
|
if (mat_accum) {
|
|
float tmpmat[3][3];
|
|
copy_m3_m4(tmpmat, deform_mat);
|
|
|
|
madd_m3_m3m3fl(mat_accum, mat_accum, tmpmat, weight);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void b_bone_deform(const bPoseChannel *pchan,
|
|
const float co[3],
|
|
float weight,
|
|
float vec[3],
|
|
DualQuat *dq,
|
|
float defmat[3][3])
|
|
{
|
|
const DualQuat *quats = pchan->runtime.bbone_dual_quats;
|
|
const Mat4 *mats = pchan->runtime.bbone_deform_mats;
|
|
const float(*mat)[4] = mats[0].mat;
|
|
float blend, y;
|
|
int index;
|
|
|
|
/* Transform co to bone space and get its y component. */
|
|
y = mat[0][1] * co[0] + mat[1][1] * co[1] + mat[2][1] * co[2] + mat[3][1];
|
|
|
|
/* Calculate the indices of the 2 affecting b_bone segments. */
|
|
BKE_pchan_bbone_deform_segment_index(pchan, y / pchan->bone->length, &index, &blend);
|
|
|
|
pchan_deform_accumulate(
|
|
&quats[index], mats[index + 1].mat, co, weight * (1.0f - blend), vec, dq, defmat);
|
|
pchan_deform_accumulate(
|
|
&quats[index + 1], mats[index + 2].mat, co, weight * blend, vec, dq, defmat);
|
|
}
|
|
|
|
float distfactor_to_bone(
|
|
const float vec[3], const float b1[3], const float b2[3], float rad1, float rad2, float rdist)
|
|
{
|
|
float dist_sq;
|
|
float bdelta[3];
|
|
float pdelta[3];
|
|
float hsqr, a, l, rad;
|
|
|
|
sub_v3_v3v3(bdelta, b2, b1);
|
|
l = normalize_v3(bdelta);
|
|
|
|
sub_v3_v3v3(pdelta, vec, b1);
|
|
|
|
a = dot_v3v3(bdelta, pdelta);
|
|
hsqr = len_squared_v3(pdelta);
|
|
|
|
if (a < 0.0f) {
|
|
/* If we're past the end of the bone, do a spherical field attenuation thing */
|
|
dist_sq = len_squared_v3v3(b1, vec);
|
|
rad = rad1;
|
|
}
|
|
else if (a > l) {
|
|
/* If we're past the end of the bone, do a spherical field attenuation thing */
|
|
dist_sq = len_squared_v3v3(b2, vec);
|
|
rad = rad2;
|
|
}
|
|
else {
|
|
dist_sq = (hsqr - (a * a));
|
|
|
|
if (l != 0.0f) {
|
|
rad = a / l;
|
|
rad = rad * rad2 + (1.0f - rad) * rad1;
|
|
}
|
|
else {
|
|
rad = rad1;
|
|
}
|
|
}
|
|
|
|
a = rad * rad;
|
|
if (dist_sq < a) {
|
|
return 1.0f;
|
|
}
|
|
|
|
l = rad + rdist;
|
|
l *= l;
|
|
if (rdist == 0.0f || dist_sq >= l) {
|
|
return 0.0f;
|
|
}
|
|
|
|
a = sqrtf(dist_sq) - rad;
|
|
return 1.0f - (a * a) / (rdist * rdist);
|
|
}
|
|
|
|
static float dist_bone_deform(
|
|
const bPoseChannel *pchan, float vec[3], DualQuat *dq, float mat[3][3], const float co[3])
|
|
{
|
|
const Bone *bone = pchan->bone;
|
|
float fac, contrib = 0.0;
|
|
|
|
if (bone == NULL) {
|
|
return 0.0f;
|
|
}
|
|
|
|
fac = distfactor_to_bone(
|
|
co, bone->arm_head, bone->arm_tail, bone->rad_head, bone->rad_tail, bone->dist);
|
|
|
|
if (fac > 0.0f) {
|
|
fac *= bone->weight;
|
|
contrib = fac;
|
|
if (contrib > 0.0f) {
|
|
if (bone->segments > 1 && pchan->runtime.bbone_segments == bone->segments) {
|
|
b_bone_deform(pchan, co, fac, vec, dq, mat);
|
|
}
|
|
else {
|
|
pchan_deform_accumulate(
|
|
&pchan->runtime.deform_dual_quat, pchan->chan_mat, co, fac, vec, dq, mat);
|
|
}
|
|
}
|
|
}
|
|
|
|
return contrib;
|
|
}
|
|
|
|
static void pchan_bone_deform(const bPoseChannel *pchan,
|
|
float weight,
|
|
float vec[3],
|
|
DualQuat *dq,
|
|
float mat[3][3],
|
|
const float co[3],
|
|
float *contrib)
|
|
{
|
|
const Bone *bone = pchan->bone;
|
|
|
|
if (!weight) {
|
|
return;
|
|
}
|
|
|
|
if (bone->segments > 1 && pchan->runtime.bbone_segments == bone->segments) {
|
|
b_bone_deform(pchan, co, weight, vec, dq, mat);
|
|
}
|
|
else {
|
|
pchan_deform_accumulate(
|
|
&pchan->runtime.deform_dual_quat, pchan->chan_mat, co, weight, vec, dq, mat);
|
|
}
|
|
|
|
(*contrib) += weight;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Armature Deform #BKE_armature_deform_coords API
|
|
*
|
|
* #BKE_armature_deform_coords and related functions.
|
|
* \{ */
|
|
|
|
typedef struct ArmatureUserdata {
|
|
const Object *ob_arm;
|
|
const Mesh *me_target;
|
|
float (*vert_coords)[3];
|
|
float (*vert_deform_mats)[3][3];
|
|
float (*vert_coords_prev)[3];
|
|
|
|
bool use_envelope;
|
|
bool use_quaternion;
|
|
bool invert_vgroup;
|
|
bool use_dverts;
|
|
|
|
int armature_def_nr;
|
|
|
|
const MDeformVert *dverts;
|
|
int dverts_len;
|
|
|
|
bPoseChannel **pchan_from_defbase;
|
|
int defbase_len;
|
|
|
|
float premat[4][4];
|
|
float postmat[4][4];
|
|
|
|
/** Specific data types. */
|
|
struct {
|
|
int cd_dvert_offset;
|
|
} bmesh;
|
|
} ArmatureUserdata;
|
|
|
|
static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
|
|
const int i,
|
|
const MDeformVert *dvert)
|
|
{
|
|
float(*const vert_coords)[3] = data->vert_coords;
|
|
float(*const vert_deform_mats)[3][3] = data->vert_deform_mats;
|
|
float(*const vert_coords_prev)[3] = data->vert_coords_prev;
|
|
const bool use_envelope = data->use_envelope;
|
|
const bool use_quaternion = data->use_quaternion;
|
|
const bool use_dverts = data->use_dverts;
|
|
const int armature_def_nr = data->armature_def_nr;
|
|
|
|
DualQuat sumdq, *dq = NULL;
|
|
const bPoseChannel *pchan;
|
|
float *co, dco[3];
|
|
float sumvec[3], summat[3][3];
|
|
float *vec = NULL, (*smat)[3] = NULL;
|
|
float contrib = 0.0f;
|
|
float armature_weight = 1.0f; /* default to 1 if no overall def group */
|
|
float prevco_weight = 0.0f; /* weight for optional cached vertexcos */
|
|
|
|
if (use_quaternion) {
|
|
memset(&sumdq, 0, sizeof(DualQuat));
|
|
dq = &sumdq;
|
|
}
|
|
else {
|
|
zero_v3(sumvec);
|
|
vec = sumvec;
|
|
|
|
if (vert_deform_mats) {
|
|
zero_m3(summat);
|
|
smat = summat;
|
|
}
|
|
}
|
|
|
|
if (armature_def_nr != -1 && dvert) {
|
|
armature_weight = BKE_defvert_find_weight(dvert, armature_def_nr);
|
|
|
|
if (data->invert_vgroup) {
|
|
armature_weight = 1.0f - armature_weight;
|
|
}
|
|
|
|
/* hackish: the blending factor can be used for blending with vert_coords_prev too */
|
|
if (vert_coords_prev) {
|
|
/* This weight specifies the contribution from the coordinates at the start of this
|
|
* modifier evaluation, while armature_weight is normally the opposite of that. */
|
|
prevco_weight = 1.0f - armature_weight;
|
|
armature_weight = 1.0f;
|
|
}
|
|
}
|
|
|
|
/* check if there's any point in calculating for this vert */
|
|
if (vert_coords_prev) {
|
|
if (prevco_weight == 1.0f) {
|
|
return;
|
|
}
|
|
|
|
/* get the coord we work on */
|
|
co = vert_coords_prev[i];
|
|
}
|
|
else {
|
|
if (armature_weight == 0.0f) {
|
|
return;
|
|
}
|
|
|
|
/* get the coord we work on */
|
|
co = vert_coords[i];
|
|
}
|
|
|
|
/* Apply the object's matrix */
|
|
mul_m4_v3(data->premat, co);
|
|
|
|
if (use_dverts && dvert && dvert->totweight) { /* use weight groups ? */
|
|
const MDeformWeight *dw = dvert->dw;
|
|
int deformed = 0;
|
|
uint j;
|
|
for (j = dvert->totweight; j != 0; j--, dw++) {
|
|
const uint index = dw->def_nr;
|
|
if (index < data->defbase_len && (pchan = data->pchan_from_defbase[index])) {
|
|
float weight = dw->weight;
|
|
const Bone *bone = pchan->bone;
|
|
|
|
deformed = 1;
|
|
|
|
if (bone && bone->flag & BONE_MULT_VG_ENV) {
|
|
weight *= distfactor_to_bone(
|
|
co, bone->arm_head, bone->arm_tail, bone->rad_head, bone->rad_tail, bone->dist);
|
|
}
|
|
|
|
pchan_bone_deform(pchan, weight, vec, dq, smat, co, &contrib);
|
|
}
|
|
}
|
|
/* If there are vertex-groups but not groups with bones (like for soft-body groups). */
|
|
if (deformed == 0 && use_envelope) {
|
|
for (pchan = data->ob_arm->pose->chanbase.first; pchan; pchan = pchan->next) {
|
|
if (!(pchan->bone->flag & BONE_NO_DEFORM)) {
|
|
contrib += dist_bone_deform(pchan, vec, dq, smat, co);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (use_envelope) {
|
|
for (pchan = data->ob_arm->pose->chanbase.first; pchan; pchan = pchan->next) {
|
|
if (!(pchan->bone->flag & BONE_NO_DEFORM)) {
|
|
contrib += dist_bone_deform(pchan, vec, dq, smat, co);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* actually should be EPSILON? weight values and contrib can be like 10e-39 small */
|
|
if (contrib > 0.0001f) {
|
|
if (use_quaternion) {
|
|
normalize_dq(dq, contrib);
|
|
|
|
if (armature_weight != 1.0f) {
|
|
copy_v3_v3(dco, co);
|
|
mul_v3m3_dq(dco, (vert_deform_mats) ? summat : NULL, dq);
|
|
sub_v3_v3(dco, co);
|
|
mul_v3_fl(dco, armature_weight);
|
|
add_v3_v3(co, dco);
|
|
}
|
|
else {
|
|
mul_v3m3_dq(co, (vert_deform_mats) ? summat : NULL, dq);
|
|
}
|
|
|
|
smat = summat;
|
|
}
|
|
else {
|
|
mul_v3_fl(vec, armature_weight / contrib);
|
|
add_v3_v3v3(co, vec, co);
|
|
}
|
|
|
|
if (vert_deform_mats) {
|
|
float pre[3][3], post[3][3], tmpmat[3][3];
|
|
|
|
copy_m3_m4(pre, data->premat);
|
|
copy_m3_m4(post, data->postmat);
|
|
copy_m3_m3(tmpmat, vert_deform_mats[i]);
|
|
|
|
if (!use_quaternion) { /* quaternion already is scale corrected */
|
|
mul_m3_fl(smat, armature_weight / contrib);
|
|
}
|
|
|
|
mul_m3_series(vert_deform_mats[i], post, smat, pre, tmpmat);
|
|
}
|
|
}
|
|
|
|
/* always, check above code */
|
|
mul_m4_v3(data->postmat, co);
|
|
|
|
/* interpolate with previous modifier position using weight group */
|
|
if (vert_coords_prev) {
|
|
float mw = 1.0f - prevco_weight;
|
|
vert_coords[i][0] = prevco_weight * vert_coords[i][0] + mw * co[0];
|
|
vert_coords[i][1] = prevco_weight * vert_coords[i][1] + mw * co[1];
|
|
vert_coords[i][2] = prevco_weight * vert_coords[i][2] + mw * co[2];
|
|
}
|
|
}
|
|
|
|
static void armature_vert_task(void *__restrict userdata,
|
|
const int i,
|
|
const TaskParallelTLS *__restrict UNUSED(tls))
|
|
{
|
|
const ArmatureUserdata *data = userdata;
|
|
const MDeformVert *dvert;
|
|
if (data->use_dverts || data->armature_def_nr != -1) {
|
|
if (data->me_target) {
|
|
BLI_assert(i < data->me_target->totvert);
|
|
if (data->dverts != NULL) {
|
|
dvert = data->dverts + i;
|
|
}
|
|
else {
|
|
dvert = NULL;
|
|
}
|
|
}
|
|
else if (data->dverts && i < data->dverts_len) {
|
|
dvert = data->dverts + i;
|
|
}
|
|
else {
|
|
dvert = NULL;
|
|
}
|
|
}
|
|
else {
|
|
dvert = NULL;
|
|
}
|
|
|
|
armature_vert_task_with_dvert(data, i, dvert);
|
|
}
|
|
|
|
static void armature_vert_task_editmesh(void *__restrict userdata,
|
|
MempoolIterData *iter,
|
|
const TaskParallelTLS *__restrict UNUSED(tls))
|
|
{
|
|
const ArmatureUserdata *data = userdata;
|
|
BMVert *v = (BMVert *)iter;
|
|
const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(v, data->bmesh.cd_dvert_offset);
|
|
armature_vert_task_with_dvert(data, BM_elem_index_get(v), dvert);
|
|
}
|
|
|
|
static void armature_vert_task_editmesh_no_dvert(void *__restrict userdata,
|
|
MempoolIterData *iter,
|
|
const TaskParallelTLS *__restrict UNUSED(tls))
|
|
{
|
|
const ArmatureUserdata *data = userdata;
|
|
BMVert *v = (BMVert *)iter;
|
|
armature_vert_task_with_dvert(data, BM_elem_index_get(v), NULL);
|
|
}
|
|
|
|
static void armature_deform_coords_impl(const Object *ob_arm,
|
|
const Object *ob_target,
|
|
float (*vert_coords)[3],
|
|
float (*vert_deform_mats)[3][3],
|
|
const int vert_coords_len,
|
|
const int deformflag,
|
|
float (*vert_coords_prev)[3],
|
|
const char *defgrp_name,
|
|
const Mesh *me_target,
|
|
BMEditMesh *em_target,
|
|
bGPDstroke *gps_target)
|
|
{
|
|
const bArmature *arm = ob_arm->data;
|
|
bPoseChannel **pchan_from_defbase = NULL;
|
|
const MDeformVert *dverts = NULL;
|
|
const bool use_envelope = (deformflag & ARM_DEF_ENVELOPE) != 0;
|
|
const bool use_quaternion = (deformflag & ARM_DEF_QUATERNION) != 0;
|
|
const bool invert_vgroup = (deformflag & ARM_DEF_INVERT_VGROUP) != 0;
|
|
int defbase_len = 0; /* safety for vertexgroup index overflow */
|
|
int dverts_len = 0; /* safety for vertexgroup overflow */
|
|
bool use_dverts = false;
|
|
int armature_def_nr = -1;
|
|
int cd_dvert_offset = -1;
|
|
|
|
/* in editmode, or not an armature */
|
|
if (arm->edbo || (ob_arm->pose == NULL)) {
|
|
return;
|
|
}
|
|
|
|
if ((ob_arm->pose->flag & POSE_RECALC) != 0) {
|
|
CLOG_ERROR(&LOG,
|
|
"Trying to evaluate influence of armature '%s' which needs Pose recalc!",
|
|
ob_arm->id.name);
|
|
BLI_assert(0);
|
|
}
|
|
|
|
if (BKE_object_supports_vertex_groups(ob_target)) {
|
|
const ID *target_data_id = NULL;
|
|
if (ob_target->type == OB_MESH) {
|
|
target_data_id = me_target == NULL ? (const ID *)ob_target->data : &me_target->id;
|
|
if (em_target == NULL) {
|
|
const Mesh *me = (const Mesh *)target_data_id;
|
|
dverts = BKE_mesh_deform_verts(me);
|
|
if (dverts) {
|
|
dverts_len = me->totvert;
|
|
}
|
|
}
|
|
}
|
|
else if (ob_target->type == OB_LATTICE) {
|
|
const Lattice *lt = ob_target->data;
|
|
target_data_id = (const ID *)ob_target->data;
|
|
dverts = lt->dvert;
|
|
if (dverts) {
|
|
dverts_len = lt->pntsu * lt->pntsv * lt->pntsw;
|
|
}
|
|
}
|
|
else if (ob_target->type == OB_GPENCIL) {
|
|
target_data_id = (const ID *)ob_target->data;
|
|
dverts = gps_target->dvert;
|
|
if (dverts) {
|
|
dverts_len = gps_target->totpoints;
|
|
}
|
|
}
|
|
|
|
/* Collect the vertex group names from the evaluated data. */
|
|
armature_def_nr = BKE_id_defgroup_name_index(target_data_id, defgrp_name);
|
|
const ListBase *defbase = BKE_id_defgroup_list_get(target_data_id);
|
|
defbase_len = BLI_listbase_count(defbase);
|
|
|
|
/* get a vertex-deform-index to posechannel array */
|
|
if (deformflag & ARM_DEF_VGROUP) {
|
|
/* if we have a Mesh, only use dverts if it has them */
|
|
if (em_target) {
|
|
cd_dvert_offset = CustomData_get_offset(&em_target->bm->vdata, CD_MDEFORMVERT);
|
|
use_dverts = (cd_dvert_offset != -1);
|
|
}
|
|
else if (me_target) {
|
|
use_dverts = (BKE_mesh_deform_verts(me_target) != NULL);
|
|
}
|
|
else if (dverts) {
|
|
use_dverts = true;
|
|
}
|
|
|
|
if (use_dverts) {
|
|
pchan_from_defbase = MEM_callocN(sizeof(*pchan_from_defbase) * defbase_len, "defnrToBone");
|
|
/* TODO(sergey): Some considerations here:
|
|
*
|
|
* - Check whether keeping this consistent across frames gives speedup.
|
|
*/
|
|
int i;
|
|
LISTBASE_FOREACH_INDEX (bDeformGroup *, dg, defbase, i) {
|
|
pchan_from_defbase[i] = BKE_pose_channel_find_name(ob_arm->pose, dg->name);
|
|
/* exclude non-deforming bones */
|
|
if (pchan_from_defbase[i]) {
|
|
if (pchan_from_defbase[i]->bone->flag & BONE_NO_DEFORM) {
|
|
pchan_from_defbase[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ArmatureUserdata data = {
|
|
.ob_arm = ob_arm,
|
|
.me_target = me_target,
|
|
.vert_coords = vert_coords,
|
|
.vert_deform_mats = vert_deform_mats,
|
|
.vert_coords_prev = vert_coords_prev,
|
|
.use_envelope = use_envelope,
|
|
.use_quaternion = use_quaternion,
|
|
.invert_vgroup = invert_vgroup,
|
|
.use_dverts = use_dverts,
|
|
.armature_def_nr = armature_def_nr,
|
|
.dverts = dverts,
|
|
.dverts_len = dverts_len,
|
|
.pchan_from_defbase = pchan_from_defbase,
|
|
.defbase_len = defbase_len,
|
|
.bmesh =
|
|
{
|
|
.cd_dvert_offset = cd_dvert_offset,
|
|
},
|
|
};
|
|
|
|
float obinv[4][4];
|
|
invert_m4_m4(obinv, ob_target->object_to_world);
|
|
|
|
mul_m4_m4m4(data.postmat, obinv, ob_arm->object_to_world);
|
|
invert_m4_m4(data.premat, data.postmat);
|
|
|
|
if (em_target != NULL) {
|
|
/* While this could cause an extra loop over mesh data, in most cases this will
|
|
* have already been properly set. */
|
|
BM_mesh_elem_index_ensure(em_target->bm, BM_VERT);
|
|
|
|
TaskParallelSettings settings;
|
|
BLI_parallel_mempool_settings_defaults(&settings);
|
|
|
|
if (use_dverts) {
|
|
BLI_task_parallel_mempool(
|
|
em_target->bm->vpool, &data, armature_vert_task_editmesh, &settings);
|
|
}
|
|
else {
|
|
BLI_task_parallel_mempool(
|
|
em_target->bm->vpool, &data, armature_vert_task_editmesh_no_dvert, &settings);
|
|
}
|
|
}
|
|
else {
|
|
TaskParallelSettings settings;
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
settings.min_iter_per_thread = 32;
|
|
BLI_task_parallel_range(0, vert_coords_len, &data, armature_vert_task, &settings);
|
|
}
|
|
|
|
if (pchan_from_defbase) {
|
|
MEM_freeN(pchan_from_defbase);
|
|
}
|
|
}
|
|
|
|
void BKE_armature_deform_coords_with_gpencil_stroke(const Object *ob_arm,
|
|
const Object *ob_target,
|
|
float (*vert_coords)[3],
|
|
float (*vert_deform_mats)[3][3],
|
|
int vert_coords_len,
|
|
int deformflag,
|
|
float (*vert_coords_prev)[3],
|
|
const char *defgrp_name,
|
|
bGPDstroke *gps_target)
|
|
{
|
|
armature_deform_coords_impl(ob_arm,
|
|
ob_target,
|
|
vert_coords,
|
|
vert_deform_mats,
|
|
vert_coords_len,
|
|
deformflag,
|
|
vert_coords_prev,
|
|
defgrp_name,
|
|
NULL,
|
|
NULL,
|
|
gps_target);
|
|
}
|
|
|
|
void BKE_armature_deform_coords_with_mesh(const Object *ob_arm,
|
|
const Object *ob_target,
|
|
float (*vert_coords)[3],
|
|
float (*vert_deform_mats)[3][3],
|
|
int vert_coords_len,
|
|
int deformflag,
|
|
float (*vert_coords_prev)[3],
|
|
const char *defgrp_name,
|
|
const Mesh *me_target)
|
|
{
|
|
armature_deform_coords_impl(ob_arm,
|
|
ob_target,
|
|
vert_coords,
|
|
vert_deform_mats,
|
|
vert_coords_len,
|
|
deformflag,
|
|
vert_coords_prev,
|
|
defgrp_name,
|
|
me_target,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
void BKE_armature_deform_coords_with_editmesh(const Object *ob_arm,
|
|
const Object *ob_target,
|
|
float (*vert_coords)[3],
|
|
float (*vert_deform_mats)[3][3],
|
|
int vert_coords_len,
|
|
int deformflag,
|
|
float (*vert_coords_prev)[3],
|
|
const char *defgrp_name,
|
|
BMEditMesh *em_target)
|
|
{
|
|
armature_deform_coords_impl(ob_arm,
|
|
ob_target,
|
|
vert_coords,
|
|
vert_deform_mats,
|
|
vert_coords_len,
|
|
deformflag,
|
|
vert_coords_prev,
|
|
defgrp_name,
|
|
NULL,
|
|
em_target,
|
|
NULL);
|
|
}
|
|
|
|
/** \} */
|