/* * $Id$ * * ***** BEGIN GPL/BL DUAL 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. The Blender * Foundation also sells licenses for use in proprietary software under * the Blender License. See http://www.blender.org/BL/ for information * about this. * * 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/BL DUAL 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 "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_editview.h" #include "BKE_deform.h" #include "BKE_mesh.h" #include "BKE_material.h" #include "BKE_main.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_displist.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 void countall(); */ #include "BLI_arithb.h" #include "BLI_blenlib.h" #include "blendef.h" #include "mydevice.h" #include "Object.h" #include "Key.h" #include "Image.h" #include "Material.h" #include "Mathutils.h" #include "constant.h" #include "gen_utils.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 /************************************************************************ * * 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 */ #if 0 unsigned int index; /* index in original param list of this edge */ /* (will be used by findEdges) */ #endif } SrchEdges; typedef struct SrchFaces { unsigned int v[4]; /* indices for verts */ unsigned char order; /* order of original verts, bitpacked */ } SrchFaces; /* * 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 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; } /* * update the DAG for all objects linked to this mesh */ static void mesh_update( Mesh * mesh ) { Object_updateDag( (void*) mesh ); } #ifdef CHECK_DVERTS /* not clear if this code is needed */ /* * if verts have been added or deleted, fix dverts also */ static void check_dverts(Mesh *me, int old_totvert) { int totvert = me->totvert; /* if all verts have been deleted, free old dverts */ if (totvert == 0) free_dverts(me->dvert, old_totvert); /* if verts have been added, expand me->dvert */ else if (totvert > old_totvert) { MDeformVert *mdv = me->dvert; me->dvert = NULL; create_dverts(me); copy_dverts(me->dvert, mdv, old_totvert); free_dverts(mdv, old_totvert); } /* if verts have been deleted, shrink me->dvert */ else { MDeformVert *mdv = me->dvert; me->dvert = NULL; create_dverts(me); copy_dverts(me->dvert, mdv, totvert); free_dverts(mdv, old_totvert); } return; } #endif /************************************************************************ * * Color attributes * ************************************************************************/ /* * get a color attribute */ static PyObject *MCol_getAttr( BPy_MCol * self, void *type ) { unsigned char param; PyObject *attr; switch( (int)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 %s", (int)type, __FUNCTION__ ); return EXPP_ReturnPyObjError( PyExc_RuntimeError, errstr ); } } attr = PyInt_FromLong( param ); if( attr ) return attr; return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed"); } /* * set a color attribute */ static int MCol_setAttr( BPy_MCol * self, PyObject * value, void * type ) { unsigned char *param; switch( (int)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 %s", (int)type, __FUNCTION__ ); 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 */ }; /************************************************************************ * * Python MCol_Type methods * ************************************************************************/ static void MCol_dealloc( BPy_MCol * self ) { PyObject_DEL( self ); } static PyObject *MCol_repr( BPy_MCol * self ) { return PyString_FromFormat( "[MCol %d %d %d %d]", (int)self->color->r, (int)self->color->g, (int)self->color->b, (int)self->color->a ); } /************************************************************************ * * 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 */ ( destructor ) MCol_dealloc,/* 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; */ NULL, /* 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; } /************************************************************************ * * Vertex attributes * ************************************************************************/ /* * get a vertex's coordinate */ static PyObject *MVert_getCoord( BPy_MVert * self ) { struct MVert *v = &self->mesh->mvert[self->index]; return newVectorObject( v->co, 3, Py_WRAP ); } /* * set a vertex's coordinate */ static int MVert_setCoord( BPy_MVert * self, VectorObject * value ) { struct MVert *v = &self->mesh->mvert[self->index]; int i; 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 ) { PyObject *attr = PyInt_FromLong( self->index ); if( attr ) return attr; return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } /* * get a vertex's normal */ static PyObject *MVert_getNormal( BPy_MVert * self ) { struct MVert *v = &self->mesh->mvert[self->index]; float no[3]; int i; for( i=0; i<3; ++i ) no[i] = (float)(v->no[i] / 32767.0); return newVectorObject( no, 3, Py_NEW ); } /* * get a vertex's select status */ static PyObject *MVert_getSel( BPy_MVert *self ) { struct MVert *v = &self->mesh->mvert[self->index]; return EXPP_getBitfield( &v->flag, SELECT, 'b' ); } /* * set a vertex's select status */ static int MVert_setSel( BPy_MVert *self, PyObject *value ) { struct MVert *v = &self->mesh->mvert[self->index]; return EXPP_setBitfield( value, &v->flag, SELECT, 'b' ); } /* * get a vertex's UV coordinates */ static PyObject *MVert_getUVco( BPy_MVert *self ) { if( !self->mesh->msticky ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "mesh has no 'sticky' coordinates" ); return newVectorObject( self->mesh->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}; struct MSticky *v; int i; /* * at least for now, don't allow creation of sticky coordinates if they * don't already exist */ if( !self->mesh->msticky ) return EXPP_ReturnIntError( PyExc_AttributeError, "mesh has no 'sticky' coordinates" ); 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 = &self->mesh->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)NULL, "vertex's normal", NULL}, {"sel", (getter)MVert_getSel, (setter)MVert_setSel, "vertex's select status", NULL}, {"uvco", (getter)MVert_getUVco, (setter)MVert_setUVco, "vertex's UV coordinates", NULL}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /************************************************************************ * * Python MVert_Type standard operations * ************************************************************************/ static void MVert_dealloc( BPy_MVert * self ) { PyObject_DEL( self ); } static int MVert_compare( BPy_MVert * a, BPy_MVert * b ) { return( a->mesh == b->mesh && a->index == b->index ) ? 0 : -1; } static PyObject *MVert_repr( BPy_MVert * self ) { struct MVert *v = &self->mesh->mvert[self->index]; char format[512]; sprintf( format, "[MVert (%f %f %f) (%f %f %f) %d]", 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), self->index ); return PyString_FromString( format ); } /************************************************************************ * * 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) */ 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_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 }; static PyObject *MVert_CreatePyObject( Mesh * mesh, int i ) { BPy_MVert *obj = PyObject_NEW( BPy_MVert, &MVert_Type ); if( !obj ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyObject_New() failed" ); obj->mesh = mesh; obj->index = i; return (PyObject *)obj; } /************************************************************************ * * Vertex sequence * ************************************************************************/ static int MVertSeq_len( BPy_MVertSeq * self ) { return self->mesh->totvert; } 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 ); }; static PySequenceMethods MVertSeq_as_sequence = { ( inquiry ) MVertSeq_len, /* sq_length */ ( binaryfunc ) 0, /* sq_concat */ ( intargfunc ) 0, /* sq_repeat */ ( intargfunc ) MVertSeq_item, /* sq_item */ ( intintargfunc ) 0, /* sq_slice */ ( intobjargproc ) 0, /* sq_ass_item */ ( intintobjargproc ) 0, /* sq_ass_slice */ 0,0,0, }; /************************************************************************ * * Python MVertSeq_Type iterator (iterates over vertices) * ************************************************************************/ /* * Initialize the interator index */ static PyObject *MVertSeq_getIter( BPy_MVertSeq * self ) { self->iter = 0; return EXPP_incr_ret ( (PyObject *) self ); } /* * Return next MVert. */ static PyObject *MVertSeq_nextIter( BPy_MVertSeq * self ) { if( self->iter == self->mesh->totvert ) 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; int i,j; PyObject *tmp; MVert *newvert, *tmpvert; Mesh *mesh = self->mesh; /* make sure we get a sequence of tuples of something */ switch( PySequence_Size ( args ) ) { case 1: /* better be a list or a tuple */ args = PyTuple_GET_ITEM( args, 0 ); if( !PySequence_Check ( args ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of tuple triplets" ); Py_INCREF( args ); /* so we can safely DECREF later */ break; case 3: /* take any three args and put into a tuple */ tmp = PyTuple_GET_ITEM( args, 0 ); if( PyTuple_Check( tmp ) ) { Py_INCREF( args ); break; } 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 tuple triplets" ); } len = PySequence_Size( args ); if( len == 0 ) { Py_DECREF ( args ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected at least one tuple" ); } newvert = MEM_callocN( sizeof( MVert ) * (mesh->totvert+len), "MVerts" ); /* scan the input list and insert the new vertices */ tmpvert = &newvert[mesh->totvert]; for( i = 0; i < len; ++i ) { float co[3]; tmp = PySequence_Fast_GET_ITEM( args, i ); if( VectorObject_Check( tmp ) ) { if( ((VectorObject *)tmp)->size != 3 ) { MEM_freeN( newvert ); 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( PyTuple_Check( tmp ) ) { int ok=1; PyObject *flt; if( PyTuple_Size( tmp ) != 3 ) ok = 0; else for( j = 0; ok && j < 3; ++j ) { flt = PyTuple_GET_ITEM( tmp, j ); if( !PyNumber_Check ( flt ) ) ok = 0; else co[j] = (float)PyFloat_AsDouble( flt ); } if( !ok ) { MEM_freeN( newvert ); Py_DECREF ( args ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected tuple triplet of floats" ); } } /* add the coordinate to the new list */ #if 0 memcpy( tmpvert->co, co, sizeof(float)*3 ); #else { int i=3; while (i--) tmpvert->co[i] = co[i]; } #endif /* TODO: anything else which needs to be done when we add a vert? */ /* probably not: NMesh's newvert() doesn't */ ++tmpvert; } /* if we got here we've added all the new verts, so just copy the old * verts over and we're done */ if( mesh->mvert ) { memcpy( newvert, mesh->mvert, mesh->totvert*sizeof(MVert) ); MEM_freeN( mesh->mvert ); } mesh->mvert = newvert; mesh->totvert += len; #ifdef CHECK_DVERTS check_dverts( mesh, mesh->totvert - len ); #endif mesh_update( mesh ); Py_DECREF ( args ); return EXPP_incr_ret( Py_None ); } static struct PyMethodDef BPy_MVertSeq_methods[] = { {"extend", (PyCFunction)MVertSeq_extend, METH_VARARGS, "add edges to mesh"}, {NULL, NULL, 0, NULL} }; /************************************************************************ * * Python MVertSeq_Type standard operations * ************************************************************************/ static void MVertSeq_dealloc( BPy_MVertSeq * self ) { PyObject_DEL( self ); } /*****************************************************************************/ /* Python NMVertSeq_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 */ ( destructor ) MVertSeq_dealloc,/* 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; */ NULL, /* 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 * ************************************************************************/ /* * get an edge's crease value */ static PyObject *MEdge_getCrease( BPy_MEdge * self ) { struct MEdge *edge = &self->mesh->medge[self->index]; PyObject *attr = PyInt_FromLong( edge->crease ); if( attr ) return attr; return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } /* * set an edge's crease value */ static int MEdge_setCrease( BPy_MEdge * self, PyObject * value ) { struct MEdge *edge = &self->mesh->medge[self->index]; return EXPP_setIValueClamped( value, &edge->crease, 0, 255, 'b' ); } /* * get an edge's flag */ static PyObject *MEdge_getFlag( BPy_MEdge * self ) { struct MEdge *edge = &self->mesh->medge[self->index]; PyObject *attr = PyInt_FromLong( edge->flag ); if( attr ) return attr; return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } /* * set an edge's flag */ static int MEdge_setFlag( BPy_MEdge * self, PyObject * value ) { struct MEdge *edge = &self->mesh->medge[self->index]; short param; static short bitmask = 1 /* 1=select */ | ME_EDGEDRAW | ME_EDGERENDER | ME_SEAM | ME_FGON; if( !PyInt_CheckExact ( 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 ) { struct MEdge *edge = &self->mesh->medge[self->index]; return MVert_CreatePyObject( self->mesh, edge->v1 ); } /* * set an edge's first vertex */ static int MEdge_setV1( BPy_MEdge * self, BPy_MVert * value ) { struct MEdge *edge = &self->mesh->medge[self->index]; edge->v1 = value->index; return 0; } /* * get an edge's second vertex */ static PyObject *MEdge_getV2( BPy_MEdge * self ) { struct MEdge *edge = &self->mesh->medge[self->index]; return MVert_CreatePyObject( self->mesh, edge->v2 ); } /* * set an edge's second vertex */ static int MEdge_setV2( BPy_MEdge * self, BPy_MVert * value ) { struct MEdge *edge = &self->mesh->medge[self->index]; edge->v2 = value->index; return 0; } /* * get an edges's index */ static PyObject *MEdge_getIndex( BPy_MEdge * self ) { PyObject *attr = PyInt_FromLong( self->index ); if( attr ) return attr; return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } /************************************************************************ * * 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}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /************************************************************************ * * Python MEdge_Type iterator (iterates over vertices) * ************************************************************************/ /* * Initialize the interator index */ static PyObject *MEdge_getIter( BPy_MEdge * self ) { self->iter = 0; return EXPP_incr_ret ( (PyObject *) self ); } /* * Return next MVert. Throw an exception after the second vertex. */ static PyObject *MEdge_nextIter( BPy_MEdge * self ) { if( self->iter == 2 ) 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 void MEdge_dealloc( BPy_MEdge * self ) { PyObject_DEL( self ); } 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 = &self->mesh->medge[self->index]; return PyString_FromFormat( "[MEdge (%d %d) %d %d]", (int)edge->v1, (int)edge->v2, (int)edge->crease, (int)self->index ); } /************************************************************************ * * 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 */ ( destructor ) MEdge_dealloc,/* 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) */ 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) MEdge_getIter, /* getiterfunc tp_iter; */ ( iternextfunc ) MEdge_nextIter, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ NULL, /* 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; 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 ) { self->iter = 0; return EXPP_incr_ret ( (PyObject *) self ); } /* * Return next MEdge. */ static PyObject *MEdgeSeq_nextIter( BPy_MEdgeSeq * self ) { if( self->iter == self->mesh->totedge ) 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; 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 sequence of tuples of something */ switch( PySequence_Size ( args ) ) { case 1: /* better be a list or a tuple */ args = PyTuple_GET_ITEM( args, 0 ); if( !PySequence_Check ( args ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of tuple pairs" ); Py_INCREF( args ); /* so we can safely DECREF later */ break; case 2: case 3: case 4: /* two to four args may be individual verts */ tmp = PyTuple_GET_ITEM( args, 0 ); if( PyTuple_Check( tmp ) ) {/* maybe just tuples, so use args as-is */ Py_INCREF( args ); /* so we can safely DECREF later */ break; } args = Py_BuildValue( "(O)", args ); 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" ); } /* make sure there is something to add */ len = PySequence_Size( args ); if( len == 0 ) { Py_DECREF( args ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected at least one tuple" ); } /* 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_Fast_GET_ITEM( args, i ); /* not a tuple of MVerts... error */ if( !PyTuple_Check( tmp ) || EXPP_check_sequence_consistency( tmp, &MVert_Type ) != 1 ) { Py_DECREF( args ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected sequence of MVert tuples" ); } /* not the right number of MVerts... error */ nverts = PyTuple_Size( tmp ); if( nverts < 2 || nverts > 4 ) { Py_DECREF( args ); return EXPP_ReturnPyObjError( PyExc_ValueError, "expected 2 to 4 MVerts per tuple" ); } 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; for( i = 0; i < len; ++i ) { tmp = PySequence_Fast_GET_ITEM( args, i ); nverts = PyTuple_Size( tmp ); /* get copies of vertices */ for(j = 0; j < nverts; ++j ) e[j] = (BPy_MVert *)PyTuple_GET_ITEM( tmp, j ); if( nverts == 2 ) nverts = 1; /* again, two verts give just one edge */ /* now add the edges to the search list */ for(j = 0; j < nverts; ++j ) { int k = j+1; if( k == nverts ) /* final edge */ k = 0; /* sort verts into search list, abort if two are the same */ if( e[j]->index < e[k]->index ) { tmppair->v[0] = e[j]->index; tmppair->v[1] = e[k]->index; tmppair->swap = 0; } else if( e[j]->index > e[k]->index ) { tmppair->v[0] = e[k]->index; tmppair->v[1] = e[j]->index; tmppair->swap = 1; } else { MEM_freeN( newpair ); Py_DECREF( args ); return EXPP_ReturnPyObjError( PyExc_ValueError, "tuple contains duplicate vertices" ); } 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 ) { int totedge = mesh->totedge+good_edges; /* new edge count */ /* allocate new edge list */ tmpedge = MEM_callocN(totedge*sizeof(MEdge), "NMesh_addEdges"); /* if we're appending, copy the old edge list and delete it */ if( mesh->medge ) { memcpy( tmpedge, mesh->medge, mesh->totedge*sizeof(MEdge)); MEM_freeN( mesh->medge ); } mesh->medge = tmpedge; /* point to the new edge list */ /* 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; mesh->totedge++; --good_edges; ++tmpedge; } tmppair++; } } /* clean up and leave */ mesh_update( mesh ); MEM_freeN( newpair ); Py_DECREF ( args ); return EXPP_incr_ret( Py_None ); } static struct PyMethodDef BPy_MEdgeSeq_methods[] = { {"extend", (PyCFunction)MEdgeSeq_extend, METH_VARARGS, "add edges to mesh"}, {NULL, NULL, 0, NULL} }; /************************************************************************ * * Python MEdgeSeq_Type standard operators * ************************************************************************/ static void MEdgeSeq_dealloc( BPy_MEdgeSeq * self ) { PyObject_DEL( self ); } /*****************************************************************************/ /* Python NMEdgeSeq_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 */ ( destructor ) MEdgeSeq_dealloc,/* 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; */ NULL, /* 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 * ************************************************************************/ /* * get a face's vertices */ static PyObject *MFace_getVerts( BPy_MFace * self ) { struct MFace *face = &self->mesh->mface[self->index]; PyObject *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 ) { struct MFace *face = &self->mesh->mface[self->index]; BPy_MVert *v1, *v2, *v3, *v4 = NULL; 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" ); 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 ) { struct MFace *face = &self->mesh->mface[self->index]; PyObject *attr = PyInt_FromLong( face->mat_nr ); if( attr ) return attr; return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } /* * set face's material index */ static int MFace_setMat( BPy_MFace * self, PyObject * value ) { struct MFace *face = &self->mesh->mface[self->index]; return EXPP_setIValueRange( value, &face->mat_nr, 0, 15, 'b' ); } /* * get a face's index */ static PyObject *MFace_getIndex( BPy_MFace * self ) { PyObject *attr = PyInt_FromLong( self->index ); if( attr ) return attr; return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } /* * get face's normal index */ static PyObject *MFace_getNormal( BPy_MFace * self ) { struct MFace *face = &self->mesh->mface[self->index]; float *vert[4]; float no[3]; vert[0] = self->mesh->mvert[face->v1].co; vert[1] = self->mesh->mvert[face->v2].co; vert[2] = self->mesh->mvert[face->v3].co; vert[3] = self->mesh->mvert[face->v4].co; if( face->v4 ) 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 one of a face's mface flag bits */ static PyObject *MFace_getMFlagBits( BPy_MFace * self, void * type ) { struct MFace *face = &self->mesh->mface[self->index]; return EXPP_getBitfield( &face->flag, (int)type, 'b' ); } /* * set one of a face's mface flag bits */ static int MFace_setMFlagBits( BPy_MFace * self, PyObject * value, void * type ) { struct MFace *face = &self->mesh->mface[self->index]; return EXPP_setBitfield( value, &face->flag, (int)type, 'b' ); } /* * get face's texture image */ static PyObject *MFace_getImage( BPy_MFace *self ) { TFace *face; if( !self->mesh->tface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no texture values" ); face = &self->mesh->tface[self->index]; if( face->tpage ) return Image_CreatePyObject( face->tpage ); else return EXPP_incr_ret( Py_None ); } /* * change or clear face's texture image */ static int MFace_setImage( BPy_MFace *self, PyObject *value ) { TFace *face; if( !self->mesh->tface ) return EXPP_ReturnIntError( PyExc_ValueError, "face has no texture values" ); face = &self->mesh->tface[self->index]; if( value == Py_None ) face->tpage = NULL; /* should memory be freed? */ else { if( !BPy_Image_Check( value ) ) return EXPP_ReturnIntError( PyExc_TypeError, "expected image object" ); face->tpage = ( ( BPy_Image * ) value )->image; } return 0; } /* * get face's texture mode */ static PyObject *MFace_getMode( BPy_MFace *self ) { PyObject *attr; if( !self->mesh->tface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no texture values" ); attr = PyInt_FromLong( self->mesh->tface[self->index].mode ); if( attr ) return attr; return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } /* * set face's texture mode */ static int MFace_setMode( BPy_MFace *self, PyObject *value ) { int param; static short bitmask = TF_SELECT | TF_HIDE; if( !self->mesh->tface ) return EXPP_ReturnIntError( PyExc_ValueError, "face has no texture values" ); if( !PyInt_CheckExact ( value ) ) { char errstr[128]; sprintf ( errstr , "expected int bitmask of 0x%04x", 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 & bitmask ) == TF_ACTIVE ) return EXPP_ReturnIntError( PyExc_ValueError, "cannot make a face active; use 'activeFace' attribute" ); if( ( param & bitmask ) != param ) return EXPP_ReturnIntError( PyExc_ValueError, "invalid bit(s) set in mask" ); /* merge active setting with other new params */ param |= (self->mesh->tface[self->index].flag & TF_ACTIVE); self->mesh->tface[self->index].flag = (char)param; return 0; } /* * get face's texture flags */ static PyObject *MFace_getFlag( BPy_MFace *self ) { PyObject *attr; if( !self->mesh->tface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no texture values" ); attr = PyInt_FromLong( self->mesh->tface[self->index].mode ); if( attr ) return attr; return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } /* * set face's texture flag */ static int MFace_setFlag( 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->tface ) return EXPP_ReturnIntError( PyExc_ValueError, "face has no texture values" ); if( !PyInt_CheckExact ( 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->tface[self->index].mode = (short)param; return 0; } /* * get face's texture transparency setting */ static PyObject *MFace_getTransp( BPy_MFace *self ) { PyObject *attr; if( !self->mesh->tface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no texture values" ); attr = PyInt_FromLong( self->mesh->tface[self->index].transp ); if( attr ) return attr; return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyInt_FromLong() failed" ); } /* * set face's texture transparency setting */ static int MFace_setTransp( BPy_MFace *self, PyObject *value ) { if( !self->mesh->tface ) return EXPP_ReturnIntError( PyExc_ValueError, "face has no texture values" ); return EXPP_setIValueRange( value, &self->mesh->tface[self->index].transp, TF_SOLID, TF_SUB, 'b' ); } /* * get a face's texture UV values */ static PyObject *MFace_getUV( BPy_MFace * self ) { TFace *face; PyObject *attr; int length, i; if( !self->mesh->tface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no texture values" ); face = &self->mesh->tface[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 values */ static int MFace_setUV( BPy_MFace * self, PyObject * value ) { TFace *face; int length, i; if( !self->mesh->tface ) return EXPP_ReturnIntError( PyExc_ValueError, "face has no texture values" ); if( 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 != PyTuple_Size( value ) ) return EXPP_ReturnIntError( PyExc_TypeError, "size of vertex and UV lists differ" ); face = &self->mesh->tface[self->index]; for( i=0; iuv[i][0] = vector->vec[0]; face->uv[i][1] = vector->vec[1]; } return 0; } /* * get a face's vertex colors. note that if mesh->tfaces 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 && !self->mesh->tface ) return EXPP_ReturnPyObjError( PyExc_ValueError, "face has no vertex colors" ); if( self->mesh->tface ) mcol = (MCol *) self->mesh->tface[self->index].col; else 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; i