/* 
 * $Id: NMesh.c 16942 2008-10-06 06:10:14Z campbellbarton $
 *
 * ***** 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, but it borrows all the old NMesh code.
 *
 * Contributor(s): Willian P. Germano, Jordi Rovira i Bonet, Joseph Gilbert,
 * Bala Gi, Alexander Szakaly, Stephane Soppera, Campbell Barton, Ken Hughes,
 * Daniel Dunbar.
 *
 * ***** END GPL LICENSE BLOCK *****
 */

#include "NMesh.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_space_types.h"
#include "DNA_curve_types.h"

#include "BDR_editface.h"	/* make_tfaces */
#include "BDR_vpaint.h"
#include "BDR_editobject.h"

#include "BIF_editdeform.h"
#include "BIF_editkey.h"	/* insert_meshkey */
#include "BIF_editview.h"
#include "BIF_space.h"

#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
#include "BKE_material.h"
#include "BKE_main.h"
#include "BKE_global.h"
#include "BKE_library.h"
#include "BKE_DerivedMesh.h"
#include "BKE_displist.h"
#include "BKE_object.h"
#include "BKE_mball.h"
#include "BKE_utildefines.h"
#include "BKE_depsgraph.h"
#include "BKE_idprop.h"

#include "BLI_arithb.h"
#include "BLI_blenlib.h"

#include "blendef.h"
#include "mydevice.h"
#include "Object.h"
#include "Key.h"
#include "Mathutils.h"
#include "IDProp.h"
#include "constant.h"
#include "gen_utils.h"
#include "gen_library.h"

extern void countall(void);

/* EXPP Mesh defines */

#define NMESH_FRAME_MAX				30000
#define NMESH_SMOOTHRESH			30
#define NMESH_SMOOTHRESH_MIN			1
#define NMESH_SMOOTHRESH_MAX			80
#define NMESH_SUBDIV				1
#define NMESH_SUBDIV_MIN			0
#define NMESH_SUBDIV_MAX			6

static int unlink_existingMeshData( Mesh * mesh );
static int convert_NMeshToMesh( Mesh *mesh, BPy_NMesh *nmesh );
static void check_dverts(Mesh *me, int old_totverts);
static PyObject *NMesh_printDebug( PyObject * self );
static PyObject *NMesh_addEdge( PyObject * self, PyObject * args );
static PyObject *NMesh_findEdge( PyObject * self, PyObject * args );
static PyObject *NMesh_removeEdge( PyObject * self, PyObject * args );
static PyObject *NMesh_addFace( PyObject * self, PyObject * args );
static PyObject *NMesh_removeFace( PyObject * self, PyObject * args );
static PyObject *NMesh_addVertGroup( PyObject * self, PyObject * args );
static PyObject *NMesh_removeVertGroup( PyObject * self, PyObject * args );
static PyObject *NMesh_assignVertsToGroup( PyObject * self, PyObject * args );
static PyObject *NMesh_removeVertsFromGroup( PyObject * self,PyObject * args );
static PyObject *NMesh_getVertsFromGroup( PyObject * self, PyObject * args );
static PyObject *NMesh_renameVertGroup( PyObject * self, PyObject * args );
static PyObject *NMesh_getVertGroupNames( PyObject * self );
static PyObject *NMesh_transform (PyObject *self, PyObject *args);

static char NMesh_printDebug_doc[] =
  "print debug info about the mesh.";

static char NMesh_getKey_doc[] =
	"get the Key object linked to this mesh";

static char NMesh_addEdge_doc[] =
  "create an edge between two vertices.\n\
If an edge already exists between those vertices, it is returned.\n\
(In Blender, only zero or one edge can link two vertices.)\n\
Created edge is automatically added to edges list.";

static char NMesh_findEdge_doc[] =
  "find an edge between two vertices.";

static char NMesh_removeEdge_doc[] =
  "remove an edge between two vertices.\n\
All faces using this edge are removed from faces list.";

static char NMesh_addFace_doc[] =
  "add a face to face list and add to edge list (if edge data exists) necessary edges.";

static char NMesh_removeFace_doc[] =
  "remove a face for face list and remove edges no more used by any other face (if \
edge data exists).";

static char NMesh_addVertGroup_doc[] =
	"add a named and empty vertex(deform) Group to a mesh that has been linked\n\
to an object. ";

static char NMesh_removeVertGroup_doc[] =
	"remove a named vertex(deform) Group from a mesh that has been linked\n\
to an object.  Will remove all verts assigned to group.";

static char NMesh_assignVertsToGroup_doc[] =
	"Adds an array (a python list) of vertex points (by index) to a named\n\
vertex group.  The list will have an associated wieght assigned to them.\n\
The weight represents the amount of influence this group has over these\n\
vertex points. Weights should be in the range of 0.0 - 1.0.\n\
The assignmode can be either 'add', 'subtract', or 'replace'.  If this vertex\n\
is not assigned to the group 'add' creates a new association with the weight\n\
specified, otherwise the weight given is added to the current weight of the\n\
vertex.\n\
'subtract' will attempt to subtract the weight passed from a vertex already\n\
associated with a group, else it does nothing. 'replace' attempts to replace\n\
the weight with the new weight value for an already associated vertex/group,\n\
else it does nothing. The mesh must have all it's vertex points set before\n\
attempting to assign any vertex points to a vertex group.";

static char NMesh_removeVertsFromGroup_doc[] =
	"Remove an array (a python list) of vertex points from a named group in a\n\
mesh that has been linked to an object. If no list is given this will remove\n\
all vertex point associations with the group passed";

static char NMesh_getVertsFromGroup_doc[] =
	"By passing a python list of vertex indices and a named group, this will\n\
return a python list representing the indeces that are a part of this vertex.\n\
group. If no association was found for the index passed nothing will be\n\
return for the index. An optional flag will also return the weights as well";

static char NMesh_renameVertGroup_doc[] = "Renames a vertex group";

static char NMesh_getVertGroupNames_doc[] =
	"Returns a list of all the vertex group names";

static char M_NMesh_doc[] = "The Blender.NMesh submodule";

static char M_NMesh_Col_doc[] = "([r, g, b, a]) - Get a new mesh color\n\n\
[r=255, g=255, b=255, a=255] Specify the color components";

static char M_NMesh_Face_doc[] =
	"(vertexlist = None) - Get a new face, and pass optional vertex list";

static char NMFace_append_doc[] =
	"(vert) - appends Vertex 'vert' to face vertex list";

static char M_NMesh_Vert_doc[] = "([x, y, z]) - Get a new vertex\n\n\
[x, y, z] Specify new coordinates";

static char NMesh_getMaterials_doc[] =
	"(i = -1) - Get this mesh's list of materials.\n\
(i = -1) - int: determines the list's contents:\n\
-1: return the current list, possibly modified by the script (default);\n\
 0: get a fresh list from the Blender mesh -- modifications not included,\n\
    unless the script called mesh.update() first;\n\
 1: like 0, but does not ignore empty slots, returns them as 'None'.";

static char NMesh_setMaterials_doc[] =
	"(matlist) - Set this mesh's list of materials.  This method makes sure\n\
the passed matlist is valid (can only include up to 16 materials and None's).";

static char NMesh_addMaterial_doc[] =
	"(material) - add a new Blender Material 'material' to this Mesh's materials\n\
list.";

static char NMesh_insertKey_doc[] =
	"(frame = None, type = 'relative') - inserts a Mesh key at the given frame\n\
if called without arguments, it inserts the key at the current Scene frame.\n\
(type) - 'relative' or 'absolute'.  Only relevant on the first call to this\n\
function for each nmesh.";

static char NMesh_removeAllKeys_doc[] =
	"() - removes all keys from this mesh\n\
returns True if successful or False if this NMesh wasn't linked to a real\n\
Blender Mesh yet or the Mesh had no keys";

static char NMesh_getSelectedFaces_doc[] =
	"(flag = None) - returns list of selected Faces\n\
If flag = 1, return indices instead";

static char NMesh_getActiveFace_doc[] =
	"returns the index of the active face ";

static char NMesh_hasVertexUV_doc[] =
	"(flag = None) - returns 1 if Mesh has per vertex UVs ('Sticky')\n\
The optional argument sets the Sticky flag";

static char NMesh_hasFaceUV_doc[] =
	"(flag = None) - returns 1 if Mesh has textured faces\n\
The optional argument sets the textured faces flag";

static char NMesh_hasVertexColours_doc[] =
	"(flag = None) - returns 1 if Mesh has vertex colors.\n\
The optional argument sets the vertex color flag";

static char NMesh_getVertexInfluences_doc[] =
	"Return a list of the influences of bones in the vertex \n\
specified by index. The list contains pairs with the \n\
bone name and the weight.";

static char NMesh_update_doc[] =
"(recalc_normals = 0, store_edges = 0, vertex_shade = 0) - Updates the Mesh.\n\
Optional arguments: if given and nonzero:\n\
'recalc_normals': normal vectors are recalculated;\n\
'store_edges': edges data is stored.\n\
'vertex_shade': vertex colors are added based on the current lamp setup.";

static char NMesh_getMode_doc[] =
	"() - get the mode flags of this nmesh as an or'ed int value.";

static char NMesh_setMode_doc[] =
	"(int or none to 5 strings) - set the mode flags of this nmesh.\n\
() - unset all flags.";

static char NMesh_getMaxSmoothAngle_doc[] =
	"() - get the max smooth angle for mesh auto smoothing.";

static char NMesh_setMaxSmoothAngle_doc[] =
	"(int) - set the max smooth angle for mesh auto smoothing in the range\n\
[1,80] in degrees.";

static char NMesh_getSubDivLevels_doc[] =
	"() - get the subdivision levels for display and rendering: [display, render]";

static char NMesh_setSubDivLevels_doc[] =
	"([int, int]) - set the subdivision levels for [display, render] -- they are\n\
clamped to the range [0,6].";

static char M_NMesh_New_doc[] =
	"() - returns a new, empty NMesh mesh object\n";

static char M_NMesh_GetRaw_doc[] = "([name]) - Get a raw mesh from Blender\n\n\
[name] Name of the mesh to be returned\n\n\
If name is not specified a new empty mesh is\n\
returned, otherwise Blender returns an existing\n\
mesh.";

static char M_NMesh_GetNames_doc[] = "\
() - Get a list with the names of all available meshes in Blender\n\n\
Any of these names can be passed to NMesh.GetRaw() for the actual mesh data.";

static char M_NMesh_GetRawFromObject_doc[] =
	"(name) - Get the raw mesh used by a Blender object\n\n\
(name) Name of the object to get the mesh from\n\n\
This returns the mesh as used by the object, which\n\
means it contains all deformations and modifications.";

static char M_NMesh_PutRaw_doc[] =
	"(mesh, name = None, recalc_normals = 1, store_edges = 0]) -\n\
Return a raw mesh to Blender\n\n\
(mesh) The NMesh object to store\n\
[name] The mesh to replace\n\
[recalc_normals = 1] Flag to control vertex normal recalculation\n\
[store_edges=0] Store edges data in the blender mesh\n\
If the name of a mesh to replace is not given a new\n\
object is created and returned.";

static char NMesh_transform_doc[] =
"(matrix, recalc_normals = 0) - Transform the mesh by the supplied 4x4 matrix\n\
if recalc_normals is True, vertex normals are transformed along with \n\
vertex coordinatess.\n";


void mesh_update( Mesh * mesh, Object * ob )
{
	if (ob) {
		DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
	}
	else {
		ob = G.main->object.first;
		while (ob) {
			if (ob->data == mesh) {
				DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
				break;
			}
			ob = ob->id.next;
		}
	}
}

/*
 * before trying to convert NMesh data back to mesh, verify that the
 * lists contain the right type of data
 */

static int check_NMeshLists( BPy_NMesh *nmesh )
{
	int i;

	if( !PySequence_Check( nmesh->verts ) )
		return EXPP_ReturnIntError( PyExc_AttributeError,
					      "nmesh verts are not a sequence" );
	if( !PySequence_Check( nmesh->edges ) )
		return EXPP_ReturnIntError( PyExc_AttributeError,
					      "nmesh edges are not a sequence" );
	if( !PySequence_Check( nmesh->faces ) )
		return EXPP_ReturnIntError( PyExc_AttributeError,
					      "nmesh faces are not a sequence" );
	if( !PySequence_Check( nmesh->materials ) )
		return EXPP_ReturnIntError( PyExc_AttributeError,
					      "nmesh materials are not a sequence" );

	if( EXPP_check_sequence_consistency( nmesh->verts, &NMVert_Type ) != 1 )
		return EXPP_ReturnIntError( PyExc_AttributeError,
					      "nmesh vertices must be NMVerts" );
	if( EXPP_check_sequence_consistency( nmesh->edges, &NMEdge_Type ) != 1 )
		return EXPP_ReturnIntError( PyExc_AttributeError,
					      "nmesh edges must be NMEdges" );
	if( EXPP_check_sequence_consistency( nmesh->faces, &NMFace_Type ) != 1 )
		return EXPP_ReturnIntError( PyExc_AttributeError,
					      "nmesh faces must be NMFaces" );
	for( i = 0 ; i < PySequence_Length(nmesh->faces); ++i ) {
		int j, err=0;
		PyObject *col, *v, *uv;
    	BPy_NMFace *face=(BPy_NMFace *)PySequence_GetItem(nmesh->faces, i);

		col = face->col;
		uv = face->uv;
		v = face->v;
		Py_DECREF( face );
		if( EXPP_check_sequence_consistency( face->col, &NMCol_Type ) != 1 ) {
			return EXPP_ReturnIntError( PyExc_AttributeError,
					      "nmesh face col must be NMCols" );
		}
		if( EXPP_check_sequence_consistency( face->v, &NMVert_Type ) != 1 )
			return EXPP_ReturnIntError( PyExc_AttributeError,
					      "nmesh face v must be NMVerts" );

		for( j = 0 ; !err && j < PySequence_Length( face->uv ); ++j ) {
			PyObject *uv = PySequence_GetItem( face->uv, j);
			if( PySequence_Check(uv) && PySequence_Length(uv) == 2 ) {
    			PyObject *p1 = PySequence_GetItem(uv, 0);
    			PyObject *p2 = PySequence_GetItem(uv, 1);
				if( !PyNumber_Check(p1) || !PyNumber_Check(p2) )
					err = 1;
				Py_DECREF( p1 );
				Py_DECREF( p2 );
			}
			else {
				err = 1;
			}

			Py_DECREF( uv );
		}
		if( err )
			return EXPP_ReturnIntError( PyExc_AttributeError,
					      "nmesh face uv must contain sequence of 2 floats" );
	}
	return 0;
}


/*****************************/
/*			Mesh Color Object		 */
/*****************************/

static void NMCol_dealloc( PyObject * self )
{
	PyObject_DEL( self );
}

static BPy_NMCol *newcol( char r, char g, char b, char a )
{
	BPy_NMCol *mc = ( BPy_NMCol * ) PyObject_NEW( BPy_NMCol, &NMCol_Type );

	mc->r = r;
	mc->g = g;
	mc->b = b;
	mc->a = a;

	return mc;
}

static PyObject *M_NMesh_Col( PyObject * self, PyObject * args )
{
	char r = 255, g = 255, b = 255, a = 255;

	if( PyArg_ParseTuple( args, "|bbbb", &r, &g, &b, &a ) )
		return ( PyObject * ) newcol( r, g, b, a );

	return NULL;
}

static PyObject *NMCol_getattr( PyObject * self, char *name )
{
	BPy_NMCol *mc = ( BPy_NMCol * ) self;

	if( strcmp( name, "r" ) == 0 )
		return Py_BuildValue( "i", mc->r );
	else if( strcmp( name, "g" ) == 0 )
		return Py_BuildValue( "i", mc->g );
	else if( strcmp( name, "b" ) == 0 )
		return Py_BuildValue( "i", mc->b );
	else if( strcmp( name, "a" ) == 0 )
		return Py_BuildValue( "i", mc->a );
	else if( strcmp( name, "__members__" ) == 0 )
		return Py_BuildValue( "[s,s,s,s]", "r", "g", "b", "a" );

	return EXPP_ReturnPyObjError( PyExc_AttributeError, name );
}

static int NMCol_setattr( PyObject * self, char *name, PyObject * v )
{
	BPy_NMCol *mc = ( BPy_NMCol * ) self;
	char ival;

	if( !PyArg_Parse( v, "b", &ival ) )
		return -1;

	ival = ( char ) EXPP_ClampInt( ival, 0, 255 );

	if( strcmp( name, "r" ) == 0 )
		mc->r = (unsigned char)ival;
	else if( strcmp( name, "g" ) == 0 )
		mc->g = (unsigned char)ival;
	else if( strcmp( name, "b" ) == 0 )
		mc->b = (unsigned char)ival;
	else if( strcmp( name, "a" ) == 0 )
		mc->a = (unsigned char)ival;
	else
		return -1;

	return 0;
}

static PyObject *NMCol_repr( BPy_NMCol * self )
{
	static char s[256];
	sprintf( s, "[NMCol - <%d, %d, %d, %d>]", self->r, self->g, self->b,
		 self->a );
	return Py_BuildValue( "s", s );
}

PyTypeObject NMCol_Type = {
	PyObject_HEAD_INIT( NULL ) 0,	/* ob_size */
	"Blender NMCol",	/* tp_name */
	sizeof( BPy_NMCol ),	/* tp_basicsize */
	0,			/* tp_itemsize */
	/* methods */
	( destructor ) NMCol_dealloc,	/* tp_dealloc */
	( printfunc ) 0,	/* tp_print */
	( getattrfunc ) NMCol_getattr,	/* tp_getattr */
	( setattrfunc ) NMCol_setattr,	/* tp_setattr */
	0,			/* tp_compare */
	( reprfunc ) NMCol_repr,	/* tp_repr */
	0,			/* tp_as_number */
	0,			/* tp_as_sequence */
	0,			/* tp_as_mapping */
	0,			/* tp_hash */
	0,			/* tp_as_number */
	0,			/* tp_as_sequence */
	0,			/* tp_as_mapping */
	0,			/* tp_hash */
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* up to tp_del to avoid a warning */
};

/*****************************/
/*		NMesh Python Object		 */
/*****************************/
static void NMFace_dealloc( PyObject * self )
{
	BPy_NMFace *mf = ( BPy_NMFace * ) self;

	Py_DECREF( mf->v );
	Py_DECREF( mf->uv );
	Py_DECREF( mf->col );

	PyObject_DEL( self );
}

static PyObject *new_NMFace( PyObject * vertexlist )
{
	BPy_NMFace *mf = PyObject_NEW( BPy_NMFace, &NMFace_Type );
	PyObject *vlcopy;

	if( vertexlist ) {	/* create a copy of the given vertex list */
		PyObject *item;
		int i, len = PyList_Size( vertexlist );

		vlcopy = PyList_New( len );

		if( !vlcopy ) {
			Py_DECREF(mf);
			return EXPP_ReturnPyObjError( PyExc_MemoryError,
						      "couldn't create PyList" );
		}
		for( i = 0; i < len; i++ ) {
			item = PySequence_GetItem( vertexlist, i );	/* PySequence increfs */

			if( item )
				PyList_SET_ITEM( vlcopy, i, item );
			else {
				Py_DECREF(mf);
				Py_DECREF(vlcopy);
				return EXPP_ReturnPyObjError
					( PyExc_RuntimeError,
					  "couldn't get vertex from a PyList" );
			}
		}
	} else			/* create an empty vertex list */
		vlcopy = PyList_New( 0 );

	mf->v = vlcopy;
	mf->uv = PyList_New( 0 );
	mf->image = NULL;
	mf->mode = TF_DYNAMIC + TF_TEX;
	mf->flag = TF_SELECT;
	mf->transp = TF_SOLID;
	mf->col = PyList_New( 0 );

	mf->mf_flag = 0;
	mf->mat_nr = 0;
	mf->orig_index = -1;

	return ( PyObject * ) mf;
}

static PyObject *M_NMesh_Face( PyObject * self, PyObject * args )
{
	PyObject *vertlist = NULL;

	if( !PyArg_ParseTuple( args, "|O!", &PyList_Type, &vertlist ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected a list of vertices or nothing as argument" );

/*	if (!vertlist) vertlist = PyList_New(0); */

	return new_NMFace( vertlist );
}

static PyObject *NMFace_append( PyObject * self, PyObject * args )
{
	PyObject *vert;
	BPy_NMFace *f = ( BPy_NMFace * ) self;

	if( !PyArg_ParseTuple( args, "O!", &NMVert_Type, &vert ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected an NMVert object" );

	PyList_Append( f->v, vert );

	Py_RETURN_NONE;
}

#undef MethodDef
#define MethodDef(func) {#func, NMFace_##func, METH_VARARGS, NMFace_##func##_doc}

static struct PyMethodDef NMFace_methods[] = {
	MethodDef( append ),
	{NULL, NULL, 0, NULL}
};

static PyObject *NMFace_getattr( PyObject * self, char *name )
{
	BPy_NMFace *mf = ( BPy_NMFace * ) self;

	if( strcmp( name, "v" ) == 0 )
		return Py_BuildValue( "O", mf->v );
	else if( strcmp( name, "col" ) == 0 )
		return Py_BuildValue( "O", mf->col );
	else if( strcmp( name, "mat" ) == 0 )	// emulation XXX
		return Py_BuildValue( "i", mf->mat_nr );
	else if( strcmp( name, "materialIndex" ) == 0 )
		return Py_BuildValue( "i", mf->mat_nr );
	else if( strcmp( name, "smooth" ) == 0 )
		return Py_BuildValue( "i", (mf->mf_flag & ME_SMOOTH) ? 1:0 );
	else if( strcmp( name, "sel" ) == 0 )
		return Py_BuildValue( "i", (mf->mf_flag & ME_FACE_SEL) ? 1:0 );
	else if( strcmp( name, "hide" ) == 0 )
		return Py_BuildValue( "i", (mf->mf_flag & ME_HIDE) ? 1:0 );

	else if( strcmp( name, "image" ) == 0 ) {
		if( mf->image )
			return Image_CreatePyObject( mf->image );
		else
			Py_RETURN_NONE;
	}

	else if( strcmp( name, "mode" ) == 0 )
		return Py_BuildValue( "i", mf->mode );
	else if( strcmp( name, "flag" ) == 0 )
		return Py_BuildValue( "i", mf->flag );
	else if( strcmp( name, "transp" ) == 0 )
		return Py_BuildValue( "i", mf->transp );
	else if( strcmp( name, "uv" ) == 0 )
		return Py_BuildValue( "O", mf->uv );

	else if( ( strcmp( name, "normal" ) == 0 )
		 || ( strcmp( name, "no" ) == 0 ) ) {

		if( EXPP_check_sequence_consistency( mf->v, &NMVert_Type ) ==
		    1 ) {

			float fNormal[3] = { 0.0, 0.0, 0.0 };
			float *vco[4] = { NULL, NULL, NULL, NULL };
			int nSize = PyList_Size( mf->v );
			int loop;

			if( nSize != 3 && nSize != 4 )
				return EXPP_ReturnPyObjError
					( PyExc_AttributeError,
					  "face must contain either 3 or 4 verts" );

			for( loop = 0; loop < nSize; loop++ ) {
				BPy_NMVert *v =
					( BPy_NMVert * ) PyList_GetItem( mf->v,
									 loop );
				vco[loop] = ( float * ) v->co;
			}

			if( nSize == 4 )
				CalcNormFloat4( vco[0], vco[1], vco[2], vco[3],
						fNormal );
			else
				CalcNormFloat( vco[0], vco[1], vco[2],
					       fNormal );

			return Py_BuildValue( "[f,f,f]", fNormal[0],
					      fNormal[1], fNormal[2] );
		} else		// EXPP_check_sequence_consistency failed
			return EXPP_ReturnPyObjError( PyExc_AttributeError,
						      "this face does not contain a series of NMVerts" );
	}

	else if( strcmp( name, "__members__" ) == 0 )
		return Py_BuildValue( "[s,s,s,s,s,s,s,s,s,s,s,s,s]",
				      "v", "col", "mat", "materialIndex",
				      "smooth", "image", "mode", "flag",
				      "transp", "uv", "normal", "sel", "hide");
	return Py_FindMethod( NMFace_methods, ( PyObject * ) self, name );
}

static int NMFace_setattr( PyObject * self, char *name, PyObject * v )
{
	BPy_NMFace *mf = ( BPy_NMFace * ) self;
	short ival;
	char cval;

	if( strcmp( name, "v" ) == 0 ) {

		if( PySequence_Check( v ) ) {
			Py_DECREF( mf->v );
			mf->v = EXPP_incr_ret( v );

			return 0;
		}
	} else if( strcmp( name, "col" ) == 0 ) {

		if( PySequence_Check( v ) ) {
			Py_DECREF( mf->col );
			mf->col = EXPP_incr_ret( v );

			return 0;
		}
	} else if( !strcmp( name, "mat" ) || !strcmp( name, "materialIndex" ) ) {
		PyArg_Parse( v, "b", &cval );
		mf->mat_nr = cval;

		return 0;
	} else if( strcmp( name, "smooth" ) == 0 ) {
		PyArg_Parse( v, "h", &ival );
		if (ival) mf->mf_flag |= ME_SMOOTH;
		else mf->mf_flag &= ~ME_SMOOTH;

		return 0;
	} else if( strcmp( name, "sel" ) == 0 ) {
		PyArg_Parse( v, "h", &ival );
		if (ival) mf->mf_flag |= ME_FACE_SEL;
		else mf->mf_flag &= ~ME_FACE_SEL;

		return 0;
	} else if( strcmp( name, "hide" ) == 0 ) {
		PyArg_Parse( v, "h", &ival );
		if (ival) mf->mf_flag |= ME_HIDE;
		else mf->mf_flag &= ~ME_HIDE;

		return 0;

	} else if( strcmp( name, "uv" ) == 0 ) {

		if( PySequence_Check( v ) ) {
			Py_DECREF( mf->uv );
			mf->uv = EXPP_incr_ret( v );

			return 0;
		}
	} else if( strcmp( name, "flag" ) == 0 ) {
		PyArg_Parse( v, "h", &ival );
		mf->flag = ival;

		return 0;
	} else if( strcmp( name, "mode" ) == 0 ) {
		PyArg_Parse( v, "h", &ival );
		mf->mode = ival;

		return 0;
	} else if( strcmp( name, "transp" ) == 0 ) {
		PyArg_Parse( v, "b", &cval );
		mf->transp = cval;

		return 0;
	} else if( strcmp( name, "image" ) == 0 ) {
		PyObject *pyimg;
		if( !PyArg_Parse( v, "O!", &Image_Type, &pyimg ) )
			return EXPP_ReturnIntError( PyExc_TypeError,
						    "expected image object" );

		if( pyimg == Py_None ) {
			mf->image = NULL;

			return 0;
		}

		mf->image = ( ( BPy_Image * ) pyimg )->image;

		return 0;
	}

	return EXPP_ReturnIntError( PyExc_AttributeError, name );
}

static PyObject *NMFace_repr( PyObject * self )
{
	return PyString_FromString( "[NMFace]" );
}

static int NMFace_len( BPy_NMFace * self )
{
	return PySequence_Length( self->v );
}

static PyObject *NMFace_item( BPy_NMFace * self, int i )
{
	return PySequence_GetItem( self->v, i );	// new ref
}

static PyObject *NMFace_slice( BPy_NMFace * self, int begin, int end )
{
	return PyList_GetSlice( self->v, begin, end );	// new ref
}

static PySequenceMethods NMFace_SeqMethods = {
	( inquiry ) NMFace_len,	/* sq_length */
	( binaryfunc ) 0,	/* sq_concat */
	( intargfunc ) 0,	/* sq_repeat */
	( intargfunc ) NMFace_item,	/* sq_item */
	( intintargfunc ) NMFace_slice,	/* sq_slice */
	( intobjargproc ) 0,	/* sq_ass_item */
	( intintobjargproc ) 0,	/* sq_ass_slice */
	0,0,0,
};

PyTypeObject NMFace_Type = {
	PyObject_HEAD_INIT( NULL ) 0,	/*ob_size */
	"Blender NMFace",	/*tp_name */
	sizeof( BPy_NMFace ),	/*tp_basicsize */
	0,			/*tp_itemsize */
	/* methods */
	( destructor ) NMFace_dealloc,	/*tp_dealloc */
	( printfunc ) 0,	/*tp_print */
	( getattrfunc ) NMFace_getattr,	/*tp_getattr */
	( setattrfunc ) NMFace_setattr,	/*tp_setattr */
	0,			/*tp_compare */
	( reprfunc ) NMFace_repr,	/*tp_repr */
	0,			/*tp_as_number */
	&NMFace_SeqMethods,	/*tp_as_sequence */
	0,			/*tp_as_mapping */
	0,			/*tp_hash */
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* up to tp_del to avoid a warning */
};

static BPy_NMVert *newvert( float *co )
{
	BPy_NMVert *mv = PyObject_NEW( BPy_NMVert, &NMVert_Type );

	mv->co[0] = co[0];
	mv->co[1] = co[1];
	mv->co[2] = co[2];

	mv->no[0] = mv->no[1] = mv->no[2] = 0.0;
	mv->uvco[0] = mv->uvco[1] = mv->uvco[2] = 0.0;
	mv->flag = 0;

	return mv;
}

static PyObject *M_NMesh_Vert( PyObject * self, PyObject * args )
{
	float co[3] = { 0.0, 0.0, 0.0 };

	if( !PyArg_ParseTuple( args, "|fff", &co[0], &co[1], &co[2] ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected three floats (or nothing) as arguments" );

	return ( PyObject * ) newvert( co );
}

static void NMVert_dealloc( PyObject * self )
{
	PyObject_DEL( self );
}

static PyObject *NMVert_getattr( PyObject * self, char *name )
{
	BPy_NMVert *mv = ( BPy_NMVert * ) self;

	if( !strcmp( name, "co" ) || !strcmp( name, "loc" ) )
		return newVectorObject(mv->co,3,Py_WRAP);

	else if( strcmp( name, "no" ) == 0 )
		return newVectorObject(mv->no,3,Py_WRAP);
	else if( strcmp( name, "uvco" ) == 0 )
		return newVectorObject(mv->uvco,3,Py_WRAP);
	else if( strcmp( name, "index" ) == 0 )
		return PyInt_FromLong( mv->index );
	else if( strcmp( name, "sel" ) == 0 )
		return PyInt_FromLong( mv->flag & 1 );
	else if( strcmp( name, "__members__" ) == 0 )
		return Py_BuildValue( "[s,s,s,s,s]", "co", "no", "uvco",
				      "index", "sel" );

	return EXPP_ReturnPyObjError( PyExc_AttributeError, name );
}

static int NMVert_setattr( PyObject * self, char *name, PyObject * v )
{
	BPy_NMVert *mv = ( BPy_NMVert * ) self;
	int i;

	if( strcmp( name, "index" ) == 0 ) {
		PyArg_Parse( v, "i", &i );
		mv->index = i;
		return 0;
	} else if( strcmp( name, "sel" ) == 0 ) {
		PyArg_Parse( v, "i", &i );
		mv->flag = i ? 1 : 0;
		return 0;
	} else if( strcmp( name, "uvco" ) == 0 ) {

		if( !PyArg_ParseTuple( v, "ff|f",
				       &( mv->uvco[0] ), &( mv->uvco[1] ),
				       &( mv->uvco[2] ) ) )
			return EXPP_ReturnIntError( PyExc_AttributeError,
						    "Vector tuple or triple expected" );

		return 0;
	}

	return EXPP_ReturnIntError( PyExc_AttributeError, name );
}

static int NMVert_len( BPy_NMVert * self )
{
	return 3;
}

static PyObject *NMVert_item( BPy_NMVert * self, int i )
{
	if( i < 0 || i >= 3 )
		return EXPP_ReturnPyObjError( PyExc_IndexError,
					      "array index out of range" );

	return Py_BuildValue( "f", self->co[i] );
}

static PyObject *NMVert_slice( BPy_NMVert * self, int begin, int end )
{
	PyObject *list;
	int count;

	if( begin < 0 )
		begin = 0;
	if( end > 3 )
		end = 3;
	if( begin > end )
		begin = end;

	list = PyList_New( end - begin );

	for( count = begin; count < end; count++ )
		PyList_SetItem( list, count - begin,
				PyFloat_FromDouble( self->co[count] ) );

	return list;
}

static int NMVert_ass_item( BPy_NMVert * self, int i, PyObject * ob )
{
	if( i < 0 || i >= 3 )
		return EXPP_ReturnIntError( PyExc_IndexError,
					    "array assignment index out of range" );

	if( !PyNumber_Check( ob ) )
		return EXPP_ReturnIntError( PyExc_IndexError,
					    "NMVert member must be a number" );

	self->co[i] = (float)PyFloat_AsDouble( ob );

	return 0;
}

static int NMVert_ass_slice( BPy_NMVert * self, int begin, int end,
			     PyObject * seq )
{
	int count;

	if( begin < 0 )
		begin = 0;
	if( end > 3 )
		end = 3;
	if( begin > end )
		begin = end;

	if( !PySequence_Check( seq ) )
		EXPP_ReturnIntError( PyExc_TypeError,
				     "illegal argument type for built-in operation" );

	if( PySequence_Length( seq ) != ( end - begin ) )
		EXPP_ReturnIntError( PyExc_TypeError,
				     "size mismatch in slice assignment" );

	for( count = begin; count < end; count++ ) {
		PyObject *ob = PySequence_GetItem( seq, count );

		if( !PyArg_Parse( ob, "f", &self->co[count] ) ) {
			Py_DECREF( ob );
			return -1;
		}

		Py_DECREF( ob );
	}

	return 0;
}

static PySequenceMethods NMVert_SeqMethods = {
	( inquiry ) NMVert_len,	/* sq_length */
	( binaryfunc ) 0,	/* sq_concat */
	( intargfunc ) 0,	/* sq_repeat */
	( intargfunc ) NMVert_item,	/* sq_item */
	( intintargfunc ) NMVert_slice,	/* sq_slice */
	( intobjargproc ) NMVert_ass_item,	/* sq_ass_item */
	( intintobjargproc ) NMVert_ass_slice,	/* sq_ass_slice */
	0,0,0,
};

PyTypeObject NMVert_Type = {
	PyObject_HEAD_INIT( NULL ) 
	0,	/*ob_size */
	"Blender NMVert",	/*tp_name */
	sizeof( BPy_NMVert ),	/*tp_basicsize */
	0,			/*tp_itemsize */
	/* methods */
	( destructor ) NMVert_dealloc,	/*tp_dealloc */
	( printfunc ) 0,	/*tp_print */
	( getattrfunc ) NMVert_getattr,	/*tp_getattr */
	( setattrfunc ) NMVert_setattr,	/*tp_setattr */
	0,			/*tp_compare */
	( reprfunc ) 0,		/*tp_repr */
	0,			/*tp_as_number */
	&NMVert_SeqMethods,	/*tp_as_sequence */
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_del */
};


/*****************************
 * NMEdge
 *****************************/

static BPy_NMEdge *new_NMEdge( BPy_NMVert * v1, BPy_NMVert * v2, char crease, short flag)
{
  BPy_NMEdge *edge=NULL;

  if (!v1 || !v2) return NULL;
  if (!BPy_NMVert_Check(v1) || !BPy_NMVert_Check(v2)) return NULL;

  edge = PyObject_NEW( BPy_NMEdge, &NMEdge_Type );

  edge->v1=EXPP_incr_ret((PyObject*)v1);
  edge->v2=EXPP_incr_ret((PyObject*)v2);
  edge->flag=flag;
  edge->crease=crease;

  return edge;
}

static void NMEdge_dealloc( PyObject * self )
{
  BPy_NMEdge *edge=(BPy_NMEdge *)self;

  Py_DECREF(edge->v1);
  Py_DECREF(edge->v2);

  PyObject_DEL(self);
}

static PyObject *NMEdge_getattr( PyObject * self, char *name )
{
  BPy_NMEdge *edge=(BPy_NMEdge *)self;

  if      ( strcmp( name, "v1" ) == 0 )
		return EXPP_incr_ret( edge->v1 );
  else if ( strcmp( name, "v2" ) == 0 )
    return EXPP_incr_ret( edge->v2 );
  else if ( strcmp( name, "flag" ) == 0 )
    return PyInt_FromLong( edge->flag );
  else if ( strcmp( name, "crease" ) == 0 )
    return PyInt_FromLong( edge->crease );
  else if( strcmp( name, "__members__" ) == 0 )
    return Py_BuildValue( "[s,s,s,s]",
                          "v1", "v2", "flag", "crease" );
  
  return EXPP_ReturnPyObjError( PyExc_AttributeError, name );
}

static int NMEdge_setattr( PyObject * self, char *name, PyObject * v )
{
  BPy_NMEdge *edge=(BPy_NMEdge *)self;

  if ( strcmp( name, "flag" ) == 0 )
  {
    short flag=0;
    if( !PyInt_Check( v ) )
      return EXPP_ReturnIntError( PyExc_TypeError,
                                  "expected int argument" );

    flag = ( short ) PyInt_AsLong( v );

    edge->flag = flag;

    return 0;
  }
  else if ( strcmp( name, "crease" ) == 0 )
  {
    char crease=0;
    if( !PyInt_Check( v ) )
      return EXPP_ReturnIntError( PyExc_TypeError,
                                  "expected int argument" );

    crease = ( char ) PyInt_AsLong( v );

    edge->crease = crease;

    return 0;
  }

  return EXPP_ReturnIntError( PyExc_AttributeError, name );
}

PyTypeObject NMEdge_Type = {
	PyObject_HEAD_INIT( NULL ) 
	0,	/*ob_size */
	"Blender NMEdge",	/*tp_name */
	sizeof( BPy_NMEdge ),	/*tp_basicsize */
	0,			/*tp_itemsize */
	/* methods */
	( destructor ) NMEdge_dealloc,	/*tp_dealloc */
	( printfunc ) 0,	/*tp_print */
	( getattrfunc ) NMEdge_getattr,	/*tp_getattr */
	( setattrfunc ) NMEdge_setattr,	/*tp_setattr */
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,
};

static void NMesh_dealloc( PyObject * self )
{
	BPy_NMesh *me = ( BPy_NMesh * ) self;

	CustomData_free( &me->fdata, me->totfdata );

	Py_DECREF( me->name );
	Py_DECREF( me->verts );
	Py_DECREF( me->faces );
	Py_DECREF( me->materials );
	Py_DECREF( me->edges );

	PyObject_DEL( self );
}

static PyObject *NMesh_getMaterials( PyObject * self, PyObject * args )
{
	BPy_NMesh *nm = ( BPy_NMesh * ) self;
	PyObject *list = NULL;
	Mesh *me = nm->mesh;
	int all = -1;

	if( !PyArg_ParseTuple( args, "|i", &all ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected nothing or an int (bool) as argument" );

	if( all >= 0 ) {
		if (!me)
			return EXPP_ReturnPyObjError(PyExc_TypeError,
					"meshes obtained with GetRawFromObject don't support this option");

		list = EXPP_PyList_fromMaterialList( me->mat, me->totcol,
						     all );
		Py_DECREF( nm->materials );	/* update nmesh.materials attribute */
		nm->materials = EXPP_incr_ret( list );
	} else
		list = EXPP_incr_ret( nm->materials );

	return list;
}

static PyObject *NMesh_setMaterials( PyObject * self, PyObject * args )
{
	BPy_NMesh *me = ( BPy_NMesh * ) self;
	PyObject *pymats = NULL;

	if( !PyArg_ParseTuple( args, "O!", &PyList_Type, &pymats ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected a list of materials (None's also accepted) as argument" );

	if( !EXPP_check_sequence_consistency( pymats, &Material_Type ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "list should only contain materials (None's also accepted)" );

	if( PyList_Size( pymats ) > 16 )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "list can't have more than 16 materials" );

	Py_DECREF( me->materials );
	me->materials = EXPP_incr_ret( pymats );

	Py_RETURN_NONE;
}

static PyObject *NMesh_addMaterial( PyObject * self, PyObject * args )
{
	BPy_NMesh *me = ( BPy_NMesh * ) self;
	BPy_Material *pymat;
	Material *mat;
	PyObject *iter;
	int i, len = 0;

	if( !PyArg_ParseTuple( args, "O!", &Material_Type, &pymat ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected Blender Material PyObject" );

	mat = pymat->material;
	len = PyList_Size( me->materials );

	if( len >= 16 )
		return EXPP_ReturnPyObjError( PyExc_RuntimeError,
					      "object data material lists can't have more than 16 materials" );

	for( i = 0; i < len; i++ ) {
		iter = PyList_GetItem( me->materials, i );
		if( mat == Material_FromPyObject( iter ) )
			return EXPP_ReturnPyObjError( PyExc_AttributeError,
						      "material already in the list" );
	}

	PyList_Append( me->materials, ( PyObject * ) pymat );

	Py_RETURN_NONE;
}

static PyObject *NMesh_getKey( BPy_NMesh * self )
{
	PyObject *keyobj;

	if( self->mesh->key )
		keyobj = Key_CreatePyObject(self->mesh->key);
	else
		keyobj = EXPP_incr_ret(Py_None);
	
	return keyobj;
}

static PyObject *NMesh_removeAllKeys( PyObject * self, PyObject * args )
{
	BPy_NMesh *nm = ( BPy_NMesh * ) self;
	Mesh *me = nm->mesh;

	if( !PyArg_ParseTuple( args, "" ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "this function expects no arguments" );

	if( !me || !me->key )
		return EXPP_incr_ret_False();

	me->key->id.us--;
	me->key = 0;

	return EXPP_incr_ret_True();
}

static PyObject *NMesh_insertKey( PyObject * self, PyObject * args )
{
	int fra = -1, oldfra = -1;
	char *type = NULL;
	short typenum;
	BPy_NMesh *nm = ( BPy_NMesh * ) self;
	Mesh *mesh = nm->mesh;

	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, NMESH_FRAME_MAX );
		oldfra = G.scene->r.cfra;
		G.scene->r.cfra = (int)fra;
	}

	if( !mesh )
		return EXPP_ReturnPyObjError( PyExc_RuntimeError,
					      "update this NMesh first with its .update() method" );

	insert_meshkey( mesh, typenum );
	allspace(REMAKEIPO, 0);

	if( fra > 0 )
		G.scene->r.cfra = (int)oldfra;

	Py_RETURN_NONE;
}

static PyObject *NMesh_getSelectedFaces( PyObject * self, PyObject * args )
{
	BPy_NMesh *nm = ( BPy_NMesh * ) self;
	Mesh *me = nm->mesh;
	int i, totfaces, flag = 0;
	PyObject *l, *pyval;

	if( !PyArg_ParseTuple( args, "|i", &flag ) )
		return EXPP_ReturnPyObjError( PyExc_ValueError,
					      "expected int argument (or nothing)" );
	
	l = PyList_New( 0 );
	if( me == NULL || me->mface == NULL)
		return l;
	
	/* make sure not to write more faces then we have */
	totfaces= MIN2(me->totface, PySequence_Length(nm->faces));
	
	if( flag ) {
		for( i = 0; i < totfaces; i++ ) {
			if( me->mface[i].flag & ME_FACE_SEL ) {
				pyval = PyInt_FromLong( i );
				PyList_Append( l, pyval );
				Py_DECREF(pyval);
			}
		}
	} else {
		for( i = 0; i < totfaces; i++ ) {
			if( me->mface[i].flag & ME_FACE_SEL )
				PyList_Append( l, PyList_GetItem( nm->faces, i ) );
		}
	}
	return l;
}

static PyObject *NMesh_getActiveFace( PyObject * self )
{
	if( ( ( BPy_NMesh * ) self )->sel_face < 0 )
		Py_RETURN_NONE;

	return Py_BuildValue( "i", ( ( BPy_NMesh * ) self )->sel_face );
}

static PyObject *NMesh_hasVertexUV( PyObject * self, PyObject * args )
{
	BPy_NMesh *me = ( BPy_NMesh * ) self;
	int flag = -1;

	if( !PyArg_ParseTuple( args, "|i", &flag ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected int argument (or nothing)" );

	switch ( flag ) {
	case 0:
		me->flags &= ~NMESH_HASVERTUV;
		break;
	case 1:
		me->flags |= NMESH_HASVERTUV;
		break;
	default:
		break;
	}

	if( me->flags & NMESH_HASVERTUV )
		return EXPP_incr_ret_True();
	else
		return EXPP_incr_ret_False();
}

static PyObject *NMesh_hasFaceUV( PyObject * self, PyObject * args )
{
	BPy_NMesh *me = ( BPy_NMesh * ) self;
	int flag = -1;

	if( !PyArg_ParseTuple( args, "|i", &flag ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected int argument (or nothing)" );

	switch ( flag ) {
	case 0:
		me->flags &= ~NMESH_HASFACEUV;
		break;
	case 1:
		me->flags |= NMESH_HASFACEUV;
		break;
	default:
		break;
	}

	if( me->flags & NMESH_HASFACEUV )
		return EXPP_incr_ret_True();
	else
		return EXPP_incr_ret_False();
}

static PyObject *NMesh_hasVertexColours( PyObject * self, PyObject * args )
{
	BPy_NMesh *me = ( BPy_NMesh * ) self;
	int flag = -1;

	if( !PyArg_ParseTuple( args, "|i", &flag ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected int argument (or nothing)" );

	switch ( flag ) {
	case 0:
		me->flags &= ~NMESH_HASMCOL;
		break;
	case 1:
		me->flags |= NMESH_HASMCOL;
		break;
	default:
		break;
	}

	if( me->flags & NMESH_HASMCOL )
		return EXPP_incr_ret_True();
	else
		return EXPP_incr_ret_False();
}

static PyObject *NMesh_update( PyObject *self, PyObject *a, PyObject *kwd )
{
	BPy_NMesh *nmesh = ( BPy_NMesh * ) self;
	Mesh *mesh = nmesh->mesh;
	int recalc_normals = 0, store_edges = 0, vertex_shade = 0;
	static char *kwlist[] = {"recalc_normals", "store_edges",
		"vertex_shade", NULL};
	int needs_redraw = 1;
	int old_totvert = 0;

	if (!PyArg_ParseTupleAndKeywords(a, kwd, "|iii", kwlist, &recalc_normals,
		&store_edges, &vertex_shade ) )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
				"expected nothing or one to three bool(s) (0 or 1) as argument" );

	if( check_NMeshLists( nmesh ) )
		return NULL;

	if( mesh ) {
		old_totvert = mesh->totvert;
		unlink_existingMeshData( mesh );
		if( !convert_NMeshToMesh( mesh, nmesh ) )
			return NULL;
		if (mesh->dvert) check_dverts(mesh, old_totvert);
	} else {
		mesh = Mesh_fromNMesh( nmesh );
		/* if mesh is NULL, there was an error */
		if( !mesh )
			return NULL;
		nmesh->mesh = mesh;
	}

	if( recalc_normals )
		mesh_calc_normals(mesh->mvert, mesh->totvert, mesh->mface, mesh->totface, NULL);

	mesh_update( mesh, nmesh->object );

	nmesh_updateMaterials( nmesh );

	if( nmesh->name && nmesh->name != Py_None )
		new_id( &( G.main->mesh ), &mesh->id,
			PyString_AsString( nmesh->name ) );

	if (vertex_shade) {
		Base *base = FIRSTBASE;

		if (!nmesh->object)
			return EXPP_ReturnPyObjError(PyExc_RuntimeError,
	    	"link this mesh to an object first with ob.link(mesh)" );

		if (G.obedit)
			return EXPP_ReturnPyObjError(PyExc_RuntimeError,
	    	"can't shade vertices while in edit mode" );

		while (base) {
			if (base->object == nmesh->object) {
				base->flag |= SELECT;
				nmesh->object->flag = (short)base->flag;
				set_active_base (base);
				needs_redraw = 0; /* already done in make_vertexcol */
				break;
			}
			base = base->next;
		}

		/* recalculate the derived mesh before trying to use it */
		makeDerivedMesh(nmesh->object, CD_MASK_BAREMESH);
		make_vertexcol(1);

		countall();
	}

	if( !during_script(  ) && needs_redraw)
		EXPP_allqueue( REDRAWVIEW3D, 0 );

	return PyInt_FromLong( 1 );
}

/** Implementation of the python method getVertexInfluence for an NMesh object.
 * This method returns a list of pairs (string,float) with bone names and
 * influences that this vertex receives.
 * @author Jordi Rovira i Bonet
 */

static PyObject *NMesh_getVertexInfluences( PyObject * self, PyObject * args )
{
	int index;
	PyObject *influence_list = NULL;
	Object *object = ( ( BPy_NMesh * ) self )->object;
	Mesh *me = ( ( BPy_NMesh * ) self )->mesh;

	/* Get a reference to the mesh object wrapped in here. */
	if( !me )
		return EXPP_ReturnPyObjError( PyExc_RuntimeError,
				"unlinked nmesh: call its .update() method first" );

	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 = (bDeformGroup *) BLI_findlink( &object->defbase,
					sweight->def_nr );
			if( defgroup )
				PyList_Append( influence_list, Py_BuildValue( "[sf]",
						defgroup->name, sweight->weight ) ); 
		}
	}

	return influence_list;
}

Mesh *Mesh_fromNMesh( BPy_NMesh * nmesh )
{
	Mesh *mesh = NULL;
	
	mesh = add_mesh( "Mesh" );

	if( !mesh ) {
		PyErr_SetString( PyExc_RuntimeError,
				"FATAL: could not create mesh object" );
		return NULL;
	}

	mesh->id.us = 0;	/* no user yet */
	G.totmesh++;
	
	if( !convert_NMeshToMesh( mesh, nmesh ) )
		return NULL;
	
	return mesh;
}

static PyObject *NMesh_getMaxSmoothAngle( BPy_NMesh * self )
{
	return PyInt_FromLong( self->smoothresh );
}

static PyObject *NMesh_setMaxSmoothAngle( PyObject * self, PyObject * args )
{
	short value = 0;
	BPy_NMesh *nmesh = ( BPy_NMesh * ) self;

	if( !PyArg_ParseTuple( args, "h", &value ) )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "expected an int in [1, 80] as argument" );

	nmesh->smoothresh =
		( short ) EXPP_ClampInt( value, NMESH_SMOOTHRESH_MIN,
					 NMESH_SMOOTHRESH_MAX );

	Py_RETURN_NONE;
}

static PyObject *NMesh_getSubDivLevels( BPy_NMesh * self )
{
	return Py_BuildValue( "[h,h]", self->subdiv[0], self->subdiv[1] );
}

static PyObject *NMesh_setSubDivLevels( PyObject * self, PyObject * args )
{
	short display = 0, render = 0;
	BPy_NMesh *nmesh = ( BPy_NMesh * ) self;

	if( !PyArg_ParseTuple( args, "(hh)", &display, &render ) )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "expected a sequence [int, int] as argument" );

	nmesh->subdiv[0] =
		( short ) EXPP_ClampInt( display, NMESH_SUBDIV_MIN,
					 NMESH_SUBDIV_MAX );

	nmesh->subdiv[1] =
		( short ) EXPP_ClampInt( render, NMESH_SUBDIV_MIN,
					 NMESH_SUBDIV_MAX );

	Py_RETURN_NONE;
}

static PyObject *NMesh_getMode( BPy_NMesh * self )
{
	return PyInt_FromLong( self->mode );
}

static PyObject *NMesh_setMode( PyObject * self, PyObject * args )
{
	BPy_NMesh *nmesh = ( BPy_NMesh * ) self;
	PyObject *arg1 = NULL;
	char *m[5] = { NULL, NULL, NULL, NULL, NULL };
	short i, mode = 0;

	if( !PyArg_ParseTuple ( args, "|Ossss", &arg1, &m[1], &m[2], &m[3], &m[4] ) )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
			"expected an int or from none to 5 strings as argument(s)" );

	if (arg1) {
		if (PyInt_Check(arg1)) {
			mode = (short)PyInt_AsLong(arg1);
		}
		else if (PyString_Check(arg1)) {
			m[0] = PyString_AsString(arg1);
			for( i = 0; i < 5; i++ ) {
				if( !m[i] ) break;
				else if( strcmp( m[i], "NoVNormalsFlip" ) == 0 )
					mode |= ME_NOPUNOFLIP;
				else if( strcmp( m[i], "TwoSided" ) == 0 )
					mode |= ME_TWOSIDED;
				else if( strcmp( m[i], "AutoSmooth" ) == 0 )
					mode |= ME_AUTOSMOOTH;
				else if( m[i][0] == '\0' )
					mode = 0;
				else
					return EXPP_ReturnPyObjError( PyExc_AttributeError,
		    	  "unknown NMesh mode" );
			}
		}
		else return EXPP_ReturnPyObjError( PyExc_AttributeError,
			"expected an int or from none to 5 strings as argument(s)" );
	}

	nmesh->mode = mode;

	Py_RETURN_NONE;
}

/* METH_VARARGS: function(PyObject *self, PyObject *args) */
#undef MethodDef
#define MethodDef(func) {#func, NMesh_##func, METH_VARARGS, NMesh_##func##_doc}

static struct PyMethodDef NMesh_methods[] = {
	MethodDef( addEdge ),
	MethodDef( findEdge ),
	MethodDef( removeEdge ),
	MethodDef( addFace ),
	MethodDef( removeFace ),
	MethodDef( addVertGroup ),
	MethodDef( removeVertGroup ),
	MethodDef( assignVertsToGroup ),
	MethodDef( removeVertsFromGroup ),
	MethodDef( getVertsFromGroup ),
	MethodDef( renameVertGroup ),
	MethodDef( hasVertexColours ),
	MethodDef( hasFaceUV ),
	MethodDef( hasVertexUV ),
	MethodDef( getSelectedFaces ),
	MethodDef( getVertexInfluences ),
	MethodDef( getMaterials ),
	MethodDef( setMaterials ),
	MethodDef( addMaterial ),
	MethodDef( insertKey ),
	MethodDef( removeAllKeys ),
	MethodDef( setMode ),
	MethodDef( setMaxSmoothAngle ),
	MethodDef( setSubDivLevels ),
	MethodDef( transform ),

/* METH_NOARGS: function(PyObject *self) */
#undef MethodDef
#define MethodDef(func) {#func, (PyCFunction)NMesh_##func, METH_NOARGS,\
	NMesh_##func##_doc}

	MethodDef( printDebug ),
	MethodDef( getVertGroupNames ),
	MethodDef( getActiveFace ),
	MethodDef( getKey ),
	MethodDef( getMode ),
	MethodDef( getMaxSmoothAngle ),
	MethodDef( getSubDivLevels ),

/* METH_VARARGS | METH_KEYWORDS:
 * function(PyObject *self, PyObject *args, PyObject *keywords) */
#undef MethodDef
#define MethodDef(func) {#func, (PyCFunction)NMesh_##func,\
	METH_VARARGS | METH_KEYWORDS, NMesh_##func##_doc}

	MethodDef( update ),
	{NULL, NULL, 0, NULL}
};

static PyObject *NMesh_getattr( PyObject * self, char *name )
{
	BPy_NMesh *me = ( BPy_NMesh * ) self;

	if( strcmp( name, "name" ) == 0 )
		return EXPP_incr_ret( me->name );

	else if ( strcmp( name, "properties" ) == 0 )
		return BPy_Wrap_IDProperty( (ID*)me->mesh, IDP_GetProperties((ID*)me->mesh, 1), NULL );

	else if( strcmp( name, "mode" ) == 0 )
		return PyInt_FromLong( me->mode );

	else if( strcmp( name, "block_type" ) == 0 )	/* for compatibility */
		return PyString_FromString( "NMesh" );

	else if( strcmp( name, "materials" ) == 0 )
		return EXPP_incr_ret( me->materials );

	else if( strcmp( name, "verts" ) == 0 )
		return EXPP_incr_ret( me->verts );

	else if( strcmp( name, "maxSmoothAngle" ) == 0 )
		return PyInt_FromLong( me->smoothresh );

	else if( strcmp( name, "subDivLevels" ) == 0 )
		return Py_BuildValue( "[h,h]", me->subdiv[0], me->subdiv[1] );

	else if( strcmp( name, "users" ) == 0 ) {
		if( me->mesh ) {
			return PyInt_FromLong( me->mesh->id.us );
		} else {	/* it's a free mesh: */
			return Py_BuildValue( "i", 0 );
		}
	}
	else if (strcmp( name, "key") == 0)
		return NMesh_getKey((BPy_NMesh*)self);

	else if( strcmp( name, "faces" ) == 0 )
		return EXPP_incr_ret( me->faces );

  else if( strcmp( name, "edges" ) == 0 )
  {
    return EXPP_incr_ret( me->edges );
  }
	else if( strcmp( name, "__members__" ) == 0 )
		return Py_BuildValue( "[s,s,s,s,s,s,s,s,s,s,s]",
				      "name", "materials", "verts", "users",
				      "faces", "maxSmoothAngle",
				      "subdivLevels", "edges", "key" );

	return Py_FindMethod( NMesh_methods, ( PyObject * ) self, name );
}

static int NMesh_setattr( PyObject * self, char *name, PyObject * v )
{
	BPy_NMesh *me = ( BPy_NMesh * ) self;

	if( !strcmp( name, "name" ) ) {

		if( !PyString_Check( v ) )
			return EXPP_ReturnIntError( PyExc_TypeError,
						    "expected string argument" );

		Py_DECREF( me->name );
		me->name = EXPP_incr_ret( v );
	}

	else if( !strcmp( name, "mode" ) ) {
		short mode;

		if( !PyInt_Check( v ) )
			return EXPP_ReturnIntError( PyExc_TypeError,
						    "expected int argument" );

		mode = ( short ) PyInt_AsLong( v );
		if( mode >= 0 )
			me->mode = mode;
		else
			return EXPP_ReturnIntError( PyExc_ValueError,
						    "expected positive int argument" );
	}

	else if( !strcmp( name, "verts" ) || !strcmp( name, "faces" ) ||
		 !strcmp( name, "materials" ) ) {

		if( PySequence_Check( v ) ) {

			if( strcmp( name, "materials" ) == 0 ) {
				Py_DECREF( me->materials );
				me->materials = EXPP_incr_ret( v );
			} else if( strcmp( name, "verts" ) == 0 ) {
				Py_DECREF( me->verts );
				me->verts = EXPP_incr_ret( v );
			} else {
				Py_DECREF( me->faces );
				me->faces = EXPP_incr_ret( v );
			}
		}

		else
			return EXPP_ReturnIntError( PyExc_TypeError,
						    "expected a list" );
	}

	else if( !strcmp( name, "maxSmoothAngle" ) ) {
		short smoothresh = 0;

		if( !PyInt_Check( v ) )
			return EXPP_ReturnIntError( PyExc_TypeError,
						    "expected int argument" );

		smoothresh = ( short ) PyInt_AsLong( v );

		me->smoothresh =
			(short)EXPP_ClampInt( smoothresh, NMESH_SMOOTHRESH_MIN,
				       NMESH_SMOOTHRESH_MAX );
	}

	else if( !strcmp( name, "subDivLevels" ) ) {
		int subdiv[2] = { 0, 0 };
		int i;
		PyObject *tmp;

		if( !PySequence_Check( v ) || ( PySequence_Length( v ) != 2 ) )
			return EXPP_ReturnIntError( PyExc_TypeError,
						    "expected a list [int, int] as argument" );

		for( i = 0; i < 2; i++ ) {
			tmp = PySequence_GetItem( v, i );
			if( tmp ) {
				if( !PyInt_Check( tmp ) ) {
					Py_DECREF( tmp );
					return EXPP_ReturnIntError
						( PyExc_TypeError,
						  "expected a list [int, int] as argument" );
				}

				subdiv[i] = PyInt_AsLong( tmp );
				me->subdiv[i] =
					( short ) EXPP_ClampInt( subdiv[i],
								 NMESH_SUBDIV_MIN,
								 NMESH_SUBDIV_MAX );
				Py_DECREF( tmp );
			} else
				return EXPP_ReturnIntError( PyExc_RuntimeError,
							    "couldn't retrieve subdiv values from list" );
		}
	}
  else if( strcmp( name, "edges" ) == 0 )
  {
    if (PySequence_Check(v))
    {
      Py_DECREF(me->edges);
      me->edges = EXPP_incr_ret( v );
    }
  }
	else
		return EXPP_ReturnIntError( PyExc_AttributeError, name );

	return 0;
}

PyTypeObject NMesh_Type = {
	PyObject_HEAD_INIT( NULL ) 0,	/*ob_size */
	"Blender NMesh",	/*tp_name */
	sizeof( BPy_NMesh ),	/*tp_basicsize */
	0,			/*tp_itemsize */
	/* methods */
	( destructor ) NMesh_dealloc,	/*tp_dealloc */
	( printfunc ) 0,	/*tp_print */
	( getattrfunc ) NMesh_getattr,	/*tp_getattr */
	( setattrfunc ) NMesh_setattr,	/*tp_setattr */
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

static BPy_NMFace *nmface_from_data( BPy_NMesh * mesh, int vidxs[4],
	char mat_nr, char flag, MTFace * tface, MCol * col, int orig_index )
{
	BPy_NMFace *newf = PyObject_NEW( BPy_NMFace, &NMFace_Type );
	int i, len;

	if( vidxs[3] )
		len = 4;
	else 
		len = 3;

	newf->v = PyList_New( len );

	for( i = 0; i < len; i++ )
		PyList_SetItem( newf->v, i,
				EXPP_incr_ret( PyList_GetItem
					       ( mesh->verts, vidxs[i] ) ) );

	if( tface ) {
		newf->uv = PyList_New( len );	// per-face UV coordinates

		for( i = 0; i < len; i++ ) {
			PyList_SetItem( newf->uv, i,
					Py_BuildValue( "(ff)", tface->uv[i][0],
						       tface->uv[i][1] ) );
		}

		if( tface->tpage )	/* pointer to image per face: */
			newf->image = ( Image * ) tface->tpage;
		else
			newf->image = NULL;

		newf->mode = tface->mode;	/* draw mode */
		newf->flag = tface->flag;	/* select flag */
		newf->transp = tface->transp;	/* transparency flag */
	} else {
		newf->mode = TF_DYNAMIC;	/* just to initialize it to something meaninful, */
		/* since without tfaces there are no tface->mode's, obviously. */
		newf->image = NULL;
		newf->uv = PyList_New( 0 );
	}

	newf->mat_nr = mat_nr;
	newf->mf_flag = flag; /* MFace flag */
	newf->orig_index = orig_index;

	if( col ) {
		newf->col = PyList_New( 4 );
		for( i = 0; i < 4; i++, col++ ) {
			PyList_SetItem( newf->col, i,
					( PyObject * ) newcol( col->b, col->g,
							       col->r,
							       col->a ) );
		}
	} else
		newf->col = PyList_New( 0 );

	return newf;
}

static BPy_NMEdge *nmedge_from_index( BPy_NMesh * mesh, int v0idx, int v1idx)
{
  BPy_NMVert *v1=(BPy_NMVert *)PyList_GetItem( mesh->verts, v0idx);
  BPy_NMVert *v2=(BPy_NMVert *)PyList_GetItem( mesh->verts, v1idx);
  return new_NMEdge(v1, v2, (char)0.0, 0);
}

static BPy_NMEdge *nmedge_from_data( BPy_NMesh * mesh, MEdge *edge )
{
  BPy_NMVert *v1=(BPy_NMVert *)PyList_GetItem( mesh->verts, edge->v1 );
  BPy_NMVert *v2=(BPy_NMVert *)PyList_GetItem( mesh->verts, edge->v2 );
  return new_NMEdge(v1, v2, edge->crease, edge->flag);
}

static BPy_NMVert *nmvert_from_data( MVert * vert, MSticky * st, float *co,
	int idx, char flag )
{
	BPy_NMVert *mv = PyObject_NEW( BPy_NMVert, &NMVert_Type );

	mv->co[0] = co[0];
	mv->co[1] = co[1];
	mv->co[2] = co[2];

	mv->no[0] = (float)(vert->no[0] / 32767.0);
	mv->no[1] = (float)(vert->no[1] / 32767.0);
	mv->no[2] = (float)(vert->no[2] / 32767.0);

	if( st ) {
		mv->uvco[0] = st->co[0];
		mv->uvco[1] = st->co[1];
		mv->uvco[2] = 0.0;

	} else
		mv->uvco[0] = mv->uvco[1] = mv->uvco[2] = 0.0;

	mv->index = idx;
	mv->flag = flag & 1;

	return mv;
}

static int get_active_faceindex( Mesh * me )
{
	if( me == NULL )
		return -1;

	if (me->act_face != -1 && me->act_face < me->totface)
		return me->act_face; 
	return -1;
}

static BPy_NMVert *nmvert_from_float(float *co, float *no, int idx) {
	BPy_NMVert *mv;

	mv = PyObject_NEW( BPy_NMVert, &NMVert_Type );

	mv->index = idx;

	mv->co[0] = co[0];
	mv->co[1] = co[1];
	mv->co[2] = co[2];

	mv->no[0] = no[0];
	mv->no[1] = no[1];
	mv->no[2] = no[2];

	mv->uvco[0] = mv->uvco[1] = mv->uvco[2] = 0.0;

	mv->flag = 0;

	return mv;
}

static BPy_NMFace *nmface_from_index( BPy_NMesh * mesh, int vidxs[4], char mat_nr )
{
	BPy_NMFace *newf = PyObject_NEW( BPy_NMFace, &NMFace_Type );
	int i, len;

	if( vidxs[3] )
		len = 4;
	else if( vidxs[2] )
		len = 3;
	else
		len = 2;

	newf->v = PyList_New( len );

	for( i = 0; i < len; i++ )
		PyList_SetItem( newf->v, i,
				EXPP_incr_ret( PyList_GetItem
					       ( mesh->verts, vidxs[i] ) ) );

	newf->mode = TF_DYNAMIC;	/* just to initialize it to something meaninful, */
	/* since without tfaces there are no tface->mode's, obviously. */
	newf->image = NULL;
	newf->uv = PyList_New( 0 );

	newf->mat_nr = mat_nr;
	newf->mf_flag = 0;
	newf->orig_index = -1;

	newf->col = PyList_New( 0 );

	return newf;
}

/* RATHER EVIL FUNCTION BORROWED FROM fix_faceindices IN editmesh.c */
static void correctFaceIndex(int vidx[4])
{
	if (vidx[3]) {
		if (vidx[1] == 0) {
			vidx[1] = vidx[2];
			vidx[2] = vidx[3];
			vidx[3] = vidx[0];
			vidx[0] = 0;
		}
		if (vidx[2] == 0) {
			int t = vidx[1];
			vidx[2] = vidx[0];
			vidx[1] = vidx[3];
			vidx[3] = t;
			vidx[0] = 0;
		}
		if (vidx[3] == 0) {
			vidx[3] = vidx[2];
			vidx[2] = vidx[1];
			vidx[1] = vidx[0];
			vidx[0] = 0;
		}
	}
	else if (vidx[1] == 0) {
		vidx[1] = vidx[2];
		vidx[2] = vidx[0];
		vidx[0] = 0;
	}
	else if (vidx[2] == 0) {
		vidx[2] = vidx[1];
		vidx[1] = vidx[0];
		vidx[0] = 0;
	}
}

/*
	CREATES A NMESH FROM DISPLIST DATA. INSPIRED BY THE FUNCTIONS CALLED WHEN
	CONVERTING OBJECTS TO MESH.
 */
static PyObject *new_NMesh_displist(ListBase *lb, Object *ob)
{
	BPy_NMesh *me;
	DispList *dl;
	float *data, *ndata;
	float normal[3] = {1, 0, 0};
	int vidx[4];
	int parts, p1, p2, p3, p4, a, b, one_normal=0, ioffset=0;
	int *index;

		/* Note: This routine does not create new edges for the faces
		 * it adds... should be fixed for consistency.
		 */

	if (ob->type == OB_CURVE || ob->type == OB_FONT)
		one_normal = 1;

	me = PyObject_NEW( BPy_NMesh, &NMesh_Type );
	me->name = EXPP_incr_ret( Py_None );
	me->flags = 0;
	me->mode = ME_TWOSIDED;	/* default for new meshes */
	me->subdiv[0] = NMESH_SUBDIV;
	me->subdiv[1] = NMESH_SUBDIV;
	me->smoothresh = NMESH_SMOOTHRESH;
	me->edges = PyList_New( 0 );

	me->object = ob;
	me->materials = EXPP_PyList_fromMaterialList( ob->mat, ob->totcol, 0 );

	me->verts = PyList_New( 0 ); 
	me->faces = PyList_New( 0 );

	memset(&me->fdata, 0, sizeof(me->fdata));
	me->totfdata = 0;

	dl= lb->first;
	while(dl) {
		parts= dl->parts;
		index= dl->index;
		data= dl->verts;
		ndata= dl->nors;

		switch(dl->type) {
		case DL_SEGM:
			for(a=0; a<dl->parts*dl->nr; a++, data+=3) {
				PyList_Append(me->verts, ( PyObject * ) nmvert_from_float(data, normal, a));
			}

			vidx[2] = vidx[3] = 0;

			for(a=0; a<dl->parts; a++) {
				for(b=1; b<dl->nr; b++) {
					int v0 = ioffset + a * dl->nr + b-1;
					int v1 = ioffset + a * dl->nr + b;
					PyList_Append(me->edges, ( PyObject * ) nmedge_from_index(me, v0, v1));
				}
			}
			ioffset += dl->parts * dl->nr;
			break;
		case DL_POLY:
			for(a=0; a<dl->parts*dl->nr; a++, data+=3) {
				PyList_Append(me->verts, ( PyObject * ) nmvert_from_float(data, normal, a));
			}

			vidx[2] = vidx[3] = 0;

			for(a=0; a<dl->parts; a++) {
				for(b=0; b<dl->nr; b++) {
					int v1, v0 = ioffset + a * dl->nr + b;
					if(b==dl->nr-1) v1 = ioffset + a * dl->nr;
					else v1= ioffset + a * dl->nr + b+1;
					PyList_Append(me->edges, ( PyObject * ) nmedge_from_index(me, v0, v1));
				}
			}
			ioffset += dl->parts * dl->nr;
			break;
		case DL_SURF:
			for(a=0; a<dl->parts*dl->nr; a++, data+=3, ndata+=3) {
				PyList_Append(me->verts, ( PyObject * ) nmvert_from_float(data, ndata, a));
			}

			for(a=0; a<dl->parts; a++) {
				
				if (surfindex_displist(dl, a, &b, &p1, &p2, &p3, &p4)==0)
					break;
				
				for(; b<dl->nr; b++) {
					vidx[0] = p2 + ioffset;
					vidx[1] = p1 + ioffset;
					vidx[2] = p3 + ioffset;
					vidx[3] = p4 + ioffset;
					correctFaceIndex(vidx);

					PyList_Append(me->faces, ( PyObject * ) nmface_from_index(me, vidx, (char)dl->col));

					p2 = p1;
					p4 = p3;
					p1++;
					p3++;
				}
			}
			ioffset += dl->parts * dl->nr;
			break;

		case DL_INDEX3:
			if (one_normal)
				for(a=0; a<dl->nr; a++, data+=3)
					PyList_Append(me->verts, ( PyObject * ) nmvert_from_float(data, ndata, a));
			else
				for(a=0; a<dl->nr; a++, data+=3, ndata+=3)
					PyList_Append(me->verts, ( PyObject * ) nmvert_from_float(data, ndata, a));

			while(parts--) {
				vidx[0] = index[0] + ioffset;
				vidx[1] = index[1] + ioffset;
				vidx[2] = index[2] + ioffset;
				vidx[3] = 0;
				correctFaceIndex(vidx);

				PyList_Append(me->faces, ( PyObject * ) nmface_from_index(me, vidx, (char)dl->col));
				index+= 3;
			}
			ioffset += dl->nr;

			break;

		case DL_INDEX4:
			for(a=0; a<dl->nr; a++, data+=3, ndata+=3) {
				PyList_Append(me->verts, ( PyObject * ) nmvert_from_float(data, ndata, a));
			}

			while(parts--) {
				vidx[0] = index[0] + ioffset;
				vidx[1] = index[1] + ioffset;
				vidx[2] = index[2] + ioffset;
				vidx[3] = index[3] + ioffset;
				correctFaceIndex(vidx);

				PyList_Append(me->faces, ( PyObject * ) nmface_from_index(me, vidx, (char)dl->col));
				index+= 4;
			}
			ioffset += dl->nr;
			break;
		}
		dl= dl->next;
	}

	return ( PyObject * ) me;
}

static PyObject *new_NMesh_internal( Mesh * oldmesh,
				     DerivedMesh *dm )
{
	BPy_NMesh *me = PyObject_NEW( BPy_NMesh, &NMesh_Type );
	me->flags = 0;
	me->mode = ME_TWOSIDED;	/* default for new meshes */
	me->subdiv[0] = NMESH_SUBDIV;
	me->subdiv[1] = NMESH_SUBDIV;
	me->smoothresh = NMESH_SMOOTHRESH;

	me->object = NULL;	/* not linked to any object yet */

	if( !oldmesh ) {
		me->name = EXPP_incr_ret( Py_None );
		me->materials = PyList_New( 0 );
		me->verts = PyList_New( 0 );
		me->edges = PyList_New( 0 );
		me->faces = PyList_New( 0 );
		me->mesh = 0;
		memset(&me->fdata, 0, sizeof(me->fdata));
		me->totfdata = 0;
	} else {
		MVert *mverts;
		MSticky *msticky;
		MFace *mfaces;
		MTFace *tfaces;
		MCol *mcols;
		MEdge *medges;
		CustomData *fdata;
		int i, totvert, totface, totedge;

		me->name = PyString_FromString( oldmesh->id.name + 2 );
		me->mesh = oldmesh;
		me->mode = oldmesh->flag;	/* yes, we save the mesh flags in nmesh->mode */
		me->subdiv[0] = oldmesh->subdiv;
		me->subdiv[1] = oldmesh->subdivr;
		me->smoothresh = oldmesh->smoothresh;

		me->sel_face = get_active_faceindex( oldmesh );

		if( dm ) {
			msticky = NULL;
			mverts = dm->getVertArray(dm);
			mfaces = dm->getFaceArray(dm);
			tfaces = dm->getFaceDataArray(dm, CD_MTFACE);
			mcols = dm->getFaceDataArray(dm, CD_MCOL);
			medges = dm->getEdgeArray(dm);
			fdata = &dm->faceData;

			totvert = dm->getNumVerts(dm);
			totedge = dm->getNumEdges(dm);
			totface = dm->getNumFaces(dm);;
		} else {
			msticky = oldmesh->msticky;
			mverts = oldmesh->mvert;
			mfaces = oldmesh->mface;
			tfaces = oldmesh->mtface;
			mcols = oldmesh->mcol;
			medges = oldmesh->medge;
			fdata = &oldmesh->fdata;

			totvert = oldmesh->totvert;
			totface = oldmesh->totface;
			totedge = oldmesh->totedge;
		}

		/* copy non active mcol and mtface layers, these can't be edited
		   but will be preserved */
		CustomData_copy( fdata, &me->fdata, CD_MASK_MCOL|CD_MASK_MTFACE,
			CD_DUPLICATE, totface );
		me->totfdata = totface;

		if( msticky )
			me->flags |= NMESH_HASVERTUV;
		if( tfaces )
			me->flags |= NMESH_HASFACEUV;
		if( mcols )
			me->flags |= NMESH_HASMCOL;

		me->verts = PyList_New( totvert );

		for( i = 0; i < totvert; i++ ) {
			MVert *oldmv = &mverts[i];
			MSticky *oldst = msticky ? &msticky[i] : NULL;

			PyList_SetItem( me->verts, i,
					( PyObject * ) nmvert_from_data( oldmv,
									 oldst,
									 oldmv->co,
									 i,
									 oldmv->flag ) );
		}

		me->faces = PyList_New( totface );
		for( i = 0; i < totface; i++ ) {
			MTFace *oldtf = tfaces ? &tfaces[i] : NULL;
			MCol *oldmc = mcols ? &mcols[i * 4] : NULL;
			MFace *oldmf = &mfaces[i];
			int vidxs[4];
			vidxs[0] = oldmf->v1;
			vidxs[1] = oldmf->v2;
			vidxs[2] = oldmf->v3;
			vidxs[3] = oldmf->v4;

			PyList_SetItem( me->faces, i,
					( PyObject * ) nmface_from_data( me,
									 vidxs,
									 oldmf->
									 mat_nr,
									 oldmf->
									 flag,
									 oldtf,
									 oldmc,
									 i) );
		}

		me->edges = PyList_New( totedge );
		for( i = 0; i < totedge; i++ )
		{
			MEdge *edge = &medges[i];
			PyList_SetItem( me->edges, i, (PyObject*)nmedge_from_data ( me, edge ) );
		}

		me->materials = EXPP_PyList_fromMaterialList( oldmesh->mat, oldmesh->totcol, 0 );
	}

	return ( PyObject * ) me;
}

PyObject *new_NMesh( Mesh * oldmesh )
{
	return new_NMesh_internal( oldmesh, NULL );
}

static PyObject *M_NMesh_New( PyObject * self, PyObject * args )
{
	char *name = NULL;
	PyObject *ret = NULL;

	if( !PyArg_ParseTuple( args, "|s", &name ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected nothing or a string as argument" );

	ret = new_NMesh( NULL );

	if( ret && name ) {
		BPy_NMesh *nmesh = ( BPy_NMesh * ) ret;
		Py_DECREF( nmesh->name );
		nmesh->name = PyString_FromString( name );
	}

	return ret;
}

static PyObject *M_NMesh_GetRaw( PyObject * self, PyObject * args )
{
	char *name = NULL;
	Mesh *oldmesh = NULL;

	if( !PyArg_ParseTuple( args, "|s", &name ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected string argument (or nothing)" );

	if( name ) {
		oldmesh = ( Mesh * ) GetIdFromList( &( G.main->mesh ), name );

		if( !oldmesh )
			return EXPP_incr_ret( Py_None );
	}

	return new_NMesh( oldmesh );
}

static PyObject *M_NMesh_GetNames(PyObject *self)
{
	PyObject *names = PyList_New(0), *tmpstr;
	Mesh *me = G.main->mesh.first;

	while (me) {
		tmpstr = PyString_FromString(me->id.name+2);
		PyList_Append(names, tmpstr);
		Py_DECREF(tmpstr);
		me = me->id.next;
	}

	return names;
}

/* Note: NMesh.GetRawFromObject gets the display list mesh from Blender:
 * the vertices are already transformed / deformed. */
static PyObject *M_NMesh_GetRawFromObject( PyObject * self, PyObject * args )
{
	Object *ob;
	PyObject *nmesh;
	ListBase *lb=0;
	DispList *dl;
	char *name;

	if( !PyArg_ParseTuple( args, "s", &name ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected string argument" );

	ob = ( Object * ) GetIdFromList( &( G.main->object ), name );

	if( !ob )
		return EXPP_ReturnPyObjError( PyExc_AttributeError, name );

 
 	switch (ob->type) {
 	case OB_MBALL:
 		if( is_basis_mball(ob)) {
 			lb= &ob->disp;
 			if(lb->first==0) makeDispListMBall(ob);
 			nmesh = new_NMesh_displist(lb, ob);
 		}
 		else {
 			return EXPP_ReturnPyObjError( PyExc_AttributeError, "Object does not have geometry data" );
 		}
 		break;
 	case OB_FONT:
 	case OB_CURVE:
 		{
 			Curve *cu= ob->data;
 			
 			lb= &cu->disp;
 			if(lb->first==0) makeDispListCurveTypes(ob, 0);
 
 			dl= lb->first;
 			if(dl==0)
 				return EXPP_ReturnPyObjError( PyExc_AttributeError, "Object does not have geometry data" );
 			
 			if(dl->nors==0) addnormalsDispList(ob, lb);
 
 			nmesh = new_NMesh_displist(lb, ob);
 		}
 		break;
 	case OB_SURF:
 	
 		lb= &((Curve *)ob->data)->disp;
 		if(lb->first==0) makeDispListCurveTypes(ob, 0);
 		
 		dl= lb->first;
 		if(dl==0)
 			return EXPP_ReturnPyObjError( PyExc_AttributeError, "Object does not have geometry data" );
 		
 		if(dl->nors==0) addnormalsDispList(ob, lb);
 		nmesh = new_NMesh_displist(lb, ob);
 
 		break;
 	case OB_MESH:
 		{
 			CustomDataMask dataMask = CD_MASK_BAREMESH | CD_MASK_MTFACE
 			                          | CD_MASK_MCOL;
			DerivedMesh *dm = mesh_create_derived_render( ob, dataMask );
			nmesh = new_NMesh_internal( ob->data, dm );
			dm->release(dm);
 		}
 		break;
 	default:
 		return EXPP_ReturnPyObjError( PyExc_AttributeError, "Object does not have geometry data" );
  	}

/* @hack: to mark that (deformed) mesh is readonly, so the update function
 * will not try to write it. */

	( ( BPy_NMesh * ) nmesh )->mesh = 0;

	return nmesh;
}

static void mvert_from_data( MVert * mv, MSticky * st, BPy_NMVert * from )
{
	mv->co[0] = from->co[0];
	mv->co[1] = from->co[1];
	mv->co[2] = from->co[2];

	mv->no[0] = (short)(from->no[0] * 32767.0);
	mv->no[1] = (short)(from->no[1] * 32767.0);
	mv->no[2] = (short)(from->no[2] * 32767.0);

	mv->flag = ( from->flag & 1 );
	mv->mat_nr = 0;

	if( st ) {
		st->co[0] = from->uvco[0];
		st->co[1] = from->uvco[1];
	}
}

static int assignFaceUV( MTFace * tf, BPy_NMFace * nmface )
{
	PyObject *fuv, *tmp;
	int i;
	int len;

	fuv = nmface->uv;
	/* if no UV info, allows things to proceed as normal */
	if( PySequence_Length( fuv ) == 0 ) {
		tf->uv[0][0] = 0.0f; tf->uv[0][1] = 1.0f;
		tf->uv[1][0] = 0.0f; tf->uv[1][1] = 0.0f;
		tf->uv[2][0] = 1.0f; tf->uv[2][1] = 0.0f;
		tf->uv[3][1] = 1.0f; tf->uv[3][1] = 1.0f;
		return 1;
	}

	/* if there are too many uv coordinates, only take the first 4 */
	len = PySequence_Length( fuv );
	if( len > 4 )
		len = 4;

	/* fuv = [(u_1, v_1), ... (u_n, v_n)] */
	for( i = 0; i < len; i++ ) {
		tmp = PySequence_GetItem( fuv, i );	/* stolen reference ! */
		if( !PyArg_ParseTuple
		    ( tmp, "ff", &( tf->uv[i][0] ), &( tf->uv[i][1] ) ) ) {
				PyErr_SetString ( PyExc_TypeError,
						      "expected tuple of two floats for uv" );
				Py_DECREF( tmp );
				return 0;
		}
		Py_DECREF( tmp );
	}
	if( nmface->image ) {	/* image assigned ? */
		tf->tpage = ( void * ) nmface->image;
	} else
		tf->tpage = 0;

	tf->mode = nmface->mode;	/* copy mode */
	tf->flag = (char)nmface->flag;	/* copy flag */
	tf->transp = nmface->transp;	/* copy transp flag */

	return 1;
}

static int mface_from_data( MFace * mf, CustomData *fdata, int findex,
			     BPy_NMFace * from )
{
	BPy_NMVert *nmv;
	MTFace *tf = CustomData_get(fdata, findex, CD_MTFACE);
	MCol *col = CustomData_get(fdata, findex, CD_MCOL);

	int numverts = PyList_Size( from->v );
	if( numverts != 3 && numverts != 4 ) {	/* face can only have three or four verts */
		PyErr_SetString ( PyExc_RuntimeError,
				"faces must have at 3 or 4 vertices" );
		return 0;
	}

	nmv = ( BPy_NMVert * ) PyList_GetItem( from->v, 0 );
	if( BPy_NMVert_Check( nmv ) && nmv->index != -1 )
		mf->v1 = nmv->index;
	else 
		return -1;

	nmv = ( BPy_NMVert * ) PyList_GetItem( from->v, 1 );
	if( BPy_NMVert_Check( nmv ) && nmv->index != -1 )
		mf->v2 = nmv->index;
	else
		return -1;

	nmv = ( BPy_NMVert * ) PyList_GetItem( from->v, 2 );
	if( BPy_NMVert_Check( nmv ) && nmv->index != -1 )
		mf->v3 = nmv->index;
	else
		return -1;

	if( numverts == 4 ) {
		nmv = ( BPy_NMVert * ) PyList_GetItem( from->v, 3 );
		if( BPy_NMVert_Check( nmv ) && nmv->index != -1 )
			mf->v4 = nmv->index;
		else
			return -1;
	}

	if( tf )
		if( !assignFaceUV( tf, from ) )
			return 0;

	mf->mat_nr = from->mat_nr;
	mf->flag = from->mf_flag;

	if( col ) {
		int i, len = PySequence_Length( from->col );

		if( len > 4 )
			len = 4;

		for( i = 0; i < len; i++, col++ ) {
			BPy_NMCol *mc =
				( BPy_NMCol * ) PySequence_GetItem( from->col,
								    i );
			if( !BPy_NMCol_Check( mc ) ) {
				Py_DECREF( mc );
				continue;
			}

			col->b = mc->r;
			col->g = mc->g;
			col->r = mc->b;
			col->a = mc->a;

			Py_DECREF( mc );
		}
	}

	test_index_face(mf, fdata, findex, numverts);

	return 1;
}

/* check for a valid UV sequence */
static int check_validFaceUV( BPy_NMesh * nmesh )
{
	PyObject *faces;
	BPy_NMFace *nmface;
	int i, n;

	faces = nmesh->faces;
	for( i = 0; i < PySequence_Length( faces ); i++ ) {
		nmface = ( BPy_NMFace * ) PyList_GetItem( faces, i );
		n = PySequence_Length( nmface->uv );
		if( n != PySequence_Length( nmface->v ) ) {
			if( n > 0 )
				printf( "Warning: different length of vertex and UV coordinate " "list in face!\n" );
			return 0;
		}
	}
	return 1;
}

/* this is a copy of unlink_mesh in mesh.c, because ... */
static void EXPP_unlink_mesh( Mesh * me )
{
	int a;

	if( me == 0 )
		return;

	for( a = 0; a < me->totcol; a++ ) {
		if( me->mat[a] )
			me->mat[a]->id.us--;
		me->mat[a] = 0;
	}

/*	... here we want to preserve mesh keys */
/* if users want to get rid of them, they can use mesh.removeAllKeys() */
/*
	if(me->key) me->key->id.us--;
	me->key= 0;
*/
	if( me->texcomesh )
		me->texcomesh = 0;

	me->totcol = 0;
}

static int unlink_existingMeshData( Mesh * mesh )
{
	MDeformVert *dvert= NULL;

	EXPP_unlink_mesh( mesh );

	if(mesh->dvert) {
		/* we don't want to remove dvert here, check_dverts still needs it */
		dvert= mesh->dvert;
		CustomData_set_layer( &mesh->vdata, CD_MDEFORMVERT, NULL );
	}

	CustomData_free( &mesh->vdata, mesh->totvert );
	CustomData_free( &mesh->edata, mesh->totedge );
	CustomData_free( &mesh->fdata, mesh->totface );
	mesh_update_customdata_pointers( mesh );
	
	if(dvert)
		mesh->dvert= CustomData_add_layer( &mesh->vdata, CD_MDEFORMVERT,
			CD_ASSIGN, dvert, mesh->totvert );

	mesh->totedge = 0;

	if( mesh->mat ) {
		MEM_freeN( mesh->mat );
		mesh->mat = NULL;
	}

	return 1;
}

Material **nmesh_updateMaterials( BPy_NMesh * nmesh )
{
	Material **matlist;
	Mesh *mesh = nmesh->mesh;
	int len = PyList_Size( nmesh->materials );

	if( !mesh ) {
		printf( "FATAL INTERNAL ERROR: illegal call to updateMaterials()\n" );
		return 0;
	}

	if( len > 0 ) {
		if (len>16) len = 16;
		matlist = EXPP_newMaterialList_fromPyList( nmesh->materials );
		EXPP_incr_mats_us( matlist, len );

		if( mesh->mat )
			MEM_freeN( mesh->mat );

		mesh->mat = matlist;

	} else {
		matlist = 0;
	}
	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 * ) mesh );

	return matlist;
}

PyObject *NMesh_assignMaterials_toObject( BPy_NMesh * nmesh, Object * ob )
{
	BPy_Material *pymat;
	Material *ma;
	int i;
	short old_matmask;
	Mesh *mesh = nmesh->mesh;
	int nmats;		/* number of mats == len(nmesh->materials) */

	old_matmask = ob->colbits;	/*@ HACK: save previous colbits */
	ob->colbits = 0;	/* make assign_material work on mesh linked material */

	nmats = PyList_Size( nmesh->materials );

	if( nmats > 0 && !mesh->mat ) {
		ob->totcol = (char)nmats;
		mesh->totcol = (short)nmats;
		mesh->mat =
			MEM_callocN( sizeof( void * ) * nmats, "bpy_memats" );

		if( ob->mat )
			MEM_freeN( ob->mat );
		ob->mat =
			MEM_callocN( sizeof( void * ) * nmats, "bpy_obmats" );
	}

	for( i = 0; i < nmats; i++ ) {
		pymat = ( BPy_Material * ) PySequence_GetItem( nmesh->
							       materials, i );

		if( BPy_Material_Check( ( PyObject * ) pymat ) ) {
			ma = pymat->material;
			assign_material( ob, ma, i + 1 );	/*@ XXX don't use this function anymore */
		} else {
			Py_DECREF( pymat );
			return EXPP_ReturnPyObjError( PyExc_TypeError,
						      "expected Material type in attribute list 'materials'!" );
		}

		Py_DECREF( pymat );
	}

	ob->colbits = old_matmask;	/*@ HACK */

	ob->actcol = 1;
	Py_RETURN_NONE;
}

static void fill_medge_from_nmesh(Mesh * mesh, BPy_NMesh * nmesh)
{
  int i,j;
  MEdge *faces_edges=NULL;
  int   tot_faces_edges=0;
  int tot_valid_faces_edges=0;
  int nmeshtotedges=PyList_Size(nmesh->edges);
  int tot_valid_nmedges=0;
  BPy_NMEdge **valid_nmedges=NULL;

  valid_nmedges=MEM_callocN(nmeshtotedges*sizeof(BPy_NMEdge *), "make BPy_NMEdge");

  /* First compute the list of edges that exists because faces exists */
  make_edges(mesh, 0);	/* 0 = draw all edges */

  faces_edges=mesh->medge;
  tot_faces_edges=mesh->totedge;
  tot_valid_faces_edges=tot_faces_edges;

  mesh->medge= CustomData_set_layer(&mesh->edata, CD_MEDGE, NULL);
  CustomData_free_layer_active(&mesh->edata, CD_MEDGE, mesh->totedge);
  mesh->totedge = 0;

  /* Flag each edge in faces_edges that is already in nmesh->edges list.
   * Flaging an edge means MEdge v1=v2=0.
   * Each time an edge is flagged, tot_valid_faces_edges is decremented.
   *
   * Also store in valid_nmedges pointers to each valid NMEdge in nmesh->edges.
   * An invalid NMEdge is an edge that has a vertex that is not in the vertices 
   * list. Ie its index is -1. 
   * Each time an valid NMEdge is flagged, tot_valid_nmedges is incremented.
   */
  for( i = 0; i < nmeshtotedges; ++i )
  {
    int v1idx,v2idx;
    BPy_NMEdge *edge=( BPy_NMEdge *) PyList_GetItem(nmesh->edges, i);
    BPy_NMVert *v=(BPy_NMVert *)edge->v1;
    v1idx=v->index;
    v=(BPy_NMVert *)edge->v2;
    v2idx=v->index;
    if (-1 == v1idx || -1 == v2idx) continue;
    valid_nmedges[tot_valid_nmedges]=edge;
    ++tot_valid_nmedges;
    for( j = 0; j < tot_faces_edges; j++ )
    {
      MEdge *me=faces_edges+j;
      if ( ((int)me->v1==v1idx && (int)me->v2==v2idx) ||
           ((int)me->v1==v2idx && (int)me->v2==v1idx) )
      {
        me->v1=0; me->v2=0;
        --tot_valid_faces_edges;
      }
    }
  }

  /* tot_valid_faces_edges < 0 causes a sigsegv crash, so we
   * clamp to prevent it
   * (this is related to faces (correctly) requiring at least 3 verts now,
   * which can break old scripts -- maybe we should also warn about the
   * 'broken' mesh the user created, but for now, until we investigate
   * better, this should do) */
  if (tot_valid_faces_edges < 0) tot_valid_faces_edges = 0;

  /* Now we have the total count of valid edges */
  mesh->totedge=tot_valid_nmedges+tot_valid_faces_edges;
  mesh->medge= CustomData_add_layer( &mesh->edata, CD_MEDGE, CD_CALLOC, NULL,
  	mesh->totedge );

  for ( i = 0; i < tot_valid_nmedges; ++i )
  {
    BPy_NMEdge *edge=valid_nmedges[i];
    MEdge *medge=mesh->medge+i;
    int v1=((BPy_NMVert *)edge->v1)->index;
    int v2=((BPy_NMVert *)edge->v2)->index;
    medge->v1=v1;
    medge->v2=v2;
    medge->flag=edge->flag;
    medge->crease=edge->crease;
  }
  for ( i = 0, j = tot_valid_nmedges; i < tot_faces_edges; ++i )
  {
    MEdge *edge=faces_edges+i;
    if (edge->v1!=0 || edge->v2!=0)  // valid edge
    {
      MEdge *medge=mesh->medge+j;
      medge->v1=edge->v1;
      medge->v2=edge->v2;
      medge->flag=ME_EDGEDRAW|ME_EDGERENDER;
      medge->crease=0;
      ++j;
    }
  }

  MEM_freeN( valid_nmedges );
  MEM_freeN( faces_edges );
}

/* this should ensure meshes don't end up with wrongly sized
 * me->dvert arrays, which can cause hangs; it's not ideal,
 * it's better to wrap dverts in NMesh, but it should do for now
 * since there are also methods in NMesh to edit dverts in the actual
 * mesh in Blender and anyway this is memory friendly */
static void check_dverts(Mesh *me, int old_totvert)
{
	int totvert = me->totvert;

	/* if vert count didn't change or there are no dverts, all is fine */
	if ((totvert == old_totvert) || (!me->dvert)) return;
	/* if all verts have been deleted, free old dverts */
	else if (totvert == 0) {
		CustomData_free_layer_active( &me->vdata, CD_MDEFORMVERT, old_totvert );
		me->dvert= NULL;
	}
	else {
		/* verts added or removed, make new me->dvert */
		MDeformVert *mdv = MEM_callocN( sizeof(MDeformVert)*totvert, "mdv" );
		copy_dverts( mdv, me->dvert, MIN2( old_totvert, totvert ) );
		free_dverts( me->dvert, old_totvert );
		me->dvert= CustomData_set_layer( &me->vdata, CD_MDEFORMVERT, mdv );
	}
}

static void check_mtface_mcols( BPy_NMesh *nmesh, Mesh *mesh )
{
	int i;

	/* copy non-active mcol and mtface layers based on original index */
	CustomData_merge( &nmesh->fdata, &mesh->fdata, CD_MASK_MCOL|CD_MASK_MTFACE,
		CD_DEFAULT, mesh->totface );

	for( i = 0; i < mesh->totface; i++ ) {
		BPy_NMFace *mf =
			( BPy_NMFace * ) PySequence_GetItem( nmesh->faces, i );

		if ( mf->orig_index != -1 )
			CustomData_copy_data( &nmesh->fdata, &mesh->fdata, mf->orig_index,
				i, 1 );

		Py_DECREF( mf );
	}

	/* add new layers if needed */
	if( ( nmesh->flags & NMESH_HASMCOL ) &&
		!CustomData_has_layer( &mesh->fdata, CD_MCOL ) ) {
		mesh->mcol = CustomData_add_layer( &mesh->fdata, CD_MCOL,
			CD_DEFAULT, NULL, mesh->totface );
	}

	if( ( nmesh->flags & NMESH_HASFACEUV ) || check_validFaceUV( nmesh ) ) {
		if ( !CustomData_has_layer( &mesh->fdata, CD_MTFACE) )
			make_tfaces( mesh );
		nmesh->flags |= NMESH_HASFACEUV;
	}
	else
		CustomData_free_layers( &mesh->fdata, CD_MTFACE, mesh->totface );

	/* active uvs and colors from NMFace will be written in mface_from_data */
}

static int convert_NMeshToMesh( Mesh * mesh, BPy_NMesh * nmesh)
{
	MFace *newmf;
	MVert *newmv;
	MSticky *newst;
	int nmeshtotedges;
	int badfaces;
	int i, j, ok;

	/* Minor note: we used 'mode' because 'flag' was already used internally
	 * by nmesh */
	mesh->flag = nmesh->mode;
	mesh->smoothresh = nmesh->smoothresh;
	mesh->subdiv = nmesh->subdiv[0];
	mesh->subdivr = nmesh->subdiv[1];

	/*@ material assignment moved to PutRaw */
	mesh->totvert = PySequence_Length( nmesh->verts );
	if( mesh->totvert ) {
		if( nmesh->flags & NMESH_HASVERTUV )
			mesh->msticky = CustomData_add_layer( &mesh->vdata, CD_MSTICKY,
				CD_CALLOC, NULL, mesh->totvert );

		mesh->mvert = CustomData_add_layer( &mesh->vdata, CD_MVERT, CD_CALLOC,
			NULL, mesh->totvert );
	}

	if( mesh->totvert )
		mesh->totface = PySequence_Length( nmesh->faces );
	else
		mesh->totface = 0;

	if( mesh->totface ) {
		check_mtface_mcols( nmesh, mesh );

		mesh->mface = CustomData_add_layer( &mesh->fdata, CD_MFACE, CD_CALLOC,
			NULL, mesh->totface );
	}

	mesh_update_customdata_pointers( mesh );

	/*@ This stuff here is to tag all the vertices referenced
	 * by faces, then untag the vertices which are actually
	 * in the vert list. Any vertices untagged will be ignored
	 * by the mface_from_data function. It comes from my
	 * screwed up decision to not make faces only store the
	 * index. - Zr
	 */
	for( i = 0; i < mesh->totface; i++ ) {
		BPy_NMFace *mf =
			( BPy_NMFace * ) PySequence_GetItem( nmesh->faces, i );

		j = PySequence_Length( mf->v );
		while( j-- ) {
			BPy_NMVert *mv =
				( BPy_NMVert * ) PySequence_GetItem( mf->v,
								     j );
			if( BPy_NMVert_Check( mv ) )
				mv->index = -1;
			Py_DECREF( mv );
		}

		Py_DECREF( mf );
	}
  /* do the same for edges if there is edge data */
    nmeshtotedges=PyList_Size(nmesh->edges);
    for( i = 0; i < nmeshtotedges; ++i )
    {
      BPy_NMEdge *edge=( BPy_NMEdge *) PyList_GetItem(nmesh->edges, i);
      BPy_NMVert *v=(BPy_NMVert *)edge->v1;
      v->index=-1;
      v=(BPy_NMVert *)edge->v2;
      v->index=-1;
    }

	for( i = 0; i < mesh->totvert; i++ ) {
		BPy_NMVert *mv =
			( BPy_NMVert * ) PySequence_GetItem( nmesh->verts, i );
		mv->index = i;
		Py_DECREF( mv );
	}

	newmv = mesh->mvert;
	newst = mesh->msticky;
	for( i = 0; i < mesh->totvert; i++ ) {
		PyObject *mv = PySequence_GetItem( nmesh->verts, i );
		mvert_from_data( newmv, newst, ( BPy_NMVert * ) mv );
		Py_DECREF( mv );

		newmv++;
		if( newst )
			newst++;
	}

	newmf = mesh->mface;
	badfaces = 0;
	for( i = 0; i < mesh->totface; i++ ) {
		PyObject *mf = PySequence_GetItem( nmesh->faces, i );
		ok = mface_from_data( newmf, &mesh->fdata, i, ( BPy_NMFace * ) mf );
		Py_DECREF( mf );
		if( ok == 0)
			return 0;
		else if ( ok == -1 )
			++badfaces;
		else
			newmf++;
	}
	mesh->totface -= badfaces;

		/* Always do this to ensure no loose edges in faces */
    fill_medge_from_nmesh(mesh, nmesh);

	return 1;
}

static PyObject *M_NMesh_PutRaw( PyObject * self, PyObject * args )
{
	char *name = NULL;
	Mesh *mesh = NULL;
	Object *ob = NULL;
	BPy_NMesh *nmesh;
	int recalc_normals = 1;
	int store_edges = 0;
	int old_totvert = 0;

	if( !PyArg_ParseTuple( args, "O!|sii",
			       &NMesh_Type, &nmesh, &name, &recalc_normals, &store_edges ) )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "expected an NMesh object and optionally also a string and two ints" );

	if( check_NMeshLists( nmesh ) )
		return NULL;

	if( name )
		mesh = ( Mesh * ) GetIdFromList( &( G.main->mesh ), name );

	if( !mesh || mesh->id.us == 0 ) {
		ob = add_object( OB_MESH );
		if( !ob ) {
			PyErr_SetString( PyExc_RuntimeError,
				"Fatal: could not create mesh object" );
			return 0;
		}

		if( !mesh )
			mesh = ( Mesh * ) ob->data;
		else
			set_mesh( ob, mesh );	// also does id.us++

		nmesh->object = ob;	// linking so vgrouping methods know which obj to work on
	}

	if( name )
		new_id( &( G.main->mesh ), &mesh->id, name );
	else if( nmesh->name && nmesh->name != Py_None )
		new_id( &( G.main->mesh ), &mesh->id,
			PyString_AsString( nmesh->name ) );

	old_totvert = mesh->totvert;

	unlink_existingMeshData( mesh );
	if( !convert_NMeshToMesh( mesh, nmesh ) )
		return NULL;
	nmesh->mesh = mesh;

	if (mesh->dvert) check_dverts(mesh, old_totvert);

	if( recalc_normals )
		mesh_calc_normals(mesh->mvert, mesh->totvert, mesh->mface, mesh->totface, NULL);

	mesh_update( mesh, nmesh->object );

	if( !during_script(  ) )
		EXPP_allqueue( REDRAWVIEW3D, 0 );

	if (ob && G.obedit) { /* prevents a crash when a new object is created */
		exit_editmode(EM_FREEDATA);
		enter_editmode(0);
	}

	// @OK...this requires some explanation:
	// Materials can be assigned two ways:
	// a) to the object data (in this case, the mesh)
	// b) to the Object
	//
	// Case a) is wanted, if Mesh data should be shared among objects,
	// as well as its materials (up to 16)
	// Case b) is wanted, when Mesh data should be shared, but not the
	// materials. For example, you want several checker boards sharing their
	// mesh data, but having different colors. So you would assign material
	// index 0 to all even, index 1 to all odd faces and bind the materials
	// to the Object instead (MaterialButtons: [OB] "link materials to object")
	//
	// This feature implies that pointers to materials can be stored in
	// an object or a mesh. The number of total materials MUST be
	// synchronized (ob->totcol <-> mesh->totcol). We avoid the dangerous
	// direct access by calling blenderkernel/material.c:assign_material().

	// The flags setting the material binding is found in ob->colbits, where 
	// each bit indicates the binding PER MATERIAL 

	if( ob ) {		// we created a new object
		NMesh_assignMaterials_toObject( nmesh, ob );
		EXPP_synchronizeMaterialLists( ob );
		return Object_CreatePyObject( ob );
	} else {
		mesh->mat =
			EXPP_newMaterialList_fromPyList( nmesh->materials );
		EXPP_incr_mats_us( mesh->mat,
				   PyList_Size( nmesh->materials ) );
		Py_RETURN_NONE;
	}

}

#undef MethodDef
#define MethodDef(func) \
	{#func, M_NMesh_##func, METH_VARARGS, M_NMesh_##func##_doc}

static struct PyMethodDef M_NMesh_methods[] = {
	MethodDef( Col ),
	MethodDef( Vert ),
	MethodDef( Face ),
	MethodDef( New ),
	MethodDef( GetRaw ),
	MethodDef( GetRawFromObject ),
	MethodDef( PutRaw ),
	{"GetNames", (PyCFunction)M_NMesh_GetNames, METH_NOARGS,
		M_NMesh_GetNames_doc},
	{NULL, NULL, 0, NULL}
};

static PyObject *M_NMesh_Modes( 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;
}

#undef EXPP_ADDCONST
#define EXPP_ADDCONST(dict, name) \
			 PyConstant_Insert(dict, #name, PyInt_FromLong(TF_##name))
/* Set constants for face drawing mode -- see drawmesh.c */

static PyObject *M_NMesh_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( 0x7fff ) );
		PyConstant_Insert( d, "HALO", PyInt_FromLong( TF_BILLBOARD ) );
		PyConstant_Insert( d, "DYNAMIC", PyInt_FromLong( TF_DYNAMIC ) );
		PyConstant_Insert( d, "INVISIBLE", PyInt_FromLong( TF_INVISIBLE ) );
		PyConstant_Insert( d, "LIGHT", PyInt_FromLong( TF_LIGHT ) );
		PyConstant_Insert( d, "OBCOL", PyInt_FromLong( TF_OBCOL ) );
		PyConstant_Insert( d, "SHADOW", PyInt_FromLong( TF_SHADOW ) );
		PyConstant_Insert( d, "TEXT", PyInt_FromLong( TF_BMFONT ) );
		PyConstant_Insert( d, "SHAREDVERT", PyInt_FromLong( TF_SHAREDVERT ) );
		PyConstant_Insert( d, "SHAREDCOL", PyInt_FromLong( TF_SHAREDCOL ) );
		PyConstant_Insert( d, "TEX", PyInt_FromLong( TF_TEX ) );
		PyConstant_Insert( d, "TILES", PyInt_FromLong( TF_TILES ) );
		PyConstant_Insert( d, "TWOSIDE", PyInt_FromLong( TF_TWOSIDE ) );
	}

	return FM;
}

static PyObject *M_NMesh_FaceFlagsDict( void )
{
	PyObject *FF = PyConstant_New(  );

	if( FF ) {
		BPy_constant *d = ( BPy_constant * ) FF;

		EXPP_ADDCONST( d, SELECT );
		EXPP_ADDCONST( d, HIDE );
		EXPP_ADDCONST( d, ACTIVE );
	}

	return FF;
}

static PyObject *M_NMesh_FaceTranspModesDict( void )
{
	PyObject *FTM = PyConstant_New(  );

	if( FTM ) {
		BPy_constant *d = ( BPy_constant * ) FTM;

		EXPP_ADDCONST( d, SOLID );
		EXPP_ADDCONST( d, ADD );
		EXPP_ADDCONST( d, ALPHA );
		EXPP_ADDCONST( d, SUB );
	}

	return FTM;
}

static PyObject *M_NMesh_EdgeFlagsDict( void )
{
	PyObject *EF = PyConstant_New(  );

	if( EF ) {
		BPy_constant *d = ( BPy_constant * ) EF;

		PyConstant_Insert(d, "SELECT", PyInt_FromLong(1));
		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));
	}

	return EF;
}

PyObject *NMesh_Init( void )
{
	PyObject *submodule;

	PyObject *Modes = M_NMesh_Modes(  );
	PyObject *FaceFlags = M_NMesh_FaceFlagsDict(  );
	PyObject *FaceModes = M_NMesh_FaceModesDict(  );
	PyObject *FaceTranspModes = M_NMesh_FaceTranspModesDict(  );
  PyObject *EdgeFlags = M_NMesh_EdgeFlagsDict(  );

	NMCol_Type.ob_type = &PyType_Type;
	NMFace_Type.ob_type = &PyType_Type;
	NMVert_Type.ob_type = &PyType_Type;
	NMesh_Type.ob_type = &PyType_Type;

	submodule =
		Py_InitModule3( "Blender.NMesh", M_NMesh_methods,
				M_NMesh_doc );

	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 );

	return submodule;
}

/* These are needed by Object.c */

PyObject *NMesh_CreatePyObject( Mesh * me, Object * ob )
{
	BPy_NMesh *nmesh = ( BPy_NMesh * ) new_NMesh( me );

	if( nmesh )
		nmesh->object = ob;	/* linking nmesh and object for vgrouping methods */

	return ( PyObject * ) nmesh;
}

int NMesh_CheckPyObject( PyObject * pyobj )
{
	return ( pyobj->ob_type == &NMesh_Type );
}

Mesh *NMesh_FromPyObject( PyObject * pyobj, Object * ob )
{
	if( pyobj->ob_type == &NMesh_Type ) {
		Mesh *mesh;
		BPy_NMesh *nmesh = ( BPy_NMesh * ) pyobj;

		if( nmesh->mesh ) {
			mesh = nmesh->mesh;
		} else {
			mesh = Mesh_fromNMesh( nmesh );
			if( !mesh )	/* NULL means an PyError */
				return NULL;

			nmesh->mesh = mesh;

			nmesh->object = ob;	/* linking for vgrouping methods */
			if( nmesh->name && nmesh->name != Py_None )
			    new_id( &( G.main->mesh ), &mesh->id,
				    PyString_AsString( nmesh->name ) );
			mesh_update( mesh, nmesh->object );
			nmesh_updateMaterials( nmesh );
		}
		return mesh;
	}

	PyErr_SetString( PyExc_AttributeError,
			"link argument type is not supported " );
	return NULL;
}

#define POINTER_CROSS_EQ(a1, a2, b1, b2) (((a1)==(b1) && (a2)==(b2)) || ((a1)==(b2) && (a2)==(b1)))

static PyObject *findEdge( BPy_NMesh *nmesh, BPy_NMVert *v1, BPy_NMVert *v2, int create)
{
  int i;

  for ( i = 0; i < PyList_Size(nmesh->edges); ++i )
  {
    BPy_NMEdge *edge=(BPy_NMEdge*)PyList_GetItem( nmesh->edges, i );
    if (!BPy_NMEdge_Check(edge)) continue;
    if ( POINTER_CROSS_EQ((BPy_NMVert*)edge->v1, (BPy_NMVert*)edge->v2, v1, v2) )
    {
      return EXPP_incr_ret((PyObject*)edge);
    }
  }

  /* if this line is reached, edge has not been found */
  if (create)
  {
    PyObject *newEdge=(PyObject *)new_NMEdge(v1, v2, 0, ME_EDGEDRAW|ME_EDGERENDER);
    PyList_Append(nmesh->edges, newEdge);
    return newEdge;
  }
  else
    Py_RETURN_NONE;
}

static void removeEdge( BPy_NMesh *nmesh, BPy_NMVert *v1, BPy_NMVert *v2, int ununsedOnly)
{
  int i,j;
  BPy_NMEdge *edge=NULL;
  int edgeUsedByFace=0;
  int totedge=PyList_Size(nmesh->edges);

  /* find the edge in the edge list */
  for ( i = 0; i < totedge; ++i )
  {
    edge=(BPy_NMEdge*)PyList_GetItem( nmesh->edges, i );
    if (!BPy_NMEdge_Check(edge)) continue;
    if ( POINTER_CROSS_EQ((BPy_NMVert*)edge->v1, (BPy_NMVert*)edge->v2, v1, v2) )
    {
      break;
    }
  }

  if (i==totedge || !edge) // edge not found
    return;

  for  ( j = PyList_Size(nmesh->faces)-1; j >= 0 ; --j )
  {
    BPy_NMFace *face=(BPy_NMFace *)PyList_GetItem(nmesh->faces, j);
    int k, del_face=0;
    int totv;
    if (!BPy_NMFace_Check(face)) continue;
    totv=PyList_Size(face->v);
    if (totv<2) continue;
    for ( k = 0; k < totv && !del_face; ++k )
    {
      BPy_NMVert *fe_v1=(BPy_NMVert *)PyList_GetItem(face->v, k ? k-1 : totv-1);
      BPy_NMVert *fe_v2=(BPy_NMVert *)PyList_GetItem(face->v, k);
      if ( POINTER_CROSS_EQ(v1, v2, fe_v1, fe_v2) )
      {
        edgeUsedByFace=1;
        del_face=1;
      }
    }
    if (del_face && !ununsedOnly) 
    {
      PySequence_DelItem(nmesh->faces, j);
    }
  }

  if (!ununsedOnly || (ununsedOnly && !edgeUsedByFace) )
    PySequence_DelItem(nmesh->edges, PySequence_Index(nmesh->edges, (PyObject*)edge));
}


static PyObject *NMesh_addEdge( PyObject * self, PyObject * args )
{
  BPy_NMesh *bmesh=(BPy_NMesh *)self;
  BPy_NMVert *v1=NULL, *v2=NULL;

  if (!PyArg_ParseTuple
	    ( args, "O!O!", &NMVert_Type, &v1, &NMVert_Type, &v2 ) ) {
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected NMVert, NMVert" );
	}

  if (v1==v2)
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "vertices must be different" );

  return findEdge(bmesh, v1, v2, 1);
}

static PyObject *NMesh_findEdge( PyObject * self, PyObject * args )
{
  BPy_NMesh *bmesh=(BPy_NMesh *)self;
  BPy_NMVert *v1=NULL, *v2=NULL;

  if (!PyArg_ParseTuple
	    ( args, "O!O!", &NMVert_Type, &v1, &NMVert_Type, &v2 ) ) {
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected NMVert, NMVert" );
	}

  if (v1==v2)
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "vertices must be different" );

  return findEdge(bmesh, v1, v2, 0);
}

static PyObject *NMesh_removeEdge( PyObject * self, PyObject * args )
{
  BPy_NMesh *bmesh=(BPy_NMesh *)self;
  BPy_NMVert *v1=NULL, *v2=NULL;

  if (!PyArg_ParseTuple
	    ( args, "O!O!", &NMVert_Type, &v1, &NMVert_Type, &v2 ) ) {
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected NMVert, NMVert" );
	}

  if (v1==v2)
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "vertices must be different" );
  removeEdge(bmesh, v1, v2, 0);

  Py_RETURN_NONE;
}


static PyObject *NMesh_addFace( PyObject * self, PyObject * args )
{
  BPy_NMesh *nmesh=(BPy_NMesh *)self;

  BPy_NMFace *face;
  int totv=0;
  
  if (!PyArg_ParseTuple
	    ( args, "O!", &NMFace_Type, &face ) ) {
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected NMFace argument" );
	}

  totv=PyList_Size(face->v);

  /*
   * Before edges data exists, having faces with two vertices was
   * the only way of storing edges not attached to any face.
   */
  if (totv!=2)
    PyList_Append(nmesh->faces, (PyObject*)face);

    if (totv>=2)
    {
      /* when totv==2, there is only one edge, when totv==3 there is three edges
       * and when totv==4 there is four edges.
       * that's why in the following line totv==2 is a special case */
      PyObject *edges = PyList_New((totv==2) ? 1 : totv);
      if (totv==2)
      {
        BPy_NMVert *fe_v1=(BPy_NMVert *)PyList_GetItem(face->v, 0);
        BPy_NMVert *fe_v2=(BPy_NMVert *)PyList_GetItem(face->v, 1);
        BPy_NMEdge *edge=(BPy_NMEdge *)findEdge(nmesh, fe_v1, fe_v2, 1);
        PyList_SetItem(edges, 0, (PyObject*)edge); // PyList_SetItem steals the reference
      }
      else
      {
        int k;
        for ( k = 0; k < totv; ++k )
        {
          BPy_NMVert *fe_v1=(BPy_NMVert *)PyList_GetItem(face->v, k ? k-1 : totv-1);
          BPy_NMVert *fe_v2=(BPy_NMVert *)PyList_GetItem(face->v, k);
          BPy_NMEdge *edge=(BPy_NMEdge *)findEdge(nmesh, fe_v1, fe_v2, 1);
          PyList_SetItem(edges, k, (PyObject*)edge); // PyList_SetItem steals the reference
        }
      }
      return edges;
    }

  Py_RETURN_NONE;
}

static PyObject *NMesh_removeFace( PyObject * self, PyObject * args )
{
  BPy_NMesh *nmesh=(BPy_NMesh *)self;

  BPy_NMFace *face;
  int totv=0;
  
  if (!PyArg_ParseTuple
	    ( args, "O!", &NMFace_Type, &face ) ) {
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected NMFace argument" );
	}

  totv=PyList_Size(face->v);

  {
    int index=PySequence_Index(nmesh->faces, (PyObject*)face);
    if (index>=0)
      PySequence_DelItem(nmesh->faces, index);
  }

    if (totv>=2)
    {
      /* when totv==2, there is only one edge, when totv==3 there is three edges
       * and when totv==4 there is four edges.
       * that's why in the following line totv==2 is a special case */
      if (totv==2)
      {
        BPy_NMVert *fe_v1=(BPy_NMVert *)PyList_GetItem(face->v, 0);
        BPy_NMVert *fe_v2=(BPy_NMVert *)PyList_GetItem(face->v, 1);
        removeEdge(nmesh, fe_v1, fe_v2, 1);
      }
      else
      {
        int k;
        for ( k = 0; k < totv; ++k )
        {
          BPy_NMVert *fe_v1=(BPy_NMVert *)PyList_GetItem(face->v, k ? k-1 : totv-1);
          BPy_NMVert *fe_v2=(BPy_NMVert *)PyList_GetItem(face->v, k);
          removeEdge(nmesh, fe_v1, fe_v2, 1);
        }
      }
    }

  Py_RETURN_NONE;
}

static PyObject *NMesh_printDebug( PyObject * self )
{
  BPy_NMesh *bmesh=(BPy_NMesh *)self;

  Mesh *mesh=bmesh->mesh;

  printf("**Vertices\n");
  {
    int i;
    for (i=0; i<mesh->totvert; ++i)
    {
      MVert *v=mesh->mvert+i;
      double x=v->co[0];
      double y=v->co[1];
      double z=v->co[2];
      printf(" %2d : %.3f %.3f %.3f\n", i, x, y, z);
    }
  }

  printf("**Edges\n");
  {
    int i;
    for (i=0; i<mesh->totedge; ++i)
    {
      MEdge *e=mesh->medge+i;
      int v1 = e->v1;
      int v2 = e->v2;
      int flag = e->flag;
      printf(" %2d : %2d %2d flag=%d\n", i, v1, v2, flag);
    }
  }

  printf("**Faces\n");
  {
    int i;
    for (i=0; i<mesh->totface; ++i)
    {
      MFace *e=((MFace*)(mesh->mface))+i;
      int v1 = e->v1;
      int v2 = e->v2;
      int v3 = e->v3;
      int v4 = e->v4;
      printf(" %2d : %2d %2d %2d %2d\n", i, v1, v2, v3, v4);
    }
  }

	Py_RETURN_NONE;
}

static PyObject *NMesh_addVertGroup( PyObject * self, PyObject * args )
{
	char *groupStr;
	struct Object *object;
	PyObject *tempStr;

	if( !PyArg_ParseTuple( args, "s", &groupStr ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected string argument" );

	if( ( ( BPy_NMesh * ) self )->object == NULL )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "mesh must be linked to an object first..." );

	object = ( ( BPy_NMesh * ) self )->object;

	//get clamped name
	tempStr = PyString_FromStringAndSize( groupStr, 32 );
	groupStr = PyString_AsString( tempStr );

	add_defgroup_name( object, groupStr );

	EXPP_allqueue( REDRAWBUTSALL, 1 );

	Py_RETURN_NONE;
}

static PyObject *NMesh_removeVertGroup( PyObject * self, PyObject * args )
{
	char *groupStr;
	struct Object *object;
	int nIndex;
	bDeformGroup *pGroup;

	if( !PyArg_ParseTuple( args, "s", &groupStr ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected string argument" );

	if( ( ( BPy_NMesh * ) self )->object == NULL )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "mesh must be linked to an object first..." );

	object = ( ( BPy_NMesh * ) 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( object );

	EXPP_allqueue( REDRAWBUTSALL, 1 );

	Py_RETURN_NONE;
}

static PyObject *NMesh_assignVertsToGroup( PyObject * self, PyObject * args )
{
	//listObject is an integer list of vertex indices to add to group
	//groupStr = group name
	//weight is a float defining the weight this group has on this vertex
	//assignmode = "replace", "add", "subtract"
	//                                                      replace weight - add addition weight to vertex for this group
	//                              - remove group influence from this vertex
	//the function will not like it if your in editmode...

	char *groupStr;
	char *assignmodeStr = NULL;
	int nIndex;
	int assignmode;
	float weight = 1.0;
	struct Object *object;
	bDeformGroup *pGroup;
	PyObject *listObject;
	int tempInt;
	int x;

	if( ( ( BPy_NMesh * ) self )->object == NULL )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "mesh must be linked to an object first..." );

	if( !PyArg_ParseTuple
	    ( args, "sO!fs", &groupStr, &PyList_Type, &listObject, &weight,
	      &assignmodeStr ) ) {
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected string, list,	float, string arguments" );
	}

	object = ( ( BPy_NMesh * ) self )->object;

	if( object->data == NULL )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "object contains no data..." );

	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( assignmodeStr == NULL )
		assignmode = 1;	/* default */
	else if( STREQ( assignmodeStr, "replace" ) )
		assignmode = 1;
	else if( STREQ( assignmodeStr, "add" ) )
		assignmode = 2;
	else if( STREQ( assignmodeStr, "subtract" ) )
		assignmode = 3;
	else
		return EXPP_ReturnPyObjError( PyExc_ValueError,
					      "bad assignment mode" );

	//makes a set of dVerts corresponding to the mVerts
	if( !( ( Mesh * ) object->data )->dvert ) {
		create_dverts( object->data );
	}
	//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 * ) object->data )->totvert ) {
			return EXPP_ReturnPyObjError( PyExc_ValueError,
						      "bad vertex index in list" );
		}

		add_vert_defnr( object, nIndex, tempInt, weight, assignmode );
	}

	Py_RETURN_NONE;
}

static PyObject *NMesh_removeVertsFromGroup( PyObject * self, PyObject * args )
{
	//not passing a list will remove all verts from group

	char *groupStr;
	int nIndex;
	struct Object *object;
	bDeformGroup *pGroup;
	PyObject *listObject;
	int tempInt;
	int x, argc;

	/* argc is the number of parameters passed in: 1 (no list given) or 2: */
	argc = PyObject_Length( args );

	if( !PyArg_ParseTuple
	    ( args, "s|O!", &groupStr, &PyList_Type, &listObject ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "expected string and optional list argument" );

	if( ( ( BPy_NMesh * ) self )->object == NULL )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "mesh must be linked to an object first..." );

	object = ( ( BPy_NMesh * ) self )->object;

	if( object->data == NULL )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "object contains no data..." );

	if( ( !( ( Mesh * ) object->data )->dvert ) )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "this mesh contains no deform vertices...'" );

	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( argc == 1 ) {	/* no list given */
		//enter editmode
		if( ( G.obedit == 0 ) ) {
			//set current object
			BASACT->object = object;
			G.obedit = BASACT->object;
		}
		//set current vertex group
		nIndex++;
		object->actdef = (unsigned short)nIndex;

		//clear all dVerts in active group
		remove_verts_defgroup( 1 );

		//exit editmode
		G.obedit = 0;
	} else {
		if( G.obedit != 0 )	//remove_vert_def_nr doesn't like it if your in editmode
			G.obedit = 0;

		//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 * ) object->data )->totvert )
				return EXPP_ReturnPyObjError( PyExc_ValueError,
							      "bad vertex index in list" );

			remove_vert_def_nr( object, nIndex, tempInt );
		}
	}

	Py_RETURN_NONE;
}

static PyObject *NMesh_getVertsFromGroup( PyObject * 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;
	int weightRet;
	struct Object *object;
	bDeformGroup *pGroup;
	MVert *mvert;
	MDeformVert *dvert;
	float weight;
	int i, k, l1, l2, count;
	int num = 0;
	PyObject *tempVertexList = NULL;
	PyObject *vertexList;
	PyObject *listObject;
	int tempInt;
	int x;

	listObject = Py_None;	//can't use NULL macro because compiler thinks
	//it's a 0 and we need to check 0 index vertex pos
	l1 = FALSE;
	l2 = FALSE;
	weightRet = 0;

	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( ( ( BPy_NMesh * ) self )->object == NULL )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "mesh must be linked to an object first..." );

	object = ( ( BPy_NMesh * ) self )->object;

	if( object->data == NULL )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "object contains no data..." );

	if( ( !( ( Mesh * ) object->data )->dvert ) )
		return EXPP_ReturnPyObjError( PyExc_AttributeError,
					      "this mesh contains no deform vertices...'" );

	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" );

	//temporary list
	tempVertexList = PyList_New( ( ( Mesh * ) object->data )->totvert );
	if( tempVertexList == NULL )
		return EXPP_ReturnPyObjError( PyExc_RuntimeError,
					      "getVertsFromGroup: can't create pylist!" );

	count = 0;

	if( listObject == Py_None )	//do entire group
	{
		for( k = 0; k < ( ( Mesh * ) object->data )->totvert; k++ ) {
			dvert = ( ( Mesh * ) object->data )->dvert + k;

			for( i = 0; i < dvert->totweight; i++ ) {
				if( dvert->dw[i].def_nr == nIndex ) {
					mvert = ( ( Mesh * ) object->data )->
						mvert + k;
					weight = dvert->dw[i].weight;
					//printf("index =%3d weight:%10f\n", k, weight);

					if( weightRet == 1 )
						PyList_SetItem( tempVertexList,
								count,
								Py_BuildValue
								( "(i,f)", k,
								  weight ) );
					else if( weightRet == 0 )
						PyList_SetItem( tempVertexList,
								count,
								Py_BuildValue
								( "i", k ) );

					count++;
				}
			}
		}
	} else			//do single vertex
	{
		//loop list adding verts to group
		for( x = 0; x < PyList_Size( listObject ); x++ ) {
			if( !
			    ( PyArg_Parse
			      ( ( PyList_GetItem( listObject, x ) ), "i",
				&tempInt ) ) ) {
				Py_DECREF(tempVertexList);
				return EXPP_ReturnPyObjError( PyExc_TypeError,
							      "python list integer not parseable" );
			}
			if( tempInt < 0
			    || tempInt >=
			    ( ( Mesh * ) object->data )->totvert ) {
				Py_DECREF(tempVertexList);
				return EXPP_ReturnPyObjError( PyExc_ValueError,
							      "bad vertex index in list" );
			}
			num = tempInt;
			dvert = ( ( Mesh * ) object->data )->dvert + num;
			for( i = 0; i < dvert->totweight; i++ ) {
				l1 = TRUE;
				if( dvert->dw[i].def_nr == nIndex ) {
					l2 = TRUE;
					mvert = ( ( Mesh * ) object->data )->
						mvert + num;

					weight = dvert->dw[i].weight;
					//printf("index =%3d weight:%10f\n", num, weight);

					if( weightRet == 1 ) {
						PyList_SetItem( tempVertexList,
								count,
								Py_BuildValue
								( "(i,f)", num,
								  weight ) );
					} else if( weightRet == 0 )
						PyList_SetItem( tempVertexList,
								count,
								Py_BuildValue
								( "i", num ) );

					count++;
				}
				if( l2 == FALSE )
					printf( "vertex at index %d is not part of passed group...\n", tempInt );
			}
			if( l1 == FALSE )
				printf( "vertex at index %d is not assigned to a vertex group...\n", tempInt );

			l1 = l2 = FALSE;	//reset flags
		}
	}
	//only return what we need
	vertexList = PyList_GetSlice( tempVertexList, 0, count );

	Py_DECREF( tempVertexList );

	return ( vertexList );
}

static PyObject *NMesh_renameVertGroup( PyObject * self, PyObject * args )
{
	char *oldGr = NULL;
	char *newGr = NULL;
	bDeformGroup *defGroup = NULL;
	/*PyObject *tempStr; */


	if( !( ( BPy_NMesh * ) self )->object )
		return EXPP_ReturnPyObjError( PyExc_RuntimeError,
					      "This mesh must be linked to an object" );

	if( !PyArg_ParseTuple( args, "ss", &oldGr, &newGr ) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
					      "Expected string & string argument" );

	defGroup =
		get_named_vertexgroup( ( ( BPy_NMesh * ) self )->object,
				       oldGr );
	if( defGroup == NULL )
		return EXPP_ReturnPyObjError( PyExc_RuntimeError,
					      "Couldn't find the expected vertex group" );

	PyOS_snprintf( defGroup->name, 32, newGr );
	unique_vertexgroup_name( defGroup, ( ( BPy_NMesh * ) self )->object );

	Py_RETURN_NONE;
}

static PyObject *NMesh_getVertGroupNames( PyObject * self )
{
	bDeformGroup *defGroup;
	PyObject *list, *tmpstr;

	if( !( ( BPy_NMesh * ) self )->object )
		return EXPP_ReturnPyObjError( PyExc_RuntimeError,
					      "This mesh must be linked to an object" );

	list = PyList_New( 0 );
	for( defGroup = ( ( ( BPy_NMesh * ) self )->object )->defbase.first;
				defGroup; defGroup = defGroup->next ) {
		
		tmpstr = PyString_FromString( defGroup->name );
		if( PyList_Append( list,  tmpstr) < 0 ) {
			Py_XDECREF(list);
			Py_XDECREF(tmpstr);
			return EXPP_ReturnPyObjError( PyExc_RuntimeError, "Couldn't add item to list" );
		}
		Py_XDECREF(tmpstr);
	}

	return list;
}

static PyObject *NMesh_transform (PyObject *self, PyObject *args)
{
	BPy_NMesh *nmesh = ( BPy_NMesh * ) self;
	BPy_NMVert *mv;
	PyObject *ob1 = NULL;
	MatrixObject *mat;
	float vx, vy, vz;
	int i, recalc_normals = 0;

	if( !PyArg_ParseTuple( args, "O!|i", &matrix_Type, &ob1, &recalc_normals ) )
		return ( EXPP_ReturnPyObjError( PyExc_TypeError,
			"expected matrix and optionally an int as arguments" ) );

	mat = ( MatrixObject * ) ob1;

	if( mat->colSize != 4 || mat->rowSize != 4 )
		return ( EXPP_ReturnPyObjError( PyExc_AttributeError,
			"matrix must be a 4x4 transformation matrix\n"
			"for example as returned by object.getMatrix()" ) );
	
	/* loop through all the verts and transform locations by the supplied
	 * matrix */
	for( i = 0; i < PySequence_Length(nmesh->verts); i++ ) {
		mv = ( BPy_NMVert * ) PySequence_GetItem( nmesh->verts, i );
		vx = mv->co[0];
		vy = mv->co[1];
		vz = mv->co[2];

		/* Mat4MulVecfl(mat->matrix, mv->co); */
		mv->co[0] = vx*mat->matrix[0][0] + vy*mat->matrix[1][0] +
								vz*mat->matrix[2][0] + mat->matrix[3][0];
		mv->co[1] = vx*mat->matrix[0][1] + vy*mat->matrix[1][1] +
								vz*mat->matrix[2][1] + mat->matrix[3][1];
		mv->co[2] = vx*mat->matrix[0][2] + vy*mat->matrix[1][2] +
					 			vz*mat->matrix[2][2] + mat->matrix[3][2];

		Py_DECREF(mv);
	}

	if ( recalc_normals ) {
		/* loop through all the verts and transform normals by the inverse
		 * of the transpose of the supplied matrix */
		float invmat[4][4];

		/* we only need to invert a 3x3 submatrix, because the 4th component of
		 * affine vectors is 0, but Mat4Invert reports non invertible matrices */
		if (!Mat4Invert((float(*)[4])*invmat, (float(*)[4])*mat->matrix))
			return EXPP_ReturnPyObjError (PyExc_AttributeError,
				"given matrix is not invertible");

		for( i = 0; i < PySequence_Length(nmesh->verts); i++ ) {
			mv = ( BPy_NMVert * ) PySequence_GetItem( nmesh->verts, i );
			vx = mv->no[0];
			vy = mv->no[1];
			vz = mv->no[2];
			mv->no[0] = vx*invmat[0][0] + vy*invmat[0][1] + vz*invmat[0][2];
			mv->no[1] = vx*invmat[1][0] + vy*invmat[1][1] + vz*invmat[1][2]; 
			mv->no[2] = vx*invmat[2][0] + vy*invmat[2][1] + vz*invmat[2][2];
			Normalize(mv->no);
			Py_DECREF(mv);
		}
	}

	/* should we alternatively return a list of changed verts (and preserve
	 * the original ones) ? */
	Py_RETURN_NONE;
}
