[Bf-blender-cvs] [ae440c8] custom-normals-bmesh: BMesh clnor: Change py API.

Bastien Montagne noreply at git.blender.org
Thu Jun 9 15:51:51 CEST 2016


Commit: ae440c84fd4ed50988c7665bc118daee6f4c47ba
Author: Bastien Montagne
Date:   Wed Jun 8 17:23:40 2016 +0200
Branches: custom-normals-bmesh
https://developer.blender.org/rBae440c84fd4ed50988c7665bc118daee6f4c47ba

BMesh clnor: Change py API.

After some talking with Campbell, this is now accessible through BMesh's CustomData system:

 - clnor data is now exposed in BMesh (same way as for UVs etc.).
 - You can now get/set **raw** values (pair of factors) of each loop independently.
   This is not so useful in itself (though it allows nice 'relative' normal edition, given what
   those two factors represent), getting/setting 'real' normals there is for later.
 - You can now set all custom normals at once using the new 'from_array' method of BMLayerItem.
   This is supposed to be generic setter, but for now it's only implemented for clnor data.

Some notes/reflections, also for future developments:
 - About from_array:
   * Do we accept that rather 'flexible' way of handling given array of data? think we do not have much choice
     if we want to keep generic BMLayerItem (else we'll have to define sub-classes of this for every type of data :/ ).
   * Currently clnor's from_array returns values actually set as a new array, not sure we want to keep this, or instead
     add another 'to_array' method (in this case, how do we control type of returned data?).
 - About clnors in BMesh in general:
   * Think ultimately we'll want to have own struct of clnors in BMesh,
     caching clnor spaces, encoded normal, real normal, etc.
   * We'll then need to add lots of stuff to handle edition, in particular a system to rebuild clnor spaces
     of affected loops each time we add/remove/modify geometry...

Latest point is important, since it means current BMesh py API will **not** be stable for now, and will
most certainly break when full support of custom normals is added to BMesh.

===================================================================

M	source/blender/blenkernel/intern/mesh_evaluate.c
M	source/blender/blenlib/BLI_math_base.h
M	source/blender/blenlib/intern/math_base_inline.c
M	source/blender/python/bmesh/bmesh_py_types_customdata.c
M	source/blender/python/bmesh/bmesh_py_utils.c

===================================================================

diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index 2c448aa..899eaf6 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -510,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. */
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index e97a250..e6c98cb 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 8d2d80c..f8cde6e 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/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c
index 908f6b5..27707d9 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,256 @@ 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" \
+"   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 nbr_val;
+	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;
+	}
+
+	nbr_val = PySequence_Fast_GET_SIZE(value);
+
+	if (!ELEM(nbr_val, 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;
+	}
+
+	vec_size = 3;  /* In case value is an array of None's only. */
+	for (Py_ssize_t i = 0; i < nbr_val; i++) {
+		PyObject *py_vec = PySequence_Fast_GET_ITEM(value, 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 && nbr_val != 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) * nbr_val, __func__);
+		for (Py_ssize_t i = 0; i < nbr_val; i++) {
+			PyObject *py_vec = PySequence_Fast_GET_ITEM(value, i);
+
+			if (py_vec == Py_None) {
+				clnors[i][0] = clnors[i][1] = 0.0f;
+			}
+			else {
+				py_vec = PySequence_Fast(py_vec, "");
+				if (!py_vec || PySequence_Fast_GET_SIZE(py_vec) != 2) {
+					PyErr_Format(PyExc_TypeError,
+								 "clnor's from_array(): clnors are expected to be pairs of floats "
+					             "in [-1.0, 1.0] range, clnor %d is not", i);
+					MEM_freeN(clnors);
+					Py_DECREF(value);
+					Py_XDECREF(py_vec);
+					return NULL;
+				}
+
+				for (int j = 0; j < 2; j++) {
+					PyObject *py_float = PyNumber_Float(PySequence_Fast_GET_ITEM(py_vec, j));
+
+					if (!py_float || !PyFloat_Check(py_float)) {
+						PyErr_Format(PyExc_TypeError,
+									 "clnor's from_array(): clnors are expected to be pairs of floats "
+						             "in [-1.0, 1.0] range, clnor %d is not", i);
+						MEM_freeN(clnors);
+						Py_DECREF(value);
+						Py_DECREF(py_vec);
+						Py_XDECREF(py_float);
+						return NULL;
+					}
+
+					clnors[i][j] = (float)PyFloat_AS_DOUBLE(py_float);
+					if (clnors[i][j] < -1.0f || clnors[i][j] > 1.0f) {
+						PyErr_Format(PyExc_TypeError,
+									 "clnor's from_array(): clnors are expected to be pairs of floats "
+						             "in [-1.0, 1.0] range, clnor %d is not", i);
+						MEM_freeN(clnors);
+						Py_DECREF(value);
+						Py_DECREF(py_vec);
+						Py_DECREF(py_float);
+						return NULL;
+					}
+					Py_DECREF(py_float);
+				}
+			}
+			Py_DECREF(py_vec);
+		}
+	}
+	else {
+		nors = MEM_mallocN(sizeof(*nors) * nbr_val, __func__);
+		for (Py_ssize_t i = 0; i < nbr_val; i++) {
+			PyObject *py_vec = PySequence_Fast_GET_ITEM(value, i);
+
+			if (py_vec == Py_None) {
+				zero_v3(nors[i]);
+			}
+			else {
+				py_vec = PySequence_Fast(py_vec, "");
+				if (!py_vec || PySequence_Fast_GET_SIZE(py_vec) != 3) {
+					PyErr_Format(PyExc_TypeError,
+								 "clnor's from_array(): normals are expected to be triplets of floats, normal %d is not", i);
+					MEM_freeN(nors);
+					Py_DECREF(value);
+					Py_XDECREF(py_vec);
+					return NULL;
+				}
+
+				for (int j = 0; j < 3; j++) {
+					PyObject *py_float = PyNumber_Float(PySequence_Fast_GET_ITEM(py_vec, j));
+
+					if (!py_float || !PyFloat_Check(py_float)) {
+						PyErr_Format(PyExc_TypeError,
+									 "clnor's from_array(): normals are expected to be triplets of floats, normal %d is not", i);
+						MEM_freeN(nors);
+						Py_DECREF(value);
+						Py_DECREF(py_vec);
+						Py_XDECREF(py_float);
+						return NULL;
+					}
+
+					nors[i][j] = (float)PyFloat_AS_DOUBLE(py_float);
+					Py_DECREF(py_float);
+				}
+				normalize_v3(nors[i]);  /* Just in case... */
+			}
+			Py_DECREF(py_vec);
+		}
+	}
+
+	Py_DECREF(value);
+	ret = PyTuple_New(nbr_val);
+
+	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_Pack(2,
+												PyFloat_FromDouble((double)clnors[lidx][0]),
+												PyFloat_FromDouble((double)clnors[lidx][1]));
+
+				PyTuple_SET_ITEM(ret, lidx, py_vec);
+			}
+		}
+	}
+	else {
+		if (nbr_val == 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 < nbr_val; i++) {
+			PyObject *py_vec = PyTuple_Pack(3,
+											PyFloa

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list