Blender V4.5
ed_transverts.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "DNA_mesh_types.h"
10#include "MEM_guardedalloc.h"
11
12#include "DNA_armature_types.h"
13#include "DNA_curve_types.h"
14#include "DNA_curves_types.h"
15#include "DNA_lattice_types.h"
16#include "DNA_meta_types.h"
17#include "DNA_object_types.h"
19#include "DNA_scene_types.h"
20
21#include "BLI_listbase.h"
22#include "BLI_utildefines.h"
23
24#include "BKE_armature.hh"
25#include "BKE_context.hh"
26#include "BKE_curve.hh"
27#include "BKE_editmesh.hh"
28#include "BKE_lattice.hh"
29#include "BKE_mesh_iterators.hh"
30#include "BKE_mesh_types.hh"
31#include "BKE_object.hh"
32
33#include "DEG_depsgraph.hh"
35
36#include "ED_armature.hh"
37#include "ED_curves.hh"
38#include "ED_pointcloud.hh"
39
40#include "ANIM_armature.hh"
41
42#include "ED_transverts.hh" /* own include */
43
45{
46 /* NOTE: copied from `editobject.c`, now uses (almost) proper depsgraph. */
47
48 const int mode = tvs->mode;
50
51 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
52
53 if (obedit->type == OB_MESH) {
56 }
57 else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
58 Curve *cu = static_cast<Curve *>(obedit->data);
60 Nurb *nu = static_cast<Nurb *>(nurbs->first);
61
62 while (nu) {
63 /* keep handles' vectors unchanged */
64 if (nu->bezt && (mode & TM_SKIP_HANDLES)) {
65 int a = nu->pntsu;
66 TransVert *tv = tvs->transverts;
67 BezTriple *bezt = nu->bezt;
68
69 while (a--) {
70 if (bezt->hide == 0) {
71 bool skip_handle = false;
72 if (bezt->f2 & SELECT) {
73 skip_handle = (mode & TM_SKIP_HANDLES) != 0;
74 }
75
76 if ((bezt->f1 & SELECT) && !skip_handle) {
77 BLI_assert(tv->loc == bezt->vec[0]);
78 tv++;
79 }
80
81 if (bezt->f2 & SELECT) {
82 float v[3];
83
84 if (((bezt->f1 & SELECT) && !skip_handle) == 0) {
85 sub_v3_v3v3(v, tv->loc, tv->oldloc);
86 add_v3_v3(bezt->vec[0], v);
87 }
88
89 if (((bezt->f3 & SELECT) && !skip_handle) == 0) {
90 sub_v3_v3v3(v, tv->loc, tv->oldloc);
91 add_v3_v3(bezt->vec[2], v);
92 }
93
94 BLI_assert(tv->loc == bezt->vec[1]);
95 tv++;
96 }
97
98 if ((bezt->f3 & SELECT) && !skip_handle) {
99 BLI_assert(tv->loc == bezt->vec[2]);
100 tv++;
101 }
102 }
103
104 bezt++;
105 }
106 }
107
108 if (CU_IS_2D(cu)) {
110 }
111 BKE_nurb_handles_test(nu, NURB_HANDLE_TEST_EACH, false); /* test for bezier too */
112 nu = nu->next;
113 }
114 }
115 else if (obedit->type == OB_ARMATURE) {
116 bArmature *arm = static_cast<bArmature *>(obedit->data);
117 TransVert *tv = tvs->transverts;
118 int a = 0;
119
120 /* Ensure all bone tails are correctly adjusted */
121 LISTBASE_FOREACH (EditBone *, ebo, arm->edbo) {
123 continue;
124 }
125 /* adjust tip if both ends selected */
126 if ((ebo->flag & BONE_ROOTSEL) && (ebo->flag & BONE_TIPSEL)) {
127 if (tv) {
128 float diffvec[3];
129
130 sub_v3_v3v3(diffvec, tv->loc, tv->oldloc);
131 add_v3_v3(ebo->tail, diffvec);
132
133 a++;
134 if (a < tvs->transverts_tot) {
135 tv++;
136 }
137 }
138 }
139 }
140
141 /* Ensure all bones are correctly adjusted */
142 LISTBASE_FOREACH (EditBone *, ebo, arm->edbo) {
143 if ((ebo->flag & BONE_CONNECTED) && ebo->parent) {
144 /* If this bone has a parent tip that has been moved */
145 if (blender::animrig::bone_is_visible_editbone(arm, ebo->parent) &&
146 (ebo->parent->flag & BONE_TIPSEL))
147 {
148 copy_v3_v3(ebo->head, ebo->parent->tail);
149 }
150 /* If this bone has a parent tip that has NOT been moved */
151 else {
152 copy_v3_v3(ebo->parent->tail, ebo->head);
153 }
154 }
155 }
156 if (arm->flag & ARM_MIRROR_EDIT) {
158 }
159 }
160 else if (obedit->type == OB_LATTICE) {
161 Lattice *lt = static_cast<Lattice *>(obedit->data);
162
163 if (lt->editlatt->latt->flag & LT_OUTSIDE) {
165 }
166 }
167 else if (obedit->type == OB_CURVES) {
168 Curves *curves_id = static_cast<Curves *>(obedit->data);
169 blender::bke::CurvesGeometry &curves = curves_id->geometry.wrap();
170 curves.tag_positions_changed();
172 }
173 else if (obedit->type == OB_POINTCLOUD) {
174 PointCloud *pointcloud = static_cast<PointCloud *>(obedit->data);
175 pointcloud->tag_positions_changed();
176 }
177}
178
179static void set_mapped_co(void *vuserdata, int index, const float co[3], const float /*no*/[3])
180{
181 void **userdata = static_cast<void **>(vuserdata);
182 BMEditMesh *em = static_cast<BMEditMesh *>(userdata[0]);
183 TransVert *tv = static_cast<TransVert *>(userdata[1]);
184 BMVert *eve = BM_vert_at_index(em->bm, index);
185
186 if (BM_elem_index_get(eve) != TM_INDEX_SKIP) {
187 tv = &tv[BM_elem_index_get(eve)];
188
189 /* Be clever, get the closest vertex to the original,
190 * behaves most logically when the mirror modifier is used for eg #33051. */
191 if ((tv->flag & TX_VERT_USE_MAPLOC) == 0) {
192 /* first time */
193 copy_v3_v3(tv->maploc, co);
195 }
196 else {
197 /* find best location to use */
198 if (len_squared_v3v3(eve->co, co) < len_squared_v3v3(eve->co, tv->maploc)) {
199 copy_v3_v3(tv->maploc, co);
200 }
201 }
202 }
203}
204
206{
207 return ELEM(obedit->type,
210 OB_MESH,
211 OB_SURF,
213 OB_MBALL,
214 OB_CURVES,
216}
217
218void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, const int mode)
219{
220 using namespace blender;
221
222 Nurb *nu;
223 BezTriple *bezt;
224 BPoint *bp;
225 TransVert *tv = nullptr;
226 MetaElem *ml;
227 BMVert *eve;
228 int a;
229
230 tvs->transverts_tot = 0;
231
232 if (obedit->type == OB_MESH) {
233 const Object *object_orig = DEG_get_original(obedit);
234 const Mesh &mesh = *static_cast<Mesh *>(object_orig->data);
235 BMEditMesh *em = mesh.runtime->edit_mesh.get();
236 BMesh *bm = em->bm;
237 BMIter iter;
238 void *userdata[2] = {em, nullptr};
239 // int proptrans = 0; /*UNUSED*/
240
241 /* abuses vertex index all over, set, just set dirty here,
242 * perhaps this could use its own array instead? - campbell */
243
244 /* transform now requires awareness for select mode, so we tag the f1 flags in verts */
245 tvs->transverts_tot = 0;
246 if (em->selectmode & SCE_SELECT_VERTEX) {
247 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
249 BM_elem_index_set(eve, TM_INDEX_ON); /* set_dirty! */
250 tvs->transverts_tot++;
251 }
252 else {
253 BM_elem_index_set(eve, TM_INDEX_OFF); /* set_dirty! */
254 }
255 }
256 }
257 else if (em->selectmode & SCE_SELECT_EDGE) {
258 BMEdge *eed;
259
260 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
261 BM_elem_index_set(eve, TM_INDEX_OFF); /* set_dirty! */
262 }
263
264 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
266 BM_elem_index_set(eed->v1, TM_INDEX_ON); /* set_dirty! */
267 BM_elem_index_set(eed->v2, TM_INDEX_ON); /* set_dirty! */
268 }
269 }
270
271 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
272 if (BM_elem_index_get(eve) == TM_INDEX_ON) {
273 tvs->transverts_tot++;
274 }
275 }
276 }
277 else {
278 BMFace *efa;
279
280 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
281 BM_elem_index_set(eve, TM_INDEX_OFF); /* set_dirty! */
282 }
283
284 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
286 BMIter liter;
287 BMLoop *l;
288
289 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
290 BM_elem_index_set(l->v, TM_INDEX_ON); /* set_dirty! */
291 }
292 }
293 }
294
295 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
296 if (BM_elem_index_get(eve) == TM_INDEX_ON) {
297 tvs->transverts_tot++;
298 }
299 }
300 }
301 /* for any of the 3 loops above which all dirty the indices */
302 bm->elem_index_dirty |= BM_VERT;
303
304 /* and now make transverts */
305 if (tvs->transverts_tot) {
306 tv = tvs->transverts = MEM_calloc_arrayN<TransVert>(tvs->transverts_tot, __func__);
307
308 a = 0;
309 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
310 if (BM_elem_index_get(eve)) {
311 BM_elem_index_set(eve, a); /* set_dirty! */
312 copy_v3_v3(tv->oldloc, eve->co);
313 tv->loc = eve->co;
314 tv->flag = (BM_elem_index_get(eve) == TM_INDEX_ON) ? SELECT : 0;
315
316 if (mode & TM_CALC_NORMALS) {
318 copy_v3_v3(tv->normal, eve->no);
319 }
320
321 tv++;
322 a++;
323 }
324 else {
325 BM_elem_index_set(eve, TM_INDEX_SKIP); /* set_dirty! */
326 }
327 }
328 /* set dirty already, above */
329
330 userdata[1] = tvs->transverts;
331 }
332
333 if (mode & TM_CALC_MAPLOC) {
334 const Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(obedit);
335 if (tvs->transverts && editmesh_eval_cage) {
338 editmesh_eval_cage, set_mapped_co, userdata, MESH_FOREACH_NOP);
339 }
340 }
341 }
342 else if (obedit->type == OB_ARMATURE) {
343 bArmature *arm = static_cast<bArmature *>(obedit->data);
344 int totmalloc = BLI_listbase_count(arm->edbo);
345
346 totmalloc *= 2; /* probably overkill but bones can have 2 trans verts each */
347
348 tv = tvs->transverts = MEM_calloc_arrayN<TransVert>(totmalloc, __func__);
349
350 LISTBASE_FOREACH (EditBone *, ebo, arm->edbo) {
352 const bool tipsel = (ebo->flag & BONE_TIPSEL) != 0;
353 const bool rootsel = (ebo->flag & BONE_ROOTSEL) != 0;
354 const bool rootok = !(ebo->parent && (ebo->flag & BONE_CONNECTED) &&
356 (ebo->parent->flag & BONE_TIPSEL)));
357
358 if ((tipsel && rootsel) || (rootsel)) {
359 /* Don't add the tip (unless mode & TM_ALL_JOINTS, for getting all joints),
360 * otherwise we get zero-length bones as tips will snap to the same
361 * location as heads.
362 */
363 if (rootok) {
364 copy_v3_v3(tv->oldloc, ebo->head);
365 tv->loc = ebo->head;
366 tv->flag = SELECT;
367 tv++;
368 tvs->transverts_tot++;
369 }
370
371 if ((mode & TM_ALL_JOINTS) && (tipsel)) {
372 copy_v3_v3(tv->oldloc, ebo->tail);
373 tv->loc = ebo->tail;
374 tv->flag = SELECT;
375 tv++;
376 tvs->transverts_tot++;
377 }
378 }
379 else if (tipsel) {
380 copy_v3_v3(tv->oldloc, ebo->tail);
381 tv->loc = ebo->tail;
382 tv->flag = SELECT;
383 tv++;
384 tvs->transverts_tot++;
385 }
386 }
387 }
388 }
389 else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
390 Curve *cu = static_cast<Curve *>(obedit->data);
391 int totmalloc = 0;
393
394 LISTBASE_FOREACH (Nurb *, nu, nurbs) {
395 if (nu->type == CU_BEZIER) {
396 totmalloc += 3 * nu->pntsu;
397 }
398 else {
399 totmalloc += nu->pntsu * nu->pntsv;
400 }
401 }
402 tv = tvs->transverts = MEM_calloc_arrayN<TransVert>(totmalloc, __func__);
403
404 nu = static_cast<Nurb *>(nurbs->first);
405 while (nu) {
406 if (nu->type == CU_BEZIER) {
407 a = nu->pntsu;
408 bezt = nu->bezt;
409 while (a--) {
410 if (bezt->hide == 0) {
411 bool skip_handle = false;
412 if (bezt->f2 & SELECT) {
413 skip_handle = (mode & TM_SKIP_HANDLES) != 0;
414 }
415
416 if ((bezt->f1 & SELECT) && !skip_handle) {
417 copy_v3_v3(tv->oldloc, bezt->vec[0]);
418 tv->loc = bezt->vec[0];
419 tv->flag = bezt->f1 & SELECT;
420
421 if (mode & TM_CALC_NORMALS) {
423 BKE_nurb_bezt_calc_plane(nu, bezt, tv->normal);
424 }
425
426 tv++;
427 tvs->transverts_tot++;
428 }
429 if (bezt->f2 & SELECT) {
430 copy_v3_v3(tv->oldloc, bezt->vec[1]);
431 tv->loc = bezt->vec[1];
432 tv->flag = bezt->f2 & SELECT;
433
434 if (mode & TM_CALC_NORMALS) {
436 BKE_nurb_bezt_calc_plane(nu, bezt, tv->normal);
437 }
438
439 tv++;
440 tvs->transverts_tot++;
441 }
442 if ((bezt->f3 & SELECT) && !skip_handle) {
443 copy_v3_v3(tv->oldloc, bezt->vec[2]);
444 tv->loc = bezt->vec[2];
445 tv->flag = bezt->f3 & SELECT;
446
447 if (mode & TM_CALC_NORMALS) {
449 BKE_nurb_bezt_calc_plane(nu, bezt, tv->normal);
450 }
451
452 tv++;
453 tvs->transverts_tot++;
454 }
455 }
456 bezt++;
457 }
458 }
459 else {
460 a = nu->pntsu * nu->pntsv;
461 bp = nu->bp;
462 while (a--) {
463 if (bp->hide == 0) {
464 if (bp->f1 & SELECT) {
465 copy_v3_v3(tv->oldloc, bp->vec);
466 tv->loc = bp->vec;
467 tv->flag = bp->f1 & SELECT;
468 tv++;
469 tvs->transverts_tot++;
470 }
471 }
472 bp++;
473 }
474 }
475 nu = nu->next;
476 }
477 }
478 else if (obedit->type == OB_MBALL) {
479 MetaBall *mb = static_cast<MetaBall *>(obedit->data);
480 int totmalloc = BLI_listbase_count(mb->editelems);
481
482 tv = tvs->transverts = MEM_calloc_arrayN<TransVert>(totmalloc, __func__);
483
484 ml = static_cast<MetaElem *>(mb->editelems->first);
485 while (ml) {
486 if (ml->flag & SELECT) {
487 tv->loc = &ml->x;
488 copy_v3_v3(tv->oldloc, tv->loc);
489 tv->flag = SELECT;
490 tv++;
491 tvs->transverts_tot++;
492 }
493 ml = ml->next;
494 }
495 }
496 else if (obedit->type == OB_LATTICE) {
497 Lattice *lt = static_cast<Lattice *>(obedit->data);
498
499 bp = lt->editlatt->latt->def;
500
501 a = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * lt->editlatt->latt->pntsw;
502
503 tv = tvs->transverts = MEM_calloc_arrayN<TransVert>(a, __func__);
504
505 while (a--) {
506 if (bp->f1 & SELECT) {
507 if (bp->hide == 0) {
508 copy_v3_v3(tv->oldloc, bp->vec);
509 tv->loc = bp->vec;
510 tv->flag = bp->f1 & SELECT;
511 tv++;
512 tvs->transverts_tot++;
513 }
514 }
515 bp++;
516 }
517 }
518 else if (obedit->type == OB_CURVES) {
519 Curves *curves_id = static_cast<Curves *>(obedit->data);
521 curves_id->geometry.wrap(), tvs, ((mode & TM_SKIP_HANDLES) != 0));
522 }
523 else if (obedit->type == OB_POINTCLOUD) {
524 PointCloud *pointcloud = static_cast<PointCloud *>(obedit->data);
525
526 IndexMaskMemory memory;
528 memory);
529 MutableSpan<float3> positions = pointcloud->positions_for_write();
530
531 tvs->transverts = MEM_calloc_arrayN<TransVert>(selection.size(), __func__);
532 tvs->transverts_tot = selection.size();
533
534 selection.foreach_index(GrainSize(1024), [&](const int64_t i, const int64_t pos) {
535 TransVert &tv = tvs->transverts[pos];
536 tv.loc = positions[i];
537 tv.flag = SELECT;
538 copy_v3_v3(tv.oldloc, tv.loc);
539 });
540 }
541
542 if (!tvs->transverts_tot && tvs->transverts) {
543 /* Prevent memory leak. happens for curves/lattices due to
544 * difficult condition of adding points to trans data. */
545 MEM_freeN(tvs->transverts);
546 tvs->transverts = nullptr;
547 }
548
549 tvs->mode = mode;
550}
551
553{
555 tvs->transverts_tot = 0;
556}
557
559{
560 Object *obedit = CTX_data_edit_object(C);
561 if (obedit) {
562 if (ED_transverts_check_obedit(obedit)) {
563 return true;
564 }
565 }
566 return false;
567}
Functions to deal with Armatures.
Object * CTX_data_edit_object(const bContext *C)
@ NURB_HANDLE_TEST_EACH
Definition BKE_curve.hh:61
void BKE_nurb_project_2d(Nurb *nu)
Definition curve.cc:688
#define CU_IS_2D(cu)
Definition BKE_curve.hh:89
void BKE_nurb_handles_test(Nurb *nu, eNurbHandleTest_Mode handle_mode, bool use_around_local)
Definition curve.cc:4088
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:423
void BKE_nurb_bezt_calc_plane(Nurb *nu, BezTriple *bezt, float r_plane[3])
Definition curve.cc:1026
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
void outside_lattice(Lattice *lt)
Definition lattice.cc:406
@ MESH_FOREACH_NOP
void BKE_mesh_foreach_mapped_vert(const Mesh *mesh, void(*func)(void *user_data, int index, const float co[3], const float no[3]), void *user_data, MeshForeachFlag flag)
General operations, lookup, etc. for blender objects.
const Mesh * BKE_object_get_editmesh_eval_cage(const Object *object)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_original(T *id)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ BONE_ROOTSEL
@ BONE_TIPSEL
@ BONE_CONNECTED
@ ARM_MIRROR_EDIT
@ CU_BEZIER
@ LT_OUTSIDE
Object is a sort of wrapper for general info.
@ OB_LATTICE
@ OB_MBALL
@ OB_SURF
@ OB_ARMATURE
@ OB_MESH
@ OB_POINTCLOUD
@ OB_CURVES_LEGACY
@ OB_CURVES
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ TM_INDEX_OFF
@ TM_INDEX_ON
@ TM_INDEX_SKIP
@ TM_SKIP_HANDLES
@ TM_CALC_MAPLOC
@ TM_CALC_NORMALS
@ TM_ALL_JOINTS
@ TX_VERT_USE_MAPLOC
@ TX_VERT_USE_NORMAL
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
void ED_armature_edit_transform_mirror_update(Object *obedit)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
#define BM_elem_index_get(ele)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_FACE
BMesh * bm
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
BLI_INLINE BMVert * BM_vert_at_index(BMesh *bm, const int index)
void BM_mesh_normals_update(BMesh *bm)
#define BM_VERT
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
long long int int64_t
int64_t size() const
void foreach_index(Fn &&fn) const
#define SELECT
bool ED_transverts_check_obedit(const Object *obedit)
bool ED_transverts_poll(bContext *C)
static void set_mapped_co(void *vuserdata, int index, const float co[3], const float[3])
void ED_transverts_free(TransVertStore *tvs)
void ED_transverts_update_obedit(TransVertStore *tvs, Object *obedit)
void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, const int mode)
uint pos
#define MEM_SAFE_FREE(v)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
bool bone_is_visible_editbone(const bArmature *armature, const EditBone *ebone)
void transverts_from_curves_positions_create(bke::CurvesGeometry &curves, TransVertStore *tvs, const bool skip_handles)
IndexMask retrieve_selected_points(const PointCloud &pointcloud, IndexMaskMemory &memory)
Definition selection.cc:342
BMVert * v1
BMVert * v2
short selectmode
float co[3]
float no[3]
uint8_t f1
float vec[4]
float vec[3][3]
CurvesGeometry geometry
struct Lattice * latt
Definition DNA_ID.h:404
struct EditLatt * editlatt
struct BPoint * def
void * first
MeshRuntimeHandle * runtime
ListBase * editelems
struct MetaElem * next
struct Nurb * next
short type
BezTriple * bezt
BPoint * bp
TransVert * transverts
float * loc
float maploc[3]
float oldloc[3]
float normal[3]
ListBase * edbo
i
Definition text_draw.cc:230