/* * $Id$ * * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * This is a new part of Blender, partially based on NMesh.c API. * * Contributor(s): Ken Hughes * * ***** END GPL LICENSE BLOCK ***** */ #include "Mesh.h" /*This must come first*/ #include "MEM_guardedalloc.h" #include "DNA_key_types.h" #include "DNA_armature_types.h" #include "DNA_scene_types.h" #include "DNA_oops_types.h" #include "DNA_space_types.h" #include "DNA_curve_types.h" #include "DNA_meta_types.h" #include "DNA_modifier_types.h" #include "BDR_editface.h" /* make_tfaces */ #include "BDR_vpaint.h" #include "BDR_editobject.h" #include "BIF_editdeform.h" #include "BIF_editkey.h" /* insert_meshkey */ #include "BIF_space.h" /* REMAKEIPO - insert_meshkey */ #include "BIF_editview.h" #include "BIF_editmesh.h" #include "BIF_meshtools.h" #include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_mesh.h" #include "BKE_material.h" #include "BKE_main.h" #include "BKE_multires.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_DerivedMesh.h" #include "BKE_object.h" #include "BKE_mball.h" #include "BKE_utildefines.h" #include "BKE_depsgraph.h" #include "BSE_edit.h" /* for countall(); */ #include "BKE_curve.h" /* for copy_curve(); */ #include "BKE_modifier.h" /* for modifier_new(), modifier_copyData(); */ #include "BKE_idprop.h" #include "BLI_arithb.h" #include "BLI_blenlib.h" #include "BLI_memarena.h" #include "blendef.h" #include "mydevice.h" #include "butspace.h" /* for mesh tools */ #include "Object.h" #include "Key.h" #include "Image.h" #include "Material.h" #include "Mathutils.h" #include "IDProp.h" #include "meshPrimitive.h" #include "constant.h" #include "gen_utils.h" #include "gen_library.h" #include "multires.h" /* EXPP Mesh defines */ #define MESH_SMOOTHRESH 30 #define MESH_SMOOTHRESH_MIN 1 #define MESH_SMOOTHRESH_MAX 80 #define MESH_SUBDIV 1 #define MESH_SUBDIV_MIN 0 #define MESH_SUBDIV_MAX 6 #define MESH_HASFACEUV 0 #define MESH_HASMCOL 1 #define MESH_HASVERTUV 2 #define MESH_HASMULTIRES 3 #define MESH_MULTIRES_LEVEL 0 #define MESH_MULTIRES_EDGE 1 #define MESH_MULTIRES_PIN 2 #define MESH_MULTIRES_RENDER 3 #define MESH_TOOL_TOSPHERE 0 #define MESH_TOOL_VERTEXSMOOTH 1 #define MESH_TOOL_FLIPNORM 2 #define MESH_TOOL_SUBDIV 3 #define MESH_TOOL_REMDOUB 4 #define MESH_TOOL_FILL 5 #define MESH_TOOL_RECALCNORM 6 #define MESH_TOOL_TRI2QUAD 7 #define MESH_TOOL_QUAD2TRI 8 static PyObject *MVertSeq_CreatePyObject( Mesh * mesh ); static PyObject *MFaceSeq_CreatePyObject( Mesh * mesh ); static PyObject *MEdgeSeq_CreatePyObject( Mesh * mesh ); static PyObject *MFace_CreatePyObject( Mesh * mesh, int i ); static PyObject *MEdge_CreatePyObject( Mesh * mesh, int i ); #define MFACE_VERT_BADRANGE_CHECK(me, face) ((int)face->v1 >= me->totvert || (int)face->v2 >= me->totvert || (int)face->v3 >= me->totvert || (int)face->v4 >= me->totvert) #define MEDGE_VERT_BADRANGE_CHECK(me, edge) ((int)edge->v1 >= me->totvert || (int)edge->v2 >= me->totvert) /************************************************************************ * * internal utilities * ************************************************************************/ /* * internal structures used for sorting edges and faces */ typedef struct SrchEdges { unsigned int v[2]; /* indices for verts */ unsigned char swap; /* non-zero if verts swapped */ unsigned int index; /* index in original param list of this edge */ /* (will be used by findEdges) */ } SrchEdges; typedef struct SrchFaces { unsigned int v[4]; /* indices for verts */ unsigned int index; /* index in original param list of this edge */ unsigned char order; /* order of original verts, bitpacked */ } SrchFaces; typedef struct FaceEdges { unsigned int v[2]; /* search key (vert indices) */ unsigned int index; /* location in edge list */ unsigned char sel; /* selection state */ } FaceEdges; /* * compare edges by vertex indices */ int medge_comp( const void *va, const void *vb ) { const unsigned int *a = ((SrchEdges *)va)->v; const unsigned int *b = ((SrchEdges *)vb)->v; /* compare first index for differences */ if (a[0] < b[0]) return -1; else if (a[0] > b[0]) return 1; /* if first indices equal, compare second index for differences */ else if (a[1] < b[1]) return -1; else return (a[1] > b[1]); } /* * compare edges by insert list indices */ int medge_index_comp( const void *va, const void *vb ) { const SrchEdges *a = (SrchEdges *)va; const SrchEdges *b = (SrchEdges *)vb; /* compare list indices for differences */ if (a->index < b->index) return -1; else return (a->index > b->index); } /* * compare faces by vertex indices */ int mface_comp( const void *va, const void *vb ) { const SrchFaces *a = va; const SrchFaces *b = vb; int i; /* compare indices, first to last, for differences */ for( i = 0; i < 4; ++i ) { if( a->v[i] < b->v[i] ) return -1; if( a->v[i] > b->v[i] ) return 1; } /* * don't think this needs be done; if order is different then either * (a) the face is good, just reversed or has a different starting * vertex, or (b) face is bad (for 4 verts) and there's a "twist" */ #if 0 /* if all the same verts, compare their order */ if( a->order < b->order ) return -1; if( a->order > b->order ) return 1; #endif return 0; } /* * compare faces by insert list indices */ int mface_index_comp( const void *va, const void *vb ) { const SrchFaces *a = va; const SrchFaces *b = vb; /* compare indices, first to last, for differences */ if( a->index < b->index ) return -1; if( a->index > b->index ) return 1; return 0; } /* * compare edges by vertex indices */ int faceedge_comp( const void *va, const void *vb ) { const unsigned int *a = ((FaceEdges *)va)->v; const unsigned int *b = ((FaceEdges *)vb)->v; /* compare first index for differences */ if (a[0] < b[0]) return -1; else if (a[0] > b[0]) return 1; /* if first indices equal, compare second index for differences */ else if (a[1] < b[1]) return -1; else return (a[1] > b[1]); } /* * update the DAG for all objects linked to this mesh */ static void mesh_update( Mesh * mesh ) { Object_updateDag( (void *) mesh ); } /* * delete vertices from mesh, then delete edges/keys/faces which used those * vertices * * Deletion is done by "smart compaction"; groups of verts/edges/faces which * remain in the list are copied to new list instead of one at a time. Since * Blender has no realloc we would have to copy things anyway, so there's no * point trying to fill empty entries with data from the end of the lists. * * vert_table is a lookup table for mapping old verts to new verts (after the * vextex list has deleted vertices removed). Each entry contains the * vertex's new index. */ static void delete_verts( Mesh *mesh, unsigned int *vert_table, int to_delete ) { /* * (1) allocate vertex table (initialize contents to 0) * (2) mark each vertex being deleted in vertex table (= UINT_MAX) * (3) update remaining table entries with "new" vertex index (after * compaction) * (4) allocate new vertex list * (5) do "smart copy" of vertices from old to new * * each moved vertex is entered into vertex table: if vertex i is * moving to index j in new list * vert_table[i] = j; * (6) if keys, do "smart copy" of keys * (7) process edge list * update vert index * delete edges which delete verts * (7) allocate new edge list * (8) do "smart copy" of edges * (9) allocate new face list * (10) do "smart copy" of face */ unsigned int *tmpvert; CustomData vdata; int i, count, state, dstindex, totvert; totvert = mesh->totvert - to_delete; CustomData_copy( &mesh->vdata, &vdata, CD_MASK_MESH, CD_CALLOC, totvert ); /* * do "smart compaction" of the table; find and copy groups of vertices * which are not being deleted */ dstindex = 0; tmpvert = vert_table; count = 0; state = 1; for( i = 0; i < mesh->totvert; ++i, ++tmpvert ) { switch( state ) { case 0: /* skipping verts */ if( *tmpvert == UINT_MAX ) { ++count; } else { count = 1; state = 1; } break; case 1: /* gathering verts */ if( *tmpvert != UINT_MAX ) { ++count; } else { if( count ) { CustomData_copy_data( &mesh->vdata, &vdata, i-count, dstindex, count ); dstindex += count; } count = 1; state = 0; } } } /* if we were gathering verts at the end of the loop, copy those */ if( state && count ) CustomData_copy_data( &mesh->vdata, &vdata, i-count, dstindex, count ); /* delete old vertex list, install the new one, update vertex count */ CustomData_free( &mesh->vdata, mesh->totvert ); mesh->vdata = vdata; mesh->totvert = totvert; mesh_update_customdata_pointers( mesh ); } static void delete_edges( Mesh *mesh, unsigned int *vert_table, int to_delete ) { int i; MEdge *tmpedge; /* if not given, then mark and count edges to be deleted */ if( !to_delete ) { tmpedge = mesh->medge; for( i = mesh->totedge; i-- ; ++tmpedge ) if( vert_table[tmpedge->v1] == UINT_MAX || vert_table[tmpedge->v2] == UINT_MAX ) { tmpedge->v1 = UINT_MAX; ++to_delete; } } /* if there are edges to delete, handle it */ if( to_delete ) { CustomData edata; int count, state, dstindex, totedge; /* allocate new edge list and populate */ totedge = mesh->totedge - to_delete; CustomData_copy( &mesh->edata, &edata, CD_MASK_MESH, CD_CALLOC, totedge); /* * do "smart compaction" of the edges; find and copy groups of edges * which are not being deleted */ dstindex = 0; tmpedge = mesh->medge; count = 0; state = 1; for( i = 0; i < mesh->totedge; ++i, ++tmpedge ) { switch( state ) { case 0: /* skipping edges */ if( tmpedge->v1 == UINT_MAX ) { ++count; } else { count = 1; state = 1; } break; case 1: /* gathering edges */ if( tmpedge->v1 != UINT_MAX ) { ++count; } else { if( count ) { CustomData_copy_data( &mesh->edata, &edata, i-count, dstindex, count ); dstindex += count; } count = 1; state = 0; } } /* if edge is good, update vertex indices */ } /* copy any pending good edges */ if( state && count ) CustomData_copy_data( &mesh->edata, &edata, i-count, dstindex, count ); /* delete old edge list, install the new one, update vertex count */ CustomData_free( &mesh->edata, mesh->totedge ); mesh->edata = edata; mesh->totedge = totedge; mesh_update_customdata_pointers( mesh ); } /* if vertices were deleted, update edge's vertices */ if( vert_table ) { tmpedge = mesh->medge; for( i = mesh->totedge; i--; ++tmpedge ) { tmpedge->v1 = vert_table[tmpedge->v1]; tmpedge->v2 = vert_table[tmpedge->v2]; } } } /* * Since all faces must have 3 or 4 verts, we can't have v3 or v4 be zero. * If that happens during the deletion, we have to shuffle the vertices * around; otherwise it can cause an Eeekadoodle or worse. If there are * texture faces as well, they have to be shuffled as well. * * (code borrowed from test_index_face() in mesh.c, but since we know the * faces already have correct number of vertices, this is a little faster) */ static void eeek_fix( MFace *mface, int len4 ) { /* if 4 verts, then neither v3 nor v4 can be zero */ if( len4 ) { if( !mface->v3 || !mface->v4 ) { SWAP( int, mface->v1, mface->v3 ); SWAP( int, mface->v2, mface->v4 ); } } else if( !mface->v3 ) { /* if 2 verts, then just v3 cannot be zero (v4 MUST be zero) */ SWAP( int, mface->v1, mface->v2 ); SWAP( int, mface->v2, mface->v3 ); } } static void delete_faces( Mesh *mesh, unsigned int *vert_table, int to_delete ) { int i; MFace *tmpface; /* if there are faces to delete, handle it */ if( to_delete ) { CustomData fdata; int count, state, dstindex, totface; totface = mesh->totface - to_delete; CustomData_copy( &mesh->fdata, &fdata, CD_MASK_MESH, CD_CALLOC, totface ); /* * do "smart compaction" of the faces; find and copy groups of faces * which are not being deleted */ dstindex = 0; tmpface = mesh->mface; count = 0; state = 1; for( i = 0; i < mesh->totface; ++i ) { switch( state ) { case 0: /* skipping faces */ if( tmpface->v1 == UINT_MAX ) { ++count; } else { count = 1; state = 1; } break; case 1: /* gathering faces */ if( tmpface->v1 != UINT_MAX ) { ++count; } else { if( count ) { CustomData_copy_data( &mesh->fdata, &fdata, i-count, dstindex, count ); dstindex += count; } count = 1; state = 0; } } ++tmpface; } /* if we were gathering faces at the end of the loop, copy those */ if ( state && count ) CustomData_copy_data( &mesh->fdata, &fdata, i-count, dstindex, count ); /* delete old face list, install the new one, update face count */ CustomData_free( &mesh->fdata, mesh->totface ); mesh->fdata = fdata; mesh->totface = totface; mesh_update_customdata_pointers( mesh ); } /* if vertices were deleted, update face's vertices */ if( vert_table ) { tmpface = mesh->mface; for( i = 0; itotface; ++i, ++tmpface ) { int len4 = tmpface->v4; tmpface->v1 = vert_table[tmpface->v1]; tmpface->v2 = vert_table[tmpface->v2]; tmpface->v3 = vert_table[tmpface->v3]; if(len4) tmpface->v4 = vert_table[tmpface->v4]; else tmpface->v4 = 0; test_index_face( tmpface, &mesh->fdata, i, len4? 4: 3); } } } /* * fill up vertex lookup table with old-to-new mappings * * returns the number of vertices marked for deletion */ static unsigned int make_vertex_table( unsigned int *vert_table, int count ) { int i; unsigned int *tmpvert = vert_table; unsigned int to_delete = 0; unsigned int new_index = 0; /* fill the lookup table with old->new index mappings */ for( i = count; i; --i, ++tmpvert ) { if( *tmpvert == UINT_MAX ) { ++to_delete; } else { *tmpvert = new_index; ++new_index; } } return to_delete; } /************************************************************************ * * Color attributes * ************************************************************************/ /* * get a color attribute */ static PyObject *MCol_getAttr( BPy_MCol * self, void *type ) { unsigned char param; switch( (long)type ) { case 'R': /* these are backwards, but that how it works */ param = self->color->b; break; case 'G': param = self->color->g; break; case 'B': /* these are backwards, but that how it works */ param = self->color->r; break; case 'A': param = self->color->a; break; default: { char errstr[1024]; sprintf( errstr, "undefined type '%d' in MCol_getAttr", (int)((long)type & 0xff)); return EXPP_ReturnPyObjError( PyExc_RuntimeError, errstr ); } } return PyInt_FromLong( param ); } /* * set a color attribute */ static int MCol_setAttr( BPy_MCol * self, PyObject * value, void * type ) { unsigned char *param; switch( (long)type ) { case 'R': /* these are backwards, but that how it works */ param = (unsigned char *)&self->color->b; break; case 'G': param = (unsigned char *)&self->color->g; break; case 'B': /* these are backwards, but that how it works */ param = (unsigned char *)&self->color->r; break; case 'A': param = (unsigned char *)&self->color->a; break; default: { char errstr[1024]; sprintf( errstr, "undefined type '%d' in MCol_setAttr", (int)((long)type & 0xff)); return EXPP_ReturnIntError( PyExc_RuntimeError, errstr ); } } return EXPP_setIValueClamped( value, param, 0, 255, 'b' ); } /************************************************************************ * * Python MCol_Type attributes get/set structure * ************************************************************************/ static PyGetSetDef BPy_MCol_getseters[] = { {"r", (getter)MCol_getAttr, (setter)MCol_setAttr, "red component", (void *)'R'}, {"g", (getter)MCol_getAttr, (setter)MCol_setAttr, "green component", (void *)'G'}, {"b", (getter)MCol_getAttr, (setter)MCol_setAttr, "blue component", (void *)'B'}, {"a", (getter)MCol_getAttr, (setter)MCol_setAttr, "alpha component", (void *)'A'}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /*----------------------------object[]--------------------------- sequence accessor (get)*/ static PyObject *MCol_item(BPy_MCol * self, int i) { unsigned char param; switch (i) { case 0: param = self->color->b; break; case 1: param = self->color->g; break; case 2: param = self->color->r; break; case 3: param = self->color->a; break; default: return EXPP_ReturnPyObjError(PyExc_IndexError, "vector[index] = x: assignment index out of range\n"); } return PyInt_FromLong( param ); } /*----------------------------object[]------------------------- sequence accessor (set)*/ static int MCol_ass_item(BPy_MCol * self, int i, PyObject * value) { unsigned char *param; switch (i) { case 0: param = (unsigned char *)&self->color->b; /* reversed? why */ break; case 1: param = (unsigned char *)&self->color->g; break; case 2: param = (unsigned char *)&self->color->r; /* reversed? why */ break; case 3: param = (unsigned char *)&self->color->a; break; default: { return EXPP_ReturnIntError( PyExc_RuntimeError, "Index out of range" ); } } return EXPP_setIValueClamped( value, param, 0, 255, 'b' ); } /************************************************************************ * * Python MCol_Type methods * ************************************************************************/ static PyObject *MCol_repr( BPy_MCol * self ) { return PyString_FromFormat( "[MCol %d %d %d %d]", (int)self->color->b, (int)self->color->g, (int)self->color->r, (int)self->color->a ); } /*-----------------PROTCOL DECLARATIONS--------------------------*/ static PySequenceMethods MCol_SeqMethods = { (inquiry) NULL, /* sq_length */ (binaryfunc) NULL, /* sq_concat */ (intargfunc) NULL, /* sq_repeat */ (intargfunc) MCol_item, /* sq_item */ (intintargfunc) NULL, /* sq_slice */ (intobjargproc) MCol_ass_item, /* sq_ass_item */ (intintobjargproc) NULL, /* sq_ass_slice */ }; /************************************************************************ * * Python MCol_Type structure definition * ************************************************************************/ PyTypeObject MCol_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "." */ "Blender MCol", /* char *tp_name; */ sizeof( BPy_MCol ), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ NULL, /* destructor tp_dealloc; */ NULL, /* printfunc tp_print; */ NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ NULL, /* cmpfunc tp_compare; */ ( reprfunc ) MCol_repr, /* reprfunc tp_repr; */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ &MCol_SeqMethods, /* PySequenceMethods *tp_as_sequence; */ NULL, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ NULL, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ NULL, /* richcmpfunc tp_richcompare; */ /*** weak reference enabler ***/ 0, /* long tp_weaklistoffset; */ /*** Added in release 2.2 ***/ /* Iterators */ NULL, /* getiterfunc tp_iter; */ NULL, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ NULL, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ BPy_MCol_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ NULL, /* descrsetfunc tp_descr_set; */ 0, /* long tp_dictoffset; */ NULL, /* initproc tp_init; */ NULL, /* allocfunc tp_alloc; */ NULL, /* newfunc tp_new; */ /* Low-level free-memory routine */ NULL, /* freefunc tp_free; */ /* For PyObject_IS_GC */ NULL, /* inquiry tp_is_gc; */ NULL, /* PyObject *tp_bases; */ /* method resolution order */ NULL, /* PyObject *tp_mro; */ NULL, /* PyObject *tp_cache; */ NULL, /* PyObject *tp_subclasses; */ NULL, /* PyObject *tp_weaklist; */ NULL }; static PyObject *MCol_CreatePyObject( MCol * color ) { BPy_MCol *obj = PyObject_NEW( BPy_MCol, &MCol_Type ); if( !obj ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyObject_New() failed" ); obj->color = color; return (PyObject *)obj; } /************************************************************************ * * BPy_MVert attributes * ************************************************************************/ static MVert * MVert_get_pointer( BPy_MVert * self ) { if( BPy_MVert_Check( self ) ) { if( self->index >= ((Mesh *)self->data)->totvert ) return (MVert *)EXPP_ReturnPyObjError( PyExc_RuntimeError, "MVert is no longer valid" ); return &((Mesh *)self->data)->mvert[self->index]; } else return (MVert *)self->data; } /* * get a vertex's coordinate */ static PyObject *MVert_getCoord( BPy_MVert * self ) { MVert *v; v = MVert_get_pointer( self ); if( !v ) return NULL; return newVectorObject( v->co, 3, Py_WRAP ); } /* * set a vertex's coordinate */ static int MVert_setCoord( BPy_MVert * self, VectorObject * value ) { int i; MVert *v; v = MVert_get_pointer( self ); if( !v ) return -1; if( !VectorObject_Check( value ) || value->size != 3 ) return EXPP_ReturnIntError( PyExc_TypeError, "expected vector argument of size 3" ); for( i=0; i<3 ; ++i) v->co[i] = value->vec[i]; return 0; } /* * get a vertex's index */ static PyObject *MVert_getIndex( BPy_MVert * self ) { if( self->index >= ((Mesh *)self->data)->totvert ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "MVert is no longer valid" ); return PyInt_FromLong( self->index ); } /* * get a verts's hidden state */ static PyObject *MVert_getMFlagBits( BPy_MVert * self, void * type ) { MVert *v; v = MVert_get_pointer( self ); if (!v) return NULL; /* error is set */ return EXPP_getBitfield( &v->flag, (int)((long)type & 0xff), 'b' ); } /* * set a verts's hidden state */ static int MVert_setMFlagBits( BPy_MVert * self, PyObject * value, void * type ) { MVert *v; v = MVert_get_pointer( self ); if (!v) return -1; /* error is set */ return EXPP_setBitfield( value, &v->flag, (int)((long)type & 0xff), 'b' ); } /* * get a vertex's normal */ static PyObject *MVert_getNormal( BPy_MVert * self ) { float no[3]; int i; MVert *v; v = MVert_get_pointer( self ); if( !v ) return NULL; /* error set */ for( i = 0; i < 3; ++i ) no[i] = (float)(v->no[i] / 32767.0); return newVectorObject( no, 3, Py_NEW ); } /* * set a vertex's normal */ static int MVert_setNormal( BPy_MVert * self, VectorObject * value ) { int i; MVert *v; float normal[3]; v = MVert_get_pointer( self ); if( !v ) return -1; /* error set */ if( !VectorObject_Check( value ) || value->size != 3 ) return EXPP_ReturnIntError( PyExc_TypeError, "expected vector argument of size 3" ); for( i=0; i<3 ; ++i) normal[i] = value->vec[i]; Normalize(normal); for( i=0; i<3 ; ++i) v->no[i] = (short)(normal[i]*32767.0); return 0; } /* * get a vertex's select status */ static PyObject *MVert_getSel( BPy_MVert *self ) { MVert *v; v = MVert_get_pointer( self ); if( !v ) return NULL; /* error is set */ return EXPP_getBitfield( &v->flag, SELECT, 'b' ); } /* * set a vertex's select status */ static int MVert_setSel( BPy_MVert *self, PyObject *value ) { MVert *v = MVert_get_pointer( self ); Mesh *me = (Mesh *)self->data; if (!v) return -1; /* error is set */ /* * if vertex exists and setting status is OK, delete select storage * of the edges and faces as well */ if( v && !EXPP_setBitfield( value, &v->flag, SELECT, 'b' ) ) { if( me && me->mselect ) { MEM_freeN( me->mselect ); me->mselect = NULL; } return 0; } return -1; } /* * get a vertex's UV coordinates */ static PyObject *MVert_getUVco( BPy_MVert *self ) { Mesh *me = (Mesh *)self->data; if( !me->msticky ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "mesh has no 'sticky' coordinates" ); if( self->index >= ((Mesh *)self->data)->totvert ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "MVert is no longer valid" ); return newVectorObject( me->msticky[self->index].co, 2, Py_WRAP ); } /* * set a vertex's UV coordinates */ static int MVert_setUVco( BPy_MVert *self, PyObject *value ) { float uvco[3] = {0.0, 0.0}; Mesh *me = (Mesh *)self->data; struct MSticky *v; int i; /* * at least for now, don't allow creation of sticky coordinates if they * don't already exist */ if( !me->msticky ) return EXPP_ReturnIntError( PyExc_AttributeError, "mesh has no 'sticky' coordinates" ); if( self->index >= ((Mesh *)self->data)->totvert ) return EXPP_ReturnIntError( PyExc_RuntimeError, "MVert is no longer valid" ); if( VectorObject_Check( value ) ) { VectorObject *vect = (VectorObject *)value; if( vect->size != 2 ) return EXPP_ReturnIntError( PyExc_AttributeError, "expected 2D vector" ); for( i = 0; i < vect->size; ++i ) uvco[i] = vect->vec[i]; } else if( !PyArg_ParseTuple( value, "ff", &uvco[0], &uvco[1] ) ) return EXPP_ReturnIntError( PyExc_TypeError, "expected 2D vector" ); v = &me->msticky[self->index]; for( i = 0; i < 2; ++i ) v->co[i] = uvco[i]; return 0; } /************************************************************************ * * Python MVert_Type attributes get/set structure * ************************************************************************/ static PyGetSetDef BPy_MVert_getseters[] = { {"co", (getter)MVert_getCoord, (setter)MVert_setCoord, "vertex's coordinate", NULL}, {"index", (getter)MVert_getIndex, (setter)NULL, "vertex's index", NULL}, {"no", (getter)MVert_getNormal, (setter)MVert_setNormal, "vertex's normal", NULL}, {"sel", (getter)MVert_getSel, (setter)MVert_setSel, "vertex's select status", NULL}, {"hide", (getter)MVert_getMFlagBits, (setter)MVert_setMFlagBits, "vert hidden in edit mode", (void *)ME_HIDE}, {"uvco", (getter)MVert_getUVco, (setter)MVert_setUVco, "vertex's UV coordinates", NULL}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; static PyGetSetDef BPy_PVert_getseters[] = { {"co", (getter)MVert_getCoord, (setter)MVert_setCoord, "vertex's coordinate", NULL}, {"no", (getter)MVert_getNormal, (setter)MVert_setNormal, "vertex's normal", NULL}, {"sel", (getter)MVert_getSel, (setter)MVert_setSel, "vertex's select status", NULL}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /************************************************************************ * * Python MVert_Type standard operations * ************************************************************************/ static void MVert_dealloc( BPy_MVert * self ) { if( BPy_PVert_Check( self ) ) /* free memory of thick objects */ MEM_freeN ( self->data ); PyObject_DEL( self ); } static int MVert_compare( BPy_MVert * a, BPy_MVert * b ) { return( a->data == b->data && a->index == b->index ) ? 0 : -1; } static PyObject *MVert_repr( BPy_MVert * self ) { char format[512]; char index[24]; MVert *v; v = MVert_get_pointer( self ); if( !v ) return NULL; if( BPy_MVert_Check( self ) ) sprintf( index, "%d", self->index ); else BLI_strncpy( index, "(None)", 24 ); sprintf( format, "[MVert (%f %f %f) (%f %f %f) %s]", v->co[0], v->co[1], v->co[2], (float)(v->no[0] / 32767.0), (float)(v->no[1] / 32767.0), (float)(v->no[2] / 32767.0), index ); return PyString_FromString( format ); } static long MVert_hash( BPy_MVert *self ) { return (long)self->index; } static PyObject *Mesh_addPropLayer_internal(Mesh *mesh, CustomData *data, int tot, PyObject *args) { char *name=NULL; int type = -1; if( !PyArg_ParseTuple( args, "si", &name, &type) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a string and an int" ); if (strlen(name)>31) return EXPP_ReturnPyObjError( PyExc_ValueError, "error, maximum name length is 31"); if((type != CD_PROP_FLT) && (type != CD_PROP_INT) && (type != CD_PROP_STR)) return EXPP_ReturnPyObjError( PyExc_ValueError, "error, unknown layer type"); if (name) CustomData_add_layer_named(data, type, CD_DEFAULT, NULL,tot,name); mesh_update_customdata_pointers(mesh); Py_RETURN_NONE; } static PyObject *Mesh_removePropLayer_internal(Mesh *mesh, CustomData *data, int tot,PyObject *value) { CustomDataLayer *layer; char *name=PyString_AsString(value); int i; if( !name ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected string argument" ); if (strlen(name)>31) return EXPP_ReturnPyObjError( PyExc_ValueError, "error, maximum name length is 31" ); i = CustomData_get_named_layer_index(data, CD_PROP_FLT, name); if(i == -1) i = CustomData_get_named_layer_index(data, CD_PROP_INT, name); if(i == -1) i = CustomData_get_named_layer_index(data, CD_PROP_STR, name); if (i==-1) return EXPP_ReturnPyObjError(PyExc_ValueError, "No matching layers to remove" ); layer = &data->layers[i]; CustomData_free_layer(data, layer->type, tot, i); mesh_update_customdata_pointers(mesh); Py_RETURN_NONE; } static PyObject *Mesh_renamePropLayer_internal(Mesh *mesh, CustomData *data, PyObject *args) { CustomDataLayer *layer; int i; char *name_from, *name_to; if( !PyArg_ParseTuple( args, "ss", &name_from, &name_to ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected 2 strings" ); if (strlen(name_from)>31 || strlen(name_to)>31) return EXPP_ReturnPyObjError( PyExc_ValueError, "error, maximum name length is 31" ); i = CustomData_get_named_layer_index(data, CD_PROP_FLT, name_from); if(i == -1) i = CustomData_get_named_layer_index(data, CD_PROP_INT, name_from); if(i == -1) i = CustomData_get_named_layer_index(data, CD_PROP_STR, name_from); if(i == -1) return EXPP_ReturnPyObjError(PyExc_ValueError, "No matching layers to rename" ); layer = &data->layers[i]; strcpy(layer->name, name_to); /* we alredy know the string sizes are under 32 */ CustomData_set_layer_unique_name(data, i); Py_RETURN_NONE; } static PyObject *Mesh_propList_internal(CustomData *data) { CustomDataLayer *layer; PyObject *list = PyList_New( 0 ), *item; int i; for(i=0; itotlayer; i++) { layer = &data->layers[i]; if( (layer->type == CD_PROP_FLT) || (layer->type == CD_PROP_INT) || (layer->type == CD_PROP_STR)) { item = PyString_FromString(layer->name); PyList_Append( list, item ); Py_DECREF(item); } } return list; } static PyObject *Mesh_getProperty_internal(CustomData *data, int eindex, PyObject *value) { CustomDataLayer *layer; char *name=PyString_AsString(value); int i; MFloatProperty *pf; MIntProperty *pi; MStringProperty *ps; if(!name) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected an string argument" ); if (strlen(name)>31) return EXPP_ReturnPyObjError( PyExc_ValueError, "error, maximum name length is 31" ); i = CustomData_get_named_layer_index(data, CD_PROP_FLT, name); if(i == -1) i = CustomData_get_named_layer_index(data, CD_PROP_INT, name); if(i == -1) i = CustomData_get_named_layer_index(data, CD_PROP_STR, name); if(i == -1) return EXPP_ReturnPyObjError(PyExc_ValueError, "No matching layers" ); layer = &data->layers[i]; if(layer->type == CD_PROP_FLT){ pf = layer->data; return PyFloat_FromDouble(pf[eindex].f); } else if(layer->type == CD_PROP_INT){ pi = layer->data; return PyInt_FromLong(pi[eindex].i); } else if(layer->type == CD_PROP_STR){ ps = layer->data; return PyString_FromString(ps[eindex].s); } Py_RETURN_NONE; } static PyObject *Mesh_setProperty_internal(CustomData *data, int eindex, PyObject *args) { CustomDataLayer *layer; int i = 0, index, type = -1; float f = 0.0f; char *s=NULL, *name=NULL; MFloatProperty *pf; MIntProperty *pi; MStringProperty *ps; PyObject *val; if(PyArg_ParseTuple(args, "sO", &name, &val)){ if (strlen(name)>31) return EXPP_ReturnPyObjError( PyExc_ValueError, "error, maximum name length is 31" ); if(PyInt_Check(val)){ type = CD_PROP_INT; i = (int)PyInt_AS_LONG(val); } else if(PyFloat_Check(val)){ type = CD_PROP_FLT; f = (float)PyFloat_AsDouble(val); } else if(PyString_Check(val)){ type = CD_PROP_STR; s = PyString_AsString(val); } else return EXPP_ReturnPyObjError( PyExc_TypeError, "expected an name plus either float/int/string" ); } index = CustomData_get_named_layer_index(data, type, name); if(index == -1) return EXPP_ReturnPyObjError(PyExc_ValueError, "No matching layers or type mismatch" ); layer = &data->layers[index]; if(type==CD_PROP_STR){ if (strlen(s)>255){ return EXPP_ReturnPyObjError( PyExc_ValueError, "error, maximum string length is 255"); } else{ ps = layer->data; strcpy(ps[eindex].s,s); } } else if(type==CD_PROP_FLT){ pf = layer->data; pf[eindex].f = f; } else{ pi = layer->data; pi[eindex].i = i; } Py_RETURN_NONE; } static PyObject *MVert_getProp( BPy_MVert *self, PyObject *args) { if( BPy_MVert_Check( self ) ){ Mesh *me = (Mesh *)self->data; if(self->index >= me->totvert) return EXPP_ReturnPyObjError( PyExc_ValueError, "error, MVert is no longer valid part of mesh!"); else return Mesh_getProperty_internal(&(me->vdata), self->index, args); } return EXPP_ReturnPyObjError( PyExc_ValueError, "error, Vertex not part of a mesh!"); } static PyObject *MVert_setProp( BPy_MVert *self, PyObject *args) { if( BPy_MVert_Check( self ) ){ Mesh *me = (Mesh *)self->data; if(self->index >= me->totvert) return EXPP_ReturnPyObjError( PyExc_ValueError, "error, MVert is no longer valid part of mesh!"); else return Mesh_setProperty_internal(&(me->vdata), self->index, args); } return EXPP_ReturnPyObjError( PyExc_ValueError, "error, Vertex not part of a mesh!"); } static struct PyMethodDef BPy_MVert_methods[] = { {"getProperty", (PyCFunction)MVert_getProp, METH_O, "get property indicated by name"}, {"setProperty", (PyCFunction)MVert_setProp, METH_VARARGS, "set property indicated by name"}, {NULL, NULL, 0, NULL} }; /************************************************************************ * * Python MVert_Type structure definition * ************************************************************************/ PyTypeObject MVert_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "." */ "Blender MVert", /* char *tp_name; */ sizeof( BPy_MVert ), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ ( destructor ) MVert_dealloc,/* destructor tp_dealloc; */ NULL, /* printfunc tp_print; */ NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ ( cmpfunc ) MVert_compare, /* cmpfunc tp_compare; */ ( reprfunc ) MVert_repr, /* reprfunc tp_repr; */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ NULL, /* PySequenceMethods *tp_as_sequence; */ NULL, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ ( hashfunc ) MVert_hash, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ NULL, /* richcmpfunc tp_richcompare; */ /*** weak reference enabler ***/ 0, /* long tp_weaklistoffset; */ /*** Added in release 2.2 ***/ /* Iterators */ NULL, /* getiterfunc tp_iter; */ NULL, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ BPy_MVert_methods, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ BPy_MVert_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ NULL, /* descrsetfunc tp_descr_set; */ 0, /* long tp_dictoffset; */ NULL, /* initproc tp_init; */ NULL, /* allocfunc tp_alloc; */ NULL, /* newfunc tp_new; */ /* Low-level free-memory routine */ NULL, /* freefunc tp_free; */ /* For PyObject_IS_GC */ NULL, /* inquiry tp_is_gc; */ NULL, /* PyObject *tp_bases; */ /* method resolution order */ NULL, /* PyObject *tp_mro; */ NULL, /* PyObject *tp_cache; */ NULL, /* PyObject *tp_subclasses; */ NULL, /* PyObject *tp_weaklist; */ NULL }; /************************************************************************ * * Python PVert_Type standard operations * ************************************************************************/ static int PVert_compare( BPy_MVert * a, BPy_MVert * b ) { return( a->data == b->data ) ? 0 : -1; } /************************************************************************ * * Python PVert_Type structure definition * ************************************************************************/ PyTypeObject PVert_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "." */ "Blender PVert", /* char *tp_name; */ sizeof( BPy_MVert ), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ ( destructor ) MVert_dealloc,/* destructor tp_dealloc; */ NULL, /* printfunc tp_print; */ NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ ( cmpfunc ) PVert_compare, /* cmpfunc tp_compare; */ ( reprfunc ) MVert_repr, /* reprfunc tp_repr; */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ NULL, /* PySequenceMethods *tp_as_sequence; */ NULL, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ ( hashfunc ) MVert_hash, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ NULL, /* richcmpfunc tp_richcompare; */ /*** weak reference enabler ***/ 0, /* long tp_weaklistoffset; */ /*** Added in release 2.2 ***/ /* Iterators */ NULL, /* getiterfunc tp_iter; */ NULL, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ NULL, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ BPy_PVert_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ NULL, /* descrsetfunc tp_descr_set; */ 0, /* long tp_dictoffset; */ NULL, /* initproc tp_init; */ NULL, /* allocfunc tp_alloc; */ NULL, /* newfunc tp_new; */ /* Low-level free-memory routine */ NULL, /* freefunc tp_free; */ /* For PyObject_IS_GC */ NULL, /* inquiry tp_is_gc; */ NULL, /* PyObject *tp_bases; */ /* method resolution order */ NULL, /* PyObject *tp_mro; */ NULL, /* PyObject *tp_cache; */ NULL, /* PyObject *tp_subclasses; */ NULL, /* PyObject *tp_weaklist; */ NULL }; /* * create 'thin' or 'thick' MVert objects * * there are two types of objects; thin (wrappers for mesh vertex) and thick * (not contains in mesh). Thin objects are MVert_Type and thick are * PVert_Type. For thin objects, data is a pointer to a Mesh and index * is the vertex's index in mesh->mvert. For thick objects, data is a * pointer to an MVert; index is unused. */ /* * create a thin MVert object */ static PyObject *MVert_CreatePyObject( Mesh *mesh, int i ) { BPy_MVert *obj = (BPy_MVert *)PyObject_NEW( BPy_MVert, &MVert_Type ); if( !obj ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyObject_New() failed" ); obj->index = i; obj->data = mesh; return (PyObject *)obj; } /* * create a thick MVert object */ static PyObject *PVert_CreatePyObject( MVert *vert ) { MVert *newvert; BPy_MVert *obj = (BPy_MVert *)PyObject_NEW( BPy_MVert, &PVert_Type ); if( !obj ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyObject_New() failed" ); newvert = (MVert *)MEM_callocN( sizeof( MVert ), "MVert" ); if( !newvert ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "MEM_callocN() failed" ); memcpy( newvert, vert, sizeof( MVert ) ); obj->data = newvert; return (PyObject *)obj; } /************************************************************************ * * Vertex sequence * ************************************************************************/ static int MVertSeq_len( BPy_MVertSeq * self ) { return self->mesh->totvert; } /* * retrive a single MVert from somewhere in the vertex list */ static PyObject *MVertSeq_item( BPy_MVertSeq * self, int i ) { if( i < 0 || i >= self->mesh->totvert ) return EXPP_ReturnPyObjError( PyExc_IndexError, "array index out of range" ); return MVert_CreatePyObject( self->mesh, i ); } /* * retrieve a slice of the vertex list (as a Python list) * * Python is nice enough to handle negative indices for us: if the user * specifies -1, Python will pass us len()-1. So we can just check for * indices in the range 0:len()-1. Of course, we should never actually * return the high index, but up to one less. */ static PyObject *MVertSeq_slice( BPy_MVertSeq *self, int low, int high ) { PyObject *list; int i; /* * Python list slice operator returns empty list when asked for a slice * outside the list, or if indices are reversed (low > high). Clamp * our input to do the same. */ if( low < 0 ) low = 0; if( high > self->mesh->totvert ) high = self->mesh->totvert; if( low > high ) low = high; list = PyList_New( high-low ); if( !list ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyList_New() failed" ); /* * return Py_NEW copies of requested vertices */ for( i = low; i < high; ++i ) PyList_SET_ITEM( list, i-low, PVert_CreatePyObject( (void *)&self->mesh->mvert[i] ) ); return list; } /* * assign a single MVert to somewhere in the vertex list */ static int MVertSeq_assign_item( BPy_MVertSeq * self, int i, BPy_MVert *v ) { MVert *dst = &self->mesh->mvert[i]; MVert *src; if( !v ) return EXPP_ReturnIntError( PyExc_IndexError, "del() not supported" ); if( i < 0 || i >= self->mesh->totvert ) return EXPP_ReturnIntError( PyExc_IndexError, "array index out of range" ); if( BPy_MVert_Check( v ) ) src = &((Mesh *)v->data)->mvert[v->index]; else src = (MVert *)v->data; memcpy( dst, src, sizeof(MVert) ); /* mesh_update( self->mesh );*/ return 0; } static int MVertSeq_assign_slice( BPy_MVertSeq *self, int low, int high, PyObject *args ) { int len, i; if( !PyList_Check( args ) ) return EXPP_ReturnIntError( PyExc_IndexError, "can only assign lists of MVerts" ); len = PyList_Size( args ); /* * Python list slice assign operator allows for changing the size of the * destination list, by replacement and appending.... * * >>> l=[1,2,3,4] * >>> m=[11,12,13,14] * >>> l[5:7]=m * >>> print l * [1, 2, 3, 4, 11, 12, 13, 14] * >>> l=[1,2,3,4] * >>> l[2:3]=m * >>> print l * [1, 2, 11, 12, 13, 14, 4] * * We don't want the size of the list to change (at least not at time * point in development) so we are a little more strict: * - low and high indices must be in range [0:len()] * - high-low == PyList_Size(v) */ if( low < 0 || high > self->mesh->totvert || low > high ) return EXPP_ReturnIntError( PyExc_IndexError, "invalid slice range" ); if( high-low != len ) return EXPP_ReturnIntError( PyExc_IndexError, "slice range and input list sizes must be equal" ); for( i = low; i < high; ++i ) { BPy_MVert *v = (BPy_MVert *)PyList_GET_ITEM( args, i-low ); MVert *dst = &self->mesh->mvert[i]; MVert *src; if( BPy_MVert_Check( v ) ) src = &((Mesh *)v->data)->mvert[v->index]; else src = (MVert *)v->data; memcpy( dst, src, sizeof(MVert) ); } /* mesh_update( self->mesh );*/ return 0; } static PySequenceMethods MVertSeq_as_sequence = { ( inquiry ) MVertSeq_len, /* sq_length */ ( binaryfunc ) 0, /* sq_concat */ ( intargfunc ) 0, /* sq_repeat */ ( intargfunc ) MVertSeq_item, /* sq_item */ ( intintargfunc ) MVertSeq_slice, /* sq_slice */ ( intobjargproc ) MVertSeq_assign_item, /* sq_ass_item */ ( intintobjargproc ) MVertSeq_assign_slice, /* sq_ass_slice */ 0,0,0, }; /************************************************************************ * * Python MVertSeq_Type iterator (iterates over vertices) * ************************************************************************/ /* * Initialize the interator index */ static PyObject *MVertSeq_getIter( BPy_MVertSeq * self ) { if (self->iter==-1) { /* iteration for this pyobject is not yet used, just return self */ self->iter = 0; return EXPP_incr_ret ( (PyObject *) self ); } else { /* were alredy using this as an iterator, make a copy to loop on */ BPy_MVertSeq *seq = (BPy_MVertSeq *)MVertSeq_CreatePyObject(self->mesh); seq->iter = 0; return (PyObject *)seq; } } /* * Return next MVert. */ static PyObject *MVertSeq_nextIter( BPy_MVertSeq * self ) { if( self->iter == self->mesh->totvert ) { self->iter= -1; /* allow it to be used as an iterator again without creating a new BPy_MVertSeq */ return EXPP_ReturnPyObjError( PyExc_StopIteration, "iterator at end" ); } return MVert_CreatePyObject( self->mesh, self->iter++ ); } /************************************************************************ * * Python MVertSeq_Type methods * ************************************************************************/ static PyObject *MVertSeq_extend( BPy_MVertSeq * self, PyObject *args ) { int len, newlen; int i,j; PyObject *tmp; MVert *newvert, *tmpvert; Mesh *mesh = self->mesh; CustomData vdata; /* make sure we get a sequence of tuples of something */ switch( PySequence_Size( args ) ) { case 1: /* better be a list or a tuple */ tmp = PyTuple_GET_ITEM( args, 0 ); if( !VectorObject_Check ( tmp ) ) { if( !PySequence_Check ( tmp ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of sequence triplets" ); else if( !PySequence_Size ( tmp ) ) { Py_RETURN_NONE; } args = tmp; } Py_INCREF( args ); /* so we can safely DECREF later */ break; case 3: tmp = PyTuple_GET_ITEM( args, 0 ); /* if first item is not a number, it's wrong */ if( !PyNumber_Check( tmp ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of sequence triplets" ); /* otherwise, put into a new tuple */ args = Py_BuildValue( "((OOO))", tmp, PyTuple_GET_ITEM( args, 1 ), PyTuple_GET_ITEM( args, 2 ) ); if( !args ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "Py_BuildValue() failed" ); break; default: /* anything else is definitely wrong */ return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of sequence triplets" ); } /* if no verts given, return quietly */ len = PySequence_Size( args ); if( len == 0 ) { Py_DECREF ( args ); Py_RETURN_NONE; } /* create custom vertex data arrays and copy existing vertices into it */ newlen = mesh->totvert + len; CustomData_copy( &mesh->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, newlen ); CustomData_copy_data( &mesh->vdata, &vdata, 0, 0, mesh->totvert ); if ( !CustomData_has_layer( &vdata, CD_MVERT ) ) CustomData_add_layer( &vdata, CD_MVERT, CD_CALLOC, NULL, newlen ); newvert = CustomData_get_layer( &vdata, CD_MVERT ); /* scan the input list and insert the new vertices */ tmpvert = &newvert[mesh->totvert]; for( i = 0; i < len; ++i ) { float co[3]; tmp = PySequence_GetItem( args, i ); if( VectorObject_Check( tmp ) ) { if( ((VectorObject *)tmp)->size != 3 ) { CustomData_free( &vdata, newlen ); Py_DECREF ( tmp ); Py_DECREF ( args ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected vector of size 3" ); } for( j = 0; j < 3; ++j ) co[j] = ((VectorObject *)tmp)->vec[j]; } else if( PySequence_Check( tmp ) ) { int ok=1; PyObject *flt; if( PySequence_Size( tmp ) != 3 ) ok = 0; else for( j = 0; ok && j < 3; ++j ) { flt = PySequence_ITEM( tmp, j ); if( !PyNumber_Check ( flt ) ) ok = 0; else co[j] = (float)PyFloat_AsDouble( flt ); Py_DECREF( flt ); } if( !ok ) { CustomData_free( &vdata, newlen ); Py_DECREF ( args ); Py_DECREF ( tmp ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected sequence triplet of floats" ); } } else { CustomData_free( &vdata, newlen ); Py_DECREF ( args ); Py_DECREF ( tmp ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected sequence triplet of floats" ); } Py_DECREF ( tmp ); /* add the coordinate to the new list */ memcpy( tmpvert->co, co, sizeof(co) ); tmpvert->flag |= SELECT; /* TODO: anything else which needs to be done when we add a vert? */ /* probably not: NMesh's newvert() doesn't */ ++tmpvert; } CustomData_free( &mesh->vdata, mesh->totvert ); mesh->vdata = vdata; mesh_update_customdata_pointers( mesh ); /* * if there are keys, have to fix those lists up */ if( mesh->key ) { KeyBlock *currkey = mesh->key->block.first; float *fp, *newkey; while( currkey ) { /* create key list, copy existing data if any */ newkey = MEM_callocN(mesh->key->elemsize*newlen, "keydata"); if( currkey->data ) { memcpy( newkey, currkey->data, mesh->totvert*mesh->key->elemsize ); MEM_freeN( currkey->data ); currkey->data = newkey; } /* add data for new vertices */ fp = (float *)((char *)currkey->data + (mesh->key->elemsize*mesh->totvert)); tmpvert = mesh->mvert + mesh->totvert; for( i = newlen - mesh->totvert; i > 0; --i ) { VECCOPY(fp, tmpvert->co); fp += 3; tmpvert++; } currkey->totelem = newlen; currkey = currkey->next; } } /* set final vertex list size */ mesh->totvert = newlen; mesh_update( mesh ); Py_DECREF ( args ); Py_RETURN_NONE; } static PyObject *MVertSeq_delete( BPy_MVertSeq * self, PyObject *args ) { unsigned int *vert_table; int vert_delete, face_count; int i; Mesh *mesh = self->mesh; MFace *tmpface; /* * if input tuple contains a single sequence, use it as input instead; * otherwise use the sequence as-is and check later that it contains * one or more integers or MVerts */ if( PySequence_Size( args ) == 1 ) { PyObject *tmp = PyTuple_GET_ITEM( args, 0 ); if( PySequence_Check( tmp ) ) args = tmp; } /* if sequence is empty, do nothing */ if( PySequence_Size( args ) == 0 ) { Py_RETURN_NONE; } /* allocate vertex lookup table */ vert_table = (unsigned int *)MEM_callocN( mesh->totvert*sizeof( unsigned int ), "vert_table" ); /* get the indices of vertices to be removed */ for( i = PySequence_Size( args ); i--; ) { PyObject *tmp = PySequence_GetItem( args, i ); int index; if( BPy_MVert_Check( tmp ) ) { if( (void *)self->mesh != ((BPy_MVert*)tmp)->data ) { MEM_freeN( vert_table ); Py_DECREF( tmp ); return EXPP_ReturnPyObjError( PyExc_ValueError, "MVert belongs to a different mesh" ); } index = ((BPy_MVert*)tmp)->index; } else if( PyInt_Check( tmp ) ) { index = PyInt_AsLong ( tmp ); } else { MEM_freeN( vert_table ); Py_DECREF( tmp ); return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of ints or MVerts" ); } Py_DECREF( tmp ); if( index < 0 || index >= mesh->totvert ) { MEM_freeN( vert_table ); return EXPP_ReturnPyObjError( PyExc_IndexError, "array index out of range" ); } vert_table[index] = UINT_MAX; } /* delete things, then clean up and return */ vert_delete = make_vertex_table( vert_table, mesh->totvert ); if( vert_delete ) delete_verts( mesh, vert_table, vert_delete ); /* calculate edges to delete, fix vertex indices */ delete_edges( mesh, vert_table, 0 ); /* * find number of faces which contain any of the deleted vertices, * and mark them, then delete them */ tmpface = mesh->mface; face_count=0; for( i = mesh->totface; i--; ++tmpface ) { if( vert_table[tmpface->v1] == UINT_MAX || vert_table[tmpface->v2] == UINT_MAX || vert_table[tmpface->v3] == UINT_MAX || ( tmpface->v4 && vert_table[tmpface->v4] == UINT_MAX ) ) { tmpface->v1 = UINT_MAX; ++face_count; } } delete_faces( mesh, vert_table, face_count ); /* clean up and exit */ MEM_freeN( vert_table ); mesh_update ( mesh ); Py_RETURN_NONE; } static PyObject *MVertSeq_selected( BPy_MVertSeq * self ) { int i, count; Mesh *mesh = self->mesh; MVert *tmpvert; PyObject *list; /* first count selected edges (quicker than appending to PyList?) */ count = 0; tmpvert = mesh->mvert; for( i = 0; i < mesh->totvert; ++i, ++tmpvert ) if( tmpvert->flag & SELECT ) ++count; list = PyList_New( count ); if( !list ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyList_New() failed" ); /* next, insert selected edges into list */ count = 0; tmpvert = mesh->mvert; for( i = 0; i < mesh->totvert; ++i, ++tmpvert ) { if( tmpvert->flag & SELECT ) { PyObject *tmp = PyInt_FromLong( i ); if( !tmp ) { Py_DECREF( list ); return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } PyList_SET_ITEM( list, count, tmp ); ++count; } } return list; } static PyObject *MVertSeq_add_layertype(BPy_MVertSeq *self, PyObject *args) { Mesh *me = (Mesh*)self->mesh; return Mesh_addPropLayer_internal(me, &(me->vdata), me->totvert, args); } static PyObject *MVertSeq_del_layertype(BPy_MVertSeq *self, PyObject *value) { Mesh *me = (Mesh*)self->mesh; return Mesh_removePropLayer_internal(me, &(me->vdata), me->totvert, value); } static PyObject *MVertSeq_rename_layertype(BPy_MVertSeq *self, PyObject *args) { Mesh *me = (Mesh*)self->mesh; return Mesh_renamePropLayer_internal(me,&(me->vdata),args); } static PyObject *MVertSeq_PropertyList(BPy_MVertSeq *self) { Mesh *me = (Mesh*)self->mesh; return Mesh_propList_internal(&(me->vdata)); } static PyObject *M_Mesh_PropertiesTypeDict(void) { PyObject *Types = PyConstant_New( ); if(Types) { BPy_constant *d = (BPy_constant *) Types; PyConstant_Insert(d, "FLOAT", PyInt_FromLong(CD_PROP_FLT)); PyConstant_Insert(d, "INT" , PyInt_FromLong(CD_PROP_INT)); PyConstant_Insert(d, "STRING", PyInt_FromLong(CD_PROP_STR)); } return Types; } static struct PyMethodDef BPy_MVertSeq_methods[] = { {"extend", (PyCFunction)MVertSeq_extend, METH_VARARGS, "add vertices to mesh"}, {"delete", (PyCFunction)MVertSeq_delete, METH_VARARGS, "delete vertices from mesh"}, {"selected", (PyCFunction)MVertSeq_selected, METH_NOARGS, "returns a list containing indices of selected vertices"}, {"addPropertyLayer",(PyCFunction)MVertSeq_add_layertype, METH_VARARGS, "add a new property layer"}, {"removePropertyLayer",(PyCFunction)MVertSeq_del_layertype, METH_O, "removes a property layer"}, {"renamePropertyLayer",(PyCFunction)MVertSeq_rename_layertype, METH_VARARGS, "renames an existing property layer"}, {NULL, NULL, 0, NULL} }; static PyGetSetDef BPy_MVertSeq_getseters[] = { {"properties", (getter)MVertSeq_PropertyList, (setter)NULL, "vertex property layers, read only", NULL}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /************************************************************************ * * Python MVertSeq_Type standard operations * ************************************************************************/ /*****************************************************************************/ /* Python MVertSeq_Type structure definition: */ /*****************************************************************************/ PyTypeObject MVertSeq_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "." */ "Blender MVertSeq", /* char *tp_name; */ sizeof( BPy_MVertSeq ), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ NULL, /* destructor tp_dealloc; */ NULL, /* printfunc tp_print; */ NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ NULL, /* cmpfunc tp_compare; */ NULL, /* reprfunc tp_repr; */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ &MVertSeq_as_sequence, /* PySequenceMethods *tp_as_sequence; */ NULL, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ NULL, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ NULL, /* richcmpfunc tp_richcompare; */ /*** weak reference enabler ***/ 0, /* long tp_weaklistoffset; */ /*** Added in release 2.2 ***/ /* Iterators */ ( getiterfunc) MVertSeq_getIter, /* getiterfunc tp_iter; */ ( iternextfunc ) MVertSeq_nextIter, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ BPy_MVertSeq_methods, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ BPy_MVertSeq_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ NULL, /* descrsetfunc tp_descr_set; */ 0, /* long tp_dictoffset; */ NULL, /* initproc tp_init; */ NULL, /* allocfunc tp_alloc; */ NULL, /* newfunc tp_new; */ /* Low-level free-memory routine */ NULL, /* freefunc tp_free; */ /* For PyObject_IS_GC */ NULL, /* inquiry tp_is_gc; */ NULL, /* PyObject *tp_bases; */ /* method resolution order */ NULL, /* PyObject *tp_mro; */ NULL, /* PyObject *tp_cache; */ NULL, /* PyObject *tp_subclasses; */ NULL, /* PyObject *tp_weaklist; */ NULL }; /************************************************************************ * * Edge attributes * ************************************************************************/ static MEdge * MEdge_get_pointer( BPy_MEdge * self ) { if( self->index >= self->mesh->totedge ) return (MEdge *)EXPP_ReturnPyObjError( PyExc_RuntimeError, "MEdge is no longer valid" ); return &self->mesh->medge[self->index]; } /* * get an edge's crease value */ static PyObject *MEdge_getCrease( BPy_MEdge * self ) { MEdge *edge = MEdge_get_pointer( self ); if( !edge ) return NULL; return PyInt_FromLong( edge->crease ); } /* * set an edge's crease value */ static int MEdge_setCrease( BPy_MEdge * self, PyObject * value ) { MEdge *edge = MEdge_get_pointer( self ); if( !edge ) return -1; return EXPP_setIValueClamped( value, &edge->crease, 0, 255, 'b' ); } /* * get an edge's flag */ static PyObject *MEdge_getFlag( BPy_MEdge * self ) { MEdge *edge = MEdge_get_pointer( self ); if( !edge ) return NULL; return PyInt_FromLong( edge->flag ); } /* * set an edge's flag */ static int MEdge_setFlag( BPy_MEdge * self, PyObject * value ) { short param; static short bitmask = SELECT | ME_EDGEDRAW | ME_SEAM | ME_FGON | ME_HIDE | ME_EDGERENDER | ME_LOOSEEDGE | ME_SEAM_LAST | ME_SHARP; MEdge *edge = MEdge_get_pointer( self ); if( !edge ) return -1; if( !PyInt_Check ( value ) ) { char errstr[128]; sprintf ( errstr , "expected int bitmask of 0x%04x", bitmask ); return EXPP_ReturnIntError( PyExc_TypeError, errstr ); } param = (short)PyInt_AS_LONG ( value ); if ( ( param & bitmask ) != param ) return EXPP_ReturnIntError( PyExc_ValueError, "invalid bit(s) set in mask" ); edge->flag = param; return 0; } /* * get an edge's first vertex */ static PyObject *MEdge_getV1( BPy_MEdge * self ) { MEdge *edge = MEdge_get_pointer( self ); if( !edge ) return NULL; return MVert_CreatePyObject( self->mesh, edge->v1 ); } /* * set an edge's first vertex */ static int MEdge_setV1( BPy_MEdge * self, BPy_MVert * value ) { MEdge *edge = MEdge_get_pointer( self ); if( !edge ) return -1; if( !BPy_MVert_Check( value ) ) return EXPP_ReturnIntError( PyExc_TypeError, "expected an MVert" ); edge->v1 = value->index; return 0; } /* * get an edge's second vertex */ static PyObject *MEdge_getV2( BPy_MEdge * self ) { MEdge *edge = MEdge_get_pointer( self ); if( !edge ) return NULL; /* error is set */ /* if v2 is out of range, the python mvert will complain, no need to check here */ return MVert_CreatePyObject( self->mesh, edge->v2 ); } /* * set an edge's second vertex */ static int MEdge_setV2( BPy_MEdge * self, BPy_MVert * value ) { MEdge *edge = MEdge_get_pointer( self ); if( !edge ) return -1; /* error is set */ if( !BPy_MVert_Check( value ) ) return EXPP_ReturnIntError( PyExc_TypeError, "expected an MVert" ); if ( edge->v1 == value->index ) return EXPP_ReturnIntError( PyExc_ValueError, "an edge cant use the same vertex for each end" ); edge->v2 = value->index; return 0; } /* * get an edge's index */ static PyObject *MEdge_getIndex( BPy_MEdge * self ) { if( !MEdge_get_pointer( self ) ) return NULL; /* error is set */ return PyInt_FromLong( self->index ); } /* * get an edge's flag */ static PyObject *MEdge_getMFlagBits( BPy_MEdge * self, void * type ) { MEdge *edge = MEdge_get_pointer( self ); if( !edge ) return NULL; /* error is set */ return EXPP_getBitfield( &edge->flag, (int)((long)type & 0xff), 'b' ); } /* * get an edge's length */ static PyObject *MEdge_getLength( BPy_MEdge * self ) { MEdge *edge = MEdge_get_pointer( self ); double dot = 0.0f; float tmpf; int i; float *v1, *v2; if (!edge) return NULL; /* error is set */ if MEDGE_VERT_BADRANGE_CHECK(self->mesh, edge) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This edge uses removed vert(s)" ); /* get the 2 edges vert locations */ v1= (&((Mesh *)self->mesh)->mvert[edge->v1])->co; v2= (&((Mesh *)self->mesh)->mvert[edge->v2])->co; if( !edge ) return NULL; for( i = 0; i < 3; i++ ) { tmpf = v1[i] - v2[i]; dot += tmpf*tmpf; } return PyFloat_FromDouble( sqrt( dot ) ); } /* * get an key for using in a dictionary or set key */ static PyObject *MEdge_getKey( BPy_MEdge * self ) { PyObject *attr; MEdge *edge = MEdge_get_pointer( self ); if (!edge) return NULL; /* error is set */ attr = PyTuple_New( 2 ); if (edge->v1 > edge->v2) { PyTuple_SET_ITEM( attr, 0, PyInt_FromLong(edge->v2) ); PyTuple_SET_ITEM( attr, 1, PyInt_FromLong(edge->v1) ); } else { PyTuple_SET_ITEM( attr, 0, PyInt_FromLong(edge->v1) ); PyTuple_SET_ITEM( attr, 1, PyInt_FromLong(edge->v2) ); } return attr; } /* * set an edge's select state */ static int MEdge_setSel( BPy_MEdge * self,PyObject * value, void * type_unused ) { MEdge *edge = MEdge_get_pointer( self ); int param = PyObject_IsTrue( value ); Mesh *me = self->mesh; if( !edge ) return -1; if MEDGE_VERT_BADRANGE_CHECK(me, edge) return EXPP_ReturnIntError( PyExc_RuntimeError, "This edge uses removed vert(s)" ); if( param == -1 ) return EXPP_ReturnIntError( PyExc_TypeError, "expected True/False or 0/1" ); if( param ) { edge->flag |= SELECT; me->mvert[edge->v1].flag |= SELECT; me->mvert[edge->v2].flag |= SELECT; } else { edge->flag &= ~SELECT; me->mvert[edge->v1].flag &= ~SELECT; me->mvert[edge->v2].flag &= ~SELECT; } if( self->mesh->mselect ) { MEM_freeN( self->mesh->mselect ); self->mesh->mselect = NULL; } return 0; } /************************************************************************ * * Python MEdge_Type attributes get/set structure * ************************************************************************/ static PyGetSetDef BPy_MEdge_getseters[] = { {"crease", (getter)MEdge_getCrease, (setter)MEdge_setCrease, "edge's crease value", NULL}, {"flag", (getter)MEdge_getFlag, (setter)MEdge_setFlag, "edge's flags", NULL}, {"v1", (getter)MEdge_getV1, (setter)MEdge_setV1, "edge's first vertex", NULL}, {"v2", (getter)MEdge_getV2, (setter)MEdge_setV2, "edge's second vertex", NULL}, {"index", (getter)MEdge_getIndex, (setter)NULL, "edge's index", NULL}, {"sel", (getter)MEdge_getMFlagBits, (setter)MEdge_setSel, "edge selected in edit mode", (void *)SELECT}, {"length", (getter)MEdge_getLength, (setter)NULL, "edge's length, read only", NULL}, {"key", (getter)MEdge_getKey, (setter)NULL, "edge's key for using with sets or dictionaries, read only", NULL}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /************************************************************************ * * Python MEdge_Type iterator (iterates over vertices) * ************************************************************************/ /* * Initialize the interator index */ static PyObject *MEdge_getIter( BPy_MEdge * self ) { if (self->iter==-1) { /* not alredy used to iterator on, just use self */ self->iter = 0; return EXPP_incr_ret ( (PyObject *) self ); } else { /* alredy being iterated on, return a copy */ BPy_MEdge *seq = (BPy_MEdge *)MEdge_CreatePyObject(self->mesh, self->index); seq->iter = 0; return (PyObject *)seq; } } /* * Return next MVert. Throw an exception after the second vertex. */ static PyObject *MEdge_nextIter( BPy_MEdge * self ) { if( self->iter == 2 ) { self->iter = -1; return EXPP_ReturnPyObjError( PyExc_StopIteration, "iterator at end" ); } self->iter++; if( self->iter == 1 ) return MEdge_getV1( self ); else return MEdge_getV2( self ); } /************************************************************************ * * Python MEdge_Type standard operations * ************************************************************************/ static int MEdge_compare( BPy_MEdge * a, BPy_MEdge * b ) { return( a->mesh == b->mesh && a->index == b->index ) ? 0 : -1; } static PyObject *MEdge_repr( BPy_MEdge * self ) { struct MEdge *edge = MEdge_get_pointer( self ); if( !edge ) return NULL; return PyString_FromFormat( "[MEdge (%d %d) %d %d]", (int)edge->v1, (int)edge->v2, (int)edge->crease, (int)self->index ); } static long MEdge_hash( BPy_MEdge *self ) { return (long)self->index; } static PyObject *MEdge_getProp( BPy_MEdge *self, PyObject *args) { Mesh *me = (Mesh *)self->mesh; return Mesh_getProperty_internal(&(me->edata), self->index, args); } static PyObject *MEdge_setProp( BPy_MEdge *self, PyObject *args) { Mesh *me = (Mesh *)self->mesh; return Mesh_setProperty_internal(&(me->edata), self->index, args); } static struct PyMethodDef BPy_MEdge_methods[] = { {"getProperty", (PyCFunction)MEdge_getProp, METH_O, "get property indicated by name"}, {"setProperty", (PyCFunction)MEdge_setProp, METH_VARARGS, "set property indicated by name"}, {NULL, NULL, 0, NULL} }; /************************************************************************ * * Python MEdge_Type structure definition * ************************************************************************/ PyTypeObject MEdge_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "." */ "Blender MEdge", /* char *tp_name; */ sizeof( BPy_MEdge ), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ NULL, /* destructor tp_dealloc; */ NULL, /* printfunc tp_print; */ NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ ( cmpfunc ) MEdge_compare, /* cmpfunc tp_compare; */ ( reprfunc ) MEdge_repr, /* reprfunc tp_repr; */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ NULL, /* PySequenceMethods *tp_as_sequence; */ NULL, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ ( hashfunc ) MEdge_hash, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ NULL, /* richcmpfunc tp_richcompare; */ /*** weak reference enabler ***/ 0, /* long tp_weaklistoffset; */ /*** Added in release 2.2 ***/ /* Iterators */ ( getiterfunc) MEdge_getIter, /* getiterfunc tp_iter; */ ( iternextfunc ) MEdge_nextIter, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ BPy_MEdge_methods, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ BPy_MEdge_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ NULL, /* descrsetfunc tp_descr_set; */ 0, /* long tp_dictoffset; */ NULL, /* initproc tp_init; */ NULL, /* allocfunc tp_alloc; */ NULL, /* newfunc tp_new; */ /* Low-level free-memory routine */ NULL, /* freefunc tp_free; */ /* For PyObject_IS_GC */ NULL, /* inquiry tp_is_gc; */ NULL, /* PyObject *tp_bases; */ /* method resolution order */ NULL, /* PyObject *tp_mro; */ NULL, /* PyObject *tp_cache; */ NULL, /* PyObject *tp_subclasses; */ NULL, /* PyObject *tp_weaklist; */ NULL }; static PyObject *MEdge_CreatePyObject( Mesh * mesh, int i ) { BPy_MEdge *obj = PyObject_NEW( BPy_MEdge, &MEdge_Type ); if( !obj ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyObject_New() failed" ); obj->mesh = mesh; obj->index = i; obj->iter = -1; return (PyObject *)obj; } /************************************************************************ * * Edge sequence * ************************************************************************/ static int MEdgeSeq_len( BPy_MEdgeSeq * self ) { return self->mesh->totedge; } static PyObject *MEdgeSeq_item( BPy_MEdgeSeq * self, int i ) { if( i < 0 || i >= self->mesh->totedge ) return EXPP_ReturnPyObjError( PyExc_IndexError, "array index out of range" ); return MEdge_CreatePyObject( self->mesh, i ); } static PySequenceMethods MEdgeSeq_as_sequence = { ( inquiry ) MEdgeSeq_len, /* sq_length */ ( binaryfunc ) 0, /* sq_concat */ ( intargfunc ) 0, /* sq_repeat */ ( intargfunc ) MEdgeSeq_item, /* sq_item */ ( intintargfunc ) 0, /* sq_slice */ ( intobjargproc ) 0, /* sq_ass_item */ ( intintobjargproc ) 0, /* sq_ass_slice */ 0,0,0, }; /************************************************************************ * * Python MEdgeSeq_Type iterator (iterates over edges) * ************************************************************************/ /* * Initialize the interator index */ static PyObject *MEdgeSeq_getIter( BPy_MEdgeSeq * self ) { if (self->iter==-1) { /* iteration for this pyobject is not yet used, just return self */ self->iter = 0; return EXPP_incr_ret ( (PyObject *) self ); } else { BPy_MEdgeSeq *seq = (BPy_MEdgeSeq *)MEdgeSeq_CreatePyObject(self->mesh); seq->iter = 0; return (PyObject *)seq; } } /* * Return next MEdge. */ static PyObject *MEdgeSeq_nextIter( BPy_MEdgeSeq * self ) { if( self->iter == self->mesh->totedge ) { self->iter= -1; return EXPP_ReturnPyObjError( PyExc_StopIteration, "iterator at end" ); } return MEdge_CreatePyObject( self->mesh, self->iter++ ); } /************************************************************************ * * Python MEdgeSeq_Type methods * ************************************************************************/ /* * Create edges from tuples of vertices. Duplicate new edges, or * edges which already exist, */ static PyObject *MEdgeSeq_extend( BPy_MEdgeSeq * self, PyObject *args ) { int len, nverts; int i, j, ok; int new_edge_count, good_edges; SrchEdges *oldpair, *newpair, *tmppair, *tmppair2; PyObject *tmp; BPy_MVert *e[4]; MEdge *tmpedge; Mesh *mesh = self->mesh; /* make sure we get a tuple of sequences of something */ switch( PySequence_Size( args ) ) { case 1: /* if a sequence... */ tmp = PyTuple_GET_ITEM( args, 0 ); if( PySequence_Check( tmp ) ) { PyObject *tmp2; /* ignore empty sequences */ if( !PySequence_Size( tmp ) ) { Py_RETURN_NONE; } /* if another sequence, use it */ tmp2 = PySequence_ITEM( tmp, 0 ); if( PySequence_Check( tmp2 ) ) args = tmp; Py_INCREF( args ); Py_DECREF( tmp2 ); } else return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of sequence pairs" ); break; case 2: case 3: case 4: /* two to four args may be individual verts */ tmp = PyTuple_GET_ITEM( args, 0 ); /* * if first item isn't a sequence, then assume it's a bunch of MVerts * and wrap inside a tuple */ if( !PySequence_Check( tmp ) ) { args = Py_BuildValue( "(O)", args ); if( !args ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "Py_BuildValue() failed" ); /* * otherwise, assume it already a bunch of sequences so use as-is */ } else { Py_INCREF( args ); /* so we can safely DECREF later */ } break; default: /* anything else is definitely wrong */ return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of sequence pairs" ); } /* make sure there is something to add */ len = PySequence_Size( args ); if( len == 0 ) { Py_DECREF ( args ); Py_RETURN_NONE; } /* verify the param list and get a total count of number of edges */ new_edge_count = 0; for( i = 0; i < len; ++i ) { tmp = PySequence_GetItem( args, i ); /* not a tuple of MVerts... error */ if( !PySequence_Check( tmp ) ) { Py_DECREF( tmp ); Py_DECREF( args ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected sequence of MVert sequences" ); } /* not the right number of MVerts... error */ nverts = PySequence_Size( tmp ); if( nverts < 2 || nverts > 4 ) { Py_DECREF( tmp ); Py_DECREF( args ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected 2 to 4 MVerts per sequence" ); } if( EXPP_check_sequence_consistency( tmp, &MVert_Type ) == 1 ) { /* get MVerts, check they're from this mesh */ ok = 1; for( j = 0; ok && j < nverts; ++j ) { e[0] = (BPy_MVert *)PySequence_GetItem( tmp, j ); if( (void *)e[0]->data != (void *)self->mesh ) ok = 0; Py_DECREF( e[0] ); } Py_DECREF( tmp ); /* not MVerts from another mesh ... error */ if( !ok ) { Py_DECREF( args ); return EXPP_ReturnPyObjError( PyExc_ValueError, "vertices are from a different mesh" ); } } else { ok = 0; for( j = 0; ok == 0 && j < nverts; ++j ) { PyObject *item = PySequence_ITEM( tmp, j ); if( !PyInt_Check( item ) ) ok = 1; else { int index = PyInt_AsLong ( item ); if( index < 0 || index >= self->mesh->totvert ) ok = 2; } Py_DECREF( item ); } Py_DECREF( tmp ); /* not ints or outside of vertex list ... error */ if( ok ) { Py_DECREF( args ); if( ok == 1 ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected an integer index" ); else return EXPP_ReturnPyObjError( PyExc_KeyError, "index out of range" ); } } if( nverts == 2 ) ++new_edge_count; /* if only two vert, then add only edge */ else new_edge_count += nverts; /* otherwise, one edge per vert */ } /* OK, commit to allocating the search structures */ newpair = (SrchEdges *)MEM_callocN( sizeof(SrchEdges)*new_edge_count, "MEdgePairs" ); /* scan the input list and build the new edge pair list */ len = PySequence_Size( args ); tmppair = newpair; new_edge_count = 0; for( i = 0; i < len; ++i ) { int edge_count; int eedges[4]; tmp = PySequence_GetItem( args, i ); nverts = PySequence_Size( tmp ); /* get new references for the vertices */ for(j = 0; j < nverts; ++j ) { PyObject *item = PySequence_ITEM( tmp, j ); if( BPy_MVert_Check( item ) ) { eedges[j] = ((BPy_MVert *)item)->index; } else { eedges[j] = PyInt_AsLong ( item ); } Py_DECREF( item ); } Py_DECREF( tmp ); if( nverts == 2 ) edge_count = 1; /* again, two verts give just one edge */ else edge_count = nverts; /* now add the edges to the search list */ for( j = 0; j < edge_count; ++j ) { int k = j+1; if( k == nverts ) /* final edge */ k = 0; /* sort verts into search list, skip if two are the same */ if( eedges[j] != eedges[k] ) { if( eedges[j] < eedges[k] ) { tmppair->v[0] = eedges[j]; tmppair->v[1] = eedges[k]; tmppair->swap = 0; } else { tmppair->v[0] = eedges[k]; tmppair->v[1] = eedges[j]; tmppair->swap = 1; } tmppair->index = new_edge_count; ++new_edge_count; tmppair++; } } } /* sort the new edge pairs */ qsort( newpair, new_edge_count, sizeof(SrchEdges), medge_comp ); /* * find duplicates in the new list and mark. if it's a duplicate, * then mark by setting second vert index to 0 (a real edge won't have * second vert index of 0 since verts are sorted) */ good_edges = new_edge_count; /* all edges are good at this point */ tmppair = newpair; /* "last good edge" */ tmppair2 = &tmppair[1]; /* "current candidate edge" */ for( i = 0; i < new_edge_count; ++i ) { if( tmppair->v[0] != tmppair2->v[0] || tmppair->v[1] != tmppair2->v[1] ) tmppair = tmppair2; /* last != current, so current == last */ else { tmppair2->v[1] = 0; /* last == current, so mark as duplicate */ --good_edges; /* one less good edge */ } tmppair2++; } /* if mesh has edges, see if any of the new edges are already in it */ if( mesh->totedge ) { oldpair = (SrchEdges *)MEM_callocN( sizeof(SrchEdges)*mesh->totedge, "MEdgePairs" ); /* * build a search list of new edges (don't need to update "swap" * field, since we're not creating edges here) */ tmppair = oldpair; tmpedge = mesh->medge; for( i = 0; i < mesh->totedge; ++i ) { if( tmpedge->v1 < tmpedge->v2 ) { tmppair->v[0] = tmpedge->v1; tmppair->v[1] = tmpedge->v2; } else { tmppair->v[0] = tmpedge->v2; tmppair->v[1] = tmpedge->v1; } ++tmpedge; ++tmppair; } /* sort the old edge pairs */ qsort( oldpair, mesh->totedge, sizeof(SrchEdges), medge_comp ); /* eliminate new edges already in the mesh */ tmppair = newpair; for( i = new_edge_count; i-- ; ) { if( tmppair->v[1] ) { if( bsearch( tmppair, oldpair, mesh->totedge, sizeof(SrchEdges), medge_comp ) ) { tmppair->v[1] = 0; /* mark as duplicate */ --good_edges; } } tmppair++; } MEM_freeN( oldpair ); } /* if any new edges are left, add to list */ if( good_edges ) { CustomData edata; int totedge = mesh->totedge+good_edges; /* create custom edge data arrays and copy existing edges into it */ CustomData_copy( &mesh->edata, &edata, CD_MASK_MESH, CD_DEFAULT, totedge ); CustomData_copy_data( &mesh->edata, &edata, 0, 0, mesh->totedge ); if ( !CustomData_has_layer( &edata, CD_MEDGE ) ) CustomData_add_layer( &edata, CD_MEDGE, CD_CALLOC, NULL, totedge ); /* replace old with new data */ CustomData_free( &mesh->edata, mesh->totedge ); mesh->edata = edata; mesh_update_customdata_pointers( mesh ); /* resort edges into original order */ qsort( newpair, new_edge_count, sizeof(SrchEdges), medge_index_comp ); /* point to the first edge we're going to add */ tmpedge = &mesh->medge[mesh->totedge]; tmppair = newpair; /* as we find a good edge, add it */ while( good_edges ) { if( tmppair->v[1] ) { /* not marked as duplicate ! */ if( !tmppair->swap ) { tmpedge->v1 = tmppair->v[0]; tmpedge->v2 = tmppair->v[1]; } else { tmpedge->v1 = tmppair->v[1]; tmpedge->v2 = tmppair->v[0]; } tmpedge->flag = ME_EDGEDRAW | ME_EDGERENDER | SELECT; mesh->totedge++; --good_edges; ++tmpedge; } tmppair++; } } /* clean up and leave */ mesh_update( mesh ); MEM_freeN( newpair ); Py_DECREF ( args ); Py_RETURN_NONE; } static PyObject *MEdgeSeq_delete( BPy_MEdgeSeq * self, PyObject *args ) { Mesh *mesh = self->mesh; MEdge *srcedge; MFace *srcface; unsigned int *vert_table, *del_table, *edge_table; int i, len; int face_count, edge_count, vert_count; /* * if input tuple contains a single sequence, use it as input instead; * otherwise use the sequence as-is and check later that it contains * one or more integers or MVerts */ if( PySequence_Size( args ) == 1 ) { PyObject *tmp = PyTuple_GET_ITEM( args, 0 ); if( PySequence_Check( tmp ) ) args = tmp; } /* if sequence is empty, do nothing */ len = PySequence_Size( args ); if( len == 0 ) { Py_RETURN_NONE; } edge_table = (unsigned int *)MEM_callocN( len*sizeof( unsigned int ), "edge_table" ); /* get the indices of edges to be removed */ for( i = len; i--; ) { PyObject *tmp = PySequence_GetItem( args, i ); if( BPy_MEdge_Check( tmp ) ) edge_table[i] = ((BPy_MEdge *)tmp)->index; else if( PyInt_Check( tmp ) ) edge_table[i] = PyInt_AsLong ( tmp ); else { MEM_freeN( edge_table ); Py_DECREF( tmp ); return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of ints or MEdges" ); } Py_DECREF( tmp ); /* if index out-of-range, throw exception */ if( edge_table[i] >= (unsigned int)mesh->totedge ) { MEM_freeN( edge_table ); return EXPP_ReturnPyObjError( PyExc_ValueError, "array index out of range" ); } } /* * build two tables: first table marks vertices which belong to an edge * which is being deleted */ del_table = (unsigned int *)MEM_callocN( mesh->totvert*sizeof( unsigned int ), "vert_table" ); /* * Borrow a trick from editmesh code: for each edge to be deleted, mark * its vertices as well. Then go through face list and look for two * consecutive marked vertices. */ /* mark each edge that's to be deleted */ srcedge = mesh->medge; for( i = len; i--; ) { unsigned int idx = edge_table[i]; del_table[srcedge[idx].v1] = UINT_MAX; del_table[srcedge[idx].v2] = UINT_MAX; srcedge[idx].v1 = UINT_MAX; } /* * second table is used for vertices which become orphaned (belong to no * edges) and need to be deleted; it's also the normal lookup table for * old->new vertex indices */ vert_table = (unsigned int *)MEM_mallocN( mesh->totvert*sizeof( unsigned int ), "vert_table" ); /* assume all edges will be deleted (fills with UINT_MAX) */ memset( vert_table, UCHAR_MAX, mesh->totvert*sizeof( unsigned int ) ); /* unmark vertices of each "good" edge; count each "bad" edge */ edge_count = 0; for( i = mesh->totedge; i--; ++srcedge ) if( srcedge->v1 != UINT_MAX ) vert_table[srcedge->v1] = vert_table[srcedge->v2] = 0; else ++edge_count; /* * find faces which no longer have all edges */ face_count = 0; srcface = mesh->mface; for( i = 0; i < mesh->totface; ++i, ++srcface ) { int len = srcface->v4 ? 4 : 3; unsigned int id[4]; int del; id[0] = del_table[srcface->v1]; id[1] = del_table[srcface->v2]; id[2] = del_table[srcface->v3]; id[3] = del_table[srcface->v4]; del = ( id[0] == UINT_MAX && id[1] == UINT_MAX ) || ( id[1] == UINT_MAX && id[2] == UINT_MAX ); if( !del ) { if( len == 3 ) del = ( id[2] == UINT_MAX && id[0] == UINT_MAX ); else del = ( id[2] == UINT_MAX && id[3] == UINT_MAX ) || ( id[3] == UINT_MAX && id[0] == UINT_MAX ); } if( del ) { srcface->v1 = UINT_MAX; ++face_count; } } /* fix the vertex lookup table, if any verts to delete, do so now */ vert_count = make_vertex_table( vert_table, mesh->totvert ); if( vert_count ) delete_verts( mesh, vert_table, vert_count ); /* delete faces which have a deleted edge */ delete_faces( mesh, vert_table, face_count ); /* now delete the edges themselves */ delete_edges( mesh, vert_table, edge_count ); /* clean up and return */ MEM_freeN( del_table ); MEM_freeN( vert_table ); MEM_freeN( edge_table ); mesh_update ( mesh ); Py_RETURN_NONE; } static PyObject *MEdgeSeq_collapse( BPy_MEdgeSeq * self, PyObject *args ) { MEdge *srcedge; unsigned int *edge_table; float (*vert_list)[3]; int i, len; Base *base, *basact; Mesh *mesh = self->mesh; Object *object = NULL; PyObject *tmp; /* * when using removedoublesflag(), we need to switch to editmode, so * nobody else can be using it */ if( G.obedit ) return EXPP_ReturnPyObjError(PyExc_RuntimeError, "can't use collapse() while in edit mode" ); /* make sure we get a tuple of sequences of something */ switch( PySequence_Size( args ) ) { case 1: /* if a sequence... */ tmp = PyTuple_GET_ITEM( args, 0 ); if( PySequence_Check( tmp ) ) { PyObject *tmp2; /* ignore empty sequences */ if( !PySequence_Size( tmp ) ) { Py_RETURN_NONE; } /* if another sequence, use it */ tmp2 = PySequence_ITEM( tmp, 0 ); if( PySequence_Check( tmp2 ) ) args = tmp; Py_INCREF( args ); Py_DECREF( tmp2 ); } else return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of sequence pairs" ); break; case 2: /* two args may be individual edges/verts */ tmp = PyTuple_GET_ITEM( args, 0 ); /* * if first item isn't a sequence, then assume it's a bunch of MVerts * and wrap inside a tuple */ if( !PySequence_Check( tmp ) ) { args = Py_BuildValue( "(O)", args ); if( !args ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "Py_BuildValue() failed" ); /* * otherwise, assume it already a bunch of sequences so use as-is */ } else { Py_INCREF( args ); /* so we can safely DECREF later */ } break; default: /* anything else is definitely wrong */ return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of sequence pairs" ); } /* if sequence is empty, do nothing */ len = PySequence_Size( args ); if( len == 0 ) { Py_RETURN_NONE; } /* allocate table of edge indices and new vertex values */ edge_table = (unsigned int *)MEM_callocN( len*sizeof( unsigned int ), "edge_table" ); vert_list = (float (*)[3])MEM_callocN( 3*len*sizeof( float ), "vert_list" ); /* get the indices of edges to be collapsed and new vert locations */ for( i = len; i--; ) { PyObject *tmp1; PyObject *tmp2; tmp = PySequence_GetItem( args, i ); /* if item isn't sequence of size 2, error */ if( !PySequence_Check( tmp ) || PySequence_Size( tmp ) != 2 ) { MEM_freeN( edge_table ); MEM_freeN( vert_list ); Py_DECREF( tmp ); Py_DECREF( args ); return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of (MEdges, vector)" ); } /* if items aren't a MEdge/int and vector, error */ tmp1 = PySequence_GetItem( tmp, 0 ); tmp2 = PySequence_GetItem( tmp, 1 ); Py_DECREF( tmp ); if( !(BPy_MEdge_Check( tmp1 ) || PyInt_Check( tmp1 )) || !VectorObject_Check ( tmp2 ) ) { MEM_freeN( edge_table ); MEM_freeN( vert_list ); Py_DECREF( tmp1 ); Py_DECREF( tmp2 ); Py_DECREF( args ); return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of (MEdges, vector)" ); } /* store edge index, new vertex location */ if( PyInt_Check( tmp1 ) ) edge_table[i] = PyInt_AsLong ( tmp1 ); else edge_table[i] = ((BPy_MEdge *)tmp1)->index; memcpy( vert_list[i], ((VectorObject *)tmp2)->vec, 3*sizeof( float ) ); Py_DECREF( tmp1 ); Py_DECREF( tmp2 ); /* if index out-of-range, throw exception */ if( edge_table[i] >= (unsigned int)mesh->totedge ) { MEM_freeN( edge_table ); MEM_freeN( vert_list ); return EXPP_ReturnPyObjError( PyExc_ValueError, "edge index out of range" ); } } /* * simple algorithm: * (1) deselect all verts * (2) for each edge * (2a) replace both verts with the new vert * (2b) select both verts * (3) call removedoublesflag() */ /* (1) deselect all verts */ for( i = mesh->totvert; i--; ) mesh->mvert[i].flag &= ~SELECT; /* (2) replace edge's verts and select them */ for( i = len; i--; ) { srcedge = &mesh->medge[edge_table[i]]; memcpy( &mesh->mvert[srcedge->v1].co, vert_list[i], 3*sizeof( float ) ); memcpy( &mesh->mvert[srcedge->v2].co, vert_list[i], 3*sizeof( float ) ); mesh->mvert[srcedge->v1].flag |= SELECT; mesh->mvert[srcedge->v2].flag |= SELECT; } /* (3) call removedoublesflag() */ for( base = FIRSTBASE; base; base = base->next ) { if( base->object->type == OB_MESH && base->object->data == self->mesh ) { object = base->object; break; } } basact = BASACT; BASACT = base; removedoublesflag( 1, 0, 0.0 ); /* make mesh's object active, enter mesh edit mode */ G.obedit = object; /* exit edit mode, free edit mesh */ load_editMesh(); free_editMesh(G.editMesh); BASACT = basact; /* clean up and exit */ Py_DECREF( args ); MEM_freeN( vert_list ); MEM_freeN( edge_table ); mesh_update ( mesh ); Py_RETURN_NONE; } static PyObject *MEdgeSeq_selected( BPy_MEdgeSeq * self ) { int i, count; Mesh *mesh = self->mesh; MEdge *tmpedge; PyObject *list; /* first count selected edges (quicker than appending to PyList?) */ count = 0; tmpedge = mesh->medge; for( i = 0; i < mesh->totedge; ++i, ++tmpedge ) if( (mesh->mvert[tmpedge->v1].flag & SELECT) && (mesh->mvert[tmpedge->v2].flag & SELECT) ) ++count; list = PyList_New( count ); if( !list ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyList_New() failed" ); /* next, insert selected edges into list */ count = 0; tmpedge = mesh->medge; for( i = 0; i < mesh->totedge; ++i, ++tmpedge ) { if( (mesh->mvert[tmpedge->v1].flag & SELECT) && (mesh->mvert[tmpedge->v2].flag & SELECT) ) { PyObject *tmp = PyInt_FromLong( i ); if( !tmp ) { Py_DECREF( list ); return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } PyList_SET_ITEM( list, count, tmp ); ++count; } } return list; } static PyObject *MEdgeSeq_add_layertype(BPy_MEdgeSeq *self, PyObject *args) { Mesh *me = (Mesh*)self->mesh; return Mesh_addPropLayer_internal(me, &(me->edata), me->totedge, args); } static PyObject *MEdgeSeq_del_layertype(BPy_MEdgeSeq *self, PyObject *value) { Mesh *me = (Mesh*)self->mesh; return Mesh_removePropLayer_internal(me, &(me->edata), me->totedge, value); } static PyObject *MEdgeSeq_rename_layertype(BPy_MEdgeSeq *self, PyObject *args) { Mesh *me = (Mesh*)self->mesh; return Mesh_renamePropLayer_internal(me,&(me->edata),args); } static PyObject *MEdgeSeq_PropertyList(BPy_MEdgeSeq *self) { Mesh *me = (Mesh*)self->mesh; return Mesh_propList_internal(&(me->edata)); } static struct PyMethodDef BPy_MEdgeSeq_methods[] = { {"extend", (PyCFunction)MEdgeSeq_extend, METH_VARARGS, "add edges to mesh"}, {"delete", (PyCFunction)MEdgeSeq_delete, METH_VARARGS, "delete edges from mesh"}, {"selected", (PyCFunction)MEdgeSeq_selected, METH_NOARGS, "returns a list containing indices of selected edges"}, {"collapse", (PyCFunction)MEdgeSeq_collapse, METH_VARARGS, "collapse one or more edges to a vertex"}, {"addPropertyLayer",(PyCFunction)MEdgeSeq_add_layertype, METH_VARARGS, "add a new property layer"}, {"removePropertyLayer",(PyCFunction)MEdgeSeq_del_layertype, METH_O, "removes a property layer"}, {"renamePropertyLayer",(PyCFunction)MEdgeSeq_rename_layertype, METH_VARARGS, "renames an existing property layer"}, {NULL, NULL, 0, NULL} }; static PyGetSetDef BPy_MEdgeSeq_getseters[] = { {"properties", (getter)MEdgeSeq_PropertyList, (setter)NULL, "edge property layers, read only", NULL}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /************************************************************************ * * Python MEdgeSeq_Type standard operators * ************************************************************************/ /*****************************************************************************/ /* Python MEdgeSeq_Type structure definition: */ /*****************************************************************************/ PyTypeObject MEdgeSeq_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "." */ "Blender MEdgeSeq", /* char *tp_name; */ sizeof( BPy_MEdgeSeq ), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ NULL, /* destructor tp_dealloc; */ NULL, /* printfunc tp_print; */ NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ NULL, /* cmpfunc tp_compare; */ NULL, /* reprfunc tp_repr; */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ &MEdgeSeq_as_sequence, /* PySequenceMethods *tp_as_sequence; */ NULL, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ NULL, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ NULL, /* richcmpfunc tp_richcompare; */ /*** weak reference enabler ***/ 0, /* long tp_weaklistoffset; */ /*** Added in release 2.2 ***/ /* Iterators */ ( getiterfunc) MEdgeSeq_getIter, /* getiterfunc tp_iter; */ ( iternextfunc ) MEdgeSeq_nextIter, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ BPy_MEdgeSeq_methods, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ BPy_MEdgeSeq_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ NULL, /* descrsetfunc tp_descr_set; */ 0, /* long tp_dictoffset; */ NULL, /* initproc tp_init; */ NULL, /* allocfunc tp_alloc; */ NULL, /* newfunc tp_new; */ /* Low-level free-memory routine */ NULL, /* freefunc tp_free; */ /* For PyObject_IS_GC */ NULL, /* inquiry tp_is_gc; */ NULL, /* PyObject *tp_bases; */ /* method resolution order */ NULL, /* PyObject *tp_mro; */ NULL, /* PyObject *tp_cache; */ NULL, /* PyObject *tp_subclasses; */ NULL, /* PyObject *tp_weaklist; */ NULL }; /************************************************************************ * * Face attributes * ************************************************************************/ static MFace * MFace_get_pointer( BPy_MFace * self ) { if( self->index >= self->mesh->totface ) return (MFace *)EXPP_ReturnPyObjError( PyExc_RuntimeError, "MFace is no longer valid" ); return &self->mesh->mface[self->index]; } /* * get a face's vertices */ static PyObject *MFace_getVerts( BPy_MFace * self ) { PyObject *attr; MFace *face = MFace_get_pointer( self ); if( !face ) return NULL; attr = PyTuple_New( face->v4 ? 4 : 3 ); if( !attr ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyTuple_New() failed" ); PyTuple_SetItem( attr, 0, MVert_CreatePyObject( self->mesh, face->v1 ) ); PyTuple_SetItem( attr, 1, MVert_CreatePyObject( self->mesh, face->v2 ) ); PyTuple_SetItem( attr, 2, MVert_CreatePyObject( self->mesh, face->v3 ) ); if( face->v4 ) PyTuple_SetItem( attr, 3, MVert_CreatePyObject( self->mesh, face->v4 ) ); return attr; } /* * set a face's vertices */ static int MFace_setVerts( BPy_MFace * self, PyObject * args ) { BPy_MVert *v1, *v2, *v3, *v4 = NULL; MFace *face = MFace_get_pointer( self ); if( !face ) return -1; if( !PyArg_ParseTuple ( args, "O!O!O!|O!", &MVert_Type, &v1, &MVert_Type, &v2, &MVert_Type, &v3, &MVert_Type, &v4 ) ) return EXPP_ReturnIntError( PyExc_TypeError, "expected tuple of 3 or 4 MVerts" ); if( v1->index == v2->index || v1->index == v3->index || v2->index == v3->index ) return EXPP_ReturnIntError( PyExc_ValueError, "cannot assign 2 or move verts that are the same" ); if(v4 && ( v1->index == v4->index || v2->index == v4->index || v3->index == v4->index )) return EXPP_ReturnIntError( PyExc_ValueError, "cannot assign 2 or move verts that are the same" ); if( v1->index >= self->mesh->totvert || v2->index >= self->mesh->totvert || v3->index >= self->mesh->totvert || (v4 &&( v4->index >= self->mesh->totvert))) return EXPP_ReturnIntError( PyExc_ValueError, "cannot assign verts that have been removed" ); face->v1 = v1->index; face->v2 = v2->index; face->v3 = v3->index; if( v4 ) face->v4 = v4->index; return 0; } /* * get face's material index */ static PyObject *MFace_getMat( BPy_MFace * self ) { MFace *face = MFace_get_pointer( self ); if( !face ) return NULL; return PyInt_FromLong( face->mat_nr ); } /* * set face's material index */ static int MFace_setMat( BPy_MFace * self, PyObject * value ) { MFace *face = MFace_get_pointer( self ); if( !face ) return -1; /* error is set */ return EXPP_setIValueRange( value, &face->mat_nr, 0, 15, 'b' ); } /* * get a face's index */ static PyObject *MFace_getIndex( BPy_MFace * self ) { MFace *face = MFace_get_pointer( self ); if( !face ) return NULL; /* error is set */ return PyInt_FromLong( self->index ); } /* * get face's normal index */ static PyObject *MFace_getNormal( BPy_MFace * self ) { float *vert[4]; float no[3]; MFace *face = MFace_get_pointer( self ); Mesh *me = self->mesh; if( !face ) return NULL; /* error is set */ if MFACE_VERT_BADRANGE_CHECK(me, face) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "one or more MFace vertices are no longer valid" ); vert[0] = me->mvert[face->v1].co; vert[1] = me->mvert[face->v2].co; vert[2] = me->mvert[face->v3].co; if( face->v4 ) { vert[3] = me->mvert[face->v4].co; CalcNormFloat4( vert[0], vert[1], vert[2], vert[3], no ); } else CalcNormFloat( vert[0], vert[1], vert[2], no ); return newVectorObject( no, 3, Py_NEW ); } /* * get face's center location */ static PyObject *MFace_getCent( BPy_MFace * self ) { float *vert[4]; float cent[3]= {0,0,0}; int i=3, j, k; Mesh *me = self->mesh; MFace *face = MFace_get_pointer( self ); if( !face ) return NULL; /* error is set */ if MFACE_VERT_BADRANGE_CHECK(me, face) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "one or more MFace vertices are no longer valid" ); vert[0] = me->mvert[face->v1].co; vert[1] = me->mvert[face->v2].co; vert[2] = me->mvert[face->v3].co; if( face->v4 ) { vert[3] = me->mvert[face->v4].co; i=4; } for (j=0;jmesh; if( !face ) return NULL; /* error is set */ if MFACE_VERT_BADRANGE_CHECK(me, face) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "one or more MFace vertices are no longer valid" ); v1 = me->mvert[face->v1].co; v2 = me->mvert[face->v2].co; v3 = me->mvert[face->v3].co; if( face->v4 ) { v4 = me->mvert[face->v4].co; return PyFloat_FromDouble( AreaQ3Dfl(v1, v2, v3, v4)); } else return PyFloat_FromDouble( AreaT3Dfl(v1, v2, v3)); } /* * get one of a face's mface flag bits */ static PyObject *MFace_getMFlagBits( BPy_MFace * self, void * type ) { MFace *face = MFace_get_pointer( self ); if( !face ) return NULL; /* error is set */ return EXPP_getBitfield( &face->flag, (int)((long)type & 0xff), 'b' ); } /* * set one of a face's mface flag bits */ static int MFace_setMFlagBits( BPy_MFace * self, PyObject * value, void * type ) { MFace *face = MFace_get_pointer( self ); if( !face ) return -1; /* error is set */ return EXPP_setBitfield( value, &face->flag, (int)((long)type & 0xff), 'b' ); } static int MFace_setSelect( BPy_MFace * self, PyObject * value, void * type_unused ) { MFace *face = MFace_get_pointer( self ); int param = PyObject_IsTrue( value ); Mesh *me; if( !face ) return -1; /* error is set */ if( param == -1 ) return EXPP_ReturnIntError( PyExc_TypeError, "expected True/False or 0/1" ); me = self->mesh; if( param ) { face->flag |= ME_FACE_SEL; me->mvert[face->v1].flag |= SELECT; me->mvert[face->v2].flag |= SELECT; me->mvert[face->v3].flag |= SELECT; if( face->v4 ) me->mvert[face->v4].flag |= SELECT; } else { face->flag &= ~ME_FACE_SEL; me->mvert[face->v1].flag &= ~SELECT; me->mvert[face->v2].flag &= ~SELECT; me->mvert[face->v3].flag &= ~SELECT; if( face->v4 ) me->mvert[face->v4].flag &= ~SELECT; } if( self->mesh->mselect ) { MEM_freeN( self->mesh->mselect ); self->mesh->mselect = NULL; } return 0; } /* * get face's texture image */ static PyObject *MFace_getImage( BPy_MFace *self ) { MTFace *face; if( !self->mesh->mtface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no texture values" ); if( !MFace_get_pointer( self ) ) return NULL; face = &self->mesh->mtface[self->index]; if( face->tpage ) return Image_CreatePyObject( face->tpage ); else Py_RETURN_NONE; } /* * change or clear face's texture image */ static int MFace_setImage( BPy_MFace *self, PyObject *value ) { MTFace *face; if( !MFace_get_pointer( self ) ) return -1; if( value && value != Py_None && !BPy_Image_Check( value ) ) return EXPP_ReturnIntError( PyExc_TypeError, "expected image object or None" ); if( !self->mesh->mtface ) #if 0 return EXPP_ReturnIntError( PyExc_ValueError, "face has no texture values" ); #else make_tfaces( self->mesh ); #endif face = &self->mesh->mtface[self->index]; if( value == NULL || value == Py_None ) face->tpage = NULL; /* should memory be freed? */ else { face->tpage = ( ( BPy_Image * ) value )->image; face->mode |= TF_TEX; } return 0; } #define MFACE_FLAG_BITMASK ( TF_SELECT | TF_SEL1 | \ TF_SEL2 | TF_SEL3 | TF_SEL4 | TF_HIDE ) /* * get face's texture flag */ static PyObject *MFace_getFlag( BPy_MFace *self ) { int flag; if( !self->mesh->mtface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no texture values" ); if( !MFace_get_pointer( self ) ) return NULL; flag = self->mesh->mtface[self->index].flag & MFACE_FLAG_BITMASK; /* so old scripts still work */ if (self->index == self->mesh->act_face) flag |= TF_ACTIVE; return PyInt_FromLong( (long)( flag ) ); } /* * set face's texture flag */ static int MFace_setFlag( BPy_MFace *self, PyObject *value ) { int param; if( !self->mesh->mtface ) return EXPP_ReturnIntError( PyExc_ValueError, "face has no texture values" ); if( !MFace_get_pointer( self ) ) return -1; if( !PyInt_Check ( value ) ) { char errstr[128]; sprintf ( errstr , "expected int bitmask of 0x%04x", MFACE_FLAG_BITMASK ); return EXPP_ReturnIntError( PyExc_TypeError, errstr ); } param = PyInt_AS_LONG ( value ); /* only one face can be active, so don't allow that here */ if( param & TF_ACTIVE ) param &= ~TF_ACTIVE; if( ( param & MFACE_FLAG_BITMASK ) != param ) return EXPP_ReturnIntError( PyExc_ValueError, "invalid bit(s) set in mask" ); /* merge active setting with other new params */ param |= (self->mesh->mtface[self->index].flag); self->mesh->mtface[self->index].flag = (char)param; return 0; } /* * get face's texture mode */ static PyObject *MFace_getMode( BPy_MFace *self ) { if( !self->mesh->mtface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no texture values" ); if( !MFace_get_pointer( self ) ) return NULL; return PyInt_FromLong( self->mesh->mtface[self->index].mode ); } /* * set face's texture mode */ static int MFace_setMode( BPy_MFace *self, PyObject *value ) { int param; static short bitmask = TF_DYNAMIC | TF_TEX | TF_SHAREDVERT | TF_LIGHT | TF_SHAREDCOL | TF_TILES | TF_BILLBOARD | TF_TWOSIDE | TF_INVISIBLE | TF_OBCOL | TF_BILLBOARD2 | TF_SHADOW | TF_BMFONT; if( !self->mesh->mtface ) return EXPP_ReturnIntError( PyExc_ValueError, "face has no texture values" ); if( !MFace_get_pointer( self ) ) return -1; if( !PyInt_Check ( value ) ) { char errstr[128]; sprintf ( errstr , "expected int bitmask of 0x%04x", bitmask ); return EXPP_ReturnIntError( PyExc_TypeError, errstr ); } param = PyInt_AS_LONG ( value ); if( param == 0xffff ) /* if param is ALL, set everything but HALO */ param = bitmask ^ TF_BILLBOARD; else if( ( param & bitmask ) != param ) return EXPP_ReturnIntError( PyExc_ValueError, "invalid bit(s) set in mask" ); /* Blender UI doesn't allow these on at the same time */ if( ( param & (TF_BILLBOARD | TF_BILLBOARD2) ) == (TF_BILLBOARD | TF_BILLBOARD2) ) return EXPP_ReturnIntError( PyExc_ValueError, "HALO and BILLBOARD cannot be enabled simultaneously" ); self->mesh->mtface[self->index].mode = (short)param; return 0; } /* * get face's texture transparency setting */ static PyObject *MFace_getTransp( BPy_MFace *self ) { if( !self->mesh->mtface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no texture values" ); if( !MFace_get_pointer( self ) ) return NULL; return PyInt_FromLong( self->mesh->mtface[self->index].transp ); } /* * set face's texture transparency setting */ static int MFace_setTransp( BPy_MFace *self, PyObject *value ) { if( !self->mesh->mtface ) return EXPP_ReturnIntError( PyExc_ValueError, "face has no texture values" ); if( !MFace_get_pointer( self ) ) return -1; return EXPP_setIValueRange( value, &self->mesh->mtface[self->index].transp, TF_SOLID, TF_SUB, 'b' ); } /* * get a face's texture UV coord values */ static PyObject *MFace_getUV( BPy_MFace * self ) { MTFace *face; PyObject *attr; int length, i; if( !self->mesh->mtface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no texture values" ); if( !MFace_get_pointer( self ) ) return NULL; face = &self->mesh->mtface[self->index]; length = self->mesh->mface[self->index].v4 ? 4 : 3; attr = PyTuple_New( length ); if( !attr ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyTuple_New() failed" ); for( i=0; iuv[i], 2, Py_WRAP ); if( !vector ) return NULL; PyTuple_SetItem( attr, i, vector ); } return attr; } /* * set a face's texture UV coord values */ static int MFace_setUV( BPy_MFace * self, PyObject * value ) { MTFace *face; int length, i; if( !MFace_get_pointer( self ) ) return -1; if( !PySequence_Check( value ) || EXPP_check_sequence_consistency( value, &vector_Type ) != 1 ) return EXPP_ReturnIntError( PyExc_TypeError, "expected sequence of vectors" ); length = self->mesh->mface[self->index].v4 ? 4 : 3; if( length != PySequence_Size( value ) ) return EXPP_ReturnIntError( PyExc_TypeError, "size of vertex and UV sequences differ" ); if( !self->mesh->mtface ) #if 0 return EXPP_ReturnIntError( PyExc_ValueError, "face has no texture values" ); #else make_tfaces( self->mesh ); #endif face = &self->mesh->mtface[self->index]; for( i=0; iuv[i][0] = vector->vec[0]; face->uv[i][1] = vector->vec[1]; Py_DECREF( vector ); } return 0; } /* * get a face's texture UV coord select state */ static PyObject *MFace_getUVSel( BPy_MFace * self ) { MTFace *face; PyObject *attr; int length, i, mask; if( !self->mesh->mtface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no texture values" ); if( !MFace_get_pointer( self ) ) return NULL; face = &self->mesh->mtface[self->index]; length = self->mesh->mface[self->index].v4 ? 4 : 3; attr = PyTuple_New( length ); if( !attr ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyTuple_New() failed" ); /* get coord select state, one bit at a time */ mask = TF_SEL1; for( i=0; iflag & mask ? 1 : 0 ); if( !value ) { Py_DECREF( attr ); return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } PyTuple_SetItem( attr, i, value ); } return attr; } /* * set a face's texture UV coord select state */ static int MFace_setUVSel( BPy_MFace * self, PyObject * value ) { MTFace *face; int length, i, mask; if( !MFace_get_pointer( self ) ) return -1; if( !PySequence_Check( value ) ) return EXPP_ReturnIntError( PyExc_TypeError, "expected a tuple of integers" ); length = self->mesh->mface[self->index].v4 ? 4 : 3; if( length != PySequence_Size( value ) ) return EXPP_ReturnIntError( PyExc_TypeError, "size of vertex and UV lists differ" ); if( !self->mesh->mtface ) #if 0 return EXPP_ReturnIntError( PyExc_ValueError, "face has no texture values" ); #else make_tfaces( self->mesh ); #endif /* set coord select state, one bit at a time */ face = &self->mesh->mtface[self->index]; mask = TF_SEL1; for( i=0; iflag |= mask; else face->flag &= ~mask; Py_DECREF(tmp); } return 0; } /* * get a face's vertex colors. note that if mesh->mtfaces is defined, then * it takes precedent over mesh->mcol */ static PyObject *MFace_getCol( BPy_MFace * self ) { PyObject *attr; int length, i; MCol * mcol; /* if there's no mesh color vectors or texture faces, nothing to do */ if( !self->mesh->mcol ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no vertex colors" ); if( !MFace_get_pointer( self ) ) return NULL; mcol = &self->mesh->mcol[self->index*4]; length = self->mesh->mface[self->index].v4 ? 4 : 3; attr = PyTuple_New( length ); if( !attr ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyTuple_New() failed" ); for( i=0; imesh->mcol ) return EXPP_ReturnIntError( PyExc_ValueError, "face has no vertex colors" ); if( !MFace_get_pointer( self ) ) return -1; mcol = &self->mesh->mcol[self->index*4]; length = self->mesh->mface[self->index].v4 ? 4 : 3; if( !PyList_Check( value ) && !PyTuple_Check( value ) ) return EXPP_ReturnIntError( PyExc_TypeError, "expected a sequence of MCols" ); if( EXPP_check_sequence_consistency( value, &MCol_Type ) != 1 ) return EXPP_ReturnIntError( PyExc_TypeError, "expected a sequence of MCols" ); if( PySequence_Size( value ) != length ) return EXPP_ReturnIntError( PyExc_ValueError, "incorrect number of colors for this face" ); for( i=0; icolor->r; mcol[i].g = obj->color->g; mcol[i].b = obj->color->b; mcol[i].a = obj->color->a; Py_DECREF( obj ); } return 0; } /* * get edge keys for using in a dictionary or set key */ static PyObject *MFace_getEdgeKeys( BPy_MFace * self ) { MFace *face = MFace_get_pointer( self ); PyObject *attr, *edpair; if (!face) return NULL; /* error set */ if (face->v4) { attr = PyTuple_New( 4 ); edpair = PyTuple_New( 2 ); if (face->v1 > face->v2) { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v2) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v1) ); } else { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v1) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v2) ); } PyTuple_SET_ITEM( attr, 0, edpair ); edpair = PyTuple_New( 2 ); if (face->v2 > face->v3) { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v3) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v2) ); } else { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v2) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v3) ); } PyTuple_SET_ITEM( attr, 1, edpair ); edpair = PyTuple_New( 2 ); if (face->v3 > face->v4) { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v4) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v3) ); } else { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v3) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v4) ); } PyTuple_SET_ITEM( attr, 2, edpair ); edpair = PyTuple_New( 2 ); if (face->v4 > face->v1) { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v1) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v4) ); } else { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v4) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v1) ); } PyTuple_SET_ITEM( attr, 3, edpair ); } else { attr = PyTuple_New( 3 ); edpair = PyTuple_New( 2 ); if (face->v1 > face->v2) { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v2) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v1) ); } else { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v1) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v2) ); } PyTuple_SET_ITEM( attr, 0, edpair ); edpair = PyTuple_New( 2 ); if (face->v2 > face->v3) { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v3) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v2) ); } else { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v2) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v3) ); } PyTuple_SET_ITEM( attr, 1, edpair ); edpair = PyTuple_New( 2 ); if (face->v3 > face->v1) { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v1) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v3) ); } else { PyTuple_SET_ITEM( edpair, 0, PyInt_FromLong(face->v3) ); PyTuple_SET_ITEM( edpair, 1, PyInt_FromLong(face->v1) ); } PyTuple_SET_ITEM( attr, 2, edpair ); } return attr; } /************************************************************************ * * Python MFace_Type attributes get/set structure * ************************************************************************/ static PyGetSetDef BPy_MFace_getseters[] = { {"verts", (getter)MFace_getVerts, (setter)MFace_setVerts, "face's vertices", NULL}, {"v", (getter)MFace_getVerts, (setter)MFace_setVerts, "deprecated: see 'verts'", NULL}, {"mat", (getter)MFace_getMat, (setter)MFace_setMat, "face's material index", NULL}, {"index", (getter)MFace_getIndex, (setter)NULL, "face's index", NULL}, {"no", (getter)MFace_getNormal, (setter)NULL, "face's normal", NULL}, {"cent", (getter)MFace_getCent, (setter)NULL, "face's center", NULL}, {"area", (getter)MFace_getArea, (setter)NULL, "face's 3D area", NULL}, {"hide", (getter)MFace_getMFlagBits, (setter)MFace_setMFlagBits, "face hidden in edit mode", (void *)ME_HIDE}, {"sel", (getter)MFace_getMFlagBits, (setter)MFace_setSelect, "face selected in edit mode", (void *)ME_FACE_SEL}, {"smooth", (getter)MFace_getMFlagBits, (setter)MFace_setMFlagBits, "face smooth enabled", (void *)ME_SMOOTH}, /* attributes for texture faces (mostly, I think) */ {"col", (getter)MFace_getCol, (setter)MFace_setCol, "face's vertex colors", NULL}, {"flag", (getter)MFace_getFlag, (setter)MFace_setFlag, "flags associated with texture faces", NULL}, {"image", (getter)MFace_getImage, (setter)MFace_setImage, "image associated with texture faces", NULL}, {"mode", (getter)MFace_getMode, (setter)MFace_setMode, "modes associated with texture faces", NULL}, {"transp", (getter)MFace_getTransp, (setter)MFace_setTransp, "transparency of texture faces", NULL}, {"uv", (getter)MFace_getUV, (setter)MFace_setUV, "face's UV coordinates", NULL}, {"uvSel", (getter)MFace_getUVSel, (setter)MFace_setUVSel, "face's UV coordinates select status", NULL}, {"edge_keys", (getter)MFace_getEdgeKeys, (setter)NULL, "for each edge this face uses return an ordered tuple edge pair that can be used as a key in a dictionary or set", NULL}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /************************************************************************ * * Python MFace_Type iterator (iterates over vertices) * ************************************************************************/ /* * Initialize the interator index */ static PyObject *MFace_getIter( BPy_MFace * self ) { if (self->iter==-1) { self->iter = 0; return EXPP_incr_ret ( (PyObject *) self ); } else { BPy_MFace *seq= (BPy_MFace *)MFace_CreatePyObject(self->mesh, self->index); seq->iter = 0; return (PyObject *) seq; } } /* * Return next MVert. Throw an exception after the final vertex. */ static PyObject *MFace_nextIter( BPy_MFace * self ) { struct MFace *face = &self->mesh->mface[self->index]; int len = self->mesh->mface[self->index].v4 ? 4 : 3; if( self->iter == len ) { self->iter = -1; return EXPP_ReturnPyObjError( PyExc_StopIteration, "iterator at end" ); } ++self->iter; switch ( self->iter ) { case 1: return MVert_CreatePyObject( self->mesh, face->v1 ); case 2: return MVert_CreatePyObject( self->mesh, face->v2 ); case 3: return MVert_CreatePyObject( self->mesh, face->v3 ); default : return MVert_CreatePyObject( self->mesh, face->v4 ); } } /************************************************************************ * * Python MFace_Type methods * ************************************************************************/ /************************************************************************ * * Python MFace_Type standard operations * ************************************************************************/ static int MFace_compare( BPy_MFace * a, BPy_MFace * b ) { return( a->mesh == b->mesh && a->index == b->index ) ? 0 : -1; } static PyObject *MFace_repr( BPy_MFace* self ) { MFace *face = MFace_get_pointer( self ); if( !face ) return NULL; if( face->v4 ) return PyString_FromFormat( "[MFace (%d %d %d %d) %d]", (int)face->v1, (int)face->v2, (int)face->v3, (int)face->v4, (int)self->index ); else return PyString_FromFormat( "[MFace (%d %d %d) %d]", (int)face->v1, (int)face->v2, (int)face->v3, (int)self->index ); } static long MFace_hash( BPy_MFace *self ) { return (long)self->index; } static int MFace_len( BPy_MFace * self ) { if( self->index >= self->mesh->totface ) return 0; return self->mesh->mface[self->index].v4 ? 4 : 3; } static PySequenceMethods MFace_as_sequence = { ( inquiry ) MFace_len, /* sq_length */ ( binaryfunc ) 0, /* sq_concat */ ( intargfunc ) 0, /* sq_repeat */ ( intargfunc ) 0, /* sq_item */ ( intintargfunc ) 0, /* sq_slice */ ( intobjargproc ) 0, /* sq_ass_item */ ( intintobjargproc ) 0, /* sq_ass_slice */ 0,0,0, }; static PyObject *MFace_getProp( BPy_MFace *self, PyObject *args) { Mesh *me = (Mesh *)self->mesh; MFace *face = MFace_get_pointer( self ); if( !face ) return NULL; mesh_update_customdata_pointers(me); //! return Mesh_getProperty_internal(&(me->fdata), self->index, args); } static PyObject *MFace_setProp( BPy_MFace *self, PyObject *args) { Mesh *me = (Mesh *)self->mesh; PyObject *obj; MFace *face = MFace_get_pointer( self ); if( !face ) return NULL; /* error set */ obj = Mesh_setProperty_internal(&(me->fdata), self->index, args); mesh_update_customdata_pointers(me); //! return obj; } static struct PyMethodDef BPy_MFace_methods[] = { {"getProperty", (PyCFunction)MFace_getProp, METH_O, "get property indicated by name"}, {"setProperty", (PyCFunction)MFace_setProp, METH_VARARGS, "set property indicated by name"}, {NULL, NULL, 0, NULL} }; /************************************************************************ * * Python MFace_Type structure definition * ************************************************************************/ PyTypeObject MFace_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "." */ "Blender MFace", /* char *tp_name; */ sizeof( BPy_MFace ), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ NULL, /* destructor tp_dealloc; */ NULL, /* printfunc tp_print; */ NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ ( cmpfunc ) MFace_compare, /* cmpfunc tp_compare; */ ( reprfunc ) MFace_repr, /* reprfunc tp_repr; */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ &MFace_as_sequence, /* PySequenceMethods *tp_as_sequence; */ NULL, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ ( hashfunc ) MFace_hash, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ NULL, /* richcmpfunc tp_richcompare; */ /*** weak reference enabler ***/ 0, /* long tp_weaklistoffset; */ /*** Added in release 2.2 ***/ /* Iterators */ ( getiterfunc ) MFace_getIter, /* getiterfunc tp_iter; */ ( iternextfunc ) MFace_nextIter, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ BPy_MFace_methods, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ BPy_MFace_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ NULL, /* descrsetfunc tp_descr_set; */ 0, /* long tp_dictoffset; */ NULL, /* initproc tp_init; */ NULL, /* allocfunc tp_alloc; */ NULL, /* newfunc tp_new; */ /* Low-level free-memory routine */ NULL, /* freefunc tp_free; */ /* For PyObject_IS_GC */ NULL, /* inquiry tp_is_gc; */ NULL, /* PyObject *tp_bases; */ /* method resolution order */ NULL, /* PyObject *tp_mro; */ NULL, /* PyObject *tp_cache; */ NULL, /* PyObject *tp_subclasses; */ NULL, /* PyObject *tp_weaklist; */ NULL }; static PyObject *MFace_CreatePyObject( Mesh * mesh, int i ) { BPy_MFace *obj = PyObject_NEW( BPy_MFace, &MFace_Type ); if( !obj ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyObject_New() failed" ); obj->mesh = mesh; obj->index = i; obj->iter= -1; return (PyObject *)obj; } /************************************************************************ * * Face sequence * ************************************************************************/ static int MFaceSeq_len( BPy_MFaceSeq * self ) { return self->mesh->totface; } static PyObject *MFaceSeq_item( BPy_MFaceSeq * self, int i ) { if( i < 0 || i >= self->mesh->totface ) return EXPP_ReturnPyObjError( PyExc_IndexError, "array index out of range" ); return MFace_CreatePyObject( self->mesh, i ); } static PySequenceMethods MFaceSeq_as_sequence = { ( inquiry ) MFaceSeq_len, /* sq_length */ ( binaryfunc ) 0, /* sq_concat */ ( intargfunc ) 0, /* sq_repeat */ ( intargfunc ) MFaceSeq_item, /* sq_item */ ( intintargfunc ) 0, /* sq_slice */ ( intobjargproc ) 0, /* sq_ass_item */ ( intintobjargproc ) 0, /* sq_ass_slice */ 0,0,0, }; /************************************************************************ * * Python MFaceSeq_Type iterator (iterates over faces) * ************************************************************************/ /* * Initialize the interator index */ static PyObject *MFaceSeq_getIter( BPy_MFaceSeq * self ) { if (self->iter==-1) { self->iter = 0; return EXPP_incr_ret ( (PyObject *) self ); } else { BPy_MFaceSeq *seq = (BPy_MFaceSeq *)MFaceSeq_CreatePyObject(self->mesh); seq->iter = 0; return (PyObject *)seq; } } /* * Return next MFace. */ static PyObject *MFaceSeq_nextIter( BPy_MFaceSeq * self ) { if( self->iter == self->mesh->totface ) { self->iter= -1; /* not being used in a seq */ return EXPP_ReturnPyObjError( PyExc_StopIteration, "iterator at end" ); } return MFace_CreatePyObject( self->mesh, self->iter++ ); } /************************************************************************ * * Python MFaceSeq_Type methods * ************************************************************************/ static PyObject *MFaceSeq_extend( BPy_MEdgeSeq * self, PyObject *args, PyObject *keywds ) { /* * (a) check input for valid edge objects, faces which consist of * only three or four edges * (b) check input to be sure edges form a closed face (each edge * contains verts in two other different edges?) * * (1) build list of new faces; remove duplicates * * use existing "v4=0 rule" for 3-vert faces * (2) build list of existing faces for searching * (3) from new face list, remove existing faces: */ int len, nverts; int i, j, k, new_face_count; int good_faces; SrchFaces *oldpair, *newpair, *tmppair, *tmppair2; PyObject *tmp; MFace *tmpface; Mesh *mesh = self->mesh; int ignore_dups = 0; PyObject *return_list = NULL; char flag = ME_FACE_SEL; /* before we try to add faces, add edges; if it fails; exit */ tmp = MEdgeSeq_extend( self, args ); if( !tmp ) return NULL; Py_DECREF( tmp ); /* process any keyword arguments */ if( keywds ) { PyObject *res = PyDict_GetItemString( keywds, "ignoreDups" ); if( res ) { ignore_dups = PyObject_IsTrue( res ); if (ignore_dups==-1) { return EXPP_ReturnPyObjError( PyExc_TypeError, "keyword argument \"ignoreDups\" expected True/False or 0/1" ); } } res = PyDict_GetItemString( keywds, "indexList" ); if (res) { switch( PyObject_IsTrue( res ) ) { case 0: break; case -1: return EXPP_ReturnPyObjError( PyExc_TypeError, "keyword argument \"indexList\" expected True/False or 0/1" ); default: return_list = PyList_New( 0 ); } } res = PyDict_GetItemString( keywds, "smooth" ); if (res) { switch( PyObject_IsTrue( res ) ) { case 0: break; case -1: return EXPP_ReturnPyObjError( PyExc_TypeError, "keyword argument \"smooth\" expected True/False or 0/1" ); default: flag |= ME_SMOOTH; } } } /* make sure we get a tuple of sequences of something */ switch( PySequence_Size( args ) ) { case 1: /* better be a sequence or a tuple */ /* if a sequence... */ tmp = PyTuple_GET_ITEM( args, 0 ); if( PySequence_Check( tmp ) ) { PyObject *tmp2; /* ignore empty sequences */ if( !PySequence_Size( tmp ) ) { Py_RETURN_NONE; } /* if another sequence, use it */ tmp2 = PySequence_ITEM( tmp, 0 ); if( PySequence_Check( tmp2 ) ) args = tmp; Py_INCREF( args ); Py_DECREF( tmp2 ); } else return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of sequence pairs" ); break; case 2: case 3: case 4: /* two to four args may be individual verts */ tmp = PyTuple_GET_ITEM( args, 0 ); /* * if first item isn't a sequence, then assume it's a bunch of MVerts * and wrap inside a tuple */ if( !PySequence_Check( tmp ) ) { args = Py_BuildValue( "(O)", args ); if( !args ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "Py_BuildValue() failed" ); /* * otherwise, assume it already a bunch of sequences so use as-is */ } else { Py_INCREF( args ); /* so we can safely DECREF later */ } break; default: /* anything else is definitely wrong */ return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of sequence pairs" ); } /* if nothing to add, just exit */ len = PySequence_Size( args ); if( len == 0 ) { Py_DECREF( args ); Py_RETURN_NONE; } /* * Since we call MEdgeSeq_extend first, we already know the input list * is valid. Here we just need to count the total number of faces. */ new_face_count = 0; for( i = 0; i < len; ++i ) { tmp = PySequence_ITEM( args, i ); nverts = PySequence_Size( tmp ); if( return_list || nverts != 2 ) ++new_face_count; /* new faces must have 3 or 4 verts */ Py_DECREF( tmp ); } /* OK, commit to allocating the search structures */ newpair = (SrchFaces *)MEM_callocN( sizeof(SrchFaces)*new_face_count, "MFacePairs" ); /* scan the input list and build the new face pair list */ len = PySequence_Size( args ); tmppair = newpair; for( i = 0; i < len; ++i ) { MFace tmpface; unsigned int vert[4]={0,0,0,0}; unsigned char order[4]={0,1,2,3}; tmp = PySequence_GetItem( args, i ); nverts = PySequence_Size( tmp ); if( nverts == 2 ) { /* again, ignore 2-vert tuples */ if( return_list ) /* if returning indices, mark as deleted */ tmppair->v[1] = 0; Py_DECREF( tmp ); continue; } /* * get the face's vertices' indexes */ for( j = 0; j < nverts; ++j ) { PyObject *item = PySequence_ITEM( tmp, j ); if( BPy_MVert_Check( item ) ) vert[j] = ((BPy_MVert *)item)->index; else vert[j] = PyInt_AsLong( item ); Py_DECREF( item ); } Py_DECREF( tmp ); tmpface.v1 = vert[0]; tmpface.v2 = vert[1]; tmpface.v3 = vert[2]; tmpface.v4 = vert[3]; /* * go through some contortions to guarantee the third and fourth * vertices are not index 0 */ eeek_fix( &tmpface, nverts == 4 ); vert[0] = tmpface.v1; vert[1] = tmpface.v2; vert[2] = tmpface.v3; if( nverts == 3 ) vert[3] = tmppair->v[3] = 0; else vert[3] = tmpface.v4; /* * sort the verts before placing in pair list. the order of * vertices in the face is very important, so keep track of * the original order */ for( j = nverts-1; j >= 0; --j ) { for( k = 0; k < j; ++k ) { if( vert[k] > vert[k+1] ) { SWAP( int, vert[k], vert[k+1] ); SWAP( char, order[k], order[k+1] ); } else if( vert[k] == vert[k+1] ) { break; } } if( k < j ) break; tmppair->v[j] = vert[j]; } if( j >= 0 ) { /* a duplicate vertex found */ if( return_list ) { /* if returning index list */ tmppair->v[1] = 0; /* mark as deleted */ } else { --new_face_count; /* otherwise skip */ continue; } } tmppair->index = i; /* pack order into a byte */ tmppair->order = order[0]|(order[1]<<2)|(order[2]<<4)|(order[3]<<6); ++tmppair; } /* * find duplicates in the new list and mark. if it's a duplicate, * then mark by setting second vert index to 0 (a real edge won't have * second vert index of 0 since verts are sorted) */ good_faces = new_face_count; /* assume all faces good to start */ tmppair = newpair; /* "last good edge" */ tmppair2 = &tmppair[1]; /* "current candidate edge" */ if( !ignore_dups ) { /* sort the new face pairs */ qsort( newpair, new_face_count, sizeof(SrchFaces), mface_comp ); for( i = 0; i < new_face_count; ++i ) { if( mface_comp( tmppair, tmppair2 ) ) tmppair = tmppair2; /* last != current, so current == last */ else { tmppair2->v[1] = 0; /* last == current, so mark as duplicate */ --good_faces; /* one less good face */ } tmppair2++; } } /* if mesh has faces, see if any of the new faces are already in it */ if( mesh->totface && !ignore_dups ) { oldpair = (SrchFaces *)MEM_callocN( sizeof(SrchFaces)*mesh->totface, "MFacePairs" ); tmppair = oldpair; tmpface = mesh->mface; for( i = 0; i < mesh->totface; ++i ) { unsigned char order[4]={0,1,2,3}; int verts[4]; verts[0]=tmpface->v1; verts[1]=tmpface->v2; verts[2]=tmpface->v3; verts[3]=tmpface->v4; len = ( tmpface->v4 ) ? 3 : 2; tmppair->v[3] = 0; /* for triangular faces */ /* sort the verts before placing in pair list here too */ for( j = len; j >= 0; --j ) { for( k = 0; k < j; ++k ) if( verts[k] > verts[k+1] ) { SWAP( int, verts[k], verts[k+1] ); SWAP( unsigned char, order[k], order[k+1] ); } tmppair->v[j] = verts[j]; } /* pack order into a byte */ tmppair->order = order[0]|(order[1]<<2)|(order[2]<<4)|(order[3]<<6); ++tmppair; ++tmpface; } /* sort the old face pairs */ qsort( oldpair, mesh->totface, sizeof(SrchFaces), mface_comp ); /* eliminate new faces already in the mesh */ tmppair = newpair; for( i = good_faces; i ; ) { if( tmppair->v[1] ) { if( bsearch( tmppair, oldpair, mesh->totface, sizeof(SrchFaces), mface_comp ) ) { tmppair->v[1] = 0; /* mark as duplicate */ --good_faces; } --i; } tmppair++; } MEM_freeN( oldpair ); } /* if any new faces are left, add to list */ if( good_faces || return_list ) { int totface = mesh->totface+good_faces; /* new face count */ CustomData fdata; CustomData_copy( &mesh->fdata, &fdata, CD_MASK_MESH, CD_DEFAULT, totface ); CustomData_copy_data( &mesh->fdata, &fdata, 0, 0, mesh->totface ); if ( !CustomData_has_layer( &fdata, CD_MFACE ) ) CustomData_add_layer( &fdata, CD_MFACE, CD_CALLOC, NULL, totface ); CustomData_free( &mesh->fdata, mesh->totface ); mesh->fdata = fdata; mesh_update_customdata_pointers( mesh ); /* sort the faces back into their original input list order */ if( !ignore_dups ) qsort( newpair, new_face_count, sizeof(SrchFaces), mface_index_comp ); /* point to the first face we're going to add */ tmpface = &mesh->mface[mesh->totface]; tmppair = newpair; if( return_list ) good_faces = new_face_count; /* assume all faces good to start */ /* as we find a good face, add it */ while ( good_faces ) { if( tmppair->v[1] ) { int i; unsigned int index[4]; unsigned char order = tmppair->order; /* unpack the order of the vertices */ for( i = 0; i < 4; ++i ) { index[(order & 0x03)] = i; order >>= 2; } /* now place vertices in the proper order */ tmpface->v1 = tmppair->v[index[0]]; tmpface->v2 = tmppair->v[index[1]]; tmpface->v3 = tmppair->v[index[2]]; tmpface->v4 = tmppair->v[index[3]]; tmpface->flag = flag; if( return_list ) { tmp = PyInt_FromLong( mesh->totface ); PyList_Append( return_list, tmp ); Py_DECREF(tmp); } mesh->totface++; ++tmpface; --good_faces; } else if( return_list ) { PyList_Append( return_list, Py_None ); --good_faces; } tmppair++; } } /* clean up and leave */ mesh_update( mesh ); Py_DECREF ( args ); MEM_freeN( newpair ); if( return_list ) return return_list; else Py_RETURN_NONE; } struct fourEdges { FaceEdges *v[4]; }; static PyObject *MFaceSeq_delete( BPy_MFaceSeq * self, PyObject *args ) { unsigned int *face_table; int i, len; Mesh *mesh = self->mesh; MFace *tmpface; int face_count; int edge_also = 0; /* check for valid inputs */ if( PySequence_Size( args ) != 2 || !PyArg_ParseTuple( args, "iO", &edge_also, &args ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected and int and a sequence of ints or MFaces" ); if( !PyList_Check( args ) && !PyTuple_Check( args ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected and int and a sequence of ints or MFaces" ); /* see how many args we need to parse */ len = PySequence_Size( args ); if( len < 1 ) return EXPP_ReturnPyObjError( PyExc_TypeError, "sequence must contain at least one int or MFace" ); face_table = MEM_callocN( len*sizeof( unsigned int ), "face_table" ); /* get the indices of faces to be removed */ for( i = len; i--; ) { PyObject *tmp = PySequence_GetItem( args, i ); if( BPy_MFace_Check( tmp ) ) face_table[i] = ((BPy_MFace *)tmp)->index; else if( PyInt_Check( tmp ) ) face_table[i] = PyInt_AsLong( tmp ); else { MEM_freeN( face_table ); Py_DECREF( tmp ); return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of ints or MFaces" ); } Py_DECREF( tmp ); /* if index out-of-range, throw exception */ if( face_table[i] >= (unsigned int)mesh->totface ) { MEM_freeN( face_table ); return EXPP_ReturnPyObjError( PyExc_ValueError, "array index out of range" ); } } if( edge_also ) { /* * long version * * (1) build sorted table of all edges * (2) construct face->edge lookup table for all faces * face->e1 = mesh->medge[i] * (3) (delete sorted table) * (4) mark all edges as live * (5) mark all edges for deleted faces as dead * (6) mark all edges for remaining faces as live * (7) delete all dead edges * (8) (delete face lookup table) * */ FaceEdges *edge_table, *tmp_et; MEdge *tmpedge; FaceEdges **face_edges; FaceEdges **tmp_fe; struct fourEdges *fface; int edge_count; edge_table = MEM_mallocN( mesh->totedge*sizeof( FaceEdges ), "edge_table" ); tmpedge = mesh->medge; tmp_et = edge_table; for( i = 0; i < mesh->totedge; ++i ) { if( tmpedge->v1 < tmpedge->v2 ) { tmp_et->v[0] = tmpedge->v1; tmp_et->v[1] = tmpedge->v2; } else { tmp_et->v[0] = tmpedge->v2; tmp_et->v[1] = tmpedge->v1; } tmp_et->index = i; tmp_et->sel = 1; /* select each edge */ ++tmpedge; ++tmp_et; } /* sort the edge pairs */ qsort( edge_table, mesh->totedge, sizeof(FaceEdges), faceedge_comp ); /* build face translation table, lookup edges */ face_edges = MEM_callocN( 4*sizeof(FaceEdges*)*mesh->totface, "face_edges" ); tmp_fe = face_edges; tmpface = mesh->mface; for( i = mesh->totface; i--; ++tmpface ) { FaceEdges *ptrs[4]; unsigned int verts[4]; int j,k; FaceEdges target; int len=tmpface->v4 ? 4 : 3; ptrs[3] = NULL; verts[0] = tmpface->v1; verts[1] = tmpface->v2; verts[2] = tmpface->v3; if( len == 4 ) verts[3] = tmpface->v4; for( j = 0; j < len; ++j ) { k = (j+1) % len; if( verts[j] < verts[k] ) { target.v[0] = verts[j]; target.v[1] = verts[k]; } else { target.v[0] = verts[k]; target.v[1] = verts[j]; } ptrs[j] = bsearch( &target, edge_table, mesh->totedge, sizeof(FaceEdges), faceedge_comp ); } for( j = 0; j < 4; ++j, ++tmp_fe ) *tmp_fe = ptrs[j]; } /* for each face, deselect each edge */ tmpface = mesh->mface; face_count = 0; for( i = len; i--; ) { if( tmpface[face_table[i]].v1 != UINT_MAX ) { fface = (void *)face_edges; fface += face_table[i]; fface->v[0]->sel = 0; fface->v[1]->sel = 0; fface->v[2]->sel = 0; if( fface->v[3] ) fface->v[3]->sel = 0; tmpface[face_table[i]].v1 = UINT_MAX; ++face_count; } } /* for each remaining face, select all edges */ tmpface = mesh->mface; fface = (struct fourEdges *)face_edges; for( i = mesh->totface; i--; ++tmpface, ++fface ) { if( tmpface->v1 != UINT_MAX ) { fface->v[0]->sel = 1; fface->v[1]->sel = 1; fface->v[2]->sel = 1; if( fface->v[3] ) fface->v[3]->sel = 1; } } /* now mark the selected edges for deletion */ edge_count = 0; for( i = 0; i < mesh->totedge; ++i ) { if( !edge_table[i].sel ) { mesh->medge[edge_table[i].index].v1 = UINT_MAX; ++edge_count; } } if( edge_count ) delete_edges( mesh, NULL, edge_count ); MEM_freeN( face_edges ); MEM_freeN( edge_table ); } else { /* mark faces to delete */ tmpface = mesh->mface; face_count = 0; for( i = len; i--; ) if( tmpface[face_table[i]].v1 != UINT_MAX ) { tmpface[face_table[i]].v1 = UINT_MAX; ++face_count; } } /* delete faces which have a deleted edge */ delete_faces( mesh, NULL, face_count ); /* clean up and return */ MEM_freeN( face_table ); mesh_update ( mesh ); Py_RETURN_NONE; } /* copied from meshtools.c - should make generic? */ static void permutate(void *list, int num, int size, int *index) { void *buf; int len; int i; len = num * size; buf = MEM_mallocN(len, "permutate"); memcpy(buf, list, len); for (i = 0; i < num; i++) { memcpy((char *)list + (i * size), (char *)buf + (index[i] * size), size); } MEM_freeN(buf); } /* this wrapps list sorting then applies back to the mesh */ static PyObject *MFaceSeq_sort( BPy_MEdgeSeq * self, PyObject *args, PyObject *keywds ) { PyObject *ret, *sort_func, *newargs; Mesh *mesh = self->mesh; PyObject *sorting_list; CustomDataLayer *layer; int i, *index; /* get a list for internal use */ sorting_list = PySequence_List( (PyObject *)self ); if( !sorting_list ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyList_New() failed" ); /* create index list */ index = (int *) MEM_mallocN(sizeof(int) * mesh->totface, "sort faces"); if (!index) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "faces.sort(...) failed to allocate memory" ); newargs = EXPP_PyTuple_New_Prepend(args, sorting_list); sort_func = PyObject_GetAttrString( ((PyObject *)&PyList_Type), "sort"); ret = PyObject_Call(sort_func, newargs, keywds); Py_DECREF(newargs); Py_DECREF(sort_func); if (ret) { /* copy the faces indicies to index */ for (i = 0; i < mesh->totface; i++) index[i] = ((BPy_MFace *)PyList_GET_ITEM(sorting_list, i))->index; for(i = 0; i < mesh->fdata.totlayer; i++) { layer = &mesh->fdata.layers[i]; permutate(layer->data, mesh->totface, CustomData_sizeof(layer->type), index); } } Py_DECREF(sorting_list); MEM_freeN(index); return ret; } static PyObject *MFaceSeq_selected( BPy_MFaceSeq * self ) { int i, count; Mesh *mesh = self->mesh; MFace *tmpface; PyObject *list; /* first count selected faces (quicker than appending to PyList?) */ count = 0; tmpface = mesh->mface; for( i = 0; i < mesh->totface; ++i, ++tmpface ) if( tmpface->flag & ME_FACE_SEL ) ++count; list = PyList_New( count ); if( !list ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyList_New() failed" ); /* next, insert selected faces into list */ count = 0; tmpface = mesh->mface; for( i = 0; i < mesh->totface; ++i, ++tmpface ) { if( tmpface->flag & ME_FACE_SEL ) { PyObject *tmp = PyInt_FromLong( i ); if( !tmp ) { Py_DECREF( list ); return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } PyList_SET_ITEM( list, count, tmp ); ++count; } } return list; } static PyObject *MFaceSeq_add_layertype(BPy_MFaceSeq *self, PyObject *args) { Mesh *me = (Mesh*)self->mesh; return Mesh_addPropLayer_internal(me, &(me->fdata), me->totface, args); } static PyObject *MFaceSeq_del_layertype(BPy_MFaceSeq *self, PyObject *value) { Mesh *me = (Mesh*)self->mesh; return Mesh_removePropLayer_internal(me, &(me->fdata), me->totface, value); } static PyObject *MFaceSeq_rename_layertype(BPy_MFaceSeq *self, PyObject *args) { Mesh *me = (Mesh*)self->mesh; return Mesh_renamePropLayer_internal(me,&(me->fdata),args); } static PyObject *MFaceSeq_PropertyList(BPy_MFaceSeq *self) { Mesh *me = (Mesh*)self->mesh; return Mesh_propList_internal(&(me->fdata)); } static struct PyMethodDef BPy_MFaceSeq_methods[] = { {"extend", (PyCFunction)MFaceSeq_extend, METH_VARARGS|METH_KEYWORDS, "add faces to mesh"}, {"delete", (PyCFunction)MFaceSeq_delete, METH_VARARGS, "delete faces from mesh"}, {"sort", (PyCFunction)MFaceSeq_sort, METH_VARARGS|METH_KEYWORDS, "sort the faces using list sorts syntax"}, {"selected", (PyCFunction)MFaceSeq_selected, METH_NOARGS, "returns a list containing indices of selected faces"}, {"addPropertyLayer",(PyCFunction)MFaceSeq_add_layertype, METH_VARARGS, "add a new property layer"}, {"removePropertyLayer",(PyCFunction)MFaceSeq_del_layertype, METH_O, "removes a property layer"}, {"renamePropertyLayer",(PyCFunction)MFaceSeq_rename_layertype, METH_VARARGS, "renames an existing property layer"}, {NULL, NULL, 0, NULL} }; static PyGetSetDef BPy_MFaceSeq_getseters[] = { {"properties", (getter)MFaceSeq_PropertyList, (setter)NULL, "vertex property layers, read only", NULL}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /************************************************************************ * * Python MFaceSeq_Type standard operations * ************************************************************************/ /*****************************************************************************/ /* Python MFaceSeq_Type structure definition: */ /*****************************************************************************/ PyTypeObject MFaceSeq_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "." */ "Blender MFaceSeq", /* char *tp_name; */ sizeof( BPy_MFaceSeq ), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ NULL, /* destructor tp_dealloc; */ NULL, /* printfunc tp_print; */ NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ NULL, /* cmpfunc tp_compare; */ NULL, /* reprfunc tp_repr; */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ &MFaceSeq_as_sequence, /* PySequenceMethods *tp_as_sequence; */ NULL, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ NULL, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ NULL, /* richcmpfunc tp_richcompare; */ /*** weak reference enabler ***/ 0, /* long tp_weaklistoffset; */ /*** Added in release 2.2 ***/ /* Iterators */ ( getiterfunc )MFaceSeq_getIter, /* getiterfunc tp_iter; */ ( iternextfunc )MFaceSeq_nextIter, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ BPy_MFaceSeq_methods, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ BPy_MFaceSeq_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ NULL, /* descrsetfunc tp_descr_set; */ 0, /* long tp_dictoffset; */ NULL, /* initproc tp_init; */ NULL, /* allocfunc tp_alloc; */ NULL, /* newfunc tp_new; */ /* Low-level free-memory routine */ NULL, /* freefunc tp_free; */ /* For PyObject_IS_GC */ NULL, /* inquiry tp_is_gc; */ NULL, /* PyObject *tp_bases; */ /* method resolution order */ NULL, /* PyObject *tp_mro; */ NULL, /* PyObject *tp_cache; */ NULL, /* PyObject *tp_subclasses; */ NULL, /* PyObject *tp_weaklist; */ NULL }; /************************************************************************ * * Python BPy_Mesh methods * ************************************************************************/ static PyObject *Mesh_calcNormals( BPy_Mesh * self ) { Mesh *mesh = self->mesh; mesh_calc_normals( mesh->mvert, mesh->totvert, mesh->mface, mesh->totface, NULL ); Py_RETURN_NONE; } static PyObject *Mesh_vertexShade( BPy_Mesh * self ) { Base *base = FIRSTBASE; if( G.obedit ) return EXPP_ReturnPyObjError(PyExc_RuntimeError, "can't shade vertices while in edit mode" ); while( base ) { if( base->object->type == OB_MESH && base->object->data == self->mesh ) { base->flag |= SELECT; set_active_base( base ); make_vertexcol(1); countall(); Py_RETURN_NONE; } base = base->next; } return EXPP_ReturnPyObjError(PyExc_RuntimeError, "object not found in baselist!" ); } /* * force display list update */ static PyObject *Mesh_Update( BPy_Mesh * self, PyObject *args, PyObject *kwd ) { char *blockname= NULL; static char *kwlist[] = {"key", NULL}; if( !PyArg_ParseTupleAndKeywords(args, kwd, "|s", kwlist, &blockname) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "Expected nothing or the name of a shapeKey"); if (blockname) { Mesh *me = self->mesh; MVert *mv = me->mvert; Key *key= me->key; KeyBlock *kb; float (*co)[3]; int i; if (!key) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "Cannot update the key for this mesh, it has no shape keys"); for (kb = key->block.first; kb; kb=kb->next) if (strcmp(blockname, kb->name)==0) break; if (!kb) return EXPP_ReturnPyObjError( PyExc_ValueError, "This requested key to update does not exist"); for(i=0, co= kb->data; itotvert; i++, mv++, co++) VECCOPY(*co, mv->co); } else { /* Normal operation */ mesh_update( self->mesh ); } Py_RETURN_NONE; } /* * search for a single edge in mesh's edge list */ static PyObject *Mesh_findEdge( BPy_Mesh * self, PyObject *args ) { int i; unsigned int v1, v2; PyObject *tmp; MEdge *edge = self->mesh->medge; if( EXPP_check_sequence_consistency( args, &MVert_Type ) == 1 && PySequence_Size( args ) == 2 ) { tmp = PyTuple_GET_ITEM( args, 0 ); v1 = ((BPy_MVert *)tmp)->index; tmp = PyTuple_GET_ITEM( args, 1 ); v2 = ((BPy_MVert *)tmp)->index; } else if( PyArg_ParseTuple( args, "ii", &v1, &v2 ) ) { if( (int)v1 >= self->mesh->totvert || (int)v2 >= self->mesh->totvert ) return EXPP_ReturnPyObjError( PyExc_IndexError, "index out of range" ); } else return EXPP_ReturnPyObjError( PyExc_RuntimeError, "expected tuple of two ints or MVerts" ); for( i = 0; i < self->mesh->totedge; ++i ) { if( ( edge->v1 == v1 && edge->v2 == v2 ) || ( edge->v1 == v2 && edge->v2 == v1 ) ) { tmp = PyInt_FromLong( i ); if( tmp ) return tmp; return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } ++edge; } Py_RETURN_NONE; } /* * search for a group of edges in mesh's edge list */ static PyObject *Mesh_findEdges( PyObject * self, PyObject *args ) { int len; int i; SrchEdges *oldpair, *tmppair, target, *result; PyObject *list, *tmp; BPy_MVert *v1, *v2; unsigned int index1, index2; MEdge *tmpedge; Mesh *mesh = ((BPy_Mesh *)self)->mesh; /* if no edges, nothing to do */ if( !mesh->totedge ) return EXPP_ReturnPyObjError( PyExc_ValueError, "mesh has no edges" ); /* make sure we get a sequence of tuples of something */ tmp = PyTuple_GET_ITEM( args, 0 ); switch( PySequence_Size ( args ) ) { case 1: /* better be a list or a tuple */ if( !PySequence_Check ( tmp ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of tuple int or MVert pairs" ); args = tmp; Py_INCREF( args ); /* so we can safely DECREF later */ break; case 2: /* take any two args and put into a tuple */ if( PyTuple_Check( tmp ) ) Py_INCREF( args ); /* if first arg is a tuple, assume both are */ else { args = Py_BuildValue( "((OO))", tmp, PyTuple_GET_ITEM( args, 1 ) ); if( !args ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "Py_BuildValue() failed" ); } break; default: /* anything else is definitely wrong */ return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of tuple pairs" ); } len = PySequence_Size( args ); if( len == 0 ) { Py_DECREF( args ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected at least one tuple" ); } /* if a single edge, handle the simpler way */ if( len == 1 ) { PyObject *result; tmp = PySequence_GetItem( args, 0 ); result = Mesh_findEdge( (BPy_Mesh *)self, tmp ); Py_DECREF( tmp ); Py_DECREF( args ); return result; } /* build a list of all edges so we can search */ oldpair = (SrchEdges *)MEM_callocN( sizeof(SrchEdges)*mesh->totedge, "MEdgePairs" ); tmppair = oldpair; tmpedge = mesh->medge; for( i = 0; i < mesh->totedge; ++i ) { if( tmpedge->v1 < tmpedge->v2 ) { tmppair->v[0] = tmpedge->v1; tmppair->v[1] = tmpedge->v2; } else { tmppair->v[0] = tmpedge->v2; tmppair->v[1] = tmpedge->v1; } tmppair->index = i; ++tmpedge; ++tmppair; } /* sort the old edge pairs */ qsort( oldpair, mesh->totedge, sizeof(SrchEdges), medge_comp ); list = PyList_New( len ); if( !len ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyList_New() failed" ); /* scan the input list, find vert pairs, then search the edge list */ for( i = 0; i < len; ++i ) { tmp = PySequence_GetItem( args, i ); if( !PyTuple_Check( tmp ) || PyTuple_Size( tmp ) != 2 ) { MEM_freeN( oldpair ); Py_DECREF( tmp ); Py_DECREF( args ); Py_DECREF( list ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected tuple pair" ); } /* get objects, check that they are both MVerts of this mesh */ v1 = (BPy_MVert *)PyTuple_GET_ITEM( tmp, 0 ); v2 = (BPy_MVert *)PyTuple_GET_ITEM( tmp, 1 ); Py_DECREF ( tmp ); if( BPy_MVert_Check( v1 ) && BPy_MVert_Check( v2 ) ) { if( v1->data != (void *)mesh || v2->data != (void *)mesh ) { MEM_freeN( oldpair ); Py_DECREF( args ); Py_DECREF( list ); return EXPP_ReturnPyObjError( PyExc_ValueError, "one or both MVerts do not belong to this mesh" ); } index1 = v1->index; index2 = v2->index; } else if( PyInt_Check( v1 ) && PyInt_Check( v2 ) ) { index1 = PyInt_AsLong( (PyObject *)v1 ); index2 = PyInt_AsLong( (PyObject *)v2 ); if( (int)index1 >= mesh->totvert || (int)index2 >= mesh->totvert ) { MEM_freeN( oldpair ); Py_DECREF( args ); Py_DECREF( list ); return EXPP_ReturnPyObjError( PyExc_IndexError, "index out of range" ); } } else { MEM_freeN( oldpair ); Py_DECREF( args ); Py_DECREF( list ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected tuple to contain MVerts" ); } /* sort verts into order */ if( index1 < index2 ) { target.v[0] = index1; target.v[1] = index2; } else { target.v[0] = index2; target.v[1] = index1; } /* search edge list for a match; result is index or None */ result = bsearch( &target, oldpair, mesh->totedge, sizeof(SrchEdges), medge_comp ); if( result ) tmp = PyInt_FromLong( result->index ); else tmp = EXPP_incr_ret( Py_None ); if( !tmp ) { MEM_freeN( oldpair ); Py_DECREF( args ); Py_DECREF( list ); return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } PyList_SET_ITEM( list, i, tmp ); } MEM_freeN( oldpair ); Py_DECREF ( args ); return list; } /* * replace mesh data with mesh data from another object */ static PyObject *Mesh_getFromObject( BPy_Mesh * self, PyObject * args ) { Object *ob = NULL; PyObject *object_arg; ID tmpid; Mesh *tmpmesh; Curve *tmpcu = NULL; DerivedMesh *dm; Object *tmpobj = NULL; int cage = 0, render = 0, i; if( !PyArg_ParseTuple( args, "O|ii", &object_arg, &cage, &render ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected object or string and optional integer arguments" ); if ( PyString_Check( object_arg ) ) { char *name; name = PyString_AsString ( object_arg ); ob = ( Object * ) GetIdFromList( &( G.main->object ), name ); if( !ob ) return EXPP_ReturnPyObjError( PyExc_AttributeError, name ); } else if ( BPy_Object_Check(object_arg) ) { ob = (( BPy_Object * ) object_arg)->object; } else { return EXPP_ReturnPyObjError( PyExc_TypeError, "expected object or string and optional integer arguments" ); } if( cage != 0 && cage != 1 ) return EXPP_ReturnPyObjError( PyExc_ValueError, "cage value must be 0 or 1" ); /* perform the mesh extraction based on type */ switch (ob->type) { case OB_FONT: case OB_CURVE: case OB_SURF: /* copies object and modifiers (but not the data) */ tmpobj= copy_object( ob ); tmpcu = (Curve *)tmpobj->data; tmpcu->id.us--; /* if getting the original caged mesh, delete object modifiers */ if( cage ) object_free_modifiers(tmpobj); /* copies the data */ tmpobj->data = copy_curve( (Curve *) ob->data ); #if 0 /* copy_curve() sets disp.first null, so currently not need */ { Curve *cu; cu = (Curve *)tmpobj->data; if( cu->disp.first ) MEM_freeN( cu->disp.first ); cu->disp.first = NULL; } #endif /* get updated display list, and convert to a mesh */ makeDispListCurveTypes( tmpobj, 0 ); nurbs_to_mesh( tmpobj ); /* nurbs_to_mesh changes the type tp a mesh, check it worked */ if (tmpobj->type != OB_MESH) { free_libblock_us( &G.main->object, tmpobj ); return EXPP_ReturnPyObjError( PyExc_RuntimeError, "cant convert curve to mesh. Does the curve have any segments?" ); } tmpmesh = tmpobj->data; free_libblock_us( &G.main->object, tmpobj ); break; case OB_MBALL: /* metaballs don't have modifiers, so just convert to mesh */ ob = find_basis_mball( ob ); tmpmesh = add_mesh("Mesh"); mball_to_mesh( &ob->disp, tmpmesh ); break; case OB_MESH: /* copies object and modifiers (but not the data) */ if (cage) { /* copies the data */ tmpmesh = copy_mesh( ob->data ); /* if not getting the original caged mesh, get final derived mesh */ } else { /* Make a dummy mesh, saves copying */ /* Write the display mesh into the dummy mesh */ if (render) dm = mesh_create_derived_render( ob, CD_MASK_MESH ); else dm = mesh_create_derived_view( ob, CD_MASK_MESH ); tmpmesh = add_mesh( "Mesh" ); DM_to_mesh( dm, tmpmesh ); dm->release( dm ); } break; default: return EXPP_ReturnPyObjError( PyExc_AttributeError, "Object does not have geometry data" ); } /* free mesh data in the original */ free_mesh( self->mesh ); /* save a copy of our ID, dup the temporary mesh, restore the ID */ tmpid = self->mesh->id; memcpy( self->mesh, tmpmesh, sizeof( Mesh ) ); self->mesh->id = tmpid; /* if mesh has keys, make sure they point back to this mesh */ if( self->mesh->key ) self->mesh->key->from = (ID *)self->mesh; /* Copy materials to new object */ switch (ob->type) { case OB_SURF: self->mesh->totcol = tmpcu->totcol; /* free old material list (if it exists) and adjust user counts */ if( tmpcu->mat ) { for( i = tmpcu->totcol; i-- > 0; ) { /* are we an object material or data based? */ if (ob->colbits & 1<mesh->mat[i] = ob->mat[i]; ob->mat[i]->id.us++; tmpmesh->mat[i]->id.us--; } else { self->mesh->mat[i] = tmpcu->mat[i]; if (self->mesh->mat[i]) { tmpmesh->mat[i]->id.us++; } } } } break; #if 0 /* Crashes when assigning the new material, not sure why */ case OB_MBALL: tmpmb = (MetaBall *)ob->data; self->mesh->totcol = tmpmb->totcol; /* free old material list (if it exists) and adjust user counts */ if( tmpmb->mat ) { for( i = tmpmb->totcol; i-- > 0; ) { self->mesh->mat[i] = tmpmb->mat[i]; /* CRASH HERE ??? */ if (self->mesh->mat[i]) { tmpmb->mat[i]->id.us++; } } } break; #endif case OB_MESH: if (!cage) { Mesh *origmesh= ob->data; self->mesh->flag= origmesh->flag; self->mesh->mat = MEM_dupallocN(origmesh->mat); self->mesh->totcol = origmesh->totcol; self->mesh->smoothresh= origmesh->smoothresh; if( origmesh->mat ) { for( i = origmesh->totcol; i-- > 0; ) { /* are we an object material or data based? */ if (ob->colbits & 1<mesh->mat[i] = ob->mat[i]; if (ob->mat[i]) ob->mat[i]->id.us++; if (origmesh->mat[i]) origmesh->mat[i]->id.us--; } else { self->mesh->mat[i] = origmesh->mat[i]; if (origmesh->mat[i]) origmesh->mat[i]->id.us++; } } } } break; } /* end copy materials */ /* remove the temporary mesh */ BLI_remlink( &G.main->mesh, tmpmesh ); MEM_freeN( tmpmesh ); /* make sure materials get updated in objects */ test_object_materials( ( ID * ) self->mesh ); mesh_update( self->mesh ); Py_RETURN_NONE; } /* * apply a transform to the mesh's vertices * * WARNING: unlike NMesh, this method ALWAYS changes the original mesh */ static PyObject *Mesh_transform( BPy_Mesh *self, PyObject *args, PyObject *kwd ) { Mesh *mesh = self->mesh; MVert *mvert; /*PyObject *pymat = NULL;*/ MatrixObject *bpymat=NULL; int i, recalc_normals = 0, selected_only = 0; static char *kwlist[] = {"matrix", "recalc_normals", "selected_only", NULL}; if( !PyArg_ParseTupleAndKeywords(args, kwd, "|O!ii", kwlist, &matrix_Type, &bpymat, &recalc_normals, &selected_only) ) { return EXPP_ReturnPyObjError( PyExc_TypeError, "matrix must be a 4x4 transformation matrix\n" "for example as returned by object.matrixWorld\n" "and optionaly keyword bools, recalc_normals and selected_only\n"); } if (!bpymat) return EXPP_ReturnPyObjError( PyExc_TypeError, "the first argument must be a matrix or\n" "matrix passed as a keyword argument\n"); /*bpymat = ( MatrixObject * ) pymat;*/ if( bpymat->colSize != 4 || bpymat->rowSize != 4 ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "matrix must be a 4x4 transformation matrix\n" "for example as returned by object.getMatrix()" ); /* loop through all the verts and transform by the supplied matrix */ mvert = mesh->mvert; if (selected_only) { for( i = 0; i < mesh->totvert; i++, mvert++ ) { if (mvert->flag & SELECT) { Mat4MulVecfl( (float(*)[4])*bpymat->matrix, mvert->co ); } } } else { for( i = 0; i < mesh->totvert; i++, mvert++ ) { Mat4MulVecfl( (float(*)[4])*bpymat->matrix, mvert->co ); } } if( recalc_normals ) { /* loop through all the verts and transform normals by the inverse * of the transpose of the supplied matrix */ float invmat[4][4], vec[3], nx, ny, nz; /* * we only need to invert a 3x3 submatrix, because the 4th component of * affine vectors is 0, but Mat4Invert reports non invertible matrices */ if (!Mat4Invert((float(*)[4])*invmat, (float(*)[4])*bpymat->matrix)) return EXPP_ReturnPyObjError (PyExc_AttributeError, "given matrix is not invertible"); /* * since normal is stored as shorts, convert to float */ mvert = mesh->mvert; for( i = 0; i < mesh->totvert; i++, mvert++ ) { nx= vec[0] = (float)(mvert->no[0] / 32767.0); ny= vec[1] = (float)(mvert->no[1] / 32767.0); nz= vec[2] = (float)(mvert->no[2] / 32767.0); vec[0] = nx*invmat[0][0] + ny*invmat[0][1] + nz*invmat[0][2]; vec[1] = nx*invmat[1][0] + ny*invmat[1][1] + nz*invmat[1][2]; vec[2] = nx*invmat[2][0] + ny*invmat[2][1] + nz*invmat[2][2]; Normalize( vec ); mvert->no[0] = (short)(vec[0] * 32767.0); mvert->no[1] = (short)(vec[1] * 32767.0); mvert->no[2] = (short)(vec[2] * 32767.0); } } Py_RETURN_NONE; } static PyObject *Mesh_addVertGroup( PyObject * self, PyObject * value ) { char *groupStr = PyString_AsString(value); struct Object *object; if( !groupStr ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected string argument" ); object = ( ( BPy_Mesh * ) self )->object; if( object == NULL ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "mesh not linked to an object" ); /* add_defgroup_name clamps the name to 32, make sure that dosnt change */ add_defgroup_name( object, groupStr ); EXPP_allqueue( REDRAWBUTSALL, 1 ); Py_RETURN_NONE; } static PyObject *Mesh_removeVertGroup( PyObject * self, PyObject * value ) { char *groupStr = PyString_AsString(value); struct Object *object; int nIndex; bDeformGroup *pGroup; if( G.obedit ) return EXPP_ReturnPyObjError(PyExc_RuntimeError, "can't use removeVertGroup() while in edit mode" ); if( !groupStr ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected string argument" ); if( ( ( BPy_Mesh * ) self )->object == NULL ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "mesh must be linked to an object first..." ); object = ( ( BPy_Mesh * ) self )->object; pGroup = get_named_vertexgroup( object, groupStr ); if( pGroup == NULL ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "group does not exist!" ); nIndex = get_defgroup_num( object, pGroup ); if( nIndex == -1 ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "no deform groups assigned to mesh" ); nIndex++; object->actdef = (unsigned short)nIndex; del_defgroup_in_object_mode( object ); EXPP_allqueue( REDRAWBUTSALL, 1 ); Py_RETURN_NONE; } extern void add_vert_defnr( Object * ob, int def_nr, int vertnum, float weight, int assignmode ); extern void remove_vert_def_nr (Object *ob, int def_nr, int vertnum); static PyObject *Mesh_assignVertsToGroup( BPy_Mesh * self, PyObject * args ) { char *groupStr; int nIndex; bDeformGroup *pGroup; PyObject *listObject; int tempInt; int x; int assignmode = WEIGHT_REPLACE; float weight = 1.0; Object *object = self->object; Mesh *mesh = self->mesh; if( !object ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "mesh must be linked to an object first" ); if( ((Mesh *)object->data) != mesh ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "object no longer linked to this mesh" ); if( !PyArg_ParseTuple ( args, "sO!fi", &groupStr, &PyList_Type, &listObject, &weight, &assignmode) ) { return EXPP_ReturnPyObjError( PyExc_TypeError, "expected string, list, float, int arguments" ); } pGroup = get_named_vertexgroup( object, groupStr ); if( pGroup == NULL ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "group does not exist!" ); nIndex = get_defgroup_num( object, pGroup ); if( nIndex == -1 ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "no deform groups assigned to mesh" ); if( assignmode != WEIGHT_REPLACE && assignmode != WEIGHT_ADD && assignmode != WEIGHT_SUBTRACT ) return EXPP_ReturnPyObjError( PyExc_ValueError, "bad assignment mode" ); /* makes a set of dVerts corresponding to the mVerts */ if( !mesh->dvert ) create_dverts( &mesh->id ); /* loop list adding verts to group */ for( x = 0; x < PyList_Size( listObject ); x++ ) { if( !PyArg_Parse ( PyList_GetItem( listObject, x ), "i", &tempInt ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "python list integer not parseable" ); if( tempInt < 0 || tempInt >= mesh->totvert ) return EXPP_ReturnPyObjError( PyExc_ValueError, "bad vertex index in list" ); add_vert_defnr( object, nIndex, tempInt, weight, assignmode ); } Py_RETURN_NONE; } static PyObject *Mesh_removeVertsFromGroup( BPy_Mesh * self, PyObject * args ) { /* not passing a list will remove all verts from group */ char *groupStr; int nIndex; Object *object; Mesh *mesh; bDeformGroup *pGroup; PyObject *listObject = NULL; int tempInt; int i; object = self->object; mesh = self->mesh; if( !object ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "mesh must be linked to an object first" ); if( ((Mesh *)object->data) != mesh ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "object no longer linked to this mesh" ); if( !PyArg_ParseTuple ( args, "s|O!", &groupStr, &PyList_Type, &listObject ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected string and optional list argument" ); if( !mesh->dvert ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "this mesh contains no deform vertices" ); pGroup = get_named_vertexgroup( object, groupStr ); if( pGroup == NULL ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "group does not exist!" ); nIndex = get_defgroup_num( object, pGroup ); if( nIndex == -1 ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "no deform groups assigned to mesh" ); /* get out of edit mode */ if( G.obedit ) { load_editMesh(); free_editMesh(G.editMesh); G.obedit = NULL; } if( !listObject ) /* no list given */ for( i = 0; i < mesh->totvert; i++ ) remove_vert_def_nr( object, nIndex, i ); else /* loop list removing verts to group */ for( i = 0; i < PyList_Size( listObject ); i++ ) { if( !PyArg_Parse( PyList_GetItem( listObject, i ), "i", &tempInt ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "python list integer not parseable" ); if( tempInt < 0 || tempInt >= mesh->totvert ) return EXPP_ReturnPyObjError( PyExc_ValueError, "bad vertex index in list" ); remove_vert_def_nr( object, nIndex, tempInt ); } Py_RETURN_NONE; } static PyObject *Mesh_getVertsFromGroup( BPy_Mesh* self, PyObject * args ) { /* * not passing a list will return all verts from group * passing indecies not part of the group will not return data in pyList * can be used as a index/group check for a vertex */ char *groupStr; int nIndex; bDeformGroup *pGroup; MDeformVert *dvert; int i, k, count; PyObject *vertexList; Object *object; Mesh *mesh; int num = 0; int weightRet = 0; PyObject *listObject = NULL; object = self->object; mesh = self->mesh; if( !object ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "mesh must be linked to an object first" ); if( ((Mesh *)object->data) != mesh ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "object no longer linked to this mesh" ); if( !PyArg_ParseTuple( args, "s|iO!", &groupStr, &weightRet, &PyList_Type, &listObject ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected string and optional int and list arguments" ); if( weightRet < 0 || weightRet > 1 ) return EXPP_ReturnPyObjError( PyExc_ValueError, "return weights flag must be 0 or 1" ); if( !mesh->dvert ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "this mesh contains no deform vertices" ); pGroup = get_named_vertexgroup( object, groupStr ); if( !pGroup ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "group does not exist!" ); nIndex = get_defgroup_num( object, pGroup ); if( nIndex == -1 ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "no deform groups assigned to mesh" ); count = 0; if( !listObject ) { /* do entire group */ vertexList = PyList_New( mesh->totvert ); if( !vertexList ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "getVertsFromGroup: can't create pylist!" ); dvert = mesh->dvert; for( num = 0; num < mesh->totvert; num++, ++dvert ) { for( i = 0; i < dvert->totweight; i++ ) { if( dvert->dw[i].def_nr == nIndex ) { PyObject *attr; if( weightRet ) attr = Py_BuildValue( "(i,f)", num, dvert->dw[i].weight ); else attr = PyInt_FromLong ( num ); PyList_SetItem( vertexList, count, attr ); count++; } } } if (count < mesh->totvert) PyList_SetSlice(vertexList, count, mesh->totvert, NULL); } else { /* do individual vertices */ int listObjectLen = PyList_Size( listObject ); vertexList = PyList_New( listObjectLen ); for( i = 0; i < listObjectLen; i++ ) { PyObject *attr = NULL; num = PyInt_AsLong( PyList_GetItem( listObject, i ) ); if (num == -1) {/* -1 is an error AND an invalid range, we dont care which */ Py_DECREF(vertexList); return EXPP_ReturnPyObjError( PyExc_TypeError, "python list integer not parseable" ); } if( num < 0 || num >= mesh->totvert ) { Py_DECREF(vertexList); return EXPP_ReturnPyObjError( PyExc_ValueError, "bad vertex index in list" ); } dvert = mesh->dvert + num; for( k = 0; k < dvert->totweight; k++ ) { if( dvert->dw[k].def_nr == nIndex ) { if( weightRet ) attr = Py_BuildValue( "(i,f)", num, dvert->dw[k].weight ); else attr = PyInt_FromLong ( num ); PyList_SetItem( vertexList, count, attr ); count++; } } } if (count < listObjectLen) PyList_SetSlice(vertexList, count, listObjectLen, NULL); } return vertexList; } static PyObject *Mesh_renameVertGroup( BPy_Mesh * self, PyObject * args ) { char *oldGr = NULL; char *newGr = NULL; bDeformGroup *defGroup; Object *object; Mesh *mesh; object = self->object; mesh = self->mesh; if( !object ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "mesh must be linked to an object first" ); if( ((Mesh *)object->data) != mesh ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "object no longer linked to this mesh" ); if( !PyArg_ParseTuple( args, "ss", &oldGr, &newGr ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected two string arguments" ); defGroup = get_named_vertexgroup( object, oldGr ); if( !defGroup ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "couldn't find the vertex group" ); PyOS_snprintf( defGroup->name, 32, newGr ); unique_vertexgroup_name( defGroup, object ); Py_RETURN_NONE; } static PyObject *Mesh_getVertGroupNames( BPy_Mesh * self ) { bDeformGroup *defGroup; PyObject *list; Object *obj = self->object; Mesh *mesh = self->mesh; int count; if( !obj ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "mesh must be linked to an object first" ); if( ((Mesh *)obj->data) != mesh ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "object no longer linked to this mesh" ); if( !obj ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This mesh must be linked to an object" ); count = 0; for( defGroup = obj->defbase.first; defGroup; defGroup = defGroup->next ) ++count; list = PyList_New( count ); count = 0; for( defGroup = obj->defbase.first; defGroup; defGroup = defGroup->next ) PyList_SET_ITEM( list, count++, PyString_FromString( defGroup->name ) ); return list; } static PyObject *Mesh_getVertexInfluences( BPy_Mesh * self, PyObject * args ) { int index; PyObject *influence_list = NULL; Object *object = self->object; Mesh *me = self->mesh; /* Get a reference to the mesh object wrapped in here. */ if( !object ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This mesh must be linked to an object" ); /* Parse the parameters: only on integer (vertex index) */ if( !PyArg_ParseTuple( args, "i", &index ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected int argument (index of the vertex)" ); /* check for valid index */ if( index < 0 || index >= me->totvert ) return EXPP_ReturnPyObjError( PyExc_IndexError, "vertex index out of range" ); influence_list = PyList_New( 0 ); /* Proceed only if we have vertex deformation information */ if( me->dvert ) { int i; MDeformWeight *sweight = NULL; /* Number of bones influencing the vertex */ int totinfluences = me->dvert[index].totweight; /* Get the reference of the first weight structure */ sweight = me->dvert[index].dw; /* Build the list only with weights and names of the influent bones */ for( i = 0; i < totinfluences; i++, sweight++ ) { bDeformGroup *defgroup = BLI_findlink( &object->defbase, sweight->def_nr ); if( defgroup ) PyList_Append( influence_list, Py_BuildValue( "[sf]", defgroup->name, sweight->weight ) ); } } return influence_list; } static PyObject *Mesh_removeAllKeys( BPy_Mesh * self ) { Mesh *mesh = self->mesh; if( !mesh || !mesh->key ) Py_RETURN_FALSE; mesh->key->id.us--; mesh->key = NULL; Py_RETURN_TRUE; } static PyObject *Mesh_insertKey( BPy_Mesh * self, PyObject * args ) { Mesh *mesh = self->mesh; int fra = -1, oldfra = -1; char *type = NULL; short typenum; if (mesh->mr) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "Shape Keys cannot be added to meshes with multires" ); if( !PyArg_ParseTuple( args, "|is", &fra, &type ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected nothing or an int and optionally a string as arguments" ); if( !type || !strcmp( type, "relative" ) ) typenum = 1; else if( !strcmp( type, "absolute" ) ) typenum = 2; else return EXPP_ReturnPyObjError( PyExc_AttributeError, "if given, type should be 'relative' or 'absolute'" ); if( fra > 0 ) { fra = EXPP_ClampInt( fra, 1, MAXFRAME ); oldfra = G.scene->r.cfra; G.scene->r.cfra = fra; } insert_meshkey( mesh, typenum ); allspace(REMAKEIPO, 0); if( fra > 0 ) G.scene->r.cfra = oldfra; Py_RETURN_NONE; } /* Custom Data Layers */ static PyObject * Mesh_addCustomLayer_internal(Mesh *me, PyObject * args, int type) { char *name = NULL; CustomData *data = &me->fdata; if( !PyArg_ParseTuple( args, "|s", &name ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a string or nothing" ); if (strlen(name)>31) return EXPP_ReturnPyObjError( PyExc_ValueError, "error, maximum name length is 31" ); if (name) CustomData_add_layer_named(data, type, CD_DEFAULT, NULL, me->totface, name); else CustomData_add_layer(data, type, CD_DEFAULT, NULL, me->totface); mesh_update_customdata_pointers(me); Py_RETURN_NONE; } static PyObject *Mesh_addUVLayer( BPy_Mesh * self, PyObject * args ) { return Mesh_addCustomLayer_internal(self->mesh, args, CD_MTFACE); } static PyObject *Mesh_addColorLayer( BPy_Mesh * self, PyObject * args ) { return Mesh_addCustomLayer_internal(self->mesh, args, CD_MCOL); } static PyObject *Mesh_removeLayer_internal( BPy_Mesh * self, PyObject * value, int type ) { Mesh *me = self->mesh; CustomData *data = &me->fdata; char *name = PyString_AsString(value); int i; if( !name ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected string argument" ); if (strlen(name)>31) return EXPP_ReturnPyObjError( PyExc_ValueError, "error, maximum name length is 31" ); i = CustomData_get_named_layer_index(data, type, name); if (i==-1) return EXPP_ReturnPyObjError(PyExc_ValueError, "No matching layers to remove" ); CustomData_free_layer(data, type, me->totface, i); mesh_update_customdata_pointers(me); /* No more Color or UV layers left ? switch modes if this is the active object */ if (!CustomData_has_layer(data, type)) { if (me == get_mesh(OBACT)) { if(type == CD_MCOL && (G.f & G_VERTEXPAINT)) G.f &= ~G_VERTEXPAINT; /* get out of vertexpaint mode */ if(type == CD_MTFACE && (G.f & G_FACESELECT)) G.f |= ~G_FACESELECT; /* get out of faceselect mode */ } } Py_RETURN_NONE; } static PyObject *Mesh_removeUVLayer( BPy_Mesh * self, PyObject * value ) { return Mesh_removeLayer_internal(self, value, CD_MTFACE); } static PyObject *Mesh_removeColorLayer( BPy_Mesh * self, PyObject * value ) { return Mesh_removeLayer_internal(self, value, CD_MCOL); } static PyObject *Mesh_renameLayer_internal( BPy_Mesh * self, PyObject * args, int type ) { CustomData *data; CustomDataLayer *layer; Mesh *mesh = self->mesh; int i; char *name_from, *name_to; data = &mesh->fdata; if( !PyArg_ParseTuple( args, "ss", &name_from, &name_to ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected 2 strings" ); if (strlen(name_from)>31 || strlen(name_to)>31) return EXPP_ReturnPyObjError( PyExc_ValueError, "error, maximum name length is 31" ); i = CustomData_get_named_layer_index(data, type, name_from); if (i==-1) return EXPP_ReturnPyObjError( PyExc_ValueError, "layer name was not found" ); layer = &data->layers[i]; strcpy(layer->name, name_to); /* we alredy know the string sizes are under 32 */ CustomData_set_layer_unique_name(data, i); Py_RETURN_NONE; } static PyObject *Mesh_renameUVLayer( BPy_Mesh * self, PyObject * args ) { return Mesh_renameLayer_internal( self, args, CD_MTFACE ); } static PyObject *Mesh_renameColorLayer( BPy_Mesh * self, PyObject * args ) { return Mesh_renameLayer_internal( self, args, CD_MCOL ); } static PyObject *Mesh_getLayerNames_internal( BPy_Mesh * self, int type ) { CustomData *data; CustomDataLayer *layer; PyObject *str, *list = PyList_New( 0 ); Mesh *mesh = self->mesh; int i; data = &mesh->fdata; /* see if there is a duplicate */ for(i=0; itotlayer; i++) { layer = &data->layers[i]; if(layer->type == type) { str = PyString_FromString(layer->name); PyList_Append( list, str ); Py_DECREF(str); } } return list; } static PyObject *Mesh_getUVLayerNames( BPy_Mesh * self ) { return Mesh_getLayerNames_internal(self, CD_MTFACE); } static PyObject *Mesh_getColorLayerNames( BPy_Mesh * self ) { return Mesh_getLayerNames_internal(self, CD_MCOL); } /* used by activeUVLayer and activeColorLayer attrs */ static PyObject *Mesh_getActiveLayer( BPy_Mesh * self, void *type ) { CustomData *data = &self->mesh->fdata; int layer_type = GET_INT_FROM_POINTER(type); int i; if (layer_type < 0) { /* hack, if negative, its the renderlayer.*/ layer_type = -layer_type; i = CustomData_get_render_layer_index(data, layer_type); } else { i = CustomData_get_active_layer_index(data, layer_type); } if (i == -1) /* so -1 is for no active layer 0+ for an active layer */ Py_RETURN_NONE; else { return PyString_FromString( data->layers[i].name); } } static int Mesh_setActiveLayer( BPy_Mesh * self, PyObject * value, void *type ) { CustomData *data = &self->mesh->fdata; char *name; int i,ok,n,layer_type = GET_INT_FROM_POINTER(type), render=0; if( !PyString_Check( value ) ) return EXPP_ReturnIntError( PyExc_ValueError, "expected a string argument" ); if (layer_type<0) { layer_type = -layer_type; render = 1; } name = PyString_AsString( value ); ok = 0; n = 0; for(i=0; i < data->totlayer; ++i) { if(data->layers[i].type == layer_type) { if (strcmp(data->layers[i].name, name)==0) { ok = 1; break; } n++; } } if (!ok) return EXPP_ReturnIntError( PyExc_ValueError, "layer name does not exist" ); if (render) { CustomData_set_layer_render(data, layer_type, n); } else { CustomData_set_layer_active(data, layer_type, n); mesh_update_customdata_pointers(self->mesh); } return 0; } /* multires */ static PyObject *Mesh_getMultiresLevelCount( BPy_Mesh * self ) { int i; if (!self->mesh->mr) i=0; else i= self->mesh->mr->level_count; return PyInt_FromLong(i); } static PyObject *Mesh_getMultires( BPy_Mesh * self, void *type ) { int i=0; if (self->mesh->mr) { switch (GET_INT_FROM_POINTER(type)) { case MESH_MULTIRES_LEVEL: i = self->mesh->mr->newlvl; break; case MESH_MULTIRES_EDGE: i = self->mesh->mr->edgelvl; break; case MESH_MULTIRES_PIN: i = self->mesh->mr->pinlvl; break; case MESH_MULTIRES_RENDER: i = self->mesh->mr->renderlvl; break; } } return PyInt_FromLong(i); } static int Mesh_setMultires( BPy_Mesh * self, PyObject *value, void *type ) { int i; if( !PyInt_Check( value ) ) return EXPP_ReturnIntError( PyExc_TypeError, "expected integer argument" ); if (!self->object) return EXPP_ReturnIntError( PyExc_RuntimeError, "This mesh must be linked to an object" ); if (!self->mesh->mr) return EXPP_ReturnIntError( PyExc_RuntimeError, "the mesh has no multires data" ); if (!self->mesh->mr->level_count) return EXPP_ReturnIntError( PyExc_RuntimeError, "multires data has no levels added" ); i = PyInt_AsLong(value); if (i<1||i>self->mesh->mr->level_count) return EXPP_ReturnIntError( PyExc_TypeError, "value out of range" ); switch (GET_INT_FROM_POINTER(type)) { case MESH_MULTIRES_LEVEL: self->mesh->mr->newlvl = i; multires_set_level_cb(self->object, self->mesh); break; case MESH_MULTIRES_EDGE: self->mesh->mr->edgelvl = i; multires_edge_level_update(self->object, self->mesh); break; case MESH_MULTIRES_PIN: self->mesh->mr->pinlvl = i; break; case MESH_MULTIRES_RENDER: self->mesh->mr->renderlvl = i; break; } return 0; } static PyObject *Mesh_addMultiresLevel( BPy_Mesh * self, PyObject * args ) { char typenum; int i, levels = 1; char *type = NULL; if( G.obedit ) return EXPP_ReturnPyObjError(PyExc_RuntimeError, "can't add multires level while in edit mode" ); if( !PyArg_ParseTuple( args, "|is", &levels, &type ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected nothing or an int and optionally a string as arguments" ); if( !type || !strcmp( type, "catmull-clark" ) ) typenum = 0; else if( !strcmp( type, "simple" ) ) typenum = 1; else return EXPP_ReturnPyObjError( PyExc_AttributeError, "if given, type should be 'catmull-clark' or 'simple'" ); if (!self->mesh->mr) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "the mesh has no multires data" ); for( i = 0; i < levels; i++ ) { multires_add_level(self->object, self->mesh, typenum); }; multires_update_levels(self->mesh, 0); multires_level_to_editmesh(self->object, self->mesh, 0); multires_finish_mesh_update(self->object); Py_RETURN_NONE; } /* end multires */ static PyObject *Mesh_Tools( BPy_Mesh * self, int type, void **args ) { Base *base; int result; Object *object = NULL; PyObject *attr = NULL; /* if already in edit mode, exit */ if( G.obedit ) return EXPP_ReturnPyObjError(PyExc_RuntimeError, "can't use mesh tools while in edit mode" ); for( base = FIRSTBASE; base; base = base->next ) { if( base->object->type == OB_MESH && base->object->data == self->mesh ) { object = base->object; break; } } if( !object ) return EXPP_ReturnPyObjError(PyExc_RuntimeError, "can't find an object for the mesh" ); if( object->type != OB_MESH ) return EXPP_ReturnPyObjError( PyExc_ValueError, "Object specified is not a mesh." ); /* make mesh's object active, enter mesh edit mode */ G.obedit = object; make_editMesh(); /* apply operation, then exit edit mode */ switch( type ) { case MESH_TOOL_TOSPHERE: vertices_to_sphere(); break; case MESH_TOOL_VERTEXSMOOTH: vertexsmooth(); break; case MESH_TOOL_FLIPNORM: /* would be simple to rewrite this to not use edit mesh */ /* see flipface() */ flip_editnormals(); break; case MESH_TOOL_SUBDIV: esubdivideflag( 1, 0.0, *((int *)args[0]), 1, 0 ); break; case MESH_TOOL_REMDOUB: result = removedoublesflag( 1, 0, *((float *)args[0]) ); attr = PyInt_FromLong( result ); if( !attr ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); break; case MESH_TOOL_FILL: fill_mesh(); break; case MESH_TOOL_RECALCNORM: righthandfaces( *((int *)args[0]) ); break; case MESH_TOOL_TRI2QUAD: join_triangles(); break; case MESH_TOOL_QUAD2TRI: convert_to_triface( *((int *)args[0]) ); break; } /* exit edit mode, free edit mesh */ load_editMesh(); free_editMesh(G.editMesh); if(G.f & G_FACESELECT) EXPP_allqueue( REDRAWIMAGE, 0 ); if(G.f & G_WEIGHTPAINT) mesh_octree_table(G.obedit, NULL, 'e'); G.obedit = NULL; DAG_object_flush_update(G.scene, object, OB_RECALC_DATA); if( attr ) return attr; Py_RETURN_NONE; } /* * "Subdivide" function */ static PyObject *Mesh_subdivide( BPy_Mesh * self, PyObject * args ) { int beauty = 0; void *params = &beauty; if( !PyArg_ParseTuple( args, "|i", &beauty ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected nothing or an int argument" ); return Mesh_Tools( self, MESH_TOOL_SUBDIV, ¶ms ); } /* * "Smooth" function */ static PyObject *Mesh_smooth( BPy_Mesh * self ) { return Mesh_Tools( self, MESH_TOOL_VERTEXSMOOTH, NULL ); } /* * "Remove doubles" function */ static PyObject *Mesh_removeDoubles( BPy_Mesh * self, PyObject *args ) { float limit; void *params = &limit; if( !PyArg_ParseTuple( args, "f", &limit ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected float argument" ); limit = EXPP_ClampFloat( limit, 0.0f, 1.0f ); return Mesh_Tools( self, MESH_TOOL_REMDOUB, ¶ms ); } /* * "recalc normals" function */ static PyObject *Mesh_recalcNormals( BPy_Mesh * self, PyObject *args ) { int direction = 0; void *params = &direction; if( !PyArg_ParseTuple( args, "|i", &direction ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected nothing or an int in range [0,1]" ); if( direction < 0 || direction > 1 ) return EXPP_ReturnPyObjError( PyExc_ValueError, "expected int in range [0,1]" ); /* righthandfaces(1) = outward, righthandfaces(2) = inward */ ++direction; return Mesh_Tools( self, MESH_TOOL_RECALCNORM, ¶ms ); } /* * "Quads to Triangles" function */ static PyObject *Mesh_quad2tri( BPy_Mesh * self, PyObject *args ) { int kind = 0; void *params = &kind; if( !PyArg_ParseTuple( args, "|i", &kind ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected nothing or an int in range [0,1]" ); if( kind < 0 || kind > 1 ) return EXPP_ReturnPyObjError( PyExc_ValueError, "expected int in range [0,1]" ); return Mesh_Tools( self, MESH_TOOL_QUAD2TRI, ¶ms ); } /* * "Triangles to Quads" function */ static PyObject *Mesh_tri2quad( BPy_Mesh * self ) { return Mesh_Tools( self, MESH_TOOL_TRI2QUAD, NULL ); } /* * "Flip normals" function */ static PyObject *Mesh_flipNormals( BPy_Mesh * self ) { return Mesh_Tools( self, MESH_TOOL_FLIPNORM, NULL ); } /* * "To sphere" function */ static PyObject *Mesh_toSphere( BPy_Mesh * self ) { return Mesh_Tools( self, MESH_TOOL_TOSPHERE, NULL ); } /* * "Fill" (scan fill) function */ static PyObject *Mesh_fill( BPy_Mesh * self ) { return Mesh_Tools( self, MESH_TOOL_FILL, NULL ); } /* * "pointInside" function */ /* Warning - this is ordered - need to test both orders to be sure */ #define SIDE_OF_LINE(pa,pb,pp) ((pa[0]-pp[0])*(pb[1]-pp[1]))-((pb[0]-pp[0])*(pa[1]-pp[1])) #define POINT_IN_TRI(p0,p1,p2,p3) ((SIDE_OF_LINE(p1,p2,p0)>=0) && (SIDE_OF_LINE(p2,p3,p0)>=0) && (SIDE_OF_LINE(p3,p1,p0)>=0)) static short pointInside_internal(float *vec, float *v1, float *v2, float *v3 ) { float z,w1,w2,w3,wtot; if (vec[2] > MAX3(v1[2], v2[2], v3[2])) return 0; /* need to test both orders */ if (!POINT_IN_TRI(vec, v1,v2,v3) && !POINT_IN_TRI(vec, v3,v2,v1)) return 0; w1= AreaF2Dfl(vec, v2, v3); w2= AreaF2Dfl(v1, vec, v3); w3= AreaF2Dfl(v1, v2, vec); wtot = w1+w2+w3; w1/=wtot; w2/=wtot; w3/=wtot; z =((v1[2] * w1) + (v2[2] * w2) + (v3[2] * w3)); /* only return true if the face is above vec*/ if (vec[2] < z ) return 1; return 0; } static PyObject *Mesh_pointInside( BPy_Mesh * self, PyObject * args, PyObject *kwd ) { Mesh *mesh = self->mesh; MFace *mf = mesh->mface; MVert *mvert = mesh->mvert; int i; int isect_count=0; int selected_only = 0; VectorObject *vec; static char *kwlist[] = {"point", "selected_only", NULL}; if( !PyArg_ParseTupleAndKeywords(args, kwd, "|O!i", kwlist, &vector_Type, &vec, &selected_only) ) { return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a vector and an optional bool argument"); } if(vec->size < 3) return EXPP_ReturnPyObjError(PyExc_AttributeError, "Mesh.pointInside(vec) expects a 3D vector object\n"); for( i = 0; i < mesh->totface; mf++, i++ ) { if (!selected_only || mf->flag & ME_FACE_SEL) { if (pointInside_internal(vec->vec, mvert[mf->v1].co, mvert[mf->v2].co, mvert[mf->v3].co)) { isect_count++; } else if (mf->v4 && pointInside_internal(vec->vec,mvert[mf->v1].co, mvert[mf->v3].co, mvert[mf->v4].co)) { isect_count++; } } } if (isect_count % 2) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /* This is a bit nasty, Blenders tangents are computed for rendering, and this isnt compatible with a normal Mesh * so we have to rewrite parts of it here, make sure these stay in sync */ static PyObject *Mesh_getTangents( BPy_Mesh * self ) { /* python stuff */ PyObject *py_tanlist; PyObject *py_tuple; PyObject *py_vector; #if 0 /* BI-TANGENT */ PyObject *py_bivector; PyObject *py_pair; float no[3]; #endif /* mesh vars */ Mesh *mesh = self->mesh; MTFace *tf = mesh->mtface; MFace *mf = mesh->mface; MVert *v1, *v2, *v3, *v4; int mf_vi[4]; /* See convertblender.c */ float *uv1, *uv2, *uv3, *uv4; float fno[3]; float tang[3]; float uv[4][2]; float *vtang; float (*orco)[3] = NULL; MemArena *arena= NULL; VertexTangent **vtangents= NULL; int i, j, len; if(!mesh->mtface) { if (!self->object) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "cannot get tangents when there are not UV's, or the mesh has no link to an object"); orco = (float(*)[3])get_mesh_orco_verts(self->object); if (!orco) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "cannot get orco's for this objects tangents"); } /* vertex normals */ arena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); BLI_memarena_use_calloc(arena); vtangents= MEM_callocN(sizeof(VertexTangent*)*mesh->totvert, "VertexTangent"); for( i = 0, tf = mesh->mtface, mf = mesh->mface; i < mesh->totface; mf++, tf++, i++ ) { v1 = &mesh->mvert[mf->v1]; v2 = &mesh->mvert[mf->v2]; v3 = &mesh->mvert[mf->v3]; if (mf->v4) { v4 = &mesh->mvert[mf->v4]; CalcNormFloat4( v1->co, v2->co, v3->co, v4->co, fno ); } else { v4 = NULL; CalcNormFloat( v1->co, v2->co, v3->co, fno ); } if(mesh->mtface) { uv1= tf->uv[0]; uv2= tf->uv[1]; uv3= tf->uv[2]; uv4= tf->uv[3]; } else { uv1= uv[0]; uv2= uv[1]; uv3= uv[2]; uv4= uv[3]; spheremap(orco[mf->v1][0], orco[mf->v1][1], orco[mf->v1][2], &uv[0][0], &uv[0][1]); spheremap(orco[mf->v2][0], orco[mf->v2][1], orco[mf->v2][2], &uv[1][0], &uv[1][1]); spheremap(orco[mf->v3][0], orco[mf->v3][1], orco[mf->v3][2], &uv[2][0], &uv[2][1]); if(v4) spheremap(orco[mf->v4][0], orco[mf->v4][1], orco[mf->v4][2], &uv[3][0], &uv[3][1]); } tangent_from_uv(uv1, uv2, uv3, v1->co, v2->co, v3->co, fno, tang); sum_or_add_vertex_tangent(arena, &vtangents[mf->v1], tang, uv1); sum_or_add_vertex_tangent(arena, &vtangents[mf->v2], tang, uv2); sum_or_add_vertex_tangent(arena, &vtangents[mf->v3], tang, uv3); if (mf->v4) { v4 = &mesh->mvert[mf->v4]; tangent_from_uv(uv1, uv3, uv4, v1->co, v3->co, v4->co, fno, tang); sum_or_add_vertex_tangent(arena, &vtangents[mf->v1], tang, uv1); sum_or_add_vertex_tangent(arena, &vtangents[mf->v3], tang, uv3); sum_or_add_vertex_tangent(arena, &vtangents[mf->v4], tang, uv4); } } py_tanlist = PyList_New(mesh->totface); for( i = 0, tf = mesh->mtface, mf = mesh->mface; i < mesh->totface; mf++, tf++, i++ ) { len = mf->v4 ? 4 : 3; if(mesh->mtface) { uv1= tf->uv[0]; uv2= tf->uv[1]; uv3= tf->uv[2]; uv4= tf->uv[3]; } else { uv1= uv[0]; uv2= uv[1]; uv3= uv[2]; uv4= uv[3]; spheremap(orco[mf->v1][0], orco[mf->v1][1], orco[mf->v1][2], &uv[0][0], &uv[0][1]); spheremap(orco[mf->v2][0], orco[mf->v2][1], orco[mf->v2][2], &uv[1][0], &uv[1][1]); spheremap(orco[mf->v3][0], orco[mf->v3][1], orco[mf->v3][2], &uv[2][0], &uv[2][1]); if(len==4) spheremap(orco[mf->v4][0], orco[mf->v4][1], orco[mf->v4][2], &uv[3][0], &uv[3][1]); } mf_vi[0] = mf->v1; mf_vi[1] = mf->v2; mf_vi[2] = mf->v3; mf_vi[3] = mf->v4; #if 0 /* BI-TANGENT */ /* now calculate the bitangent */ if (mf->flag & ME_SMOOTH) { no[0] = (float)(mesh->mvert[mf_vi[j]]->no[0] / 32767.0); no[1] = (float)(mesh->mvert[mf_vi[j]]->no[1] / 32767.0); no[2] = (float)(mesh->mvert[mf_vi[j]]->no[2] / 32767.0); } else { /* calc face normal */ if (len==4) CalcNormFloat4( mesh->mvert[0]->co, mesh->mvert[1]->co, mesh->mvert[2]->co, mesh->mvert[3]->co, no ); else CalcNormFloat4( mesh->mvert[0]->co, mesh->mvert[1]->co, mesh->mvert[2]->co, no ); } #endif py_tuple = PyTuple_New( len ); for (j=0; jmtface ? tf->uv[j] : uv[j]); /* mf_vi[j] == mf->v1, uv[j] == tf->uv[0] */ py_vector = newVectorObject( vtang, 3, Py_NEW ); Normalize(((VectorObject *)py_vector)->vec); #if 0 /* BI-TANGENT */ py_pair = PyTuple_New( 2 ); PyTuple_SetItem( py_pair, 0, py_vector ); PyTuple_SetItem( py_pair, 1, py_bivector ); /* qdn: tangent space */ /* copied from texture.c */ float B[3], tv[3]; Crossf(B, shi->vn, shi->nmaptang); /* bitangent */ /* transform norvec from tangent space to object surface in camera space */ tv[0] = texres.nor[0]*shi->nmaptang[0] + texres.nor[1]*B[0] + texres.nor[2]*shi->vn[0]; tv[1] = texres.nor[0]*shi->nmaptang[1] + texres.nor[1]*B[1] + texres.nor[2]*shi->vn[1]; tv[2] = texres.nor[0]*shi->nmaptang[2] + texres.nor[1]*B[2] + texres.nor[2]*shi->vn[2]; shi->vn[0]= facm*shi->vn[0] + fact*tv[0]; shi->vn[1]= facm*shi->vn[1] + fact*tv[1]; shi->vn[2]= facm*shi->vn[2] + fact*tv[2]; PyTuple_SetItem( py_tuple, j, py_pair ); #else PyTuple_SetItem( py_tuple, j, py_vector ); #endif } PyList_SetItem( py_tanlist, i, py_tuple ); } BLI_memarena_free(arena); if (orco) MEM_freeN( orco ); MEM_freeN( vtangents ); return py_tanlist; } /* * "__copy__" return a copy of the mesh */ static PyObject *Mesh_copy( BPy_Mesh * self ) { BPy_Mesh *obj; obj = (BPy_Mesh *)PyObject_NEW( BPy_Mesh, &Mesh_Type ); if( !obj ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyObject_New() failed" ); obj->mesh = copy_mesh( self->mesh ); obj->mesh->id.us= 0; obj->object = NULL; obj->new = 1; return (PyObject *)obj; } static struct PyMethodDef BPy_Mesh_methods[] = { {"calcNormals", (PyCFunction)Mesh_calcNormals, METH_NOARGS, "all recalculate vertex normals"}, {"vertexShade", (PyCFunction)Mesh_vertexShade, METH_VARARGS, "color vertices based on the current lighting setup"}, {"findEdges", (PyCFunction)Mesh_findEdges, METH_VARARGS, "find indices of an multiple edges in the mesh"}, {"getFromObject", (PyCFunction)Mesh_getFromObject, METH_VARARGS, "Get a mesh by name"}, {"update", (PyCFunction)Mesh_Update, METH_VARARGS | METH_KEYWORDS, "Update display lists after changes to mesh"}, {"transform", (PyCFunction)Mesh_transform, METH_VARARGS | METH_KEYWORDS, "Applies a transformation matrix to mesh's vertices"}, {"addVertGroup", (PyCFunction)Mesh_addVertGroup, METH_O, "Assign vertex group name to the object linked to the mesh"}, {"removeVertGroup", (PyCFunction)Mesh_removeVertGroup, METH_O, "Delete vertex group name from the object linked to the mesh"}, {"assignVertsToGroup", (PyCFunction)Mesh_assignVertsToGroup, METH_VARARGS, "Assigns vertices to a vertex group"}, {"removeVertsFromGroup", (PyCFunction)Mesh_removeVertsFromGroup, METH_VARARGS, "Removes vertices from a vertex group"}, {"getVertsFromGroup", (PyCFunction)Mesh_getVertsFromGroup, METH_VARARGS, "Get index and optional weight for vertices in vertex group"}, {"renameVertGroup", (PyCFunction)Mesh_renameVertGroup, METH_VARARGS, "Rename an existing vertex group"}, {"getVertGroupNames", (PyCFunction)Mesh_getVertGroupNames, METH_NOARGS, "Get names of vertex groups"}, {"getVertexInfluences", (PyCFunction)Mesh_getVertexInfluences, METH_VARARGS, "Get list of the influences of bones for a given mesh vertex"}, /* Shape Keys */ {"removeAllKeys", (PyCFunction)Mesh_removeAllKeys, METH_NOARGS, "Remove all the shape keys from a mesh"}, {"insertKey", (PyCFunction)Mesh_insertKey, METH_VARARGS, "(frame = None, type = 'relative') - inserts a Mesh key at the given frame"}, /* Mesh tools */ {"smooth", (PyCFunction)Mesh_smooth, METH_NOARGS, "Flattens angle of selected faces (experimental)"}, {"flipNormals", (PyCFunction)Mesh_flipNormals, METH_NOARGS, "Toggles the direction of selected face's normals (experimental)"}, {"toSphere", (PyCFunction)Mesh_toSphere, METH_NOARGS, "Moves selected vertices outward in a spherical shape (experimental)"}, {"fill", (PyCFunction)Mesh_fill, METH_NOARGS, "Scan fill a closed edge loop (experimental)"}, {"triangleToQuad", (PyCFunction)Mesh_tri2quad, METH_VARARGS, "Convert selected triangles to quads (experimental)"}, {"quadToTriangle", (PyCFunction)Mesh_quad2tri, METH_VARARGS, "Convert selected quads to triangles (experimental)"}, {"subdivide", (PyCFunction)Mesh_subdivide, METH_VARARGS, "Subdivide selected edges in a mesh (experimental)"}, {"remDoubles", (PyCFunction)Mesh_removeDoubles, METH_VARARGS, "Removes duplicates from selected vertices (experimental)"}, {"recalcNormals", (PyCFunction)Mesh_recalcNormals, METH_VARARGS, "Recalculates inside or outside normals (experimental)"}, {"pointInside", (PyCFunction)Mesh_pointInside, METH_VARARGS|METH_KEYWORDS, "Recalculates inside or outside normals (experimental)"}, {"getTangents", (PyCFunction)Mesh_getTangents, METH_VARARGS|METH_KEYWORDS, "Return a list of face tangents"}, /* mesh custom data layers */ {"addUVLayer", (PyCFunction)Mesh_addUVLayer, METH_VARARGS, "adds a UV layer to this mesh"}, {"addColorLayer", (PyCFunction)Mesh_addColorLayer, METH_VARARGS, "adds a color layer to this mesh "}, {"removeUVLayer", (PyCFunction)Mesh_removeUVLayer, METH_O, "removes a UV layer to this mesh"}, {"removeColorLayer", (PyCFunction)Mesh_removeColorLayer, METH_O, "removes a color layer to this mesh"}, {"getUVLayerNames", (PyCFunction)Mesh_getUVLayerNames, METH_NOARGS, "Get names of UV layers"}, {"getColorLayerNames", (PyCFunction)Mesh_getColorLayerNames, METH_NOARGS, "Get names of Color layers"}, {"renameUVLayer", (PyCFunction)Mesh_renameUVLayer, METH_VARARGS, "Rename a UV Layer"}, {"renameColorLayer", (PyCFunction)Mesh_renameColorLayer, METH_VARARGS, "Rename a Color Layer"}, /* mesh multires */ {"addMultiresLevel", (PyCFunction)Mesh_addMultiresLevel, METH_VARARGS, "(levels=1, type='catmull-clark') - adds multires levels of given type"}, /* python standard class functions */ {"__copy__", (PyCFunction)Mesh_copy, METH_NOARGS, "Return a copy of the mesh"}, {"copy", (PyCFunction)Mesh_copy, METH_NOARGS, "Return a copy of the mesh"}, {NULL, NULL, 0, NULL} }; /************************************************************************ * * Python BPy_Mesh attributes * ************************************************************************/ static PyObject *MVertSeq_CreatePyObject( Mesh * mesh ) { BPy_MVertSeq *obj = PyObject_NEW( BPy_MVertSeq, &MVertSeq_Type); obj->mesh = mesh; /* an iter of -1 means this seq has not been used as an iterator yet once it is, then any other calls on getIter will return a new copy of BPy_MVertSeq This means you can loop do nested loops with the same iterator without worrying about the iter variable being used twice and messing up the loops. */ obj->iter = -1; return (PyObject *)obj; } static PyObject *Mesh_getVerts( BPy_Mesh * self ) { return MVertSeq_CreatePyObject(self->mesh); } static int Mesh_setVerts( BPy_Mesh * self, PyObject * args ) { MVert *dst; MVert *src; int i; /* special case if None: delete the mesh */ if( args == NULL || args == Py_None ) { Mesh *me = self->mesh; free_mesh( me ); me->mvert = NULL; me->medge = NULL; me->mface = NULL; me->mtface = NULL; me->dvert = NULL; me->mcol = NULL; me->msticky = NULL; me->mat = NULL; me->bb = NULL; me->mselect = NULL; me->totvert = me->totedge = me->totface = me->totcol = 0; mesh_update( me ); return 0; } if( PyList_Check( args ) ) { if( EXPP_check_sequence_consistency( args, &MVert_Type ) != 1 && EXPP_check_sequence_consistency( args, &PVert_Type ) != 1 ) return EXPP_ReturnIntError( PyExc_TypeError, "expected a list of MVerts" ); if( PyList_Size( args ) != self->mesh->totvert ) return EXPP_ReturnIntError( PyExc_TypeError, "list must have the same number of vertices as the mesh" ); dst = self->mesh->mvert; for( i = 0; i < PyList_Size( args ); ++i ) { BPy_MVert *v = (BPy_MVert *)PyList_GET_ITEM( args, i ); if( BPy_MVert_Check( v ) ) src = &((Mesh *)v->data)->mvert[v->index]; else src = (MVert *)v->data; memcpy( dst, src, sizeof(MVert) ); ++dst; } } else if( args->ob_type == &MVertSeq_Type ) { Mesh *mesh = ( (BPy_MVertSeq *) args)->mesh; if( mesh->totvert != self->mesh->totvert ) return EXPP_ReturnIntError( PyExc_TypeError, "vertex sequences must have the same number of vertices" ); memcpy( self->mesh->mvert, mesh->mvert, mesh->totvert*sizeof(MVert) ); } else return EXPP_ReturnIntError( PyExc_TypeError, "expected a list or sequence of MVerts" ); return 0; } static PyObject *MEdgeSeq_CreatePyObject( Mesh *mesh ) { BPy_MEdgeSeq *obj = PyObject_NEW( BPy_MEdgeSeq, &MEdgeSeq_Type); obj->mesh = mesh; obj->iter = -1; /* iterator not yet used */ return (PyObject *)obj; } static PyObject *Mesh_getEdges( BPy_Mesh * self ) { return MEdgeSeq_CreatePyObject(self->mesh); } static PyObject *MFaceSeq_CreatePyObject( Mesh * mesh ) { BPy_MFaceSeq *obj= PyObject_NEW( BPy_MFaceSeq, &MFaceSeq_Type); obj->mesh = mesh; obj->iter = -1; /* iterator not yet used */ return (PyObject *)obj; } static PyObject *Mesh_getFaces( BPy_Mesh * self ) { return MFaceSeq_CreatePyObject( self->mesh ); } static PyObject *Mesh_getMaterials( BPy_Mesh *self ) { return EXPP_PyList_fromMaterialList( self->mesh->mat, self->mesh->totcol, 1 ); } static int Mesh_setMaterials( BPy_Mesh *self, PyObject * value ) { Material **matlist; int len; if( !PySequence_Check( value ) || !EXPP_check_sequence_consistency( value, &Material_Type ) ) return EXPP_ReturnIntError( PyExc_TypeError, "list should only contain materials or None)" ); len = PyList_Size( value ); if( len > 16 ) return EXPP_ReturnIntError( PyExc_TypeError, "list can't have more than 16 materials" ); /* free old material list (if it exists) and adjust user counts */ if( self->mesh->mat ) { Mesh *me = self->mesh; int i; for( i = me->totcol; i-- > 0; ) if( me->mat[i] ) me->mat[i]->id.us--; MEM_freeN( me->mat ); } /* build the new material list, increment user count, store it */ matlist = EXPP_newMaterialList_fromPyList( value ); EXPP_incr_mats_us( matlist, len ); self->mesh->mat = matlist; self->mesh->totcol = (short)len; /**@ This is another ugly fix due to the weird material handling of blender. * it makes sure that object material lists get updated (by their length) * according to their data material lists, otherwise blender crashes. * It just stupidly runs through all objects...BAD BAD BAD. */ test_object_materials( ( ID * ) self->mesh ); return 0; } static PyObject *Mesh_getMaxSmoothAngle( BPy_Mesh * self ) { return PyInt_FromLong( self->mesh->smoothresh ); } static int Mesh_setMaxSmoothAngle( BPy_Mesh *self, PyObject *value ) { return EXPP_setIValueClamped( value, &self->mesh->smoothresh, MESH_SMOOTHRESH_MIN, MESH_SMOOTHRESH_MAX, 'h' ); } static PyObject *Mesh_getSubDivLevels( BPy_Mesh * self ) { return Py_BuildValue( "(h,h)", self->mesh->subdiv, self->mesh->subdivr ); } static int Mesh_setSubDivLevels( BPy_Mesh *self, PyObject *value ) { int subdiv[2]; int i; PyObject *tmp; if( !PyTuple_Check( value ) || PyTuple_Size( value ) != 2 ) return EXPP_ReturnIntError( PyExc_TypeError, "expected (int, int) as argument" ); for( i = 0; i < 2; i++ ) { tmp = PyTuple_GET_ITEM( value, i ); if( !PyInt_Check( tmp ) ) return EXPP_ReturnIntError ( PyExc_TypeError, "expected a list [int, int] as argument" ); subdiv[i] = EXPP_ClampInt( PyInt_AsLong( tmp ), MESH_SUBDIV_MIN, MESH_SUBDIV_MAX ); } self->mesh->subdiv = (short)subdiv[0]; self->mesh->subdivr = (short)subdiv[1]; return 0; } static PyObject *Mesh_getFlag( BPy_Mesh * self, void *type ) { switch( (long)type ) { case MESH_HASFACEUV: return self->mesh->mtface ? EXPP_incr_ret_True() : EXPP_incr_ret_False(); case MESH_HASMCOL: return self->mesh->mcol ? EXPP_incr_ret_True() : EXPP_incr_ret_False(); case MESH_HASVERTUV: return self->mesh->msticky ? EXPP_incr_ret_True() : EXPP_incr_ret_False(); case MESH_HASMULTIRES: return self->mesh->mr ? EXPP_incr_ret_True() : EXPP_incr_ret_False(); default: return EXPP_ReturnPyObjError( PyExc_RuntimeError, "couldn't get attribute" ); } } static int Mesh_setFlag( BPy_Mesh * self, PyObject *value, void *type ) { int param; Mesh *mesh = self->mesh; param = PyObject_IsTrue( value ); if( param == -1 ) return EXPP_ReturnIntError( PyExc_TypeError, "expected True/False or 0/1" ); /* sticky is independent of faceUV and vertUV */ switch( (long)type ) { case MESH_HASFACEUV: if( !param ) { if( mesh->mtface ) { CustomData_free_layers( &mesh->fdata, CD_MTFACE, mesh->totface ); mesh->mtface = NULL; } } else if( !mesh->mtface ) { if( !mesh->totface ) return EXPP_ReturnIntError( PyExc_RuntimeError, "mesh has no faces" ); make_tfaces( mesh ); } return 0; case MESH_HASMCOL: if( !param ) { if( mesh->mcol ) { CustomData_free_layers( &mesh->fdata, CD_MCOL, mesh->totface ); mesh->mcol = NULL; } } else if( !mesh->mcol ) { /* TODO: mesh_create_shadedColors */ mesh->mcol = CustomData_add_layer( &mesh->fdata, CD_MCOL, CD_DEFAULT, NULL, mesh->totface ); } return 0; case MESH_HASVERTUV: if( !param ) { if( mesh->msticky ) { CustomData_free_layer_active( &mesh->vdata, CD_MSTICKY, mesh->totvert ); mesh->msticky = NULL; } } else { if( !mesh->msticky ) { mesh->msticky = CustomData_add_layer( &mesh->vdata, CD_MSTICKY, CD_CALLOC, NULL, mesh->totvert ); memset( mesh->msticky, 255, mesh->totvert*sizeof( MSticky ) ); /* TODO: rework RE_make_sticky() so we can calculate */ } } return 0; case MESH_HASMULTIRES: if (!self->object) return EXPP_ReturnIntError( PyExc_RuntimeError, "This mesh must be linked to an object" ); if( !param ) { if ( mesh->mr ) { multires_delete(self->object, mesh); } } else { if ( !mesh->mr ) { if (mesh->key) return EXPP_ReturnIntError( PyExc_RuntimeError, "Cannot enable multires for a mesh with shape keys" ); multires_make(self->object, mesh); } } return 0; default: return EXPP_ReturnIntError( PyExc_RuntimeError, "couldn't get attribute" ); } } static PyObject *Mesh_getMode( BPy_Mesh * self ) { return PyInt_FromLong( self->mesh->flag ); } static int Mesh_setMode( BPy_Mesh *self, PyObject *value ) { short param; static short bitmask = ME_ISDONE | ME_NOPUNOFLIP | ME_TWOSIDED | ME_UVEFFECT | ME_VCOLEFFECT | ME_AUTOSMOOTH | ME_SMESH | ME_SUBSURF | ME_OPT_EDGES; if( !PyInt_Check ( value ) ) { char errstr[128]; sprintf ( errstr , "expected int bitmask of 0x%04x", bitmask ); return EXPP_ReturnIntError( PyExc_TypeError, errstr ); } param = (short)PyInt_AS_LONG ( value ); if( ( param & bitmask ) != param ) return EXPP_ReturnIntError( PyExc_ValueError, "invalid bit(s) set in mask" ); self->mesh->flag = param; return 0; } static PyObject *Mesh_getKey( BPy_Mesh * self ) { if( self->mesh->key ) return Key_CreatePyObject(self->mesh->key); else Py_RETURN_NONE; } static PyObject *Mesh_getActiveFace( BPy_Mesh * self ) { /* not needed but keep incase exceptions make use of it */ if( !self->mesh->mtface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no texture values" ); if (self->mesh->act_face != -1 && self->mesh->act_face <= self->mesh->totface) return PyInt_FromLong( self->mesh->act_face ); Py_RETURN_NONE; } static int Mesh_setActiveFace( BPy_Mesh * self, PyObject * value ) { int param; /* if no texture faces, error */ if( !self->mesh->mtface ) return EXPP_ReturnIntError( PyExc_ValueError, "face has no texture values" ); /* if param isn't an int, error */ if( !PyInt_Check( value ) ) return EXPP_ReturnIntError( PyExc_TypeError, "expected an int argument" ); /* check for a valid index */ param = PyInt_AsLong( value ); if( param < 0 || param > self->mesh->totface ) return EXPP_ReturnIntError( PyExc_TypeError, "face index out of range" ); self->mesh->act_face = param; return 0; } static PyObject *Mesh_getActiveGroup( BPy_Mesh * self ) { bDeformGroup *defGroup; Object *object = self->object; if( !object ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This mesh must be linked to an object" ); if( object->actdef ) { defGroup = BLI_findlink( &object->defbase, object->actdef-1 ); return PyString_FromString( defGroup->name ); } Py_RETURN_NONE; } static int Mesh_setActiveGroup( BPy_Mesh * self, PyObject * arg ) { char *name; int tmp; Object *object = self->object; if( !object ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This mesh must be linked to an object" ); if( !PyString_Check( arg ) ) return EXPP_ReturnIntError( PyExc_AttributeError, "expected a string argument" ); name = PyString_AsString( arg ); tmp = object->actdef; vertexgroup_select_by_name( object, name ); if( !object->actdef ) { object->actdef = tmp; return EXPP_ReturnIntError( PyExc_ValueError, "vertex group not found" ); } return 0; } static PyObject *Mesh_getTexMesh( BPy_Mesh * self ) { Mesh *texme= self->mesh->texcomesh; if (texme) return Mesh_CreatePyObject( texme, NULL ); else Py_RETURN_NONE; } static int Mesh_setTexMesh( BPy_Mesh * self, PyObject * value ) { int ret = GenericLib_assignData(value, (void **) &self->mesh->texcomesh, 0, 1, ID_ME, 0); if (ret==0 && value!=Py_None) /*This must be a mesh type*/ (( BPy_Mesh * ) value)->new= 0; return ret; } static int Mesh_setSel( BPy_Mesh * self, PyObject * value ) { int i, param = PyObject_IsTrue( value ); Mesh *me = self->mesh; MVert *mvert = me->mvert; MEdge *medge = me->medge; MFace *mface = me->mface; if( param == -1 ) return EXPP_ReturnIntError( PyExc_TypeError, "expected True/False or 0/1" ); if( param ) { for( i = 0; i < me->totvert; ++mvert, ++i ) mvert->flag |= SELECT; for( i = 0; i < me->totedge; ++medge, ++i ) medge->flag |= SELECT; for( i = 0; i < me->totface; ++mface, ++i ) mface->flag |= ME_FACE_SEL; } else { for( i = 0; i < me->totvert; ++mvert, ++i ) mvert->flag &= ~SELECT; for( i = 0; i < me->totedge; ++medge, ++i ) medge->flag &= ~SELECT; for( i = 0; i < me->totface; ++mface, ++i ) mface->flag &= ~ME_FACE_SEL; } return 0; } static int Mesh_setHide( BPy_Mesh * self, PyObject * value ) { int i, param = PyObject_IsTrue( value ); Mesh *me = self->mesh; MVert *mvert = me->mvert; MEdge *medge = me->medge; MFace *mface = me->mface; if( param == -1 ) return EXPP_ReturnIntError( PyExc_TypeError, "expected True/False or 0/1" ); if( param ) { for( i = 0; i < me->totvert; ++mvert, ++i ) mvert->flag |= ME_HIDE; for( i = 0; i < me->totedge; ++medge, ++i ) medge->flag |= ME_HIDE; for( i = 0; i < me->totface; ++mface, ++i ) mface->flag |= ME_HIDE; } else { for( i = 0; i < me->totvert; ++mvert, ++i ) mvert->flag &= ~ME_HIDE; for( i = 0; i < me->totedge; ++medge, ++i ) medge->flag &= ~ME_HIDE; for( i = 0; i < me->totface; ++mface, ++i ) mface->flag &= ~ME_HIDE; } return 0; } /************************************************************************ * * Python Mesh_Type standard operations * ************************************************************************/ static void Mesh_dealloc( BPy_Mesh * self ) { Mesh *mesh = self->mesh; /* if the mesh is new and has no users, delete it */ if( self->new && !mesh->id.us ) free_libblock( &G.main->mesh, mesh ); PyObject_DEL( self ); } static int Mesh_compare( BPy_Mesh * a, BPy_Mesh * b ) { return ( a->mesh == b->mesh ) ? 0 : -1; } static PyObject *Mesh_repr( BPy_Mesh * self ) { return PyString_FromFormat( "[Mesh \"%s\"]", self->mesh->id.name + 2 ); } /*****************************************************************************/ /* Python Mesh_Type attributes get/set structure: */ /*****************************************************************************/ static PyGetSetDef BPy_Mesh_getseters[] = { GENERIC_LIB_GETSETATTR, {"verts", (getter)Mesh_getVerts, (setter)Mesh_setVerts, "The mesh's vertices (MVert)", NULL}, {"edges", (getter)Mesh_getEdges, (setter)NULL, "The mesh's edge data (MEdge)", NULL}, {"faces", (getter)Mesh_getFaces, (setter)NULL, "The mesh's face data (MFace)", NULL}, {"materials", (getter)Mesh_getMaterials, (setter)Mesh_setMaterials, "List of the mesh's materials", NULL}, {"degr", (getter)Mesh_getMaxSmoothAngle, (setter)Mesh_setMaxSmoothAngle, "The max angle for auto smoothing", NULL}, {"maxSmoothAngle", (getter)Mesh_getMaxSmoothAngle, (setter)Mesh_setMaxSmoothAngle, "deprecated: see 'degr'", NULL}, {"subDivLevels", (getter)Mesh_getSubDivLevels, (setter)Mesh_setSubDivLevels, "The display and rendering subdivision levels", NULL}, {"mode", (getter)Mesh_getMode, (setter)Mesh_setMode, "The mesh's mode bitfield", NULL}, {"key", (getter)Mesh_getKey, (setter)NULL, "The mesh's key", NULL}, {"faceUV", (getter)Mesh_getFlag, (setter)Mesh_setFlag, "UV-mapped textured faces enabled", (void *)MESH_HASFACEUV}, {"vertexColors", (getter)Mesh_getFlag, (setter)Mesh_setFlag, "Vertex colors for the mesh enabled", (void *)MESH_HASMCOL}, {"vertexUV", (getter)Mesh_getFlag, (setter)Mesh_setFlag, "'Sticky' flag for per vertex UV coordinates enabled", (void *)MESH_HASVERTUV}, {"multires", (getter)Mesh_getFlag, (setter)Mesh_setFlag, "'Sticky' flag for per vertex UV coordinates enabled", (void *)MESH_HASMULTIRES}, {"activeFace", (getter)Mesh_getActiveFace, (setter)Mesh_setActiveFace, "Index of the mesh's active texture face (in UV editor)", NULL}, {"activeGroup", (getter)Mesh_getActiveGroup, (setter)Mesh_setActiveGroup, "Active group for the mesh", NULL}, /* uv layers */ {"activeColorLayer", (getter)Mesh_getActiveLayer, (setter)Mesh_setActiveLayer, "Name of the active UV layer", (void *)CD_MCOL}, {"activeUVLayer", (getter)Mesh_getActiveLayer, (setter)Mesh_setActiveLayer, "Name of the active vertex color layer", (void *)CD_MTFACE}, /* hack flip CD_MCOL so it uses the render setting */ {"renderColorLayer", (getter)Mesh_getActiveLayer, (setter)Mesh_setActiveLayer, "Name of the render UV layer", (void *)-CD_MCOL}, {"renderUVLayer", (getter)Mesh_getActiveLayer, (setter)Mesh_setActiveLayer, "Name of the render vertex color layer", (void *)-CD_MTFACE}, /* Multires */ {"multiresLevelCount", (getter)Mesh_getMultiresLevelCount, (setter)NULL, "The total number of multires levels", NULL}, {"multiresDrawLevel", (getter)Mesh_getMultires, (setter)Mesh_setMultires, "The current multires display level", (void *)MESH_MULTIRES_LEVEL}, {"multiresEdgeLevel", (getter)Mesh_getMultires, (setter)Mesh_setMultires, "The current multires edge level", (void *)MESH_MULTIRES_EDGE}, {"multiresPinLevel", (getter)Mesh_getMultires, (setter)Mesh_setMultires, "The current multires pin level", (void *)MESH_MULTIRES_PIN}, {"multiresRenderLevel", (getter)Mesh_getMultires, (setter)Mesh_setMultires, "The current multires render level", (void *)MESH_MULTIRES_RENDER}, {"texMesh", (getter)Mesh_getTexMesh, (setter)Mesh_setTexMesh, "The meshes tex mesh proxy texture coord mesh", NULL}, {"sel", (getter)NULL, (setter)Mesh_setSel, "Select/deselect all verts, edges, faces in the mesh", NULL}, {"hide", (getter)NULL, (setter)Mesh_setHide, "Hide/unhide all verts, edges, faces in the mesh", NULL}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /*****************************************************************************/ /* Python Mesh_Type structure definition: */ /*****************************************************************************/ PyTypeObject Mesh_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "." */ "Blender Mesh", /* char *tp_name; */ sizeof( BPy_Mesh ), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ ( destructor ) Mesh_dealloc,/* destructor tp_dealloc; */ NULL, /* printfunc tp_print; */ NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ ( cmpfunc ) Mesh_compare, /* cmpfunc tp_compare; */ ( reprfunc ) Mesh_repr, /* reprfunc tp_repr; */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ NULL, /* PySequenceMethods *tp_as_sequence; */ NULL, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ ( hashfunc ) GenericLib_hash, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ NULL, /* richcmpfunc tp_richcompare; */ /*** weak reference enabler ***/ 0, /* long tp_weaklistoffset; */ /*** Added in release 2.2 ***/ /* Iterators */ NULL, /* getiterfunc tp_iter; */ NULL, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ BPy_Mesh_methods, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ BPy_Mesh_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ NULL, /* descrsetfunc tp_descr_set; */ 0, /* long tp_dictoffset; */ NULL, /* initproc tp_init; */ NULL, /* allocfunc tp_alloc; */ NULL, /* newfunc tp_new; */ /* Low-level free-memory routine */ NULL, /* freefunc tp_free; */ /* For PyObject_IS_GC */ NULL, /* inquiry tp_is_gc; */ NULL, /* PyObject *tp_bases; */ /* method resolution order */ NULL, /* PyObject *tp_mro; */ NULL, /* PyObject *tp_cache; */ NULL, /* PyObject *tp_subclasses; */ NULL, /* PyObject *tp_weaklist; */ NULL }; /* * get one or all mesh data objects */ static PyObject *M_Mesh_Get( PyObject * self_unused, PyObject * args ) { char *name = NULL; Mesh *mesh = NULL; BPy_Mesh* obj; if( !PyArg_ParseTuple( args, "|s", &name ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected zero or one string arguments" ); if( name ) { mesh = ( Mesh * ) GetIdFromList( &( G.main->mesh ), name ); if( !mesh ) { char error_msg[64]; PyOS_snprintf( error_msg, sizeof( error_msg ), "Mesh \"%s\" not found", name ); return ( EXPP_ReturnPyObjError ( PyExc_NameError, error_msg ) ); } return Mesh_CreatePyObject( mesh, NULL ); } else { /* () - return a list with all meshes in the scene */ PyObject *meshlist; Link *link; int index = 0; meshlist = PyList_New( BLI_countlist( &( G.main->mesh ) ) ); if( !meshlist ) return EXPP_ReturnPyObjError( PyExc_MemoryError, "couldn't create PyList" ); link = G.main->mesh.first; index = 0; while( link ) { obj = ( BPy_Mesh * ) Mesh_CreatePyObject( ( Mesh * )link, NULL ); PyList_SetItem( meshlist, index, ( PyObject * ) obj ); index++; link = link->next; } return meshlist; } } /* * create a new mesh data object */ static PyObject *M_Mesh_New( PyObject * self_unused, PyObject * args ) { char *name = "Mesh"; Mesh *mesh; BPy_Mesh *obj; if( !PyArg_ParseTuple( args, "|s", &name ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected nothing or a string as argument" ); obj = (BPy_Mesh *)PyObject_NEW( BPy_Mesh, &Mesh_Type ); if( !obj ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyObject_New() failed" ); mesh = add_mesh(name); /* doesn't return NULL now, but might someday */ if( !mesh ) { Py_DECREF ( obj ); return EXPP_ReturnPyObjError( PyExc_RuntimeError, "FATAL: could not create mesh object" ); } /* Bound box set to null needed because a new mesh is initialized with a bounding box of -1 -1 -1 -1 -1 -1 if its not set to null the bounding box is not re-calculated when ob.getBoundBox() is called.*/ MEM_freeN(mesh->bb); mesh->bb= NULL; mesh->id.us = 0; obj->mesh = mesh; obj->object = NULL; obj->new = 1; return (PyObject *)obj; } /* * creates a new MVert for users to manipulate */ static PyObject *M_Mesh_MVert( PyObject * self_unused, PyObject * args ) { int i; MVert vert; /* initialize the new vert's data */ memset( &vert, 0, sizeof( MVert ) ); /* * accept either a 3D vector or tuple of three floats */ if( PyTuple_Size ( args ) == 1 ) { PyObject *tmp = PyTuple_GET_ITEM( args, 0 ); if( !VectorObject_Check( tmp ) || ((VectorObject *)tmp)->size != 3 ) return EXPP_ReturnPyObjError( PyExc_ValueError, "expected three floats or vector of size 3" ); for( i = 0; i < 3; ++i ) vert.co[i] = ((VectorObject *)tmp)->vec[i]; } else if( !PyArg_ParseTuple ( args, "fff", &vert.co[0], &vert.co[1], &vert.co[2] ) ) return EXPP_ReturnPyObjError( PyExc_ValueError, "expected three floats or vector of size 3" ); /* make a new MVert from the data */ return PVert_CreatePyObject( &vert ); } static PyObject *M_Mesh_Modes( PyObject * self_unused, PyObject * args ) { int modes = 0; if( !G.scene ) { Py_RETURN_NONE; } if( !PyArg_ParseTuple( args, "|i", &modes ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected optional int as argument" ); if( modes > ( SCE_SELECT_VERTEX | SCE_SELECT_EDGE | SCE_SELECT_FACE ) ) return EXPP_ReturnPyObjError( PyExc_ValueError, "value out of range" ); if( modes > 0 ) G.scene->selectmode = (short)modes; return PyInt_FromLong( G.scene->selectmode ); } static struct PyMethodDef M_Mesh_methods[] = { {"New", (PyCFunction)M_Mesh_New, METH_VARARGS, "Create a new mesh"}, {"Get", (PyCFunction)M_Mesh_Get, METH_VARARGS, "Get a mesh by name"}, {"MVert", (PyCFunction)M_Mesh_MVert, METH_VARARGS, "Create a new MVert"}, {"Mode", (PyCFunction)M_Mesh_Modes, METH_VARARGS, "Get/set edit selection mode(s)"}, {NULL, NULL, 0, NULL}, }; static PyObject *M_Mesh_ModesDict( void ) { PyObject *Modes = PyConstant_New( ); if( Modes ) { BPy_constant *d = ( BPy_constant * ) Modes; PyConstant_Insert( d, "NOVNORMALSFLIP", PyInt_FromLong( ME_NOPUNOFLIP ) ); PyConstant_Insert( d, "TWOSIDED", PyInt_FromLong( ME_TWOSIDED ) ); PyConstant_Insert( d, "AUTOSMOOTH", PyInt_FromLong( ME_AUTOSMOOTH ) ); } return Modes; } /* Set constants for face drawing mode -- see drawmesh.c */ static PyObject *M_Mesh_FaceModesDict( void ) { PyObject *FM = PyConstant_New( ); if( FM ) { BPy_constant *d = ( BPy_constant * ) FM; PyConstant_Insert( d, "BILLBOARD", PyInt_FromLong( TF_BILLBOARD2 ) ); PyConstant_Insert( d, "ALL", PyInt_FromLong( 0xffff ) ); PyConstant_Insert( d, "HALO", PyInt_FromLong( TF_BILLBOARD ) ); PyConstant_Insert( d, "DYNAMIC", PyInt_FromLong( TF_DYNAMIC ) ); PyConstant_Insert( d, "INVISIBLE", PyInt_FromLong( TF_INVISIBLE ) ); PyConstant_Insert( d, "LIGHT", PyInt_FromLong( TF_LIGHT ) ); PyConstant_Insert( d, "OBCOL", PyInt_FromLong( TF_OBCOL ) ); PyConstant_Insert( d, "SHADOW", PyInt_FromLong( TF_SHADOW ) ); PyConstant_Insert( d, "TEXT", PyInt_FromLong( TF_BMFONT ) ); PyConstant_Insert( d, "SHAREDVERT", PyInt_FromLong( TF_SHAREDVERT ) ); PyConstant_Insert( d, "SHAREDCOL", PyInt_FromLong( TF_SHAREDCOL ) ); PyConstant_Insert( d, "TEX", PyInt_FromLong( TF_TEX ) ); PyConstant_Insert( d, "TILES", PyInt_FromLong( TF_TILES ) ); PyConstant_Insert( d, "TWOSIDE", PyInt_FromLong( TF_TWOSIDE ) ); } return FM; } static PyObject *M_Mesh_FaceFlagsDict( void ) { PyObject *FF = PyConstant_New( ); if( FF ) { BPy_constant *d = ( BPy_constant * ) FF; PyConstant_Insert( d, "SELECT", PyInt_FromLong( TF_SELECT ) ); PyConstant_Insert( d, "HIDE", PyInt_FromLong( TF_HIDE ) ); PyConstant_Insert( d, "ACTIVE", PyInt_FromLong( TF_ACTIVE ) ); /* deprecated */ } return FF; } static PyObject *M_Mesh_FaceTranspModesDict( void ) { PyObject *FTM = PyConstant_New( ); if( FTM ) { BPy_constant *d = ( BPy_constant * ) FTM; PyConstant_Insert( d, "SOLID", PyInt_FromLong( TF_SOLID ) ); PyConstant_Insert( d, "ADD", PyInt_FromLong( TF_ADD ) ); PyConstant_Insert( d, "ALPHA", PyInt_FromLong( TF_ALPHA ) ); PyConstant_Insert( d, "SUB", PyInt_FromLong( TF_SUB ) ); } return FTM; } static PyObject *M_Mesh_EdgeFlagsDict( void ) { PyObject *EF = PyConstant_New( ); if( EF ) { BPy_constant *d = ( BPy_constant * ) EF; PyConstant_Insert(d, "SELECT", PyInt_FromLong( SELECT ) ); PyConstant_Insert(d, "EDGEDRAW", PyInt_FromLong( ME_EDGEDRAW ) ); PyConstant_Insert(d, "EDGERENDER", PyInt_FromLong( ME_EDGERENDER ) ); PyConstant_Insert(d, "SEAM", PyInt_FromLong( ME_SEAM ) ); PyConstant_Insert(d, "FGON", PyInt_FromLong( ME_FGON ) ); PyConstant_Insert(d, "LOOSE", PyInt_FromLong( ME_LOOSEEDGE ) ); PyConstant_Insert(d, "SHARP", PyInt_FromLong( ME_SHARP ) ); } return EF; } static PyObject *M_Mesh_VertAssignDict( void ) { PyObject *Vert = PyConstant_New( ); if( Vert ) { BPy_constant *d = ( BPy_constant * ) Vert; PyConstant_Insert(d, "ADD", PyInt_FromLong(WEIGHT_ADD)); PyConstant_Insert(d, "REPLACE", PyInt_FromLong(WEIGHT_REPLACE)); PyConstant_Insert(d, "SUBTRACT", PyInt_FromLong(WEIGHT_SUBTRACT)); } return Vert; } static PyObject *M_Mesh_SelectModeDict( void ) { PyObject *Mode = PyConstant_New( ); if( Mode ) { BPy_constant *d = ( BPy_constant * ) Mode; PyConstant_Insert(d, "VERTEX", PyInt_FromLong(SCE_SELECT_VERTEX)); PyConstant_Insert(d, "EDGE", PyInt_FromLong(SCE_SELECT_EDGE)); PyConstant_Insert(d, "FACE", PyInt_FromLong(SCE_SELECT_FACE)); } return Mode; } static char M_Mesh_doc[] = "The Blender.Mesh submodule"; PyObject *Mesh_Init( void ) { PyObject *submodule; PyObject *Modes = M_Mesh_ModesDict( ); PyObject *FaceFlags = M_Mesh_FaceFlagsDict( ); PyObject *FaceModes = M_Mesh_FaceModesDict( ); PyObject *FaceTranspModes = M_Mesh_FaceTranspModesDict( ); PyObject *EdgeFlags = M_Mesh_EdgeFlagsDict( ); PyObject *AssignModes = M_Mesh_VertAssignDict( ); PyObject *SelectModes = M_Mesh_SelectModeDict( ); PyObject *PropertyTypes = M_Mesh_PropertiesTypeDict( ); if( PyType_Ready( &MCol_Type ) < 0 ) return NULL; if( PyType_Ready( &MVert_Type ) < 0 ) return NULL; if( PyType_Ready( &PVert_Type ) < 0 ) return NULL; if( PyType_Ready( &MVertSeq_Type ) < 0 ) return NULL; if( PyType_Ready( &MEdge_Type ) < 0 ) return NULL; if( PyType_Ready( &MEdgeSeq_Type ) < 0 ) return NULL; if( PyType_Ready( &MFace_Type ) < 0 ) return NULL; if( PyType_Ready( &MFaceSeq_Type ) < 0 ) return NULL; if( PyType_Ready( &Mesh_Type ) < 0 ) return NULL; submodule = Py_InitModule3( "Blender.Mesh", M_Mesh_methods, M_Mesh_doc ); PyDict_SetItemString( PyModule_GetDict( submodule ), "Primitives", MeshPrimitives_Init( ) ); if( Modes ) PyModule_AddObject( submodule, "Modes", Modes ); if( FaceFlags ) PyModule_AddObject( submodule, "FaceFlags", FaceFlags ); if( FaceModes ) PyModule_AddObject( submodule, "FaceModes", FaceModes ); if( FaceTranspModes ) PyModule_AddObject( submodule, "FaceTranspModes", FaceTranspModes ); if( EdgeFlags ) PyModule_AddObject( submodule, "EdgeFlags", EdgeFlags ); if( AssignModes ) PyModule_AddObject( submodule, "AssignModes", AssignModes ); if( SelectModes ) PyModule_AddObject( submodule, "SelectModes", SelectModes ); if( PropertyTypes ) PyModule_AddObject( submodule, "PropertyTypes", PropertyTypes ); return submodule; } /* These are needed by Object.c */ PyObject *Mesh_CreatePyObject( Mesh * me, Object *obj ) { BPy_Mesh *nmesh = PyObject_NEW( BPy_Mesh, &Mesh_Type ); if( !nmesh ) return EXPP_ReturnPyObjError( PyExc_MemoryError, "couldn't create BPy_Mesh object" ); nmesh->mesh = me; nmesh->object = obj; nmesh->new = 0; G.totmesh++; return ( PyObject * ) nmesh; } Mesh *Mesh_FromPyObject( PyObject * pyobj, Object *obj ) { BPy_Mesh *blen_obj; blen_obj = ( BPy_Mesh * ) pyobj; if (obj) blen_obj->object = obj; return blen_obj->mesh; }