From 1da3b9f517103f6e04312faf08ca7c71edf7d0dd Mon Sep 17 00:00:00 2001 From: Willian Padovani Germano Date: Sat, 22 Jan 2005 02:48:03 +0000 Subject: BPython: - Stephane Soppera added long missed support for edge data in Blender.NMesh + related doc; - Michael Reimpell improved script registration (fixes bug report #2160) and the file and image selectors in Blender.Window (improved with suggestions from Yann Vernier). They now suppport methods as callbacks; - World.get/setMode were not registered, so could not be directly called (reported by Ken Hughes). Still needs some work to improve things, including docs. Scripts: - Jean-Michel Soler updated his texture baker based on input from Appolux; - Campbell and Jean-Michel improved the bvh importer: faster, better float units scaling (by Campbell); supports Poser 3.01 files (by jms). Thanks guys! --- release/scripts/bvh_import.py | 114 ++++- release/scripts/tex2uvbaker.py | 97 ++-- source/blender/python/BPY_menus.c | 6 +- source/blender/python/api2_2x/NMesh.c | 789 ++++++++++++++++++++++++++--- source/blender/python/api2_2x/NMesh.h | 13 +- source/blender/python/api2_2x/Window.c | 41 +- source/blender/python/api2_2x/World.c | 8 +- source/blender/python/api2_2x/doc/NMesh.py | 88 +++- 8 files changed, 1009 insertions(+), 147 deletions(-) diff --git a/release/scripts/bvh_import.py b/release/scripts/bvh_import.py index 3d29dc926c8..8c39d32eb8f 100644 --- a/release/scripts/bvh_import.py +++ b/release/scripts/bvh_import.py @@ -2,30 +2,44 @@ """ Name: 'Motion Capture (.bvh)...' -Blender: 232 +Blender: 236 Group: 'Import' Tip: 'Import a (.bvh) motion capture file' """ __author__ = "Campbell Barton" -__url__ = ("blender", "elysiun") -__version__ = "1.0 03/25/04" +__url__ = ("blender", "elysiun", "http://jmsoler.free.fr/util/blenderfile/py/bvh_import.py") +__version__ = "1.0.2 04/12/28" __bpydoc__ = """\ This script imports BVH motion capture data to Blender. -Supported:
+Supported: Poser 3.01
Missing:
Known issues:
Notes:
+ Jean-Michel Soler improved importer to support Poser 3.01 files. """ # $Id$ # + +#===============================================# +# BVH Import script 1.03 patched by Campbell # +# Small optimizations and scale input # +# 01/01/2005, # +#===============================================# + +#===============================================# +# BVH Import script 1.02 patched by Jm Soler # +# to the Poser 3.01 bvh file # +# 28/12/2004, # +#===============================================# + #===============================================# # BVH Import script 1.0 by Campbell Barton # # 25/03/2004, euler rotation code taken from # @@ -67,7 +81,7 @@ Notes:
import string import math import Blender -from Blender import Window, Object, Scene, Ipo +from Blender import Window, Object, Scene, Ipo, Draw from Blender.Scene import Render @@ -80,14 +94,13 @@ from Blender.Scene import Render # # except: # print 'psyco is not present on this system' - + +# Default scale +scale = 0.01 # Update as we load? debug = 0 -# Global scale facctor # sHOULD BE 1 BY DEFAULT -scale = 1 - # Get the current scene. scn = Scene.GetCurrent() context = scn.getRenderingContext() @@ -100,6 +113,9 @@ channelCurves = [] # Chenging there rotation to EULER rotation objectList = [] +def getScale(): + return Draw.PupFloatInput('BVH Scale: ', 0.01, 0.001, 10.0, 0.1, 3) + def MAT(m): if len(m) == 3: return Blender.Mathutils.Matrix(m[0], m[1], m[2]) @@ -127,7 +143,6 @@ def eulerRotate(x,y,z): for k in range(3): for j in range(3): mat3[i][k]=mat3[i][k]+mat1[i][j]*mat2[j][k] - mat1 = mat2 = i = k = j = None # Save memory return mat3 @@ -162,7 +177,6 @@ def eulerRotate(x,y,z): mat3[2][1]=t*y*z-s*x mat3[2][2]=t*z*z+c - rot4 = s = c = t = x = y = z = None # Save some memory return mat3 eul = [x,y,z] @@ -202,8 +216,6 @@ def eulerRotate(x,y,z): y =- eul[1]/-10 z =- eul[2]/-10 - - eul = mat = zmat = xmat = ymat = jj = None return x, y, z # Returm euler roration values. @@ -213,6 +225,7 @@ def eulerRotate(x,y,z): # from the BVA file to create an empty # #===============================================# def makeJoint(name, parent, prefix, offset, channels): + global scale # Make Empty, with the prefix in front of the name ob = Object.New('Empty', prefix + name) # New object, ob is shorter and nicer to use. scn.link(ob) # place the object in the current scene @@ -253,8 +266,6 @@ def makeJoint(name, parent, prefix, offset, channels): # Add to object list objectList.append(ob) - ob = newIpo = opParent = None - # Redraw if debugging if debug: Blender.Redraw() @@ -280,13 +291,22 @@ def makeEnd(parent, prefix, offset): + #===============================================# # MAIN FUNCTION - All things are done from here # #===============================================# def loadBVH(filename): + global scale print '' print 'BVH Importer 1.0 by Campbell Barton (Ideasman) - ideasman@linuxmail.org' - + alpha='abcdefghijklmnopqrstuvewxyz' + ALPHA=alpha+alpha.upper() + ALPHA+=' 0123456789+-{}. ' + time1 = Blender.sys.time() + tmpScale = getScale() + if tmpScale != None: + scale = tmpScale + # File loading stuff # Open the file for importing file = open(filename, 'r') @@ -294,10 +314,19 @@ def loadBVH(filename): # Make a list of lines lines = [] for fileLine in fileData: + fileLine=fileLine.replace('..','.') newLine = string.split(fileLine) if newLine != []: - lines.append(string.split(fileLine)) - fileData = None + t=[] + for n in newLine: + for n0 in n: + if n0 not in ALPHA: + n=n.replace(n0,'') + t.append(n) + lines.append(t) + + + del fileData # End file loading code # Call object names with this prefix, mainly for scenes with multiple BVH's - Can imagine most partr names are the same @@ -320,18 +349,25 @@ def loadBVH(filename): #channelList [(, [channelType1, channelType2...]), (, [channelType1, channelType2...)] channelList = [] channelIndex = -1 + + lineIdx = 1 # An index for the file. while lineIdx < len(lines) -1: #... if lines[lineIdx][0] == 'ROOT' or lines[lineIdx][0] == 'JOINT': + if lines[lineIdx][0] == 'JOINT' and len(lines[lineIdx])>2: + for j in range(2,len(lines[lineIdx])) : + lines[lineIdx][1]+='_'+lines[lineIdx][j] + # MAY NEED TO SUPPORT MULTIPLE ROOT's HERE!!!, Still unsure weather multiple roots are possible.?? print len(parent) * ' ' + 'node:',lines[lineIdx][1],' parent:',parent[-1] - + print lineIdx name = lines[lineIdx][1] + print name,lines[lineIdx+1],lines[lineIdx+2] lineIdx += 2 # Incriment to the next line (Offset) - offset = ( eval(lines[lineIdx][1]), eval(lines[lineIdx][2]), eval(lines[lineIdx][3]) ) + offset = ( float(lines[lineIdx][1]), float(lines[lineIdx][2]), float(lines[lineIdx][3]) ) lineIdx += 1 # Incriment to the next line (Channels) # newChannel[Xposition, Yposition, Zposition, Xrotation, Yrotation, Zrotation] @@ -367,7 +403,7 @@ def loadBVH(filename): # Account for an end node if lines[lineIdx][0] == 'End' and lines[lineIdx][1] == 'Site': # There is somtimes a name afetr 'End Site' but we will ignore it. lineIdx += 2 # Incriment to the next line (Offset) - offset = ( eval(lines[lineIdx][1]), eval(lines[lineIdx][2]), eval(lines[lineIdx][3]) ) + offset = ( float(lines[lineIdx][1]), float(lines[lineIdx][2]), float(lines[lineIdx][3]) ) makeEnd(parent, prefix, offset) # Just so we can remove the Parents in a uniform way- End end never has kids @@ -431,21 +467,46 @@ def loadBVH(filename): if debug: Blender.Redraw() while obIdx < len(objectList) -1: if channelList[obIdx][0] != -1: - objectList[obIdx].getIpo().getCurve('LocX').addBezier((currentFrame, scale * eval(lines[lineIdx][channelList[obIdx][0]]))) + VAL0=lines[lineIdx][channelList[obIdx][0]] + if VAL0.find('.')==-1: + VAL0=VAL0[:len(VAL0)-6]+'.'+VAL0[-6:] + objectList[obIdx].getIpo().getCurve('LocX').addBezier((currentFrame, scale * float(VAL0))) + if channelList[obIdx][1] != -1: - objectList[obIdx].getIpo().getCurve('LocY').addBezier((currentFrame, scale * eval(lines[lineIdx][channelList[obIdx][1]]))) + VAL1=lines[lineIdx][channelList[obIdx][0]] + if VAL1.find('.')==-1: + VAL1=VAL1[:len(VAL1)-6]+'.'+VAL1[-6:] + objectList[obIdx].getIpo().getCurve('LocY').addBezier((currentFrame, scale * float(VAL1))) + if channelList[obIdx][2] != -1: - objectList[obIdx].getIpo().getCurve('LocZ').addBezier((currentFrame, scale * eval(lines[lineIdx][channelList[obIdx][2]]))) + VAL2=lines[lineIdx][channelList[obIdx][0]] + if VAL2.find('.')==-1: + VAL2=VAL2[:len(VAL2)-6]+'.'+VAL2[-6:] + objectList[obIdx].getIpo().getCurve('LocZ').addBezier((currentFrame, scale * float(VAL2))) if channelList[obIdx][3] != '-1' or channelList[obIdx][4] != '-1' or channelList[obIdx][5] != '-1': - x, y, z = eulerRotate(eval(lines[lineIdx][channelList[obIdx][3]]), eval(lines[lineIdx][channelList[obIdx][4]]), eval(lines[lineIdx][channelList[obIdx][5]])) + VAL3=lines[lineIdx][channelList[obIdx][3]] + if VAL3.find('.')==-1: + VAL3=VAL3[:len(VAL3)-6]+'.'+VAL3[-6:] + + VAL4=lines[lineIdx][channelList[obIdx][4]] + if VAL4.find('.')==-1: + VAL4=VAL4[:len(VAL4)-6]+'.'+VAL4[-6:] + + VAL5=lines[lineIdx][channelList[obIdx][5]] + if VAL5.find('.')==-1: + VAL5=VAL5[:len(VAL5)-6]+'.'+VAL5[-6:] + + x, y, z = eulerRotate(float(VAL3), float(VAL4), float(VAL5)) + objectList[obIdx].getIpo().getCurve('RotX').addBezier((currentFrame, x)) objectList[obIdx].getIpo().getCurve('RotY').addBezier((currentFrame, y)) objectList[obIdx].getIpo().getCurve('RotZ').addBezier((currentFrame, z)) + obIdx += 1 # Done importing motion data # - lines[lineIdx] = None # Scrap old motion data, save some memory? + # lines[lineIdx] = None # Scrap old motion data, save some memory? lineIdx += 1 # We have finished now print currentFrame, 'done.' @@ -457,5 +518,6 @@ def loadBVH(filename): # Main file loop lineIdx += 1 + print "bvh import time: ", Blender.sys.time() - time1 Blender.Window.FileSelector(loadBVH, "Import BVH") diff --git a/release/scripts/tex2uvbaker.py b/release/scripts/tex2uvbaker.py index 7a172ee5868..1e46e06475c 100644 --- a/release/scripts/tex2uvbaker.py +++ b/release/scripts/tex2uvbaker.py @@ -11,7 +11,7 @@ __author__ = "Jean-Michel Soler (jms)" __url__ = ("blender", "elysiun", "Script online, http://jmsoler.free.fr/util/blenderfile/py/text2uvbaker.py", "Communicate problems and errors, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender") -__version__ = "0.2.2 2004/08/01" +__version__ = "0.2.3 2004/12/30" __bpydoc__ = """\ This script "bakes" Blender procedural materials (including textures): it saves @@ -33,7 +33,7 @@ Notes:
""" #--------------------------------------------- -# Last release : 0.2.2 , 2004/08/01 , 22h13 +# Last release : 0.2.3 , 2004/12/30 , 22h13 #--------------------------------------------- #--------------------------------------------- # (c) jm soler 07/2004 : 'Procedural Texture Baker' @@ -43,12 +43,40 @@ Notes:
# original mesh. # released under Blender Artistic Licence # -# 0.2.2 : if the uv mesh objet exists it used, -# no creation of a new one. As the lamp and -# the camera -# 0.2.1 : This script automaticaly frame and shoot the -# new uv mesh . The image file is saved ine the -# /render folder. +# +# 0.2.3 : +# Great thanks for Apollux who sees a lot of these +# problems +# +# --Everytime you run the script a new set +# of objects is created. File size and memory +# consumption can go pretty high if you are +# not aware of that . +# Now it ONLY creates 3 objects: a flattened +# mesh, a camera and a lamp. +# --all the 3 objects was placed on layer 1, but if +# that layer was not visible while you used the script +# all you will get a is an empty render. +# Now the layer is tst and activated befor the shoot +# --The flattened mesh was really flattend only after +# frame 100 (if you playbacked the animation, you can +# actually see the mesh becoming flat on the first 100 +# frames). No more. +# -- When the script is run, it changes temporary to +# the new cammera, set the render output to a square +# (i.e. 1024 x 1024 or else), does the render, and then +# resets the render output and the active camera to the +# original one. But if no original camera was found +# this produce an error. +# +# 0.2.2 : +# if the uv mesh objet exists it used, +# no creation of a new one. As the lamp and +# the camera +# 0.2.1 : +# This script automaticaly frame and shoot the +# new uv mesh . The image file is saved ine the +# /render folder. # #--------------------------------------------- # On user-friendly side : @@ -83,9 +111,9 @@ Basic instructions: - Run this script and check the console. """ -def GET_newobject (TYPE): +def GET_newobject (TYPE,NAME): SCENE = Blender.Scene.getCurrent() - OBJECT = Blender.Object.New(TYPE) + OBJECT = Blender.Object.New(TYPE,NAME) SCENE.link(OBJECT) return OBJECT, SCENE @@ -138,7 +166,7 @@ def SHOOT (XYlimit, frame, obj, name, FRAME): except: Cam = Blender.Camera.New() Cam.name = 'UVCamera' - CAM, SC = GET_newobject('Camera') + CAM, SC = GET_newobject('Camera','UVCAMERA') CAM.link(Cam) CAM.setName('UVCAMERA') Cam.lens = 30 @@ -151,22 +179,23 @@ def SHOOT (XYlimit, frame, obj, name, FRAME): CAM.setEuler (0.0, 0.0, 0.0) try: - LAMP = Blender.Object.Get('Eclairage') + LAMP = Blender.Object.Get('ECLAIRAGE') lampe = LAMP.getData() SC = Blender.Scene.getCurrent() except: lampe = Blender.Lamp.New() lampe.name = 'lumin' - LAMP, SC = GET_newobject('Lamp') + LAMP, SC = GET_newobject('Lamp','ECLAIRAGE') LAMP.link(lampe) - LAMP.setName('Eclairage') + LAMP.setName('ECLAIRAGE') LAMP.setLocation(obj.getLocation()) LAMP.LocX += XYlimit[0] / 2.0 LAMP.LocY += XYlimit[1] / 2.0 LAMP.LocZ += max (XYlimit[0], XYlimit[1]) LAMP.setEuler (0.0, 0.0, 0.0) + context = SC.getRenderingContext() Camold = SC.getCurrentCamera() SC.setCurrentCamera(CAM) @@ -189,16 +218,27 @@ def SHOOT (XYlimit, frame, obj, name, FRAME): SAVE_image (context, name, FRAME) context.imageSizeY(OLDy) context.imageSizeX(OLDx) - SC.setCurrentCamera(Camold) + + if Camold :SC.setCurrentCamera(Camold) + Blender.Set ('curframe', frame) def Mesh2UVCoord (): - try: + if 1:#try: MESH3D = Object.GetSelected()[0] if MESH3D.getType() == 'Mesh': MESH = MESH3D.getData() - MESH2 = Blender.NMesh.GetRaw() + + try: + NewOBJECT=Blender.Object.Get('UVOBJECT') + CurSCENE=Blender.Scene.getCurrent() + MESH2 = NewOBJECT.getData() + MESH2.faces=[] + + except: + NewOBJECT, CurSCENE = GET_newobject('Mesh','UVOBJECT') + MESH2 = Blender.NMesh.GetRaw() for f in MESH.faces: f1 = Blender.NMesh.Face() @@ -218,25 +258,14 @@ def Mesh2UVCoord (): MESH2.materials = MESH.materials[:] - try: - NewOBJECT=Blender.Object.Get('UVOBJECT') - CurSCENE=Blender.Scene.getCurrent() - except: - NewOBJECT, CurSCENE = GET_newobject('Mesh') - - NewOBJECT.link(MESH2) - #NewOBJECT, CurSCENE = GET_newobject('Mesh') #NewOBJECT.link(MESH2) - + NewOBJECT.setLocation (OBJPOS, OBJPOS, 0.0) NewOBJECT.setEuler (0.0, 0.0, 0.0) MESH2.removeAllKeys() - MESH2.update() - MESH2.insertKey (1, 'absolute') - MESH2.update() for f in MESH2.faces: for v in f.v: @@ -248,6 +277,10 @@ def Mesh2UVCoord (): print XYLIMIT + MESH2.update() + MESH2.insertKey (1, 'absolute') + MESH2.update() + MESH2.update() MESH2.insertKey (FRAME, 'absolute') MESH2.update() @@ -271,9 +304,9 @@ def Mesh2UVCoord (): result = Draw.PupMenu(name) print 'problem : no object selected or not mesh' - except: - name = "Error%t|Active object is not a mesh or has no UV coordinates" - result = Draw.PupMenu(name) + #except: + # name = "Error%t|Active object is not a mesh or has no UV coordinates" + # result = Draw.PupMenu(name) print 'problem : no object selected or not mesh' Mesh2UVCoord() diff --git a/source/blender/python/BPY_menus.c b/source/blender/python/BPY_menus.c index 6bae8fdf3e0..a67ada0cfee 100644 --- a/source/blender/python/BPY_menus.c +++ b/source/blender/python/BPY_menus.c @@ -647,14 +647,13 @@ static int bpymenu_CreateFromDir( char *dirname, int whichdir ) int scriptGroup; BPyMenu *scriptMenu = NULL; /* other */ - int scanDir = 1; int returnValue = 0; /* open directory stream */ dir = opendir(dirname); if (dir != NULL) { /* directory stream opened */ - while (((dirEntry = readdir(dir)) != NULL) && (scanDir == 1)) { + while ((dirEntry = readdir(dir)) != NULL) { /* Check if filename does not start with a dot, * ends with '.py' and is a regular file. */ BLI_make_file_string("/", fileName, dirname, dirEntry->d_name); @@ -722,10 +721,7 @@ static int bpymenu_CreateFromDir( char *dirname, int whichdir ) if (DEBUG) { fprintf(stderr, "BPyMenus error: Couldn't create entry for: %s\n", fileName); } - /* abort */ parserState = 0; - scanDir = 0; - returnValue = -2; } else { parserState++; } diff --git a/source/blender/python/api2_2x/NMesh.c b/source/blender/python/api2_2x/NMesh.c index c2d4656cfbc..70bc9150a28 100644 --- a/source/blender/python/api2_2x/NMesh.c +++ b/source/blender/python/api2_2x/NMesh.c @@ -26,7 +26,7 @@ * This is a new part of Blender. * * Contributor(s): Willian P. Germano, Jordi Rovira i Bonet, Joseph Gilbert, - * Bala Gi, Alexander Szakaly + * Bala Gi, Alexander Szakaly, Stephane Soppera * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ @@ -46,6 +46,7 @@ #include "BIF_editmesh.h" /* vertexnormals_mesh() : still needed???*/ #include "BIF_meshtools.h" /* current loc of vertexnormals_mesh() */ #include "BIF_space.h" +#include "BKE_deform.h" #include "BKE_mesh.h" #include "BKE_main.h" #include "BKE_global.h" @@ -86,7 +87,16 @@ static PyObject *g_nmeshmodule = NULL; static int unlink_existingMeshData( Mesh * mesh ); -static int convert_NMeshToMesh( Mesh * mesh, BPy_NMesh * nmesh ); +static int convert_NMeshToMesh( Mesh * mesh, BPy_NMesh * nmesh, int store_edges ); +/* */ +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_addEdgesData( PyObject * self ); +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 ); @@ -94,8 +104,37 @@ 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, PyObject * args ); +static PyObject *NMesh_getVertGroupNames( PyObject * self ); +/* */ + +static char NMesh_printDebug_doc[] = + "print debug info about the mesh."; + +/* */ + +static char NMesh_addEdge_doc[] = + "create an edge between two vertices.\n\ +If an edge already exists between those vertices, it is returned. (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_addEdgesData_doc[] = + "add edges data to the mesh."; + +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\ @@ -202,8 +241,9 @@ specified by index. The list contains pairs with the \n\ bone name and the weight."; -static char NMesh_update_doc[] = "(recalc_normals = 0) - updates the Mesh.\n\ -if recalc_normals is given and is equal to 1, normal vectors are recalculated."; +static char NMesh_update_doc[] = "(recalc_normals = 0, store_edges = 0) - updates the Mesh.\n\ +if recalc_normals is given and is equal to 1, normal vectors are recalculated.\n\ +if store_edges is given qnd is equal to 1, egdes data are stored."; static char NMesh_getMode_doc[] = "() - get the mode flags of this nmesh as an or'ed int value."; @@ -242,10 +282,11 @@ 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, renormal]) - Return a raw mesh to Blender\n\n\ + "(mesh, [name, renormal, store_edges]) - Return a raw mesh to Blender\n\n\ (mesh) The NMesh object to store\n\ [name] The mesh to replace\n\ -[renormal=1] Flag to control vertex normal recalculation\n\n\ +[renormal=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."; @@ -840,6 +881,109 @@ PyTypeObject NMVert_Type = { &NMVert_SeqMethods, /*tp_as_sequence */ }; + +/***************************** + * 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 */ +}; + + + + + + static void NMesh_dealloc( PyObject * self ) { BPy_NMesh *me = ( BPy_NMesh * ) self; @@ -848,6 +992,7 @@ static void NMesh_dealloc( PyObject * self ) Py_DECREF( me->verts ); Py_DECREF( me->faces ); Py_DECREF( me->materials ); + Py_XDECREF( me->edges ); PyObject_DEL( self ); } @@ -1020,7 +1165,7 @@ static PyObject *NMesh_getSelectedFaces( PyObject * self, PyObject * args ) return l; } -static PyObject *NMesh_getActiveFace( PyObject * self, PyObject * args ) +static PyObject *NMesh_getActiveFace( PyObject * self ) { if( ( ( BPy_NMesh * ) self )->sel_face < 0 ) return EXPP_incr_ret( Py_None ); @@ -1108,13 +1253,13 @@ static PyObject *NMesh_hasVertexColours( PyObject * self, PyObject * args ) static PyObject *NMesh_update( PyObject * self, PyObject * args ) { - int recalc_normals = 0; + int recalc_normals = 0, store_edges = 0; BPy_NMesh *nmesh = ( BPy_NMesh * ) self; Mesh *mesh = nmesh->mesh; - if( !PyArg_ParseTuple( args, "|i", &recalc_normals ) ) + if( !PyArg_ParseTuple( args, "|ii", &recalc_normals, &store_edges ) ) return EXPP_ReturnPyObjError( PyExc_AttributeError, - "expected nothing or an int (0 or 1) as argument" ); + "expected nothing, one or two int(s) (0 or 1) as argument" ); if( recalc_normals && recalc_normals != 1 ) return EXPP_ReturnPyObjError( PyExc_ValueError, @@ -1122,9 +1267,9 @@ static PyObject *NMesh_update( PyObject * self, PyObject * args ) if( mesh ) { unlink_existingMeshData( mesh ); - convert_NMeshToMesh( mesh, nmesh ); + convert_NMeshToMesh( mesh, nmesh, store_edges ); } else { - nmesh->mesh = Mesh_fromNMesh( nmesh ); + nmesh->mesh = Mesh_fromNMesh( nmesh, store_edges ); mesh = nmesh->mesh; } @@ -1215,7 +1360,7 @@ static PyObject *NMesh_getVertexInfluences( PyObject * self, PyObject * args ) return influence_list; } -Mesh *Mesh_fromNMesh( BPy_NMesh * nmesh ) +Mesh *Mesh_fromNMesh( BPy_NMesh * nmesh , int store_edges ) { Mesh *mesh = NULL; mesh = add_mesh( ); @@ -1226,26 +1371,11 @@ Mesh *Mesh_fromNMesh( BPy_NMesh * nmesh ) mesh->id.us = 0; /* no user yet */ G.totmesh++; - convert_NMeshToMesh( mesh, nmesh ); + convert_NMeshToMesh( mesh, nmesh, store_edges ); return mesh; } -PyObject *NMesh_link( PyObject * self, PyObject * args ) -{ /* - BPy_Object *bl_obj; - - if (!PyArg_ParseTuple(args, "O!", &Object_Type, &bl_obj)) - return EXPP_ReturnPyErrorObj (PyExc_TypeError, - "NMesh can only be linked to Objects"); - - bl_obj->data = (PyObject *)self; */ - -/* Better use object.link(nmesh), no need for this nmesh.link(object) */ - - return EXPP_incr_ret( Py_None ); -} - static PyObject *NMesh_getMaxSmoothAngle( BPy_NMesh * self ) { PyObject *attr = PyInt_FromLong( self->smoothresh ); @@ -1353,21 +1483,25 @@ static PyObject *NMesh_setMode( PyObject * self, PyObject * args ) return Py_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( getVertGroupNames ), MethodDef( hasVertexColours ), MethodDef( hasFaceUV ), MethodDef( hasVertexUV ), - MethodDef( getActiveFace ), MethodDef( getSelectedFaces ), MethodDef( getVertexInfluences ), MethodDef( getMaterials ), @@ -1379,13 +1513,19 @@ static struct PyMethodDef NMesh_methods[] = { MethodDef( setMode ), MethodDef( setMaxSmoothAngle ), MethodDef( setSubDivLevels ), - {"getMode", ( PyCFunction ) NMesh_getMode, METH_NOARGS, - NMesh_getMode_doc}, - {"getMaxSmoothAngle", ( PyCFunction ) NMesh_getMaxSmoothAngle, - METH_NOARGS, - NMesh_getMaxSmoothAngle_doc}, - {"getSubDivLevels", ( PyCFunction ) NMesh_getSubDivLevels, METH_NOARGS, - NMesh_getSubDivLevels_doc}, + +/* METH_NOARGS: function(PyObject *self) */ +#undef MethodDef +#define MethodDef(func) {#func, (PyCFunction)NMesh_##func, METH_NOARGS,\ + NMesh_##func##_doc} + + MethodDef( printDebug ), + MethodDef( addEdgesData ), + MethodDef( getVertGroupNames ), + MethodDef( getActiveFace ), + MethodDef( getMode ), + MethodDef( getMaxSmoothAngle ), + MethodDef( getSubDivLevels ), {NULL, NULL, 0, NULL} }; @@ -1425,11 +1565,18 @@ static PyObject *NMesh_getattr( PyObject * self, char *name ) else if( strcmp( name, "faces" ) == 0 ) return EXPP_incr_ret( me->faces ); + else if( strcmp( name, "edges" ) == 0 ) + { + if (me->edges) + return EXPP_incr_ret( me->edges ); + else + return EXPP_incr_ret( Py_None ); + } else if( strcmp( name, "__members__" ) == 0 ) - return Py_BuildValue( "[s,s,s,s,s,s,s]", + return Py_BuildValue( "[s,s,s,s,s,s,s,s]", "name", "materials", "verts", "users", "faces", "maxSmoothAngle", - "subdivLevels" ); + "subdivLevels", "edges" ); return Py_FindMethod( NMesh_methods, ( PyObject * ) self, name ); } @@ -1529,7 +1676,20 @@ static int NMesh_setattr( PyObject * self, char *name, PyObject * v ) "couldn't retrieve subdiv values from list" ); } } - + else if( strcmp( name, "edges" ) == 0 ) + { + if (me->edges) + { + if (PySequence_Check(v)) + { + Py_DECREF(me->edges); + me->edges = EXPP_incr_ret( v ); + } + } + else + return EXPP_ReturnIntError( PyExc_RuntimeError, + "mesh has no edge information" ); + } else return EXPP_ReturnIntError( PyExc_AttributeError, name ); @@ -1611,9 +1771,15 @@ static BPy_NMFace *nmface_from_data( BPy_NMesh * mesh, int vidxs[4], return newf; } -static BPy_NMVert *nmvert_from_data( BPy_NMesh * me, - MVert * vert, MSticky * st, float *co, - int idx, char flag ) +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 ); @@ -1667,6 +1833,7 @@ static PyObject *new_NMesh_internal( Mesh * oldmesh, me->subdiv[0] = NMESH_SUBDIV; me->subdiv[1] = NMESH_SUBDIV; me->smoothresh = NMESH_SMOOTHRESH; + me->edges = NULL; /* no edge data by default */ me->object = NULL; /* not linked to any object yet */ @@ -1682,7 +1849,8 @@ static PyObject *new_NMesh_internal( Mesh * oldmesh, MFace *mfaces; TFace *tfaces; MCol *mcols; - int i, totvert, totface; + MEdge *medges; + int i, totvert, totface, totedge; if( dlm ) { me->name = EXPP_incr_ret( Py_None ); @@ -1694,9 +1862,11 @@ static PyObject *new_NMesh_internal( Mesh * oldmesh, mfaces = dlm->mface; tfaces = dlm->tface; mcols = dlm->mcol; + medges = dlm->medge; totvert = dlm->totvert; totface = dlm->totface; + totedge = dlm->totedge; } else { me->name = PyString_FromString( oldmesh->id.name + 2 ); me->mesh = oldmesh; @@ -1710,9 +1880,11 @@ static PyObject *new_NMesh_internal( Mesh * oldmesh, mfaces = oldmesh->mface; tfaces = oldmesh->tface; mcols = oldmesh->mcol; + medges = oldmesh->medge; totvert = oldmesh->totvert; totface = oldmesh->totface; + totedge = oldmesh->totedge; me->sel_face = get_active_faceindex( oldmesh ); } @@ -1732,8 +1904,7 @@ static PyObject *new_NMesh_internal( Mesh * oldmesh, float *vco = extverts ? &extverts[i * 3] : oldmv->co; PyList_SetItem( me->verts, i, - ( PyObject * ) nmvert_from_data( me, - oldmv, + ( PyObject * ) nmvert_from_data( oldmv, oldst, vco, i, @@ -1762,6 +1933,17 @@ static PyObject *new_NMesh_internal( Mesh * oldmesh, oldtf, oldmc ) ); } + + if (medges) + { + 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 ); @@ -2170,7 +2352,93 @@ PyObject *NMesh_assignMaterials_toObject( BPy_NMesh * nmesh, Object * ob ) return EXPP_incr_ret( Py_None ); } -static int convert_NMeshToMesh( Mesh * mesh, BPy_NMesh * nmesh ) +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); + + faces_edges=mesh->medge; + tot_faces_edges=mesh->totedge; + tot_valid_faces_edges=tot_faces_edges; + + mesh->medge=NULL; + 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; + } + } + } + + /* Now we have the total count of valid edges */ + mesh->totedge=tot_valid_nmedges+tot_valid_faces_edges; + mesh->medge=MEM_callocN(mesh->totedge*sizeof(MEdge), "make mesh edges"); + 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; + medge->crease=0; + ++j; + } + } + + MEM_freeN( valid_nmedges ); + MEM_freeN( faces_edges ); +} + +static int convert_NMeshToMesh( Mesh * mesh, BPy_NMesh * nmesh, int store_edges) { MFace *newmf; TFace *newtf; @@ -2187,6 +2455,7 @@ static int convert_NMeshToMesh( Mesh * mesh, BPy_NMesh * nmesh ) mesh->msticky = NULL; mesh->tface = NULL; mesh->mat = NULL; + mesh->medge = NULL; /* Minor note: we used 'mode' because 'flag' was already used internally * by nmesh */ @@ -2257,6 +2526,19 @@ static int convert_NMeshToMesh( Mesh * mesh, BPy_NMesh * nmesh ) Py_DECREF( mf ); } + /* do the same for edges if there is edge data */ + if (nmesh->edges) + { + int 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 = @@ -2317,6 +2599,15 @@ static int convert_NMeshToMesh( Mesh * mesh, BPy_NMesh * nmesh ) } } + /* After face data has been written, write edge data. + * Edge data are not stored before face ones since we need + * mesh->mface to be correctly initialized. + */ + if (nmesh->edges && store_edges) + { + fill_medge_from_nmesh(mesh, nmesh); + } + return 1; } @@ -2327,11 +2618,12 @@ static PyObject *M_NMesh_PutRaw( PyObject * self, PyObject * args ) Object *ob = NULL; BPy_NMesh *nmesh; int recalc_normals = 1; + int store_edges = 0; - if( !PyArg_ParseTuple( args, "O!|si", - &NMesh_Type, &nmesh, &name, &recalc_normals ) ) + 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 an int" ); + "expected an NMesh object and optionally also a string and two ints" ); if( !PySequence_Check( nmesh->verts ) ) return EXPP_ReturnPyObjError( PyExc_AttributeError, @@ -2376,7 +2668,7 @@ static PyObject *M_NMesh_PutRaw( PyObject * self, PyObject * args ) PyString_AsString( nmesh->name ) ); unlink_existingMeshData( mesh ); - convert_NMeshToMesh( mesh, nmesh ); + convert_NMeshToMesh( mesh, nmesh, store_edges ); nmesh->mesh = mesh; if( recalc_normals ) @@ -2524,6 +2816,22 @@ static PyObject *M_NMesh_FaceTranspModesDict( void ) return FTM; } +static PyObject *M_NMesh_EdgeFlagsDict( void ) +{ + PyObject *EF = M_constant_New( ); + + if( EF ) { + BPy_constant *d = ( BPy_constant * ) EF; + + constant_insert(d, "SELECT", PyInt_FromLong(1)); + constant_insert(d, "EDGEDRAW", PyInt_FromLong(ME_EDGEDRAW)); + constant_insert(d, "SEAM", PyInt_FromLong(ME_SEAM)); + constant_insert(d, "FGON", PyInt_FromLong(ME_FGON)); + } + + return EF; +} + PyObject *NMesh_Init( void ) { PyObject *submodule; @@ -2532,6 +2840,7 @@ PyObject *NMesh_Init( void ) 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; @@ -2551,6 +2860,8 @@ PyObject *NMesh_Init( void ) if( FaceTranspModes ) PyModule_AddObject( submodule, "FaceTranspModes", FaceTranspModes ); + if( EdgeFlags ) + PyModule_AddObject( submodule, "EdgeFlags", EdgeFlags ); g_nmeshmodule = submodule; return submodule; @@ -2581,29 +2892,376 @@ Mesh *Mesh_FromPyObject( PyObject * pyobj, Object * ob ) if( nmesh->mesh ) { mesh = nmesh->mesh; - unlink_existingMeshData( mesh ); - convert_NMeshToMesh( mesh, nmesh ); } else { - nmesh->mesh = Mesh_fromNMesh( nmesh ); + nmesh->mesh = Mesh_fromNMesh( nmesh, 1 ); mesh = nmesh->mesh; - } - nmesh->object = ob; /* linking for vgrouping methods */ + 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 ) ); + if( nmesh->name && nmesh->name != Py_None ) + new_id( &( G.main->mesh ), &mesh->id, + PyString_AsString( nmesh->name ) ); - mesh_update( mesh ); - - nmesh_updateMaterials( nmesh ); + mesh_update( mesh ); + nmesh_updateMaterials( nmesh ); + } return mesh; } 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); + PyList_Append(nmesh->edges, newEdge); + return newEdge; + } + else + return EXPP_incr_ret( Py_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 (!bmesh->edges) + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "NMesh has no edge data." ); + + 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 (!bmesh->edges) + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "NMesh has no edge data." ); + + 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 (!bmesh->edges) + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "NMesh has no edge data." ); + + 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); + + return EXPP_incr_ret( Py_None ); +} + + +static PyObject *NMesh_addEdgesData( PyObject * self ) +{ + /* Here we uses make_edges to create edges data. + * Since Mesh corresponding to NMesh may not content the same data as + * the NMesh and since maybe the NMesh has been created from scratch, + * we creates a temporary Mesh to use to call make_edges + */ + BPy_NMesh *nmesh=(BPy_NMesh *)self; + Mesh *tempMesh=NULL; + int i; + + if (nmesh->edges) + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "NMesh has already edge data." ); + + tempMesh=MEM_callocN(sizeof(Mesh), "temp mesh"); + convert_NMeshToMesh(tempMesh, nmesh, 0); + + make_edges(tempMesh); + + nmesh->edges = PyList_New( tempMesh->totedge ); + for( i = 0; i < tempMesh->totedge; ++i ) + { + MEdge *edge = (tempMesh->medge) + i; + /* By using nmedge_from_data, an important assumption is made: + * every vertex in nmesh has been written in tempMesh in the same order + * than in nmesh->verts. + * Actually this assumption is needed since nmedge_from_data get the + * two NMVert for the newly created edge by using a PyList_GetItem with + * the indices stored in edge. Those indices are valid for nmesh only if + * nmesh->verts and tempMesh->mvert are identical (same number of vertices + * in same order). + */ + PyList_SetItem( nmesh->edges, i, (PyObject*)nmedge_from_data ( nmesh, edge ) ); + } + + unlink_existingMeshData(tempMesh); + MEM_freeN(tempMesh); + + return EXPP_incr_ret( Py_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 || !nmesh->edges) + PyList_Append(nmesh->faces, (PyObject*)face); + + if (nmesh->edges) + { + + 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; + } + } + + return EXPP_incr_ret( Py_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 (nmesh->edges) + { + + 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); + } + } + } + } + + return EXPP_incr_ret( Py_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; itotvert; ++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"); + if (mesh->medge) + { + int i; + for (i=0; itotedge; ++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); + } + } + else + printf(" No edge informations\n"); + + printf("**Faces\n"); + { + int i; + for (i=0; itotface; ++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); + } + } + + return EXPP_incr_ret( Py_None ); +} + +/* */ static PyObject *NMesh_addVertGroup( PyObject * self, PyObject * args ) { char *groupStr; @@ -3024,7 +3682,8 @@ static PyObject *NMesh_renameVertGroup( PyObject * self, PyObject * args ) return EXPP_incr_ret( Py_None ); } -static PyObject *NMesh_getVertGroupNames( PyObject * self, PyObject * args ) + +static PyObject *NMesh_getVertGroupNames( PyObject * self ) { bDeformGroup *defGroup; PyObject *list; diff --git a/source/blender/python/api2_2x/NMesh.h b/source/blender/python/api2_2x/NMesh.h index 6602e9a251c..a139deb2647 100644 --- a/source/blender/python/api2_2x/NMesh.h +++ b/source/blender/python/api2_2x/NMesh.h @@ -52,6 +52,7 @@ extern PyTypeObject NMesh_Type; extern PyTypeObject NMFace_Type; extern PyTypeObject NMVert_Type; extern PyTypeObject NMCol_Type; +extern PyTypeObject NMEdge_Type; struct BPy_Object; @@ -71,6 +72,7 @@ void remove_vert_def_nr( Object * ob, int def_nr, int vertnum ); #define BPy_NMFace_Check(v) ((v)->ob_type == &NMFace_Type) #define BPy_NMVert_Check(v) ((v)->ob_type == &NMVert_Type) #define BPy_NMCol_Check(v) ((v)->ob_type == &NMCol_Type) +#define BPy_NMEdge_Check(v) ((v)->ob_type == &NMEdge_Type) /* Typedefs for the new types */ @@ -103,6 +105,14 @@ typedef struct { } BPy_NMFace; /* an NMesh face */ +typedef struct { + PyObject_HEAD /* required python macro */ + PyObject *v1; + PyObject *v2; + char crease; + short flag; +} BPy_NMEdge; /* an NMesh edge */ + typedef struct { PyObject_HEAD /* required python macro */ Mesh * mesh; @@ -111,6 +121,7 @@ typedef struct { PyObject *materials; PyObject *verts; PyObject *faces; + PyObject *edges; int sel_face; /*@ XXX remove */ short smoothresh; /* max AutoSmooth angle */ short subdiv[2]; /* SubDiv Levels: display and rendering */ @@ -134,7 +145,7 @@ int NMesh_CheckPyObject( PyObject * pyobj ); void mesh_update( Mesh * mesh ); PyObject *new_NMesh( Mesh * oldmesh ); -Mesh *Mesh_fromNMesh( BPy_NMesh * nmesh ); +Mesh *Mesh_fromNMesh( BPy_NMesh * nmesh , int store_edges ); PyObject *NMesh_assignMaterials_toObject( BPy_NMesh * nmesh, Object * ob ); Material **nmesh_updateMaterials( BPy_NMesh * nmesh ); Material **newMaterialList_fromPyList( PyObject * list ); diff --git a/source/blender/python/api2_2x/Window.c b/source/blender/python/api2_2x/Window.c index 28a17d08936..750f7ef30b3 100644 --- a/source/blender/python/api2_2x/Window.c +++ b/source/blender/python/api2_2x/Window.c @@ -25,7 +25,7 @@ * * This is a new part of Blender. * - * Contributor(s): Willian P. Germano, Tom Musgrove + * Contributor(s): Willian P. Germano, Tom Musgrove, Michael Reimpell, Yann Vernier * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ @@ -68,7 +68,7 @@ extern int EXPP_disable_force_draw; /* Callback used by the file and image selector access functions */ -static PyObject *( *EXPP_FS_PyCallback ) ( PyObject * arg ) = NULL; +static PyObject *EXPP_FS_PyCallback = NULL; /*****************************************************************************/ /* Python API function prototypes for the Window module. */ @@ -452,13 +452,24 @@ static PyObject *M_Window_QRedrawAll( PyObject * self, PyObject * args ) static void getSelectedFile( char *name ) { - if( !EXPP_FS_PyCallback ) - return; - - PyObject_CallFunction( ( PyObject * ) EXPP_FS_PyCallback, "s", name ); - - EXPP_FS_PyCallback = NULL; - + PyObject *callback; + PyObject *result; + + callback = EXPP_FS_PyCallback; + result = PyObject_CallFunction( EXPP_FS_PyCallback, "s", name ); + if ((!result) && (G.f & G_DEBUG)) { + fprintf(stderr, "BPy error: Callback call failed!\n"); + } + Py_XDECREF(result); + /* Catch changes of EXPP_FS_PyCallback during the callback call + * due to calls to Blender.Window.FileSelector or + * Blender.Window.ImageSelector inside the python callback. */ + if (callback == EXPP_FS_PyCallback) { + Py_DECREF(EXPP_FS_PyCallback); + EXPP_FS_PyCallback = NULL; + } else { + Py_DECREF(callback); + } return; } @@ -470,12 +481,12 @@ static PyObject *M_Window_FileSelector( PyObject * self, PyObject * args ) Script *script = G.main->script.last; int startspace = 0; - if( !PyArg_ParseTuple( args, "O!|ss", - &PyFunction_Type, &EXPP_FS_PyCallback, &title, - &filename ) ) + if( (!PyArg_ParseTuple( args, "O|ss", &EXPP_FS_PyCallback, &title, &filename ) ) + || (!PyCallable_Check(EXPP_FS_PyCallback))) return EXPP_ReturnPyObjError( PyExc_AttributeError, "\nexpected a callback function (and optionally one or two strings) " "as argument(s)" ); + Py_XINCREF(EXPP_FS_PyCallback); /* trick: we move to a spacescript because then the fileselector will properly * unset our SCRIPT_FILESEL flag when the user chooses a file or cancels the @@ -514,13 +525,13 @@ static PyObject *M_Window_ImageSelector( PyObject * self, PyObject * args ) Script *script = G.main->script.last; int startspace = 0; - if( !PyArg_ParseTuple( args, "O!|ss", - &PyFunction_Type, &EXPP_FS_PyCallback, &title, - &filename ) ) + if( !PyArg_ParseTuple( args, "O|ss", &EXPP_FS_PyCallback, &title, &filename ) + || (!PyCallable_Check(EXPP_FS_PyCallback))) return ( EXPP_ReturnPyObjError ( PyExc_AttributeError, "\nexpected a callback function (and optionally one or two strings) " "as argument(s)" ) ); + Py_XINCREF(EXPP_FS_PyCallback); /* trick: we move to a spacescript because then the fileselector will properly * unset our SCRIPT_FILESEL flag when the user chooses a file or cancels the diff --git a/source/blender/python/api2_2x/World.c b/source/blender/python/api2_2x/World.c index 88f8576cde8..c6e4dae4efa 100644 --- a/source/blender/python/api2_2x/World.c +++ b/source/blender/python/api2_2x/World.c @@ -68,6 +68,8 @@ static PyObject *World_setIpo( BPy_World * self, PyObject * args ); static PyObject *World_clearIpo( BPy_World * self ); static PyObject *World_getName( BPy_World * self ); static PyObject *World_setName( BPy_World * self, PyObject * args ); +static PyObject *World_getMode( BPy_World * self ); +static PyObject *World_setMode( BPy_World * self, PyObject * args ); static PyObject *World_getSkytype( BPy_World * self ); static PyObject *World_setSkytype( BPy_World * self, PyObject * args ); static PyObject *World_getMistype( BPy_World * self ); @@ -162,7 +164,11 @@ static PyMethodDef BPy_World_methods[] = { {"getName", ( PyCFunction ) World_getName, METH_NOARGS, "() - Return World Data name"}, {"setName", ( PyCFunction ) World_setName, METH_VARARGS, - "() - Return World Data name"}, + "() - Set World Data name"}, + {"getMode", ( PyCFunction ) World_getMode, METH_NOARGS, + "() - Return World Data mode"}, + {"setMode", ( PyCFunction ) World_setMode, METH_VARARGS, + "(i) - Set World Data mode"}, {"getSkytype", ( PyCFunction ) World_getSkytype, METH_NOARGS, "() - Return World Data skytype"}, {"setSkytype", ( PyCFunction ) World_setSkytype, METH_VARARGS, diff --git a/source/blender/python/api2_2x/doc/NMesh.py b/source/blender/python/api2_2x/doc/NMesh.py index ca2e42ccdbf..ed016880c24 100644 --- a/source/blender/python/api2_2x/doc/NMesh.py +++ b/source/blender/python/api2_2x/doc/NMesh.py @@ -66,6 +66,11 @@ Example:: - ADD - add to background (halo). - ALPHA - draw with transparency. - SUB - subtract from background. +@var EdgeFlags: The available edge flags. + - SELECT - selected. + - EDGEDRAW - edge is drawn out of edition mode. + - SEAM - edge is a seam for LSCM UV unwrapping + - FGON - edge is part of a F-Gon. """ def Col(col = [255, 255, 255, 255]): @@ -139,16 +144,18 @@ def GetRawFromObject(name): be created. """ -def PutRaw(nmesh, name = None, recalculate_normals = 1): +def PutRaw(nmesh, name = None, recalculate_normals = 1, store_edges = 0): """ Put an NMesh object back in Blender. @type nmesh: NMesh @type name: string @type recalculate_normals: int + @type store_edges: int @param name: The name of the mesh data object in Blender which will receive this nmesh data. It can be an existing mesh data object or a new one. @param recalculate_normals: If non-zero, the vertex normals for the mesh will be recalculated. + @param store_edges: if non-zero, the edges data are stored @rtype: None or Object @return: It depends on the 'name' parameter: - I{name} refers to an existing mesh data obj already linked to an @@ -193,6 +200,21 @@ class NMVert: each face can be independently mapped to any part of its texture. """ +class NMEdge: + """ + The NMEdge object + ================= + This object holds mesh edge data. + @type v1: NMVert + @cvar v1: The first vertex of the edge. + @type v2: NMVert + @cvar v2: The second vertex of the edge. + @type crease: int + @cvar crease: The crease value of the edge. It is in the range [0,255]. + @type flag: int + @cvar flag: The bitmask describing edge properties. See L{NMesh.EdgeFlags}. + """ + class NMFace: """ The NMFace object @@ -264,11 +286,71 @@ class NMesh: @cvar verts: The list of NMesh vertices (NMVerts). @cvar users: The number of Objects using (linked to) this mesh. @cvar faces: The list of NMesh faces (NMFaces). + @cvar edges: None if mesh has no edge data, else a list of L{NMEdge} edges. Use L{addEdgesData} to create edge data if it do not exist. @cvar mode: The mode flags for this mesh. See L{setMode}. @cvar subDivLevels: The [display, rendering] subdivision levels in [1, 6]. @cvar maxSmoothAngle: The max angle for auto smoothing. See L{setMode}. """ + def addEdge(v1, v2): + """ + Create an edge between two vertices. + If an edge already exists between those vertices, it is returned. (in blender, only zero or one edge can link two vertices). + Created edge is automatically added to edges list. + You can only call this method if mesh has edge data. + @type v1: NMVert + @param v1: the first vertex of the edge. + @type v2: NMVert + @param v2: the second vertex of the edge. + @rtype: NMEdge + @return: The created or already existing edge. + """ + + def findEdge(v1, v2): + """ + Try to find an edge between two vertices. + If no edge exists between v1 and v2, None is returned. + You can only call this method if mesh has edge data. + @type v1: NMVert + @param v1: the first vertex of the edge. + @type v2: NMVert + @param v2: the second vertex of the edge. + @rtype: NMEdge + @return: The found edge. None if no edge was found. + """ + + def removeEdge(): + """ + remove an edge between two vertices. + All faces using this edge are removed from faces list. + You can only call this method if mesh has edge data. + @type v1: NMVert + @param v1: the first vertex of the edge. + @type v2: NMVert + @param v2: the second vertex of the edge. + """ + + def addFace(face): + """ + Add a face to face list and add to edge list (if edge data exists) necessary edges. + @type face: NMFace + @param face: the face to add to the mesh. + @rtype: list of NMEdge + @return: If mesh has edge data, return the list of face edges. + """ + + def removeFace(): + """ + Remove a face for face list and remove edges no more used by any other face (if edge data exists). + @type face: NMFace + @param face: the face to add to the mesh. + """ + + def addEdgesData(): + """ + If edge data does not exist for the mesh (ie L{edges}==None), then create them. + """ + def addMaterial(material): """ Add a new material to this NMesh's list of materials. This method is the @@ -412,7 +494,7 @@ class NMesh: add them. """ - def update(recalc_normals = 0): + def update(recalc_normals = 0, store_edges = 0): """ Update the mesh in Blender. The changes made are put back to the mesh in Blender, if available, or put in a newly created mesh object if this NMesh @@ -420,6 +502,8 @@ class NMesh: @type recalc_normals: int @param recalc_normals: If given and equal to 1, the vertex normals are recalculated. + @type store_edges: int + @param store_edges: if not 0, then edge data are stored. @note: if your mesh disappears after it's updated, try L{Object.Object.makeDisplayList}. 'Subsurf' meshes (see L{getMode}, L{setMode}) need their display lists updated, too. -- cgit v1.2.3