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/blenkernel/intern/mesh_convert.c')
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c413
1 files changed, 389 insertions, 24 deletions
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index a48a74f2d67..a3eef7b17b2 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -26,6 +26,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_scene_types.h"
+#include "DNA_key_types.h"
#include "DNA_material_types.h"
#include "DNA_meta_types.h"
#include "DNA_object_types.h"
@@ -40,7 +41,9 @@
#include "BKE_main.h"
#include "BKE_DerivedMesh.h"
#include "BKE_global.h"
+#include "BKE_key.h"
#include "BKE_mesh.h"
+#include "BKE_modifier.h"
#include "BKE_displist.h"
#include "BKE_library.h"
#include "BKE_material.h"
@@ -58,6 +61,12 @@
*/
// #undef VALIDATE_MESH
+#ifdef VALIDATE_MESH
+# define ASSERT_IS_VALID_MESH(mesh) (BLI_assert((mesh == NULL) || (BKE_mesh_is_valid(mesh) == true)))
+#else
+# define ASSERT_IS_VALID_MESH(mesh)
+#endif
+
void BKE_mesh_from_metaball(ListBase *lb, Mesh *me)
{
DispList *dl;
@@ -534,7 +543,7 @@ Mesh *BKE_mesh_new_nomain_from_curve(Object *ob)
}
/* this may fail replacing ob->data, be sure to check ob->type */
-void BKE_mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const bool use_orco_uv, const char *obdata_name)
+void BKE_mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const bool use_orco_uv, const char *obdata_name, bool temporary)
{
Main *bmain = G.main;
Object *ob1;
@@ -620,7 +629,16 @@ void BKE_mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const bool use
ob1 = ob1->id.next;
}
- BKE_libblock_free_us(bmain, cu);
+ if (temporary) {
+ /* For temporary objects in BKE_mesh_new_from_object don't remap
+ * the entire scene with associated depsgraph updates, which are
+ * problematic for renderers exporting data. */
+ id_us_min(&cu->id);
+ BKE_libblock_free(bmain, cu);
+ }
+ else {
+ BKE_libblock_free_us(bmain, cu);
+ }
}
void BKE_mesh_from_nurbs(Object *ob)
@@ -633,7 +651,7 @@ void BKE_mesh_from_nurbs(Object *ob)
disp = ob->curve_cache->disp;
}
- BKE_mesh_from_nurbs_displist(ob, &disp, use_orco_uv, cu->id.name);
+ BKE_mesh_from_nurbs_displist(ob, &disp, use_orco_uv, cu->id.name, false);
}
typedef struct EdgeLink {
@@ -660,15 +678,15 @@ static void appendPolyLineVert(ListBase *lb, unsigned int index)
BLI_addtail(lb, vl);
}
-void BKE_mesh_to_curve_nurblist(DerivedMesh *dm, ListBase *nurblist, const int edge_users_test)
+void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int edge_users_test)
{
- MVert *mvert = dm->getVertArray(dm);
- MEdge *med, *medge = dm->getEdgeArray(dm);
- MPoly *mp, *mpoly = dm->getPolyArray(dm);
- MLoop *mloop = dm->getLoopArray(dm);
+ MVert *mvert = me->mvert;
+ MEdge *med, *medge = me->medge;
+ MPoly *mp, *mpoly = me->mpoly;
+ MLoop *mloop = me->mloop;
- int dm_totedge = dm->getNumEdges(dm);
- int dm_totpoly = dm->getNumPolys(dm);
+ int medge_len = me->totedge;
+ int mpoly_len = me->totpoly;
int totedges = 0;
int i;
@@ -678,8 +696,8 @@ void BKE_mesh_to_curve_nurblist(DerivedMesh *dm, ListBase *nurblist, const int e
ListBase edges = {NULL, NULL};
/* get boundary edges */
- edge_users = MEM_calloc_arrayN(dm_totedge, sizeof(int), __func__);
- for (i = 0, mp = mpoly; i < dm_totpoly; i++, mp++) {
+ edge_users = MEM_calloc_arrayN(medge_len, sizeof(int), __func__);
+ for (i = 0, mp = mpoly; i < mpoly_len; i++, mp++) {
MLoop *ml = &mloop[mp->loopstart];
int j;
for (j = 0; j < mp->totloop; j++, ml++) {
@@ -689,7 +707,7 @@ void BKE_mesh_to_curve_nurblist(DerivedMesh *dm, ListBase *nurblist, const int e
/* create edges from all faces (so as to find edges not in any faces) */
med = medge;
- for (i = 0; i < dm_totedge; i++, med++) {
+ for (i = 0; i < medge_len; i++, med++) {
if (edge_users[i] == edge_users_test) {
EdgeLink *edl = MEM_callocN(sizeof(EdgeLink), "EdgeLink");
edl->edge = med;
@@ -796,12 +814,12 @@ void BKE_mesh_to_curve_nurblist(DerivedMesh *dm, ListBase *nurblist, const int e
void BKE_mesh_to_curve(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
/* make new mesh data from the original copy */
- DerivedMesh *dm = mesh_get_derived_final(depsgraph, scene, ob, CD_MASK_MESH);
+ Mesh *me_eval = mesh_get_eval_final(depsgraph, scene, ob, CD_MASK_MESH);
ListBase nurblist = {NULL, NULL};
bool needsFree = false;
- BKE_mesh_to_curve_nurblist(dm, &nurblist, 0);
- BKE_mesh_to_curve_nurblist(dm, &nurblist, 1);
+ BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 0);
+ BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 1);
if (nurblist.first) {
Curve *cu = BKE_curve_add(G.main, ob->id.name + 2, OB_CURVE);
@@ -817,11 +835,20 @@ void BKE_mesh_to_curve(Depsgraph *depsgraph, Scene *scene, Object *ob)
needsFree = true;
}
- dm->needsFree = needsFree;
- dm->release(dm);
+ /* Just to avoid dangling pointer, dm will be removed. */
+ {
+ DerivedMesh *dm = ob->derivedFinal;
+ if (dm != NULL) {
+ dm->needsFree = needsFree;
+ dm->release(dm);
+ }
+ }
if (needsFree) {
+ BKE_mesh_free(me_eval);
+
ob->derivedFinal = NULL;
+ ob->runtime.mesh_eval = NULL;
/* curve object could have got bounding box only in special cases */
if (ob->bb) {
@@ -856,9 +883,8 @@ Mesh *BKE_mesh_new_from_object(
/* copies object and modifiers (but not the data) */
Object *tmpobj;
/* TODO: make it temp copy outside bmain! */
- BKE_id_copy_ex(bmain, &ob->id, (ID **)&tmpobj, LIB_ID_COPY_CACHES, false);
+ BKE_id_copy_ex(bmain, &ob->id, (ID **)&tmpobj, LIB_ID_COPY_CACHES | LIB_ID_CREATE_NO_DEG_TAG, false);
tmpcu = (Curve *)tmpobj->data;
- id_us_min(&tmpcu->id);
/* Copy cached display list, it might be needed by the stack evaluation.
* Ideally stack should be able to use render-time display list, but doing
@@ -878,7 +904,8 @@ Mesh *BKE_mesh_new_from_object(
BKE_object_free_modifiers(tmpobj, 0);
/* copies the data */
- copycu = tmpobj->data = BKE_curve_copy(bmain, (Curve *) ob->data);
+ BKE_id_copy_ex(bmain, ob->data, (ID **)&copycu, LIB_ID_CREATE_NO_DEG_TAG, false);
+ tmpobj->data = copycu;
/* make sure texture space is calculated for a copy of curve,
* it will be used for the final result.
@@ -901,7 +928,7 @@ Mesh *BKE_mesh_new_from_object(
/* convert object type to mesh */
uv_from_orco = (tmpcu->flag & CU_UV_ORCO) != 0;
- BKE_mesh_from_nurbs_displist(tmpobj, &dispbase, uv_from_orco, tmpcu->id.name + 2);
+ BKE_mesh_from_nurbs_displist(tmpobj, &dispbase, uv_from_orco, tmpcu->id.name + 2, true);
tmpmesh = tmpobj->data;
@@ -911,11 +938,11 @@ Mesh *BKE_mesh_new_from_object(
* if it didn't the curve did not have any segments or otherwise
* would have generated an empty mesh */
if (tmpobj->type != OB_MESH) {
- BKE_libblock_free_us(bmain, tmpobj);
+ BKE_libblock_free(bmain, tmpobj);
return NULL;
}
- BKE_libblock_free_us(bmain, tmpobj);
+ BKE_libblock_free(bmain, tmpobj);
/* XXX The curve to mesh conversion is convoluted... But essentially, BKE_mesh_from_nurbs_displist()
* already transfers the ownership of materials from the temp copy of the Curve ID to the new
@@ -1070,3 +1097,341 @@ Mesh *BKE_mesh_new_from_object(
return tmpmesh;
}
+
+
+static void add_shapekey_layers(Mesh *mesh_dest, Mesh *mesh_src)
+{
+ KeyBlock *kb;
+ Key *key = mesh_src->key;
+ int i;
+
+ if (!mesh_src->key)
+ return;
+
+ /* ensure we can use mesh vertex count for derived mesh custom data */
+ if (mesh_src->totvert != mesh_dest->totvert) {
+ fprintf(stderr,
+ "%s: vertex size mismatch (mesh/dm) '%s' (%d != %d)\n",
+ __func__, mesh_src->id.name + 2, mesh_src->totvert, mesh_dest->totvert);
+ return;
+ }
+
+ for (i = 0, kb = key->block.first; kb; kb = kb->next, i++) {
+ int ci;
+ float *array;
+
+ if (mesh_src->totvert != kb->totelem) {
+ fprintf(stderr,
+ "%s: vertex size mismatch (Mesh '%s':%d != KeyBlock '%s':%d)\n",
+ __func__, mesh_src->id.name + 2, mesh_src->totvert, kb->name, kb->totelem);
+ array = MEM_calloc_arrayN((size_t)mesh_src->totvert, 3 * sizeof(float), __func__);
+ }
+ else {
+ array = MEM_malloc_arrayN((size_t)mesh_src->totvert, 3 * sizeof(float), __func__);
+ memcpy(array, kb->data, (size_t)mesh_src->totvert * 3 * sizeof(float));
+ }
+
+ CustomData_add_layer_named(&mesh_dest->vdata, CD_SHAPEKEY, CD_ASSIGN, array, mesh_dest->totvert, kb->name);
+ ci = CustomData_get_layer_index_n(&mesh_dest->vdata, CD_SHAPEKEY, i);
+
+ mesh_dest->vdata.layers[ci].uid = kb->uid;
+ }
+}
+
+
+Mesh *BKE_mesh_create_derived_for_modifier(
+ struct Depsgraph *depsgraph, Scene *scene, Object *ob,
+ ModifierData *md, int build_shapekey_layers)
+{
+ Mesh *me = ob->data;
+ const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
+ Mesh *result;
+ KeyBlock *kb;
+ ModifierEvalContext mectx = {depsgraph, ob, 0};
+
+ md->scene = scene;
+
+ if (!(md->mode & eModifierMode_Realtime)) {
+ return NULL;
+ }
+
+ if (mti->isDisabled && mti->isDisabled(md, 0)) {
+ return NULL;
+ }
+
+ if (build_shapekey_layers && me->key && (kb = BLI_findlink(&me->key->block, ob->shapenr - 1))) {
+ BKE_keyblock_convert_to_mesh(kb, me);
+ }
+
+ if (mti->type == eModifierTypeType_OnlyDeform) {
+ int numVerts;
+ float (*deformedVerts)[3] = BKE_mesh_vertexCos_get(me, &numVerts);
+
+ modifier_deformVerts(md, &mectx, NULL, deformedVerts, numVerts);
+ BKE_id_copy_ex(NULL, &me->id, (ID **)&result,
+ LIB_ID_CREATE_NO_MAIN |
+ LIB_ID_CREATE_NO_USER_REFCOUNT |
+ LIB_ID_CREATE_NO_DEG_TAG |
+ LIB_ID_COPY_NO_PREVIEW,
+ false);
+ BKE_mesh_apply_vert_coords(result, deformedVerts);
+
+ if (build_shapekey_layers)
+ add_shapekey_layers(result, me);
+
+ MEM_freeN(deformedVerts);
+ }
+ else {
+ Mesh *mesh_temp;
+ BKE_id_copy_ex(NULL, &me->id, (ID **)&mesh_temp,
+ LIB_ID_CREATE_NO_MAIN |
+ LIB_ID_CREATE_NO_USER_REFCOUNT |
+ LIB_ID_CREATE_NO_DEG_TAG |
+ LIB_ID_COPY_NO_PREVIEW,
+ false);
+
+ if (build_shapekey_layers)
+ add_shapekey_layers(mesh_temp, me);
+
+ result = modifier_applyModifier(md, &mectx, mesh_temp);
+ ASSERT_IS_VALID_MESH(result);
+
+ if (mesh_temp != result) {
+ BKE_id_free(NULL, mesh_temp);
+ }
+ }
+
+ return result;
+}
+
+/* This is a Mesh-based copy of the same function in DerivedMesh.c */
+static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int actshape_uid)
+{
+ KeyBlock *kb;
+ int i, j, tot;
+
+ if (!mesh_dst->key)
+ return;
+
+ tot = CustomData_number_of_layers(&mesh_src->vdata, CD_SHAPEKEY);
+ for (i = 0; i < tot; i++) {
+ CustomDataLayer *layer = &mesh_src->vdata.layers[CustomData_get_layer_index_n(&mesh_src->vdata, CD_SHAPEKEY, i)];
+ float (*cos)[3], (*kbcos)[3];
+
+ for (kb = mesh_dst->key->block.first; kb; kb = kb->next) {
+ if (kb->uid == layer->uid)
+ break;
+ }
+
+ if (!kb) {
+ kb = BKE_keyblock_add(mesh_dst->key, layer->name);
+ kb->uid = layer->uid;
+ }
+
+ if (kb->data)
+ MEM_freeN(kb->data);
+
+ cos = CustomData_get_layer_n(&mesh_src->vdata, CD_SHAPEKEY, i);
+ kb->totelem = mesh_src->totvert;
+
+ kb->data = kbcos = MEM_malloc_arrayN(kb->totelem, 3 * sizeof(float), "kbcos DerivedMesh.c");
+ if (kb->uid == actshape_uid) {
+ MVert *mvert = mesh_src->mvert;
+
+ for (j = 0; j < mesh_src->totvert; j++, kbcos++, mvert++) {
+ copy_v3_v3(*kbcos, mvert->co);
+ }
+ }
+ else {
+ for (j = 0; j < kb->totelem; j++, cos++, kbcos++) {
+ copy_v3_v3(*kbcos, *cos);
+ }
+ }
+ }
+
+ for (kb = mesh_dst->key->block.first; kb; kb = kb->next) {
+ if (kb->totelem != mesh_src->totvert) {
+ if (kb->data)
+ MEM_freeN(kb->data);
+
+ kb->totelem = mesh_src->totvert;
+ kb->data = MEM_calloc_arrayN(kb->totelem, 3 * sizeof(float), "kb->data derivedmesh.c");
+ fprintf(stderr, "%s: lost a shapekey layer: '%s'! (bmesh internal error)\n", __func__, kb->name);
+ }
+ }
+}
+
+
+/* This is a Mesh-based copy of DM_to_mesh() */
+void BKE_nomain_mesh_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob, CustomDataMask mask, bool take_ownership)
+{
+ /* mesh_src might depend on mesh_dst, so we need to do everything with a local copy */
+ /* TODO(Sybren): the above claim came from DM_to_mesh(); check whether it is still true with Mesh */
+ Mesh tmp = *mesh_dst;
+ int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly;
+ int did_shapekeys = 0;
+ eCDAllocType alloctype = CD_DUPLICATE;
+
+ if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) {
+ bool has_any_referenced_layers =
+ CustomData_has_referenced(&mesh_src->vdata) ||
+ CustomData_has_referenced(&mesh_src->edata) ||
+ CustomData_has_referenced(&mesh_src->ldata) ||
+ CustomData_has_referenced(&mesh_src->fdata) ||
+ CustomData_has_referenced(&mesh_src->pdata);
+ if (!has_any_referenced_layers) {
+ alloctype = CD_ASSIGN;
+ }
+ }
+ CustomData_reset(&tmp.vdata);
+ CustomData_reset(&tmp.edata);
+ CustomData_reset(&tmp.fdata);
+ CustomData_reset(&tmp.ldata);
+ CustomData_reset(&tmp.pdata);
+
+ BKE_mesh_ensure_normals(mesh_src);
+
+ totvert = tmp.totvert = mesh_src->totvert;
+ totedge = tmp.totedge = mesh_src->totedge;
+ totloop = tmp.totloop = mesh_src->totloop;
+ totpoly = tmp.totpoly = mesh_src->totpoly;
+ tmp.totface = 0;
+
+ CustomData_copy(&mesh_src->vdata, &tmp.vdata, mask, alloctype, totvert);
+ CustomData_copy(&mesh_src->edata, &tmp.edata, mask, alloctype, totedge);
+ CustomData_copy(&mesh_src->ldata, &tmp.ldata, mask, alloctype, totloop);
+ CustomData_copy(&mesh_src->pdata, &tmp.pdata, mask, alloctype, totpoly);
+ tmp.cd_flag = mesh_src->cd_flag;
+ tmp.runtime.deformed_only = mesh_src->runtime.deformed_only;
+
+ if (CustomData_has_layer(&mesh_src->vdata, CD_SHAPEKEY)) {
+ KeyBlock *kb;
+ int uid;
+
+ if (ob) {
+ kb = BLI_findlink(&mesh_dst->key->block, ob->shapenr - 1);
+ if (kb) {
+ uid = kb->uid;
+ }
+ else {
+ printf("%s: error - could not find active shapekey %d!\n",
+ __func__, ob->shapenr - 1);
+
+ uid = INT_MAX;
+ }
+ }
+ else {
+ /* if no object, set to INT_MAX so we don't mess up any shapekey layers */
+ uid = INT_MAX;
+ }
+
+ shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid);
+ did_shapekeys = 1;
+ }
+
+ /* copy texture space */
+ if (ob) {
+ BKE_mesh_texspace_copy_from_object(&tmp, ob);
+ }
+
+ /* not all DerivedMeshes store their verts/edges/faces in CustomData, so
+ * we set them here in case they are missing */
+ /* TODO(Sybren): we could probably replace CD_ASSIGN with alloctype and always directly pass mesh_src->mxxx,
+ * instead of using a ternary operator. */
+ if (!CustomData_has_layer(&tmp.vdata, CD_MVERT)) {
+ CustomData_add_layer(&tmp.vdata, CD_MVERT, CD_ASSIGN,
+ (alloctype == CD_ASSIGN) ? mesh_src->mvert : MEM_dupallocN(mesh_src->mvert),
+ totvert);
+ }
+ if (!CustomData_has_layer(&tmp.edata, CD_MEDGE)) {
+ CustomData_add_layer(&tmp.edata, CD_MEDGE, CD_ASSIGN,
+ (alloctype == CD_ASSIGN) ? mesh_src->medge : MEM_dupallocN(mesh_src->medge),
+ totedge);
+ }
+ if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) {
+ /* TODO(Sybren): assigment to tmp.mxxx is probably not necessary due to the
+ * BKE_mesh_update_customdata_pointers() call below. */
+ tmp.mloop = (alloctype == CD_ASSIGN) ? mesh_src->mloop : MEM_dupallocN(mesh_src->mloop);
+ tmp.mpoly = (alloctype == CD_ASSIGN) ? mesh_src->mpoly : MEM_dupallocN(mesh_src->mpoly);
+
+ CustomData_add_layer(&tmp.ldata, CD_MLOOP, CD_ASSIGN, tmp.mloop, tmp.totloop);
+ CustomData_add_layer(&tmp.pdata, CD_MPOLY, CD_ASSIGN, tmp.mpoly, tmp.totpoly);
+ }
+
+ /* object had got displacement layer, should copy this layer to save sculpted data */
+ /* NOTE: maybe some other layers should be copied? nazgul */
+ if (CustomData_has_layer(&mesh_dst->ldata, CD_MDISPS)) {
+ if (totloop == mesh_dst->totloop) {
+ MDisps *mdisps = CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS);
+ CustomData_add_layer(&tmp.ldata, CD_MDISPS, alloctype, mdisps, totloop);
+ }
+ }
+
+ /* yes, must be before _and_ after tessellate */
+ BKE_mesh_update_customdata_pointers(&tmp, false);
+
+ /* since 2.65 caller must do! */
+ // BKE_mesh_tessface_calc(&tmp);
+
+ CustomData_free(&mesh_dst->vdata, mesh_dst->totvert);
+ CustomData_free(&mesh_dst->edata, mesh_dst->totedge);
+ CustomData_free(&mesh_dst->fdata, mesh_dst->totface);
+ CustomData_free(&mesh_dst->ldata, mesh_dst->totloop);
+ CustomData_free(&mesh_dst->pdata, mesh_dst->totpoly);
+
+ /* ok, this should now use new CD shapekey data,
+ * which should be fed through the modifier
+ * stack */
+ if (tmp.totvert != mesh_dst->totvert && !did_shapekeys && mesh_dst->key) {
+ printf("%s: YEEK! this should be recoded! Shape key loss!: ID '%s'\n", __func__, tmp.id.name);
+ if (tmp.key && !(tmp.id.tag & LIB_TAG_NO_MAIN)) {
+ id_us_min(&tmp.key->id);
+ }
+ tmp.key = NULL;
+ }
+
+ /* Clear selection history */
+ MEM_SAFE_FREE(tmp.mselect);
+ tmp.totselect = 0;
+ BLI_assert(ELEM(tmp.bb, NULL, mesh_dst->bb));
+ if (mesh_dst->bb) {
+ MEM_freeN(mesh_dst->bb);
+ tmp.bb = NULL;
+ }
+
+ /* skip the listbase */
+ MEMCPY_STRUCT_OFS(mesh_dst, &tmp, id.prev);
+
+ if (take_ownership) {
+ if (alloctype == CD_ASSIGN) {
+ CustomData_free_typemask(&mesh_src->vdata, mesh_src->totvert, ~mask);
+ CustomData_free_typemask(&mesh_src->edata, mesh_src->totedge, ~mask);
+ CustomData_free_typemask(&mesh_src->ldata, mesh_src->totloop, ~mask);
+ CustomData_free_typemask(&mesh_src->pdata, mesh_src->totpoly, ~mask);
+ }
+ BKE_id_free(NULL, mesh_src);
+ }
+}
+
+/* This is a Mesh-based copy of DM_to_meshkey() */
+void BKE_nomain_mesh_to_meshkey(Mesh *mesh_src, Mesh *mesh_dst, KeyBlock *kb)
+{
+ int a, totvert = mesh_src->totvert;
+ float *fp;
+ MVert *mvert;
+
+ if (totvert == 0 || mesh_dst->totvert == 0 || mesh_dst->totvert != totvert) {
+ return;
+ }
+
+ if (kb->data) MEM_freeN(kb->data);
+ kb->data = MEM_malloc_arrayN(mesh_dst->key->elemsize, mesh_dst->totvert, "kb->data");
+ kb->totelem = totvert;
+
+ fp = kb->data;
+ mvert = mesh_src->mvert;
+
+ for (a = 0; a < kb->totelem; a++, fp += 3, mvert++) {
+ copy_v3_v3(fp, mvert->co);
+ }
+}