Compare commits
10 Commits
tmp-eevee-
...
custom-nor
Author | SHA1 | Date | |
---|---|---|---|
224edde807 | |||
af2d39d902 | |||
5c4ea862b1 | |||
4cb6a326f7 | |||
d7b3bf1614 | |||
89c978b14f | |||
ade260aa7a | |||
ae440c84fd | |||
399b56aacc | |||
d3f578c7e7 |
@@ -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.
|
||||
*/
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
|
@@ -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__` !!!
|
||||
|
@@ -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 */
|
||||
|
@@ -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);
|
||||
|
@@ -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];
|
||||
|
@@ -33,6 +33,9 @@
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
|
Reference in New Issue
Block a user