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

886 lines
26 KiB
C
Raw Normal View History

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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.
*
* Contributor(s): Campbell Barton
* Shinsuke Irie
*
* ***** END GPL LICENSE BLOCK *****
*
*/
/** \file blender/modifiers/intern/MOD_solidify.c
* \ingroup modifiers
*/
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_math.h"
#include "BLI_edgehash.h"
#include "BLI_string.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_mesh.h"
#include "BKE_particle.h"
#include "BKE_deform.h"
#include "MOD_modifiertypes.h"
#include "MOD_util.h"
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;
static void dm_calc_normal(DerivedMesh *dm, float (*temp_nors)[3])
{
int i, numVerts, numEdges, numFaces;
MPoly *mpoly, *mp;
MLoop *mloop, *ml;
MVert *mvert, *mv;
float (*face_nors)[3];
float *f_no;
bool calc_face_nors = false;
numVerts = dm->getNumVerts(dm);
numEdges = dm->getNumEdges(dm);
numFaces = dm->getNumPolys(dm);
mpoly = dm->getPolyArray(dm);
mvert = dm->getVertArray(dm);
mloop = dm->getLoopArray(dm);
/* we don't want to overwrite any referenced layers */
2012-03-09 18:28:30 +00:00
/* Doesn't work here! */
#if 0
mv = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT, numVerts);
cddm->mvert = mv;
2012-03-09 18:28:30 +00:00
#endif
face_nors = CustomData_get_layer(&dm->polyData, CD_NORMAL);
if (!face_nors) {
calc_face_nors = true;
face_nors = CustomData_add_layer(&dm->polyData, CD_NORMAL, CD_CALLOC, NULL, numFaces);
}
mv = mvert;
mp = mpoly;
{
EdgeHash *edge_hash = BLI_edgehash_new();
EdgeHashIterator *edge_iter;
int edge_ref_count = 0;
unsigned int ed_v1, ed_v2; /* use when getting the key */
EdgeFaceRef *edge_ref_array = MEM_callocN(numEdges * sizeof(EdgeFaceRef), "Edge Connectivity");
EdgeFaceRef *edge_ref;
float edge_normal[3];
/* This loop adds an edge hash if its not there, and adds the face index */
for (i = 0; i < numFaces; i++, mp++) {
unsigned int ml_v1;
unsigned int ml_v2;
int j;
f_no = face_nors[i];
if (calc_face_nors)
BKE_mesh_calc_poly_normal(mp, mloop + mp->loopstart, mvert, f_no);
ml = mloop + mp->loopstart;
for (j = 0, ml_v2 = ml[mp->totloop - 1].v;
j < mp->totloop;
j++, ml++, ml_v2 = ml_v1)
{
ml_v1 = ml->v;
/* --- add edge ref to face --- */
edge_ref = (EdgeFaceRef *)BLI_edgehash_lookup(edge_hash, ml_v1, ml_v2);
if (!edge_ref) {
edge_ref = &edge_ref_array[edge_ref_count++];
edge_ref->f1 = i;
edge_ref->f2 = -1;
BLI_edgehash_insert(edge_hash, ml_v1, ml_v2, edge_ref);
}
else {
edge_ref->f2 = i;
}
/* --- done --- */
}
}
2012-05-06 13:38:33 +00:00
for (edge_iter = BLI_edgehashIterator_new(edge_hash);
!BLI_edgehashIterator_isDone(edge_iter);
BLI_edgehashIterator_step(edge_iter))
{
2011-01-18 01:58:19 +00:00
/* Get the edge vert indices, and edge value (the face indices that use it)*/
BLI_edgehashIterator_getKey(edge_iter, &ed_v1, &ed_v2);
edge_ref = BLI_edgehashIterator_getValue(edge_iter);
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(edge_normal);
mul_v3_fl(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(temp_nors[ed_v1], edge_normal);
add_v3_v3(temp_nors[ed_v2], edge_normal);
}
BLI_edgehashIterator_free(edge_iter);
BLI_edgehash_free(edge_hash, NULL);
MEM_freeN(edge_ref_array);
}
/* normalize vertex normals and assign */
for (i = 0; i < numVerts; i++, mv++) {
if (normalize_v3(temp_nors[i]) == 0.0f) {
normal_short_to_float_v3(temp_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 void copyData(ModifierData *md, ModifierData *target)
{
2012-05-06 13:38:33 +00:00
SolidifyModifierData *smd = (SolidifyModifierData *) md;
SolidifyModifierData *tsmd = (SolidifyModifierData *) target;
tsmd->offset = smd->offset;
tsmd->offset_fac = smd->offset_fac;
tsmd->offset_fac_vg = smd->offset_fac_vg;
tsmd->offset_clamp = smd->offset_clamp;
tsmd->crease_inner = smd->crease_inner;
tsmd->crease_outer = smd->crease_outer;
tsmd->crease_rim = smd->crease_rim;
tsmd->flag = smd->flag;
tsmd->mat_ofs = smd->mat_ofs;
tsmd->mat_ofs_rim = smd->mat_ofs_rim;
BLI_strncpy(tsmd->defgrp_name, smd->defgrp_name, sizeof(tsmd->defgrp_name));
}
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;
}
2012-11-09 09:33:28 +00:00
static DerivedMesh *applyModifier(
ModifierData *md, Object *ob,
DerivedMesh *dm,
ModifierApplyFlag UNUSED(flag))
{
int i;
DerivedMesh *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 int numVerts = dm->getNumVerts(dm);
const int numEdges = dm->getNumEdges(dm);
const int numFaces = dm->getNumPolys(dm);
2012-05-06 13:38:33 +00:00
int numLoops = 0, newLoops = 0, newFaces = 0, newEdges = 0;
/* only use material offsets if we have 2 or more materials */
2012-05-06 13:38:33 +00:00
const short mat_nr_max = ob->totcol > 1 ? ob->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 */
2013-06-12 06:20:24 +00:00
/* over-alloc new_vert_arr, old_vert_arr */
2012-05-06 13:38:33 +00:00
int *new_vert_arr = NULL;
2013-06-12 06:20:24 +00:00
STACK_DECLARE(new_vert_arr);
2012-05-06 13:38:33 +00:00
int *new_edge_arr = NULL;
2013-06-12 06:20:24 +00:00
STACK_DECLARE(new_edge_arr);
2012-05-06 13:38:33 +00:00
int *old_vert_arr = MEM_callocN(sizeof(int) * numVerts, "old_vert_arr in solidify");
2012-05-06 13:38:33 +00:00
int *edge_users = NULL;
char *edge_order = NULL;
2012-05-06 13:38:33 +00:00
float (*vert_nors)[3] = NULL;
float (*face_nors_result)[3] = NULL;
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);
/* weights */
2012-05-06 13:38:33 +00:00
MDeformVert *dvert, *dv = NULL;
const int defgrp_invert = ((smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0);
int defgrp_index;
modifier_get_vgroup(ob, dm, smd->defgrp_name, &dvert, &defgrp_index);
numLoops = dm->numLoopData;
newLoops = 0;
orig_mvert = dm->getVertArray(dm);
orig_medge = dm->getEdgeArray(dm);
orig_mloop = dm->getLoopArray(dm);
orig_mpoly = dm->getPolyArray(dm);
2013-06-12 06:20:24 +00:00
STACK_INIT(new_vert_arr);
STACK_INIT(new_edge_arr);
if (smd->flag & MOD_SOLIDIFY_RIM) {
EdgeHash *edgehash = BLI_edgehash_new();
EdgeHashIterator *ehi;
unsigned int v1, v2;
int eidx;
#define INVALID_UNUSED -1
#define INVALID_PAIR -2
2013-06-12 06:20:24 +00:00
new_vert_arr = MEM_mallocN(sizeof(*new_vert_arr) * (numVerts * 2), __func__);
new_edge_arr = MEM_mallocN(sizeof(*new_edge_arr) * ((numEdges * 2) + numVerts), __func__);
edge_users = MEM_mallocN(sizeof(*edge_users) * numEdges, "solid_mod edges");
edge_order = MEM_mallocN(sizeof(*edge_order) * numEdges, "solid_mod eorder");
2012-05-06 13:38:33 +00:00
for (i = 0, mv = orig_mvert; i < numVerts; i++, mv++) {
mv->flag &= ~ME_VERT_TMP_TAG;
}
/* save doing 2 loops here... */
#if 0
fill_vn_i(edge_users, numEdges, INVALID_UNUSED);
#endif
2012-05-06 13:38:33 +00:00
for (i = 0, ed = orig_medge; i < numEdges; i++, ed++) {
BLI_edgehash_insert(edgehash, ed->v1, ed->v2, SET_INT_IN_POINTER(i));
edge_users[i] = INVALID_UNUSED;
}
2012-05-06 13:38:33 +00:00
for (i = 0, mp = orig_mpoly; i < numFaces; i++, mp++) {
unsigned int ml_v1;
unsigned int ml_v2;
int j;
ml = orig_mloop + mp->loopstart;
for (j = 0, ml_v2 = ml[mp->totloop - 1].v;
j < mp->totloop;
j++, ml++, ml_v2 = ml_v1)
{
ml_v1 = ml->v;
/* add edge user */
eidx = GET_INT_FROM_POINTER(BLI_edgehash_lookup(edgehash, ml_v1, ml_v2));
if (edge_users[eidx] == INVALID_UNUSED) {
ed = orig_medge + eidx;
edge_users[eidx] = (ml_v1 < ml_v2) == (ed->v1 < ed->v2) ? i : (i + numFaces);
edge_order[eidx] = j;
}
else {
edge_users[eidx] = INVALID_PAIR;
}
}
}
#undef INVALID_UNUSED
#undef INVALID_PAIR
2012-05-06 13:38:33 +00:00
ehi = BLI_edgehashIterator_new(edgehash);
for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
2012-05-06 13:38:33 +00:00
eidx = GET_INT_FROM_POINTER(BLI_edgehashIterator_getValue(ehi));
if (edge_users[eidx] >= 0) {
BLI_edgehashIterator_getKey(ehi, &v1, &v2);
orig_mvert[v1].flag |= ME_VERT_TMP_TAG;
orig_mvert[v2].flag |= ME_VERT_TMP_TAG;
2013-06-12 06:20:24 +00:00
STACK_PUSH(new_edge_arr, eidx);
newFaces++;
newLoops += 4;
}
}
BLI_edgehashIterator_free(ehi);
2012-05-06 13:38:33 +00:00
for (i = 0, mv = orig_mvert; i < numVerts; i++, mv++) {
if (mv->flag & ME_VERT_TMP_TAG) {
2013-06-12 06:20:24 +00:00
old_vert_arr[i] = STACK_SIZE(new_vert_arr);
STACK_PUSH(new_vert_arr, i);
newEdges++;
mv->flag &= ~ME_VERT_TMP_TAG;
}
}
BLI_edgehash_free(edgehash, NULL);
}
if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) {
2012-05-06 13:38:33 +00:00
vert_nors = MEM_callocN(sizeof(float) * numVerts * 3, "mod_solid_vno_hq");
dm_calc_normal(dm, vert_nors);
}
2012-05-06 13:38:33 +00:00
result = CDDM_from_template(dm, numVerts * 2, (numEdges * 2) + newEdges, 0,
(numLoops * 2) + newLoops, (numFaces * 2) + newFaces);
mpoly = CDDM_get_polys(result);
mloop = CDDM_get_loops(result);
medge = CDDM_get_edges(result);
mvert = CDDM_get_verts(result);
DM_copy_edge_data(dm, result, 0, 0, numEdges);
DM_copy_edge_data(dm, result, 0, numEdges, numEdges);
DM_copy_vert_data(dm, result, 0, 0, numVerts);
DM_copy_vert_data(dm, result, 0, numVerts, numVerts);
DM_copy_loop_data(dm, result, 0, 0, numLoops);
DM_copy_loop_data(dm, result, 0, numLoops, numLoops);
DM_copy_poly_data(dm, result, 0, 0, numFaces);
DM_copy_poly_data(dm, result, 0, numFaces, numFaces);
/* if the original has it, get the result so we can update it */
face_nors_result = CustomData_get_layer(&result->polyData, CD_NORMAL);
/* flip normals */
mp = mpoly + numFaces;
2012-05-06 13:38:33 +00:00
for (i = 0; i < dm->numPolyData; i++, mp++) {
MLoop *ml2;
int e;
int j;
ml2 = mloop + mp->loopstart + dm->numLoopData;
2012-05-06 13:38:33 +00:00
for (j = 0; j < mp->totloop; j++) {
CustomData_copy_data(&dm->loopData, &result->loopData, mp->loopstart + j,
mp->loopstart + (mp->totloop - j - 1) + dm->numLoopData, 1);
}
if (mat_ofs) {
mp->mat_nr += mat_ofs;
CLAMP(mp->mat_nr, 0, mat_nr_max);
}
e = ml2[0].e;
2012-05-06 13:38:33 +00:00
for (j = 0; j < mp->totloop - 1; j++) {
ml2[j].e = ml2[j + 1].e;
}
2012-05-06 13:38:33 +00:00
ml2[mp->totloop - 1].e = e;
mp->loopstart += dm->numLoopData;
2012-05-06 13:38:33 +00:00
for (j = 0; j < mp->totloop; j++) {
ml2[j].e += numEdges;
ml2[j].v += numVerts;
}
if (face_nors_result) {
negate_v3_v3(face_nors_result[numFaces + i], face_nors_result[i]);
}
}
2012-05-06 13:38:33 +00:00
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) {
vert_lens = MEM_callocN(sizeof(float) * numVerts, "vert_lens");
fill_vn_fl(vert_lens, 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[medge[i].v1] = min_ff(vert_lens[medge[i].v1], ed_len);
vert_lens[medge[i].v2] = min_ff(vert_lens[medge[i].v2], ed_len);
}
}
if (ofs_new != 0.0f) {
2012-05-06 13:38:33 +00:00
scalar_short = scalar_short_vgroup = ofs_new / 32767.0f;
2012-05-26 09:18:47 +00:00
mv = mvert + (((ofs_new >= ofs_orig) == do_flip) ? numVerts : 0);
2012-05-06 13:38:33 +00:00
dv = dvert;
for (i = 0; i < numVerts; i++, mv++) {
if (dv) {
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;
dv++;
}
if (do_clamp) {
/* always reset becaise we may have set before */
if (dv == 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) {
2012-05-06 13:38:33 +00:00
scalar_short = scalar_short_vgroup = ofs_orig / 32767.0f;
2012-06-04 20:11:09 +00:00
mv = mvert + (((ofs_new >= ofs_orig) == do_flip) ? 0 : numVerts); /* as above but swapped */
2012-05-06 13:38:33 +00:00
dv = dvert;
for (i = 0; i < numVerts; i++, mv++) {
if (dv) {
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;
dv++;
}
if (do_clamp) {
/* always reset becaise we may have set before */
if (dv == 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 {
/* make a face normal layer if not present */
float (*face_nors)[3];
bool face_nors_calc = false;
/* same as EM_solidify() in editmesh_lib.c */
2012-05-06 13:38:33 +00:00
float *vert_angles = MEM_callocN(sizeof(float) * numVerts * 2, "mod_solid_pair"); /* 2 in 1 */
float *vert_accum = vert_angles + numVerts;
int vidx;
face_nors = CustomData_get_layer(&dm->polyData, CD_NORMAL);
if (!face_nors) {
face_nors = CustomData_add_layer(&dm->polyData, CD_NORMAL, CD_CALLOC, NULL, dm->numPolyData);
face_nors_calc = true;
}
2012-05-06 13:38:33 +00:00
if (vert_nors == NULL) {
vert_nors = MEM_mallocN(sizeof(float) * numVerts * 3, "mod_solid_vno");
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_this = mp->totloop - 1;
int i_next = 0;
ml = &mloop[mp->loopstart];
/* --- not related to angle calc --- */
if (face_nors_calc)
BKE_mesh_calc_poly_normal(mp, ml, mvert, face_nors[i]);
/* --- end non-angle-calc section --- */
sub_v3_v3v3(nor_prev, mvert[ml[i_this - 1].v].co, mvert[ml[i_this].v].co);
normalize_v3(nor_prev);
while (i_next < mp->totloop) {
float angle;
sub_v3_v3v3(nor_next, mvert[ml[i_this].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_this].v;
vert_accum[vidx] += angle;
vert_angles[vidx] += shell_angle_to_dist(angle_normalized_v3v3(vert_nors[vidx], face_nors[i])) * angle;
/* --- end non-angle-calc section --- */
/* step */
copy_v3_v3(nor_prev, nor_next);
i_this = i_next;
i_next++;
}
}
/* vertex group support */
if (dvert) {
float scalar;
2012-05-06 13:38:33 +00:00
dv = dvert;
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 = MEM_callocN(sizeof(float) * numVerts, "vert_lens");
const float offset = fabsf(smd->offset) * smd->offset_clamp;
const float offset_sq = offset * offset;
fill_vn_fl(vert_lens, 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[medge[i].v1] = min_ff(vert_lens[medge[i].v1], ed_len);
vert_lens[medge[i].v2] = min_ff(vert_lens[medge[i].v2], ed_len);
}
for (i = 0; i < numVerts; i++) {
if (vert_lens[i] < offset_sq) {
float scalar = sqrtf(vert_lens[i]) / offset;
vert_angles[i] *= scalar;
}
}
MEM_freeN(vert_lens);
}
if (ofs_new) {
2012-05-26 09:18:47 +00:00
mv = mvert + (((ofs_new >= ofs_orig) == do_flip) ? numVerts : 0);
2012-05-06 13:38:33 +00:00
for (i = 0; i < numVerts; i++, mv++) {
if (vert_accum[i]) { /* zero if unselected */
madd_v3_v3fl(mv->co, vert_nors[i], ofs_new * (vert_angles[i] / vert_accum[i]));
}
}
}
if (ofs_orig) {
2012-05-06 13:38:33 +00:00
/* same as above but swapped, intentional use of 'ofs_new' */
2012-05-26 09:18:47 +00:00
mv = mvert + (((ofs_new >= ofs_orig) == do_flip) ? 0 : numVerts);
2012-05-06 13:38:33 +00:00
for (i = 0; i < numVerts; i++, mv++) {
if (vert_accum[i]) { /* zero if unselected */
madd_v3_v3fl(mv->co, vert_nors[i], ofs_orig * (vert_angles[i] / vert_accum[i]));
}
}
}
MEM_freeN(vert_angles);
}
if (vert_nors)
MEM_freeN(vert_nors);
/* must recalculate normals with vgroups since they can displace unevenly [#26888] */
if ((dm->dirty & DM_DIRTY_NORMALS) || (smd->flag & MOD_SOLIDIFY_RIM) || dvert) {
result->dirty |= DM_DIRTY_NORMALS;
}
else {
/* 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) {
/* 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
const bool do_side_normals = !(result->dirty & DM_DIRTY_NORMALS);
/* annoying to allocate these since we only need the edge verts, */
float (*edge_vert_nos)[3] = do_side_normals ? MEM_callocN(sizeof(float) * numVerts * 3, __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;
int j;
if (crease_rim || crease_outer || crease_inner) {
result->cd_flag |= ME_CDFLAG_EDGE_CREASE;
}
/* add faces & edges */
origindex_edge = result->getEdgeDataArray(result, CD_ORIGINDEX);
ed = &medge[numEdges * 2];
orig_ed = &origindex_edge[numEdges * 2];
for (i = 0; i < newEdges; i++, ed++, orig_ed++) {
2012-05-06 13:38:33 +00:00
ed->v1 = new_vert_arr[i];
ed->v2 = new_vert_arr[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 * 2);
ml = mloop + (numLoops * 2);
j = 0;
2012-05-06 13:38:33 +00:00
for (i = 0; i < newFaces; i++, mp++) {
int eidx = new_edge_arr[i];
int fidx = edge_users[eidx];
int flip, k1, k2;
if (fidx >= numFaces) {
fidx -= numFaces;
flip = TRUE;
}
else {
flip = FALSE;
}
2012-05-06 13:38:33 +00:00
ed = medge + eidx;
/* copy most of the face settings */
DM_copy_poly_data(dm, result, fidx, (numFaces * 2) + i, 1);
2012-05-06 13:38:33 +00:00
mp->loopstart = j + numLoops * 2;
mp->flag = mpoly[fidx].flag;
/* notice we use 'mp->totloop' which is later overwritten,
* we could lookup the original face but theres 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 */
k2 = mpoly[fidx].loopstart + (edge_order[eidx]);
mp->totloop = 4;
CustomData_copy_data(&dm->loopData, &result->loopData, k2, numLoops * 2 + j + 0, 1);
CustomData_copy_data(&dm->loopData, &result->loopData, k1, numLoops * 2 + j + 1, 1);
CustomData_copy_data(&dm->loopData, &result->loopData, k1, numLoops * 2 + j + 2, 1);
CustomData_copy_data(&dm->loopData, &result->loopData, k2, numLoops * 2 + j + 3, 1);
if (flip == FALSE) {
ml[j].v = ed->v1;
ml[j++].e = eidx;
ml[j].v = ed->v2;
2012-05-06 13:38:33 +00:00
ml[j++].e = numEdges * 2 + old_vert_arr[ed->v2];
2012-05-06 13:38:33 +00:00
ml[j].v = ed->v2 + numVerts;
ml[j++].e = eidx + numEdges;
2012-05-06 13:38:33 +00:00
ml[j].v = ed->v1 + numVerts;
ml[j++].e = numEdges * 2 + old_vert_arr[ed->v1];
}
else {
ml[j].v = ed->v2;
ml[j++].e = eidx;
ml[j].v = ed->v1;
2012-05-06 13:38:33 +00:00
ml[j++].e = numEdges * 2 + old_vert_arr[ed->v1];
2012-05-06 13:38:33 +00:00
ml[j].v = ed->v1 + numVerts;
ml[j++].e = eidx + numEdges;
ml[j].v = ed->v2 + numVerts;
ml[j++].e = numEdges * 2 + old_vert_arr[ed->v2];
}
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 */
2012-05-06 13:38:33 +00:00
unsigned char *cr = (unsigned char *)&(ed->crease);
int tcr = *cr + crease_outer;
*cr = tcr > 255 ? 255 : tcr;
}
if (crease_inner) {
/* crease += crease_inner; without wrapping */
unsigned char *cr = (unsigned char *)&(medge[numEdges + eidx].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);
if (face_nors_result) {
copy_v3_v3(face_nors_result[(numFaces * 2) + i], nor);
}
}
#endif
}
#ifdef SOLIDIFY_SIDE_NORMALS
if (do_side_normals) {
ed = medge + (numEdges * 2);
for (i = 0; i < newEdges; i++, ed++) {
float nor_cpy[3];
short *nor_short;
int k;
/* note, only the first vertex (lower half of the index) is calculated */
normalize_v3_v3(nor_cpy, edge_vert_nos[ed->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);
MEM_freeN(edge_users);
MEM_freeN(edge_order);
}
if (old_vert_arr)
MEM_freeN(old_vert_arr);
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 */ copyData,
/* deformVerts */ NULL,
/* deformMatrices */ NULL,
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* applyModifier */ applyModifier,
/* applyModifierEM */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
/* freeData */ NULL,
/* isDisabled */ NULL,
/* updateDepgraph */ NULL,
/* dependsOnTime */ NULL,
/* dependsOnNormals */ dependsOnNormals,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
/* foreachTexLink */ NULL,
};