4171 lines
111 KiB
C
4171 lines
111 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* This is a new part of Blender, 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
|
|
|
|
/* Globals */
|
|
static PyObject *g_nmeshmodule = NULL;
|
|
|
|
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++) {
|
|
|
|
DL_SURFINDEX(dl->flag & DL_CYCL_U, dl->flag & DL_CYCL_V, dl->nr, dl->parts);
|
|
|
|
|
|
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 );
|
|
|
|
g_nmeshmodule = submodule;
|
|
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;
|
|
}
|