1
1

Compare commits

...

10 Commits

Author SHA1 Message Date
224edde807 Merge branch 'master' into custom-normals-bmesh 2016-06-13 12:36:24 +02:00
af2d39d902 Minor docstring formatting edit 2016-06-10 01:52:31 +10:00
5c4ea862b1 Use mathutils_array_parse instead of inline array parsing
Also fixes decref bug when None was given.
2016-06-10 01:46:45 +10:00
4cb6a326f7 More minor changes:
- replace PyTuple_Pack with PyTuple_SET_ITEMS (avoids using varargs, and instead assign values directly).
- rename nbr_val -> value_len (consistent with value, value_items).
2016-06-10 01:24:33 +10:00
d7b3bf1614 Cleanup: whitespace 2016-06-10 01:20:22 +10:00
89c978b14f Use PySequence_Fast_ITEMS for direct access 2016-06-10 01:16:50 +10:00
ade260aa7a Merge branch 'master' into custom-normals-bmesh 2016-06-09 15:45:26 +02:00
ae440c84fd 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.
2016-06-08 17:23:40 +02:00
399b56aacc Merge branch 'master' into custom-normals-bmesh 2016-06-08 12:34:12 +02:00
d3f578c7e7 Initial work to allow custom normals setting in BMesh, with quick bmesh py API call too.
Seems to be working OK from quick tests...

Some notes:
 - This is mimicking BKE_mesh_evaluate code. While this is OK for some cases,
   we'll likely want some more integrated and fine-grained ways to set/edit those
   normals in Edit mode once we create real editing tools for those (most likely based
   on some BMesh operators).
   This implies some kind of dynamic caching/update of clnors spaces though, which
   is not trivial. So kept for later, for now you have to set all custom normals at once.
 - Not sure where/how to expose this in py API, for now just added a func in bmesh.utils.
2016-06-07 21:55:28 +02:00
8 changed files with 607 additions and 15 deletions

View File

@@ -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.
*/

View File

@@ -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,

View File

@@ -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

View File

@@ -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__` !!!

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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];

View File

@@ -33,6 +33,9 @@
#include <Python.h>
#include "BLI_utildefines.h"
#include "BLI_math.h"
#include "BKE_customdata.h"
#include "MEM_guardedalloc.h"