Add Custom Loop Normals.

This is the core code for it, tools (datatransfer and modifier) will come in next commits).
RNA api is already there, though.

See the code for details, but basically, we define, for each 'smooth fan'
(which is a set of adjacent loops around a same vertex that are smooth, i.e. have a single same normal),
a 'loop normal space' (or lnor space), using auto-computed normal and relevant edges, and store
custom normal as two angular factors inside that space. This allows to have custom normals
'following' deformations of the geometry, and to only save two shorts per loop in new clnor CDLayer.

Normal manipulation (editing, mixing, interpolating, etc.) shall always happen with plain 3D vectors normals,
and be converted back into storage format at the end.

Clnor computation has also been threaded (at least for Mesh case, not for BMesh), since the process can
be rather heavy with high poly meshes.

Also, bumping subversion, and fix mess in 2.70 versioning code.
This commit is contained in:
2015-02-05 14:24:48 +01:00
parent 7bae9ee6b6
commit 138c9dba9b
23 changed files with 1709 additions and 324 deletions

View File

@@ -313,7 +313,7 @@ static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector<
int n = (vi[3] == 0)? 3: 4;
int mi = clamp(f->material_index(), 0, used_shaders.size()-1);
int shader = used_shaders[mi];
bool smooth = f->use_smooth();
bool smooth = f->use_smooth() || use_loop_normals;
/* split vertices if normal is different
*

View File

@@ -154,7 +154,7 @@ class DATA_PT_normals(MeshButtonsPanel, Panel):
col = split.column()
col.prop(mesh, "use_auto_smooth")
sub = col.column()
sub.active = mesh.use_auto_smooth
sub.active = mesh.use_auto_smooth and not mesh.has_custom_normals
sub.prop(mesh, "auto_smooth_angle", text="Angle")
split.prop(mesh, "show_double_sided")
@@ -372,6 +372,11 @@ class DATA_PT_customdata(MeshButtonsPanel, Panel):
col.operator("mesh.customdata_clear_mask", icon='X')
col.operator("mesh.customdata_clear_skin", icon='X')
if me.has_custom_normals:
col.operator("mesh.customdata_custom_splitnormals_clear", icon='X')
else:
col.operator("mesh.customdata_custom_splitnormals_add", icon='ZOOMIN')
col = layout.column()
col.enabled = (obj.mode != 'EDIT')

View File

@@ -88,6 +88,7 @@ struct MTFace;
struct Object;
struct Scene;
struct Mesh;
struct MLoopNorSpaceArray;
struct BMEditMesh;
struct KeyBlock;
struct ModifierData;
@@ -199,6 +200,10 @@ struct DerivedMesh {
/** Calculate loop (split) normals */
void (*calcLoopNormals)(DerivedMesh *dm, const bool use_split_normals, const float split_angle);
/** Calculate loop (split) normals, and returns split loop normal spacearr. */
void (*calcLoopNormalsSpaceArray)(DerivedMesh *dm, const bool use_split_normals, const float split_angle,
struct MLoopNorSpaceArray *r_lnors_spacearr);
/** Recalculates mesh tessellation */
void (*recalcTessellation)(DerivedMesh *dm);

View File

@@ -42,7 +42,7 @@ extern "C" {
* and keep comment above the defines.
* Use STRINGIFY() rather than defining with quotes */
#define BLENDER_VERSION 273
#define BLENDER_SUBVERSION 5
#define BLENDER_SUBVERSION 6
/* 262 was the last editmesh release but it has compatibility code for bmesh data */
#define BLENDER_MINVERSION 270
#define BLENDER_MINSUBVERSION 5

View File

@@ -40,6 +40,7 @@
struct DerivedMesh;
struct BMEditMesh;
struct Mesh;
struct MLoopNorSpaceArray;
struct Object;
/* creates a new CDDerivedMesh */
@@ -107,6 +108,8 @@ void CDDM_calc_normals(struct DerivedMesh *dm);
void CDDM_calc_normals_tessface(struct DerivedMesh *dm);
void CDDM_calc_loop_normals(struct DerivedMesh *dm, const bool use_split_normals, const float split_angle);
void CDDM_calc_loop_normals_spacearr(struct DerivedMesh *dm, const bool use_split_normals, const float split_angle,
struct MLoopNorSpaceArray *r_lnors_spacearr);
/* calculates edges for a CDDerivedMesh (from face data)
* this completely replaces the current edge data in the DerivedMesh

View File

@@ -36,6 +36,9 @@ struct BoundBox;
struct DispList;
struct EdgeHash;
struct ListBase;
struct LinkNode;
struct BLI_Stack;
struct MemArena;
struct BMEditMesh;
struct BMesh;
struct Main;
@@ -174,11 +177,6 @@ void BKE_mesh_calc_normals_tessface(
struct MVert *mverts, int numVerts,
struct MFace *mfaces, int numFaces,
float (*r_faceNors)[3]);
void BKE_mesh_normals_loop_split(
struct MVert *mverts, const int numVerts, struct MEdge *medges, const int numEdges,
struct MLoop *mloops, float (*r_loopnors)[3], const int numLoops,
struct MPoly *mpolys, float (*polynors)[3], const int numPolys,
const bool use_split_normals, float split_angle);
void BKE_mesh_loop_tangents_ex(
struct MVert *mverts, const int numVerts, struct MLoop *mloops, float (*r_looptangent)[4], float (*loopnors)[3],
struct MLoopUV *loopuv, const int numLoops, struct MPoly *mpolys, const int numPolys,
@@ -186,6 +184,56 @@ void BKE_mesh_loop_tangents_ex(
void BKE_mesh_loop_tangents(
struct Mesh *mesh, const char *uvmap, float (*r_looptangents)[4], struct ReportList *reports);
/**
* References a contiguous loop-fan with normal offset vars.
*/
typedef struct MLoopNorSpace {
float vec_lnor[3]; /* Automatically computed loop normal. */
float vec_ref[3]; /* Reference vector, orthogonal to vec_lnor. */
float vec_ortho[3]; /* Third vector, orthogonal to vec_lnor and vec_ref. */
float ref_alpha; /* Reference angle, around vec_ortho, in ]0, pi] range (0.0 marks that space as invalid). */
float ref_beta; /* Reference angle, around vec_lnor, in ]0, 2pi] range (0.0 marks that space as invalid). */
struct LinkNode *loops; /* All indices (uint_in_ptr) of loops using this lnor space (i.e. smooth fan of loops). */
} MLoopNorSpace;
/**
* Collection of #MLoopNorSpace basic storage & pre-allocation.
*/
typedef struct MLoopNorSpaceArray {
MLoopNorSpace **lspacearr; /* MLoop aligned array */
struct LinkNode *loops_pool; /* Allocated once, avoids to call BLI_linklist_prepend_arena() for each loop! */
struct MemArena *mem;
} MLoopNorSpaceArray;
void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, const int numLoops);
void BKE_lnor_spacearr_clear(MLoopNorSpaceArray *lnors_spacearr);
void BKE_lnor_spacearr_free(MLoopNorSpaceArray *lnors_spacearr);
MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr);
void BKE_lnor_space_define(
MLoopNorSpace *lnor_space, const float lnor[3], float vec_ref[3], float vec_other[3],
struct BLI_Stack *edge_vectors);
void BKE_lnor_space_add_loop(
MLoopNorSpaceArray *lnors_spacearr, MLoopNorSpace *lnor_space, const int ml_index, const bool add_to_list);
void BKE_lnor_space_custom_data_to_normal(MLoopNorSpace *lnor_space, const short clnor_data[2], float r_custom_lnor[3]);
void BKE_lnor_space_custom_normal_to_data(MLoopNorSpace *lnor_space, const float custom_lnor[3], short r_clnor_data[2]);
bool BKE_mesh_has_custom_loop_normals(struct Mesh *me);
void BKE_mesh_normals_loop_split(struct MVert *mverts, const int numVerts, struct MEdge *medges, const int numEdges,
struct MLoop *mloops, float (*r_loopnors)[3], const int numLoops,
struct MPoly *mpolys, const float (*polynors)[3], const int numPolys,
const bool use_split_normals, float split_angle,
MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], int *r_loop_to_poly);
void BKE_mesh_normals_loop_custom_set(
struct MVert *mverts, const int numVerts, struct MEdge *medges, const int numEdges,
struct MLoop *mloops, float (*custom_loopnors)[3], const int numLoops,
struct MPoly *mpolys, const float (*polynors)[3], const int numPolys,
short (*r_clnors_data)[2]);
void BKE_mesh_normals_loop_custom_from_vertices_set(
struct MVert *mverts, float (*custom_vertnors)[3], const int numVerts,
struct MEdge *medges, const int numEdges, struct MLoop *mloops, const int numLoops,
struct MPoly *mpolys, const float (*polynors)[3], const int numPolys,
short (*r_clnors_data)[2]);
void BKE_mesh_calc_poly_normal(
struct MPoly *mpoly, struct MLoop *loopstart,
struct MVert *mvarray, float no[3]);

View File

@@ -1540,6 +1540,7 @@ static CDDerivedMesh *cdDM_create(const char *desc)
dm->calcNormals = CDDM_calc_normals;
dm->calcLoopNormals = CDDM_calc_loop_normals;
dm->calcLoopNormalsSpaceArray = CDDM_calc_loop_normals_spacearr;
dm->recalcTessellation = CDDM_recalc_tessellation;
dm->getVertCos = cdDM_getVertCos;
@@ -2157,6 +2158,14 @@ void CDDM_calc_normals(DerivedMesh *dm)
#endif
void CDDM_calc_loop_normals(DerivedMesh *dm, const bool use_split_normals, const float split_angle)
{
CDDM_calc_loop_normals_spacearr(dm, use_split_normals, split_angle, NULL);
}
/* #define DEBUG_CLNORS */
void CDDM_calc_loop_normals_spacearr(
DerivedMesh *dm, const bool use_split_normals, const float split_angle, MLoopNorSpaceArray *r_lnors_spacearr)
{
MVert *mverts = dm->getVertArray(dm);
MEdge *medges = dm->getEdgeArray(dm);
@@ -2166,6 +2175,7 @@ void CDDM_calc_loop_normals(DerivedMesh *dm, const bool use_split_normals, const
CustomData *ldata, *pdata;
float (*lnors)[3];
short (*clnor_data)[2];
float (*pnors)[3];
const int numVerts = dm->getNumVerts(dm);
@@ -2193,8 +2203,37 @@ void CDDM_calc_loop_normals(DerivedMesh *dm, const bool use_split_normals, const
dm->dirty &= ~DM_DIRTY_NORMALS;
clnor_data = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL);
BKE_mesh_normals_loop_split(mverts, numVerts, medges, numEdges, mloops, lnors, numLoops,
mpolys, pnors, numPolys, use_split_normals, split_angle);
mpolys, (const float (*)[3])pnors, numPolys,
use_split_normals, split_angle,
r_lnors_spacearr, clnor_data, NULL);
#ifdef DEBUG_CLNORS
if (r_lnors_spacearr) {
int i;
for (i = 0; i < numLoops; i++) {
if (r_lnors_spacearr->lspacearr[i]->ref_alpha != 0.0f) {
LinkNode *loops = r_lnors_spacearr->lspacearr[i]->loops;
printf("Loop %d uses lnor space %p:\n", i, r_lnors_spacearr->lspacearr[i]);
print_v3("\tfinal lnor", lnors[i]);
print_v3("\tauto lnor", r_lnors_spacearr->lspacearr[i]->vec_lnor);
print_v3("\tref_vec", r_lnors_spacearr->lspacearr[i]->vec_ref);
printf("\talpha: %f\n\tbeta: %f\n\tloops: %p\n", r_lnors_spacearr->lspacearr[i]->ref_alpha,
r_lnors_spacearr->lspacearr[i]->ref_beta, r_lnors_spacearr->lspacearr[i]->loops);
printf("\t\t(shared with loops");
while (loops) {
printf(" %d", GET_INT_FROM_POINTER(loops->link));
loops = loops->next;
}
printf(")\n");
}
else {
printf("Loop %d has no lnor space\n", i);
}
}
}
#endif
}

View File

@@ -1255,6 +1255,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{sizeof(float[4]), "", 0, NULL, NULL, NULL, NULL, NULL, NULL},
/* 40: CD_TESSLOOPNORMAL */
{sizeof(short[4][3]), "", 0, NULL, NULL, NULL, NULL, layerSwap_flnor, NULL},
/* 41: CD_CUSTOMLOOPNORMAL */
{sizeof(short[2]), "vec2s", 1, NULL, NULL, NULL, NULL, NULL, NULL},
};
/* note, numbers are from trunk and need updating for bmesh */
@@ -1270,7 +1272,8 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
/* 25-29 */ "CDMPoly", "CDMLoop", "CDShapeKeyIndex", "CDShapeKey", "CDBevelWeight",
/* 30-34 */ "CDSubSurfCrease", "CDOrigSpaceLoop", "CDPreviewLoopCol", "CDBMElemPyPtr", "CDPaintMask",
/* 35-36 */ "CDGridPaintMask", "CDMVertSkin",
/* 37-40 */ "CDFreestyleEdge", "CDFreestyleFace", "CDMLoopTangent", "CDTessLoopNormal",
/* 37-38 */ "CDFreestyleEdge", "CDFreestyleFace",
/* 39-41 */ "CDMLoopTangent", "CDTessLoopNormal", "CDCustomLoopNormal",
};
@@ -1282,26 +1285,29 @@ const CustomDataMask CD_MASK_MESH =
CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_MDISPS |
CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MPOLY | CD_MASK_MLOOP |
CD_MASK_MTEXPOLY | CD_MASK_RECAST | CD_MASK_PAINT_MASK |
CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE;
CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE |
CD_MASK_CUSTOMLOOPNORMAL;
const CustomDataMask CD_MASK_EDITMESH =
CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE | CD_MASK_MLOOPUV |
CD_MASK_MLOOPCOL | CD_MASK_MTEXPOLY | CD_MASK_SHAPE_KEYINDEX |
CD_MASK_MCOL | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR |
CD_MASK_MDISPS | CD_MASK_SHAPEKEY | CD_MASK_RECAST | CD_MASK_PAINT_MASK |
CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN;
CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_CUSTOMLOOPNORMAL;
const CustomDataMask CD_MASK_DERIVEDMESH =
CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE |
CD_MASK_MCOL | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_CLOTH_ORCO |
CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MTEXPOLY | CD_MASK_PREVIEW_MLOOPCOL |
CD_MASK_PROP_STR | CD_MASK_ORIGSPACE | CD_MASK_ORIGSPACE_MLOOP | CD_MASK_ORCO | CD_MASK_TANGENT |
CD_MASK_PREVIEW_MCOL | CD_MASK_SHAPEKEY | CD_MASK_RECAST |
CD_MASK_ORIGINDEX | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE;
CD_MASK_ORIGINDEX | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE |
CD_MASK_CUSTOMLOOPNORMAL;
const CustomDataMask CD_MASK_BMESH =
CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MTEXPOLY |
CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_PROP_FLT | CD_MASK_PROP_INT |
CD_MASK_PROP_STR | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_MDISPS |
CD_MASK_CREASE | CD_MASK_BWEIGHT | CD_MASK_RECAST | CD_MASK_PAINT_MASK |
CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE;
CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE |
CD_MASK_CUSTOMLOOPNORMAL;
const CustomDataMask CD_MASK_FACECORNERS = /* XXX Not used anywhere! */
CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_MTEXPOLY | CD_MASK_MLOOPUV |
CD_MASK_MLOOPCOL | CD_MASK_NORMAL | CD_MASK_MLOOPTANGENT;
@@ -1316,7 +1322,7 @@ const CustomDataMask CD_MASK_EVERYTHING =
/* BMESH ONLY END */
CD_MASK_PAINT_MASK | CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN |
CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE |
CD_MASK_MLOOPTANGENT | CD_MASK_TESSLOOPNORMAL;
CD_MASK_MLOOPTANGENT | CD_MASK_TESSLOOPNORMAL | CD_MASK_CUSTOMLOOPNORMAL;
static const LayerTypeInfo *layerType_getInfo(int type)
{

View File

@@ -169,12 +169,25 @@ static void emDM_calcNormals(DerivedMesh *dm)
dm->dirty &= ~DM_DIRTY_NORMALS;
}
static void emDM_calcLoopNormalsSpaceArray(
DerivedMesh *dm, const bool use_split_normals, const float split_angle, MLoopNorSpaceArray *r_lnors_spacearr);
static void emDM_calcLoopNormals(DerivedMesh *dm, const bool use_split_normals, const float split_angle)
{
emDM_calcLoopNormalsSpaceArray(dm, use_split_normals, split_angle, NULL);
}
/* #define DEBUG_CLNORS */
static void emDM_calcLoopNormalsSpaceArray(
DerivedMesh *dm, const bool use_split_normals, const float split_angle, MLoopNorSpaceArray *r_lnors_spacearr)
{
EditDerivedBMesh *bmdm = (EditDerivedBMesh *)dm;
BMesh *bm = bmdm->em->bm;
const float (*vertexCos)[3], (*vertexNos)[3], (*polyNos)[3];
float (*loopNos)[3];
short (*clnors_data)[2];
int cd_loop_clnors_offset;
/* calculate loop normals from poly and vertex normals */
emDM_ensureVertNormals(bmdm);
@@ -191,7 +204,37 @@ static void emDM_calcLoopNormals(DerivedMesh *dm, const bool use_split_normals,
loopNos = dm->getLoopDataArray(dm, CD_NORMAL);
}
BM_loops_calc_normal_vcos(bm, vertexCos, vertexNos, polyNos, use_split_normals, split_angle, loopNos);
/* We can have both, give priority to dm's data, and fallback to bm's ones. */
clnors_data = dm->getLoopDataArray(dm, CD_CUSTOMLOOPNORMAL);
cd_loop_clnors_offset = clnors_data ? -1 : CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
BM_loops_calc_normal_vcos(bm, vertexCos, vertexNos, polyNos, use_split_normals, split_angle, loopNos,
r_lnors_spacearr, clnors_data, cd_loop_clnors_offset);
#ifdef DEBUG_CLNORS
if (r_lnors_spacearr) {
int i;
for (i = 0; i < numLoops; i++) {
if (r_lnors_spacearr->lspacearr[i]->ref_alpha != 0.0f) {
LinkNode *loops = r_lnors_spacearr->lspacearr[i]->loops;
printf("Loop %d uses lnor space %p:\n", i, r_lnors_spacearr->lspacearr[i]);
print_v3("\tfinal lnor:", loopNos[i]);
print_v3("\tauto lnor:", r_lnors_spacearr->lspacearr[i]->vec_lnor);
print_v3("\tref_vec:", r_lnors_spacearr->lspacearr[i]->vec_ref);
printf("\talpha: %f\n\tbeta: %f\n\tloops: %p\n", r_lnors_spacearr->lspacearr[i]->ref_alpha,
r_lnors_spacearr->lspacearr[i]->ref_beta, r_lnors_spacearr->lspacearr[i]->loops);
printf("\t\t(shared with loops");
while (loops) {
printf(" %d", GET_INT_FROM_POINTER(loops->link));
loops = loops->next;
}
printf(")\n");
}
else {
printf("Loop %d has no lnor space\n", i);
}
}
}
#endif
}
static void emDM_recalcTessellation(DerivedMesh *UNUSED(dm))
@@ -1764,6 +1807,7 @@ DerivedMesh *getEditDerivedBMesh(BMEditMesh *em,
bmdm->dm.calcNormals = emDM_calcNormals;
bmdm->dm.calcLoopNormals = emDM_calcLoopNormals;
bmdm->dm.calcLoopNormalsSpaceArray = emDM_calcLoopNormalsSpaceArray;
bmdm->dm.recalcTessellation = emDM_recalcTessellation;
bmdm->dm.foreachMappedVert = emDM_foreachMappedVert;

View File

@@ -414,6 +414,16 @@ void BKE_mesh_update_customdata_pointers(Mesh *me, const bool do_ensure_tess_cd)
me->mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV);
}
bool BKE_mesh_has_custom_loop_normals(Mesh *me)
{
if (me->edit_btmesh) {
return CustomData_has_layer(&me->edit_btmesh->bm->ldata, CD_CUSTOMLOOPNORMAL);
}
else {
return CustomData_has_layer(&me->ldata, CD_CUSTOMLOOPNORMAL);
}
}
/* Note: unlinking is called when me->id.us is 0, question remains how
* much unlinking of Library data in Mesh should be done... probably
* we need a more generic method, like the expand() functions in

File diff suppressed because it is too large Load Diff

View File

@@ -1047,6 +1047,8 @@ void BKE_mesh_remap_calc_loops_from_dm(
}
}
if (need_lnors_dst) {
short (*custom_nors_dst)[2] = CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL);
/* Cache poly nors into a temp CDLayer. */
loop_nors_dst = CustomData_get_layer(ldata_dst, CD_NORMAL);
if (dirty_nors_dst || !loop_nors_dst) {
@@ -1056,8 +1058,8 @@ void BKE_mesh_remap_calc_loops_from_dm(
}
BKE_mesh_normals_loop_split(verts_dst, numverts_dst, edges_dst, numedges_dst,
loops_dst, loop_nors_dst, numloops_dst,
polys_dst, poly_nors_dst, numpolys_dst,
use_split_nors_dst, split_angle_dst);
polys_dst, (const float (*)[3])poly_nors_dst, numpolys_dst,
use_split_nors_dst, split_angle_dst, NULL, custom_nors_dst, NULL);
}
}
if (need_pnors_src || need_lnors_src) {

View File

@@ -3465,6 +3465,7 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss,
ccgdm->dm.calcNormals = ccgDM_calcNormals;
ccgdm->dm.calcLoopNormals = CDDM_calc_loop_normals;
ccgdm->dm.calcLoopNormalsSpaceArray = CDDM_calc_loop_normals_spacearr;
ccgdm->dm.recalcTessellation = ccgDM_recalcTessellation;
ccgdm->dm.getVertCos = ccgdm_getVertCos;

View File

@@ -473,66 +473,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
if (!DNA_struct_elem_find(fd->filesdna, "ClothSimSettings", "float", "bending_damping")) {
Object *ob;
ModifierData *md;
for (ob = main->object.first; ob; ob = ob->id.next) {
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Cloth) {
ClothModifierData *clmd = (ClothModifierData *)md;
clmd->sim_parms->bending_damping = 0.5f;
}
else if (md->type == eModifierType_ParticleSystem) {
ParticleSystemModifierData *pmd = (ParticleSystemModifierData *)md;
if (pmd->psys->clmd) {
pmd->psys->clmd->sim_parms->bending_damping = 0.5f;
}
}
}
}
}
if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "float", "clump_noise_size")) {
ParticleSettings *part;
for (part = main->particle.first; part; part = part->id.next) {
part->clump_noise_size = 1.0f;
}
}
if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "int", "kink_extra_steps")) {
ParticleSettings *part;
for (part = main->particle.first; part; part = part->id.next) {
part->kink_extra_steps = 4;
}
}
if (!DNA_struct_elem_find(fd->filesdna, "MTex", "float", "kinkampfac")) {
ParticleSettings *part;
for (part = main->particle.first; part; part = part->id.next) {
int a;
for (a = 0; a < MAX_MTEX; a++) {
MTex *mtex = part->mtex[a];
if (mtex) {
mtex->kinkampfac = 1.0f;
}
}
}
}
if (!DNA_struct_elem_find(fd->filesdna, "HookModifierData", "char", "flag")) {
Object *ob;
for (ob = main->object.first; ob; ob = ob->id.next) {
ModifierData *md;
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Hook) {
HookModifierData *hmd = (HookModifierData *)md;
hmd->falloff_type = eHook_Falloff_InvSquare;
}
}
}
}
if (!MAIN_VERSION_ATLEAST(main, 273, 3)) {
ParticleSettings *part;
for (part = main->particle.first; part; part = part->id.next) {
@@ -543,20 +483,82 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
if (!DNA_struct_elem_find(fd->filesdna, "NodePlaneTrackDeformData", "char", "flag")) {
FOREACH_NODETREE(main, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
bNode *node;
for (node = ntree->nodes.first; node; node = node->next) {
if (ELEM(node->type, CMP_NODE_PLANETRACKDEFORM)) {
NodePlaneTrackDeformData *data = node->storage;
data->flag = 0;
data->motion_blur_samples = 16;
data->motion_blur_shutter = 0.5f;
if (!MAIN_VERSION_ATLEAST(main, 273, 6)) {
if (!DNA_struct_elem_find(fd->filesdna, "ClothSimSettings", "float", "bending_damping")) {
Object *ob;
ModifierData *md;
for (ob = main->object.first; ob; ob = ob->id.next) {
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Cloth) {
ClothModifierData *clmd = (ClothModifierData *)md;
clmd->sim_parms->bending_damping = 0.5f;
}
else if (md->type == eModifierType_ParticleSystem) {
ParticleSystemModifierData *pmd = (ParticleSystemModifierData *)md;
if (pmd->psys->clmd) {
pmd->psys->clmd->sim_parms->bending_damping = 0.5f;
}
}
}
}
}
FOREACH_NODETREE_END
if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "float", "clump_noise_size")) {
ParticleSettings *part;
for (part = main->particle.first; part; part = part->id.next) {
part->clump_noise_size = 1.0f;
}
}
if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "int", "kink_extra_steps")) {
ParticleSettings *part;
for (part = main->particle.first; part; part = part->id.next) {
part->kink_extra_steps = 4;
}
}
if (!DNA_struct_elem_find(fd->filesdna, "MTex", "float", "kinkampfac")) {
ParticleSettings *part;
for (part = main->particle.first; part; part = part->id.next) {
int a;
for (a = 0; a < MAX_MTEX; a++) {
MTex *mtex = part->mtex[a];
if (mtex) {
mtex->kinkampfac = 1.0f;
}
}
}
}
if (!DNA_struct_elem_find(fd->filesdna, "HookModifierData", "char", "flag")) {
Object *ob;
for (ob = main->object.first; ob; ob = ob->id.next) {
ModifierData *md;
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Hook) {
HookModifierData *hmd = (HookModifierData *)md;
hmd->falloff_type = eHook_Falloff_InvSquare;
}
}
}
}
if (!DNA_struct_elem_find(fd->filesdna, "NodePlaneTrackDeformData", "char", "flag")) {
FOREACH_NODETREE(main, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
bNode *node;
for (node = ntree->nodes.first; node; node = node->next) {
if (ELEM(node->type, CMP_NODE_PLANETRACKDEFORM)) {
NodePlaneTrackDeformData *data = node->storage;
data->flag = 0;
data->motion_blur_samples = 16;
data->motion_blur_shutter = 0.5f;
}
}
}
}
FOREACH_NODETREE_END
}
}
}

View File

@@ -34,10 +34,12 @@
#include "BLI_linklist_stack.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_stack.h"
#include "BLI_utildefines.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_editmesh.h"
#include "BKE_mesh.h"
#include "BKE_multires.h"
#include "intern/bmesh_private.h"
@@ -438,7 +440,8 @@ void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (*
static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], float split_angle,
float (*r_lnos)[3])
{
BMIter eiter;
BMIter eiter, viter;
BMVert *v;
BMEdge *e;
int i;
@@ -450,15 +453,18 @@ static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const flo
{
char htype = BM_LOOP;
if (vnos) {
htype |= BM_VERT;
}
if (fnos) {
htype |= BM_FACE;
}
BM_mesh_elem_index_ensure(bm, htype);
}
/* Clear all vertices' tags (means they are all smooth for now). */
BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
BM_elem_index_set(v, i); /* set_inline */
BM_elem_flag_disable(v, BM_ELEM_TAG);
}
/* This first loop checks which edges are actually smooth, and pre-populate lnos with vnos (as if they were
* all smooth).
*/
@@ -481,13 +487,13 @@ static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const flo
* If the angle between both its polys' normals is below split_angle value,
* and it is tagged as such,
* and both its faces are smooth,
* and both its faces have compatible (non-flipped) normals, i.e. both loops on the same edge do not share
* the same vertex.
* and both its faces have compatible (non-flipped) normals,
* i.e. both loops on the same edge do not share the same vertex.
*/
if (is_angle_smooth &&
BM_elem_flag_test_bool(e, BM_ELEM_SMOOTH) &&
BM_elem_flag_test_bool(l_a->f, BM_ELEM_SMOOTH) &&
BM_elem_flag_test_bool(l_b->f, BM_ELEM_SMOOTH) &&
BM_elem_flag_test(e, BM_ELEM_SMOOTH) &&
BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) &&
BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH) &&
l_a->v != l_b->v)
{
const float *no;
@@ -499,20 +505,40 @@ static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const flo
no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no;
copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no);
}
else {
/* Sharp edge, tag its verts as such. */
BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
}
}
else {
/* Sharp edge, tag its verts as such. */
BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
}
}
bm->elem_index_dirty &= ~BM_EDGE;
bm->elem_index_dirty &= ~(BM_EDGE | BM_VERT);
}
/* BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c */
static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const float (*fnos)[3], float (*r_lnos)[3])
/* BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c
* Will use first clnors_data array, and fallback to cd_loop_clnors_offset (use NULL and -1 to not use clnors). */
static void bm_mesh_loops_calc_normals(
BMesh *bm, const float (*vcos)[3], const float (*fnos)[3], float (*r_lnos)[3],
MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset)
{
BMIter fiter;
BMFace *f_curr;
const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
MLoopNorSpaceArray _lnors_spacearr = {NULL};
/* Temp normal stack. */
BLI_SMALLSTACK_DECLARE(normal, float *);
/* Temp clnors stack. */
BLI_SMALLSTACK_DECLARE(clnors, short *);
/* Temp edge vectors stack, only used when computing lnor spacearr. */
BLI_Stack *edge_vectors = NULL;
{
char htype = BM_LOOP;
@@ -525,6 +551,15 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const
BM_mesh_elem_index_ensure(bm, htype);
}
if (!r_lnors_spacearr && has_clnors) {
/* We need to compute lnor spacearr if some custom lnor data are given to us! */
r_lnors_spacearr = &_lnors_spacearr;
}
if (r_lnors_spacearr) {
BKE_lnor_spacearr_init(r_lnors_spacearr, bm->totloop);
edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
}
/* We now know edges that can be smoothed (they are tagged), and edges that will be hard (they aren't).
* Now, time to generate the normals.
*/
@@ -533,8 +568,10 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const
l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
do {
if (BM_elem_flag_test_bool(l_curr->e, BM_ELEM_TAG)) {
/* A smooth edge.
if (BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
(!r_lnors_spacearr || BM_elem_flag_test(l_curr->v, BM_ELEM_TAG)))
{
/* A smooth edge, and we are not generating lnors_spacearr, or the related vertex is sharp.
* We skip it because it is either:
* - in the middle of a 'smooth fan' already computed (or that will be as soon as we hit
* one of its ends, i.e. one of its two sharp edges), or...
@@ -542,12 +579,45 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const
* are just fine!
*/
}
else if (!BM_elem_flag_test_bool(l_curr->prev->e, BM_ELEM_TAG)) {
else if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
!BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG))
{
/* Simple case (both edges around that vertex are sharp in related polygon),
* this vertex just takes its poly normal.
*/
const int l_curr_index = BM_elem_index_get(l_curr);
const float *no = fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no;
copy_v3_v3(r_lnos[BM_elem_index_get(l_curr)], no);
copy_v3_v3(r_lnos[l_curr_index], no);
/* If needed, generate this (simple!) lnor space. */
if (r_lnors_spacearr) {
float vec_curr[3], vec_prev[3];
MLoopNorSpace *lnor_space = BKE_lnor_space_create(r_lnors_spacearr);
{
const BMVert *v_pivot = l_curr->v;
const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
const BMVert *v_1 = BM_edge_other_vert(l_curr->e, v_pivot);
const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co;
const BMVert *v_2 = BM_edge_other_vert(l_curr->prev->e, v_pivot);
const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
sub_v3_v3v3(vec_curr, co_1, co_pivot);
normalize_v3(vec_curr);
sub_v3_v3v3(vec_prev, co_2, co_pivot);
normalize_v3(vec_prev);
}
BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, NULL);
/* We know there is only one loop in this space, no need to create a linklist in this case... */
BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, l_curr_index, false);
if (has_clnors) {
short (*clnor)[2] = clnors_data ? &clnors_data[l_curr_index] :
BM_ELEM_CD_GET_VOID_P(l_curr, cd_loop_clnors_offset);
BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor, r_lnos[l_curr_index]);
}
}
}
/* We *do not need* to check/tag loops as already computed!
* Due to the fact a loop only links to one of its two edges, a same fan *will never be walked more than
@@ -567,13 +637,26 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const
*/
BMVert *v_pivot = l_curr->v;
BMEdge *e_next;
const BMEdge *e_org = l_curr->e;
BMLoop *lfan_pivot, *lfan_pivot_next;
int lfan_pivot_index;
float lnor[3] = {0.0f, 0.0f, 0.0f};
float vec_curr[3], vec_next[3];
float vec_curr[3], vec_next[3], vec_org[3];
/* We validate clnors data on the fly - cheapest way to do! */
int clnors_avg[2] = {0, 0};
short (*clnor_ref)[2] = NULL;
int clnors_nbr = 0;
bool clnors_invalid = false;
const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) : NULL;
BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors));
lfan_pivot = l_curr;
lfan_pivot_index = BM_elem_index_get(lfan_pivot);
e_next = lfan_pivot->e; /* Current edge here, actually! */
/* Only need to compute previous edge's vector once, then we can just reuse old current one! */
@@ -581,8 +664,13 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const
const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
sub_v3_v3v3(vec_curr, co_2, co_pivot);
normalize_v3(vec_curr);
sub_v3_v3v3(vec_org, co_2, co_pivot);
normalize_v3(vec_org);
copy_v3_v3(vec_curr, vec_org);
if (r_lnors_spacearr) {
BLI_stack_push(edge_vectors, vec_org);
}
}
while (true) {
@@ -617,12 +705,38 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const
const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no;
/* Accumulate */
madd_v3_v3fl(lnor, no, fac);
if (has_clnors) {
/* Accumulate all clnors, if they are not all equal we have to fix that! */
short (*clnor)[2] = clnors_data ? &clnors_data[lfan_pivot_index] :
BM_ELEM_CD_GET_VOID_P(lfan_pivot, cd_loop_clnors_offset);
if (clnors_nbr) {
clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] || (*clnor_ref)[1] != (*clnor)[1]);
}
else {
clnor_ref = clnor;
}
clnors_avg[0] += (*clnor)[0];
clnors_avg[1] += (*clnor)[1];
clnors_nbr++;
/* We store here a pointer to all custom lnors processed. */
BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor);
}
}
/* We store here a pointer to all loop-normals processed. */
BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[BM_elem_index_get(lfan_pivot)]);
BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[lfan_pivot_index]);
if (!BM_elem_flag_test_bool(e_next, BM_ELEM_TAG)) {
if (r_lnors_spacearr) {
/* Assign current lnor space to current 'vertex' loop. */
BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, lfan_pivot_index, true);
if (e_next != e_org) {
/* We store here all edges-normalized vectors processed. */
BLI_stack_push(edge_vectors, vec_next);
}
}
if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
/* Next edge is sharp, we have finished with this fan of faces around this vert! */
break;
}
@@ -631,23 +745,74 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const
copy_v3_v3(vec_curr, vec_next);
/* Next pivot loop to current one. */
lfan_pivot = lfan_pivot_next;
lfan_pivot_index = BM_elem_index_get(lfan_pivot);
}
/* In case we get a zero normal here, just use vertex normal already set! */
if (LIKELY(normalize_v3(lnor) != 0.0f)) {
/* Copy back the final computed normal into all related loop-normals. */
float *nor;
while ((nor = BLI_SMALLSTACK_POP(normal))) {
copy_v3_v3(nor, lnor);
{
float lnor_len = normalize_v3(lnor);
/* If we are generating lnor spacearr, we can now define the one for this fan. */
if (r_lnors_spacearr) {
if (UNLIKELY(lnor_len == 0.0f)) {
/* Use vertex normal as fallback! */
copy_v3_v3(lnor, r_lnos[lfan_pivot_index]);
lnor_len = 1.0f;
}
BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, edge_vectors);
if (has_clnors) {
if (clnors_invalid) {
short *clnor;
clnors_avg[0] /= clnors_nbr;
clnors_avg[1] /= clnors_nbr;
/* Fix/update all clnors of this fan with computed average value. */
printf("Invalid clnors in this fan!\n");
while ((clnor = BLI_SMALLSTACK_POP(clnors))) {
//print_v2("org clnor", clnor);
clnor[0] = (short)clnors_avg[0];
clnor[1] = (short)clnors_avg[1];
}
//print_v2("new clnors", clnors_avg);
}
else {
/* We still have to consume the stack! */
while (BLI_SMALLSTACK_POP(clnors));
}
BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor);
}
}
/* In case we get a zero normal here, just use vertex normal already set! */
if (LIKELY(lnor_len != 0.0f)) {
/* Copy back the final computed normal into all related loop-normals. */
float *nor;
while ((nor = BLI_SMALLSTACK_POP(normal))) {
copy_v3_v3(nor, lnor);
}
}
else {
/* We still have to consume the stack! */
while (BLI_SMALLSTACK_POP(normal));
}
}
else {
/* We still have to clear the stack! */
while (BLI_SMALLSTACK_POP(normal));
/* Tag related vertex as sharp, to avoid fanning around it again (in case it was a smooth one). */
if (r_lnors_spacearr) {
BM_elem_flag_enable(l_curr->v, BM_ELEM_TAG);
}
}
} while ((l_curr = l_curr->next) != l_first);
}
if (r_lnors_spacearr) {
BLI_stack_free(edge_vectors);
if (r_lnors_spacearr == &_lnors_spacearr) {
BKE_lnor_spacearr_free(r_lnors_spacearr);
}
}
}
static void bm_mesh_loops_from_vert_normals(BMesh *bm, const float (*vnos)[3], float (*r_lnos)[3])
@@ -682,16 +847,22 @@ static void bm_mesh_loops_from_vert_normals(BMesh *bm, const float (*vnos)[3], f
* Updates the loop normals of a mesh. Assumes vertex and face normals are valid (else call BM_mesh_normals_update()
* first)!
*/
void BM_mesh_loop_normals_update(BMesh *bm, const bool use_split_normals, const float split_angle, float (*r_lnos)[3])
void BM_mesh_loop_normals_update(
BMesh *bm, const bool use_split_normals, const float split_angle, float (*r_lnos)[3],
MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset)
{
if (use_split_normals) {
/* Tag smooth edges and set lnos from vnos when they might be completely smooth... */
bm_mesh_edges_sharp_tag(bm, NULL, NULL, split_angle, r_lnos);
const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
/* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */
bm_mesh_loops_calc_normals(bm, NULL, NULL, r_lnos);
if (use_split_normals) {
/* Tag smooth edges and set lnos from vnos when they might be completely smooth...
* When using custom loop normals, disable the angle feature! */
bm_mesh_edges_sharp_tag(bm, NULL, NULL, has_clnors ? (float)M_PI : split_angle, r_lnos);
/* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */
bm_mesh_loops_calc_normals(bm, NULL, NULL, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset);
}
else {
BLI_assert(!r_lnors_spacearr);
bm_mesh_loops_from_vert_normals(bm, NULL, r_lnos);
}
}
@@ -703,17 +874,23 @@ void BM_mesh_loop_normals_update(BMesh *bm, const bool use_split_normals, const
* Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals').
* Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry (splitting edges).
*/
void BM_loops_calc_normal_vcos(BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3],
const bool use_split_normals, const float split_angle, float (*r_lnos)[3])
void BM_loops_calc_normal_vcos(
BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3],
const bool use_split_normals, const float split_angle, float (*r_lnos)[3],
MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset)
{
const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
if (use_split_normals) {
/* Tag smooth edges and set lnos from vnos when they might be completely smooth... */
bm_mesh_edges_sharp_tag(bm, vnos, fnos, split_angle, r_lnos);
/* Tag smooth edges and set lnos from vnos when they might be completely smooth...
* When using custom loop normals, disable the angle feature! */
bm_mesh_edges_sharp_tag(bm, vnos, fnos, has_clnors ? (float)M_PI : split_angle, r_lnos);
/* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */
bm_mesh_loops_calc_normals(bm, vcos, fnos, r_lnos);
bm_mesh_loops_calc_normals(bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset);
}
else {
BLI_assert(!r_lnors_spacearr);
bm_mesh_loops_from_vert_normals(bm, vnos, r_lnos);
}
}

View File

@@ -28,6 +28,7 @@
*/
struct BMAllocTemplate;
struct MLoopNorSpaceArray;
void BM_mesh_elem_toolflags_ensure(BMesh *bm);
void BM_mesh_elem_toolflags_clear(BMesh *bm);
@@ -39,8 +40,10 @@ void BM_mesh_clear(BMesh *bm);
void BM_mesh_normals_update(BMesh *bm);
void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (*vcos)[3], float (*vnos)[3]);
void BM_loops_calc_normal_vcos(BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*pnos)[3],
const bool use_split_normals, const float split_angle, float (*r_lnos)[3]);
void BM_loops_calc_normal_vcos(
BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*pnos)[3],
const bool use_split_normals, const float split_angle, float (*r_lnos)[3],
struct MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset);
void bmesh_edit_begin(BMesh *bm, const BMOpTypeFlag type_flag);
void bmesh_edit_end(BMesh *bm, const BMOpTypeFlag type_flag);

View File

@@ -57,6 +57,7 @@
#include "ED_mesh.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_uvedit.h"
#include "ED_view3d.h"
@@ -838,6 +839,71 @@ void MESH_OT_customdata_clear_skin(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* Clear custom loop normals */
static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
Mesh *me = ob->data;
if (!BKE_mesh_has_custom_loop_normals(me)) {
CustomData *data = GET_CD_DATA(me, ldata);
if (me->edit_btmesh) {
BM_data_layer_add(me->edit_btmesh->bm, data, CD_CUSTOMLOOPNORMAL);
}
else {
CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, NULL, me->totloop);
}
DAG_id_tag_update(&me->id, 0);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MESH_OT_customdata_custom_splitnormals_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Custom Split Normals Data";
ot->idname = "MESH_OT_customdata_custom_splitnormals_add";
ot->description = "Add a custom split normals layer, if none exists yet";
/* api callbacks */
ot->exec = mesh_customdata_custom_splitnormals_add_exec;
ot->poll = ED_operator_object_active_editable_mesh;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int mesh_customdata_custom_splitnormals_clear_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
Mesh *me = ob->data;
if (BKE_mesh_has_custom_loop_normals(me)) {
return mesh_customdata_clear_exec__internal(C, BM_LOOP, CD_CUSTOMLOOPNORMAL);
}
return OPERATOR_CANCELLED;
}
void MESH_OT_customdata_custom_splitnormals_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Custom Split Normals Data";
ot->idname = "MESH_OT_customdata_custom_splitnormals_clear";
ot->description = "Remove the custom split normals layer, if it exists";
/* api callbacks */
ot->exec = mesh_customdata_custom_splitnormals_clear_exec;
ot->poll = ED_operator_object_active_editable_mesh;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/************************** Add Geometry Layers *************************/
void ED_mesh_update(Mesh *mesh, bContext *C, int calc_edges, int calc_tessface)

View File

@@ -238,6 +238,8 @@ void MESH_OT_vertex_color_remove(struct wmOperatorType *ot);
/* no create_mask yet */
void MESH_OT_customdata_clear_mask(struct wmOperatorType *ot);
void MESH_OT_customdata_clear_skin(struct wmOperatorType *ot);
void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot);
void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot);
void MESH_OT_drop_named_image(struct wmOperatorType *ot);

View File

@@ -153,6 +153,8 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_vertex_color_remove);
WM_operatortype_append(MESH_OT_customdata_clear_mask);
WM_operatortype_append(MESH_OT_customdata_clear_skin);
WM_operatortype_append(MESH_OT_customdata_custom_splitnormals_add);
WM_operatortype_append(MESH_OT_customdata_custom_splitnormals_clear);
WM_operatortype_append(MESH_OT_drop_named_image);
WM_operatortype_append(MESH_OT_edgering_select);

View File

@@ -63,9 +63,10 @@ typedef struct CustomDataExternal {
* layers, each with a data type (e.g. MTFace, MDeformVert, etc.). */
typedef struct CustomData {
CustomDataLayer *layers; /* CustomDataLayers, ordered by type */
int typemap[41]; /* runtime only! - maps types to indices of first layer of that type,
int typemap[42]; /* runtime only! - maps types to indices of first layer of that type,
* MUST be >= CD_NUMTYPES, but we cant use a define here.
* Correct size is ensured in CustomData_update_typemap assert() */
int pad_i1;
int totlayer, maxlayer; /* number of layers, size of layers array */
int totsize; /* in editmode, total size of all data layers */
struct BLI_mempool *pool; /* (BMesh Only): Memory pool for allocation of blocks */
@@ -119,8 +120,9 @@ typedef enum CustomDataType {
CD_FREESTYLE_FACE = 38,
CD_MLOOPTANGENT = 39,
CD_TESSLOOPNORMAL = 40,
CD_CUSTOMLOOPNORMAL = 41,
CD_NUMTYPES = 41
CD_NUMTYPES = 42
} CustomDataType;
/* Bits for CustomDataMask */
@@ -167,6 +169,7 @@ typedef enum CustomDataType {
#define CD_MASK_FREESTYLE_FACE (1LL << CD_FREESTYLE_FACE)
#define CD_MASK_MLOOPTANGENT (1LL << CD_MLOOPTANGENT)
#define CD_MASK_TESSLOOPNORMAL (1LL << CD_TESSLOOPNORMAL)
#define CD_MASK_CUSTOMLOOPNORMAL (1LL << CD_CUSTOMLOOPNORMAL)
/* CustomData.flag */
enum {

View File

@@ -207,6 +207,11 @@ static void rna_MeshAnyLayer_name_set(PointerRNA *ptr, const char *value)
rna_cd_layer_name_set(cd, (CustomDataLayer *)ptr->data, value);
}
static int rna_Mesh_has_custom_normals_get(PointerRNA *ptr)
{
Mesh *me = ptr->data;
return (int)BKE_mesh_has_custom_loop_normals(me);
}
/* -------------------------------------------------------------------- */
/* Update Callbacks */
@@ -338,6 +343,17 @@ static void rna_MeshLoop_normal_get(PointerRNA *ptr, float *values)
}
}
static void rna_MeshLoop_normal_set(PointerRNA *ptr, const float *values)
{
Mesh *me = rna_mesh(ptr);
MLoop *ml = (MLoop *)ptr->data;
float (*vec)[3] = CustomData_get(&me->ldata, (int)(ml - me->mloop), CD_NORMAL);
if (vec) {
normalize_v3_v3(*vec, values);
}
}
static void rna_MeshLoop_tangent_get(PointerRNA *ptr, float *values)
{
Mesh *me = rna_mesh(ptr);
@@ -1974,8 +1990,7 @@ static void rna_def_mloop(BlenderRNA *brna)
prop = RNA_def_property(srna, "normal", PROP_FLOAT, PROP_DIRECTION);
RNA_def_property_array(prop, 3);
RNA_def_property_range(prop, -1.0f, 1.0f);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_float_funcs(prop, "rna_MeshLoop_normal_get", NULL, NULL);
RNA_def_property_float_funcs(prop, "rna_MeshLoop_normal_get", "rna_MeshLoop_normal_set", NULL);
RNA_def_property_ui_text(prop, "Normal",
"Local space unit length split normal vector of this vertex for this polygon "
"(must be computed beforehand using calc_normals_split or calc_tangents)");
@@ -3186,8 +3201,8 @@ static void rna_def_mesh(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_auto_smooth", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_AUTOSMOOTH);
RNA_def_property_ui_text(prop, "Auto Smooth",
"Treat all set-smoothed faces with angles less than the specified angle "
"as 'smooth', unless they are linked by a sharp edge");
"Auto smooth (based on smooth/sharp faces/edges and angle between faces), "
"or use custom split normals data if available");
RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
prop = RNA_def_property(srna, "auto_smooth_angle", PROP_FLOAT, PROP_ANGLE);
@@ -3196,9 +3211,19 @@ static void rna_def_mesh(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f));
RNA_def_property_ui_range(prop, DEG2RADF(0.0f), DEG2RADF(180.0f), 1.0, 1);
RNA_def_property_ui_text(prop, "Auto Smooth Angle",
"Maximum angle between face normals that 'Auto Smooth' will operate on");
"Maximum angle between face normals that will be considered as smooth "
"(unused if custom split normals data are available)");
RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
RNA_define_verify_sdna(false);
prop = RNA_def_property(srna, "has_custom_normals", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "", 0);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Has Custom Normals",
"True if there are custom split normals data in this mesh");
RNA_def_property_boolean_funcs(prop, "rna_Mesh_has_custom_normals_get", NULL);
RNA_define_verify_sdna(true);
prop = RNA_def_property(srna, "show_double_sided", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_TWOSIDED);
RNA_def_property_ui_text(prop, "Double Sided", "Render/display the mesh with double or single sided lighting");

View File

@@ -61,10 +61,19 @@ static const char *rna_Mesh_unit_test_compare(struct Mesh *mesh, struct Mesh *me
return ret;
}
static void rna_Mesh_create_normals_split(Mesh *mesh)
{
if (!CustomData_has_layer(&mesh->ldata, CD_NORMAL)) {
CustomData_add_layer(&mesh->ldata, CD_NORMAL, CD_CALLOC, NULL, mesh->totloop);
CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
}
}
static void rna_Mesh_calc_normals_split(Mesh *mesh)
{
float (*r_loopnors)[3];
float (*polynors)[3];
short (*clnors)[2] = NULL;
bool free_polynors = false;
if (CustomData_has_layer(&mesh->ldata, CD_NORMAL)) {
@@ -76,6 +85,9 @@ static void rna_Mesh_calc_normals_split(Mesh *mesh)
CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
}
/* may be NULL */
clnors = CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL);
if (CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
/* This assume that layer is always up to date, not sure this is the case (esp. in Edit mode?)... */
polynors = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
@@ -88,9 +100,10 @@ static void rna_Mesh_calc_normals_split(Mesh *mesh)
free_polynors = true;
}
BKE_mesh_normals_loop_split(mesh->mvert, mesh->totvert, mesh->medge, mesh->totedge,
mesh->mloop, r_loopnors, mesh->totloop, mesh->mpoly, polynors, mesh->totpoly,
(mesh->flag & ME_AUTOSMOOTH) != 0, mesh->smoothresh);
BKE_mesh_normals_loop_split(
mesh->mvert, mesh->totvert, mesh->medge, mesh->totedge,
mesh->mloop, r_loopnors, mesh->totloop, mesh->mpoly, (const float (*)[3])polynors, mesh->totpoly,
(mesh->flag & ME_AUTOSMOOTH) != 0, mesh->smoothresh, NULL, clnors, NULL);
if (free_polynors) {
MEM_freeN(polynors);
@@ -139,6 +152,78 @@ static void rna_Mesh_calc_smooth_groups(Mesh *mesh, int use_bitflags, int *r_pol
r_group_total, use_bitflags);
}
static void rna_Mesh_normals_split_custom_do(Mesh *mesh, float (*custom_loopnors)[3], const bool use_vertices)
{
float (*polynors)[3];
short (*clnors)[2];
const int numloops = mesh->totloop;
bool free_polynors = false;
clnors = CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL);
if (clnors) {
memset(clnors, 0, sizeof(*clnors) * numloops);
}
else {
clnors = CustomData_add_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, NULL, numloops);
}
if (CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
polynors = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
}
else {
polynors = MEM_mallocN(sizeof(float[3]) * mesh->totpoly, __func__);
BKE_mesh_calc_normals_poly(mesh->mvert, mesh->totvert, mesh->mloop, mesh->mpoly, mesh->totloop, mesh->totpoly,
polynors, false);
free_polynors = true;
}
if (use_vertices) {
BKE_mesh_normals_loop_custom_from_vertices_set(
mesh->mvert, custom_loopnors, mesh->totvert, mesh->medge, mesh->totedge, mesh->mloop, mesh->totloop,
mesh->mpoly, (const float (*)[3])polynors, mesh->totpoly, clnors);
}
else {
BKE_mesh_normals_loop_custom_set(
mesh->mvert, mesh->totvert, mesh->medge, mesh->totedge, mesh->mloop, custom_loopnors, mesh->totloop,
mesh->mpoly, (const float (*)[3])polynors, mesh->totpoly, clnors);
}
if (free_polynors) {
MEM_freeN(polynors);
}
}
static void rna_Mesh_normals_split_custom_set(Mesh *mesh, ReportList *reports, int normals_len, float *normals)
{
float (*loopnors)[3] = (float (*)[3])normals;
const int numloops = mesh->totloop;
if (normals_len != numloops * 3) {
BKE_reportf(reports, RPT_ERROR,
"number of custom normals is not number of loops (%f / %d)",
(float)normals_len / 3.0f, numloops);
return;
}
rna_Mesh_normals_split_custom_do(mesh, loopnors, false);
}
static void rna_Mesh_normals_split_custom_set_from_vertices(
Mesh *mesh, ReportList *reports, int normals_len, float *normals)
{
float (*vertnors)[3] = (float (*)[3])normals;
const int numverts = mesh->totvert;
if (normals_len != numverts * 3) {
BKE_reportf(reports, RPT_ERROR,
"number of custom normals is not number of vertices (%f / %d)",
(float)normals_len / 3.0f, numverts);
return;
}
rna_Mesh_normals_split_custom_do(mesh, vertnors, true);
}
static void rna_Mesh_transform(Mesh *mesh, float *mat, int shape_keys)
{
BKE_mesh_transform(mesh, (float (*)[4])mat, shape_keys);
@@ -152,6 +237,7 @@ void RNA_api_mesh(StructRNA *srna)
{
FunctionRNA *func;
PropertyRNA *parm;
const int normals_array_dim[] = {1, 3};
func = RNA_def_function(srna, "transform", "rna_Mesh_transform");
RNA_def_function_ui_description(func, "Transform mesh vertices by a matrix");
@@ -162,6 +248,9 @@ void RNA_api_mesh(StructRNA *srna)
func = RNA_def_function(srna, "calc_normals", "BKE_mesh_calc_normals");
RNA_def_function_ui_description(func, "Calculate vertex normals");
func = RNA_def_function(srna, "create_normals_split", "rna_Mesh_create_normals_split");
RNA_def_function_ui_description(func, "Empty split vertex normals");
func = RNA_def_function(srna, "calc_normals_split", "rna_Mesh_calc_normals_split");
RNA_def_function_ui_description(func, "Calculate split vertex normals, which preserve sharp edges");
@@ -192,6 +281,26 @@ void RNA_api_mesh(StructRNA *srna)
parm = RNA_def_int(func, "groups", 0, 0, INT_MAX, "groups", "Total number of groups", 0, INT_MAX);
RNA_def_property_flag(parm, PROP_OUTPUT);
func = RNA_def_function(srna, "normals_split_custom_set", "rna_Mesh_normals_split_custom_set");
RNA_def_function_ui_description(func,
"Define custom split normals of this mesh "
"(use zero-vectors to keep auto ones)");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
/* TODO, see how array size of 0 works, this shouldnt be used */
parm = RNA_def_float_array(func, "normals", 1, NULL, -1.0f, 1.0f, "", "Normals", 0.0f, 0.0f);
RNA_def_property_multi_array(parm, 2, normals_array_dim);
RNA_def_property_flag(parm, PROP_DYNAMIC | PROP_REQUIRED);
func = RNA_def_function(srna, "normals_split_custom_set_from_vertices",
"rna_Mesh_normals_split_custom_set_from_vertices");
RNA_def_function_ui_description(func,
"Define custom split normals of this mesh, from vertices' normals "
"(use zero-vectors to keep auto ones)");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
/* TODO, see how array size of 0 works, this shouldnt be used */
parm = RNA_def_float_array(func, "normals", 1, NULL, -1.0f, 1.0f, "", "Normals", 0.0f, 0.0f);
RNA_def_property_multi_array(parm, 2, normals_array_dim);
RNA_def_property_flag(parm, PROP_DYNAMIC | PROP_REQUIRED);
func = RNA_def_function(srna, "update", "ED_mesh_update");
RNA_def_boolean(func, "calc_edges", 0, "Calculate Edges", "Force recalculation of edges");

View File

@@ -3316,7 +3316,7 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset)
v2= mface->v2;
v3= reverse_verts==0 ? mface->v3 : mface->v1;
v4= mface->v4;
flag= mface->flag & ME_SMOOTH;
flag = do_autosmooth ? ME_SMOOTH : mface->flag & ME_SMOOTH;
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->v1= RE_findOrAddVert(obr, vertofs+v1);