This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/modifiers/intern/MOD_solidify.c

978 lines
29 KiB
C
Raw Normal View History

/*
* 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) 2005 by the Blender Foundation.
* All rights reserved.
*/
/** \file \ingroup modifiers
*/
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_utildefines_stack.h"
#include "BLI_bitmap.h"
#include "BLI_math.h"
#include "BKE_mesh.h"
#include "BKE_particle.h"
#include "BKE_deform.h"
#include "MOD_modifiertypes.h"
#include "MOD_util.h"
2013-06-12 06:51:02 +00:00
#ifdef __GNUC__
# pragma GCC diagnostic error "-Wsign-conversion"
#endif
/* skip shell thickness for non-manifold edges, see [#35710] */
#define USE_NONMANIFOLD_WORKAROUND
2012-05-26 09:18:47 +00:00
/* *** derived mesh high quality normal calculation function *** */
/* could be exposed for other functions to use */
typedef struct EdgeFaceRef {
int f1; /* init as -1 */
int f2;
} EdgeFaceRef;
BLI_INLINE bool edgeref_is_init(const EdgeFaceRef *edge_ref)
{
return !((edge_ref->f1 == 0) && (edge_ref->f2 == 0));
}
/**
* \param dm: Mesh to calculate normals for.
* \param face_nors: Precalculated face normals.
* \param r_vert_nors: Return vert normals.
*/
static void mesh_calc_hq_normal(Mesh *mesh, float (*face_nors)[3], float (*r_vert_nors)[3])
{
int i, numVerts, numEdges, numFaces;
MPoly *mpoly, *mp;
MLoop *mloop, *ml;
MEdge *medge, *ed;
MVert *mvert, *mv;
numVerts = mesh->totvert;
numEdges = mesh->totedge;
numFaces = mesh->totface;
mpoly = mesh->mpoly;
medge = mesh->medge;
mvert = mesh->mvert;
mloop = mesh->mloop;
/* we don't want to overwrite any referenced layers */
/* Doesn't work here! */
#if 0
mv = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT, numVerts);
cddm->mvert = mv;
#endif
mv = mvert;
mp = mpoly;
{
EdgeFaceRef *edge_ref_array = MEM_calloc_arrayN((size_t)numEdges, sizeof(EdgeFaceRef), "Edge Connectivity");
EdgeFaceRef *edge_ref;
float edge_normal[3];
/* Add an edge reference if it's not there, pointing back to the face index. */
for (i = 0; i < numFaces; i++, mp++) {
int j;
ml = mloop + mp->loopstart;
for (j = 0; j < mp->totloop; j++, ml++) {
/* --- add edge ref to face --- */
edge_ref = &edge_ref_array[ml->e];
if (!edgeref_is_init(edge_ref)) {
edge_ref->f1 = i;
edge_ref->f2 = -1;
}
else if ((edge_ref->f1 != -1) && (edge_ref->f2 == -1)) {
edge_ref->f2 = i;
}
else {
/* 3+ faces using an edge, we can't handle this usefully */
edge_ref->f1 = edge_ref->f2 = -1;
#ifdef USE_NONMANIFOLD_WORKAROUND
medge[ml->e].flag |= ME_EDGE_TMP_TAG;
#endif
}
/* --- done --- */
}
}
for (i = 0, ed = medge, edge_ref = edge_ref_array; i < numEdges; i++, ed++, edge_ref++) {
/* Get the edge vert indices, and edge value (the face indices that use it) */
if (edgeref_is_init(edge_ref) && (edge_ref->f1 != -1)) {
if (edge_ref->f2 != -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(
2016-07-08 10:14:49 +10:00
edge_normal,
angle_normalized_v3v3(face_nors[edge_ref->f1], face_nors[edge_ref->f2]));
#else
mid_v3_v3v3_angle_weighted(edge_normal, face_nors[edge_ref->f1], face_nors[edge_ref->f2]);
#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, face_nors[edge_ref->f1]);
}
add_v3_v3(r_vert_nors[ed->v1], edge_normal);
add_v3_v3(r_vert_nors[ed->v2], edge_normal);
}
}
MEM_freeN(edge_ref_array);
}
/* normalize vertex normals and assign */
for (i = 0; i < numVerts; i++, mv++) {
if (normalize_v3(r_vert_nors[i]) == 0.0f) {
normal_short_to_float_v3(r_vert_nors[i], mv->no);
}
}
}
static void initData(ModifierData *md)
{
2012-05-06 13:38:33 +00:00
SolidifyModifierData *smd = (SolidifyModifierData *) md;
smd->offset = 0.01f;
smd->offset_fac = -1.0f;
smd->flag = MOD_SOLIDIFY_RIM;
}
static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md)
{
2012-05-06 13:38:33 +00:00
SolidifyModifierData *smd = (SolidifyModifierData *) md;
CustomDataMask dataMask = 0;
/* ask for vertexgroups if we need them */
if (smd->defgrp_name[0]) dataMask |= CD_MASK_MDEFORMVERT;
return dataMask;
}
2012-07-16 23:23:33 +00:00
/* specific function for solidify - define locally */
2012-05-26 09:18:47 +00:00
BLI_INLINE void madd_v3v3short_fl(float r[3], const short a[3], const float f)
{
r[0] += (float)a[0] * f;
r[1] += (float)a[1] * f;
r[2] += (float)a[2] * f;
}
static Mesh *applyModifier(
ModifierData *md, const ModifierEvalContext *ctx,
Mesh *mesh)
{
Mesh *result;
2012-05-06 13:38:33 +00:00
const SolidifyModifierData *smd = (SolidifyModifierData *) md;
MVert *mv, *mvert, *orig_mvert;
MEdge *ed, *medge, *orig_medge;
MLoop *ml, *mloop, *orig_mloop;
MPoly *mp, *mpoly, *orig_mpoly;
const unsigned int numVerts = (unsigned int)mesh->totvert;
const unsigned int numEdges = (unsigned int)mesh->totedge;
const unsigned int numFaces = (unsigned int)mesh->totpoly;
const unsigned int numLoops = (unsigned int)mesh->totloop;
unsigned int newLoops = 0, newFaces = 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;
2012-05-06 13:38:33 +00:00
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 */
2013-06-12 06:20:24 +00:00
/* over-alloc new_vert_arr, old_vert_arr */
2013-06-12 06:51:02 +00:00
unsigned int *new_vert_arr = NULL;
2013-06-12 06:20:24 +00:00
STACK_DECLARE(new_vert_arr);
2013-06-12 06:51:02 +00:00
unsigned int *new_edge_arr = NULL;
2013-06-12 06:20:24 +00:00
STACK_DECLARE(new_edge_arr);
unsigned int *old_vert_arr = MEM_calloc_arrayN(numVerts, sizeof(*old_vert_arr), "old_vert_arr in solidify");
2013-06-12 06:51:02 +00:00
unsigned int *edge_users = NULL;
2012-05-06 13:38:33 +00:00
char *edge_order = NULL;
2012-05-06 13:38:33 +00:00
float (*vert_nors)[3] = NULL;
float (*face_nors)[3] = NULL;
const bool need_face_normals = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) || (smd->flag & MOD_SOLIDIFY_EVEN);
const float ofs_orig = -(((-smd->offset_fac + 1.0f) * 0.5f) * smd->offset);
const float ofs_new = smd->offset + ofs_orig;
2012-05-06 13:38:33 +00:00
const float offset_fac_vg = smd->offset_fac_vg;
const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg;
const bool do_flip = (smd->flag & MOD_SOLIDIFY_FLIP) != 0;
const bool do_clamp = (smd->offset_clamp != 0.0f);
const bool do_shell = ((smd->flag & MOD_SOLIDIFY_RIM) && (smd->flag & MOD_SOLIDIFY_NOSHELL)) == 0;
/* weights */
2014-08-20 20:30:45 +10:00
MDeformVert *dvert;
2015-04-20 23:37:04 +10:00
const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0;
int defgrp_index;
/* array size is doubled in case of using a shell */
const unsigned int stride = do_shell ? 2 : 1;
2018-06-29 19:02:19 +02:00
MOD_get_vgroup(ctx->object, mesh, smd->defgrp_name, &dvert, &defgrp_index);
orig_mvert = mesh->mvert;
orig_medge = mesh->medge;
orig_mloop = mesh->mloop;
orig_mpoly = mesh->mpoly;
if (need_face_normals) {
/* calculate only face normals */
face_nors = MEM_malloc_arrayN(numFaces, sizeof(*face_nors), __func__);
BKE_mesh_calc_normals_poly(
orig_mvert, NULL, (int)numVerts,
orig_mloop, orig_mpoly,
(int)numLoops, (int)numFaces,
face_nors, true);
}
STACK_INIT(new_vert_arr, numVerts * 2);
STACK_INIT(new_edge_arr, numEdges * 2);
2013-06-12 06:20:24 +00:00
if (smd->flag & MOD_SOLIDIFY_RIM) {
BLI_bitmap *orig_mvert_tag = BLI_BITMAP_NEW(numVerts, __func__);
2013-06-12 06:51:02 +00:00
unsigned int eidx;
2014-08-20 20:30:45 +10:00
unsigned int i;
2013-06-12 06:51:02 +00:00
#define INVALID_UNUSED ((unsigned int)-1)
#define INVALID_PAIR ((unsigned int)-2)
new_vert_arr = MEM_malloc_arrayN(numVerts, 2 * sizeof(*new_vert_arr), __func__);
new_edge_arr = MEM_malloc_arrayN(((numEdges * 2) + numVerts), sizeof(*new_edge_arr), __func__);
2013-06-12 06:20:24 +00:00
edge_users = MEM_malloc_arrayN(numEdges, sizeof(*edge_users), "solid_mod edges");
edge_order = MEM_malloc_arrayN(numEdges, sizeof(*edge_order), "solid_mod eorder");
/* save doing 2 loops here... */
#if 0
copy_vn_i(edge_users, numEdges, INVALID_UNUSED);
#endif
for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) {
edge_users[eidx] = INVALID_UNUSED;
}
2012-05-06 13:38:33 +00:00
for (i = 0, mp = orig_mpoly; i < numFaces; i++, mp++) {
MLoop *ml_prev;
int j;
ml = orig_mloop + mp->loopstart;
ml_prev = ml + (mp->totloop - 1);
for (j = 0; j < mp->totloop; j++, ml++) {
/* add edge user */
eidx = ml_prev->e;
if (edge_users[eidx] == INVALID_UNUSED) {
ed = orig_medge + eidx;
BLI_assert(ELEM(ml_prev->v, ed->v1, ed->v2) &&
ELEM(ml->v, ed->v1, ed->v2));
edge_users[eidx] = (ml_prev->v > ml->v) == (ed->v1 < ed->v2) ? i : (i + numFaces);
edge_order[eidx] = j;
}
else {
edge_users[eidx] = INVALID_PAIR;
}
ml_prev = ml;
}
}
for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) {
2013-06-12 06:51:02 +00:00
if (!ELEM(edge_users[eidx], INVALID_UNUSED, INVALID_PAIR)) {
BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v1);
BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v2);
2013-06-12 06:20:24 +00:00
STACK_PUSH(new_edge_arr, eidx);
newFaces++;
newLoops += 4;
}
}
for (i = 0; i < numVerts; i++) {
if (BLI_BITMAP_TEST(orig_mvert_tag, i)) {
2013-06-12 06:20:24 +00:00
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 = newFaces;
}
else {
/* (stride == 2) in this case, so no need to add newVerts/newEdges */
BLI_assert(newVerts == 0);
BLI_assert(newEdges == 0);
}
if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) {
vert_nors = MEM_calloc_arrayN(numVerts, 3 * sizeof(float), "mod_solid_vno_hq");
mesh_calc_hq_normal(mesh, face_nors, vert_nors);
}
result = BKE_mesh_new_nomain_from_template(
mesh,
(int)((numVerts * stride) + newVerts),
(int)((numEdges * stride) + newEdges + rimVerts), 0,
(int)((numLoops * stride) + newLoops),
(int)((numFaces * stride) + newFaces));
mpoly = result->mpoly;
mloop = result->mloop;
medge = result->medge;
mvert = result->mvert;
if (do_shell) {
CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, (int)numVerts);
CustomData_copy_data(&mesh->vdata, &result->vdata, 0, (int)numVerts, (int)numVerts);
CustomData_copy_data(&mesh->edata, &result->edata, 0, 0, (int)numEdges);
CustomData_copy_data(&mesh->edata, &result->edata, 0, (int)numEdges, (int)numEdges);
CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, (int)numLoops);
/* 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 memleak). */
CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, (int)numFaces);
CustomData_copy_data(&mesh->pdata, &result->pdata, 0, (int)numFaces, (int)numFaces);
}
else {
int i, j;
CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, (int)numVerts);
for (i = 0, j = (int)numVerts; i < numVerts; 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)numEdges);
for (i = 0, j = (int)numEdges; i < numEdges; i++) {
if (!ELEM(edge_users[i], INVALID_UNUSED, INVALID_PAIR)) {
2014-08-18 16:04:44 +10:00
MEdge *ed_src, *ed_dst;
CustomData_copy_data(&mesh->edata, &result->edata, i, j, 1);
ed_src = &medge[i];
ed_dst = &medge[j];
ed_dst->v1 = old_vert_arr[ed_src->v1] + numVerts;
ed_dst->v2 = old_vert_arr[ed_src->v2] + numVerts;
j++;
}
}
/* will be created later */
CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, (int)numLoops);
CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, (int)numFaces);
}
#undef INVALID_UNUSED
#undef INVALID_PAIR
2014-08-20 20:30:45 +10:00
/* initializes: (i_end, do_shell_align, mv) */
#define INIT_VERT_ARRAY_OFFSETS(test) \
if (((ofs_new >= ofs_orig) == do_flip) == test) { \
i_end = numVerts; \
do_shell_align = true; \
mv = mvert; \
} \
else { \
if (do_shell) { \
i_end = numVerts; \
do_shell_align = true; \
} \
else { \
i_end = newVerts ; \
do_shell_align = false; \
} \
mv = &mvert[numVerts]; \
} (void)0
/* flip normals */
if (do_shell) {
2014-08-20 20:30:45 +10:00
unsigned int i;
mp = mpoly + numFaces;
for (i = 0; i < mesh->totpoly; i++, mp++) {
const int loop_end = mp->totloop - 1;
MLoop *ml2;
unsigned int e;
int j;
/* reverses the loop direction (MLoop.v as well as custom-data)
* MLoop.e also needs to be corrected too, done in a separate loop below. */
ml2 = mloop + mp->loopstart + mesh->totloop;
#if 0
for (j = 0; j < mp->totloop; j++) {
CustomData_copy_data(&mesh->ldata, &result->ldata, mp->loopstart + j,
mp->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 < mp->totloop; j_prev = j++) {
CustomData_copy_data(&mesh->ldata, &result->ldata, mp->loopstart + j,
mp->loopstart + (loop_end - j_prev) + mesh->totloop, 1);
}
#endif
if (mat_ofs) {
mp->mat_nr += mat_ofs;
CLAMP(mp->mat_nr, 0, mat_nr_max);
}
e = ml2[0].e;
for (j = 0; j < loop_end; j++) {
ml2[j].e = ml2[j + 1].e;
}
ml2[loop_end].e = e;
mp->loopstart += mesh->totloop;
for (j = 0; j < mp->totloop; j++) {
ml2[j].e += numEdges;
ml2[j].v += numVerts;
}
}
for (i = 0, ed = medge + numEdges; i < numEdges; i++, ed++) {
ed->v1 += numVerts;
ed->v2 += numVerts;
}
}
2012-03-18 07:38:51 +00:00
/* 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 scalar_short;
float scalar_short_vgroup;
/* for clamping */
float *vert_lens = NULL;
const float offset = fabsf(smd->offset) * smd->offset_clamp;
const float offset_sq = offset * offset;
if (do_clamp) {
2014-08-20 20:30:45 +10:00
unsigned int i;
vert_lens = MEM_malloc_arrayN(numVerts, sizeof(float), "vert_lens");
copy_vn_fl(vert_lens, (int)numVerts, FLT_MAX);
for (i = 0; i < numEdges; i++) {
const float ed_len_sq = len_squared_v3v3(mvert[medge[i].v1].co, mvert[medge[i].v2].co);
vert_lens[medge[i].v1] = min_ff(vert_lens[medge[i].v1], ed_len_sq);
vert_lens[medge[i].v2] = min_ff(vert_lens[medge[i].v2], ed_len_sq);
}
}
if (ofs_new != 0.0f) {
2014-08-20 20:30:45 +10:00
unsigned int i_orig, i_end;
bool do_shell_align;
2012-05-06 13:38:33 +00:00
scalar_short = scalar_short_vgroup = ofs_new / 32767.0f;
2014-08-20 20:30:45 +10:00
INIT_VERT_ARRAY_OFFSETS(false);
2014-08-20 20:30:45 +10:00
for (i_orig = 0; i_orig < i_end; i_orig++, mv++) {
const unsigned int i = do_shell_align ? i_orig : new_vert_arr[i_orig];
if (dvert) {
MDeformVert *dv = &dvert[i];
2012-05-06 13:38:33 +00:00
if (defgrp_invert) scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index);
else scalar_short_vgroup = defvert_find_weight(dv, defgrp_index);
scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * scalar_short;
}
if (do_clamp) {
/* always reset becaise we may have set before */
2014-08-20 20:30:45 +10:00
if (dvert == NULL) {
scalar_short_vgroup = scalar_short;
}
if (vert_lens[i] < offset_sq) {
float scalar = sqrtf(vert_lens[i]) / offset;
scalar_short_vgroup *= scalar;
}
}
madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup);
}
}
if (ofs_orig != 0.0f) {
2014-08-20 20:30:45 +10:00
unsigned int i_orig, i_end;
bool do_shell_align;
2012-05-06 13:38:33 +00:00
scalar_short = scalar_short_vgroup = ofs_orig / 32767.0f;
/* as above but swapped */
2014-08-20 20:30:45 +10:00
INIT_VERT_ARRAY_OFFSETS(true);
2014-08-20 20:30:45 +10:00
for (i_orig = 0; i_orig < i_end; i_orig++, mv++) {
const unsigned int i = do_shell_align ? i_orig : new_vert_arr[i_orig];
if (dvert) {
MDeformVert *dv = &dvert[i];
2012-05-06 13:38:33 +00:00
if (defgrp_invert) scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index);
else scalar_short_vgroup = defvert_find_weight(dv, defgrp_index);
scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * scalar_short;
}
if (do_clamp) {
/* always reset becaise we may have set before */
2014-08-20 20:30:45 +10:00
if (dvert == NULL) {
scalar_short_vgroup = scalar_short;
}
if (vert_lens[i] < offset_sq) {
float scalar = sqrtf(vert_lens[i]) / offset;
scalar_short_vgroup *= scalar;
}
}
madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup);
}
}
if (do_clamp) {
MEM_freeN(vert_lens);
}
}
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 = MEM_calloc_arrayN(numVerts, 2 * sizeof(float), "mod_solid_pair"); /* 2 in 1 */
2012-05-06 13:38:33 +00:00
float *vert_accum = vert_angles + numVerts;
2013-06-12 06:51:02 +00:00
unsigned int vidx;
2014-08-20 20:30:45 +10:00
unsigned int i;
2012-05-06 13:38:33 +00:00
if (vert_nors == NULL) {
vert_nors = MEM_malloc_arrayN(numVerts, 3 * sizeof(float), "mod_solid_vno");
2012-05-06 13:38:33 +00:00
for (i = 0, mv = mvert; i < numVerts; i++, mv++) {
normal_short_to_float_v3(vert_nors[i], mv->no);
}
}
2012-05-06 13:38:33 +00:00
for (i = 0, mp = mpoly; i < numFaces; i++, mp++) {
/* #BKE_mesh_calc_poly_angles logic is inlined here */
float nor_prev[3];
float nor_next[3];
int i_curr = mp->totloop - 1;
int i_next = 0;
ml = &mloop[mp->loopstart];
sub_v3_v3v3(nor_prev, mvert[ml[i_curr - 1].v].co, mvert[ml[i_curr].v].co);
normalize_v3(nor_prev);
while (i_next < mp->totloop) {
float angle;
sub_v3_v3v3(nor_next, mvert[ml[i_curr].v].co, mvert[ml[i_next].v].co);
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 = ml[i_curr].v;
vert_accum[vidx] += angle;
#ifdef USE_NONMANIFOLD_WORKAROUND
/* skip 3+ face user edges */
if ((check_non_manifold == false) ||
LIKELY(((orig_medge[ml[i_curr].e].flag & ME_EDGE_TMP_TAG) == 0) &&
((orig_medge[ml[i_next].e].flag & ME_EDGE_TMP_TAG) == 0)))
{
vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], face_nors[i]) * angle;
}
else {
vert_angles[vidx] += angle;
}
#else
vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], face_nors[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) {
2014-08-20 20:30:45 +10:00
MDeformVert *dv = dvert;
float scalar;
if (defgrp_invert) {
2012-05-06 13:38:33 +00:00
for (i = 0; i < numVerts; i++, dv++) {
scalar = 1.0f - defvert_find_weight(dv, defgrp_index);
scalar = offset_fac_vg + (scalar * offset_fac_vg_inv);
vert_angles[i] *= scalar;
}
}
else {
2012-05-06 13:38:33 +00:00
for (i = 0; i < numVerts; i++, dv++) {
scalar = defvert_find_weight(dv, defgrp_index);
scalar = offset_fac_vg + (scalar * offset_fac_vg_inv);
vert_angles[i] *= scalar;
}
}
}
if (do_clamp) {
float *vert_lens_sq = MEM_malloc_arrayN(numVerts, sizeof(float), "vert_lens");
const float offset = fabsf(smd->offset) * smd->offset_clamp;
const float offset_sq = offset * offset;
copy_vn_fl(vert_lens_sq, (int)numVerts, FLT_MAX);
for (i = 0; i < numEdges; i++) {
const float ed_len = len_squared_v3v3(mvert[medge[i].v1].co, mvert[medge[i].v2].co);
vert_lens_sq[medge[i].v1] = min_ff(vert_lens_sq[medge[i].v1], ed_len);
vert_lens_sq[medge[i].v2] = min_ff(vert_lens_sq[medge[i].v2], ed_len);
}
for (i = 0; i < numVerts; 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);
}
2014-08-20 20:30:45 +10:00
if (ofs_new != 0.0f) {
unsigned int i_orig, i_end;
bool do_shell_align;
2014-08-20 20:30:45 +10:00
INIT_VERT_ARRAY_OFFSETS(false);
2014-08-20 20:30:45 +10:00
for (i_orig = 0; i_orig < i_end; i_orig++, mv++) {
const unsigned int i_other = do_shell_align ? i_orig : new_vert_arr[i_orig];
if (vert_accum[i_other]) { /* zero if unselected */
madd_v3_v3fl(mv->co, vert_nors[i_other], ofs_new * (vert_angles[i_other] / vert_accum[i_other]));
}
}
}
2014-08-20 20:30:45 +10:00
if (ofs_orig != 0.0f) {
unsigned int i_orig, i_end;
bool do_shell_align;
2012-05-06 13:38:33 +00:00
/* same as above but swapped, intentional use of 'ofs_new' */
2014-08-20 20:30:45 +10:00
INIT_VERT_ARRAY_OFFSETS(true);
2014-08-20 20:30:45 +10:00
for (i_orig = 0; i_orig < i_end; i_orig++, mv++) {
const unsigned int i_other = do_shell_align ? i_orig : new_vert_arr[i_orig];
if (vert_accum[i_other]) { /* zero if unselected */
madd_v3_v3fl(mv->co, vert_nors[i_other], ofs_orig * (vert_angles[i_other] / vert_accum[i_other]));
}
}
}
MEM_freeN(vert_angles);
}
if (vert_nors)
MEM_freeN(vert_nors);
/* must recalculate normals with vgroups since they can displace unevenly [#26888] */
if ((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || (smd->flag & MOD_SOLIDIFY_RIM) || dvert) {
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
}
else if (do_shell) {
2014-08-20 20:30:45 +10:00
unsigned int i;
/* flip vertex normals for copied verts */
mv = mvert + numVerts;
for (i = 0; i < numVerts; i++, mv++) {
negate_v3_short(mv->no);
}
}
if (smd->flag & MOD_SOLIDIFY_RIM) {
2014-08-20 20:30:45 +10:00
unsigned int i;
/* bugger, need to re-calculate the normals for the new edge faces.
2012-05-06 13:38:33 +00:00
* 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.
*
2012-05-06 13:38:33 +00:00
* at the moment its easiest to allocate an entire array for every vertex,
* even though we only need edge verts - campbell
*/
#define SOLIDIFY_SIDE_NORMALS
#ifdef SOLIDIFY_SIDE_NORMALS
/* Note that, due to the code setting cd_dirty_vert a few lines above,
* do_side_normals is always false. - Sybren */
const bool do_side_normals = !(result->runtime.cd_dirty_vert & CD_MASK_NORMAL);
/* annoying to allocate these since we only need the edge verts, */
float (*edge_vert_nos)[3] = do_side_normals ? MEM_calloc_arrayN(numVerts, 3 * sizeof(float), __func__) : NULL;
float nor[3];
#endif
2012-05-06 13:38:33 +00:00
const unsigned char crease_rim = smd->crease_rim * 255.0f;
const unsigned char crease_outer = smd->crease_outer * 255.0f;
const unsigned char crease_inner = smd->crease_inner * 255.0f;
int *origindex_edge;
int *orig_ed;
2013-06-12 06:51:02 +00:00
unsigned int j;
if (crease_rim || crease_outer || crease_inner) {
result->cd_flag |= ME_CDFLAG_EDGE_CREASE;
}
/* add faces & edges */
origindex_edge = CustomData_get_layer(&result->edata, CD_ORIGINDEX);
BLI_assert((numEdges == 0) || (origindex_edge != NULL));
ed = &medge[(numEdges * stride) + newEdges]; /* start after copied edges */
orig_ed = &origindex_edge[(numEdges * stride) + newEdges];
for (i = 0; i < rimVerts; i++, ed++, orig_ed++) {
2012-05-06 13:38:33 +00:00
ed->v1 = new_vert_arr[i];
ed->v2 = (do_shell ? new_vert_arr[i] : i) + numVerts;
ed->flag |= ME_EDGEDRAW;
*orig_ed = ORIGINDEX_NONE;
if (crease_rim) {
2012-05-06 13:38:33 +00:00
ed->crease = crease_rim;
}
}
/* faces */
mp = mpoly + (numFaces * stride);
ml = mloop + (numLoops * stride);
j = 0;
2012-05-06 13:38:33 +00:00
for (i = 0; i < newFaces; i++, mp++) {
2013-06-12 06:51:02 +00:00
unsigned int eidx = new_edge_arr[i];
unsigned int fidx = edge_users[eidx];
int k1, k2;
bool flip;
if (fidx >= numFaces) {
fidx -= numFaces;
2013-06-12 06:51:02 +00:00
flip = true;
}
else {
2013-06-12 06:51:02 +00:00
flip = false;
}
2012-05-06 13:38:33 +00:00
ed = medge + eidx;
/* copy most of the face settings */
CustomData_copy_data(&mesh->pdata, &result->pdata, (int)fidx, (int)((numFaces * stride) + i), 1);
mp->loopstart = (int)(j + (numLoops * stride));
mp->flag = mpoly[fidx].flag;
/* notice we use 'mp->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 */
k1 = mpoly[fidx].loopstart + (((edge_order[eidx] - 1) + mp->totloop) % mp->totloop); /* prev loop */
2013-06-12 06:51:02 +00:00
k2 = mpoly[fidx].loopstart + (edge_order[eidx]);
mp->totloop = 4;
CustomData_copy_data(&mesh->ldata, &result->ldata, k2, (int)((numLoops * stride) + j + 0), 1);
CustomData_copy_data(&mesh->ldata, &result->ldata, k1, (int)((numLoops * stride) + j + 1), 1);
CustomData_copy_data(&mesh->ldata, &result->ldata, k1, (int)((numLoops * stride) + j + 2), 1);
CustomData_copy_data(&mesh->ldata, &result->ldata, k2, (int)((numLoops * stride) + j + 3), 1);
2014-03-20 22:56:28 +11:00
if (flip == false) {
ml[j].v = ed->v1;
ml[j++].e = eidx;
ml[j].v = ed->v2;
ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v2] + newEdges;
ml[j].v = (do_shell ? ed->v2 : old_vert_arr[ed->v2]) + numVerts;
ml[j++].e = (do_shell ? eidx : i) + numEdges;
ml[j].v = (do_shell ? ed->v1 : old_vert_arr[ed->v1]) + numVerts;
ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v1] + newEdges;
}
else {
ml[j].v = ed->v2;
ml[j++].e = eidx;
ml[j].v = ed->v1;
ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v1] + newEdges;
ml[j].v = (do_shell ? ed->v1 : old_vert_arr[ed->v1]) + numVerts;
ml[j++].e = (do_shell ? eidx : i) + numEdges;
ml[j].v = (do_shell ? ed->v2 : old_vert_arr[ed->v2]) + numVerts;
ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v2] + newEdges;
}
origindex_edge[ml[j - 3].e] = ORIGINDEX_NONE;
origindex_edge[ml[j - 1].e] = ORIGINDEX_NONE;
/* use the next material index if option enabled */
if (mat_ofs_rim) {
mp->mat_nr += mat_ofs_rim;
CLAMP(mp->mat_nr, 0, mat_nr_max);
}
if (crease_outer) {
/* crease += crease_outer; without wrapping */
char *cr = &(ed->crease);
2012-05-06 13:38:33 +00:00
int tcr = *cr + crease_outer;
*cr = tcr > 255 ? 255 : tcr;
}
if (crease_inner) {
/* crease += crease_inner; without wrapping */
char *cr = &(medge[numEdges + (do_shell ? eidx : i)].crease);
int tcr = *cr + crease_inner;
*cr = tcr > 255 ? 255 : tcr;
}
#ifdef SOLIDIFY_SIDE_NORMALS
if (do_side_normals) {
normal_quad_v3(nor,
mvert[ml[j - 4].v].co,
mvert[ml[j - 3].v].co,
mvert[ml[j - 2].v].co,
mvert[ml[j - 1].v].co);
add_v3_v3(edge_vert_nos[ed->v1], nor);
add_v3_v3(edge_vert_nos[ed->v2], nor);
}
#endif
}
#ifdef SOLIDIFY_SIDE_NORMALS
if (do_side_normals) {
const MEdge *ed_orig = medge;
ed = medge + (numEdges * stride);
for (i = 0; i < rimVerts; i++, ed++, ed_orig++) {
float nor_cpy[3];
short *nor_short;
int k;
/* note, only the first vertex (lower half of the index) is calculated */
BLI_assert(ed->v1 < numVerts);
normalize_v3_v3(nor_cpy, edge_vert_nos[ed_orig->v1]);
for (k = 0; k < 2; k++) { /* loop over both verts of the edge */
nor_short = mvert[*(&ed->v1 + k)].no;
normal_short_to_float_v3(nor, nor_short);
add_v3_v3(nor, nor_cpy);
normalize_v3(nor);
normal_float_to_short_v3(nor_short, nor);
}
}
MEM_freeN(edge_vert_nos);
}
#endif
2013-06-12 06:20:24 +00:00
MEM_freeN(new_vert_arr);
MEM_freeN(new_edge_arr);
2013-06-12 06:51:02 +00:00
MEM_freeN(edge_users);
MEM_freeN(edge_order);
}
if (old_vert_arr)
MEM_freeN(old_vert_arr);
if (face_nors)
MEM_freeN(face_nors);
if (numFaces == 0 && numEdges != 0) {
modifier_setError(md, "Faces needed for useful output");
}
return result;
}
#undef SOLIDIFY_SIDE_NORMALS
static bool dependsOnNormals(ModifierData *UNUSED(md))
{
/* even when we calculate our own normals,
* the vertex normals are used as a fallback */
return true;
}
ModifierTypeInfo modifierType_Solidify = {
/* name */ "Solidify",
/* structName */ "SolidifyModifierData",
/* structSize */ sizeof(SolidifyModifierData),
/* type */ eModifierTypeType_Constructive,
2012-05-06 13:38:33 +00:00
/* flags */ eModifierTypeFlag_AcceptsMesh |
eModifierTypeFlag_AcceptsCVs |
eModifierTypeFlag_SupportsMapping |
eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode,
/* copyData */ modifier_copyData_generic,
/* deformVerts_DM */ NULL,
/* deformMatrices_DM */ NULL,
/* deformVertsEM_DM */ NULL,
/* deformMatricesEM_DM*/NULL,
/* applyModifier_DM */ NULL,
/* deformVerts */ NULL,
/* deformMatrices */ NULL,
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* applyModifier */ applyModifier,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
/* freeData */ NULL,
/* isDisabled */ NULL,
Depsgraph: New dependency graph integration commit This commit integrates the work done so far on the new dependency graph system, where goal was to replace legacy depsgraph with the new one, supporting loads of neat features like: - More granular dependency relation nature, which solves issues with fake cycles in the dependencies. - Move towards all-animatable, by better integration of drivers into the system. - Lay down some basis for upcoming copy-on-write, overrides and so on. The new system is living side-by-side with the previous one and disabled by default, so nothing will become suddenly broken. The way to enable new depsgraph is to pass `--new-depsgraph` command line argument. It's a bit early to consider the system production-ready, there are some TODOs and issues were discovered during the merge period, they'll be addressed ASAP. But it's important to merge, because it's the only way to attract artists to really start testing this system. There are number of assorted documents related on the design of the new system: * http://wiki.blender.org/index.php/User:Aligorith/GSoC2013_Depsgraph#Design_Documents * http://wiki.blender.org/index.php/User:Nazg-gul/DependencyGraph There are also some user-related information online: * http://code.blender.org/2015/02/blender-dependency-graph-branch-for-users/ * http://code.blender.org/2015/03/more-dependency-graph-tricks/ Kudos to everyone who was involved into the project: - Joshua "Aligorith" Leung -- design specification, initial code - Lukas "lukas_t" Toenne -- integrating code into blender, with further fixes - Sergey "Sergey" "Sharybin" -- some mocking around, trying to wrap up the project and so - Bassam "slikdigit" Kurdali -- stressing the new system, reporting all the issues and recording/writing documentation. - Everyone else who i forgot to mention here :)
2015-05-12 15:05:57 +05:00
/* updateDepsgraph */ NULL,
/* dependsOnTime */ NULL,
/* dependsOnNormals */ dependsOnNormals,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
/* foreachTexLink */ NULL,
};