Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_mesh.h3
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.c15
-rw-r--r--source/blender/blenlib/BLI_math_base.h4
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c12
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c328
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h10
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_customdata.c247
-rw-r--r--source/blender/python/bmesh/bmesh_py_utils.c3
8 files changed, 607 insertions, 15 deletions
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index d8d869015a3..3ec597fa1a3 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -194,6 +194,9 @@ void BKE_mesh_loop_tangents_ex(
void BKE_mesh_loop_tangents(
struct Mesh *mesh, const char *uvmap, float (*r_looptangents)[4], struct ReportList *reports);
+/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */
+#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-6f)
+
/**
* References a contiguous loop-fan with normal offset vars.
*/
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index 1c86fbcfe8e..899eaf6a759 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -433,9 +433,6 @@ MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr)
return BLI_memarena_calloc(lnors_spacearr->mem, sizeof(MLoopNorSpace));
}
-/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */
-#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-6f)
-
/* Should only be called once.
* Beware, this modifies ref_vec and other_vec in place!
* In case no valid space can be generated, ref_alpha and ref_beta are set to zero (which means 'use auto lnors').
@@ -513,17 +510,6 @@ void BKE_lnor_space_add_loop(MLoopNorSpaceArray *lnors_spacearr, MLoopNorSpace *
}
}
-MINLINE float unit_short_to_float(const short val)
-{
- return (float)val / (float)SHRT_MAX;
-}
-
-MINLINE short unit_float_to_short(const float val)
-{
- /* Rounding... */
- return (short)floorf(val * (float)SHRT_MAX + 0.5f);
-}
-
void BKE_lnor_space_custom_data_to_normal(MLoopNorSpace *lnor_space, const short clnor_data[2], float r_custom_lnor[3])
{
/* NOP custom normal data or invalid lnor space, return. */
@@ -1370,6 +1356,7 @@ void BKE_mesh_normals_loop_split(
* r_custom_loopnors is expected to have normalized normals, or zero ones, in which case they will be replaced
* by default loop/vertex normal.
*/
+/* XXX Keep in sync with BMesh's bm_mesh_loops_normals_custom_set(). */
static void mesh_normals_loop_custom_set(
const MVert *mverts, const int numVerts, MEdge *medges, const int numEdges,
MLoop *mloops, float (*r_custom_loopnors)[3], const int numLoops,
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index e97a250cd24..e6c98cb7173 100644
--- a/source/blender/blenlib/BLI_math_base.h
+++ b/source/blender/blenlib/BLI_math_base.h
@@ -210,6 +210,10 @@ MINLINE int mod_i(int i, int n);
int pow_i(int base, int exp);
double double_round(double x, int ndigits);
+MINLINE float unit_short_to_float(const short val);
+MINLINE short unit_float_to_short(const float val);
+
+
#ifdef BLI_MATH_GCC_WARN_PRAGMA
# pragma GCC diagnostic pop
#endif
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index 8d2d80c2a35..f8cde6e5c4c 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -33,6 +33,7 @@
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
+#include <limits.h>
#ifdef __SSE2__
# include <emmintrin.h>
@@ -314,6 +315,17 @@ MINLINE int signum_i(float a)
else return 0;
}
+MINLINE float unit_short_to_float(const short val)
+{
+ return (float)val / (float)SHRT_MAX;
+}
+
+MINLINE short unit_float_to_short(const float val)
+{
+ /* Rounding... */
+ return (short)floorf(val * (float)SHRT_MAX + 0.5f);
+}
+
/* Internal helpers for SSE2 implementation.
*
* NOTE: Are to be called ONLY from inside `#ifdef __SSE2__` !!!
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index ed1bd16b2e4..9147b226922 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -26,11 +26,14 @@
* BM mesh level functions.
*/
+#include <limits.h>
+
#include "MEM_guardedalloc.h"
#include "DNA_listBase.h"
#include "DNA_object_types.h"
+#include "BLI_bitmap.h"
#include "BLI_linklist_stack.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -39,6 +42,7 @@
#include "BKE_cdderivedmesh.h"
#include "BKE_editmesh.h"
+#include "BKE_global.h"
#include "BKE_mesh.h"
#include "BKE_multires.h"
@@ -903,6 +907,330 @@ void BM_loops_calc_normal_vcos(
}
}
+/* BMesh version of mesh_normals_loop_custom_set() in mesh_evaluate.c, keep it in sync.
+ * 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_normals_custom_set(
+ BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3], float (*r_lnos)[3],
+ short (*r_clnors_data)[2], const int cd_loop_clnors_offset, const bool use_vertices)
+{
+ BMIter viter;
+ BMVert *v_curr;
+ BMIter fiter;
+ BMFace *f_curr;
+ BMIter liter;
+ BMLoop *l_curr;
+
+ MLoopNorSpaceArray lnors_spacearr = {NULL};
+ BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)bm->totloop, __func__);
+ float (*lnos)[3] = MEM_callocN(sizeof(*lnos) * (size_t)bm->totloop, __func__);
+ /* In this case we always consider split nors as ON, and do not want to use angle to define smooth fans! */
+ const bool use_split_normals = true;
+ const float split_angle = (float)M_PI;
+ int i;
+
+ {
+ char htype = BM_LOOP;
+ if (vcos || use_vertices) {
+ htype |= BM_VERT;
+ }
+ if (fnos) {
+ htype |= BM_FACE;
+ }
+ BM_mesh_elem_index_ensure(bm, htype);
+ }
+
+ /* Compute current lnor spacearr. */
+ BM_loops_calc_normal_vcos(bm, vcos, vnos, fnos, use_split_normals, split_angle, lnos, &lnors_spacearr, NULL, -1);
+
+ /* Set all given zero vectors to their default value. */
+ if (use_vertices) {
+ BM_ITER_MESH_INDEX(v_curr, &viter, bm, BM_VERTS_OF_MESH, i) {
+ if (is_zero_v3(r_lnos[i])) {
+ copy_v3_v3(r_lnos[i], v_curr->no);
+ }
+ }
+ }
+ else {
+ BM_ITER_MESH(f_curr, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM(l_curr, &liter, f_curr, BM_LOOPS_OF_FACE) {
+ i = BM_elem_index_get(l_curr);
+ if (is_zero_v3(r_lnos[i])) {
+ copy_v3_v3(r_lnos[i], lnos[i]);
+ }
+ }
+ }
+ }
+
+ /* Now, check each current smooth fan (one lnor space per smooth fan!), and if all its matching custom lnors
+ * are not (enough) equal, add sharp edges as needed.
+ * This way, next time we run BM_mesh_loop_normals_update(), we'll get lnor spacearr/smooth fans matching
+ * given custom lnors.
+ * Note this code *will never* unsharp edges!
+ * And quite obviously, when we set custom normals per vertices, running this is absolutely useless.
+ */
+ if (!use_vertices) {
+ BM_ITER_MESH(f_curr, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM(l_curr, &liter, f_curr, BM_LOOPS_OF_FACE) {
+ i = BM_elem_index_get(l_curr);
+ if (!lnors_spacearr.lspacearr[i]) {
+ /* This should not happen in theory, but in some rare case (probably ugly geometry)
+ * we can get some NULL loopspacearr at this point. :/
+ * Maybe we should set those loops' edges as sharp?
+ */
+ BLI_BITMAP_ENABLE(done_loops, i);
+ if (G.debug & G_DEBUG) {
+ printf("WARNING! Getting invalid NULL loop space for loop %d!\n", i);
+ }
+ continue;
+ }
+
+ if (!BLI_BITMAP_TEST(done_loops, i)) {
+ /* Notes:
+ * * In case of mono-loop smooth fan, loops is NULL, so everything is fine (we have nothing to do).
+ * * Loops in this linklist are ordered (in reversed order compared to how they were discovered by
+ * BKE_mesh_normals_loop_split(), but this is not a problem). Which means if we find a
+ * mismatching clnor, we know all remaining loops will have to be in a new, different smooth fan/
+ * lnor space.
+ * * In smooth fan case, we compare each clnor against a ref one, to avoid small differences adding
+ * up into a real big one in the end!
+ */
+ LinkNode *loops = lnors_spacearr.lspacearr[i]->loops;
+ BMLoop *l_prev = NULL;
+ const float *org_no = NULL;
+
+ if (loops) {
+ if (BM_elem_index_get(l_curr) != GET_INT_FROM_POINTER(loops->link)) {
+ /* l_curr is not first loop of our loop fan, just skip until we find that one.
+ * Simpler than to go searching for the first loop immediately, since getting a BMLoop
+ * from its index is not trivial currently.*/
+ continue;
+ }
+
+ BLI_assert(loops->next);
+
+ BMLoop *l = l_curr;
+ BMEdge *e_next; /* Used to handle fan loop direction... */
+
+ /* Set e_next to loop along fan in matching direction with loop space's fan. */
+ {
+ const int lidx_next = GET_INT_FROM_POINTER(loops->next->link);
+ if (BM_elem_index_get(l->radial_next) == lidx_next) {
+ e_next = ELEM(l->e, l->radial_next->e, l->radial_next->prev->e) ? l->prev->e : l->e;
+ }
+ else {
+ BLI_assert(BM_elem_index_get(l->radial_prev) == lidx_next);
+ e_next = ELEM(l->e, l->radial_prev->e, l->radial_prev->prev->e) ? l->prev->e : l->e;
+ }
+ }
+
+ while (loops) {
+ const int lidx = GET_INT_FROM_POINTER(loops->link);
+ const int nidx = lidx;
+ float *no = r_lnos[nidx];
+
+ BLI_assert(l != NULL);
+ BLI_assert(BM_elem_index_get(l) == lidx);
+
+ if (!org_no) {
+ org_no = no;
+ }
+ else if (dot_v3v3(org_no, no) < LNOR_SPACE_TRIGO_THRESHOLD) {
+ /* Current normal differs too much from org one, we have to tag the edge between
+ * previous loop's face and current's one as sharp.
+ * We know those two loops do not point to the same edge, since we do not allow reversed winding
+ * in a same smooth fan.
+ */
+ const BMLoop *lp = l->prev;
+
+ BLI_assert(((l_prev->e == lp->e) ? l_prev->e : l->e) == e_next);
+
+ BM_elem_flag_disable(e_next, BM_ELEM_SMOOTH);
+ org_no = no;
+ }
+
+ l_prev = l;
+ l = BM_vert_step_fan_loop(l, &e_next);
+ loops = loops->next;
+ BLI_BITMAP_ENABLE(done_loops, lidx);
+ }
+
+ /* We also have to check between last and first loops, otherwise we may miss some sharp edges here!
+ * This is just a simplified version of above while loop.
+ * See T45984. */
+ loops = lnors_spacearr.lspacearr[i]->loops;
+ if (org_no) {
+ const int lidx = GET_INT_FROM_POINTER(loops->link);
+ l = l_curr;
+ const int nidx = lidx;
+ float *no = r_lnos[nidx];
+
+ if (dot_v3v3(org_no, no) < LNOR_SPACE_TRIGO_THRESHOLD) {
+ const BMLoop *lp = l->prev;
+ BM_elem_flag_disable((l_prev->e == lp->e) ? l_prev->e : l->e, BM_ELEM_SMOOTH);
+ }
+ }
+ }
+
+ /* For single loops, where lnors_spacearr.lspacearr[i]->loops is NULL. */
+ BLI_BITMAP_ENABLE(done_loops, i);
+ }
+ }
+ }
+
+ /* And now, recompute our new auto lnors and lnor spacearr! */
+ BKE_lnor_spacearr_clear(&lnors_spacearr);
+ BM_loops_calc_normal_vcos(bm, vcos, vnos, fnos, use_split_normals, split_angle, lnos, &lnors_spacearr, NULL, -1);
+ }
+ else {
+ BLI_BITMAP_SET_ALL(done_loops, true, (size_t)bm->totloop);
+ }
+
+ /* And we just have to convert plain object-space custom normals to our lnor space-encoded ones. */
+ BM_ITER_MESH(f_curr, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM(l_curr, &liter, f_curr, BM_LOOPS_OF_FACE) {
+ i = BM_elem_index_get(l_curr);
+
+ if (!lnors_spacearr.lspacearr[i]) {
+ BLI_BITMAP_DISABLE(done_loops, i);
+ if (G.debug & G_DEBUG) {
+ printf("WARNING! Still getting invalid NULL loop space in second loop for loop %d!\n", i);
+ }
+ continue;
+ }
+
+ if (BLI_BITMAP_TEST_BOOL(done_loops, i)) {
+ /* Note we accumulate and average all custom normals in current smooth fan, to avoid getting different
+ * clnors data (tiny differences in plain custom normals can give rather huge differences in
+ * computed 2D factors).
+ */
+ LinkNode *loops = lnors_spacearr.lspacearr[i]->loops;
+ if (loops) {
+ if (BM_elem_index_get(l_curr) != GET_INT_FROM_POINTER(loops->link)) {
+ /* l_curr is not first loop of our loop fan, just skip until we find that one.
+ * Simpler than to go searching for the first loop immediately, since getting a BMLoop
+ * from its index is not trivial currently.*/
+ continue;
+ }
+
+ BLI_assert(loops->next);
+
+ int nbr_nos = 0;
+ float avg_no[3] = {0.0f};
+ short clnor_data_tmp[2], *clnor_data;
+
+ BLI_SMALLSTACK_DECLARE(clnors_data, short *);
+
+ BMLoop *l = l_curr;
+ BMEdge *e_next; /* Used to handle fan loop direction... */
+
+ /* Set e_next to loop along fan in matching direction with loop space's fan. */
+ {
+ const int lidx_next = GET_INT_FROM_POINTER(loops->next->link);
+ if (BM_elem_index_get(l->radial_next) == lidx_next) {
+ e_next = ELEM(l->e, l->radial_next->e, l->radial_next->prev->e) ? l->prev->e : l->e;
+ }
+ else {
+ BLI_assert(BM_elem_index_get(l->radial_prev) == lidx_next);
+ e_next = ELEM(l->e, l->radial_prev->e, l->radial_prev->prev->e) ? l->prev->e : l->e;
+ }
+ }
+
+ while (loops) {
+ const int lidx = GET_INT_FROM_POINTER(loops->link);
+
+ BLI_assert(l != NULL);
+ BLI_assert(BM_elem_index_get(l) == lidx);
+
+ const int nidx = use_vertices ? BM_elem_index_get(l->v) : lidx;
+ float *no = r_lnos[nidx];
+ clnor_data = r_clnors_data ? r_clnors_data[lidx] :
+ BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
+
+ nbr_nos++;
+ add_v3_v3(avg_no, no);
+ BLI_SMALLSTACK_PUSH(clnors_data, clnor_data);
+
+ l = BM_vert_step_fan_loop(l, &e_next);
+ loops = loops->next;
+ BLI_BITMAP_DISABLE(done_loops, lidx);
+ }
+
+ mul_v3_fl(avg_no, 1.0f / (float)nbr_nos);
+ BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], avg_no, clnor_data_tmp);
+
+ while ((clnor_data = BLI_SMALLSTACK_POP(clnors_data))) {
+ clnor_data[0] = clnor_data_tmp[0];
+ clnor_data[1] = clnor_data_tmp[1];
+ }
+ }
+ else {
+ const int nidx = use_vertices ? BM_elem_index_get(l_curr->v) : i;
+ float *no = r_lnos[nidx];
+ short *clnor_data = r_clnors_data ? r_clnors_data[i] :
+ BM_ELEM_CD_GET_VOID_P(l_curr, cd_loop_clnors_offset);
+
+ BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], no, clnor_data);
+ BLI_BITMAP_DISABLE(done_loops, i);
+ }
+ }
+ }
+ }
+
+ MEM_freeN(lnos);
+ MEM_freeN(done_loops);
+ BKE_lnor_spacearr_free(&lnors_spacearr);
+}
+
+/**
+ * \brief BMesh Set custom Loop Normals.
+ *
+ * Store given custom per-loop normals.
+ * Caller must ensure a matching CD layer is already available, or feature its own array of clnors.
+ */
+void BM_loops_normal_custom_set(
+ BMesh *bm, float (*r_lnos)[3], short (*r_clnors_data)[2], const int cd_loop_clnors_offset)
+{
+ bm_mesh_loops_normals_custom_set(bm, NULL, NULL, NULL, r_lnos, r_clnors_data, cd_loop_clnors_offset, false);
+}
+
+/**
+ * \brief BMesh Set custom Loop Normals.
+ *
+ * Store given custom per-loop normals.
+ * Caller must ensure a matching CD layer is already available, or feature its own array of clnors.
+ */
+void BM_loops_normal_custom_set_vcos(
+ BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3], float (*r_lnos)[3],
+ short (*r_clnors_data)[2], const int cd_loop_clnors_offset)
+{
+ bm_mesh_loops_normals_custom_set(bm, vcos, vnos, fnos, r_lnos, r_clnors_data, cd_loop_clnors_offset, false);
+}
+
+/**
+ * \brief BMesh Set custom Loop Normals from vertex normals.
+ *
+ * Store given custom per-vertex normals.
+ * Caller must ensure a matching CD layer is already available, or feature its own array of clnors.
+ */
+void BM_loops_normal_custom_set_from_vertices(
+ BMesh *bm, float (*r_lnos)[3], short (*r_clnors_data)[2], const int cd_loop_clnors_offset)
+{
+ bm_mesh_loops_normals_custom_set(bm, NULL, NULL, NULL, r_lnos, r_clnors_data, cd_loop_clnors_offset, true);
+}
+
+/**
+ * \brief BMesh Set custom Loop Normals from vertex normals.
+ *
+ * Store given custom per-vertex normals.
+ * Caller must ensure a matching CD layer is already available, or feature its own array of clnors.
+ */
+void BM_loops_normal_custom_set_from_vertices_vcos(
+ BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3], float (*r_lnos)[3],
+ short (*r_clnors_data)[2], const int cd_loop_clnors_offset)
+{
+ bm_mesh_loops_normals_custom_set(bm, vcos, vnos, fnos, r_lnos, r_clnors_data, cd_loop_clnors_offset, true);
+}
+
static void UNUSED_FUNCTION(bm_mdisps_space_set)(Object *ob, BMesh *bm, int from, int to)
{
/* switch multires data out of tangent space */
diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h
index b9cdc4ccf66..b982b14340f 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.h
+++ b/source/blender/bmesh/intern/bmesh_mesh.h
@@ -44,6 +44,16 @@ 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 BM_loops_normal_custom_set(
+ BMesh *bm, float (*r_lnos)[3], short (*r_clnors_data)[2], const int cd_loop_clnors_offset);
+void BM_loops_normal_custom_set_vcos(
+ BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3], float (*r_lnos)[3],
+ short (*r_clnors_data)[2], const int cd_loop_clnors_offset);
+void BM_loops_normal_custom_set_from_vertices(
+ BMesh *bm, float (*r_lnos)[3], short (*r_clnors_data)[2], const int cd_loop_clnors_offset);
+void BM_loops_normal_custom_set_from_vertices_vcos(
+ BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3], float (*r_lnos)[3],
+ short (*r_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);
diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c
index 908f6b5a734..21a70c93e0a 100644
--- a/source/blender/python/bmesh/bmesh_py_types_customdata.c
+++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c
@@ -44,6 +44,8 @@
#include "../mathutils/mathutils.h"
#include "../generic/python_utildefines.h"
+#include "MEM_guardedalloc.h"
+
#include "BKE_customdata.h"
#include "DNA_meshdata_types.h"
@@ -113,6 +115,9 @@ PyDoc_STRVAR(bpy_bmlayeraccess_collection__uv_doc,
PyDoc_STRVAR(bpy_bmlayeraccess_collection__color_doc,
"Accessor for vertex color layer.\n\ntype: :class:`BMLayerCollection`"
);
+PyDoc_STRVAR(bpy_bmlayeraccess_collection__clnor_doc,
+"Accessor for custom loop normal layer.\n\ntype: :class:`BMLayerCollection`"
+);
PyDoc_STRVAR(bpy_bmlayeraccess_collection__skin_doc,
"Accessor for skin layer.\n\ntype: :class:`BMLayerCollection`"
);
@@ -239,6 +244,8 @@ static PyGetSetDef bpy_bmlayeraccess_loop_getseters[] = {
{(char *)"uv", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__uv_doc, (void *)CD_MLOOPUV},
{(char *)"color", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__color_doc, (void *)CD_MLOOPCOL},
+ {(char *)"clnor", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, (char *)bpy_bmlayeraccess_collection__clnor_doc, (void *)CD_CUSTOMLOOPNORMAL},
+
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
};
@@ -311,6 +318,216 @@ static PyObject *bpy_bmlayeritem_copy_from(BPy_BMLayerItem *self, BPy_BMLayerIte
Py_RETURN_NONE;
}
+#define bpy_bmlayeritem_from_array__clnors_doc \
+" clnor layer: Array may be either:\n" \
+" - A sequence of num_loop tuples (float, float):\n" \
+" Raw storage of custom normals, as (alpha, beta) factors in [-1.0, 1.0] range.\n" \
+" - A sequence of num_loop vectors (float, float, float):\n" \
+" Custom normals per loop, as (x, y, z) components (normalization is ensured internaly).\n" \
+" - A sequence of num_vert vectors (float, float, float):\n" \
+" Custom normals per vertex, as (x, y, z) components (normalization is ensured internaly).\n" \
+"\n" \
+" In all cases, items which are None or null vectors will use default auto-computed normal.\n" \
+"\n" \
+" Returns an array of the same type as given one, with None/null-vector values replaced by actual ones.\n"
+static PyObject *bpy_bmlayeritem_from_array__clnors(BPy_BMLayerItem *self, PyObject *value)
+{
+ PyObject *ret;
+
+ float (*nors)[3] = NULL;
+ float (*clnors)[2] = NULL;
+ Py_ssize_t vec_size;
+ int cd_loop_clnors_offset;
+
+ BMesh *bm = self->bm;
+
+ if ((cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL)) == -1) {
+ /* Should never ever happen! */
+ PyErr_Format(PyExc_SystemError,
+ "clnor's from_array(): No custom normal data layer in the bmesh");
+ return NULL;
+ }
+
+ value = PySequence_Fast(value, "normals must be an iterable");
+ if (!value) {
+ return NULL;
+ }
+
+ const int value_len = PySequence_Fast_GET_SIZE(value);
+
+ if (!ELEM(value_len, bm->totloop, bm->totvert)) {
+ PyErr_Format(PyExc_TypeError,
+ "clnor's from_array(): "
+ "There must be either one data per vertex or one per loop");
+ Py_DECREF(value);
+ return NULL;
+ }
+
+ PyObject **value_items = PySequence_Fast_ITEMS(value);
+
+ vec_size = 3; /* In case value is an array of None's only. */
+ for (Py_ssize_t i = 0; i < value_len; i++) {
+ PyObject *py_vec = value_items[i];
+
+ if (py_vec == Py_None) {
+ continue;
+ }
+ py_vec = PySequence_Fast(py_vec, "");
+ if (py_vec) {
+ vec_size = PySequence_Fast_GET_SIZE(py_vec);
+ }
+ if (!py_vec || (vec_size == 2 && value_len != bm->totloop) || vec_size != 3) {
+ PyErr_Format(PyExc_TypeError,
+ "clnor's from_array(): array items must be either triplets of floats, "
+ "or pair of floats factors for raw clnor data, first item is neither "
+ "(or total number of items does match expected one, %d verts/%d loops)",
+ bm->totvert, bm->totloop);
+ MEM_freeN(nors);
+ Py_DECREF(value);
+ Py_XDECREF(py_vec);
+ return NULL;
+ }
+ break;
+ }
+
+ if (vec_size == 2) {
+ clnors = MEM_mallocN(sizeof(*clnors) * value_len, __func__);
+ for (Py_ssize_t i = 0; i < value_len; i++) {
+ PyObject *py_vec = value_items[i];
+
+ if (py_vec == Py_None) {
+ zero_v2(clnors[i]);
+ }
+ else {
+ if (mathutils_array_parse(
+ clnors[i], 2, 2, py_vec,
+ "clnor's from_array(): clnors are expected to be pairs of floats "
+ "in [-1.0, 1.0] range") == -1)
+ {
+ MEM_freeN(clnors);
+ Py_DECREF(value);
+ return NULL;
+ }
+
+ for (int j = 0; j < 2; j++) {
+ CLAMP(clnors[i][j], -1.0, 1.0f);
+ }
+ }
+ }
+ }
+ else {
+ nors = MEM_mallocN(sizeof(*nors) * value_len, __func__);
+ for (Py_ssize_t i = 0; i < value_len; i++) {
+ PyObject *py_vec = value_items[i];
+ if (py_vec == Py_None) {
+ zero_v3(nors[i]);
+ }
+ else {
+ if (mathutils_array_parse(
+ nors[i], 3, 3, py_vec,
+ "clnor's from_array(): clnors are expected to be triplets of floats") == -1)
+ {
+ MEM_freeN(clnors);
+ Py_DECREF(value);
+ return NULL;
+ }
+ normalize_v3(nors[i]); /* Just in case... */
+ }
+ }
+ }
+
+ Py_DECREF(value);
+ ret = PyTuple_New(value_len);
+
+ if (vec_size == 2) {
+ BMIter fiter;
+ BMIter liter;
+ BMFace *f;
+ BMLoop *l;
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ const int lidx = BM_elem_index_get(l);
+ short *clnor = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
+ clnor[0] = unit_float_to_short(clnors[lidx][0]);
+ clnor[1] = unit_float_to_short(clnors[lidx][1]);
+
+ PyObject *py_vec = PyTuple_New(2);
+ PyTuple_SET_ITEMS(py_vec,
+ PyFloat_FromDouble((double)clnors[lidx][0]),
+ PyFloat_FromDouble((double)clnors[lidx][1]));
+
+ PyTuple_SET_ITEM(ret, lidx, py_vec);
+ }
+ }
+ }
+ else {
+ if (value_len == bm->totloop) {
+ BM_loops_normal_custom_set(bm, nors, NULL, cd_loop_clnors_offset);
+ }
+ else {
+ BM_loops_normal_custom_set_from_vertices(bm, nors, NULL, cd_loop_clnors_offset);
+ }
+
+ for (Py_ssize_t i = 0; i < value_len; i++) {
+ PyObject *py_vec = PyTuple_New(3);
+ PyTuple_SET_ITEMS(py_vec,
+ PyFloat_FromDouble((double)nors[i][0]),
+ PyFloat_FromDouble((double)nors[i][1]),
+ PyFloat_FromDouble((double)nors[i][2]));
+
+ PyTuple_SET_ITEM(ret, i, py_vec);
+ }
+
+ MEM_freeN(nors);
+ }
+
+ return ret;
+}
+
+PyDoc_STRVAR(bpy_bmlayeritem_from_array_doc,
+".. method:: from_array(array)\n"
+"\n"
+" Set whole layer data from values in given array (or any type of iterable).\n"
+"\n"
+"\n"
+bpy_bmlayeritem_from_array__clnors_doc
+"\n"
+"\n"
+" :arg array: Iterable of data to set from.\n"
+);
+static PyObject *bpy_bmlayeritem_from_array(BPy_BMLayerItem *self, PyObject *value)
+{
+ PyObject *ret;
+
+ BPY_BM_CHECK_OBJ(self);
+
+ switch (self->htype) {
+ case BM_LOOP:
+ switch (self->type) {
+ case CD_CUSTOMLOOPNORMAL:
+ ret = bpy_bmlayeritem_from_array__clnors(self, value);
+ break;
+ default:
+ ret = Py_NotImplemented; /* TODO */
+ Py_INCREF(ret);
+ }
+ break;
+ case BM_VERT:
+ case BM_EDGE:
+ case BM_FACE:
+ ret = Py_NotImplemented; /* TODO */
+ Py_INCREF(ret);
+ break;
+ default:
+ ret = NULL;
+ }
+
+ return ret;
+}
+
/* similar to new(), but no name arg. */
PyDoc_STRVAR(bpy_bmlayercollection_verify_doc,
".. method:: verify()\n"
@@ -564,7 +781,10 @@ static PyObject *bpy_bmlayercollection_get(BPy_BMLayerCollection *self, PyObject
}
static struct PyMethodDef bpy_bmlayeritem_methods[] = {
- {"copy_from", (PyCFunction)bpy_bmlayeritem_copy_from, METH_O, bpy_bmlayeritem_copy_from_doc},
+ {"copy_from", (PyCFunction)bpy_bmlayeritem_copy_from, METH_O, bpy_bmlayeritem_copy_from_doc},
+
+ {"from_array", (PyCFunction)bpy_bmlayeritem_from_array, METH_O, bpy_bmlayeritem_from_array_doc},
+
{NULL, NULL, 0, NULL}
};
@@ -1013,6 +1233,14 @@ PyObject *BPy_BMLayerItem_GetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer)
ret = BPy_BMLoopColor_CreatePyObject(value);
break;
}
+ case CD_CUSTOMLOOPNORMAL:
+ {
+ float vec[2];
+ vec[0] = unit_short_to_float(((short *)value)[0]);
+ vec[1] = unit_short_to_float(((short *)value)[1]);
+ ret = Vector_CreatePyObject(vec, 2, NULL);
+ break;
+ }
case CD_SHAPEKEY:
{
ret = Vector_CreatePyObject_wrap((float *)value, 3, NULL);
@@ -1116,6 +1344,23 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj
ret = BPy_BMLoopColor_AssignPyObject(value, py_value);
break;
}
+ case CD_CUSTOMLOOPNORMAL:
+ {
+ float vec[2];
+ if (UNLIKELY(mathutils_array_parse(vec, 2, 2, py_value, "BMLoop[clnor] = value") == -1)) {
+ ret = -1;
+ break;
+ }
+ if (vec[0] < -1.0f || vec[0] > 1.0f || vec[1] < -1.0f || vec[1] > 1.0f) {
+ PyErr_Format(PyExc_ValueError, "expected float values between -1.0 and 1.0, not (%f, %f)",
+ vec[0], vec[1]);
+ ret = -1;
+ break;
+ }
+ ((short *)value)[0] = unit_float_to_short(vec[0]);
+ ((short *)value)[1] = unit_float_to_short(vec[1]);
+ break;
+ }
case CD_SHAPEKEY:
{
float tmp_val[3];
diff --git a/source/blender/python/bmesh/bmesh_py_utils.c b/source/blender/python/bmesh/bmesh_py_utils.c
index 89c196dbcad..df3bfb1f545 100644
--- a/source/blender/python/bmesh/bmesh_py_utils.c
+++ b/source/blender/python/bmesh/bmesh_py_utils.c
@@ -33,6 +33,9 @@
#include <Python.h>
#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "BKE_customdata.h"
#include "MEM_guardedalloc.h"