tornavis/source/blender/editors/transform/transform_convert_mesh_skin.c

300 lines
9.2 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup edtransform
*/
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BKE_context.h"
#include "BKE_crazyspace.h"
#include "BKE_editmesh.h"
#include "BKE_modifier.h"
#include "BKE_scene.h"
#include "ED_mesh.h"
#include "DEG_depsgraph_query.h"
#include "transform.h"
#include "transform_orientations.h"
#include "transform_convert.h"
/* -------------------------------------------------------------------- */
/** \name Edit Mesh #CD_MVERT_SKIN Transform Creation
* \{ */
static float *tc_mesh_skin_transdata_center(const struct TransIslandData *island_data,
const int island_index,
BMVert *eve)
{
if (island_data->center && island_index != -1) {
return island_data->center[island_index];
}
return eve->co;
}
static void tc_mesh_skin_transdata_create(TransDataBasic *td,
BMEditMesh *em,
BMVert *eve,
const struct TransIslandData *island_data,
const int island_index)
{
BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0);
MVertSkin *vs = CustomData_bmesh_get(&em->bm->vdata, eve->head.data, CD_MVERT_SKIN);
td->flag = 0;
if (vs) {
copy_v3_v3(td->iloc, vs->radius);
td->loc = vs->radius;
}
else {
td->flag |= TD_SKIP;
}
if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
td->flag |= TD_SELECTED;
}
copy_v3_v3(td->center, tc_mesh_skin_transdata_center(island_data, island_index, eve));
td->extra = eve;
}
static void createTransMeshSkin(bContext *UNUSED(C), TransInfo *t)
{
BLI_assert(t->mode == TFM_SKIN_RESIZE);
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
Mesh *me = tc->obedit->data;
BMesh *bm = em->bm;
BMVert *eve;
BMIter iter;
float mtx[3][3], smtx[3][3];
int a;
const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0;
struct TransIslandData island_data = {NULL};
struct TransMirrorData mirror_data = {NULL};
struct TransMeshDataCrazySpace crazyspace_data = {NULL};
/**
* Quick check if we can transform.
*
* \note ignore modes here, even in edge/face modes,
* transform data is created by selected vertices.
*/
if (!CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN)) {
continue;
}
/* Support other objects using proportional editing to adjust these, unless connected is
* enabled. */
if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) {
continue;
}
int data_len = 0;
if (prop_mode) {
BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
data_len++;
}
}
}
else {
data_len = bm->totvertsel;
}
if (data_len == 0) {
continue;
}
const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS);
if (is_island_center) {
/* In this specific case, near-by vertices will need to know
* the island of the nearest connected vertex. */
const bool calc_single_islands = ((prop_mode & T_PROP_CONNECTED) &&
(t->around == V3D_AROUND_LOCAL_ORIGINS) &&
(em->selectmode & SCE_SELECT_VERTEX));
const bool calc_island_center = false;
const bool calc_island_axismtx = false;
transform_convert_mesh_islands_calc(
em, calc_single_islands, calc_island_center, calc_island_axismtx, &island_data);
}
copy_m3_m4(mtx, tc->obedit->object_to_world);
/* we use a pseudo-inverse so that when one of the axes is scaled to 0,
* matrix inversion still works and we can still moving along the other */
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
/* Original index of our connected vertex when connected distances are calculated.
* Optional, allocate if needed. */
int *dists_index = NULL;
float *dists = NULL;
if (prop_mode & T_PROP_CONNECTED) {
dists = MEM_mallocN(bm->totvert * sizeof(float), __func__);
if (is_island_center) {
dists_index = MEM_mallocN(bm->totvert * sizeof(int), __func__);
}
transform_convert_mesh_connectivity_distance(em->bm, mtx, dists, dists_index);
}
/* Create TransDataMirror. */
if (tc->use_mirror_axis_any) {
bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
bool use_select = (t->flag & T_PROP_EDIT) == 0;
const bool mirror_axis[3] = {
tc->use_mirror_axis_x, tc->use_mirror_axis_y, tc->use_mirror_axis_z};
transform_convert_mesh_mirrordata_calc(
em, use_select, use_topology, mirror_axis, &mirror_data);
if (mirror_data.vert_map) {
tc->data_mirror_len = mirror_data.mirror_elem_len;
tc->data_mirror = MEM_callocN(mirror_data.mirror_elem_len * sizeof(*tc->data_mirror),
__func__);
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
if (mirror_data.vert_map[a].index != -1) {
data_len--;
}
}
}
}
}
/* Detect CrazySpace [tm]. */
transform_convert_mesh_crazyspace_detect(t, tc, em, &crazyspace_data);
/* Create TransData. */
BLI_assert(data_len >= 1);
tc->data_len = data_len;
tc->data = MEM_callocN(data_len * sizeof(TransData), "TransObData(Mesh EditMode)");
TransData *td = tc->data;
TransDataMirror *td_mirror = tc->data_mirror;
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
continue;
}
int island_index = -1;
if (island_data.island_vert_map) {
const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a;
island_index = island_data.island_vert_map[connected_index];
}
if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) {
tc_mesh_skin_transdata_create(
(TransDataBasic *)td_mirror, em, eve, &island_data, island_index);
int elem_index = mirror_data.vert_map[a].index;
BMVert *v_src = BM_vert_at_index(bm, elem_index);
MVertSkin *vs = CustomData_bmesh_get(&em->bm->vdata, v_src->head.data, CD_MVERT_SKIN);
td_mirror->flag |= mirror_data.vert_map[a].flag;
td_mirror->loc_src = vs->radius;
td_mirror++;
}
else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
tc_mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index);
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
createSpaceNormal(td->axismtx, eve->no);
}
else {
/* Setting normals */
copy_v3_v3(td->axismtx[2], eve->no);
td->axismtx[0][0] = td->axismtx[0][1] = td->axismtx[0][2] = td->axismtx[1][0] =
td->axismtx[1][1] = td->axismtx[1][2] = 0.0f;
}
if (prop_mode) {
if (prop_mode & T_PROP_CONNECTED) {
td->dist = dists[a];
}
else {
td->flag |= TD_NOTCONNECTED;
td->dist = FLT_MAX;
}
}
/* CrazySpace */
transform_convert_mesh_crazyspace_transdata_set(
mtx,
smtx,
crazyspace_data.defmats ? crazyspace_data.defmats[a] : NULL,
crazyspace_data.quats && BM_elem_flag_test(eve, BM_ELEM_TAG) ?
crazyspace_data.quats[a] :
NULL,
td);
td++;
}
}
transform_convert_mesh_islanddata_free(&island_data);
transform_convert_mesh_mirrordata_free(&mirror_data);
transform_convert_mesh_crazyspace_free(&crazyspace_data);
if (dists) {
MEM_freeN(dists);
}
if (dists_index) {
MEM_freeN(dists_index);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Recalc Mesh Data
* \{ */
static void tc_mesh_skin_apply_to_mirror(TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
if (tc->use_mirror_axis_any) {
TransDataMirror *td_mirror = tc->data_mirror;
for (int i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
copy_v3_v3(td_mirror->loc, td_mirror->loc_src);
}
}
}
}
static void recalcData_mesh_skin(TransInfo *t)
{
bool is_canceling = t->state == TRANS_CANCEL;
/* mirror modifier clipping? */
if (!is_canceling) {
if (!(t->flag & T_NO_MIRROR)) {
tc_mesh_skin_apply_to_mirror(t);
}
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
BKE_editmesh_looptri_and_normals_calc(em);
}
}
/** \} */
TransConvertTypeInfo TransConvertType_MeshSkin = {
/*flags*/ (T_EDIT | T_POINTS),
/*createTransData*/ createTransMeshSkin,
/*recalcData*/ recalcData_mesh_skin,
/*special_aftertrans_update*/ NULL,
};