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