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:
Diffstat (limited to 'source/blender/bmesh/intern')
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.c28
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.h6
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators_inline.h3
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_conv.c43
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_conv.h6
-rw-r--r--source/blender/bmesh/intern/bmesh_operators_private.h1
-rw-r--r--source/blender/bmesh/intern/bmesh_strands.c145
-rw-r--r--source/blender/bmesh/intern/bmesh_strands.h215
-rw-r--r--source/blender/bmesh/intern/bmesh_strands_conv.c1314
-rw-r--r--source/blender/bmesh/intern/bmesh_strands_conv.h67
10 files changed, 1815 insertions, 13 deletions
diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c
index 6e468bf44f2..d5c5b20723e 100644
--- a/source/blender/bmesh/intern/bmesh_interp.c
+++ b/source/blender/bmesh/intern/bmesh_interp.c
@@ -911,6 +911,34 @@ void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float
if (f) *f = val;
}
+float BM_elem_float_data_named_get(CustomData *cd, void *element, int type, const char *name)
+{
+ const float *f = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name);
+ return f ? *f : 0.0f;
+}
+
+void BM_elem_float_data_named_set(CustomData *cd, void *element, int type, const char *name, const float val)
+{
+ float *f = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name);
+ if (f) *f = val;
+}
+
+void BM_elem_meshsample_data_named_get(CustomData *cd, void *element, int type, const char *name, MSurfaceSample *val)
+{
+ const MSurfaceSample *s = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name);
+ if (s)
+ memcpy(val, s, sizeof(MSurfaceSample));
+ else
+ memset(val, 0, sizeof(MSurfaceSample));
+}
+
+void BM_elem_meshsample_data_named_set(CustomData *cd, void *element, int type, const char *name, const MSurfaceSample *val)
+{
+ MSurfaceSample *s = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name);
+ if (s)
+ memcpy(s, val, sizeof(MSurfaceSample));
+}
+
/** \name Loop interpolation functions: BM_vert_loop_groups_data_layer_***
*
* Handling loop custom-data such as UV's, while keeping contiguous fans is rather tedious.
diff --git a/source/blender/bmesh/intern/bmesh_interp.h b/source/blender/bmesh/intern/bmesh_interp.h
index 969e92f37db..6168a655c93 100644
--- a/source/blender/bmesh/intern/bmesh_interp.h
+++ b/source/blender/bmesh/intern/bmesh_interp.h
@@ -29,6 +29,8 @@
struct LinkNode;
struct MemArena;
+struct MSurfaceSample;
+
void BM_loop_interp_multires(BMesh *bm, BMLoop *l_dst, const BMFace *f_src);
void BM_vert_interp_from_face(BMesh *bm, BMVert *v_dst, const BMFace *f_src);
@@ -44,6 +46,10 @@ void BM_data_layer_copy(BMesh *bm, CustomData *data, int type, int src_n, int d
float BM_elem_float_data_get(CustomData *cd, void *element, int type);
void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float val);
+float BM_elem_float_data_named_get(CustomData *cd, void *element, int type, const char *name);
+void BM_elem_float_data_named_set(CustomData *cd, void *element, int type, const char *name, const float val);
+void BM_elem_meshsample_data_named_get(CustomData *cd, void *element, int type, const char *name, struct MSurfaceSample *val);
+void BM_elem_meshsample_data_named_set(CustomData *cd, void *element, int type, const char *name, const struct MSurfaceSample *val);
void BM_face_interp_from_face_ex(
BMesh *bm, BMFace *f_dst, const BMFace *f_src, const bool do_vertex,
diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.h b/source/blender/bmesh/intern/bmesh_iterators_inline.h
index e68440021e6..43b054d7845 100644
--- a/source/blender/bmesh/intern/bmesh_iterators_inline.h
+++ b/source/blender/bmesh/intern/bmesh_iterators_inline.h
@@ -29,6 +29,8 @@
#ifndef __BMESH_ITERATORS_INLINE_H__
#define __BMESH_ITERATORS_INLINE_H__
+#include "BLI_mempool.h"
+
/* inline here optimizes out the switch statement when called with
* constant values (which is very common), nicer for loop-in-loop situations */
@@ -43,7 +45,6 @@ BLI_INLINE void *BM_iter_step(BMIter *iter)
return iter->step(iter);
}
-
/**
* \brief Iterator Init
*
diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.c b/source/blender/bmesh/intern/bmesh_mesh_conv.c
index 24d70cefb2e..a0ef12c28db 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_conv.c
+++ b/source/blender/bmesh/intern/bmesh_mesh_conv.c
@@ -98,6 +98,9 @@
#include "bmesh.h"
#include "intern/bmesh_private.h" /* for element checking */
+/* XXX stupid hack: linker otherwise strips bmesh_strands_conv.c because it is not used inside bmesh */
+void *__dummy_hack__ = &BM_strands_count_psys_keys;
+
/**
* Currently this is only used for Python scripts
* which may fail to keep matching UV/TexFace layers.
@@ -226,6 +229,17 @@ void BM_mesh_bm_from_me(
BMesh *bm, Mesh *me,
const bool calc_face_normal, const bool set_key, int act_key_nr)
{
+ BM_mesh_bm_from_me_ex(bm, me, CD_MASK_BMESH, calc_face_normal, set_key, act_key_nr);
+}
+
+/**
+ * \brief Mesh -> BMesh
+ *
+ * \warning This function doesn't calculate face normals.
+ */
+void BM_mesh_bm_from_me_ex(BMesh *bm, Mesh *me, CustomDataMask mask,
+ const bool calc_face_normal, const bool set_key, int act_key_nr)
+{
MVert *mvert;
MEdge *medge;
MLoop *mloop;
@@ -251,10 +265,10 @@ void BM_mesh_bm_from_me(
if (!me || !me->totvert) {
if (me) { /*no verts? still copy customdata layout*/
- CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_ASSIGN, 0);
- CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_ASSIGN, 0);
- CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_ASSIGN, 0);
- CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_ASSIGN, 0);
+ CustomData_copy(&me->vdata, &bm->vdata, mask, CD_ASSIGN, 0);
+ CustomData_copy(&me->edata, &bm->edata, mask, CD_ASSIGN, 0);
+ CustomData_copy(&me->ldata, &bm->ldata, mask, CD_ASSIGN, 0);
+ CustomData_copy(&me->pdata, &bm->pdata, mask, CD_ASSIGN, 0);
CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);
@@ -266,10 +280,10 @@ void BM_mesh_bm_from_me(
vtable = MEM_mallocN(sizeof(void **) * me->totvert, "mesh to bmesh vtable");
- CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
- CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0);
- CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
- CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&me->vdata, &bm->vdata, mask, CD_CALLOC, 0);
+ CustomData_copy(&me->edata, &bm->edata, mask, CD_CALLOC, 0);
+ CustomData_copy(&me->ldata, &bm->ldata, mask, CD_CALLOC, 0);
+ CustomData_copy(&me->pdata, &bm->pdata, mask, CD_CALLOC, 0);
/* make sure uv layer names are consisten */
totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY);
@@ -570,6 +584,11 @@ BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface)
{
+ BM_mesh_bm_to_me_ex(bm, me, CD_MASK_MESH, do_tessface);
+}
+
+void BM_mesh_bm_to_me_ex(BMesh *bm, Mesh *me, CustomDataMask mask, bool do_tessface)
+{
MLoop *mloop;
MPoly *mpoly;
MVert *mvert, *oldverts;
@@ -629,10 +648,10 @@ void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface)
me->totface = 0;
me->act_face = -1;
- CustomData_copy(&bm->vdata, &me->vdata, CD_MASK_MESH, CD_CALLOC, me->totvert);
- CustomData_copy(&bm->edata, &me->edata, CD_MASK_MESH, CD_CALLOC, me->totedge);
- CustomData_copy(&bm->ldata, &me->ldata, CD_MASK_MESH, CD_CALLOC, me->totloop);
- CustomData_copy(&bm->pdata, &me->pdata, CD_MASK_MESH, CD_CALLOC, me->totpoly);
+ CustomData_copy(&bm->vdata, &me->vdata, mask, CD_CALLOC, me->totvert);
+ CustomData_copy(&bm->edata, &me->edata, mask, CD_CALLOC, me->totedge);
+ CustomData_copy(&bm->ldata, &me->ldata, mask, CD_CALLOC, me->totloop);
+ CustomData_copy(&bm->pdata, &me->pdata, mask, CD_CALLOC, me->totpoly);
CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert);
CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge);
diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.h b/source/blender/bmesh/intern/bmesh_mesh_conv.h
index ce286f6c662..0d235a128ab 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_conv.h
+++ b/source/blender/bmesh/intern/bmesh_mesh_conv.h
@@ -32,7 +32,10 @@
* \ingroup bmesh
*/
+#include "BLI_sys_types.h"
+
struct Mesh;
+typedef uint64_t CustomDataMask;
void BM_mesh_cd_validate(BMesh *bm);
void BM_mesh_cd_flag_ensure(BMesh *bm, struct Mesh *mesh, const char cd_flag);
@@ -42,6 +45,9 @@ char BM_mesh_cd_flag_from_bmesh(BMesh *bm);
void BM_mesh_bm_from_me(
BMesh *bm, struct Mesh *me,
const bool calc_face_normal, const bool set_key, int act_key_nr);
+void BM_mesh_bm_from_me_ex(BMesh *bm, struct Mesh *me, CustomDataMask mask,
+ const bool calc_face_normal, const bool set_key, int act_key_nr);
void BM_mesh_bm_to_me(BMesh *bm, struct Mesh *me, bool do_tessface);
+void BM_mesh_bm_to_me_ex(BMesh *bm, struct Mesh *me, CustomDataMask mask, bool do_tessface);
#endif /* __BMESH_MESH_CONV_H__ */
diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h
index 5548ee7c361..c0896691fd3 100644
--- a/source/blender/bmesh/intern/bmesh_operators_private.h
+++ b/source/blender/bmesh/intern/bmesh_operators_private.h
@@ -54,6 +54,7 @@ void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op);
void bmo_create_monkey_exec(BMesh *bm, BMOperator *op);
void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op);
void bmo_create_vert_exec(BMesh *bm, BMOperator *op);
+//void bmo_create_strand_exec(BMesh *bm, BMOperator *op);
void bmo_delete_exec(BMesh *bm, BMOperator *op);
void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op);
void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op);
diff --git a/source/blender/bmesh/intern/bmesh_strands.c b/source/blender/bmesh/intern/bmesh_strands.c
new file mode 100644
index 00000000000..ae76c4761ad
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_strands.c
@@ -0,0 +1,145 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Lukas Toenne.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/bmesh/intern/bmesh_strands.c
+ * \ingroup bmesh
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+#include "BLI_mempool.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+/*
+ * STRANDS OF MESH CALLBACKS
+ */
+
+void bmstranditer__strands_of_mesh_begin(struct BMIter__elem_of_mesh *iter)
+{
+ BLI_mempool_iternew(iter->pooliter.pool, &iter->pooliter);
+}
+
+void *bmstranditer__strands_of_mesh_step(struct BMIter__elem_of_mesh *iter)
+{
+ BMVert *v;
+
+ do {
+ v = BLI_mempool_iterstep(&iter->pooliter);
+ } while (v && !BM_strands_vert_is_root(v));
+
+ return v;
+}
+
+/*
+ * VERTS OF STRAND CALLBACKS
+ */
+
+/* BMIter__vert_of_strand is not included in the union in BMIter, just make sure it is big enough */
+BLI_STATIC_ASSERT(sizeof(BMIter__vert_of_strand) <= sizeof(BMIter), "BMIter must be at least as large as BMIter__vert_of_strand")
+
+void bmstranditer__verts_of_strand_begin(struct BMIter__vert_of_strand *iter)
+{
+ iter->e_next = iter->v_next->e;
+}
+
+void *bmstranditer__verts_of_strand_step(struct BMIter__vert_of_strand *iter)
+{
+ BMVert *v_curr = iter->v_next;
+
+ if (iter->e_next) {
+ BMEdge *e_first = iter->e_next;
+
+ /* select the other vertex of the current edge */
+ iter->v_next = (iter->v_next == iter->e_next->v1 ? iter->e_next->v2 : iter->e_next->v1);
+
+ /* select the next edge of the current vertex */
+ iter->e_next = bmesh_disk_edge_next(iter->e_next, iter->v_next);
+ if (iter->e_next == e_first) {
+ /* only one edge means the last segment, terminate */
+ iter->e_next = NULL;
+ }
+ }
+ else
+ iter->v_next = NULL; /* last vertex, terminate */
+
+ return v_curr;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int BM_strands_count(BMesh *bm)
+{
+ BMVert *v;
+ BMIter iter;
+
+ int count = 0;
+ BM_ITER_STRANDS(v, &iter, bm, BM_STRANDS_OF_MESH) {
+ ++count;
+ }
+
+ return count;
+}
+
+int BM_strands_keys_count(BMVert *root)
+{
+ BMVert *v;
+ BMIter iter;
+
+ int count = 0;
+ BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) {
+ ++count;
+ }
+
+ return count;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Create a new strand */
+BMVert *BM_strands_create(BMesh *bm, int len, bool set_defaults)
+{
+ float co[3] = {0.0f, 0.0f, 0.0f};
+
+ BMVert *root, *v = NULL, *vprev;
+ int k;
+
+ for (k = 0; k < len; ++k) {
+ vprev = v;
+ v = BM_vert_create(bm, co, NULL, set_defaults ? BM_CREATE_NOP : BM_CREATE_SKIP_CD);
+
+ zero_v3(v->no);
+
+ /* root */
+ if (k == 0) {
+ root = v;
+ }
+ else {
+ /*BMEdge *e =*/ BM_edge_create(bm, vprev, v, NULL, set_defaults ? BM_CREATE_NOP : BM_CREATE_SKIP_CD);
+ }
+ }
+
+ return root;
+}
diff --git a/source/blender/bmesh/intern/bmesh_strands.h b/source/blender/bmesh/intern/bmesh_strands.h
new file mode 100644
index 00000000000..cd4267f0bb3
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_strands.h
@@ -0,0 +1,215 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Lukas Toenne.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_STRANDS_H__
+#define __BMESH_STRANDS_H__
+
+/** \file blender/bmesh/intern/bmesh_strands.h
+ * \ingroup bmesh
+ */
+
+#include "BLI_utildefines.h"
+
+#include "bmesh.h"
+#include "bmesh_queries.h"
+#include "bmesh_structure.h"
+
+/* True if v is the root of a strand */
+BLI_INLINE bool BM_strands_vert_is_root(BMVert *v)
+{
+ BMEdge *e_first = v->e;
+ BMEdge *e_next;
+
+ if (!e_first)
+ return true; /* single vertex is both root and tip */
+ e_next = bmesh_disk_edge_next(e_first, v);
+
+ /* with a single edge, the vertex is either first or last of the curve;
+ * first vertex is defined as the root
+ */
+ if (e_next == e_first) {
+ if (e_first->v1 == v)
+ return true;
+ }
+ return false;
+}
+
+/* True if v is the tip of a strand */
+BLI_INLINE bool BM_strands_vert_is_tip(BMVert *v)
+{
+ BMEdge *e_first = v->e;
+ BMEdge *e_next;
+
+ if (!e_first)
+ return true; /* single vertex is both root and tip */
+ e_next = bmesh_disk_edge_next(e_first, v);
+
+ /* with a single edge, the vertex is either first or last of the curve;
+ * last vertex is defined as the tip
+ */
+ if (e_next == e_first) {
+ if (e_first->v2 == v)
+ return true;
+ }
+ return false;
+}
+
+/* Next vertex on a strand */
+BLI_INLINE BMVert *BM_strands_vert_next(BMVert *v)
+{
+ BMEdge *e_first = v->e;
+ BMEdge *e_next;
+
+ /* one of the edges leads to the previous vertex */
+ if (e_first) {
+ if (e_first->v1 == v)
+ return e_first->v2;
+
+ e_next = bmesh_disk_edge_next(e_first, v);
+ if (e_next->v1 == v)
+ return e_next->v2;
+ }
+ return NULL;
+}
+
+/* Previous vertex on a strand */
+BLI_INLINE BMVert *BM_strands_vert_prev(BMVert *v)
+{
+ BMEdge *e_first = v->e;
+ BMEdge *e_next;
+
+ /* one of the edges leads to the previous vertex */
+ if (e_first) {
+ if (e_first->v2 == v)
+ return e_first->v1;
+
+ e_next = bmesh_disk_edge_next(e_first, v);
+ if (e_next->v2 == v)
+ return e_next->v1;
+ }
+ return NULL;
+}
+
+int BM_strands_count(BMesh *bm);
+int BM_strands_keys_count(BMVert *root);
+
+/* Create a new strand */
+struct BMVert *BM_strands_create(struct BMesh *bm, int len, bool set_defaults);
+
+/* ==== Iterators ==== */
+
+typedef enum BMStrandsIterType {
+ BM_STRANDS_OF_MESH,
+ BM_VERTS_OF_STRAND,
+} BMStrandsIterType;
+
+#define BM_ITER_STRANDS(ele, iter, bm, itype) \
+ for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, bm, itype, NULL); \
+ ele; \
+ BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter))
+
+#define BM_ITER_STRANDS_INDEX(ele, iter, bm, itype, indexvar) \
+ for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, bm, itype, NULL), indexvar = 0; \
+ ele; \
+ BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++)
+
+#define BM_ITER_STRANDS_ELEM(ele, iter, data, itype) \
+ for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, NULL, itype, data); \
+ ele; \
+ BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter))
+
+#define BM_ITER_STRANDS_ELEM_INDEX(ele, iter, data, itype, indexvar) \
+ for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, NULL, itype, data), indexvar = 0; \
+ ele; \
+ BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++)
+
+typedef struct BMIter__vert_of_strand {
+ BMVert *v_next;
+ BMEdge *e_next;
+} BMIter__vert_of_strand;
+
+void bmstranditer__strands_of_mesh_begin(struct BMIter__elem_of_mesh *iter);
+void *bmstranditer__strands_of_mesh_step(struct BMIter__elem_of_mesh *iter);
+
+void bmstranditer__verts_of_strand_begin(struct BMIter__vert_of_strand *iter);
+void *bmstranditer__verts_of_strand_step(struct BMIter__vert_of_strand *iter);
+
+BLI_INLINE bool BM_strand_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data)
+{
+ /* int argtype; */
+ iter->itype = itype;
+
+ /* inlining optimizes out this switch when called with the defined type */
+ switch ((BMStrandsIterType)itype) {
+ case BM_STRANDS_OF_MESH:
+ BLI_assert(bm != NULL);
+ BLI_assert(data == NULL);
+ iter->begin = (BMIter__begin_cb)bmstranditer__strands_of_mesh_begin;
+ iter->step = (BMIter__step_cb)bmstranditer__strands_of_mesh_step;
+ iter->data.elem_of_mesh.pooliter.pool = bm->vpool;
+ break;
+ case BM_VERTS_OF_STRAND: {
+ BMVert *root;
+
+ BLI_assert(data != NULL);
+ BLI_assert(((BMElem *)data)->head.htype == BM_VERT);
+ root = (BMVert *)data;
+ BLI_assert(BM_strands_vert_is_root(root));
+ iter->begin = (BMIter__begin_cb)bmstranditer__verts_of_strand_begin;
+ iter->step = (BMIter__step_cb)bmstranditer__verts_of_strand_step;
+ ((BMIter__vert_of_strand *)(&iter->data))->v_next = root;
+ break;
+ }
+ default:
+ /* fallback to regular bmesh iterator */
+ return BM_iter_init(iter, bm, itype, data);
+ break;
+ }
+
+ iter->begin(iter);
+
+ return true;
+}
+
+/**
+ * \brief Iterator New
+ *
+ * Takes a bmesh iterator structure and fills
+ * it with the appropriate function pointers based
+ * upon its type and then calls BMeshIter_step()
+ * to return the first element of the iterator.
+ *
+ */
+BLI_INLINE void *BM_strand_iter_new(BMIter *iter, BMesh *bm, const char itype, void *data)
+{
+ if (LIKELY(BM_strand_iter_init(iter, bm, itype, data))) {
+ return BM_iter_step(iter);
+ }
+ else {
+ return NULL;
+ }
+}
+
+#define BM_strand_iter_new(iter, bm, itype, data) \
+ (BM_ITER_CHECK_TYPE_DATA(data), BM_strand_iter_new(iter, bm, itype, data))
+
+#endif /* __BMESH_STRANDS_H__ */
diff --git a/source/blender/bmesh/intern/bmesh_strands_conv.c b/source/blender/bmesh/intern/bmesh_strands_conv.c
new file mode 100644
index 00000000000..50ea50c9299
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_strands_conv.c
@@ -0,0 +1,1314 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Lukas Toenne.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/bmesh/intern/bmesh_strands_conv.c
+ * \ingroup bmesh
+ *
+ * BM mesh conversion functions.
+ */
+
+#include "DNA_cache_library_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_key_types.h"
+#include "DNA_strands_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "BKE_cache_library.h"
+#include "BKE_customdata.h"
+#include "BKE_key.h"
+#include "BKE_main.h"
+#include "BKE_mesh_sample.h"
+#include "BKE_strands.h"
+#include "BKE_particle.h"
+
+#include "bmesh.h"
+#include "intern/bmesh_private.h" /* for element checking */
+
+const char *CD_HAIR_SEGMENT_LENGTH = "HAIR_SEGMENT_LENGTH";
+const char *CD_HAIR_MASS = "HAIR_MASS";
+const char *CD_HAIR_WEIGHT = "HAIR_WEIGHT";
+const char *CD_HAIR_ROOT_LOCATION = "HAIR_ROOT_LOCATION";
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * Currently this is only used for Python scripts
+ * which may fail to keep matching UV/TexFace layers.
+ *
+ * \note This should only perform any changes in exceptional cases,
+ * if we need this to be faster we could inline #BM_data_layer_add and only
+ * call #update_data_blocks once at the end.
+ */
+void BM_strands_cd_validate(BMesh *UNUSED(bm))
+{
+}
+
+void BM_strands_cd_flag_ensure(BMesh *bm, const char cd_flag)
+{
+ const char cd_flag_all = BM_strands_cd_flag_from_bmesh(bm) | cd_flag;
+ BM_strands_cd_flag_apply(bm, cd_flag_all);
+}
+
+void BM_strands_cd_flag_apply(BMesh *bm, const char UNUSED(cd_flag))
+{
+ /* CustomData_bmesh_init_pool() must run first */
+ BLI_assert(bm->vdata.totlayer == 0 || bm->vdata.pool != NULL);
+ BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != NULL);
+
+ if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_MASS) < 0) {
+ BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_MASS);
+ }
+ if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_WEIGHT) < 0) {
+ BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_WEIGHT);
+ }
+ if (CustomData_get_named_layer_index(&bm->vdata, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION) < 0) {
+ BM_data_layer_add_named(bm, &bm->vdata, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION);
+ }
+ if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH) < 0) {
+ BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH);
+ }
+}
+
+char BM_strands_cd_flag_from_bmesh(BMesh *UNUSED(bm))
+{
+ char cd_flag = 0;
+ return cd_flag;
+}
+
+/* ------------------------------------------------------------------------- */
+/* CacheLibrary */
+
+static KeyBlock *bm_set_shapekey_from_strands_key(BMesh *bm, Strands *strands, Key *key, int act_key_nr)
+{
+ int totvert = strands->totverts;
+ KeyBlock *actkey, *block;
+ int i, j;
+
+ if (!key) {
+ return NULL;
+ }
+
+ if (act_key_nr != 0)
+ actkey = BLI_findlink(&key->block, act_key_nr - 1);
+ else
+ actkey = NULL;
+
+ CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0);
+
+ if (actkey && actkey->totelem == totvert) {
+ bm->shapenr = act_key_nr;
+ }
+
+ for (i = 0, block = key->block.first; block; block = block->next, i++) {
+ CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY,
+ CD_ASSIGN, NULL, 0, block->name);
+
+ j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i);
+ bm->vdata.layers[j].uid = block->uid;
+ }
+
+ return actkey;
+}
+
+/* create vertex and edge data for BMesh based on strand data */
+static void bm_make_strands(BMesh *bm, Strands *strands, Key *key, struct DerivedMesh *UNUSED(emitter_dm), float mat[4][4], float (*keyco)[3], int cd_shape_keyindex_offset)
+{
+ KeyBlock *block;
+ StrandIterator it_strand;
+
+ int vindex, eindex;
+ BMVert *v = NULL, *v_prev;
+ BMEdge *e;
+
+ vindex = 0;
+ eindex = 0;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ StrandVertexIterator it_vert;
+
+ for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) {
+ float co[3];
+
+ copy_v3_v3(co, keyco ? keyco[vindex] : it_vert.vertex->co);
+ /* transform to duplicator local space */
+ mul_m4_v3(mat, co);
+
+ v_prev = v;
+ v = BM_vert_create(bm, co, NULL, BM_CREATE_SKIP_CD);
+ BM_elem_index_set(v, vindex); /* set_ok */
+
+ /* transfer flag */
+// v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT);
+
+ /* this is necessary for selection counts to work properly */
+// if (hkey->editflag & SELECT) {
+// BM_vert_select_set(bm, v, true);
+// }
+
+// normal_short_to_float_v3(v->no, mvert->no);
+
+ /* Copy Custom Data */
+// CustomData_to_bmesh_block(&me->vdata, &bm->vdata, vindex, &v->head.data, true);
+ CustomData_bmesh_set_default(&bm->vdata, &v->head.data);
+
+// BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_MASS, mass);
+ BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT, it_vert.vertex->weight);
+
+ /* root */
+ BM_elem_meshsample_data_named_set(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &it_strand.curve->msurf);
+
+ /* set shapekey data */
+ if (key) {
+ int k;
+
+ /* set shape key original index */
+ if (cd_shape_keyindex_offset != -1)
+ BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, vindex);
+
+ for (block = key->block.first, k = 0; block; block = block->next, k++) {
+ float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, k);
+
+ if (co) {
+ mul_v3_m4v3(co, mat, ((float *)block->data) + 3 * vindex);
+ }
+ }
+ }
+
+ vindex += 1;
+
+ if (it_vert.index > 0) {
+ e = BM_edge_create(bm, v_prev, v, NULL, BM_CREATE_SKIP_CD);
+ BM_elem_index_set(e, eindex); /* set_ok; one less edge than vertices for each particle */
+
+ /* transfer flags */
+// e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT);
+
+ /* this is necessary for selection counts to work properly */
+// if (medge->flag & SELECT) {
+// BM_edge_select_set(bm, e, true);
+// }
+
+ /* Copy Custom Data */
+// CustomData_to_bmesh_block(&me->edata, &bm->edata, eindex, &e->head.data, true);
+ CustomData_bmesh_set_default(&bm->edata, &e->head.data);
+
+ eindex += 1;
+ }
+ }
+
+ }
+
+ bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE); /* added in order, clear dirty flag */
+}
+
+/**
+ * \brief ParticleSystem -> BMesh
+ */
+void BM_strands_bm_from_strands(BMesh *bm, Strands *strands, float mat[4][4], Key *key, struct DerivedMesh *emitter_dm,
+ const bool set_key, int act_key_nr)
+{
+ KeyBlock *actkey;
+ float (*keyco)[3] = NULL;
+ int totvert, totedge;
+
+ int cd_shape_keyindex_offset;
+
+ /* free custom data */
+ /* this isnt needed in most cases but do just incase */
+ CustomData_free(&bm->vdata, bm->totvert);
+ CustomData_free(&bm->edata, bm->totedge);
+ CustomData_free(&bm->ldata, bm->totloop);
+ CustomData_free(&bm->pdata, bm->totface);
+
+ totvert = strands->totverts;
+ totedge = strands->totverts - strands->totcurves;
+
+ if (!strands || !totvert || !totedge) {
+ if (strands) { /*no verts? still copy customdata layout*/
+ CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT);
+ CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE);
+ CustomData_bmesh_init_pool(&bm->ldata, 0, BM_LOOP);
+ CustomData_bmesh_init_pool(&bm->pdata, 0, BM_FACE);
+ }
+ return; /* sanity check */
+ }
+
+ actkey = bm_set_shapekey_from_strands_key(bm, strands, key, act_key_nr);
+ if (actkey)
+ keyco = actkey->data;
+
+ CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT);
+ CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE);
+
+ BM_strands_cd_flag_apply(bm, 0);
+
+ cd_shape_keyindex_offset = key ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1;
+
+ bm_make_strands(bm, strands, key, emitter_dm, mat, set_key ? keyco : NULL, cd_shape_keyindex_offset);
+
+#if 0 /* TODO */
+ if (me->mselect && me->totselect != 0) {
+
+ BMVert **vert_array = MEM_mallocN(sizeof(BMVert *) * bm->totvert, "VSelConv");
+ BMEdge **edge_array = MEM_mallocN(sizeof(BMEdge *) * bm->totedge, "ESelConv");
+ BMFace **face_array = MEM_mallocN(sizeof(BMFace *) * bm->totface, "FSelConv");
+ MSelect *msel;
+
+#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT)
+ {
+#pragma omp section
+ { BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)vert_array, bm->totvert); }
+#pragma omp section
+ { BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)edge_array, bm->totedge); }
+#pragma omp section
+ { BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)face_array, bm->totface); }
+ }
+
+ for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) {
+ switch (msel->type) {
+ case ME_VSEL:
+ BM_select_history_store(bm, (BMElem *)vert_array[msel->index]);
+ break;
+ case ME_ESEL:
+ BM_select_history_store(bm, (BMElem *)edge_array[msel->index]);
+ break;
+ case ME_FSEL:
+ BM_select_history_store(bm, (BMElem *)face_array[msel->index]);
+ break;
+ }
+ }
+
+ MEM_freeN(vert_array);
+ MEM_freeN(edge_array);
+ MEM_freeN(face_array);
+ }
+ else {
+ me->totselect = 0;
+ if (me->mselect) {
+ MEM_freeN(me->mselect);
+ me->mselect = NULL;
+ }
+ }
+#endif
+}
+
+/* ------------------------------------------------------------------------- */
+
+#if 0
+/**
+ * \brief BMesh -> Mesh
+ */
+static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert)
+{
+ const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
+ BMVert **vertMap = NULL;
+ BMVert *eve;
+ int i = 0;
+ BMIter iter;
+
+ /* caller needs to ensure this */
+ BLI_assert(ototvert > 0);
+
+ vertMap = MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap");
+ if (cd_shape_keyindex_offset != -1) {
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+ if ((keyi != ORIGINDEX_NONE) && (keyi < ototvert)) {
+ vertMap[keyi] = eve;
+ }
+ }
+ }
+ else {
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ if (i < ototvert) {
+ vertMap[i] = eve;
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ return vertMap;
+}
+
+BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
+{
+ /* this is a cheap way to set the edge draw, its not precise and will
+ * pick the first 2 faces an edge uses.
+ * The dot comparison is a little arbitrary, but set so that a 5 subd
+ * IcoSphere won't vanish but subd 6 will (as with pre-bmesh blender) */
+
+
+ if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */
+ (e->l && (e->l != e->l->radial_next)) &&
+ (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.9995f))
+ {
+ med->flag &= ~ME_EDGEDRAW;
+ }
+ else {
+ med->flag |= ME_EDGEDRAW;
+ }
+}
+#endif
+
+static void bm_strands_make_strand(BMesh *bm, BMVert *root, Strands *UNUSED(strands), float imat[4][4], Key *UNUSED(key),
+ struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *UNUSED(emitter_bvhtree),
+ StrandIterator *it_strand)
+{
+ int numverts = BM_strands_keys_count(root);
+
+ BMVert *v;
+ BMIter iter;
+ StrandVertexIterator it_vert;
+
+ it_strand->curve->numverts = numverts;
+ /* init root matrix, fully constructed below for non-degenerate strands */
+ unit_m3(it_strand->curve->root_matrix);
+
+ BKE_strand_vertex_iter_init(&it_vert, it_strand);
+ BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) {
+ BLI_assert(BKE_strand_vertex_iter_valid(&it_vert));
+
+ /* root */
+ if (it_vert.index == 0) {
+ float loc[3], nor[3], tang[3];
+ BM_elem_meshsample_data_named_get(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &it_strand->curve->msurf);
+ BKE_mesh_sample_eval(emitter_dm, &it_strand->curve->msurf, loc, nor, tang);
+
+ /* construct root matrix */
+ copy_v3_v3(it_strand->curve->root_matrix[2], nor);
+ copy_v3_v3(it_strand->curve->root_matrix[0], tang);
+ cross_v3_v3v3(it_strand->curve->root_matrix[1], it_strand->curve->root_matrix[2], it_strand->curve->root_matrix[0]);
+
+ /* transform from edit space (duplicator local space) back to the original object space */
+ mul_mat3_m4_v3(imat, it_strand->curve->root_matrix[0]);
+ mul_mat3_m4_v3(imat, it_strand->curve->root_matrix[1]);
+ mul_mat3_m4_v3(imat, it_strand->curve->root_matrix[2]);
+ }
+
+ /* transform from edit space (duplicator local space) back to the original object space */
+ mul_v3_m4v3(it_vert.vertex->co, imat, v->co);
+ it_vert.vertex->time = numverts > 0 ? (float)it_vert.index / (float)(numverts - 1) : 0.0f;
+
+ if (it_vert.index == 0) {
+ /* weight 1.0 is used for pinning hair roots in particles */
+ it_vert.vertex->weight = 1.0f;
+ }
+ else {
+ it_vert.vertex->weight = BM_elem_float_data_named_get(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT);
+ }
+
+ BKE_strand_vertex_iter_next(&it_vert);
+
+ BM_CHECK_ELEMENT(v);
+ }
+}
+
+/**
+ * returns customdata shapekey index from a keyblock or -1
+ * \note could split this out into a more generic function */
+static int bm_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey)
+{
+ int i;
+ int j = 0;
+
+ for (i = 0; i < bm->vdata.totlayer; i++) {
+ if (bm->vdata.layers[i].type == CD_SHAPEKEY) {
+ if (currkey->uid == bm->vdata.layers[i].uid) {
+ return j;
+ }
+ j++;
+ }
+ }
+ return -1;
+}
+
+/* go through and find any shapekey customdata layers
+ * that might not have corresponding KeyBlocks, and add them if
+ * necessary */
+static void bm_strands_add_missing_shapekeys(BMesh *bm, Key *key)
+{
+ KeyBlock *currkey;
+ int i;
+
+ for (i = 0; i < bm->vdata.totlayer; i++) {
+ const CustomDataLayer *layer = &bm->vdata.layers[i];
+ if (layer->type != CD_SHAPEKEY)
+ continue;
+
+ for (currkey = key->block.first; currkey; currkey = currkey->next) {
+ if (currkey->uid == layer->uid)
+ break;
+ }
+
+ if (!currkey) {
+ currkey = BKE_keyblock_add(key, layer->name);
+ currkey->uid = layer->uid;
+ }
+ }
+}
+
+/* returns offset of the edit against the active shape, so other shapes can compensate accordingly to avoid deformation */
+static void bm_strands_get_basiskey_offset(BMesh *bm, Strands *strands, Key *key, int cd_shape_keyindex_offset, float (**r_offset)[3])
+{
+ *r_offset = NULL;
+
+ /* only need offsets for relative shape keys */
+ if (key->type == KEY_RELATIVE) {
+
+ KeyBlock *actkey = BLI_findlink(&key->block, bm->shapenr - 1);
+ /* unlikely, but the active key may not be valid if the bmesh and the mesh are out of sync */
+ if (!actkey)
+ return;
+
+ /* only if active key is a base */
+ if (BKE_keyblock_is_basis(key, bm->shapenr - 1) && cd_shape_keyindex_offset >= 0) {
+ float (*fp)[3] = actkey->data;
+ float (*ofs)[3] = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data");
+ BMIter iter;
+ BMVert *eve;
+ StrandsVertex *svert;
+ int i;
+
+ svert = strands->verts;
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+
+ if (keyi != ORIGINDEX_NONE) {
+ sub_v3_v3v3(ofs[i], svert->co, fp[keyi]);
+ }
+ else {
+ /* if there are new vertices in the mesh, we can't propagate the offset
+ * because it will only work for the existing vertices and not the new
+ * ones, creating a mess when doing e.g. subdivide + translate */
+ MEM_freeN(ofs);
+ ofs = NULL;
+ break;
+ }
+
+ svert++;
+ }
+
+ *r_offset = ofs;
+ }
+ }
+}
+
+static float *bm_strands_apply_keyblock(BMesh *bm, Strands *strands, StrandsVertex *oldverts, float imat[4][4], Key *key, int cd_shape_keyindex_offset,
+ KeyBlock *kb, KeyBlock *actkb, float (*oldkey)[3], float (*offset)[3])
+{
+ const bool apply_offset = (offset && (kb != actkb) && (bm->shapenr - 1 == kb->relative));
+ const int shape_layer_index = bm_shape_layer_index_from_kb(bm, kb);
+ const int cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, shape_layer_index);
+
+ float *newkey, *fp;
+ BMIter iter;
+ BMVert *eve;
+ StrandsVertex *svert;
+ int keyi;
+ float (*ofs_pt)[3] = offset;
+
+ fp = newkey = MEM_callocN(key->elemsize * bm->totvert, "currkey->data");
+
+ svert = strands->verts;
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+
+ if (kb == actkb) {
+ copy_v3_v3(fp, eve->co);
+
+ if (actkb != key->refkey) { /* important see bug [#30771] */
+ if (cd_shape_keyindex_offset != -1) {
+ if (oldverts) {
+ keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+ if (keyi != ORIGINDEX_NONE && keyi < kb->totelem) { /* valid old vertex */
+ copy_v3_v3(svert->co, oldverts[keyi].co);
+ }
+ }
+ }
+ }
+ }
+ else if (shape_layer_index != -1) {
+ /* in most cases this runs */
+ copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset));
+ }
+ else if ((oldkey != NULL) &&
+ (cd_shape_keyindex_offset != -1) &&
+ ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
+ (keyi < kb->totelem))
+ {
+ /* old method of reconstructing keys via vertice's original key indices,
+ * currently used if the new method above fails (which is theoretically
+ * possible in certain cases of undo) */
+ copy_v3_v3(fp, oldkey[keyi]);
+ }
+ else {
+ /* fail! fill in with dummy value */
+ copy_v3_v3(fp, svert->co);
+ }
+
+ /* propagate edited basis offsets to other shapes */
+ if (apply_offset) {
+ add_v3_v3(fp, *ofs_pt++);
+ }
+
+ /* transform from edit space (duplicator local space) back to the original object space */
+ mul_m4_v3(imat, fp);
+
+ fp += 3;
+ svert++;
+ }
+
+ return newkey;
+}
+
+static void bm_strands_apply_shapekeys(BMesh *bm, Strands *strands, StrandsVertex *oldverts, float imat[4][4], Key *key)
+{
+ const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
+ KeyBlock *actkb = BLI_findlink(&key->block, bm->shapenr - 1);
+ KeyBlock *kb;
+ float (*offset)[3] = NULL;
+
+ bm_strands_add_missing_shapekeys(bm, key);
+
+ if (oldverts)
+ bm_strands_get_basiskey_offset(bm, strands, key, cd_shape_keyindex_offset, &offset);
+
+ for (kb = key->block.first; kb; kb = kb->next) {
+ float *newkey;
+
+ newkey = bm_strands_apply_keyblock(bm, strands, oldverts, imat, key, cd_shape_keyindex_offset, kb, actkb, kb->data, offset);
+
+ kb->totelem = bm->totvert;
+ if (kb->data) {
+ MEM_freeN(kb->data);
+ }
+ kb->data = newkey;
+ }
+
+ if (offset)
+ MEM_freeN(offset);
+}
+
+Strands *BM_strands_bm_to_strands(BMesh *bm, Strands *strands, float mat[4][4], Key *key, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree)
+{
+ Strands *oldstrands;
+ int ntotcurves;
+ float imat[4][4];
+
+ BMVert *root;
+ BMIter iter;
+ StrandIterator it_strand;
+
+ ntotcurves = BM_strands_count(bm);
+
+ /* lets save the old strands just in case we are actually working on
+ * a key ... we now do processing of the keys at the end */
+ oldstrands = strands;
+
+ invert_m4_m4(imat, mat);
+
+ strands = BKE_strands_new(ntotcurves, bm->totvert);
+
+// strands->cd_flag = BM_strands_cd_flag_from_bmesh(bm);
+
+ BKE_strand_iter_init(&it_strand, strands);
+ BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
+ BLI_assert(BKE_strand_iter_valid(&it_strand));
+
+ bm_strands_make_strand(bm, root, strands, imat, key, emitter_dm, emitter_bvhtree, &it_strand);
+
+ BKE_strand_iter_next(&it_strand);
+ }
+ bm->elem_index_dirty &= ~BM_VERT;
+
+ BKE_strands_ensure_normals(strands);
+
+
+#if 0 // TODO
+ {
+ BMEditSelection *selected;
+ me->totselect = BLI_listbase_count(&(bm->selected));
+
+ if (me->mselect) MEM_freeN(me->mselect);
+
+ me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history");
+
+
+ for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) {
+ if (selected->htype == BM_VERT) {
+ me->mselect[i].type = ME_VSEL;
+
+ }
+ else if (selected->htype == BM_EDGE) {
+ me->mselect[i].type = ME_ESEL;
+
+ }
+ else if (selected->htype == BM_FACE) {
+ me->mselect[i].type = ME_FSEL;
+ }
+
+ me->mselect[i].index = BM_elem_index_get(selected->ele);
+ }
+ }
+#endif
+
+ if (key) {
+ bm_strands_apply_shapekeys(bm, strands, oldstrands ? oldstrands->verts : NULL, imat, key);
+ }
+
+ if (oldstrands) {
+ BKE_strands_free(oldstrands);
+ }
+
+ return strands;
+}
+
+/* ------------------------------------------------------------------------- */
+/* ParticleSystem */
+
+int BM_strands_count_psys_keys(ParticleSystem *psys)
+{
+ ParticleData *pa;
+ int p;
+ int totkeys = 0;
+
+ for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa)
+ totkeys += pa->totkey;
+
+ return totkeys;
+}
+
+#if 0
+static KeyBlock *bm_set_shapekey_from_psys(BMesh *bm, ParticleSystem *psys, int totvert, int act_key_nr)
+{
+ KeyBlock *actkey, *block;
+ int i, j;
+
+ if (!psys->key) {
+ return NULL;
+ }
+
+ if (act_key_nr != 0)
+ actkey = BLI_findlink(&psys->key->block, act_key_nr - 1);
+ else
+ actkey = NULL;
+
+ CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0);
+
+ /* check if we need to generate unique ids for the shapekeys.
+ * this also exists in the file reading code, but is here for
+ * a sanity check */
+ if (!psys->key->uidgen) {
+ fprintf(stderr,
+ "%s had to generate shape key uid's in a situation we shouldn't need to! "
+ "(bmesh internal error)\n",
+ __func__);
+
+ psys->key->uidgen = 1;
+ for (block = psys->key->block.first; block; block = block->next) {
+ block->uid = psys->key->uidgen++;
+ }
+ }
+
+ if (actkey && actkey->totelem == totvert) {
+ bm->shapenr = act_key_nr;
+ }
+
+ for (i = 0, block = psys->key->block.first; block; block = block->next, i++) {
+ CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY,
+ CD_ASSIGN, NULL, 0, block->name);
+
+ j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i);
+ bm->vdata.layers[j].uid = block->uid;
+ }
+
+ return actkey;
+}
+#endif
+
+/* create vertex and edge data for BMesh based on particle hair keys */
+static void bm_make_particles(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, float (*keyco)[3], int cd_shape_keyindex_offset)
+{
+// KeyBlock *block;
+ ParticleData *pa;
+ HairKey *hkey;
+ int p, k;
+
+ int vindex, eindex;
+ BMVert *v = NULL, *v_prev;
+ BMEdge *e;
+
+ float hairmat[4][4];
+
+ /* XXX currently all particles and keys have the same mass, this may change */
+ float mass = psys->part->mass;
+
+ vindex = 0;
+ eindex = 0;
+ for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) {
+
+ /* hair keys are in a local "hair space", but edit data should be in object space */
+ psys_mat_hair_to_object(ob, emitter_dm, psys->part->from, pa, hairmat);
+
+ for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) {
+ float co[3];
+
+ copy_v3_v3(co, keyco ? keyco[vindex] : hkey->co);
+ mul_m4_v3(hairmat, co);
+
+ v_prev = v;
+ v = BM_vert_create(bm, co, NULL, BM_CREATE_SKIP_CD);
+ BM_elem_index_set(v, vindex); /* set_ok */
+
+ /* transfer flag */
+// v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT);
+
+ /* this is necessary for selection counts to work properly */
+// if (hkey->editflag & SELECT) {
+// BM_vert_select_set(bm, v, true);
+// }
+
+// normal_short_to_float_v3(v->no, mvert->no);
+
+ /* Copy Custom Data */
+// CustomData_to_bmesh_block(&me->vdata, &bm->vdata, vindex, &v->head.data, true);
+ CustomData_bmesh_set_default(&bm->vdata, &v->head.data);
+
+ BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_MASS, mass);
+ BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT, hkey->weight);
+
+ /* root */
+ if (k == 0) {
+ MSurfaceSample root_loc;
+ if (BKE_mesh_sample_from_particle(&root_loc, psys, emitter_dm, pa)) {
+ BM_elem_meshsample_data_named_set(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &root_loc);
+ }
+ }
+
+#if 0
+ /* set shapekey data */
+ if (psys->key) {
+ /* set shape key original index */
+ if (cd_shape_keyindex_offset != -1) BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, vindex);
+
+ for (block = psys->key->block.first, j = 0; block; block = block->next, j++) {
+ float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j);
+
+ if (co) {
+ copy_v3_v3(co, ((float *)block->data) + 3 * vindex);
+ }
+ }
+ }
+#else
+ (void)cd_shape_keyindex_offset;
+#endif
+
+ vindex += 1;
+
+ if (k > 0) {
+ e = BM_edge_create(bm, v_prev, v, NULL, BM_CREATE_SKIP_CD);
+ BM_elem_index_set(e, eindex); /* set_ok; one less edge than vertices for each particle */
+
+ /* transfer flags */
+// e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT);
+
+ /* this is necessary for selection counts to work properly */
+// if (medge->flag & SELECT) {
+// BM_edge_select_set(bm, e, true);
+// }
+
+ /* Copy Custom Data */
+// CustomData_to_bmesh_block(&me->edata, &bm->edata, eindex, &e->head.data, true);
+ CustomData_bmesh_set_default(&bm->edata, &e->head.data);
+
+ eindex += 1;
+ }
+
+ } /* hair keys */
+
+ } /* particles */
+
+ bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE); /* added in order, clear dirty flag */
+}
+
+/**
+ * \brief ParticleSystem -> BMesh
+ */
+void BM_strands_bm_from_psys(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm,
+ const bool set_key, int act_key_nr)
+{
+ // KeyBlock *actkey;
+ float (*keyco)[3] = NULL;
+ int totvert, totedge;
+
+ int cd_shape_keyindex_offset;
+
+ /* free custom data */
+ /* this isnt needed in most cases but do just incase */
+ CustomData_free(&bm->vdata, bm->totvert);
+ CustomData_free(&bm->edata, bm->totedge);
+ CustomData_free(&bm->ldata, bm->totloop);
+ CustomData_free(&bm->pdata, bm->totface);
+
+ totvert = BM_strands_count_psys_keys(psys);
+ totedge = totvert - psys->totpart;
+
+ if (!psys || !totvert || !totedge) {
+ if (psys) { /*no verts? still copy customdata layout*/
+ CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT);
+ CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE);
+ CustomData_bmesh_init_pool(&bm->ldata, 0, BM_LOOP);
+ CustomData_bmesh_init_pool(&bm->pdata, 0, BM_FACE);
+ }
+ return; /* sanity check */
+ }
+
+#if 0
+ actkey = bm_set_shapekey_from_psys(bm, psys, totvert, act_key_nr);
+ if (actkey)
+ keyco = actkey->data;
+#else
+ (void)act_key_nr;
+#endif
+
+ CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT);
+ CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE);
+
+ BM_strands_cd_flag_apply(bm, /*psys->cd_flag*/0);
+
+ cd_shape_keyindex_offset = /*psys->key ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) :*/ -1;
+
+ bm_make_particles(bm, ob, psys, emitter_dm, set_key ? keyco : NULL, cd_shape_keyindex_offset);
+
+
+#if 0 /* TODO */
+ if (me->mselect && me->totselect != 0) {
+
+ BMVert **vert_array = MEM_mallocN(sizeof(BMVert *) * bm->totvert, "VSelConv");
+ BMEdge **edge_array = MEM_mallocN(sizeof(BMEdge *) * bm->totedge, "ESelConv");
+ BMFace **face_array = MEM_mallocN(sizeof(BMFace *) * bm->totface, "FSelConv");
+ MSelect *msel;
+
+#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT)
+ {
+#pragma omp section
+ { BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)vert_array, bm->totvert); }
+#pragma omp section
+ { BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)edge_array, bm->totedge); }
+#pragma omp section
+ { BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)face_array, bm->totface); }
+ }
+
+ for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) {
+ switch (msel->type) {
+ case ME_VSEL:
+ BM_select_history_store(bm, (BMElem *)vert_array[msel->index]);
+ break;
+ case ME_ESEL:
+ BM_select_history_store(bm, (BMElem *)edge_array[msel->index]);
+ break;
+ case ME_FSEL:
+ BM_select_history_store(bm, (BMElem *)face_array[msel->index]);
+ break;
+ }
+ }
+
+ MEM_freeN(vert_array);
+ MEM_freeN(edge_array);
+ MEM_freeN(face_array);
+ }
+ else {
+ me->totselect = 0;
+ if (me->mselect) {
+ MEM_freeN(me->mselect);
+ me->mselect = NULL;
+ }
+ }
+#endif
+}
+
+#if 0
+/**
+ * \brief BMesh -> Mesh
+ */
+static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert)
+{
+ const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
+ BMVert **vertMap = NULL;
+ BMVert *eve;
+ int i = 0;
+ BMIter iter;
+
+ /* caller needs to ensure this */
+ BLI_assert(ototvert > 0);
+
+ vertMap = MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap");
+ if (cd_shape_keyindex_offset != -1) {
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+ if ((keyi != ORIGINDEX_NONE) && (keyi < ototvert)) {
+ vertMap[keyi] = eve;
+ }
+ }
+ }
+ else {
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ if (i < ototvert) {
+ vertMap[i] = eve;
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ return vertMap;
+}
+
+/**
+ * returns customdata shapekey index from a keyblock or -1
+ * \note could split this out into a more generic function */
+static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey)
+{
+ int i;
+ int j = 0;
+
+ for (i = 0; i < bm->vdata.totlayer; i++) {
+ if (bm->vdata.layers[i].type == CD_SHAPEKEY) {
+ if (currkey->uid == bm->vdata.layers[i].uid) {
+ return j;
+ }
+ j++;
+ }
+ }
+ return -1;
+}
+
+BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
+{
+ /* this is a cheap way to set the edge draw, its not precise and will
+ * pick the first 2 faces an edge uses.
+ * The dot comparison is a little arbitrary, but set so that a 5 subd
+ * IcoSphere won't vanish but subd 6 will (as with pre-bmesh blender) */
+
+
+ if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */
+ (e->l && (e->l != e->l->radial_next)) &&
+ (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.9995f))
+ {
+ med->flag &= ~ME_EDGEDRAW;
+ }
+ else {
+ med->flag |= ME_EDGEDRAW;
+ }
+}
+#endif
+
+static void make_particle_hair(BMesh *bm, BMVert *root, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree, struct ParticleData *pa)
+{
+ int totkey = BM_strands_keys_count(root);
+ HairKey *hair;
+
+ BMVert *v;
+ BMIter iter;
+ HairKey *hkey;
+ int k;
+
+ float inv_hairmat[4][4];
+
+ pa->alive = PARS_ALIVE;
+ pa->flag = 0;
+
+ pa->time = 0.0f;
+ pa->lifetime = 100.0f;
+ pa->dietime = 100.0f;
+
+ pa->size = psys->part->size;
+
+ // TODO define other particle stuff ...
+
+ hair = MEM_callocN(totkey * sizeof(HairKey), "hair keys");
+
+ hkey = hair;
+ k = 0;
+ BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) {
+ /* root */
+ if (k == 0) {
+ MSurfaceSample root_loc;
+ BM_elem_meshsample_data_named_get(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &root_loc);
+ if (!BKE_mesh_sample_to_particle(&root_loc, psys, emitter_dm, emitter_bvhtree, pa)) {
+ pa->num = 0;
+ pa->num_dmcache = DMCACHE_NOTFOUND;
+ zero_v4(pa->fuv);
+ pa->foffset = 0.0f;
+ }
+
+ /* edit data is in object space, hair keys must be converted back into "hair space" */
+ psys_mat_hair_to_object(ob, emitter_dm, psys->part->from, pa, inv_hairmat);
+ invert_m4(inv_hairmat);
+ }
+
+ mul_v3_m4v3(hkey->co, inv_hairmat, v->co);
+ mul_v3_m4v3(hkey->world_co, ob->obmat, v->co);
+
+ hkey->time = totkey > 0 ? (float)k / (float)(totkey - 1) : 0.0f;
+ if (k == 0) {
+ /* weight 1.0 is used for pinning hair roots in particles */
+ hkey->weight = 1.0f;
+ }
+ else {
+ hkey->weight = BM_elem_float_data_named_get(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT);
+ }
+
+ ++hkey;
+ ++k;
+
+ BM_CHECK_ELEMENT(v);
+ }
+
+ if (pa->hair)
+ MEM_freeN(pa->hair);
+
+ pa->hair = hair;
+ pa->totkey = totkey;
+}
+
+void BM_strands_bm_to_psys(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree)
+{
+ ParticleData *particles, *oldparticles;
+ int ototpart, ntotpart;
+
+ BMVert *root;
+ BMIter iter;
+ ParticleData *pa;
+ int p;
+
+ ototpart = psys->totpart;
+
+ ntotpart = BM_strands_count(bm);
+
+ /* new particles block */
+ if (bm->totvert == 0) particles = NULL;
+ else particles = MEM_callocN(ntotpart * sizeof(ParticleData), "particles");
+
+ /* lets save the old particles just in case we are actually working on
+ * a key ... we now do processing of the keys at the end */
+ oldparticles = psys->particles;
+
+ psys->totpart = ntotpart;
+
+// psys->cd_flag = BM_strands_cd_flag_from_bmesh(bm);
+
+ pa = particles;
+ p = 0;
+ BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
+
+ make_particle_hair(bm, root, ob, psys, emitter_dm, emitter_bvhtree, pa);
+
+ ++pa;
+ ++p;
+ }
+ bm->elem_index_dirty &= ~BM_VERT;
+
+
+#if 0 // TODO
+ {
+ BMEditSelection *selected;
+ me->totselect = BLI_listbase_count(&(bm->selected));
+
+ if (me->mselect) MEM_freeN(me->mselect);
+
+ me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history");
+
+
+ for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) {
+ if (selected->htype == BM_VERT) {
+ me->mselect[i].type = ME_VSEL;
+
+ }
+ else if (selected->htype == BM_EDGE) {
+ me->mselect[i].type = ME_ESEL;
+
+ }
+ else if (selected->htype == BM_FACE) {
+ me->mselect[i].type = ME_FSEL;
+ }
+
+ me->mselect[i].index = BM_elem_index_get(selected->ele);
+ }
+ }
+#endif
+
+#if 0 // TODO
+ /* see comment below, this logic is in twice */
+
+ if (me->key) {
+ const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
+
+ KeyBlock *currkey;
+ KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1);
+
+ float (*ofs)[3] = NULL;
+
+ /* go through and find any shapekey customdata layers
+ * that might not have corresponding KeyBlocks, and add them if
+ * necessary */
+ j = 0;
+ for (i = 0; i < bm->vdata.totlayer; i++) {
+ if (bm->vdata.layers[i].type != CD_SHAPEKEY)
+ continue;
+
+ for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ if (currkey->uid == bm->vdata.layers[i].uid)
+ break;
+ }
+
+ if (!currkey) {
+ currkey = BKE_keyblock_add(me->key, bm->vdata.layers[i].name);
+ currkey->uid = bm->vdata.layers[i].uid;
+ }
+
+ j++;
+ }
+
+
+ /* editing the base key should update others */
+ if ((me->key->type == KEY_RELATIVE) && /* only need offsets for relative shape keys */
+ (actkey != NULL) && /* unlikely, but the active key may not be valid if the
+ * bmesh and the mesh are out of sync */
+ (oldverts != NULL)) /* not used here, but 'oldverts' is used later for applying 'ofs' */
+ {
+ const bool act_is_basis = BKE_keyblock_is_basis(me->key, bm->shapenr - 1);
+
+ /* active key is a base */
+ if (act_is_basis && (cd_shape_keyindex_offset != -1)) {
+ float (*fp)[3] = actkey->data;
+
+ ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data");
+ mvert = me->mvert;
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+
+ if (keyi != ORIGINDEX_NONE) {
+ sub_v3_v3v3(ofs[i], mvert->co, fp[keyi]);
+ }
+ else {
+ /* if there are new vertices in the mesh, we can't propagate the offset
+ * because it will only work for the existing vertices and not the new
+ * ones, creating a mess when doing e.g. subdivide + translate */
+ MEM_freeN(ofs);
+ ofs = NULL;
+ break;
+ }
+
+ mvert++;
+ }
+ }
+ }
+
+ for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ const bool apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative));
+ int cd_shape_offset;
+ int keyi;
+ float (*ofs_pt)[3] = ofs;
+ float *newkey, (*oldkey)[3], *fp;
+
+ j = bm_to_mesh_shape_layer_index_from_kb(bm, currkey);
+ cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, j);
+
+
+ fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data");
+ oldkey = currkey->data;
+
+ mvert = me->mvert;
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+
+ if (currkey == actkey) {
+ copy_v3_v3(fp, eve->co);
+
+ if (actkey != me->key->refkey) { /* important see bug [#30771] */
+ if (cd_shape_keyindex_offset != -1) {
+ if (oldverts) {
+ keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+ if (keyi != ORIGINDEX_NONE && keyi < currkey->totelem) { /* valid old vertex */
+ copy_v3_v3(mvert->co, oldverts[keyi].co);
+ }
+ }
+ }
+ }
+ }
+ else if (j != -1) {
+ /* in most cases this runs */
+ copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset));
+ }
+ else if ((oldkey != NULL) &&
+ (cd_shape_keyindex_offset != -1) &&
+ ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
+ (keyi < currkey->totelem))
+ {
+ /* old method of reconstructing keys via vertice's original key indices,
+ * currently used if the new method above fails (which is theoretically
+ * possible in certain cases of undo) */
+ copy_v3_v3(fp, oldkey[keyi]);
+ }
+ else {
+ /* fail! fill in with dummy value */
+ copy_v3_v3(fp, mvert->co);
+ }
+
+ /* propagate edited basis offsets to other shapes */
+ if (apply_offset) {
+ add_v3_v3(fp, *ofs_pt++);
+ }
+
+ fp += 3;
+ mvert++;
+ }
+
+ currkey->totelem = bm->totvert;
+ if (currkey->data) {
+ MEM_freeN(currkey->data);
+ }
+ currkey->data = newkey;
+ }
+
+ if (ofs) MEM_freeN(ofs);
+ }
+#else
+ psys->particles = particles;
+#endif
+
+ if (oldparticles) {
+ ParticleData *pa;
+ int p;
+ for (p = 0, pa = oldparticles; p < ototpart; ++p, ++pa)
+ if (pa->hair)
+ MEM_freeN(pa->hair);
+ MEM_freeN(oldparticles);
+ }
+}
diff --git a/source/blender/bmesh/intern/bmesh_strands_conv.h b/source/blender/bmesh/intern/bmesh_strands_conv.h
new file mode 100644
index 00000000000..aa18b779b17
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_strands_conv.h
@@ -0,0 +1,67 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2014 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_STRANDS_CONV_H__
+#define __BMESH_STRANDS_CONV_H__
+
+/** \file blender/bmesh/intern/bmesh_strands_conv.h
+ * \ingroup bmesh
+ */
+
+struct BMesh;
+struct Mesh;
+struct Object;
+struct ParticleSystem;
+struct DerivedMesh;
+struct BVHTreeFromMesh;
+struct Strands;
+struct Key;
+
+extern const char *CD_HAIR_SEGMENT_LENGTH;
+extern const char *CD_HAIR_MASS;
+extern const char *CD_HAIR_WEIGHT;
+extern const char *CD_HAIR_ROOT_LOCATION;
+
+void BM_strands_cd_validate(struct BMesh *bm);
+void BM_strands_cd_flag_ensure(struct BMesh *bm, const char cd_flag);
+void BM_strands_cd_flag_apply(struct BMesh *bm, const char cd_flag);
+char BM_strands_cd_flag_from_bmesh(struct BMesh *bm);
+
+void BM_strands_bm_from_strands(struct BMesh *bm, struct Strands *strands, float mat[4][4], struct Key *key, struct DerivedMesh *emitter_dm, const bool set_key, int act_key_nr);
+struct Strands *BM_strands_bm_to_strands(struct BMesh *bm, struct Strands *strands, float mat[4][4], struct Key *key, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree);
+
+int BM_strands_count_psys_keys(struct ParticleSystem *psys);
+void BM_strands_bm_from_psys(struct BMesh *bm, struct Object *ob, struct ParticleSystem *psys, struct DerivedMesh *emitter_dm,
+ const bool set_key, int act_key_nr);
+void BM_strands_bm_to_psys(struct BMesh *bm, struct Object *ob, struct ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree);
+
+#define BMALLOC_TEMPLATE_FROM_STRANDS(strands) { (CHECK_TYPE_INLINE(strands, Strands *), \
+ strands->totverts), (strands->totverts - strands->totcurves), 0, 0 }
+#define BMALLOC_TEMPLATE_FROM_PSYS(psys) { (CHECK_TYPE_INLINE(psys, ParticleSystem *), \
+ BM_strands_count_psys_keys(psys)), (BM_strands_count_psys_keys(psys) - (psys)->totpart), 0, 0 }
+
+#endif /* __BMESH_STRANDS_CONV_H__ */