1222 lines
42 KiB
C++
1222 lines
42 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup modifiers
|
|
*/
|
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BLI_bitmap.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_utildefines_stack.h"
|
|
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_object_types.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BKE_deform.h"
|
|
#include "BKE_mesh.hh"
|
|
#include "BKE_particle.h"
|
|
|
|
#include "MOD_modifiertypes.h"
|
|
#include "MOD_solidify_util.hh" /* own include */
|
|
#include "MOD_util.h"
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name High Quality Normal Calculation Function
|
|
* \{ */
|
|
|
|
/* skip shell thickness for non-manifold edges, see #35710. */
|
|
#define USE_NONMANIFOLD_WORKAROUND
|
|
|
|
/* *** derived mesh high quality normal calculation function *** */
|
|
/* could be exposed for other functions to use */
|
|
|
|
struct EdgeFaceRef {
|
|
int p1; /* init as -1 */
|
|
int p2;
|
|
};
|
|
|
|
BLI_INLINE bool edgeref_is_init(const EdgeFaceRef *edge_ref)
|
|
{
|
|
return !((edge_ref->p1 == 0) && (edge_ref->p2 == 0));
|
|
}
|
|
|
|
/**
|
|
* \param mesh: Mesh to calculate normals for.
|
|
* \param poly_normals: Precalculated face normals.
|
|
* \param r_vert_nors: Return vert normals.
|
|
*/
|
|
static void mesh_calc_hq_normal(Mesh *mesh,
|
|
const blender::Span<blender::float3> poly_normals,
|
|
float (*r_vert_nors)[3],
|
|
#ifdef USE_NONMANIFOLD_WORKAROUND
|
|
BLI_bitmap *edge_tmp_tag
|
|
#endif
|
|
)
|
|
{
|
|
const int verts_num = mesh->totvert;
|
|
const blender::Span<MEdge> edges = mesh->edges();
|
|
const blender::Span<MPoly> polys = mesh->polys();
|
|
const blender::Span<int> corner_edges = mesh->corner_edges();
|
|
|
|
{
|
|
EdgeFaceRef *edge_ref_array = MEM_cnew_array<EdgeFaceRef>(size_t(edges.size()), __func__);
|
|
EdgeFaceRef *edge_ref;
|
|
float edge_normal[3];
|
|
|
|
/* Add an edge reference if it's not there, pointing back to the face index. */
|
|
for (const int i : polys.index_range()) {
|
|
int j;
|
|
|
|
for (j = 0; j < polys[i].totloop; j++) {
|
|
const int edge_i = corner_edges[polys[i].loopstart + j];
|
|
|
|
/* --- add edge ref to face --- */
|
|
edge_ref = &edge_ref_array[edge_i];
|
|
if (!edgeref_is_init(edge_ref)) {
|
|
edge_ref->p1 = i;
|
|
edge_ref->p2 = -1;
|
|
}
|
|
else if ((edge_ref->p1 != -1) && (edge_ref->p2 == -1)) {
|
|
edge_ref->p2 = i;
|
|
}
|
|
else {
|
|
/* 3+ faces using an edge, we can't handle this usefully */
|
|
edge_ref->p1 = edge_ref->p2 = -1;
|
|
#ifdef USE_NONMANIFOLD_WORKAROUND
|
|
BLI_BITMAP_ENABLE(edge_tmp_tag, edge_i);
|
|
#endif
|
|
}
|
|
/* --- done --- */
|
|
}
|
|
}
|
|
|
|
int i;
|
|
const MEdge *edge;
|
|
for (i = 0, edge = edges.data(), edge_ref = edge_ref_array; i < edges.size();
|
|
i++, edge++, edge_ref++) {
|
|
/* Get the edge vert indices, and edge value (the face indices that use it) */
|
|
|
|
if (edgeref_is_init(edge_ref) && (edge_ref->p1 != -1)) {
|
|
if (edge_ref->p2 != -1) {
|
|
/* We have 2 faces using this edge, calculate the edges normal
|
|
* using the angle between the 2 faces as a weighting */
|
|
#if 0
|
|
add_v3_v3v3(edge_normal, face_nors[edge_ref->f1], face_nors[edge_ref->f2]);
|
|
normalize_v3_length(
|
|
edge_normal,
|
|
angle_normalized_v3v3(face_nors[edge_ref->f1], face_nors[edge_ref->f2]));
|
|
#else
|
|
mid_v3_v3v3_angle_weighted(
|
|
edge_normal, poly_normals[edge_ref->p1], poly_normals[edge_ref->p2]);
|
|
#endif
|
|
}
|
|
else {
|
|
/* only one face attached to that edge */
|
|
/* an edge without another attached- the weight on this is undefined */
|
|
copy_v3_v3(edge_normal, poly_normals[edge_ref->p1]);
|
|
}
|
|
add_v3_v3(r_vert_nors[edge->v1], edge_normal);
|
|
add_v3_v3(r_vert_nors[edge->v2], edge_normal);
|
|
}
|
|
}
|
|
MEM_freeN(edge_ref_array);
|
|
}
|
|
|
|
/* normalize vertex normals and assign */
|
|
const blender::Span<blender::float3> vert_normals = mesh->vert_normals();
|
|
for (int i = 0; i < verts_num; i++) {
|
|
if (normalize_v3(r_vert_nors[i]) == 0.0f) {
|
|
copy_v3_v3(r_vert_nors[i], vert_normals[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Main Solidify Function
|
|
* \{ */
|
|
|
|
/* NOLINTNEXTLINE: readability-function-size */
|
|
Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
|
|
{
|
|
Mesh *result;
|
|
const SolidifyModifierData *smd = (SolidifyModifierData *)md;
|
|
|
|
const uint verts_num = uint(mesh->totvert);
|
|
const uint edges_num = uint(mesh->totedge);
|
|
const uint polys_num = uint(mesh->totpoly);
|
|
const uint loops_num = uint(mesh->totloop);
|
|
uint newLoops = 0, newPolys = 0, newEdges = 0, newVerts = 0, rimVerts = 0;
|
|
|
|
/* Only use material offsets if we have 2 or more materials. */
|
|
const short mat_nr_max = ctx->object->totcol > 1 ? ctx->object->totcol - 1 : 0;
|
|
const short mat_ofs = mat_nr_max ? smd->mat_ofs : 0;
|
|
const short mat_ofs_rim = mat_nr_max ? smd->mat_ofs_rim : 0;
|
|
|
|
/* use for edges */
|
|
/* over-alloc new_vert_arr, old_vert_arr */
|
|
uint *new_vert_arr = nullptr;
|
|
STACK_DECLARE(new_vert_arr);
|
|
|
|
uint *new_edge_arr = nullptr;
|
|
STACK_DECLARE(new_edge_arr);
|
|
|
|
uint *old_vert_arr = MEM_cnew_array<uint>(verts_num, "old_vert_arr in solidify");
|
|
|
|
uint *edge_users = nullptr;
|
|
int *edge_order = nullptr;
|
|
|
|
float(*vert_nors)[3] = nullptr;
|
|
blender::Span<blender::float3> poly_normals;
|
|
|
|
const bool need_poly_normals = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) ||
|
|
(smd->flag & MOD_SOLIDIFY_EVEN) ||
|
|
(smd->flag & MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP) ||
|
|
(smd->bevel_convex != 0);
|
|
|
|
const float ofs_orig = -(((-smd->offset_fac + 1.0f) * 0.5f) * smd->offset);
|
|
const float ofs_new = smd->offset + ofs_orig;
|
|
const float offset_fac_vg = smd->offset_fac_vg;
|
|
const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg;
|
|
const float bevel_convex = smd->bevel_convex;
|
|
const bool do_flip = (smd->flag & MOD_SOLIDIFY_FLIP) != 0;
|
|
const bool do_clamp = (smd->offset_clamp != 0.0f);
|
|
const bool do_angle_clamp = do_clamp && (smd->flag & MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP) != 0;
|
|
const bool do_bevel_convex = bevel_convex != 0.0f;
|
|
const bool do_rim = (smd->flag & MOD_SOLIDIFY_RIM) != 0;
|
|
const bool do_shell = !(do_rim && (smd->flag & MOD_SOLIDIFY_NOSHELL) != 0);
|
|
|
|
/* weights */
|
|
const MDeformVert *dvert;
|
|
const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0;
|
|
int defgrp_index;
|
|
const int shell_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->shell_defgrp_name);
|
|
const int rim_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->rim_defgrp_name);
|
|
|
|
/* array size is doubled in case of using a shell */
|
|
const uint stride = do_shell ? 2 : 1;
|
|
|
|
const blender::Span<blender::float3> vert_normals = mesh->vert_normals();
|
|
|
|
MOD_get_vgroup(ctx->object, mesh, smd->defgrp_name, &dvert, &defgrp_index);
|
|
|
|
const float(*orig_vert_positions)[3] = BKE_mesh_vert_positions(mesh);
|
|
const blender::Span<MEdge> orig_edges = mesh->edges();
|
|
const blender::Span<MPoly> orig_polys = mesh->polys();
|
|
const blender::Span<int> orig_corner_verts = mesh->corner_verts();
|
|
const blender::Span<int> orig_corner_edges = mesh->corner_edges();
|
|
|
|
if (need_poly_normals) {
|
|
/* calculate only face normals */
|
|
poly_normals = mesh->poly_normals();
|
|
}
|
|
|
|
STACK_INIT(new_vert_arr, verts_num * 2);
|
|
STACK_INIT(new_edge_arr, edges_num * 2);
|
|
|
|
if (do_rim) {
|
|
BLI_bitmap *orig_mvert_tag = BLI_BITMAP_NEW(verts_num, __func__);
|
|
uint eidx;
|
|
uint i;
|
|
|
|
#define INVALID_UNUSED uint(-1)
|
|
#define INVALID_PAIR uint(-2)
|
|
|
|
new_vert_arr = static_cast<uint *>(
|
|
MEM_malloc_arrayN(verts_num, 2 * sizeof(*new_vert_arr), __func__));
|
|
new_edge_arr = static_cast<uint *>(
|
|
MEM_malloc_arrayN(((edges_num * 2) + verts_num), sizeof(*new_edge_arr), __func__));
|
|
|
|
edge_users = static_cast<uint *>(MEM_malloc_arrayN(edges_num, sizeof(*edge_users), __func__));
|
|
edge_order = static_cast<int *>(MEM_malloc_arrayN(edges_num, sizeof(*edge_order), __func__));
|
|
|
|
/* save doing 2 loops here... */
|
|
#if 0
|
|
copy_vn_i(edge_users, edges_num, INVALID_UNUSED);
|
|
#endif
|
|
|
|
const MEdge *edge;
|
|
for (eidx = 0, edge = orig_edges.data(); eidx < edges_num; eidx++, edge++) {
|
|
edge_users[eidx] = INVALID_UNUSED;
|
|
}
|
|
|
|
for (const int64_t i : orig_polys.index_range()) {
|
|
const MPoly &poly = orig_polys[i];
|
|
int j;
|
|
|
|
int corner_i_prev = poly.loopstart + (poly.totloop - 1);
|
|
|
|
for (j = 0; j < poly.totloop; j++) {
|
|
const int corner_i = poly.loopstart + j;
|
|
const int vert_i = orig_corner_verts[corner_i];
|
|
const int prev_vert_i = orig_corner_verts[corner_i_prev];
|
|
/* add edge user */
|
|
eidx = int(orig_corner_edges[corner_i_prev]);
|
|
if (edge_users[eidx] == INVALID_UNUSED) {
|
|
edge = &orig_edges[eidx];
|
|
BLI_assert(ELEM(prev_vert_i, edge->v1, edge->v2) && ELEM(vert_i, edge->v1, edge->v2));
|
|
edge_users[eidx] = (prev_vert_i > vert_i) == (edge->v1 < edge->v2) ?
|
|
uint(i) :
|
|
(uint(i) + polys_num);
|
|
edge_order[eidx] = j;
|
|
}
|
|
else {
|
|
edge_users[eidx] = INVALID_PAIR;
|
|
}
|
|
corner_i_prev = corner_i;
|
|
}
|
|
}
|
|
|
|
for (eidx = 0, edge = orig_edges.data(); eidx < edges_num; eidx++, edge++) {
|
|
if (!ELEM(edge_users[eidx], INVALID_UNUSED, INVALID_PAIR)) {
|
|
BLI_BITMAP_ENABLE(orig_mvert_tag, edge->v1);
|
|
BLI_BITMAP_ENABLE(orig_mvert_tag, edge->v2);
|
|
STACK_PUSH(new_edge_arr, eidx);
|
|
newPolys++;
|
|
newLoops += 4;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < verts_num; i++) {
|
|
if (BLI_BITMAP_TEST(orig_mvert_tag, i)) {
|
|
old_vert_arr[i] = STACK_SIZE(new_vert_arr);
|
|
STACK_PUSH(new_vert_arr, i);
|
|
rimVerts++;
|
|
}
|
|
else {
|
|
old_vert_arr[i] = INVALID_UNUSED;
|
|
}
|
|
}
|
|
|
|
MEM_freeN(orig_mvert_tag);
|
|
}
|
|
|
|
if (do_shell == false) {
|
|
/* only add rim vertices */
|
|
newVerts = rimVerts;
|
|
/* each extruded face needs an opposite edge */
|
|
newEdges = newPolys;
|
|
}
|
|
else {
|
|
/* (stride == 2) in this case, so no need to add newVerts/newEdges */
|
|
BLI_assert(newVerts == 0);
|
|
BLI_assert(newEdges == 0);
|
|
}
|
|
|
|
#ifdef USE_NONMANIFOLD_WORKAROUND
|
|
BLI_bitmap *edge_tmp_tag = BLI_BITMAP_NEW(mesh->totedge, __func__);
|
|
#endif
|
|
|
|
if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) {
|
|
vert_nors = static_cast<float(*)[3]>(MEM_calloc_arrayN(verts_num, sizeof(float[3]), __func__));
|
|
mesh_calc_hq_normal(mesh,
|
|
poly_normals,
|
|
vert_nors
|
|
#ifdef USE_NONMANIFOLD_WORKAROUND
|
|
,
|
|
edge_tmp_tag
|
|
#endif
|
|
);
|
|
}
|
|
|
|
result = BKE_mesh_new_nomain_from_template(mesh,
|
|
int((verts_num * stride) + newVerts),
|
|
int((edges_num * stride) + newEdges + rimVerts),
|
|
int((loops_num * stride) + newLoops),
|
|
int((polys_num * stride) + newPolys));
|
|
|
|
float(*vert_positions)[3] = BKE_mesh_vert_positions_for_write(result);
|
|
blender::MutableSpan<MEdge> edges = result->edges_for_write();
|
|
blender::MutableSpan<MPoly> polys = result->polys_for_write();
|
|
blender::MutableSpan<int> corner_verts = result->corner_verts_for_write();
|
|
blender::MutableSpan<int> corner_edges = result->corner_edges_for_write();
|
|
|
|
if (do_shell) {
|
|
CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, int(verts_num));
|
|
CustomData_copy_data(&mesh->vdata, &result->vdata, 0, int(verts_num), int(verts_num));
|
|
|
|
CustomData_copy_data(&mesh->edata, &result->edata, 0, 0, int(edges_num));
|
|
CustomData_copy_data(&mesh->edata, &result->edata, 0, int(edges_num), int(edges_num));
|
|
|
|
CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, int(loops_num));
|
|
/* DO NOT copy here the 'copied' part of loop data, we want to reverse loops
|
|
* (so that winding of copied face get reversed, so that normals get reversed
|
|
* and point in expected direction...).
|
|
* If we also copy data here, then this data get overwritten
|
|
* (and allocated memory becomes a memory leak). */
|
|
|
|
CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, int(polys_num));
|
|
CustomData_copy_data(&mesh->pdata, &result->pdata, 0, int(polys_num), int(polys_num));
|
|
}
|
|
else {
|
|
int i, j;
|
|
CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, int(verts_num));
|
|
for (i = 0, j = int(verts_num); i < verts_num; i++) {
|
|
if (old_vert_arr[i] != INVALID_UNUSED) {
|
|
CustomData_copy_data(&mesh->vdata, &result->vdata, i, j, 1);
|
|
j++;
|
|
}
|
|
}
|
|
|
|
CustomData_copy_data(&mesh->edata, &result->edata, 0, 0, int(edges_num));
|
|
|
|
for (i = 0, j = int(edges_num); i < edges_num; i++) {
|
|
if (!ELEM(edge_users[i], INVALID_UNUSED, INVALID_PAIR)) {
|
|
MEdge *ed_src, *ed_dst;
|
|
CustomData_copy_data(&mesh->edata, &result->edata, i, j, 1);
|
|
|
|
ed_src = &edges[i];
|
|
ed_dst = &edges[j];
|
|
ed_dst->v1 = old_vert_arr[ed_src->v1] + verts_num;
|
|
ed_dst->v2 = old_vert_arr[ed_src->v2] + verts_num;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
/* will be created later */
|
|
CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, int(loops_num));
|
|
CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, int(polys_num));
|
|
}
|
|
|
|
float *result_edge_bweight = nullptr;
|
|
if (do_bevel_convex) {
|
|
result_edge_bweight = static_cast<float *>(
|
|
CustomData_add_layer(&result->edata, CD_BWEIGHT, CD_SET_DEFAULT, result->totedge));
|
|
}
|
|
|
|
/* Initializes: (`i_end`, `do_shell_align`, `vert_index`). */
|
|
#define INIT_VERT_ARRAY_OFFSETS(test) \
|
|
if (((ofs_new >= ofs_orig) == do_flip) == test) { \
|
|
i_end = verts_num; \
|
|
do_shell_align = true; \
|
|
vert_index = 0; \
|
|
} \
|
|
else { \
|
|
if (do_shell) { \
|
|
i_end = verts_num; \
|
|
do_shell_align = true; \
|
|
} \
|
|
else { \
|
|
i_end = newVerts; \
|
|
do_shell_align = false; \
|
|
} \
|
|
vert_index = verts_num; \
|
|
} \
|
|
(void)0
|
|
|
|
int *dst_material_index = BKE_mesh_material_indices_for_write(result);
|
|
|
|
/* flip normals */
|
|
|
|
if (do_shell) {
|
|
for (const int64_t i : blender::IndexRange(mesh->totpoly)) {
|
|
const int64_t poly_i = polys_num + i;
|
|
MPoly &poly = polys[poly_i];
|
|
const int loop_end = poly.totloop - 1;
|
|
int e;
|
|
int j;
|
|
|
|
/* reverses the loop direction (corner verts as well as custom-data)
|
|
* Corner edges also need to be corrected too, done in a separate loop below. */
|
|
const int corner_2 = poly.loopstart + mesh->totloop;
|
|
#if 0
|
|
for (j = 0; j < poly.totloop; j++) {
|
|
CustomData_copy_data(&mesh->ldata,
|
|
&result->ldata,
|
|
poly.loopstart + j,
|
|
poly.loopstart + (loop_end - j) + mesh->totloop,
|
|
1);
|
|
}
|
|
#else
|
|
/* slightly more involved, keep the first vertex the same for the copy,
|
|
* ensures the diagonals in the new face match the original. */
|
|
j = 0;
|
|
for (int j_prev = loop_end; j < poly.totloop; j_prev = j++) {
|
|
CustomData_copy_data(&mesh->ldata,
|
|
&result->ldata,
|
|
poly.loopstart + j,
|
|
poly.loopstart + (loop_end - j_prev) + mesh->totloop,
|
|
1);
|
|
}
|
|
#endif
|
|
|
|
if (mat_ofs) {
|
|
dst_material_index[poly_i] += mat_ofs;
|
|
CLAMP(dst_material_index[poly_i], 0, mat_nr_max);
|
|
}
|
|
|
|
e = corner_edges[corner_2 + 0];
|
|
for (j = 0; j < loop_end; j++) {
|
|
corner_edges[corner_2 + j] = corner_edges[corner_2 + j + 1];
|
|
}
|
|
corner_edges[corner_2 + loop_end] = e;
|
|
|
|
poly.loopstart += mesh->totloop;
|
|
|
|
for (j = 0; j < poly.totloop; j++) {
|
|
corner_verts[corner_2 + j] += verts_num;
|
|
corner_edges[corner_2 + j] += edges_num;
|
|
}
|
|
}
|
|
|
|
for (MEdge &edge : edges.slice(edges_num, edges_num)) {
|
|
edge.v1 += verts_num;
|
|
edge.v2 += verts_num;
|
|
}
|
|
}
|
|
|
|
/* NOTE: copied vertex layers don't have flipped normals yet. do this after applying offset. */
|
|
if ((smd->flag & MOD_SOLIDIFY_EVEN) == 0) {
|
|
/* no even thickness, very simple */
|
|
float ofs_new_vgroup;
|
|
|
|
/* for clamping */
|
|
float *vert_lens = nullptr;
|
|
float *vert_angs = nullptr;
|
|
const float offset = fabsf(smd->offset) * smd->offset_clamp;
|
|
const float offset_sq = offset * offset;
|
|
|
|
/* for bevel weight */
|
|
float *edge_angs = nullptr;
|
|
|
|
if (do_clamp) {
|
|
vert_lens = static_cast<float *>(MEM_malloc_arrayN(verts_num, sizeof(float), "vert_lens"));
|
|
copy_vn_fl(vert_lens, int(verts_num), FLT_MAX);
|
|
for (uint i = 0; i < edges_num; i++) {
|
|
const float ed_len_sq = len_squared_v3v3(vert_positions[edges[i].v1],
|
|
vert_positions[edges[i].v2]);
|
|
vert_lens[edges[i].v1] = min_ff(vert_lens[edges[i].v1], ed_len_sq);
|
|
vert_lens[edges[i].v2] = min_ff(vert_lens[edges[i].v2], ed_len_sq);
|
|
}
|
|
}
|
|
|
|
if (do_angle_clamp || do_bevel_convex) {
|
|
uint eidx;
|
|
if (do_angle_clamp) {
|
|
vert_angs = static_cast<float *>(MEM_malloc_arrayN(verts_num, sizeof(float), "vert_angs"));
|
|
copy_vn_fl(vert_angs, int(verts_num), 0.5f * M_PI);
|
|
}
|
|
if (do_bevel_convex) {
|
|
edge_angs = static_cast<float *>(MEM_malloc_arrayN(edges_num, sizeof(float), "edge_angs"));
|
|
if (!do_rim) {
|
|
edge_users = static_cast<uint *>(
|
|
MEM_malloc_arrayN(edges_num, sizeof(*edge_users), "solid_mod edges"));
|
|
}
|
|
}
|
|
uint(*edge_user_pairs)[2] = static_cast<uint(*)[2]>(
|
|
MEM_malloc_arrayN(edges_num, sizeof(*edge_user_pairs), "edge_user_pairs"));
|
|
for (eidx = 0; eidx < edges_num; eidx++) {
|
|
edge_user_pairs[eidx][0] = INVALID_UNUSED;
|
|
edge_user_pairs[eidx][1] = INVALID_UNUSED;
|
|
}
|
|
for (const int64_t i : orig_polys.index_range()) {
|
|
const MPoly &poly = orig_polys[i];
|
|
int prev_corner_i = poly.loopstart + poly.totloop - 1;
|
|
for (int j = 0; j < poly.totloop; j++) {
|
|
const int corner_i = poly.loopstart + j;
|
|
const int vert_i = orig_corner_verts[corner_i];
|
|
const int prev_vert_i = orig_corner_verts[prev_corner_i];
|
|
/* add edge user */
|
|
eidx = orig_corner_edges[prev_corner_i];
|
|
const MEdge *ed = &orig_edges[eidx];
|
|
BLI_assert(ELEM(prev_vert_i, ed->v1, ed->v2) && ELEM(vert_i, ed->v1, ed->v2));
|
|
char flip = char((prev_vert_i > vert_i) == (ed->v1 < ed->v2));
|
|
if (edge_user_pairs[eidx][flip] == INVALID_UNUSED) {
|
|
edge_user_pairs[eidx][flip] = uint(i);
|
|
}
|
|
else {
|
|
edge_user_pairs[eidx][0] = INVALID_PAIR;
|
|
edge_user_pairs[eidx][1] = INVALID_PAIR;
|
|
}
|
|
prev_corner_i = corner_i;
|
|
}
|
|
}
|
|
const MEdge *edge = orig_edges.data();
|
|
float e[3];
|
|
for (uint i = 0; i < edges_num; i++, edge++) {
|
|
if (!ELEM(edge_user_pairs[i][0], INVALID_UNUSED, INVALID_PAIR) &&
|
|
!ELEM(edge_user_pairs[i][1], INVALID_UNUSED, INVALID_PAIR)) {
|
|
const float *n0 = poly_normals[edge_user_pairs[i][0]];
|
|
const float *n1 = poly_normals[edge_user_pairs[i][1]];
|
|
sub_v3_v3v3(e, orig_vert_positions[edge->v1], orig_vert_positions[edge->v2]);
|
|
normalize_v3(e);
|
|
const float angle = angle_signed_on_axis_v3v3_v3(n0, n1, e);
|
|
if (do_angle_clamp) {
|
|
vert_angs[edge->v1] = max_ff(vert_angs[edge->v1], angle);
|
|
vert_angs[edge->v2] = max_ff(vert_angs[edge->v2], angle);
|
|
}
|
|
if (do_bevel_convex) {
|
|
edge_angs[i] = angle;
|
|
if (!do_rim) {
|
|
edge_users[i] = INVALID_PAIR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
MEM_freeN(edge_user_pairs);
|
|
}
|
|
|
|
if (ofs_new != 0.0f) {
|
|
uint i_orig, i_end;
|
|
bool do_shell_align;
|
|
|
|
ofs_new_vgroup = ofs_new;
|
|
|
|
uint vert_index;
|
|
INIT_VERT_ARRAY_OFFSETS(false);
|
|
|
|
for (i_orig = 0; i_orig < i_end; i_orig++, vert_index++) {
|
|
const uint i = do_shell_align ? i_orig : new_vert_arr[i_orig];
|
|
if (dvert) {
|
|
const MDeformVert *dv = &dvert[i];
|
|
if (defgrp_invert) {
|
|
ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
|
|
}
|
|
else {
|
|
ofs_new_vgroup = BKE_defvert_find_weight(dv, defgrp_index);
|
|
}
|
|
ofs_new_vgroup = (offset_fac_vg + (ofs_new_vgroup * offset_fac_vg_inv)) * ofs_new;
|
|
}
|
|
if (do_clamp && offset > FLT_EPSILON) {
|
|
/* always reset because we may have set before */
|
|
if (dvert == nullptr) {
|
|
ofs_new_vgroup = ofs_new;
|
|
}
|
|
if (do_angle_clamp) {
|
|
float cos_ang = cosf(((2 * M_PI) - vert_angs[i]) * 0.5f);
|
|
if (cos_ang > 0) {
|
|
float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang;
|
|
if (max_off < offset * 0.5f) {
|
|
ofs_new_vgroup *= max_off / offset * 2;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (vert_lens[i] < offset_sq) {
|
|
float scalar = sqrtf(vert_lens[i]) / offset;
|
|
ofs_new_vgroup *= scalar;
|
|
}
|
|
}
|
|
}
|
|
if (vert_nors) {
|
|
madd_v3_v3fl(vert_positions[vert_index], vert_nors[i], ofs_new_vgroup);
|
|
}
|
|
else {
|
|
madd_v3_v3fl(vert_positions[vert_index], vert_normals[i], ofs_new_vgroup);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ofs_orig != 0.0f) {
|
|
uint i_orig, i_end;
|
|
bool do_shell_align;
|
|
|
|
ofs_new_vgroup = ofs_orig;
|
|
|
|
/* as above but swapped */
|
|
uint vert_index;
|
|
INIT_VERT_ARRAY_OFFSETS(true);
|
|
|
|
for (i_orig = 0; i_orig < i_end; i_orig++, vert_index++) {
|
|
const uint i = do_shell_align ? i_orig : new_vert_arr[i_orig];
|
|
if (dvert) {
|
|
const MDeformVert *dv = &dvert[i];
|
|
if (defgrp_invert) {
|
|
ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
|
|
}
|
|
else {
|
|
ofs_new_vgroup = BKE_defvert_find_weight(dv, defgrp_index);
|
|
}
|
|
ofs_new_vgroup = (offset_fac_vg + (ofs_new_vgroup * offset_fac_vg_inv)) * ofs_orig;
|
|
}
|
|
if (do_clamp && offset > FLT_EPSILON) {
|
|
/* always reset because we may have set before */
|
|
if (dvert == nullptr) {
|
|
ofs_new_vgroup = ofs_orig;
|
|
}
|
|
if (do_angle_clamp) {
|
|
float cos_ang = cosf(vert_angs[i_orig] * 0.5f);
|
|
if (cos_ang > 0) {
|
|
float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang;
|
|
if (max_off < offset * 0.5f) {
|
|
ofs_new_vgroup *= max_off / offset * 2;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (vert_lens[i] < offset_sq) {
|
|
float scalar = sqrtf(vert_lens[i]) / offset;
|
|
ofs_new_vgroup *= scalar;
|
|
}
|
|
}
|
|
}
|
|
if (vert_nors) {
|
|
madd_v3_v3fl(vert_positions[vert_index], vert_nors[i], ofs_new_vgroup);
|
|
}
|
|
else {
|
|
madd_v3_v3fl(vert_positions[vert_index], vert_normals[i], ofs_new_vgroup);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (do_bevel_convex) {
|
|
for (uint i = 0; i < edges_num; i++) {
|
|
if (edge_users[i] == INVALID_PAIR) {
|
|
float angle = edge_angs[i];
|
|
result_edge_bweight[i] = clamp_f(result_edge_bweight[i] +
|
|
(angle < M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) :
|
|
clamp_f(bevel_convex, -1.0f, 0.0f)),
|
|
0.0f,
|
|
1.0f);
|
|
if (do_shell) {
|
|
result_edge_bweight[i + edges_num] = clamp_f(
|
|
result_edge_bweight[i + edges_num] + (angle > M_PI ?
|
|
clamp_f(bevel_convex, 0.0f, 1.0f) :
|
|
clamp_f(bevel_convex, -1.0f, 0.0f)),
|
|
0,
|
|
1.0f);
|
|
}
|
|
}
|
|
}
|
|
if (!do_rim) {
|
|
MEM_freeN(edge_users);
|
|
}
|
|
MEM_freeN(edge_angs);
|
|
}
|
|
|
|
if (do_clamp) {
|
|
MEM_freeN(vert_lens);
|
|
if (do_angle_clamp) {
|
|
MEM_freeN(vert_angs);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
#ifdef USE_NONMANIFOLD_WORKAROUND
|
|
const bool check_non_manifold = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) != 0;
|
|
#endif
|
|
/* same as EM_solidify() in editmesh_lib.c */
|
|
float *vert_angles = static_cast<float *>(
|
|
MEM_calloc_arrayN(verts_num, sizeof(float[2]), "mod_solid_pair")); /* 2 in 1 */
|
|
float *vert_accum = vert_angles + verts_num;
|
|
uint vidx;
|
|
uint i;
|
|
|
|
if (vert_nors == nullptr) {
|
|
vert_nors = static_cast<float(*)[3]>(
|
|
MEM_malloc_arrayN(verts_num, sizeof(float[3]), "mod_solid_vno"));
|
|
for (i = 0; i < verts_num; i++) {
|
|
copy_v3_v3(vert_nors[i], vert_normals[i]);
|
|
}
|
|
}
|
|
|
|
for (const int64_t i : blender::IndexRange(polys_num)) {
|
|
/* #bke::mesh::poly_angles_calc logic is inlined here */
|
|
float nor_prev[3];
|
|
float nor_next[3];
|
|
|
|
int i_curr = polys[i].totloop - 1;
|
|
int i_next = 0;
|
|
|
|
const int *poly_verts = &corner_verts[polys[i].loopstart];
|
|
const int *poly_edges = &corner_edges[polys[i].loopstart];
|
|
|
|
sub_v3_v3v3(
|
|
nor_prev, vert_positions[poly_verts[i_curr - 1]], vert_positions[poly_verts[i_curr]]);
|
|
normalize_v3(nor_prev);
|
|
|
|
while (i_next < polys[i].totloop) {
|
|
float angle;
|
|
sub_v3_v3v3(
|
|
nor_next, vert_positions[poly_verts[i_curr]], vert_positions[poly_verts[i_next]]);
|
|
normalize_v3(nor_next);
|
|
angle = angle_normalized_v3v3(nor_prev, nor_next);
|
|
|
|
/* --- not related to angle calc --- */
|
|
if (angle < FLT_EPSILON) {
|
|
angle = FLT_EPSILON;
|
|
}
|
|
|
|
vidx = poly_verts[i_curr];
|
|
vert_accum[vidx] += angle;
|
|
|
|
#ifdef USE_NONMANIFOLD_WORKAROUND
|
|
/* skip 3+ face user edges */
|
|
if ((check_non_manifold == false) ||
|
|
LIKELY(!BLI_BITMAP_TEST(edge_tmp_tag, poly_edges[i_curr]) &&
|
|
!BLI_BITMAP_TEST(edge_tmp_tag, poly_edges[i_next]))) {
|
|
vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], poly_normals[i]) *
|
|
angle;
|
|
}
|
|
else {
|
|
vert_angles[vidx] += angle;
|
|
}
|
|
#else
|
|
vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], poly_normals[i]) *
|
|
angle;
|
|
#endif
|
|
/* --- end non-angle-calc section --- */
|
|
|
|
/* step */
|
|
copy_v3_v3(nor_prev, nor_next);
|
|
i_curr = i_next;
|
|
i_next++;
|
|
}
|
|
}
|
|
|
|
/* vertex group support */
|
|
if (dvert) {
|
|
const MDeformVert *dv = dvert;
|
|
float scalar;
|
|
|
|
if (defgrp_invert) {
|
|
for (i = 0; i < verts_num; i++, dv++) {
|
|
scalar = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
|
|
scalar = offset_fac_vg + (scalar * offset_fac_vg_inv);
|
|
vert_angles[i] *= scalar;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < verts_num; i++, dv++) {
|
|
scalar = BKE_defvert_find_weight(dv, defgrp_index);
|
|
scalar = offset_fac_vg + (scalar * offset_fac_vg_inv);
|
|
vert_angles[i] *= scalar;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* for angle clamp */
|
|
float *vert_angs = nullptr;
|
|
/* for bevel convex */
|
|
float *edge_angs = nullptr;
|
|
|
|
if (do_angle_clamp || do_bevel_convex) {
|
|
uint eidx;
|
|
if (do_angle_clamp) {
|
|
vert_angs = static_cast<float *>(
|
|
MEM_malloc_arrayN(verts_num, sizeof(float), "vert_angs even"));
|
|
copy_vn_fl(vert_angs, int(verts_num), 0.5f * M_PI);
|
|
}
|
|
if (do_bevel_convex) {
|
|
edge_angs = static_cast<float *>(
|
|
MEM_malloc_arrayN(edges_num, sizeof(float), "edge_angs even"));
|
|
if (!do_rim) {
|
|
edge_users = static_cast<uint *>(
|
|
MEM_malloc_arrayN(edges_num, sizeof(*edge_users), "solid_mod edges"));
|
|
}
|
|
}
|
|
uint(*edge_user_pairs)[2] = static_cast<uint(*)[2]>(
|
|
MEM_malloc_arrayN(edges_num, sizeof(*edge_user_pairs), "edge_user_pairs"));
|
|
for (eidx = 0; eidx < edges_num; eidx++) {
|
|
edge_user_pairs[eidx][0] = INVALID_UNUSED;
|
|
edge_user_pairs[eidx][1] = INVALID_UNUSED;
|
|
}
|
|
for (const int i : orig_polys.index_range()) {
|
|
const MPoly &poly = orig_polys[i];
|
|
int prev_corner_i = poly.loopstart + poly.totloop - 1;
|
|
for (int j = 0; j < poly.totloop; j++) {
|
|
const int corner_i = poly.loopstart + j;
|
|
const int vert_i = orig_corner_verts[corner_i];
|
|
const int prev_vert_i = orig_corner_verts[prev_corner_i];
|
|
|
|
/* add edge user */
|
|
eidx = orig_corner_edges[prev_corner_i];
|
|
const MEdge *edge = &orig_edges[eidx];
|
|
BLI_assert(ELEM(prev_vert_i, edge->v1, edge->v2) && ELEM(vert_i, edge->v1, edge->v2));
|
|
char flip = char((prev_vert_i > vert_i) == (edge->v1 < edge->v2));
|
|
if (edge_user_pairs[eidx][flip] == INVALID_UNUSED) {
|
|
edge_user_pairs[eidx][flip] = uint(i);
|
|
}
|
|
else {
|
|
edge_user_pairs[eidx][0] = INVALID_PAIR;
|
|
edge_user_pairs[eidx][1] = INVALID_PAIR;
|
|
}
|
|
prev_corner_i = corner_i;
|
|
}
|
|
}
|
|
const MEdge *edge = orig_edges.data();
|
|
float e[3];
|
|
for (i = 0; i < edges_num; i++, edge++) {
|
|
if (!ELEM(edge_user_pairs[i][0], INVALID_UNUSED, INVALID_PAIR) &&
|
|
!ELEM(edge_user_pairs[i][1], INVALID_UNUSED, INVALID_PAIR)) {
|
|
const float *n0 = poly_normals[edge_user_pairs[i][0]];
|
|
const float *n1 = poly_normals[edge_user_pairs[i][1]];
|
|
if (do_angle_clamp) {
|
|
const float angle = M_PI - angle_normalized_v3v3(n0, n1);
|
|
vert_angs[edge->v1] = max_ff(vert_angs[edge->v1], angle);
|
|
vert_angs[edge->v2] = max_ff(vert_angs[edge->v2], angle);
|
|
}
|
|
if (do_bevel_convex) {
|
|
sub_v3_v3v3(e, orig_vert_positions[edge->v1], orig_vert_positions[edge->v2]);
|
|
normalize_v3(e);
|
|
edge_angs[i] = angle_signed_on_axis_v3v3_v3(n0, n1, e);
|
|
if (!do_rim) {
|
|
edge_users[i] = INVALID_PAIR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
MEM_freeN(edge_user_pairs);
|
|
}
|
|
|
|
if (do_clamp) {
|
|
const float clamp_fac = 1 + (do_angle_clamp ? fabsf(smd->offset_fac) : 0);
|
|
const float offset = fabsf(smd->offset) * smd->offset_clamp * clamp_fac;
|
|
if (offset > FLT_EPSILON) {
|
|
float *vert_lens_sq = static_cast<float *>(
|
|
MEM_malloc_arrayN(verts_num, sizeof(float), "vert_lens_sq"));
|
|
const float offset_sq = offset * offset;
|
|
copy_vn_fl(vert_lens_sq, int(verts_num), FLT_MAX);
|
|
for (i = 0; i < edges_num; i++) {
|
|
const float ed_len = len_squared_v3v3(vert_positions[edges[i].v1],
|
|
vert_positions[edges[i].v2]);
|
|
vert_lens_sq[edges[i].v1] = min_ff(vert_lens_sq[edges[i].v1], ed_len);
|
|
vert_lens_sq[edges[i].v2] = min_ff(vert_lens_sq[edges[i].v2], ed_len);
|
|
}
|
|
if (do_angle_clamp) {
|
|
for (i = 0; i < verts_num; i++) {
|
|
float cos_ang = cosf(vert_angs[i] * 0.5f);
|
|
if (cos_ang > 0) {
|
|
float max_off = sqrtf(vert_lens_sq[i]) * 0.5f / cos_ang;
|
|
if (max_off < offset * 0.5f) {
|
|
vert_angles[i] *= max_off / offset * 2;
|
|
}
|
|
}
|
|
}
|
|
MEM_freeN(vert_angs);
|
|
}
|
|
else {
|
|
for (i = 0; i < verts_num; i++) {
|
|
if (vert_lens_sq[i] < offset_sq) {
|
|
float scalar = sqrtf(vert_lens_sq[i]) / offset;
|
|
vert_angles[i] *= scalar;
|
|
}
|
|
}
|
|
}
|
|
MEM_freeN(vert_lens_sq);
|
|
}
|
|
}
|
|
|
|
if (do_bevel_convex) {
|
|
for (i = 0; i < edges_num; i++) {
|
|
if (edge_users[i] == INVALID_PAIR) {
|
|
float angle = edge_angs[i];
|
|
result_edge_bweight[i] = clamp_f(result_edge_bweight[i] +
|
|
(angle < M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) :
|
|
clamp_f(bevel_convex, -1.0f, 0.0f)),
|
|
0.0f,
|
|
1.0f);
|
|
if (do_shell) {
|
|
result_edge_bweight[i + edges_num] = clamp_f(
|
|
result_edge_bweight[i + edges_num] +
|
|
(angle > M_PI ? clamp_f(bevel_convex, 0, 1) : clamp_f(bevel_convex, -1, 0)),
|
|
0.0f,
|
|
1.0f);
|
|
}
|
|
}
|
|
}
|
|
if (!do_rim) {
|
|
MEM_freeN(edge_users);
|
|
}
|
|
MEM_freeN(edge_angs);
|
|
}
|
|
|
|
#undef INVALID_UNUSED
|
|
#undef INVALID_PAIR
|
|
|
|
if (ofs_new != 0.0f) {
|
|
uint i_orig, i_end;
|
|
bool do_shell_align;
|
|
|
|
uint vert_index;
|
|
INIT_VERT_ARRAY_OFFSETS(false);
|
|
|
|
for (i_orig = 0; i_orig < i_end; i_orig++, vert_index++) {
|
|
const uint i_other = do_shell_align ? i_orig : new_vert_arr[i_orig];
|
|
if (vert_accum[i_other]) { /* zero if unselected */
|
|
madd_v3_v3fl(vert_positions[vert_index],
|
|
vert_nors[i_other],
|
|
ofs_new * (vert_angles[i_other] / vert_accum[i_other]));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ofs_orig != 0.0f) {
|
|
uint i_orig, i_end;
|
|
bool do_shell_align;
|
|
|
|
/* same as above but swapped, intentional use of 'ofs_new' */
|
|
uint vert_index;
|
|
INIT_VERT_ARRAY_OFFSETS(true);
|
|
|
|
for (i_orig = 0; i_orig < i_end; i_orig++, vert_index++) {
|
|
const uint i_other = do_shell_align ? i_orig : new_vert_arr[i_orig];
|
|
if (vert_accum[i_other]) { /* zero if unselected */
|
|
madd_v3_v3fl(vert_positions[vert_index],
|
|
vert_nors[i_other],
|
|
ofs_orig * (vert_angles[i_other] / vert_accum[i_other]));
|
|
}
|
|
}
|
|
}
|
|
|
|
MEM_freeN(vert_angles);
|
|
}
|
|
|
|
#ifdef USE_NONMANIFOLD_WORKAROUND
|
|
MEM_SAFE_FREE(edge_tmp_tag);
|
|
#endif
|
|
|
|
if (vert_nors) {
|
|
MEM_freeN(vert_nors);
|
|
}
|
|
|
|
/* must recalculate normals with vgroups since they can displace unevenly #26888. */
|
|
if (BKE_mesh_vert_normals_are_dirty(mesh) || do_rim || dvert) {
|
|
/* Pass. */
|
|
}
|
|
else if (do_shell) {
|
|
uint i;
|
|
/* flip vertex normals for copied verts */
|
|
for (i = 0; i < verts_num; i++) {
|
|
negate_v3((float *)&vert_normals[i].x);
|
|
}
|
|
}
|
|
|
|
/* Add vertex weights for rim and shell vgroups. */
|
|
if (shell_defgrp_index != -1 || rim_defgrp_index != -1) {
|
|
MDeformVert *dst_dvert = BKE_mesh_deform_verts_for_write(result);
|
|
|
|
/* Ultimate security check. */
|
|
if (dst_dvert != nullptr) {
|
|
|
|
if (rim_defgrp_index != -1) {
|
|
for (uint i = 0; i < rimVerts; i++) {
|
|
BKE_defvert_ensure_index(&dst_dvert[new_vert_arr[i]], rim_defgrp_index)->weight = 1.0f;
|
|
BKE_defvert_ensure_index(&dst_dvert[(do_shell ? new_vert_arr[i] : i) + verts_num],
|
|
rim_defgrp_index)
|
|
->weight = 1.0f;
|
|
}
|
|
}
|
|
|
|
if (shell_defgrp_index != -1) {
|
|
for (uint i = verts_num; i < result->totvert; i++) {
|
|
BKE_defvert_ensure_index(&dst_dvert[i], shell_defgrp_index)->weight = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (do_rim) {
|
|
uint i;
|
|
|
|
/* NOTE(@ideasman42): Unfortunately re-calculate the normals for the new edge
|
|
* faces is necessary. This could be done in many ways, but probably the quickest
|
|
* way is to calculate the average normals for side faces only.
|
|
* Then blend them with the normals of the edge verts.
|
|
*
|
|
* At the moment its easiest to allocate an entire array for every vertex,
|
|
* even though we only need edge verts. */
|
|
|
|
#define SOLIDIFY_SIDE_NORMALS
|
|
|
|
#ifdef SOLIDIFY_SIDE_NORMALS
|
|
/* NOTE(@sybren): due to the code setting normals dirty a few lines above,
|
|
* do_side_normals is always false. */
|
|
const bool do_side_normals = !BKE_mesh_vert_normals_are_dirty(result);
|
|
/* annoying to allocate these since we only need the edge verts, */
|
|
float(*edge_vert_nos)[3] = do_side_normals ? static_cast<float(*)[3]>(MEM_calloc_arrayN(
|
|
verts_num, sizeof(float[3]), __func__)) :
|
|
nullptr;
|
|
float nor[3];
|
|
#endif
|
|
const float crease_rim = smd->crease_rim;
|
|
const float crease_outer = smd->crease_outer;
|
|
const float crease_inner = smd->crease_inner;
|
|
|
|
int *origindex_edge;
|
|
int *orig_ed;
|
|
uint j;
|
|
|
|
float *result_edge_crease = nullptr;
|
|
if (crease_rim || crease_outer || crease_inner) {
|
|
result_edge_crease = (float *)CustomData_add_layer(
|
|
&result->edata, CD_CREASE, CD_SET_DEFAULT, result->totedge);
|
|
}
|
|
|
|
/* add faces & edges */
|
|
origindex_edge = static_cast<int *>(
|
|
CustomData_get_layer_for_write(&result->edata, CD_ORIGINDEX, result->totedge));
|
|
orig_ed = (origindex_edge) ? &origindex_edge[(edges_num * stride) + newEdges] : nullptr;
|
|
/* Start after copied edges. */
|
|
int new_edge_index = int(edges_num * stride + newEdges);
|
|
for (i = 0; i < rimVerts; i++) {
|
|
edges[new_edge_index].v1 = new_vert_arr[i];
|
|
edges[new_edge_index].v2 = (do_shell ? new_vert_arr[i] : i) + verts_num;
|
|
|
|
if (orig_ed) {
|
|
*orig_ed = ORIGINDEX_NONE;
|
|
orig_ed++;
|
|
}
|
|
|
|
if (crease_rim) {
|
|
result_edge_crease[new_edge_index] = crease_rim;
|
|
}
|
|
new_edge_index++;
|
|
}
|
|
|
|
/* faces */
|
|
int new_poly_index = int(polys_num * stride);
|
|
blender::MutableSpan<int> new_corner_verts = corner_verts.drop_front(loops_num * stride);
|
|
blender::MutableSpan<int> new_corner_edges = corner_edges.drop_front(loops_num * stride);
|
|
j = 0;
|
|
for (i = 0; i < newPolys; i++) {
|
|
uint eidx = new_edge_arr[i];
|
|
uint pidx = edge_users[eidx];
|
|
int k1, k2;
|
|
bool flip;
|
|
|
|
if (pidx >= polys_num) {
|
|
pidx -= polys_num;
|
|
flip = true;
|
|
}
|
|
else {
|
|
flip = false;
|
|
}
|
|
|
|
const MEdge &edge = edges[eidx];
|
|
|
|
/* copy most of the face settings */
|
|
CustomData_copy_data(
|
|
&mesh->pdata, &result->pdata, int(pidx), int((polys_num * stride) + i), 1);
|
|
polys[new_poly_index].loopstart = int(j + (loops_num * stride));
|
|
|
|
/* notice we use 'polys[new_poly_index].totloop' which is later overwritten,
|
|
* we could lookup the original face but there's no point since this is a copy
|
|
* and will have the same value, just take care when changing order of assignment */
|
|
|
|
/* prev loop */
|
|
k1 = polys[pidx].loopstart + (((edge_order[eidx] - 1) + polys[new_poly_index].totloop) %
|
|
polys[new_poly_index].totloop);
|
|
|
|
k2 = polys[pidx].loopstart + (edge_order[eidx]);
|
|
|
|
polys[new_poly_index].totloop = 4;
|
|
|
|
CustomData_copy_data(&mesh->ldata, &result->ldata, k2, int((loops_num * stride) + j + 0), 1);
|
|
CustomData_copy_data(&mesh->ldata, &result->ldata, k1, int((loops_num * stride) + j + 1), 1);
|
|
CustomData_copy_data(&mesh->ldata, &result->ldata, k1, int((loops_num * stride) + j + 2), 1);
|
|
CustomData_copy_data(&mesh->ldata, &result->ldata, k2, int((loops_num * stride) + j + 3), 1);
|
|
|
|
if (flip == false) {
|
|
new_corner_verts[j] = edge.v1;
|
|
new_corner_edges[j++] = eidx;
|
|
|
|
new_corner_verts[j] = edge.v2;
|
|
new_corner_edges[j++] = (edges_num * stride) + old_vert_arr[edge.v2] + newEdges;
|
|
|
|
new_corner_verts[j] = (do_shell ? edge.v2 : old_vert_arr[edge.v2]) + verts_num;
|
|
new_corner_edges[j++] = (do_shell ? eidx : i) + edges_num;
|
|
|
|
new_corner_verts[j] = (do_shell ? edge.v1 : old_vert_arr[edge.v1]) + verts_num;
|
|
new_corner_edges[j++] = (edges_num * stride) + old_vert_arr[edge.v1] + newEdges;
|
|
}
|
|
else {
|
|
new_corner_verts[j] = edge.v2;
|
|
new_corner_edges[j++] = eidx;
|
|
|
|
new_corner_verts[j] = edge.v1;
|
|
new_corner_edges[j++] = (edges_num * stride) + old_vert_arr[edge.v1] + newEdges;
|
|
|
|
new_corner_verts[j] = (do_shell ? edge.v1 : old_vert_arr[edge.v1]) + verts_num;
|
|
new_corner_edges[j++] = (do_shell ? eidx : i) + edges_num;
|
|
|
|
new_corner_verts[j] = (do_shell ? edge.v2 : old_vert_arr[edge.v2]) + verts_num;
|
|
new_corner_edges[j++] = (edges_num * stride) + old_vert_arr[edge.v2] + newEdges;
|
|
}
|
|
|
|
if (origindex_edge) {
|
|
origindex_edge[new_corner_edges[j - 3]] = ORIGINDEX_NONE;
|
|
origindex_edge[new_corner_edges[j - 1]] = ORIGINDEX_NONE;
|
|
}
|
|
|
|
/* use the next material index if option enabled */
|
|
if (mat_ofs_rim) {
|
|
dst_material_index[new_poly_index] += mat_ofs_rim;
|
|
CLAMP(dst_material_index[new_poly_index], 0, mat_nr_max);
|
|
}
|
|
if (crease_outer) {
|
|
/* crease += crease_outer; without wrapping */
|
|
float *cr = &(result_edge_crease[eidx]);
|
|
float tcr = *cr + crease_outer;
|
|
*cr = tcr > 1.0f ? 1.0f : tcr;
|
|
}
|
|
|
|
if (crease_inner) {
|
|
/* crease += crease_inner; without wrapping */
|
|
float *cr = &(result_edge_crease[edges_num + (do_shell ? eidx : i)]);
|
|
float tcr = *cr + crease_inner;
|
|
*cr = tcr > 1.0f ? 1.0f : tcr;
|
|
}
|
|
|
|
#ifdef SOLIDIFY_SIDE_NORMALS
|
|
if (do_side_normals) {
|
|
normal_quad_v3(nor,
|
|
vert_positions[new_corner_verts[j - 4]],
|
|
vert_positions[new_corner_verts[j - 3]],
|
|
vert_positions[new_corner_verts[j - 2]],
|
|
vert_positions[new_corner_verts[j - 1]]);
|
|
|
|
add_v3_v3(edge_vert_nos[edge.v1], nor);
|
|
add_v3_v3(edge_vert_nos[edge.v2], nor);
|
|
}
|
|
#endif
|
|
|
|
new_poly_index++;
|
|
}
|
|
|
|
#ifdef SOLIDIFY_SIDE_NORMALS
|
|
if (do_side_normals) {
|
|
for (i = 0; i < rimVerts; i++) {
|
|
const MEdge &edge_orig = edges[i];
|
|
const MEdge &edge = edges[edges_num * stride + i];
|
|
float nor_cpy[3];
|
|
int k;
|
|
|
|
/* NOTE: only the first vertex (lower half of the index) is calculated. */
|
|
BLI_assert(edge.v1 < verts_num);
|
|
normalize_v3_v3(nor_cpy, edge_vert_nos[edge_orig.v1]);
|
|
|
|
for (k = 0; k < 2; k++) { /* loop over both verts of the edge */
|
|
copy_v3_v3(nor, vert_normals[*(&edge.v1 + k)]);
|
|
add_v3_v3(nor, nor_cpy);
|
|
normalize_v3(nor);
|
|
copy_v3_v3((float *)&vert_normals[*(&edge.v1 + k)].x, nor);
|
|
}
|
|
}
|
|
|
|
MEM_freeN(edge_vert_nos);
|
|
}
|
|
#endif
|
|
|
|
MEM_freeN(new_vert_arr);
|
|
MEM_freeN(new_edge_arr);
|
|
|
|
MEM_freeN(edge_users);
|
|
MEM_freeN(edge_order);
|
|
}
|
|
|
|
if (old_vert_arr) {
|
|
MEM_freeN(old_vert_arr);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#undef SOLIDIFY_SIDE_NORMALS
|
|
|
|
/** \} */
|