- New additions to Mesh module

- new methods from NMesh (transform, getFromObject, findEdges)
  - new methods for deleting groups of verts, edges and faces
  - new methods for accessing mesh editing tools: fill, flipNormals,
    recalcNormals, remDoubles, smooth, subdivide, toSphere
- Added PVertType to Types module (not my favorite name; any suggestions?)
This commit is contained in:
Ken Hughes
2005-10-19 21:24:18 +00:00
parent b83f3183c0
commit 7d325f1ed4
6 changed files with 1585 additions and 70 deletions

View File

@@ -40,6 +40,7 @@
#include "DNA_oops_types.h"
#include "DNA_space_types.h"
#include "DNA_curve_types.h"
#include "DNA_meta_types.h"
#include "BDR_editface.h" /* make_tfaces */
#include "BDR_vpaint.h"
@@ -48,7 +49,7 @@
#include "BIF_editdeform.h"
#include "BIF_editkey.h" /* insert_meshkey */
#include "BIF_editview.h"
#include "BIF_space.h" /* allqueue */
#include "BIF_editmesh.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
@@ -63,12 +64,14 @@
#include "BKE_utildefines.h"
#include "BKE_depsgraph.h"
#include "BSE_edit.h" /* for void countall(); */
#include "BKE_curve.h" /* copy_curve(); */
#include "BLI_arithb.h"
#include "BLI_blenlib.h"
#include "blendef.h"
#include "mydevice.h"
#include "butspace.h" /* for mesh tools */
#include "Object.h"
#include "Key.h"
#include "Image.h"
@@ -77,6 +80,8 @@
#include "constant.h"
#include "gen_utils.h"
#define MESH_TOOLS /* add access to mesh tools */
/* EXPP Mesh defines */
#define MESH_SMOOTHRESH 30
@@ -90,6 +95,14 @@
#define MESH_HASMCOL 1
#define MESH_HASVERTUV 2
#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
/************************************************************************
*
* internal utilities
@@ -103,10 +116,8 @@
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 {
@@ -114,6 +125,12 @@ typedef struct SrchFaces {
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
*/
@@ -169,43 +186,35 @@ int mface_comp( const void *va, const void *vb )
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 )
{
allqueue( REDRAWVIEW3D, 1);
Object_updateDag( (void *) mesh );
}
/*
* 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.
*/
static void eeek_fix( MFace *tmpface , int len4 )
{
if( len4 && !tmpface->v4 ) {
unsigned int tmp = tmpface->v1;
tmpface->v1 = tmpface->v4;
tmpface->v4 = tmpface->v3;
tmpface->v3 = tmpface->v2;
tmpface->v2 = tmp;
} else if( !tmpface->v3 ) {
unsigned int tmp = tmpface->v1;
tmpface->v1 = tmpface->v2;
tmpface->v2 = tmpface->v3;
if( !len4 ) {
tmpface->v3 = tmp;
} else {
tmpface->v3 = tmpface->v4;
tmpface->v4 = tmp;
}
}
}
#ifdef CHECK_DVERTS /* not clear if this code is needed */
/*
@@ -239,6 +248,347 @@ static void check_dverts(Mesh *me, int old_totvert)
}
#endif
/*
* 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;
int i;
char state;
MVert *newvert, *srcvert, *dstvert;
int count;
newvert = (MVert *)MEM_mallocN(
sizeof( MVert )*( mesh->totvert-to_delete ), "MVerts" );
/*
* do "smart compaction" of the table; find and copy groups of vertices
* which are not being deleted
*/
dstvert = newvert;
srcvert = mesh->mvert;
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 {
srcvert = mesh->mvert + i;
count = 1;
state = 1;
}
break;
case 1: /* gathering verts */
if( *tmpvert != UINT_MAX ) {
++count;
} else {
if( count ) {
memcpy( dstvert, srcvert, sizeof( MVert ) * count );
dstvert += count;
}
count = 1;
state = 0;
}
}
}
/* if we were gathering verts at the end of the loop, copy those */
if( state && count )
memcpy( dstvert, srcvert, sizeof( MVert ) * count );
/* delete old vertex list, install the new one, update vertex count */
MEM_freeN( mesh->mvert );
mesh->mvert = newvert;
mesh->totvert -= to_delete;
}
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 ) {
MEdge *newedge, *srcedge, *dstedge;
int count, state;
/* allocate new edge list and populate */
newedge = (MEdge *)MEM_mallocN(
sizeof( MEdge )*( mesh->totedge-to_delete ), "MEdges" );
/*
* do "smart compaction" of the edges; find and copy groups of edges
* which are not being deleted
*/
dstedge = newedge;
srcedge = mesh->medge;
tmpedge = srcedge;
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 {
srcedge = tmpedge;
count = 1;
state = 1;
}
break;
case 1: /* gathering edges */
if( tmpedge->v1 != UINT_MAX ) {
++count;
} else {
if( count ) {
memcpy( dstedge, srcedge, sizeof( MEdge ) * count );
dstedge += count;
}
count = 1;
state = 0;
}
}
/* if edge is good, update vertex indices */
}
/* copy any pending good edges */
if( state && count )
memcpy( dstedge, srcedge, sizeof( MEdge ) * count );
/* delete old vertex list, install the new one, update vertex count */
MEM_freeN( mesh->medge );
mesh->medge = newedge;
mesh->totedge -= to_delete;
}
/* 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, TFace *tface, 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 );
if( tface ) {
SWAP( float, tface->uv[0][0], tface->uv[2][0] );
SWAP( float, tface->uv[0][1], tface->uv[2][1] );
SWAP( float, tface->uv[1][0], tface->uv[3][0] );
SWAP( float, tface->uv[1][1], tface->uv[3][1] );
SWAP( unsigned int, tface->col[0], tface->col[2] );
SWAP( unsigned int, tface->col[1], tface->col[3] );
}
}
} 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 );
if( tface ) {
SWAP( float, tface->uv[0][0], tface->uv[1][0] );
SWAP( float, tface->uv[0][1], tface->uv[1][1] );
SWAP( float, tface->uv[2][0], tface->uv[1][0] );
SWAP( float, tface->uv[2][1], tface->uv[1][1] );
SWAP( unsigned int, tface->col[0], tface->col[1] );
SWAP( unsigned int, tface->col[1], tface->col[2] );
}
}
}
static void delete_faces( Mesh *mesh, unsigned int *vert_table, int to_delete )
{
int i;
MFace *tmpface;
TFace *tmptface;
/* if there are faces to delete, handle it */
if( to_delete ) {
MFace *newface, *srcface, *dstface;
TFace *newtface = NULL, *srctface, *dsttface;
char state;
int count;
newface = (MFace *)MEM_mallocN( ( mesh->totface-to_delete )
* sizeof( MFace ), "MFace" );
if( mesh->tface )
newtface = (TFace *)MEM_mallocN( ( mesh->totface-to_delete )
* sizeof( TFace ), "TFace" );
/*
* do "smart compaction" of the faces; find and copy groups of faces
* which are not being deleted
*/
dstface = newface;
srcface = mesh->mface;
tmpface = srcface;
dsttface = newtface;
srctface = mesh->tface;
tmptface = srctface;
count = 0;
state = 1;
for( i = 0; i < mesh->totface; ++i ) {
switch( state ) {
case 0: /* skipping faces */
if( tmpface->v1 == UINT_MAX ) {
++count;
} else {
srcface = tmpface;
srctface = tmptface;
count = 1;
state = 1;
}
break;
case 1: /* gathering faces */
if( tmpface->v1 != UINT_MAX ) {
++count;
} else {
if( count ) {
memcpy( dstface, srcface, sizeof( MFace ) * count );
dstface += count;
if( newtface ) {
memcpy( dsttface, srctface, sizeof( TFace )
* count );
dsttface += count;
}
}
count = 1;
state = 0;
}
}
++tmpface;
++tmptface;
}
/* if we were gathering faces at the end of the loop, copy those */
if ( state && count ) {
memcpy( dstface, srcface, sizeof( MFace ) * count );
if( newtface )
memcpy( dsttface, srctface, sizeof( TFace ) * count );
}
/* delete old face list, install the new one, update face count */
MEM_freeN( mesh->mface );
mesh->mface = newface;
mesh->totface -= to_delete;
if( newtface ) {
MEM_freeN( mesh->tface );
mesh->tface = newtface;
}
}
/* if vertices were deleted, update face's vertices */
if( vert_table ) {
tmpface = mesh->mface;
tmptface = mesh->tface;
for( i = mesh->totface; i--; ) {
int len4 = tmpface->v4;
tmpface->v1 = vert_table[tmpface->v1];
tmpface->v2 = vert_table[tmpface->v2];
tmpface->v3 = vert_table[tmpface->v3];
tmpface->v4 = vert_table[tmpface->v4];
eeek_fix( tmpface, tmptface, len4 );
++tmpface;
if( mesh->tface )
++tmptface;
}
}
}
/*
* 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
@@ -936,8 +1286,8 @@ static PyObject *MVert_CreatePyObject( Mesh *mesh, int i )
static PyObject *PVert_CreatePyObject( MVert *vert )
{
BPy_MVert *obj = PyObject_NEW( BPy_MVert, &PVert_Type );
MVert *newvert;
BPy_MVert *obj = (BPy_MVert *)PyObject_NEW( BPy_MVert, &PVert_Type );
if( !obj )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
@@ -1198,10 +1548,11 @@ static PyObject *MVertSeq_extend( BPy_MVertSeq * self, PyObject *args )
tmpvert = &newvert[mesh->totvert];
for( i = 0; i < len; ++i ) {
float co[3];
tmp = PySequence_Fast_GET_ITEM( args, i );
tmp = PySequence_GetItem( args, i );
if( VectorObject_Check( tmp ) ) {
if( ((VectorObject *)tmp)->size != 3 ) {
MEM_freeN( newvert );
Py_DECREF ( tmp );
Py_DECREF ( args );
return EXPP_ReturnPyObjError( PyExc_ValueError,
"expected vector of size 3" );
@@ -1225,10 +1576,12 @@ static PyObject *MVertSeq_extend( BPy_MVertSeq * self, PyObject *args )
if( !ok ) {
MEM_freeN( newvert );
Py_DECREF ( args );
Py_DECREF ( tmp );
return EXPP_ReturnPyObjError( PyExc_ValueError,
"expected tuple triplet of floats" );
}
}
Py_DECREF ( tmp );
/* add the coordinate to the new list */
memcpy( tmpvert->co, co, sizeof(co) );
@@ -1270,7 +1623,7 @@ static PyObject *MVertSeq_extend( BPy_MVertSeq * self, PyObject *args )
/* add data for new vertices */
fp = (float *)((char *)currkey->data +
mesh->key->elemsize*mesh->totvert );
(mesh->key->elemsize*mesh->totvert));
tmpvert = mesh->mvert + mesh->totvert;
for( i = newlen - mesh->totvert; i > 0; --i ) {
VECCOPY(fp, tmpvert->co);
@@ -1294,9 +1647,100 @@ static PyObject *MVertSeq_extend( BPy_MVertSeq * self, PyObject *args )
return EXPP_incr_ret( Py_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;
Py_INCREF( args ); /* so we can safely DECREF later */
/* accept a sequence (lists or tuples) also */
if( PySequence_Size( args ) == 1 ) {
PyObject *tmp = PyTuple_GET_ITEM( args, 0 );
if( PySequence_Check ( tmp ) ) {
Py_DECREF( args ); /* release previous reference */
args = tmp; /* PyTuple_GET_ITEM returns new ref */
}
}
/* 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( args );
Py_DECREF( tmp );
return EXPP_ReturnPyObjError( PyExc_ValueError,
"MVert belongs to a different mesh" );
}
index = ((BPy_MVert*)tmp)->index;
}
else if( PyInt_CheckExact( tmp ) )
index = PyInt_AsLong ( tmp );
else {
MEM_freeN( vert_table );
Py_DECREF( args );
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 );
Py_DECREF( args );
return EXPP_ReturnPyObjError( PyExc_ValueError,
"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 ||
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_DECREF( args );
return EXPP_incr_ret( Py_None );
}
static struct PyMethodDef BPy_MVertSeq_methods[] = {
{"extend", (PyCFunction)MVertSeq_extend, METH_VARARGS,
"add vertices to mesh"},
{"delete", (PyCFunction)MVertSeq_delete, METH_VARARGS,
"delete vertices to mesh"},
{NULL, NULL, 0, NULL}
};
@@ -1740,6 +2184,7 @@ static PyObject *MEdgeSeq_item( BPy_MEdgeSeq * self, int i )
return MEdge_CreatePyObject( self->mesh, i );
}
static PySequenceMethods MEdgeSeq_as_sequence = {
( inquiry ) MEdgeSeq_len, /* sq_length */
( binaryfunc ) 0, /* sq_concat */
@@ -1841,11 +2286,12 @@ static PyObject *MEdgeSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
/* 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 );
tmp = PySequence_GetItem( args, i );
/* not a tuple of MVerts... error */
if( !PyTuple_Check( tmp ) ||
EXPP_check_sequence_consistency( tmp, &MVert_Type ) != 1 ) {
Py_DECREF( tmp );
Py_DECREF( args );
return EXPP_ReturnPyObjError( PyExc_ValueError,
"expected sequence of MVert tuples" );
@@ -1854,10 +2300,13 @@ static PyObject *MEdgeSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
/* not the right number of MVerts... error */
nverts = PyTuple_Size( tmp );
if( nverts < 2 || nverts > 4 ) {
Py_DECREF( tmp );
Py_DECREF( args );
return EXPP_ReturnPyObjError( PyExc_ValueError,
"expected 2 to 4 MVerts per tuple" );
}
Py_DECREF( tmp );
if( nverts == 2 )
++new_edge_count; /* if only two vert, then add only edge */
else
@@ -1872,12 +2321,13 @@ static PyObject *MEdgeSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
len = PySequence_Size( args );
tmppair = newpair;
for( i = 0; i < len; ++i ) {
tmp = PySequence_Fast_GET_ITEM( args, i );
tmp = PySequence_GetItem( 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 );
Py_DECREF( tmp );
if( nverts == 2 )
nverts = 1; /* again, two verts give just one edge */
@@ -2016,9 +2466,154 @@ static PyObject *MEdgeSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
return EXPP_incr_ret( Py_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;
Py_INCREF( args ); /* so we can safely DECREF later */
/* accept a sequence (lists or tuples) also */
if( PySequence_Size( args ) == 1 ) {
PyObject *tmp = PyTuple_GET_ITEM( args, 0 );
if( PySequence_Check ( tmp ) ) {
Py_DECREF( args ); /* release previous reference */
args = tmp; /* PyTuple_GET_ITEM returns new ref */
}
}
/* see how many args we need to parse */
len = PySequence_Size( args );
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_CheckExact( tmp ) )
edge_table[i] = PyInt_AsLong ( tmp );
else {
MEM_freeN( edge_table );
Py_DECREF( tmp );
Py_DECREF( args );
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 );
Py_DECREF( args );
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 );
Py_DECREF( args );
mesh_update ( mesh );
return EXPP_incr_ret( Py_None );
}
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"},
{NULL, NULL, 0, NULL}
};
@@ -2297,7 +2892,7 @@ static int MFace_setImage( BPy_MFace *self, PyObject *value )
}
/*
* get face's texture flags
* get face's texture flag
*/
static PyObject *MFace_getFlag( BPy_MFace *self )
@@ -2318,7 +2913,7 @@ static PyObject *MFace_getFlag( BPy_MFace *self )
}
/*
* set face's texture flags
* set face's texture flag
*/
static int MFace_setFlag( BPy_MFace *self, PyObject *value )
@@ -2949,7 +3544,7 @@ static PyObject *MFaceSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
/* verify the param list and get a total count of number of edges */
new_face_count = 0;
for( i = 0; i < len; ++i ) {
tmp = PySequence_Fast_GET_ITEM( args, i );
tmp = PySequence_GetItem( args, i );
/* not a tuple of MVerts... error */
if( !PyTuple_Check( tmp ) ||
@@ -2983,12 +3578,20 @@ static PyObject *MFaceSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
MFace tmpface;
unsigned int vert[4]={0,0,0,0};
unsigned char order[4]={0,1,2,3};
tmp = PySequence_Fast_GET_ITEM( args, i );
tmp = PySequence_GetItem( args, i );
nverts = PyTuple_Size( tmp );
if( nverts == 2 ) /* again, ignore 2-vert tuples */
break;
/* get copies of vertices */
#if 0
for( j = 0; j < nverts; ++j ) {
BPy_MVert *e = (BPy_MVert *)PyTuple_GET_ITEM( tmp, j );
vert[j] = e->index;
}
#endif
/*
* go through some contortions to guarantee the third and fourth
* vertices are not index 0
@@ -3004,7 +3607,9 @@ static PyObject *MFaceSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
e = (BPy_MVert *)PyTuple_GET_ITEM( tmp, 3 );
tmpface.v4 = e->index;
}
eeek_fix( &tmpface, nverts==4 );
Py_DECREF( tmp );
eeek_fix( &tmpface, NULL, nverts==4 );
vert[0] = tmpface.v1;
vert[1] = tmpface.v2;
vert[2] = tmpface.v3;
@@ -3150,6 +3755,7 @@ static PyObject *MFaceSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
tmpface->v2 = tmppair->v[index[1]];
tmpface->v3 = tmppair->v[index[2]];
tmpface->v4 = tmppair->v[index[3]];
tmpface->flag = 0;
mesh->totface++;
++tmpface;
@@ -3166,9 +3772,218 @@ static PyObject *MFaceSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
return EXPP_incr_ret( Py_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" );
/* see how many args we need to parse */
len = PySequence_Size( args );
if( len < 1 ) {
Py_DECREF( args );
return EXPP_ReturnPyObjError( PyExc_TypeError,
"sequence must contain at least one int or MFace" );
}
face_table = (unsigned int *)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_MEdge_Check( tmp ) )
face_table[i] = ((BPy_MEdge *)tmp)->index;
else if( PyInt_CheckExact( tmp ) )
face_table[i] = PyInt_AsLong ( tmp );
else {
MEM_freeN( face_table );
Py_DECREF( tmp );
Py_DECREF( args );
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 );
Py_DECREF( args );
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 face, deselect each edge */
tmpface = mesh->mface;
fface = (struct fourEdges *)face_edges;
for( i = mesh->totface; i--; ++tmpface, ++fface ) {
if( tmpface->v1 != UINT_MAX ) {
FaceEdges (*face)[4];
face = (void *)face_edges;
face += face_table[i];
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 );
Py_DECREF( args );
mesh_update ( mesh );
return EXPP_incr_ret( Py_None );
}
static struct PyMethodDef BPy_MFaceSeq_methods[] = {
{"extend", (PyCFunction)MFaceSeq_extend, METH_VARARGS,
"add faces and edges to mesh"},
"add faces to mesh"},
{"delete", (PyCFunction)MFaceSeq_delete, METH_VARARGS,
"delete faces to mesh"},
{NULL, NULL, 0, NULL}
};
@@ -3316,15 +4131,350 @@ static PyObject *Mesh_Update( BPy_Mesh * self )
return EXPP_incr_ret( Py_None );
}
// #define MESH_TOOLS
/*
* 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;
}
return EXPP_incr_ret( Py_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_CheckExact( v1 ) && PyInt_CheckExact( 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;
char *name;
ID tmpid;
Mesh *tmpmesh;
Object *tmpobj = NULL;
if( !PyArg_ParseTuple( args, "s", &name ) )
return EXPP_ReturnPyObjError( PyExc_TypeError,
"expected string argument" );
/* find the specified object */
ob = ( Object * ) GetIdFromList( &( G.main->object ), name );
if( !ob )
return EXPP_ReturnPyObjError( PyExc_AttributeError, name );
/* perform the mesh extraction based on type */
switch (ob->type) {
case OB_FONT:
case OB_CURVE:
case OB_SURF:
tmpobj = alloc_libblock( &( G.main->object ), ID_OB, "i_tmp" );
tmpobj->id.us = 1;
tmpobj->flag = 0;
tmpobj->type = ob->type;
tmpobj->data = copy_curve( (Curve *) ob->data );
makeDispListCurveTypes( tmpobj, 0 );
nurbs_to_mesh( tmpobj );
tmpmesh = tmpobj->data;
free_libblock_us( &G.main->object, tmpobj );
break;
case OB_MBALL:
ob = find_basis_mball( ob );
tmpmesh = add_mesh();
mball_to_mesh( &ob->disp, tmpmesh );
break;
case OB_MESH:
tmpmesh = copy_mesh( (Mesh *) ob->data );
tmpmesh->id.us = 0;
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;
/* remove the temporary mesh */
BLI_remlink( &G.main->mesh, tmpmesh );
MEM_freeN( tmpmesh );
mesh_update( self->mesh );
return EXPP_incr_ret( Py_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 )
{
Mesh *mesh = self->mesh;
MVert *mv;
PyObject *ob1 = NULL;
MatrixObject *mat;
int i, recalc_normals = 0;
if( !PyArg_ParseTuple( args, "O!|i", &matrix_Type, &ob1, &recalc_normals ) )
return ( EXPP_ReturnPyObjError( PyExc_TypeError,
"expected matrix and optionally an int as arguments" ) );
mat = ( MatrixObject * ) ob1;
if( mat->colSize != 4 || mat->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 */
mv = mesh->mvert;
for( i = 0; i < mesh->totvert; i++, mv++ )
Mat4MulVecfl( (float(*)[4])*mat->matrix, mv->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];
/*
* 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])*mat->matrix))
return EXPP_ReturnPyObjError (PyExc_AttributeError,
"given matrix is not invertible");
/*
* since normal is stored as shorts, convert to float
*/
mv = mesh->mvert;
for( i = 0; i < mesh->totvert; i++, mv++ ) {
float vec[3];
vec[0] = (float)mv->no[0] / 32767.0;
vec[1] = (float)mv->no[1] / 32767.0;
vec[2] = (float)mv->no[2] / 32767.0;
Mat4MulVecfl( (float(*)[4])*invmat, vec );
Normalise( vec );
mv->no[0] = (short)(vec[0] * 32767.0);
mv->no[1] = (short)(vec[1] * 32767.0);
mv->no[2] = (short)(vec[2] * 32767.0);
}
}
return EXPP_incr_ret( Py_None );
}
#ifdef MESH_TOOLS
static PyObject *Mesh_Tools( BPy_Mesh * self, int type )
static PyObject *Mesh_Tools( BPy_Mesh * self, int type, void **args )
{
Base *base;
int result;
Object *object = NULL;
Base *basact = BASACT;
Base *base = FIRSTBASE;
PyObject *attr = NULL;
/* if already in edit mode, exit */
@@ -3352,15 +4502,38 @@ static PyObject *Mesh_Tools( BPy_Mesh * self, int type )
G.obedit = object;
enter_editmode( );
switch( type ) {
case B_SUBDIV:
esubdivideflag(1, 0.0,
G.scene->toolsettings->editbutflag & B_BEAUTY,1,0);
case MESH_TOOL_TOSPHERE:
vertices_to_sphere();
break;
case B_VERTEXSMOOTH:
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, *((float *)args[0]) );
attr = PyInt_FromLong( result );
if( !attr )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"PyInt_FromLong() failed" );
case MESH_TOOL_FILL:
fill_mesh();
break;
case MESH_TOOL_RECALCNORM:
righthandfaces( *((int *)args[0]) );
break;
}
exit_editmode( 1 );
if( attr )
return attr;
return EXPP_incr_ret( Py_None );
}
@@ -3368,18 +4541,93 @@ static PyObject *Mesh_Tools( BPy_Mesh * self, int type )
* "Subdivide" function
*/
static PyObject *Mesh_Subdivide( BPy_Mesh * self )
static PyObject *Mesh_subdivide( BPy_Mesh * self, PyObject * args )
{
return Mesh_Tools( self, B_SUBDIV );
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, &params );
}
/*
* "Smooth" function
*/
static PyObject *Mesh_Smooth( BPy_Mesh * self )
static PyObject *Mesh_smooth( BPy_Mesh * self )
{
return Mesh_Tools( self, B_VERTEXSMOOTH );
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, &params );
}
/*
* "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, &params );
}
/*
* "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 );
}
#endif
@@ -3389,13 +4637,29 @@ static struct PyMethodDef BPy_Mesh_methods[] = {
"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_NOARGS,
"Update display lists after changes to mesh"},
{"transform", (PyCFunction)Mesh_transform, METH_VARARGS,
"Applies a transformation matrix to mesh's vertices"},
#ifdef MESH_TOOLS
{"subdivide", (PyCFunction)Mesh_Subdivide, METH_NOARGS,
"Subdivide selected edges in a mesh (experimental)"},
{"smooth", (PyCFunction)Mesh_Smooth, METH_NOARGS,
{"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)"},
{"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)"},
#endif
{NULL, NULL, 0, NULL}
};
@@ -3413,6 +4677,56 @@ static PyObject *Mesh_getVerts( BPy_Mesh * self )
return (PyObject *)seq;
}
static int Mesh_setVerts( BPy_Mesh * self, PyObject * args )
{
static int disabled = 0;
MVert *dst;
MVert *src;
char err[256];
int i;
if( disabled ) {
sprintf( err, "attribute 'verts' of '%s' objects is not writable",
self->ob_type->tp_name );
return EXPP_ReturnIntError( PyExc_TypeError, err );
}
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 *Mesh_getEdges( BPy_Mesh * self )
{
BPy_MEdgeSeq *seq = PyObject_NEW( BPy_MEdgeSeq, &MEdgeSeq_Type);
@@ -3715,7 +5029,7 @@ static PyObject *Mesh_repr( BPy_Mesh * self )
/*****************************************************************************/
static PyGetSetDef BPy_Mesh_getseters[] = {
{"verts",
(getter)Mesh_getVerts, (setter)NULL,
(getter)Mesh_getVerts, (setter)Mesh_setVerts,
"The mesh's vertices (MVert)",
NULL},
{"edges",
@@ -3921,6 +5235,7 @@ static PyObject *M_Mesh_Get( PyObject * self, PyObject * args )
static PyObject *M_Mesh_New( PyObject * self, PyObject * args )
{
char *name = "Mesh";
PyObject *ret = NULL;
Mesh *mesh;
BPy_Mesh *obj;
char buf[21];
@@ -3969,7 +5284,7 @@ static PyObject *M_Mesh_MVert( PyObject * self, PyObject * args )
*/
if( PyTuple_Size ( args ) == 1 ) {
PyObject *tmp = PySequence_Fast_GET_ITEM( args, 0 );
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" );

View File

@@ -52,19 +52,19 @@ extern PyTypeObject Mesh_Type;
extern PyTypeObject MVert_Type;
extern PyTypeObject PVert_Type;
extern PyTypeObject MVertSeq_Type;
extern PyTypeObject MEdge_Type;
extern PyTypeObject MFace_Type;
extern PyTypeObject MCol_Type;
extern PyTypeObject MEdge_Type;
struct BPy_Object;
/* Type checking for EXPP PyTypes */
#define BPy_Mesh_Check(v) ((v)->ob_type == &Mesh_Type)
#define BPy_MFace_Check(v) ((v)->ob_type == &MFace_Type)
#define BPy_MEdge_Check(v) ((v)->ob_type == &MEdge_Type)
#define BPy_MVert_Check(v) ((v)->ob_type == &MVert_Type)
#define BPy_PVert_Check(v) ((v)->ob_type == &PVert_Type)
#define BPy_MCol_Check(v) ((v)->ob_type == &MCol_Type)
#define BPy_MEdge_Check(v) ((v)->ob_type == &MEdge_Type)
/* Typedefs for the new types */
@@ -87,7 +87,7 @@ typedef struct {
typedef struct {
PyObject_VAR_HEAD /* required python macro */
Mesh * mesh;
Mesh *mesh; /* points to a Mesh */
int index;
int iter;
} BPy_MEdge; /* a Mesh edge */

View File

@@ -71,6 +71,7 @@ void types_InitAll( void )
Mesh_Type.ob_type = &PyType_Type;
MFace_Type.ob_type = &PyType_Type;
MVert_Type.ob_type = &PyType_Type;
PVert_Type.ob_type = &PyType_Type;
MEdge_Type.ob_type = &PyType_Type;
MCol_Type.ob_type = &PyType_Type;
Mesh_Type.ob_type = &PyType_Type;
@@ -130,6 +131,8 @@ PyObject *Types_Init( void )
( PyObject * ) &MEdge_Type );
PyDict_SetItemString( dict, "MVertType",
( PyObject * ) &MVert_Type );
PyDict_SetItemString( dict, "PVertType",
( PyObject * ) &PVert_Type );
PyDict_SetItemString( dict, "MColType", ( PyObject * ) &MCol_Type );
PyDict_SetItemString( dict, "ArmatureType",

View File

@@ -45,7 +45,8 @@ extern PyTypeObject Image_Type, Ipo_Type, IpoCurve_Type;
extern PyTypeObject Lamp_Type, Lattice_Type;
extern PyTypeObject Material_Type, Metaball_Type, MTex_Type;
extern PyTypeObject NMFace_Type, NMVert_Type, NMCol_Type, NMesh_Type;
extern PyTypeObject MFace_Type, MVert_Type, MEdge_Type, MCol_Type, Mesh_Type;
extern PyTypeObject MFace_Type, MVert_Type, PVert_Type, MEdge_Type, MCol_Type,
Mesh_Type;
extern PyTypeObject Object_Type;
extern PyTypeObject Particle_Type;
extern PyTypeObject Scene_Type, RenderData_Type;

View File

@@ -3,6 +3,24 @@
"""
The Blender.Mesh submodule.
B{New}:
- L{transform()<Mesh.transform>}: apply transform matrix to mesh vertices
- L{getFromObject()<Mesh.getFromObject>}: get mesh data from other
geometry objects
- L{findEdges()<Mesh.findEdges>}: determine if and where edges exist in the
mesh's edge list
- delete methods for L{verts<MVertSeq.delete>}, L{edges<MEdgeSeq.delete>}
and L{faces<MFaceSeq.delete>}
- new experimental mesh tools:
L{fill()<Mesh.Mesh.fill>},
L{flipNormals()<Mesh.Mesh.flipNormals>},
L{recalcNormals()<Mesh.Mesh.recalcNormals>},
L{remDoubles()<Mesh.Mesh.remDoubles>},
L{smooth()<Mesh.Mesh.smooth>},
L{subdivide()<Mesh.Mesh.subdivide>} and
L{toSphere()<Mesh.Mesh.toSphere>}
- and if you're never used Mesh before, everything!
Mesh Data
=========
@@ -82,14 +100,15 @@ class MVert:
if vertex coordinates are changed, it may be necessary to use
L{Mesh.calcNormals()} to update the vertex normals.
@type no: vector
@ivar uvco: The vertex texture "sticky" coordinates (x, y), if present.
@ivar uvco: (MVerts only). The vertex texture "sticky" coordinates (x, y),
if present.
Use L{Mesh.vertexUV} to test for presence before trying to access;
otherwise an exception will may be thrown.
(Sticky coordinates can be set when the object is in the Edit mode;
from the Editing Panel (F9), look under the "Mesh" properties for the
"Sticky" button).
@type uvco: vector
@ivar index: The vertex's index within the mesh. Read-only.
@ivar index: (MVerts only). The vertex's index within the mesh. Read-only.
@type index: int
@ivar sel: The vertex's selection state (selected=1).
B{Note}: a Mesh will return the selection state of the mesh when EditMode
@@ -107,15 +126,29 @@ class MVert:
def __init__(coord):
"""
Create a new MVert object.
Create a new PVert object.
@note: PVert-type objects are designed to be used for creating and
modifying a mesh's vertex list, but since they do not "wrap" any Blender
data there are some differences. The B{index} and B{uvco} attributes
are not defined for PVerts, and the B{no} attribute contains valid
data only if the PVert was created from an MVert (using a slice
operation on the mesh's vertex list.) PVerts also cannot be used as an
argument to any method which expects data wrapping a Blender mesh, such
as L{MVertSeq.delete()}.
Example::
v = Blender.Mesh.MVert(1,0,0)
v = Blender.Mesh.MVert(Blender.Mathutils.Vector([1,0,0]))
m = Blender.Mesh.Get('Mesh')
vlist = m.verts[:] # slice operation also returns PVerts
@type coord: three floats or a Vector object
@param coord: the coordinate values for the new vertex
@rtype: MVert
@return: a new MVert object
@rtype: PVert
@return: a new PVert object
"""
class MVertSeq:
@@ -128,7 +161,7 @@ class MVertSeq:
a MVert object which "wraps" the actual vertex in the mesh; changing any
of the vertex's attributes will immediately change the data in the mesh.
When a slice of the vertex list is accessed, however, the operator[]
returns a list of MVert objects which are copies of the mesh's vertex
returns a list of PVert objects which are copies of the mesh's vertex
data. Changes to these objects have no effect on the mesh; they must be
assigned back to the mesh's vertex list.
@@ -173,6 +206,20 @@ class MVertSeq:
- a sequence (list or tuple) of either of the above.
"""
def delete(verts):
"""
Deletes one or more vertices from the mesh. Any edge or face which
uses the specified vertices are also deleted.
@type verts: multiple ints or MVerts
@param verts: can be
- a single MVert belonging to the mesh (B{note:} will not work with
PVerts)
- a single integer, specifying an index into the mesh's vertex list
- a sequence (list or tuple) containing two or more of either of
the above.
"""
class MEdge:
"""
The MEdge object
@@ -228,6 +275,20 @@ class MEdgeSeq:
of tuples each containing two to four MVerts.
"""
def delete(edges):
"""
Deletes one or more edges from the mesh. In addition, also delete:
- any faces which uses the specified edge(s)
- any "orphan" vertices (belonging only to specified edge(s))
@type edges: multiple ints or MEdges
@param edges: can be
- a single MEdge belonging to the mesh
- a single integer, specifying an index into the mesh's edge list
- a sequence (list or tuple) containing two or more of either of
the above.
"""
class MFace:
"""
The MFace object
@@ -383,6 +444,20 @@ class MFaceSeq:
of tuples each containing two to four MVerts.
"""
def delete(deledges, faces):
"""
Deletes one or more faces (and optionally the edges associated with
the face(s)) from the mesh.
@type deledges: int
@param deledges: controls whether just the faces (deledges=0)
or the faces and edges (deledges=1) are deleted. These correspond to the
"Only Faces" and "Edges & Faces" options in the Edit Mode pop-up menu
@type faces: multiple ints or MFaces
@param faces: a sequence (list or tuple) containing one or more of:
- an MEdge belonging to the mesh
- a integer, specifying an index into the mesh's face list
"""
class Mesh:
"""
@@ -433,11 +508,63 @@ class Mesh:
@type activeFace: int
"""
def getFromObject(name):
"""
Replace the mesh's existing data with the raw mesh data from a Blender
Object. This method support all the geometry based objects (mesh, text,
curve, surface, and meta).
@note: The mesh coordinates are in i{local space}, not the world space of
its object. For world space vertex coordinates, each vertex location must
be multiplied by the object's 4x4 transform matrix (see L{transform}).
@type name: string
@param name: name of the Blender object which contains the geometry data.
"""
def calcNormals():
"""
Recalculates the vertex normals using face data.
"""
def transform(matrix, recalc_normals = False):
"""
Transforms the mesh by the specified 4x4 matrix (such as returned by
L{Object.Object.getMatrix}). The matrix should be invertible.
Ideal usage for this is exporting to an external file where
global vertex locations are required for each object.
Sometimes external renderers or file formats do not use vertex normals.
In this case, you can skip transforming the vertex normals by leaving
the optional parameter recalc_normals as False or 0 (the default value).
Example::
# This script outputs deformed meshes worldspace vertex locations
# for a selected object without changing the object
import Blender
from Blender import Mesh, Object
ob = Object.GetSelected()[0] # Get the first selected object
me = Mesh.New() # Create a new mesh
me.getFromObject(ob.name) # Get the object's mesh data
verts = me.verts[:] # Save a copy of the vertices
me.transform(ob.matrix) # Convert verts to world space
for v in me.verts:
print 'worldspace vert', v.co
me.verts = verts # Restore the original verts
@type matrix: Py_Matrix
@param matrix: 4x4 Matrix which can contain location, scale and rotation.
@type recalc_normals: int
@param recalc_normals: if True or 1, also transform vertex normals.
@warn: unlike L{NMesh.transform()<NMesh.NMesh.transform>}, this method
I{will immediately modify the mesh data} when it is used. If you
transform the mesh using the object's matrix to get the vertices'
world positions, the result will be a "double transform". To avoid
this you either need to set the object's matrix to the identity
matrix, perform the inverse transform after outputting the transformed
vertices, or make a copy of the vertices prior to using this method
and restore them after outputting the transformed vertices (as shown
in the example).
"""
def vertexShade(object):
"""
Colors vertices based on the current lighting setup, like when there
@@ -456,3 +583,69 @@ class Mesh:
releases.
"""
def findEdges(edges):
"""
Quickly search for the location of an edge.
@type edges: tuple(s) of ints or MVerts
@param edges: can be tuples of MVerts or integer indexes (B{note:} will
not work with PVerts) or a sequence (list or tuple) containing two or
tuples.
@rtype: int, None or list
@return: if an edge is found, its index is returned; otherwise None is
returned. If a sequence of edges is passed, a list is returned.
"""
def smooth():
"""
Flattens angle of selected faces. Experimental mesh tool.
An exception is thrown if called while in EditMode.
"""
def flipNormals():
"""
Toggles the direction of selected face's normals. Experimental mesh tool.
An exception is thrown if called while in EditMode.
"""
def toSphere():
"""
Moves selected vertices outward in a spherical shape. Experimental mesh
tool.
An exception is thrown if called while in EditMode.
"""
def subdivide(beauty=0):
"""
Subdivide selected edges in a mesh. Experimental mesh tool.
An exception is thrown if called while in EditMode.
@type beauty: int
@param beauty: specifies whether a "beauty" subdivide should be
enabled (disabled is default). Value must be in the range [0,1].
"""
def remDoubles(limit):
"""
Removes duplicates from selected vertices. Experimental mesh tool.
An exception is thrown if called while in EditMode.
@type limit: float
@param limit: specifies the maximum distance considered for vertices
to be "doubles". Value is clamped to the range [0.0,1.0].
@rtype: int
@return: the number of vertices deleted
"""
def fill():
"""
Scan fill a closed selected edge loop. Experimental mesh tool.
An exception is thrown if called while in EditMode.
"""
def recalcNormals(direction=0):
"""
Recalculates inside or outside normals for selected faces. Experimental
mesh tool.
An exception is thrown if called while in EditMode.
@type direction: int
@param direction: specifies outward (0) or inward (1) normals. Outward
is the default. Value must be in the range [0,1].
"""

View File

@@ -36,7 +36,10 @@ Example::
@var MFaceType: Blender MFace. A mesh face, with
three (a triangular face) or four (a quad face) vertices.
@var MEdgeType: Blender MEdge. A mesh edge, with two vertices
@var MVertType: Blender MVert. A mesh vertex.
@var MVertType: Blender MVert. A mesh vertex which wraps a Blender mesh vertex
(typically an object returned from the mesh.verts sequence).
@var PVertType: Blender MVert. A mesh vertex which does not wrap a Blender
mesh vertex (returned from L{Blender.Mesh.MVert()<Mesh.MVert.__init__>}).
@var MColType: Blender MCol. A mesh rgba color.
@var ArmatureType: Blender Armature. The "skeleton", for animating and deforming
objects.