From a5a2d8c871ac7eb104174467b5e8c074e267974d Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Tue, 26 May 2009 16:22:00 +0000 Subject: [PATCH 02/68] Start OBJ exporter conversion. Just copied export_obj.py to export_obj-2.5.py leaving old one for reference. Once conversion is done, the new one will replace it. --- release/scripts/export_obj-2.5.py | 791 ++++++++++++++++++++++++++++++ 1 file changed, 791 insertions(+) create mode 100644 release/scripts/export_obj-2.5.py diff --git a/release/scripts/export_obj-2.5.py b/release/scripts/export_obj-2.5.py new file mode 100644 index 00000000000..1eb34a62af0 --- /dev/null +++ b/release/scripts/export_obj-2.5.py @@ -0,0 +1,791 @@ +#!BPY + +""" +Name: 'Wavefront (.obj)...' +Blender: 248 +Group: 'Export' +Tooltip: 'Save a Wavefront OBJ File' +""" + +__author__ = "Campbell Barton, Jiri Hnidek" +__url__ = ['http://wiki.blender.org/index.php/Scripts/Manual/Export/wavefront_obj', 'www.blender.org', 'blenderartists.org'] +__version__ = "1.2" + +__bpydoc__ = """\ +This script is an exporter to OBJ file format. + +Usage: + +Select the objects you wish to export and run this script from "File->Export" menu. +Selecting the default options from the popup box will be good in most cases. +All objects that can be represented as a mesh (mesh, curve, metaball, surface, text3d) +will be exported as mesh data. +""" + + +# -------------------------------------------------------------------------- +# OBJ Export v1.1 by Campbell Barton (AKA Ideasman) +# -------------------------------------------------------------------------- +# ***** 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. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + + +import bpy + +# import Blender +# from Blender import Mesh, Scene, Window, sys, Image, Draw +# import BPyMesh +# import BPyObject +# import BPySys +# import BPyMessages + +# Returns a tuple - path,extension. +# 'hello.obj' > ('hello', '.obj') +def splitExt(path): + dotidx = path.rfind('.') + if dotidx == -1: + return path, '' + else: + return path[:dotidx], path[dotidx:] + +def fixName(name): + if name == None: + return 'None' + else: + return name.replace(' ', '_') + +# A Dict of Materials +# (material.name, image.name):matname_imagename # matname_imagename has gaps removed. +MTL_DICT = {} + +def write_mtl(filename): + + world = Blender.World.GetCurrent() + if world: + worldAmb = world.getAmb() + else: + worldAmb = (0,0,0) # Default value + + file = open(filename, "w") + file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1]) + file.write('# Material Count: %i\n' % len(MTL_DICT)) + # Write material/image combinations we have used. + for key, (mtl_mat_name, mat, img) in MTL_DICT.iteritems(): + + # Get the Blender data for the material and the image. + # Having an image named None will make a bug, dont do it :) + + file.write('newmtl %s\n' % mtl_mat_name) # Define a new material: matname_imgname + + if mat: + file.write('Ns %.6f\n' % ((mat.getHardness()-1) * 1.9607843137254901) ) # Hardness, convert blenders 1-511 to MTL's + file.write('Ka %.6f %.6f %.6f\n' % tuple([c*mat.amb for c in worldAmb]) ) # Ambient, uses mirror colour, + file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.ref for c in mat.rgbCol]) ) # Diffuse + file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.spec for c in mat.specCol]) ) # Specular + file.write('Ni %.6f\n' % mat.IOR) # Refraction index + file.write('d %.6f\n' % mat.alpha) # Alpha (obj uses 'd' for dissolve) + + # 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting. + if mat.getMode() & Blender.Material.Modes['SHADELESS']: + file.write('illum 0\n') # ignore lighting + elif mat.getSpec() == 0: + file.write('illum 1\n') # no specular. + else: + file.write('illum 2\n') # light normaly + + else: + #write a dummy material here? + file.write('Ns 0\n') + file.write('Ka %.6f %.6f %.6f\n' % tuple([c for c in worldAmb]) ) # Ambient, uses mirror colour, + file.write('Kd 0.8 0.8 0.8\n') + file.write('Ks 0.8 0.8 0.8\n') + file.write('d 1\n') # No alpha + file.write('illum 2\n') # light normaly + + # Write images! + if img: # We have an image on the face! + file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image + + elif mat: # No face image. if we havea material search for MTex image. + for mtex in mat.getTextures(): + if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: + try: + filename = mtex.tex.image.filename.split('\\')[-1].split('/')[-1] + file.write('map_Kd %s\n' % filename) # Diffuse mapping image + break + except: + # Texture has no image though its an image type, best ignore. + pass + + file.write('\n\n') + + file.close() + +def copy_file(source, dest): + file = open(source, 'rb') + data = file.read() + file.close() + + file = open(dest, 'wb') + file.write(data) + file.close() + + +def copy_images(dest_dir): + if dest_dir[-1] != sys.sep: + dest_dir += sys.sep + + # Get unique image names + uniqueImages = {} + for matname, mat, image in MTL_DICT.itervalues(): # Only use image name + # Get Texface images + if image: + uniqueImages[image] = image # Should use sets here. wait until Python 2.4 is default. + + # Get MTex images + if mat: + for mtex in mat.getTextures(): + if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: + image_tex = mtex.tex.image + if image_tex: + try: + uniqueImages[image_tex] = image_tex + except: + pass + + # Now copy images + copyCount = 0 + + for bImage in uniqueImages.itervalues(): + image_path = sys.expandpath(bImage.filename) + if sys.exists(image_path): + # Make a name for the target path. + dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] + if not sys.exists(dest_image_path): # Image isnt alredy there + print '\tCopying "%s" > "%s"' % (image_path, dest_image_path) + copy_file(image_path, dest_image_path) + copyCount+=1 + print '\tCopied %d images' % copyCount + +def write(filename, objects,\ +EXPORT_TRI=False, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_NORMALS_HQ=False,\ +EXPORT_UV=True, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False,\ +EXPORT_APPLY_MODIFIERS=True, EXPORT_ROTX90=True, EXPORT_BLEN_OBS=True,\ +EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_KEEP_VERT_ORDER=False): + ''' + Basic write function. The context and options must be alredy set + This can be accessed externaly + eg. + write( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options. + ''' + + def veckey3d(v): + return round(v.x, 6), round(v.y, 6), round(v.z, 6) + + def veckey2d(v): + return round(v.x, 6), round(v.y, 6) + + print 'OBJ Export path: "%s"' % filename + temp_mesh_name = '~tmp-mesh' + + time1 = sys.time() + scn = Scene.GetCurrent() + + file = open(filename, "w") + + # Write Header + file.write('# Blender3D v%s OBJ File: %s\n' % (Blender.Get('version'), Blender.Get('filename').split('/')[-1].split('\\')[-1] )) + file.write('# www.blender3d.org\n') + + # Tell the obj file what material file to use. + if EXPORT_MTL: + mtlfilename = '%s.mtl' % '.'.join(filename.split('.')[:-1]) + file.write('mtllib %s\n' % ( mtlfilename.split('\\')[-1].split('/')[-1] )) + + # Get the container mesh. - used for applying modifiers and non mesh objects. + containerMesh = meshName = tempMesh = None + for meshName in Blender.NMesh.GetNames(): + if meshName.startswith(temp_mesh_name): + tempMesh = Mesh.Get(meshName) + if not tempMesh.users: + containerMesh = tempMesh + if not containerMesh: + containerMesh = Mesh.New(temp_mesh_name) + + if EXPORT_ROTX90: + mat_xrot90= Blender.Mathutils.RotationMatrix(-90, 4, 'x') + + del meshName + del tempMesh + + # Initialize totals, these are updated each object + totverts = totuvco = totno = 1 + + face_vert_index = 1 + + globalNormals = {} + + # Get all meshs + for ob_main in objects: + for ob, ob_mat in BPyObject.getDerivedObjects(ob_main): + # Will work for non meshes now! :) + # getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None) + me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, False, scn) + if not me: + continue + + if EXPORT_UV: + faceuv= me.faceUV + else: + faceuv = False + + # We have a valid mesh + if EXPORT_TRI and me.faces: + # Add a dummy object to it. + has_quads = False + for f in me.faces: + if len(f) == 4: + has_quads = True + break + + if has_quads: + oldmode = Mesh.Mode() + Mesh.Mode(Mesh.SelectModes['FACE']) + + me.sel = True + tempob = scn.objects.new(me) + me.quadToTriangle(0) # more=0 shortest length + oldmode = Mesh.Mode(oldmode) + scn.objects.unlink(tempob) + + Mesh.Mode(oldmode) + + # Make our own list so it can be sorted to reduce context switching + faces = [ f for f in me.faces ] + + if EXPORT_EDGES: + edges = me.edges + else: + edges = [] + + if not (len(faces)+len(edges)+len(me.verts)): # Make sure there is somthing to write + continue # dont bother with this mesh. + + if EXPORT_ROTX90: + me.transform(ob_mat*mat_xrot90) + else: + me.transform(ob_mat) + + # High Quality Normals + if EXPORT_NORMALS and faces: + if EXPORT_NORMALS_HQ: + BPyMesh.meshCalcNormals(me) + else: + # transforming normals is incorrect + # when the matrix is scaled, + # better to recalculate them + me.calcNormals() + + # # Crash Blender + #materials = me.getMaterials(1) # 1 == will return None in the list. + materials = me.materials + + materialNames = [] + materialItems = materials[:] + if materials: + for mat in materials: + if mat: # !=None + materialNames.append(mat.name) + else: + materialNames.append(None) + # Cant use LC because some materials are None. + # materialNames = map(lambda mat: mat.name, materials) # Bug Blender, dosent account for null materials, still broken. + + # Possible there null materials, will mess up indicies + # but at least it will export, wait until Blender gets fixed. + materialNames.extend((16-len(materialNames)) * [None]) + materialItems.extend((16-len(materialItems)) * [None]) + + # Sort by Material, then images + # so we dont over context switch in the obj file. + if EXPORT_KEEP_VERT_ORDER: + pass + elif faceuv: + try: faces.sort(key = lambda a: (a.mat, a.image, a.smooth)) + except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) + elif len(materials) > 1: + try: faces.sort(key = lambda a: (a.mat, a.smooth)) + except: faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth))) + else: + # no materials + try: faces.sort(key = lambda a: a.smooth) + except: faces.sort(lambda a,b: cmp(a.smooth, b.smooth)) + + # Set the default mat to no material and no image. + contextMat = (0, 0) # Can never be this, so we will label a new material teh first chance we get. + contextSmooth = None # Will either be true or false, set bad to force initialization switch. + + if EXPORT_BLEN_OBS or EXPORT_GROUP_BY_OB: + name1 = ob.name + name2 = ob.getData(1) + if name1 == name2: + obnamestring = fixName(name1) + else: + obnamestring = '%s_%s' % (fixName(name1), fixName(name2)) + + if EXPORT_BLEN_OBS: + file.write('o %s\n' % obnamestring) # Write Object name + else: # if EXPORT_GROUP_BY_OB: + file.write('g %s\n' % obnamestring) + + + # Vert + for v in me.verts: + file.write('v %.6f %.6f %.6f\n' % tuple(v.co)) + + # UV + if faceuv: + uv_face_mapping = [[0,0,0,0] for f in faces] # a bit of a waste for tri's :/ + + uv_dict = {} # could use a set() here + for f_index, f in enumerate(faces): + + for uv_index, uv in enumerate(f.uv): + uvkey = veckey2d(uv) + try: + uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] + except: + uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) + file.write('vt %.6f %.6f\n' % tuple(uv)) + + uv_unique_count = len(uv_dict) + del uv, uvkey, uv_dict, f_index, uv_index + # Only need uv_unique_count and uv_face_mapping + + # NORMAL, Smooth/Non smoothed. + if EXPORT_NORMALS: + for f in faces: + if f.smooth: + for v in f: + noKey = veckey3d(v.no) + if not globalNormals.has_key( noKey ): + globalNormals[noKey] = totno + totno +=1 + file.write('vn %.6f %.6f %.6f\n' % noKey) + else: + # Hard, 1 normal from the face. + noKey = veckey3d(f.no) + if not globalNormals.has_key( noKey ): + globalNormals[noKey] = totno + totno +=1 + file.write('vn %.6f %.6f %.6f\n' % noKey) + + if not faceuv: + f_image = None + + for f_index, f in enumerate(faces): + f_v= f.v + f_smooth= f.smooth + f_mat = min(f.mat, len(materialNames)-1) + if faceuv: + f_image = f.image + f_uv= f.uv + + # MAKE KEY + if faceuv and f_image: # Object is always true. + key = materialNames[f_mat], f_image.name + else: + key = materialNames[f_mat], None # No image, use None instead. + + # CHECK FOR CONTEXT SWITCH + if key == contextMat: + pass # Context alredy switched, dont do anythoing + else: + if key[0] == None and key[1] == None: + # Write a null material, since we know the context has changed. + if EXPORT_GROUP_BY_MAT: + file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.getData(1))) ) # can be mat_image or (null) + file.write('usemtl (null)\n') # mat, image + + else: + mat_data= MTL_DICT.get(key) + if not mat_data: + # First add to global dict so we can export to mtl + # Then write mtl + + # Make a new names from the mat and image name, + # converting any spaces to underscores with fixName. + + # If none image dont bother adding it to the name + if key[1] == None: + mat_data = MTL_DICT[key] = ('%s'%fixName(key[0])), materialItems[f_mat], f_image + else: + mat_data = MTL_DICT[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image + + if EXPORT_GROUP_BY_MAT: + file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.getData(1)), mat_data[0]) ) # can be mat_image or (null) + file.write('usemtl %s\n' % mat_data[0]) # can be mat_image or (null) + + contextMat = key + if f_smooth != contextSmooth: + if f_smooth: # on now off + file.write('s 1\n') + contextSmooth = f_smooth + else: # was off now on + file.write('s off\n') + contextSmooth = f_smooth + + file.write('f') + if faceuv: + if EXPORT_NORMALS: + if f_smooth: # Smoothed, use vertex normals + for vi, v in enumerate(f_v): + file.write( ' %d/%d/%d' % (\ + v.index+totverts,\ + totuvco + uv_face_mapping[f_index][vi],\ + globalNormals[ veckey3d(v.no) ])) # vert, uv, normal + + else: # No smoothing, face normals + no = globalNormals[ veckey3d(f.no) ] + for vi, v in enumerate(f_v): + file.write( ' %d/%d/%d' % (\ + v.index+totverts,\ + totuvco + uv_face_mapping[f_index][vi],\ + no)) # vert, uv, normal + + else: # No Normals + for vi, v in enumerate(f_v): + file.write( ' %d/%d' % (\ + v.index+totverts,\ + totuvco + uv_face_mapping[f_index][vi])) # vert, uv + + face_vert_index += len(f_v) + + else: # No UV's + if EXPORT_NORMALS: + if f_smooth: # Smoothed, use vertex normals + for v in f_v: + file.write( ' %d//%d' % (\ + v.index+totverts,\ + globalNormals[ veckey3d(v.no) ])) + else: # No smoothing, face normals + no = globalNormals[ veckey3d(f.no) ] + for v in f_v: + file.write( ' %d//%d' % (\ + v.index+totverts,\ + no)) + else: # No Normals + for v in f_v: + file.write( ' %d' % (\ + v.index+totverts)) + + file.write('\n') + + # Write edges. + if EXPORT_EDGES: + LOOSE= Mesh.EdgeFlags.LOOSE + for ed in edges: + if ed.flag & LOOSE: + file.write('f %d %d\n' % (ed.v1.index+totverts, ed.v2.index+totverts)) + + # Make the indicies global rather then per mesh + totverts += len(me.verts) + if faceuv: + totuvco += uv_unique_count + me.verts= None + file.close() + + + # Now we have all our materials, save them + if EXPORT_MTL: + write_mtl(mtlfilename) + if EXPORT_COPY_IMAGES: + dest_dir = filename + # Remove chars until we are just the path. + while dest_dir and dest_dir[-1] not in '\\/': + dest_dir = dest_dir[:-1] + if dest_dir: + copy_images(dest_dir) + else: + print '\tError: "%s" could not be used as a base for an image path.' % filename + + print "OBJ Export time: %.2f" % (sys.time() - time1) + + +# converted: 0% +def write_ui(filename): + + if not filename.lower().endswith('.obj'): + filename += '.obj' + + if not BPyMessages.Warning_SaveOver(filename): + return + + global EXPORT_APPLY_MODIFIERS, EXPORT_ROTX90, EXPORT_TRI, EXPORT_EDGES,\ + EXPORT_NORMALS, EXPORT_NORMALS_HQ, EXPORT_UV,\ + EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES,\ + EXPORT_ANIMATION, EXPORT_COPY_IMAGES, EXPORT_BLEN_OBS,\ + EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER + + EXPORT_APPLY_MODIFIERS = Draw.Create(0) + EXPORT_ROTX90 = Draw.Create(1) + EXPORT_TRI = Draw.Create(0) + EXPORT_EDGES = Draw.Create(1) + EXPORT_NORMALS = Draw.Create(0) + EXPORT_NORMALS_HQ = Draw.Create(0) + EXPORT_UV = Draw.Create(1) + EXPORT_MTL = Draw.Create(1) + EXPORT_SEL_ONLY = Draw.Create(1) + EXPORT_ALL_SCENES = Draw.Create(0) + EXPORT_ANIMATION = Draw.Create(0) + EXPORT_COPY_IMAGES = Draw.Create(0) + EXPORT_BLEN_OBS = Draw.Create(0) + EXPORT_GROUP_BY_OB = Draw.Create(0) + EXPORT_GROUP_BY_MAT = Draw.Create(0) + EXPORT_KEEP_VERT_ORDER = Draw.Create(1) + + # Old UI + ''' + # removed too many options are bad! + + # Get USER Options + pup_block = [\ + ('Context...'),\ + ('Selection Only', EXPORT_SEL_ONLY, 'Only export objects in visible selection. Else export whole scene.'),\ + ('All Scenes', EXPORT_ALL_SCENES, 'Each scene as a separate OBJ file.'),\ + ('Animation', EXPORT_ANIMATION, 'Each frame as a numbered OBJ file.'),\ + ('Object Prefs...'),\ + ('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object. May break vert order for morph targets.'),\ + ('Rotate X90', EXPORT_ROTX90 , 'Rotate on export so Blenders UP is translated into OBJs UP'),\ + ('Keep Vert Order', EXPORT_KEEP_VERT_ORDER, 'Keep vert and face order, disables some other options.'),\ + ('Extra Data...'),\ + ('Edges', EXPORT_EDGES, 'Edges not connected to faces.'),\ + ('Normals', EXPORT_NORMALS, 'Export vertex normal data (Ignored on import).'),\ + ('High Quality Normals', EXPORT_NORMALS_HQ, 'Calculate high quality normals for rendering.'),\ + ('UVs', EXPORT_UV, 'Export texface UV coords.'),\ + ('Materials', EXPORT_MTL, 'Write a separate MTL file with the OBJ.'),\ + ('Copy Images', EXPORT_COPY_IMAGES, 'Copy image files to the export directory, never overwrite.'),\ + ('Triangulate', EXPORT_TRI, 'Triangulate quads.'),\ + ('Grouping...'),\ + ('Objects', EXPORT_BLEN_OBS, 'Export blender objects as "OBJ objects".'),\ + ('Object Groups', EXPORT_GROUP_BY_OB, 'Export blender objects as "OBJ Groups".'),\ + ('Material Groups', EXPORT_GROUP_BY_MAT, 'Group by materials.'),\ + ] + + if not Draw.PupBlock('Export...', pup_block): + return + ''' + + # BEGIN ALTERNATIVE UI ******************* + if True: + + EVENT_NONE = 0 + EVENT_EXIT = 1 + EVENT_REDRAW = 2 + EVENT_EXPORT = 3 + + GLOBALS = {} + GLOBALS['EVENT'] = EVENT_REDRAW + #GLOBALS['MOUSE'] = Window.GetMouseCoords() + GLOBALS['MOUSE'] = [i/2 for i in Window.GetScreenSize()] + + def obj_ui_set_event(e,v): + GLOBALS['EVENT'] = e + + def do_split(e,v): + global EXPORT_BLEN_OBS, EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_APPLY_MODIFIERS, KEEP_VERT_ORDER + if EXPORT_BLEN_OBS.val or EXPORT_GROUP_BY_OB.val or EXPORT_GROUP_BY_MAT.val or EXPORT_APPLY_MODIFIERS.val: + EXPORT_KEEP_VERT_ORDER.val = 0 + else: + EXPORT_KEEP_VERT_ORDER.val = 1 + + def do_vertorder(e,v): + global EXPORT_BLEN_OBS, EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_APPLY_MODIFIERS, KEEP_VERT_ORDER + if EXPORT_KEEP_VERT_ORDER.val: + EXPORT_BLEN_OBS.val = EXPORT_GROUP_BY_OB.val = EXPORT_GROUP_BY_MAT.val = EXPORT_APPLY_MODIFIERS.val = 0 + else: + if not (EXPORT_BLEN_OBS.val or EXPORT_GROUP_BY_OB.val or EXPORT_GROUP_BY_MAT.val or EXPORT_APPLY_MODIFIERS.val): + EXPORT_KEEP_VERT_ORDER.val = 1 + + def do_help(e,v): + url = __url__[0] + print 'Trying to open web browser with documentation at this address...' + print '\t' + url + + try: + import webbrowser + webbrowser.open(url) + except: + print '...could not open a browser window.' + + def obj_ui(): + ui_x, ui_y = GLOBALS['MOUSE'] + + # Center based on overall pup size + ui_x -= 165 + ui_y -= 110 + + global EXPORT_APPLY_MODIFIERS, EXPORT_ROTX90, EXPORT_TRI, EXPORT_EDGES,\ + EXPORT_NORMALS, EXPORT_NORMALS_HQ, EXPORT_UV,\ + EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES,\ + EXPORT_ANIMATION, EXPORT_COPY_IMAGES, EXPORT_BLEN_OBS,\ + EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER + + Draw.Label('Context...', ui_x+9, ui_y+209, 220, 20) + Draw.BeginAlign() + EXPORT_SEL_ONLY = Draw.Toggle('Selection Only', EVENT_NONE, ui_x+9, ui_y+189, 110, 20, EXPORT_SEL_ONLY.val, 'Only export objects in visible selection. Else export whole scene.') + EXPORT_ALL_SCENES = Draw.Toggle('All Scenes', EVENT_NONE, ui_x+119, ui_y+189, 110, 20, EXPORT_ALL_SCENES.val, 'Each scene as a separate OBJ file.') + EXPORT_ANIMATION = Draw.Toggle('Animation', EVENT_NONE, ui_x+229, ui_y+189, 110, 20, EXPORT_ANIMATION.val, 'Each frame as a numbered OBJ file.') + Draw.EndAlign() + + + Draw.Label('Output Options...', ui_x+9, ui_y+159, 220, 20) + Draw.BeginAlign() + EXPORT_APPLY_MODIFIERS = Draw.Toggle('Apply Modifiers', EVENT_REDRAW, ui_x+9, ui_y+140, 110, 20, EXPORT_APPLY_MODIFIERS.val, 'Use transformed mesh data from each object. May break vert order for morph targets.', do_split) + EXPORT_ROTX90 = Draw.Toggle('Rotate X90', EVENT_NONE, ui_x+119, ui_y+140, 110, 20, EXPORT_ROTX90.val, 'Rotate on export so Blenders UP is translated into OBJs UP') + EXPORT_COPY_IMAGES = Draw.Toggle('Copy Images', EVENT_NONE, ui_x+229, ui_y+140, 110, 20, EXPORT_COPY_IMAGES.val, 'Copy image files to the export directory, never overwrite.') + Draw.EndAlign() + + + Draw.Label('Export...', ui_x+9, ui_y+109, 220, 20) + Draw.BeginAlign() + EXPORT_EDGES = Draw.Toggle('Edges', EVENT_NONE, ui_x+9, ui_y+90, 50, 20, EXPORT_EDGES.val, 'Edges not connected to faces.') + EXPORT_TRI = Draw.Toggle('Triangulate', EVENT_NONE, ui_x+59, ui_y+90, 70, 20, EXPORT_TRI.val, 'Triangulate quads.') + Draw.EndAlign() + Draw.BeginAlign() + EXPORT_MTL = Draw.Toggle('Materials', EVENT_NONE, ui_x+139, ui_y+90, 70, 20, EXPORT_MTL.val, 'Write a separate MTL file with the OBJ.') + EXPORT_UV = Draw.Toggle('UVs', EVENT_NONE, ui_x+209, ui_y+90, 31, 20, EXPORT_UV.val, 'Export texface UV coords.') + Draw.EndAlign() + Draw.BeginAlign() + EXPORT_NORMALS = Draw.Toggle('Normals', EVENT_NONE, ui_x+250, ui_y+90, 59, 20, EXPORT_NORMALS.val, 'Export vertex normal data (Ignored on import).') + EXPORT_NORMALS_HQ = Draw.Toggle('HQ', EVENT_NONE, ui_x+309, ui_y+90, 31, 20, EXPORT_NORMALS_HQ.val, 'Calculate high quality normals for rendering.') + Draw.EndAlign() + + + Draw.Label('Blender Objects as OBJ:', ui_x+9, ui_y+59, 220, 20) + Draw.BeginAlign() + EXPORT_BLEN_OBS = Draw.Toggle('Objects', EVENT_REDRAW, ui_x+9, ui_y+39, 60, 20, EXPORT_BLEN_OBS.val, 'Export blender objects as "OBJ objects".', do_split) + EXPORT_GROUP_BY_OB = Draw.Toggle('Groups', EVENT_REDRAW, ui_x+69, ui_y+39, 60, 20, EXPORT_GROUP_BY_OB.val, 'Export blender objects as "OBJ Groups".', do_split) + EXPORT_GROUP_BY_MAT = Draw.Toggle('Material Groups', EVENT_REDRAW, ui_x+129, ui_y+39, 100, 20, EXPORT_GROUP_BY_MAT.val, 'Group by materials.', do_split) + Draw.EndAlign() + + EXPORT_KEEP_VERT_ORDER = Draw.Toggle('Keep Vert Order', EVENT_REDRAW, ui_x+239, ui_y+39, 100, 20, EXPORT_KEEP_VERT_ORDER.val, 'Keep vert and face order, disables some other options. Use for morph targets.', do_vertorder) + + Draw.BeginAlign() + Draw.PushButton('Online Help', EVENT_REDRAW, ui_x+9, ui_y+9, 110, 20, 'Load the wiki page for this script', do_help) + Draw.PushButton('Cancel', EVENT_EXIT, ui_x+119, ui_y+9, 110, 20, '', obj_ui_set_event) + Draw.PushButton('Export', EVENT_EXPORT, ui_x+229, ui_y+9, 110, 20, 'Export with these settings', obj_ui_set_event) + Draw.EndAlign() + + + # hack so the toggle buttons redraw. this is not nice at all + while GLOBALS['EVENT'] not in (EVENT_EXIT, EVENT_EXPORT): + Draw.UIBlock(obj_ui, 0) + + if GLOBALS['EVENT'] != EVENT_EXPORT: + return + + # END ALTERNATIVE UI ********************* + + + if EXPORT_KEEP_VERT_ORDER.val: + EXPORT_BLEN_OBS.val = False + EXPORT_GROUP_BY_OB.val = False + EXPORT_GROUP_BY_MAT.val = False + EXPORT_APPLY_MODIFIERS.val = False + + Window.EditMode(0) + Window.WaitCursor(1) + + EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val + EXPORT_ROTX90 = EXPORT_ROTX90.val + EXPORT_TRI = EXPORT_TRI.val + EXPORT_EDGES = EXPORT_EDGES.val + EXPORT_NORMALS = EXPORT_NORMALS.val + EXPORT_NORMALS_HQ = EXPORT_NORMALS_HQ.val + EXPORT_UV = EXPORT_UV.val + EXPORT_MTL = EXPORT_MTL.val + EXPORT_SEL_ONLY = EXPORT_SEL_ONLY.val + EXPORT_ALL_SCENES = EXPORT_ALL_SCENES.val + EXPORT_ANIMATION = EXPORT_ANIMATION.val + EXPORT_COPY_IMAGES = EXPORT_COPY_IMAGES.val + EXPORT_BLEN_OBS = EXPORT_BLEN_OBS.val + EXPORT_GROUP_BY_OB = EXPORT_GROUP_BY_OB.val + EXPORT_GROUP_BY_MAT = EXPORT_GROUP_BY_MAT.val + EXPORT_KEEP_VERT_ORDER = EXPORT_KEEP_VERT_ORDER.val + + + + base_name, ext = splitExt(filename) + context_name = [base_name, '', '', ext] # basename, scene_name, framenumber, extension + + # Use the options to export the data using write() + # def write(filename, objects, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False, EXPORT_APPLY_MODIFIERS=True): + orig_scene = Scene.GetCurrent() + if EXPORT_ALL_SCENES: + export_scenes = Scene.Get() + else: + export_scenes = [orig_scene] + + # Export all scenes. + for scn in export_scenes: + scn.makeCurrent() # If alredy current, this is not slow. + context = scn.getRenderingContext() + orig_frame = Blender.Get('curframe') + + if EXPORT_ALL_SCENES: # Add scene name into the context_name + context_name[1] = '_%s' % BPySys.cleanName(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied. + + # Export an animation? + if EXPORT_ANIMATION: + scene_frames = xrange(context.startFrame(), context.endFrame()+1) # up to and including the end frame. + else: + scene_frames = [orig_frame] # Dont export an animation. + + # Loop through all frames in the scene and export. + for frame in scene_frames: + if EXPORT_ANIMATION: # Add frame to the filename. + context_name[2] = '_%.6d' % frame + + Blender.Set('curframe', frame) + if EXPORT_SEL_ONLY: + export_objects = scn.objects.context + else: + export_objects = scn.objects + + full_path= ''.join(context_name) + + # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad. + # EXPORT THE FILE. + write(full_path, export_objects,\ + EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS,\ + EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL,\ + EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS,\ + EXPORT_ROTX90, EXPORT_BLEN_OBS,\ + EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER) + + Blender.Set('curframe', orig_frame) + + # Restore old active scene. + orig_scene.makeCurrent() + Window.WaitCursor(0) + + +if __name__ == '__main__': + Window.FileSelector(write_ui, 'Export Wavefront OBJ', sys.makename(ext='.obj')) From ab6ec6be7bc8bbd0f22d5b5d8e37527c13aef308 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Tue, 26 May 2009 17:08:10 +0000 Subject: [PATCH 03/68] In bpy, renamed "exec" operator method to "execu" for compatibility with py 2.x ("exec" is a keyword in py 2.x). --- source/blender/python/intern/bpy_operator_wrap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index 43d62b3005f..ab4d5c6169d 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -232,7 +232,7 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve PyTuple_SET_ITEM(args, 1, pyop_dict_from_event(event)); } else if (mode==PYOP_EXEC) { - item= PyObject_GetAttrString(py_class, "exec"); + item= PyObject_GetAttrString(py_class, "execu"); args = PyTuple_New(1); } else if (mode==PYOP_POLL) { @@ -328,7 +328,7 @@ void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata) /* api callbacks, detailed checks dont on adding */ if (PyObject_HasAttrString(py_class, "invoke")) ot->invoke= PYTHON_OT_invoke; - if (PyObject_HasAttrString(py_class, "exec")) + if (PyObject_HasAttrString(py_class, "execu")) ot->exec= PYTHON_OT_exec; if (PyObject_HasAttrString(py_class, "poll")) ot->poll= PYTHON_OT_poll; @@ -391,7 +391,7 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class) {PYOP_ATTR_UINAME, 's', 0, BPY_CLASS_ATTR_OPTIONAL}, {PYOP_ATTR_PROP, 'l', 0, BPY_CLASS_ATTR_OPTIONAL}, {PYOP_ATTR_DESCRIPTION, 's', 0, BPY_CLASS_ATTR_NONE_OK}, - {"exec", 'f', 1, BPY_CLASS_ATTR_OPTIONAL}, + {"execu", 'f', 1, BPY_CLASS_ATTR_OPTIONAL}, {"invoke", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, {"poll", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, {NULL, 0, 0, 0} From 628b06e9c551036d2e4f03610ce52c1b7aa3254e Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Mon, 1 Jun 2009 19:44:22 +0000 Subject: [PATCH 04/68] Woohoo! Context is now passed to operator's exec. Thanks Brecht and Campbell! --- source/blender/python/intern/bpy_operator_wrap.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index ab4d5c6169d..215e42dd95d 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -189,6 +189,8 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve PyObject *args; PyObject *ret= NULL, *py_class_instance, *item= NULL; int ret_flag= (mode==PYOP_POLL ? 0:OPERATOR_CANCELLED); + PointerRNA ptr_context; + PyObject *py_context; PyGILState_STATE gilstate = PyGILState_Ensure(); @@ -233,7 +235,11 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve } else if (mode==PYOP_EXEC) { item= PyObject_GetAttrString(py_class, "execu"); - args = PyTuple_New(1); + args = PyTuple_New(2); + + RNA_pointer_create(NULL, &RNA_Context, C, &ptr_context); + py_context = pyrna_struct_CreatePyObject(&ptr_context); + PyTuple_SET_ITEM(args, 1, py_context); } else if (mode==PYOP_POLL) { item= PyObject_GetAttrString(py_class, "poll"); @@ -391,7 +397,7 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class) {PYOP_ATTR_UINAME, 's', 0, BPY_CLASS_ATTR_OPTIONAL}, {PYOP_ATTR_PROP, 'l', 0, BPY_CLASS_ATTR_OPTIONAL}, {PYOP_ATTR_DESCRIPTION, 's', 0, BPY_CLASS_ATTR_NONE_OK}, - {"execu", 'f', 1, BPY_CLASS_ATTR_OPTIONAL}, + {"execu", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, {"invoke", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, {"poll", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, {NULL, 0, 0, 0} From c09cbacf975f429c7b3f535e5e0ae897499f2e15 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Tue, 2 Jun 2009 20:16:33 +0000 Subject: [PATCH 05/68] Continuing OBJ exporter conversion. - export is initiated by an operator, output filepath is hardcoded for now. - added code in bpy_interface.c to put 'scripts/bpymodules' in sys.path. - no UI atm, using default option values, don't know how to do it yet --- release/scripts/export_obj-2.5.py | 151 +++++++++++++++++-- source/blender/python/intern/bpy_interface.c | 56 ++++++- 2 files changed, 189 insertions(+), 18 deletions(-) diff --git a/release/scripts/export_obj-2.5.py b/release/scripts/export_obj-2.5.py index 1eb34a62af0..2effe7556bc 100644 --- a/release/scripts/export_obj-2.5.py +++ b/release/scripts/export_obj-2.5.py @@ -35,18 +35,19 @@ will be exported as mesh data. # # 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 +# 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. +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- import bpy +import BPySys # import Blender # from Blender import Mesh, Scene, Window, sys, Image, Draw @@ -56,7 +57,7 @@ import bpy # import BPyMessages # Returns a tuple - path,extension. -# 'hello.obj' > ('hello', '.obj') +# 'hello.obj' > ('hello', '.obj') def splitExt(path): dotidx = path.rfind('.') if dotidx == -1: @@ -95,7 +96,7 @@ def write_mtl(filename): if mat: file.write('Ns %.6f\n' % ((mat.getHardness()-1) * 1.9607843137254901) ) # Hardness, convert blenders 1-511 to MTL's - file.write('Ka %.6f %.6f %.6f\n' % tuple([c*mat.amb for c in worldAmb]) ) # Ambient, uses mirror colour, + file.write('Ka %.6f %.6f %.6f\n' % tuple([c*mat.amb for c in worldAmb]) ) # Ambient, uses mirror colour, file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.ref for c in mat.rgbCol]) ) # Diffuse file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.spec for c in mat.specCol]) ) # Specular file.write('Ni %.6f\n' % mat.IOR) # Refraction index @@ -112,14 +113,14 @@ def write_mtl(filename): else: #write a dummy material here? file.write('Ns 0\n') - file.write('Ka %.6f %.6f %.6f\n' % tuple([c for c in worldAmb]) ) # Ambient, uses mirror colour, + file.write('Ka %.6f %.6f %.6f\n' % tuple([c for c in worldAmb]) ) # Ambient, uses mirror colour, file.write('Kd 0.8 0.8 0.8\n') file.write('Ks 0.8 0.8 0.8\n') file.write('d 1\n') # No alpha file.write('illum 2\n') # light normaly # Write images! - if img: # We have an image on the face! + if img: # We have an image on the face! file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image elif mat: # No face image. if we havea material search for MTex image. @@ -183,11 +184,12 @@ def copy_images(dest_dir): copyCount+=1 print '\tCopied %d images' % copyCount -def write(filename, objects,\ -EXPORT_TRI=False, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_NORMALS_HQ=False,\ -EXPORT_UV=True, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False,\ -EXPORT_APPLY_MODIFIERS=True, EXPORT_ROTX90=True, EXPORT_BLEN_OBS=True,\ -EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_KEEP_VERT_ORDER=False): +def write(filename, objects, + EXPORT_TRI=False, EXPORT_EDGES=False, + EXPORT_NORMALS=False, EXPORT_NORMALS_HQ=False, + EXPORT_UV=True, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False, + EXPORT_APPLY_MODIFIERS=True, EXPORT_ROTX90=True, EXPORT_BLEN_OBS=True, + EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_KEEP_VERT_ORDER=False): ''' Basic write function. The context and options must be alredy set This can be accessed externaly @@ -409,9 +411,9 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_KEEP_VERT_ORDER=Fal # MAKE KEY if faceuv and f_image: # Object is always true. - key = materialNames[f_mat], f_image.name + key = materialNames[f_mat], f_image.name else: - key = materialNames[f_mat], None # No image, use None instead. + key = materialNames[f_mat], None # No image, use None instead. # CHECK FOR CONTEXT SWITCH if key == contextMat: @@ -528,7 +530,7 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_KEEP_VERT_ORDER=Fal print "OBJ Export time: %.2f" % (sys.time() - time1) -# converted: 0% +# replaced by do_export def write_ui(filename): if not filename.lower().endswith('.obj'): @@ -787,5 +789,122 @@ def write_ui(filename): Window.WaitCursor(0) -if __name__ == '__main__': - Window.FileSelector(write_ui, 'Export Wavefront OBJ', sys.makename(ext='.obj')) +def do_export(filename, context): +# Window.EditMode(0) +# Window.WaitCursor(1) + + EXPORT_APPLY_MODIFIERS = True + EXPORT_ROTX90 = True + EXPORT_TRI = False + EXPORT_EDGES = False + EXPORT_NORMALS = False + EXPORT_NORMALS_HQ = False + EXPORT_UV = True + EXPORT_MTL = True + EXPORT_SEL_ONLY = True + EXPORT_ALL_SCENES = False # XXX not working atm + EXPORT_ANIMATION = False + EXPORT_COPY_IMAGES = False + EXPORT_BLEN_OBS = True + EXPORT_GROUP_BY_OB = False + EXPORT_GROUP_BY_MAT = False + EXPORT_KEEP_VERT_ORDER = False + + base_name, ext = splitExt(filename) + context_name = [base_name, '', '', ext] # Base name, scene name, frame number, extension + + orig_scene = context.scene + +# if EXPORT_ALL_SCENES: +# export_scenes = bpy.data.scenes +# else: +# export_scenes = [orig_scene] + + # XXX only exporting one scene atm since changing + # current scene is not possible. + # Brecht says that ideally in 2.5 we won't need such a function, + # allowing multiple scenes open at once. + export_scenes = [orig_scene] + + # Export all scenes. + for scn in export_scenes: +# scn.makeCurrent() # If already current, this is not slow. +# context = scn.getRenderingContext() + orig_frame = scn.current_frame + + if EXPORT_ALL_SCENES: # Add scene name into the context_name + context_name[1] = '_%s' % BPySys.cleanName(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied. + + # Export an animation? + if EXPORT_ANIMATION: + scene_frames = xrange(scn.start_frame, context.end_frame+1) # Up to and including the end frame. + else: + scene_frames = [orig_frame] # Dont export an animation. + + # Loop through all frames in the scene and export. + for frame in scene_frames: + if EXPORT_ANIMATION: # Add frame to the filename. + context_name[2] = '_%.6d' % frame + + scn.current_frame = frame + if EXPORT_SEL_ONLY: + export_objects = context.selected_objects + else: + export_objects = scn.objects + + full_path= ''.join(context_name) + + # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad. + # EXPORT THE FILE. +# write(full_path, export_objects, +# EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS, +# EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL, +# EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS, +# EXPORT_ROTX90, EXPORT_BLEN_OBS, +# EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER) + + scn.current_frame = orig_frame + + # Restore old active scene. +# orig_scene.makeCurrent() +# Window.WaitCursor(0) + + +class SCRIPT_OT_export_obj(bpy.types.Operator): + ''' + Operator documentatuon text, will be used for the operator tooltip and python docs. + ''' + __label__ = 'My Operator' + + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + __props__ = [ + # bpy.types.FloatProperty(attr="setting_1", name="Example 1", + # default=10.0, min=0, max=10, description="Add info here"), + # bpy.types.IntProperty(attr="setting_2", default=2), + # bpy.types.BoolProperty(attr="toggle", default=True) + ] + + def execu(self, context): + print("Selected: " + context.active_object.name) + + do_export("/tmp/test.obj", context) + + return 'FINISHED' + + def invoke(self, event): + print("Invoke") + return 'FINISHED' + + def poll(self, context): # Poll isnt working yet + print("Poll") + return True + +if (hasattr(bpy.ops, "SCRIPT_OT_export_obj")): + bpy.ops.remove(bpy.ops.SCRIPT_OT_export_obj) + +bpy.ops.add(SCRIPT_OT_export_obj) + +bpy.ops.SCRIPT_OT_export_obj() + +bpy.ops.remove(bpy.ops.SCRIPT_OT_export_obj) diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 999f6d8e9cb..d6e3b0c3dd5 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -35,6 +35,8 @@ #include "BPY_extern.h" +static void bpy_init_syspath(); + void BPY_free_compiled_text( struct Text *text ) { if( text->compiled ) { @@ -126,14 +128,14 @@ void BPY_start_python( int argc, char **argv ) /* Initialize thread support (also acquires lock) */ PyEval_InitThreads(); - /* bpy.* and lets us import it */ bpy_init_modules(); + /* init sys.path */ + bpy_init_syspath(); py_tstate = PyGILState_GetThisThreadState(); PyEval_ReleaseThread(py_tstate); - } void BPY_end_python( void ) @@ -148,6 +150,56 @@ void BPY_end_python( void ) return; } +void syspath_append(char *dirname) +{ + PyObject *mod_sys= NULL, *dict= NULL, *path= NULL, *dir= NULL; + short ok=1; + + mod_sys = PyImport_ImportModule( "sys" ); /* new ref */ + + if (mod_sys) { + dict = PyModule_GetDict( mod_sys ); /* borrowed ref */ + path = PyDict_GetItemString( dict, "path" ); /* borrowed ref */ + if ( !PyList_Check( path ) ) { + ok = 0; + } + } else { + /* cant get the sys module */ + /* PyErr_Clear(); is called below */ + ok = 0; + } + + dir = PyString_FromString( dirname ); + + if (ok && PySequence_Contains(path, dir)==0) { /* Only add if we need to */ + if (PyList_Append( path, dir ) != 0) /* decref below */ + ok = 0; /* append failed */ + } + + if( (ok==0) || PyErr_Occurred( ) ) + fprintf(stderr, "Warning: could import or build sys.path\n" ); + + PyErr_Clear(); + Py_DECREF( dir ); + Py_XDECREF( mod_sys ); +} + +/* Adds bpymodules to sys.path */ +static void bpy_init_syspath() +{ + char *dir; + char mod_dir[FILE_MAX]; + + // make path to [home]/scripts/bpymodules + dir = BLI_gethome_folder("scripts"); + BLI_make_file_string("/", mod_dir, dir, "bpymodules"); + + if (BLI_exists(mod_dir)) { + syspath_append(mod_dir); + fprintf(stderr, "'%s' has been added to sys.path\n", mod_dir); + } +} + /* Can run a file or text block */ int BPY_run_python_script( bContext *C, const char *fn, struct Text *text ) { From 0474acc4cb21ca5603579d8b5d835b7467dc6e5e Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Sat, 6 Jun 2009 16:22:54 +0000 Subject: [PATCH 06/68] Small fix: use PyUnicode_FromString instead of PyString_FromString in Python 3.x builds. PyString_FromString no longer exists in Python 3.x. --- source/blender/python/intern/bpy_interface.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index d6e3b0c3dd5..147358776bc 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -168,8 +168,12 @@ void syspath_append(char *dirname) /* PyErr_Clear(); is called below */ ok = 0; } - + +#if PY_MAJOR_VERSION >= 3 + dir = PyUnicode_FromString( dirname ); +#else dir = PyString_FromString( dirname ); +#endif if (ok && PySequence_Contains(path, dir)==0) { /* Only add if we need to */ if (PyList_Append( path, dir ) != 0) /* decref below */ From cb82ef0d5288aed62fcab2271b4b15ef15a71aaf Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Sat, 6 Jun 2009 17:23:06 +0000 Subject: [PATCH 07/68] Renamed "execu" python operator method back to "exec" - switched to Python 3.0... --- source/blender/python/intern/bpy_operator_wrap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index 215e42dd95d..9c530bd63e1 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -234,7 +234,7 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve PyTuple_SET_ITEM(args, 1, pyop_dict_from_event(event)); } else if (mode==PYOP_EXEC) { - item= PyObject_GetAttrString(py_class, "execu"); + item= PyObject_GetAttrString(py_class, "exec"); args = PyTuple_New(2); RNA_pointer_create(NULL, &RNA_Context, C, &ptr_context); @@ -334,7 +334,7 @@ void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata) /* api callbacks, detailed checks dont on adding */ if (PyObject_HasAttrString(py_class, "invoke")) ot->invoke= PYTHON_OT_invoke; - if (PyObject_HasAttrString(py_class, "execu")) + if (PyObject_HasAttrString(py_class, "exec")) ot->exec= PYTHON_OT_exec; if (PyObject_HasAttrString(py_class, "poll")) ot->poll= PYTHON_OT_poll; @@ -397,7 +397,7 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class) {PYOP_ATTR_UINAME, 's', 0, BPY_CLASS_ATTR_OPTIONAL}, {PYOP_ATTR_PROP, 'l', 0, BPY_CLASS_ATTR_OPTIONAL}, {PYOP_ATTR_DESCRIPTION, 's', 0, BPY_CLASS_ATTR_NONE_OK}, - {"execu", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, + {"exec", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, {"invoke", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, {"poll", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, {NULL, 0, 0, 0} From b429a65ed6f1d53e74bd6cc913a32092a1d191a9 Mon Sep 17 00:00:00 2001 From: Chingiz Dyussenov Date: Tue, 9 Jun 2009 14:53:19 +0000 Subject: [PATCH 08/68] Added two RNA struct functions - merely wrappers around the C api. - add_mesh to Main - calls C add_mesh and returns a new mesh - copy to Mesh - calls C copy_mesh and returns a new copy Not sure about function placement and naming though. Put both functions in editmesh.c, mesh editor module. Added prototypes to rna_internal.h. Prefixed both with "RNA_api_". Wanted to code Mesh.copy so that it copies Mesh data from another object instead of creating a new Mesh, but this needs CustomData manipulations which I should study later. Maybe we need a separate file for API functions? e.g. mesh_api.c? --- source/blender/editors/mesh/editmesh.c | 48 +++++++++++++++++++ source/blender/makesrna/intern/rna_internal.h | 2 + source/blender/makesrna/intern/rna_main.c | 9 ++++ source/blender/makesrna/intern/rna_mesh.c | 6 +++ 4 files changed, 65 insertions(+) diff --git a/source/blender/editors/mesh/editmesh.c b/source/blender/editors/mesh/editmesh.c index 40373c9f327..96348972f4f 100644 --- a/source/blender/editors/mesh/editmesh.c +++ b/source/blender/editors/mesh/editmesh.c @@ -2027,3 +2027,51 @@ void em_setup_viewcontext(bContext *C, ViewContext *vc) vc->em= me->edit_mesh; } } + +/* Python API */ +Mesh *RNA_api_add_mesh(Main *main, char *name) +{ + return add_mesh(name); +} + +Mesh *RNA_api_mesh_copy(Mesh *me) +{ + return copy_mesh(me); +} + +/* +void RNA_api_mesh_copy_(Mesh *me, Object *ob, int apply_transform) +{ + if (ob->type != OB_MESH) { + return; + } + + Mesh *src= (Mesh*)ob->data; + + CustomData_free(&me->vdata, me->totvert); + CustomData_free(&me->edata, me->totedge); + CustomData_free(&me->fdata, me->totface); + + CustomData_copy(&src->vdata, &me->vdata, CD_MASK_MESH, CD_DUPLICATE, me->totvert); + CustomData_copy(&src->edata, &me->edata, CD_MASK_MESH, CD_DUPLICATE, me->totedge); + CustomData_copy(&src->fdata, &me->fdata, CD_MASK_MESH, CD_DUPLICATE, me->totface); + mesh_update_customdata_pointers(me); + + // ensure indirect linked data becomes lib-extern + for(i=0; ifdata.totlayer; i++) { + if(src->fdata.layers[i].type == CD_MTFACE) { + tface= (MTFace*)src->fdata.layers[i].data; + + for(a=0; atotface; a++, tface++) + if(tface->tpage) + id_lib_extern((ID*)tface->tpage); + } + } + + me->mselect= NULL; + me->bb= src->bb; + + //men->key= copy_key(me->key); + //if(men->key) men->key->from= (ID *)men; +} +*/ diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 9071efe71f7..ee08465f544 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -189,6 +189,8 @@ void rna_object_vcollayer_name_set(struct PointerRNA *ptr, const char *value, ch /* API functions */ void RNA_api_ui_layout(struct StructRNA *srna); +struct Mesh *RNA_api_add_mesh(struct Main *main, char *name); +struct Mesh *RNA_api_mesh_copy(struct Mesh *me); /* ID Properties */ diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index fdd0349b25e..347aef69a76 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -218,6 +218,8 @@ void RNA_def_main(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; + FunctionRNA *func; + const char *lists[][5]= { {"cameras", "Camera", "rna_Main_camera_begin", "Cameras", "Camera datablocks."}, {"scenes", "Scene", "rna_Main_scene_begin", "Scenes", "Scene datablocks."}, @@ -265,6 +267,13 @@ void RNA_def_main(BlenderRNA *brna) RNA_def_property_collection_funcs(prop, lists[i][2], "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", 0, 0, 0); RNA_def_property_ui_text(prop, lists[i][3], lists[i][4]); } + + func= RNA_def_function(srna, "add_mesh", "RNA_api_add_mesh"); + RNA_def_function_ui_description(func, "Add a new mesh."); + prop= RNA_def_string(func, "name", "", 0, "", "New name for the datablock."); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop= RNA_def_pointer(func, "mesh", "Mesh", "", "A new mesh."); + RNA_def_function_return(func, prop); } #endif diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 92e53cf7606..a15e36f947f 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1044,6 +1044,7 @@ static void rna_def_mesh(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; + FunctionRNA *func; srna= RNA_def_struct(brna, "Mesh", "ID"); RNA_def_struct_ui_text(srna, "Mesh", "Mesh datablock to define geometric surfaces."); @@ -1126,6 +1127,11 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Shape Keys", ""); rna_def_texmat_common(srna, "rna_Mesh_texspace_editable"); + + func= RNA_def_function(srna, "copy", "RNA_api_mesh_copy"); + RNA_def_function_ui_description(func, "Create a copy of this mesh."); + prop= RNA_def_pointer(func, "mesh", "Mesh", "", "A new mesh."); + RNA_def_function_return(func, prop); } void RNA_def_mesh(BlenderRNA *brna) From ad503a32d60459d627b74479064b0536e0a73ef2 Mon Sep 17 00:00:00 2001 From: Chingiz Dyussenov Date: Wed, 10 Jun 2009 09:56:22 +0000 Subject: [PATCH 09/68] - added copy_mesh_data C function which, unlike copy_mesh, copies data between two existing meshes. - API's Mesh.copy reflects copy_mesh_data. --- source/blender/editors/mesh/editmesh.c | 51 ++++++++++++++++++- source/blender/makesrna/intern/rna_internal.h | 2 +- source/blender/makesrna/intern/rna_mesh.c | 6 ++- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/mesh/editmesh.c b/source/blender/editors/mesh/editmesh.c index 96348972f4f..a1b6bc56742 100644 --- a/source/blender/editors/mesh/editmesh.c +++ b/source/blender/editors/mesh/editmesh.c @@ -2029,16 +2029,63 @@ void em_setup_viewcontext(bContext *C, ViewContext *vc) } /* Python API */ +void copy_mesh_data(Mesh *dest, Mesh *src); + Mesh *RNA_api_add_mesh(Main *main, char *name) { return add_mesh(name); } -Mesh *RNA_api_mesh_copy(Mesh *me) +void RNA_api_mesh_copy(Mesh *me, Mesh *from) { - return copy_mesh(me); + copy_mesh_data(me, from); } +void RNA_api_mesh_copy_transformed() +{ +} + +/* + * This version of copy_mesh doesn't allocate a new mesh, + * instead it copies data between two existing meshes. + */ +void copy_mesh_data(Mesh *dest, Mesh *src) +{ + int totvert, totedge, totface; + int has_layer; + + CustomData_free(&dest->vdata, dest->totvert); + CustomData_free(&dest->edata, dest->totedge); + CustomData_free(&dest->fdata, dest->totface); + + memset(&dest->vdata, 0, sizeof(dest->vdata)); + memset(&dest->edata, 0, sizeof(dest->edata)); + memset(&dest->fdata, 0, sizeof(dest->fdata)); + + totvert = dest->totvert = src->totvert; + totedge = dest->totedge = src->totedge; + totface = dest->totface = src->totface; + + CustomData_copy(&src->vdata, &dest->vdata, CD_MASK_MESH, CD_DUPLICATE, totvert); + CustomData_copy(&src->edata, &dest->edata, CD_MASK_MESH, CD_DUPLICATE, totedge); + CustomData_copy(&src->fdata, &dest->fdata, CD_MASK_MESH, CD_DUPLICATE, totface); + + CustomData_has_layer(&dest->vdata, CD_MVERT); + + CustomData_add_layer(&dest->vdata, CD_MVERT, CD_ASSIGN, src->mvert, totvert); + CustomData_add_layer(&dest->edata, CD_MEDGE, CD_ASSIGN, src->medge, totedge); + CustomData_add_layer(&dest->fdata, CD_MFACE, CD_ASSIGN, src->mface, totface); + + mesh_update_customdata_pointers(dest); +} + +/* +void RNA_api_mesh_apply_transform(Mesh *me) +{ + +} +*/ + /* void RNA_api_mesh_copy_(Mesh *me, Object *ob, int apply_transform) { diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index ee08465f544..d93c296ca93 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -190,7 +190,7 @@ void rna_object_vcollayer_name_set(struct PointerRNA *ptr, const char *value, ch void RNA_api_ui_layout(struct StructRNA *srna); struct Mesh *RNA_api_add_mesh(struct Main *main, char *name); -struct Mesh *RNA_api_mesh_copy(struct Mesh *me); +void RNA_api_mesh_copy(struct Mesh *me, struct Mesh *from); /* ID Properties */ diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index a15e36f947f..b330bf277d1 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1129,9 +1129,13 @@ static void rna_def_mesh(BlenderRNA *brna) rna_def_texmat_common(srna, "rna_Mesh_texspace_editable"); func= RNA_def_function(srna, "copy", "RNA_api_mesh_copy"); - RNA_def_function_ui_description(func, "Create a copy of this mesh."); + RNA_def_function_ui_description(func, "Copy mesh data."); + prop= RNA_def_pointer(func, "src", "Mesh", "", "A mesh to copy data from."); + RNA_def_property_flag(prop, PROP_REQUIRED); + /* prop= RNA_def_pointer(func, "mesh", "Mesh", "", "A new mesh."); RNA_def_function_return(func, prop); + */ } void RNA_def_mesh(BlenderRNA *brna) From aaa17a285dc46adf354678bf14e9c7043a9f645a Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 11 Jun 2009 11:46:12 +0000 Subject: [PATCH 10/68] Allow defining string property on py operator - a first step towards interfacing the file selector :) --- source/blender/editors/mesh/editmesh.c | 2 +- source/blender/makesrna/intern/rna_object.c | 6 +++++ source/blender/python/intern/bpy_interface.c | 1 + source/blender/python/intern/bpy_rna.c | 28 ++++++++++++++++++++ source/blender/python/intern/bpy_rna.h | 1 + 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/mesh/editmesh.c b/source/blender/editors/mesh/editmesh.c index a1b6bc56742..bce5c202366 100644 --- a/source/blender/editors/mesh/editmesh.c +++ b/source/blender/editors/mesh/editmesh.c @@ -2041,7 +2041,7 @@ void RNA_api_mesh_copy(Mesh *me, Mesh *from) copy_mesh_data(me, from); } -void RNA_api_mesh_copy_transformed() +void RNA_api_mesh_transform(Mesh *me, float **mat) { } diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 08eca7b0528..86cf501295c 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -799,6 +799,12 @@ static StructRNA *rna_def_object(BlenderRNA *brna) RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Lock Scale", "Lock editing of scale in the interface."); + /* + // error on compile + prop= RNA_def_float_matrix(srna, "mat", 16, NULL, 0.0f, 0.0f, "Matrix", "Transform matrix of the object.", 0.0f, 0.0f); + RNA_def_property_float_sdna(prop, NULL, "obmat"); + */ + /* collections */ prop= RNA_def_property(srna, "constraints", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "Constraint"); diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 49cc4d678e5..4903af475ed 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -102,6 +102,7 @@ static PyObject *CreateGlobalDictionary( bContext *C ) {"FloatProperty", (PyCFunction)BPy_FloatProperty, METH_VARARGS|METH_KEYWORDS, ""}, {"IntProperty", (PyCFunction)BPy_IntProperty, METH_VARARGS|METH_KEYWORDS, ""}, {"BoolProperty", (PyCFunction)BPy_BoolProperty, METH_VARARGS|METH_KEYWORDS, ""}, + {"StringProperty", (PyCFunction)BPy_StringProperty, METH_VARARGS|METH_KEYWORDS, ""}, {NULL, NULL, 0, NULL} }; diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 12c19bd3471..3ef3c878826 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -38,6 +38,7 @@ #include "BKE_context.h" #include "BKE_global.h" /* evil G.* */ #include "BKE_report.h" +#include "BKE_utildefines.h" /* FILE_MAX */ static int pyrna_struct_compare( BPy_StructRNA * a, BPy_StructRNA * b ) { @@ -1803,6 +1804,33 @@ PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) } } +PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw) +{ + static char *kwlist[] = {"attr", "name", "description", "maxlen", "default", NULL}; + char *id, *name="", *description="", *def=""; + int maxlen=FILE_MAX; // XXX need greater? + + if (!PyArg_ParseTupleAndKeywords(args, kw, "s|ssis:StringProperty", kwlist, &id, &name, &description, &maxlen, &def)) + return NULL; + + if (PyTuple_Size(args) > 0) { + PyErr_SetString(PyExc_ValueError, "all args must be keywors"); // TODO - py3 can enforce this. + return NULL; + } + + if (self) { + StructRNA *srna = PyCObject_AsVoidPtr(self); + RNA_def_string(srna, id, def, maxlen, name, description); + Py_RETURN_NONE; + } else { + PyObject *ret = PyTuple_New(2); + PyTuple_SET_ITEM(ret, 0, PyCObject_FromVoidPtr((void *)BPy_StringProperty, NULL)); + PyTuple_SET_ITEM(ret, 1, kw); + Py_INCREF(kw); + return ret; + } +} + /*-------------------- Type Registration ------------------------*/ static int rna_function_arg_count(FunctionRNA *func) diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index a2a3015912b..7789e083a4e 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -76,6 +76,7 @@ PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop); PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw); PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw); PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw); +PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw); /* function for registering types */ PyObject *pyrna_basetype_register(PyObject *self, PyObject *args); From a892799db7a9019b592d742b1782c35fa53be01f Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 11 Jun 2009 15:10:23 +0000 Subject: [PATCH 11/68] Pass context to the "invoke" operator method. "invoke" should have three args now: self, context and event respectively. --- .../blender/python/intern/bpy_operator_wrap.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index 6ab990acdf5..a8f993f512b 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -190,7 +190,6 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve PyObject *ret= NULL, *py_class_instance, *item= NULL; int ret_flag= (mode==PYOP_POLL ? 0:OPERATOR_CANCELLED); PointerRNA ptr_context; - PyObject *py_context; PyGILState_STATE gilstate = PyGILState_Ensure(); @@ -226,20 +225,23 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve RNA_property_collection_end(&iter); } - + + RNA_pointer_create(NULL, &RNA_Context, C, &ptr_context); if (mode==PYOP_INVOKE) { item= PyObject_GetAttrString(py_class, "invoke"); - args = PyTuple_New(2); - PyTuple_SET_ITEM(args, 1, pyop_dict_from_event(event)); + args = PyTuple_New(3); + + // PyTuple_SET_ITEM "steals" object reference, it is + // an object passed shouldn't be DECREF'ed + PyTuple_SET_ITEM(args, 1, pyrna_struct_CreatePyObject(&ptr_context)); + PyTuple_SET_ITEM(args, 2, pyop_dict_from_event(event)); } else if (mode==PYOP_EXEC) { item= PyObject_GetAttrString(py_class, "exec"); args = PyTuple_New(2); - RNA_pointer_create(NULL, &RNA_Context, C, &ptr_context); - py_context = pyrna_struct_CreatePyObject(&ptr_context); - PyTuple_SET_ITEM(args, 1, py_context); + PyTuple_SET_ITEM(args, 1, pyrna_struct_CreatePyObject(&ptr_context)); } else if (mode==PYOP_POLL) { item= PyObject_GetAttrString(py_class, "poll"); @@ -398,7 +400,7 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class) {PYOP_ATTR_PROP, 'l', 0, BPY_CLASS_ATTR_OPTIONAL}, {PYOP_ATTR_DESCRIPTION, 's', 0, BPY_CLASS_ATTR_NONE_OK}, {"exec", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, - {"invoke", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, + {"invoke", 'f', 3, BPY_CLASS_ATTR_OPTIONAL}, {"poll", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, {NULL, 0, 0, 0} }; From 6563e9ff8a6acd7f63038d8a9bbe727d312a2b29 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 11 Jun 2009 18:15:04 +0000 Subject: [PATCH 12/68] Started operator registration with python. This is a piece of cake! Made space_script header area customizable with python - need Brecht to check this. Added a base for operator registration: a menu in Scripts Window with two items "Reload Scripts" and "Export". The former will do guess what? The latter will be populated with submenu items each corresponding to an exporter :) --- release/ui/space_script.py | 73 +++++++++++++++++++ .../editors/space_script/space_script.c | 31 ++++---- 2 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 release/ui/space_script.py diff --git a/release/ui/space_script.py b/release/ui/space_script.py new file mode 100644 index 00000000000..ea6f4be311c --- /dev/null +++ b/release/ui/space_script.py @@ -0,0 +1,73 @@ +import bpy + +class SCRIPT_HT_header(bpy.types.Header): + __space_type__ = "SCRIPTS_WINDOW" + __idname__ = "SCRIPT_HT_header" + + def draw(self, context): + st = context.space_data + layout = self.layout + + layout.template_header(context) + + if context.area.show_menus: + row = layout.row(align=True) + row.itemM(context, "SCRIPT_MT_scripts") + + # draw menu item to reload scripts from + # release/io + # + # it should call operator or + # a func that will: + # for each .py file in the dir, + # import/reload module, in the module: + # find subclasses of bpy.types.Operator, + # for each subclass create menus under "Export" + # with (row.)itemO + # + # for interface api documentation, see + # see source/blender/editors/interface/interface_api.c + # + # hint: reloading ui scripts in scripts window is Shift+P + + +class SCRIPT_MT_scripts(bpy.types.Menu): + __space_type__ = "SCRIPTS_WINDOW" + __label__ = "Scripts" + + def draw(self, context): + layout = self.layout + layout.column() + layout.itemM(context, "SCRIPT_MT_export") + layout.itemO("SCRIPT_OT_reload_scripts") + +class SCRIPT_MT_export(bpy.types.Menu): + __space_type__ = "SCRIPTS_WINDOW" + __label__ = "Export" + + def draw(self, context): + pass + +class SCRIPT_OT_reload_scripts(bpy.types.Operator): + __label__ = 'Reload Scripts' + + def exec(self, context): + print("SCRIPT_OT_reload_scripts: exec") + return 'FINISHED' + + def invoke(self, context, event): + print("SCRIPT_OT_reload_scripts: invoke") + return self.exec(context) + + def poll(self, context): + pass + + +bpy.types.register(SCRIPT_HT_header) +bpy.types.register(SCRIPT_MT_scripts) +bpy.types.register(SCRIPT_MT_export) + +if (hasattr(bpy.ops, "SCRIPT_OT_reload_scripts")): + bpy.ops.remove(bpy.ops.SCRIPT_OT_reload_scripts) + +bpy.ops.add(SCRIPT_OT_reload_scripts) diff --git a/source/blender/editors/space_script/space_script.c b/source/blender/editors/space_script/space_script.c index 4c17ed16475..01f19b3a0ab 100644 --- a/source/blender/editors/space_script/space_script.c +++ b/source/blender/editors/space_script/space_script.c @@ -175,29 +175,32 @@ static void script_main_area_draw(const bContext *C, ARegion *ar) /* add handlers, stuff you only do once or on area/region changes */ static void script_header_area_init(wmWindowManager *wm, ARegion *ar) { - UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_HEADER, ar->winx, ar->winy); + /* UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_HEADER, ar->winx, ar->winy); */ + ED_region_header_init(ar); } static void script_header_area_draw(const bContext *C, ARegion *ar) { - float col[3]; + /* float col[3]; */ - /* clear */ - if(ED_screen_area_active(C)) - UI_GetThemeColor3fv(TH_HEADER, col); - else - UI_GetThemeColor3fv(TH_HEADERDESEL, col); + /* /\* clear *\/ */ + /* if(ED_screen_area_active(C)) */ + /* UI_GetThemeColor3fv(TH_HEADER, col); */ + /* else */ + /* UI_GetThemeColor3fv(TH_HEADERDESEL, col); */ - glClearColor(col[0], col[1], col[2], 0.0); - glClear(GL_COLOR_BUFFER_BIT); + /* glClearColor(col[0], col[1], col[2], 0.0); */ + /* glClear(GL_COLOR_BUFFER_BIT); */ - /* set view2d view matrix for scrolling (without scrollers) */ - UI_view2d_view_ortho(C, &ar->v2d); + /* /\* set view2d view matrix for scrolling (without scrollers) *\/ */ + /* UI_view2d_view_ortho(C, &ar->v2d); */ - script_header_buttons(C, ar); + /* script_header_buttons(C, ar); */ - /* restore view matrix? */ - UI_view2d_view_restore(C); + /* /\* restore view matrix? *\/ */ + /* UI_view2d_view_restore(C); */ + + ED_region_header(C, ar); } static void script_main_area_listener(ARegion *ar, wmNotifier *wmn) From 23eecb1e6e1824b8f6b13448a7eb164e6c73bc0f Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Fri, 12 Jun 2009 09:54:28 +0000 Subject: [PATCH 13/68] Import/export operator registration working. Written in python, it traverses scripts in .blender/io extracting and registering Operator subclasses, and binding to menu items under Scripts->Export in Scripts Window. release/io dir has to be copied to .blender manually for now. --- release/io/export_obj.py | 28 +++++++++++ release/ui/space_script.py | 96 ++++++++++++++++++++++++++++++-------- 2 files changed, 105 insertions(+), 19 deletions(-) create mode 100644 release/io/export_obj.py diff --git a/release/io/export_obj.py b/release/io/export_obj.py new file mode 100644 index 00000000000..37039bb11cb --- /dev/null +++ b/release/io/export_obj.py @@ -0,0 +1,28 @@ +import bpy + +class SCRIPT_OT_export_obj(bpy.types.Operator): + '''Operator documentation text, will be used for the operator tooltip and python docs.''' + __label__ = 'Export OBJ' + + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + __props__ = [ +# FloatProperty(attr="setting_1", name="Example 1", default=10.0, min=0, max=10, description="Add info here"), +# StringProperty(attr="filename") + ] + + def debug(self, message): + print("{0}: {1}".format(self.__class__.__name__, message)) + + def exec(self, context): +# print(self.setting_1) + self.debug("exec") + return 'FINISHED' + + def invoke(self, context, event): + self.debug("invoke") + return self.exec(context) + + def poll(self, context): # poll isnt working yet + self.debug("poll") + return True diff --git a/release/ui/space_script.py b/release/ui/space_script.py index ea6f4be311c..0ab091e8fd5 100644 --- a/release/ui/space_script.py +++ b/release/ui/space_script.py @@ -1,5 +1,25 @@ +import sys +import os +import imp +# import glob import bpy +operators = [] + +def register_op(opclass): + if (hasattr(bpy.ops, opclass.__name__)): + bpy.ops.remove(getattr(bpy.ops, opclass.__name__)) + + bpy.ops.add(opclass) + + global operators + if opclass.__name__ not in operators: + operators.append(opclass.__name__) + + +# hint for myself: for interface api documentation, see source/blender/editors/interface/interface_api.c +# another hint: reloading ui scripts in scripts window is Shift + P + class SCRIPT_HT_header(bpy.types.Header): __space_type__ = "SCRIPTS_WINDOW" __idname__ = "SCRIPT_HT_header" @@ -14,23 +34,6 @@ class SCRIPT_HT_header(bpy.types.Header): row = layout.row(align=True) row.itemM(context, "SCRIPT_MT_scripts") - # draw menu item to reload scripts from - # release/io - # - # it should call operator or - # a func that will: - # for each .py file in the dir, - # import/reload module, in the module: - # find subclasses of bpy.types.Operator, - # for each subclass create menus under "Export" - # with (row.)itemO - # - # for interface api documentation, see - # see source/blender/editors/interface/interface_api.c - # - # hint: reloading ui scripts in scripts window is Shift+P - - class SCRIPT_MT_scripts(bpy.types.Menu): __space_type__ = "SCRIPTS_WINDOW" __label__ = "Scripts" @@ -46,13 +49,69 @@ class SCRIPT_MT_export(bpy.types.Menu): __label__ = "Export" def draw(self, context): - pass + global operators + + print("drawing {0} operators: {1}".format(len(operators), operators)) + + layout = self.layout + layout.column() + for opname in operators: + layout.itemO(opname) class SCRIPT_OT_reload_scripts(bpy.types.Operator): __label__ = 'Reload Scripts' def exec(self, context): print("SCRIPT_OT_reload_scripts: exec") + + # add ../io to sys.path + + # this module's absolute path + abspath = os.path.abspath(sys.modules[__name__].__file__) + print("Current abspath: {0}".format(abspath)) + + # ../io + io = os.path.normpath(os.path.dirname(abspath) + "/../io") + print("abspath = " + io) + + if io not in sys.path: + sys.path.append(io) + + # for each .py file in release/io, + # import/reload module, in the module: + # find subclasses of bpy.types.Operator, + # for each subclass create menus under "Export" + # with (row.)itemO + + global operators + operators = [] + + # glob unavailable :( +# for path in glob.glob("../io/*.py"): + for path in os.listdir(io): + modname, ext = os.path.splitext(os.path.basename(path)) + + if ext != ".py": + continue + + print("Found module {0}.".format(modname)) + + if modname in sys.modules: + mod = imp.reload(sys.modules[modname]) + print("Reloaded it.") + else: + mod = __import__(modname) + print("Imported it.") + + for attr in dir(mod): + cls = getattr(mod, attr) + + # XXX is there a better way to check that cls is a class? + if type(cls) == bpy.types.Operator.__class__ and issubclass(cls, bpy.types.Operator): + print("Found class {0}.".format(cls.__name__)) + register_op(cls) + print("Registered it.") + return 'FINISHED' def invoke(self, context, event): @@ -62,7 +121,6 @@ class SCRIPT_OT_reload_scripts(bpy.types.Operator): def poll(self, context): pass - bpy.types.register(SCRIPT_HT_header) bpy.types.register(SCRIPT_MT_scripts) bpy.types.register(SCRIPT_MT_export) From 93d21f36b3450803958ab0a5686ad0a16fa367cd Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Sun, 14 Jun 2009 16:48:19 +0000 Subject: [PATCH 14/68] Make release/io dir installable on scons build. --- SConstruct | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/SConstruct b/SConstruct index dcea0f511eb..808fa09bea0 100644 --- a/SConstruct +++ b/SConstruct @@ -468,27 +468,17 @@ if env['OURPLATFORM']!='darwin': dotblenderinstall.append(env.Install(dir=td, source=srcfile)) if env['WITH_BF_PYTHON']: - #-- .blender/scripts - scriptpath='release/scripts' - for dp, dn, df in os.walk(scriptpath): - if 'CVS' in dn: - dn.remove('CVS') - if '.svn' in dn: - dn.remove('.svn') - dir=env['BF_INSTALLDIR']+'/.blender/scripts'+dp[len(scriptpath):] - source=[dp+os.sep+f for f in df] - scriptinstall.append(env.Install(dir=dir,source=source)) - - #-- .blender/ui - scriptpath='release/ui' - for dp, dn, df in os.walk(scriptpath): - if 'CVS' in dn: - dn.remove('CVS') - if '.svn' in dn: - dn.remove('.svn') - dir=env['BF_INSTALLDIR']+'/.blender/ui'+dp[len(scriptpath):] - source=[dp+os.sep+f for f in df] - scriptinstall.append(env.Install(dir=dir,source=source)) + #-- .blender/scripts, .blender/ui, .blender/io + scriptpaths=['release/scripts', 'release/ui', 'release/io'] + for scriptpath in scriptpaths: + for dp, dn, df in os.walk(scriptpath): + if 'CVS' in dn: + dn.remove('CVS') + if '.svn' in dn: + dn.remove('.svn') + dir=env['BF_INSTALLDIR']+'/.blender/'+os.path.basename(scriptpath)+dp[len(scriptpath):] + source=[dp+os.sep+f for f in df] + scriptinstall.append(env.Install(dir=dir,source=source)) #-- icons if env['OURPLATFORM']=='linux2': From fd893819bcc3d4952e785fcf903ed9bc10d7830e Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Mon, 15 Jun 2009 07:47:09 +0000 Subject: [PATCH 15/68] Fix bug in BPY_flag_from_seq: reset the flag before adjusting it. Otherwise py operators ended up returning something like OPERATOR_CANCELLED | OPERATOR_FINISHED. --- source/blender/python/intern/bpy_util.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/python/intern/bpy_util.c b/source/blender/python/intern/bpy_util.c index c447e7de982..8dd81834213 100644 --- a/source/blender/python/intern/bpy_util.c +++ b/source/blender/python/intern/bpy_util.c @@ -81,6 +81,7 @@ int BPY_flag_from_seq(BPY_flag_def *flagdef, PyObject *seq, int *flag) char *cstring; PyObject *item; BPY_flag_def *fd; + *flag = 0; if (PySequence_Check(seq)) { i= PySequence_Length(seq); From c7cdb9cc38aa1cbe0772041a21f0bb1640b06220 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Mon, 15 Jun 2009 08:12:28 +0000 Subject: [PATCH 16/68] File selector working. For now it is accessed as context.add_fileselect(self.__operator__). To allow file selector, I made the following changes: - moved property definition funcs (FloatProperty, etc.) to "bpy.props" to make them accessible from io scripts. Previously they were only accessible in scripts running from Text Editor. - added the "__operator__" instance attribute to py operators. The value is RNA operator pointer. Note that "context.add_fileselect" changes were mistakenly committed with my last merge. --- release/io/export_obj.py | 8 ++++++-- source/blender/python/intern/bpy_interface.c | 17 +---------------- .../blender/python/intern/bpy_operator_wrap.c | 8 ++++++++ source/blender/python/intern/bpy_rna.c | 17 +++++++++++++++++ 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/release/io/export_obj.py b/release/io/export_obj.py index 37039bb11cb..663ec6c0478 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -1,3 +1,4 @@ + import bpy class SCRIPT_OT_export_obj(bpy.types.Operator): @@ -7,6 +8,7 @@ class SCRIPT_OT_export_obj(bpy.types.Operator): # List of operator properties, the attributes will be assigned # to the class instance from the operator settings before calling. __props__ = [ + bpy.props["StringProperty"](attr="filename", name="filename", default="/tmp") # FloatProperty(attr="setting_1", name="Example 1", default=10.0, min=0, max=10, description="Add info here"), # StringProperty(attr="filename") ] @@ -17,11 +19,13 @@ class SCRIPT_OT_export_obj(bpy.types.Operator): def exec(self, context): # print(self.setting_1) self.debug("exec") - return 'FINISHED' + self.debug("filename = " + self.filename) + return ('FINISHED',) def invoke(self, context, event): self.debug("invoke") - return self.exec(context) + context.add_fileselect(self.__operator__) + return ('RUNNING_MODAL',) #self.exec(context) def poll(self, context): # poll isnt working yet self.debug("poll") diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 4903af475ed..5c33c63028a 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -60,6 +60,7 @@ static void bpy_init_modules( void ) PyModule_AddObject( mod, "types", BPY_rna_types() ); PyModule_AddObject( mod, "ops", BPY_operator_module() ); PyModule_AddObject( mod, "ui", BPY_ui_module() ); // XXX very experemental, consider this a test, especially PyCObject is not meant to be perminant + PyModule_AddObject( mod, "props", BPY_rna_props() ); /* add the module so we can import it */ PyDict_SetItemString(PySys_GetObject("modules"), "bpy", mod); @@ -95,22 +96,6 @@ static PyObject *CreateGlobalDictionary( bContext *C ) // XXX - evil, need to access context BPy_SetContext(C); - // XXX - put somewhere more logical - { - PyMethodDef *ml; - static PyMethodDef bpy_prop_meths[] = { - {"FloatProperty", (PyCFunction)BPy_FloatProperty, METH_VARARGS|METH_KEYWORDS, ""}, - {"IntProperty", (PyCFunction)BPy_IntProperty, METH_VARARGS|METH_KEYWORDS, ""}, - {"BoolProperty", (PyCFunction)BPy_BoolProperty, METH_VARARGS|METH_KEYWORDS, ""}, - {"StringProperty", (PyCFunction)BPy_StringProperty, METH_VARARGS|METH_KEYWORDS, ""}, - {NULL, NULL, 0, NULL} - }; - - for(ml = bpy_prop_meths; ml->ml_name; ml++) { - PyDict_SetItemString( dict, ml->ml_name, PyCFunction_New(ml, NULL)); - } - } - /* add bpy to global namespace */ mod= PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0); PyDict_SetItemString( dict, "bpy", mod ); diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index d951d40b9db..33715a97a43 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -225,6 +225,8 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve PyObject *ret= NULL, *py_class_instance, *item= NULL; int ret_flag= (mode==PYOP_POLL ? 0:OPERATOR_CANCELLED); PointerRNA ptr_context; + PyObject ptr_operator; + PyObject *py_operator; PyGILState_STATE gilstate = PyGILState_Ensure(); @@ -261,6 +263,12 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve RNA_property_collection_end(&iter); } + /* set operator pointer RNA as instance "__operator__" attribute */ + RNA_pointer_create(NULL, &RNA_Operator, op, &ptr_operator); + py_operator= pyrna_struct_CreatePyObject(&ptr_operator); + PyObject_SetAttrString(py_class_instance, "__operator__", py_operator); + Py_DECREF(py_operator); + RNA_pointer_create(NULL, &RNA_Context, C, &ptr_context); if (mode==PYOP_INVOKE) { diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 09e2ab15c56..d1c06802f0a 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -1750,7 +1750,24 @@ PyObject *BPY_rna_types(void) return (PyObject *)self; } +PyObject *BPY_rna_props( void ) +{ + PyObject *dict = PyDict_New( ); + PyMethodDef *ml; + static PyMethodDef bpy_prop_meths[] = { + {"FloatProperty", (PyCFunction)BPy_FloatProperty, METH_VARARGS|METH_KEYWORDS, ""}, + {"IntProperty", (PyCFunction)BPy_IntProperty, METH_VARARGS|METH_KEYWORDS, ""}, + {"BoolProperty", (PyCFunction)BPy_BoolProperty, METH_VARARGS|METH_KEYWORDS, ""}, + {"StringProperty", (PyCFunction)BPy_StringProperty, METH_VARARGS|METH_KEYWORDS, ""}, + {NULL, NULL, 0, NULL} + }; + + for(ml = bpy_prop_meths; ml->ml_name; ml++) { + PyDict_SetItemString( dict, ml->ml_name, PyCFunction_New(ml, NULL)); + } + return dict; +} /* Orphan functions, not sure where they should go */ From 07e1b84ca8f2ad83b148df03b697a0bcfdb04e3a Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Mon, 15 Jun 2009 09:26:31 +0000 Subject: [PATCH 17/68] Another fix in BPY_flag_from_seq: unrecognized flag is also an error. Print operator return value for debugging. --- release/ui/space_script.py | 2 +- .../blender/python/intern/bpy_operator_wrap.c | 26 ++++++++++++++++++- source/blender/python/intern/bpy_util.c | 3 +++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/release/ui/space_script.py b/release/ui/space_script.py index 0ab091e8fd5..d2c73adb00f 100644 --- a/release/ui/space_script.py +++ b/release/ui/space_script.py @@ -112,7 +112,7 @@ class SCRIPT_OT_reload_scripts(bpy.types.Operator): register_op(cls) print("Registered it.") - return 'FINISHED' + return ('FINISHED',) def invoke(self, context, event): print("SCRIPT_OT_reload_scripts: invoke") diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index 33715a97a43..4624fe764b3 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -316,7 +316,8 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve } else if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) { /* the returned value could not be converted into a flag */ pyop_error_report(op->reports); - + + ret_flag = OPERATOR_CANCELLED; } /* there is no need to copy the py keyword dict modified by * pyot->py_invoke(), back to the operator props since they are just @@ -329,6 +330,29 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve Py_DECREF(ret); } + /* print operator return value */ + if (mode != PYOP_POLL) { + char flag_str[100]; + char class_name[100]; + BPY_flag_def *flag_def = pyop_ret_flags; + + strcpy(flag_str, ""); + + while(flag_def->name) { + if (ret_flag & flag_def->flag) { + flag_str[1] ? sprintf(flag_str, "%s | %s", flag_str, flag_def->name) : strcpy(flag_str, flag_def->name); + } + flag_def++; + } + + /* get class name */ + item= PyObject_GetAttrString(py_class, PYOP_ATTR_IDNAME); + Py_DECREF(item); + strcpy(class_name, _PyUnicode_AsString(item)); + + fprintf(stderr, "%s's %s returned %s\n", class_name, mode == PYOP_EXEC ? "exec" : "invoke", flag_str); + } + PyGILState_Release(gilstate); return ret_flag; diff --git a/source/blender/python/intern/bpy_util.c b/source/blender/python/intern/bpy_util.c index 8dd81834213..2c7626fea7f 100644 --- a/source/blender/python/intern/bpy_util.c +++ b/source/blender/python/intern/bpy_util.c @@ -109,6 +109,9 @@ int BPY_flag_from_seq(BPY_flag_def *flagdef, PyObject *seq, int *flag) error_val= 1; } + if (*flag == 0) + error_val = 1; + if (error_val) { char *buf = bpy_flag_error_str(flagdef); PyErr_SetString(PyExc_AttributeError, buf); From 678417b76c228d30417889aa91ad9c2b14cbe4fe Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Mon, 15 Jun 2009 13:49:02 +0000 Subject: [PATCH 18/68] Added copy_applied method on Mesh objects. Uses DerivedMesh funcs to get a mesh with all modifiers applied. --- release/io/export_obj.py | 29 +++++++--- source/blender/editors/mesh/editmesh.c | 56 ++++--------------- source/blender/makesrna/intern/rna_internal.h | 6 ++ source/blender/makesrna/intern/rna_mesh.c | 12 ++-- 4 files changed, 48 insertions(+), 55 deletions(-) diff --git a/release/io/export_obj.py b/release/io/export_obj.py index 663ec6c0478..c5b8038a3d7 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -1,4 +1,3 @@ - import bpy class SCRIPT_OT_export_obj(bpy.types.Operator): @@ -8,24 +7,40 @@ class SCRIPT_OT_export_obj(bpy.types.Operator): # List of operator properties, the attributes will be assigned # to the class instance from the operator settings before calling. __props__ = [ - bpy.props["StringProperty"](attr="filename", name="filename", default="/tmp") -# FloatProperty(attr="setting_1", name="Example 1", default=10.0, min=0, max=10, description="Add info here"), -# StringProperty(attr="filename") + bpy.props["StringProperty"](attr="filename", name="filename") ] def debug(self, message): print("{0}: {1}".format(self.__class__.__name__, message)) def exec(self, context): -# print(self.setting_1) self.debug("exec") self.debug("filename = " + self.filename) + + self.debug("num selected objects: {0}".format(len(context.selected_objects))) + + ob = bpy.data.objects["Cube"] + o = ob.data + + m = bpy.data.add_mesh("tmpmesh") + m.copy_applied(context.scene, ob, True) + + def vert(co): + return "{0}, {1}, {2}".format(co[0], co[1], co[2]) + + print(" orig: {0} with totverts={1}".format(vert(o.verts[0].co), len(o.verts))) + print("applied: {0} with totverts={1}".format(vert(m.verts[0].co), len(m.verts))) + + # XXX errors are silenced for some reason +# raise Exception("oops!") + return ('FINISHED',) def invoke(self, context, event): self.debug("invoke") - context.add_fileselect(self.__operator__) - return ('RUNNING_MODAL',) #self.exec(context) +# context.add_fileselect(self.__operator__) +# return ('RUNNING_MODAL',) + return self.exec(context) def poll(self, context): # poll isnt working yet self.debug("poll") diff --git a/source/blender/editors/mesh/editmesh.c b/source/blender/editors/mesh/editmesh.c index bce5c202366..de4bab57a72 100644 --- a/source/blender/editors/mesh/editmesh.c +++ b/source/blender/editors/mesh/editmesh.c @@ -2041,6 +2041,16 @@ void RNA_api_mesh_copy(Mesh *me, Mesh *from) copy_mesh_data(me, from); } +void RNA_api_mesh_copy_applied(Mesh *me, Scene *sce, Object *ob, int apply_obmat) +{ + DerivedMesh *dm= mesh_create_derived_view(sce, ob, CD_MASK_MESH); + DM_to_mesh(dm, me); + dm->release(dm); + + if (apply_obmat) { + } +} + void RNA_api_mesh_transform(Mesh *me, float **mat) { } @@ -2048,6 +2058,8 @@ void RNA_api_mesh_transform(Mesh *me, float **mat) /* * This version of copy_mesh doesn't allocate a new mesh, * instead it copies data between two existing meshes. + * + * XXX is this already possible with DerivedMesh? */ void copy_mesh_data(Mesh *dest, Mesh *src) { @@ -2078,47 +2090,3 @@ void copy_mesh_data(Mesh *dest, Mesh *src) mesh_update_customdata_pointers(dest); } - -/* -void RNA_api_mesh_apply_transform(Mesh *me) -{ - -} -*/ - -/* -void RNA_api_mesh_copy_(Mesh *me, Object *ob, int apply_transform) -{ - if (ob->type != OB_MESH) { - return; - } - - Mesh *src= (Mesh*)ob->data; - - CustomData_free(&me->vdata, me->totvert); - CustomData_free(&me->edata, me->totedge); - CustomData_free(&me->fdata, me->totface); - - CustomData_copy(&src->vdata, &me->vdata, CD_MASK_MESH, CD_DUPLICATE, me->totvert); - CustomData_copy(&src->edata, &me->edata, CD_MASK_MESH, CD_DUPLICATE, me->totedge); - CustomData_copy(&src->fdata, &me->fdata, CD_MASK_MESH, CD_DUPLICATE, me->totface); - mesh_update_customdata_pointers(me); - - // ensure indirect linked data becomes lib-extern - for(i=0; ifdata.totlayer; i++) { - if(src->fdata.layers[i].type == CD_MTFACE) { - tface= (MTFace*)src->fdata.layers[i].data; - - for(a=0; atotface; a++, tface++) - if(tface->tpage) - id_lib_extern((ID*)tface->tpage); - } - } - - me->mselect= NULL; - me->bb= src->bb; - - //men->key= copy_key(me->key); - //if(men->key) men->key->from= (ID *)men; -} -*/ diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index d93c296ca93..4d3b9dfa327 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -188,9 +188,15 @@ void rna_object_vcollayer_name_set(struct PointerRNA *ptr, const char *value, ch /* API functions */ +struct Main; +struct Scene; +struct Object; +struct Mesh; + void RNA_api_ui_layout(struct StructRNA *srna); struct Mesh *RNA_api_add_mesh(struct Main *main, char *name); void RNA_api_mesh_copy(struct Mesh *me, struct Mesh *from); +void RNA_api_mesh_copy_applied(struct Mesh *me, struct Scene *sce, struct Object *ob, int apply_obmat); /* ID Properties */ diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index a071a79230e..35b8164fae3 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1132,10 +1132,14 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_function_ui_description(func, "Copy mesh data."); prop= RNA_def_pointer(func, "src", "Mesh", "", "A mesh to copy data from."); RNA_def_property_flag(prop, PROP_REQUIRED); - /* - prop= RNA_def_pointer(func, "mesh", "Mesh", "", "A new mesh."); - RNA_def_function_return(func, prop); - */ + + func= RNA_def_function(srna, "copy_applied", "RNA_api_mesh_copy_applied"); + RNA_def_function_ui_description(func, "Copy mesh data from object with all modifiers and transformations applied."); + prop= RNA_def_pointer(func, "sce", "Scene", "", "Scene."); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop= RNA_def_pointer(func, "ob", "Object", "", "Object to copy data from."); + RNA_def_property_flag(prop, PROP_REQUIRED); + RNA_def_boolean(func, "apply_obmat", 1, "", "Apply object matrix."); } void RNA_def_mesh(BlenderRNA *brna) From 556369fe2fcb33ec4a028cc1a9c1ae1d0701e6ba Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Tue, 16 Jun 2009 08:54:38 +0000 Subject: [PATCH 19/68] The basic OBJ exporter working. The code is release/io/export_obj.py. To run it, switch to Scripts Window, click Scripts->Reload Scripts menu, it appears under Scripts->Export. --- release/io/export_obj.py | 54 +++++++++++++------ source/blender/editors/mesh/editmesh.c | 5 +- source/blender/makesrna/intern/rna_internal.h | 2 +- source/blender/makesrna/intern/rna_mesh.c | 3 +- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/release/io/export_obj.py b/release/io/export_obj.py index c5b8038a3d7..df10c3e9089 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -1,7 +1,37 @@ import bpy +def write_obj(filepath, scene, ob): + out = open(filepath, 'w') + + # create a temporary mesh + mesh = bpy.data.add_mesh("tmpmesh") + + # copy data with modifiers applied + mesh.copy_applied(scene, ob) + + # for vert in mesh.verts: + # ^ iterating that way doesn't work atm for some reason + + for i in range(len(mesh.verts)): + vert = mesh.verts[i] + out.write('v {0} {1} {2}\n'.format(vert.co[0], vert.co[1], vert.co[2])) + + for i in range(len(mesh.faces)): + face = mesh.faces[i] + out.write('f') + + # but this works + for index in face.verts: + out.write(' {0}'.format(index + 1)) + out.write('\n') + + # TODO: delete mesh here + + out.close() + class SCRIPT_OT_export_obj(bpy.types.Operator): - '''Operator documentation text, will be used for the operator tooltip and python docs.''' + '''A very basic OBJ exporter, writes only active object's mesh.''' + __label__ = 'Export OBJ' # List of operator properties, the attributes will be assigned @@ -17,19 +47,12 @@ class SCRIPT_OT_export_obj(bpy.types.Operator): self.debug("exec") self.debug("filename = " + self.filename) - self.debug("num selected objects: {0}".format(len(context.selected_objects))) + act = context.active_object - ob = bpy.data.objects["Cube"] - o = ob.data - - m = bpy.data.add_mesh("tmpmesh") - m.copy_applied(context.scene, ob, True) - - def vert(co): - return "{0}, {1}, {2}".format(co[0], co[1], co[2]) - - print(" orig: {0} with totverts={1}".format(vert(o.verts[0].co), len(o.verts))) - print("applied: {0} with totverts={1}".format(vert(m.verts[0].co), len(m.verts))) + if act.type == 'MESH': + write_obj(self.filename, context.scene, act) + else: + self.debug("Active object is not a MESH.") # XXX errors are silenced for some reason # raise Exception("oops!") @@ -38,9 +61,8 @@ class SCRIPT_OT_export_obj(bpy.types.Operator): def invoke(self, context, event): self.debug("invoke") -# context.add_fileselect(self.__operator__) -# return ('RUNNING_MODAL',) - return self.exec(context) + context.add_fileselect(self.__operator__) + return ('RUNNING_MODAL',) def poll(self, context): # poll isnt working yet self.debug("poll") diff --git a/source/blender/editors/mesh/editmesh.c b/source/blender/editors/mesh/editmesh.c index de4bab57a72..33174e31df5 100644 --- a/source/blender/editors/mesh/editmesh.c +++ b/source/blender/editors/mesh/editmesh.c @@ -2041,14 +2041,11 @@ void RNA_api_mesh_copy(Mesh *me, Mesh *from) copy_mesh_data(me, from); } -void RNA_api_mesh_copy_applied(Mesh *me, Scene *sce, Object *ob, int apply_obmat) +void RNA_api_mesh_copy_applied(Mesh *me, Scene *sce, Object *ob) { DerivedMesh *dm= mesh_create_derived_view(sce, ob, CD_MASK_MESH); DM_to_mesh(dm, me); dm->release(dm); - - if (apply_obmat) { - } } void RNA_api_mesh_transform(Mesh *me, float **mat) diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 4d3b9dfa327..aad70ec57b0 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -196,7 +196,7 @@ struct Mesh; void RNA_api_ui_layout(struct StructRNA *srna); struct Mesh *RNA_api_add_mesh(struct Main *main, char *name); void RNA_api_mesh_copy(struct Mesh *me, struct Mesh *from); -void RNA_api_mesh_copy_applied(struct Mesh *me, struct Scene *sce, struct Object *ob, int apply_obmat); +void RNA_api_mesh_copy_applied(struct Mesh *me, struct Scene *sce, struct Object *ob); /* ID Properties */ diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 35b8164fae3..574bfee49f6 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1134,12 +1134,11 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_REQUIRED); func= RNA_def_function(srna, "copy_applied", "RNA_api_mesh_copy_applied"); - RNA_def_function_ui_description(func, "Copy mesh data from object with all modifiers and transformations applied."); + RNA_def_function_ui_description(func, "Copy mesh data from object with all modifiers applied."); prop= RNA_def_pointer(func, "sce", "Scene", "", "Scene."); RNA_def_property_flag(prop, PROP_REQUIRED); prop= RNA_def_pointer(func, "ob", "Object", "", "Object to copy data from."); RNA_def_property_flag(prop, PROP_REQUIRED); - RNA_def_boolean(func, "apply_obmat", 1, "", "Apply object matrix."); } void RNA_def_mesh(BlenderRNA *brna) From 16d8cc633db5e1e3168d9ce9bbea62957d973024 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Tue, 16 Jun 2009 12:42:14 +0000 Subject: [PATCH 20/68] WARNING: I'm starting generic RNA collection add ;) --- source/blender/makesrna/intern/rna_access.c | 24 +++++++++++++++++++ source/blender/makesrna/intern/rna_internal.h | 6 +++++ 2 files changed, 30 insertions(+) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 870fa4d9aa3..71044cbfe13 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -1342,6 +1342,8 @@ void RNA_property_collection_add(PointerRNA *ptr, PropertyRNA *prop, PointerRNA else printf("RNA_property_collection_add %s.%s: only supported for id properties.\n", ptr->type->identifier, prop->identifier); + /* TODO: call cprop->add on non-ID props here */ + if(r_ptr) { if(idprop) { CollectionPropertyRNA *cprop= (CollectionPropertyRNA*)prop; @@ -1510,6 +1512,13 @@ void rna_iterator_listbase_end(CollectionPropertyIterator *iter) iter->internal= NULL; } +void *rna_iterator_listbase_add(ListBase *lb, void *item) +{ + BLI_addtail(lb, item); + + return item; +} + void rna_iterator_array_begin(CollectionPropertyIterator *iter, void *ptr, int itemsize, int length, IteratorSkipFunc skip) { ArrayIterator *internal; @@ -1567,6 +1576,21 @@ void rna_iterator_array_end(CollectionPropertyIterator *iter) iter->internal= NULL; } +void *rna_iterator_array_add(void *ptr, int itemsize, int length, void *item) +{ + // alloc new block, copy old data + void *newptr= MEM_callocN(length * itemsize + itemsize, "RNA collection add"); + memcpy(newptr, ptr, length * itemsize); + + // copy new item + memcpy(((char*)newptr) + length * itemsize, item, itemsize); + + // free old block + MEM_freeN(ptr); + + return newptr; +} + /* RNA Path - Experiment */ static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen, int bracket) diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index aad70ec57b0..531268768d3 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -237,6 +237,9 @@ void rna_iterator_listbase_next(struct CollectionPropertyIterator *iter); void *rna_iterator_listbase_get(struct CollectionPropertyIterator *iter); void rna_iterator_listbase_end(struct CollectionPropertyIterator *iter); +/* experimental */ +void *rna_iterator_listbase_add(ListBase *lb, void *item); + typedef struct ArrayIterator { char *ptr; char *endptr; @@ -250,6 +253,9 @@ void *rna_iterator_array_get(struct CollectionPropertyIterator *iter); void *rna_iterator_array_dereference_get(struct CollectionPropertyIterator *iter); void rna_iterator_array_end(struct CollectionPropertyIterator *iter); +/* experimental */ +void *rna_iterator_array_add(void *ptr, void *data); + /* Duplicated code since we can't link in blenlib */ void rna_addtail(struct ListBase *listbase, void *vlink); From 7a54e45ccdaed5d7fa802fa20a31fda205b04294 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Tue, 16 Jun 2009 13:57:28 +0000 Subject: [PATCH 21/68] Just added another param to RNA_def_property_collection_funcs and zeroed it in each call. --- source/blender/makesrna/RNA_define.h | 2 +- source/blender/makesrna/intern/rna_color.c | 2 +- source/blender/makesrna/intern/rna_curve.c | 2 +- source/blender/makesrna/intern/rna_define.c | 5 +++-- source/blender/makesrna/intern/rna_group.c | 2 +- source/blender/makesrna/intern/rna_internal.h | 2 +- .../makesrna/intern/rna_internal_types.h | 2 ++ source/blender/makesrna/intern/rna_key.c | 2 +- source/blender/makesrna/intern/rna_lattice.c | 4 ++-- source/blender/makesrna/intern/rna_main.c | 2 +- source/blender/makesrna/intern/rna_material.c | 2 +- source/blender/makesrna/intern/rna_mesh.c | 22 +++++++++---------- source/blender/makesrna/intern/rna_modifier.c | 2 +- source/blender/makesrna/intern/rna_object.c | 2 +- source/blender/makesrna/intern/rna_rna.c | 10 ++++----- source/blender/makesrna/intern/rna_scene.c | 2 +- source/blender/makesrna/intern/rna_sequence.c | 2 +- 17 files changed, 35 insertions(+), 32 deletions(-) diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h index c40f50c34fc..553d0ecf028 100644 --- a/source/blender/makesrna/RNA_define.h +++ b/source/blender/makesrna/RNA_define.h @@ -146,7 +146,7 @@ void RNA_def_property_float_funcs(PropertyRNA *prop, const char *get, const char void RNA_def_property_enum_funcs(PropertyRNA *prop, const char *get, const char *set, const char *item); void RNA_def_property_string_funcs(PropertyRNA *prop, const char *get, const char *length, const char *set); void RNA_def_property_pointer_funcs(PropertyRNA *prop, const char *get, const char *set, const char *typef); -void RNA_def_property_collection_funcs(PropertyRNA *prop, const char *begin, const char *next, const char *end, const char *get, const char *length, const char *lookupint, const char *lookupstring); +void RNA_def_property_collection_funcs(PropertyRNA *prop, const char *begin, const char *next, const char *end, const char *get, const char *length, const char *lookupint, const char *lookupstring, const char *add); /* Function */ diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index dcd5a494e5d..ef9bd86c2d9 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -212,7 +212,7 @@ static void rna_def_curvemapping(BlenderRNA *brna) RNA_def_property_float_funcs(prop, NULL, NULL, "rna_CurveMapping_clipmaxy_range"); prop= RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_funcs(prop, "rna_CurveMapping_curves_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_CurveMapping_curves_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_CurveMapping_curves_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_CurveMapping_curves_length", 0, 0, 0); RNA_def_property_struct_type(prop, "CurveMap"); RNA_def_property_ui_text(prop, "Curves", ""); diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index aac9d75c6a6..6b3c9767e2e 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -605,7 +605,7 @@ static void rna_def_curve_nurb(BlenderRNA *brna) prop= RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "bp", NULL); RNA_def_property_struct_type(prop, "CurvePoint"); - RNA_def_property_collection_funcs(prop, "rna_BPoint_array_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_Nurb_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_BPoint_array_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_Nurb_length", 0, 0, 0); RNA_def_property_ui_text(prop, "Points", "Collection of points for Poly and Nurbs curves."); prop= RNA_def_property(srna, "bezier_points", PROP_COLLECTION, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 33d94e800d1..6b72646da4a 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -604,7 +604,7 @@ StructRNA *RNA_def_struct(BlenderRNA *brna, const char *identifier, const char * if(DefRNA.preprocess) { RNA_def_property_struct_type(prop, "Property"); - RNA_def_property_collection_funcs(prop, "rna_builtin_properties_begin", "rna_builtin_properties_next", "rna_iterator_listbase_end", "rna_builtin_properties_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, "rna_builtin_properties_begin", "rna_builtin_properties_next", "rna_iterator_listbase_end", "rna_builtin_properties_get", 0, 0, 0, 0); } else { #ifdef RNA_RUNTIME @@ -1769,7 +1769,7 @@ void RNA_def_property_pointer_funcs(PropertyRNA *prop, const char *get, const ch } } -void RNA_def_property_collection_funcs(PropertyRNA *prop, const char *begin, const char *next, const char *end, const char *get, const char *length, const char *lookupint, const char *lookupstring) +void RNA_def_property_collection_funcs(PropertyRNA *prop, const char *begin, const char *next, const char *end, const char *get, const char *length, const char *lookupint, const char *lookupstring, const char *add) { StructRNA *srna= DefRNA.laststruct; @@ -1789,6 +1789,7 @@ void RNA_def_property_collection_funcs(PropertyRNA *prop, const char *begin, con if(length) cprop->length= (PropCollectionLengthFunc)length; if(lookupint) cprop->lookupint= (PropCollectionLookupIntFunc)lookupint; if(lookupstring) cprop->lookupstring= (PropCollectionLookupStringFunc)lookupstring; + if(add) cprop->add= (PropCollectionAddFunc)add; break; } default: diff --git a/source/blender/makesrna/intern/rna_group.c b/source/blender/makesrna/intern/rna_group.c index 059b2ce78f7..f284423ef7f 100644 --- a/source/blender/makesrna/intern/rna_group.c +++ b/source/blender/makesrna/intern/rna_group.c @@ -61,7 +61,7 @@ void RNA_def_group(BlenderRNA *brna) RNA_def_property_collection_sdna(prop, NULL, "gobject", NULL); RNA_def_property_struct_type(prop, "Object"); RNA_def_property_ui_text(prop, "Objects", "A collection of this groups objects."); - RNA_def_property_collection_funcs(prop, 0, 0, 0, "rna_Group_objects_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, 0, 0, 0, "rna_Group_objects_get", 0, 0, 0, 0); prop= RNA_def_property(srna, "layer", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "layer", 1); diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 531268768d3..467e0dabb24 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -254,7 +254,7 @@ void *rna_iterator_array_dereference_get(struct CollectionPropertyIterator *iter void rna_iterator_array_end(struct CollectionPropertyIterator *iter); /* experimental */ -void *rna_iterator_array_add(void *ptr, void *data); +void *rna_iterator_array_add(void *ptr, int itemsize, int length, void *item); /* Duplicated code since we can't link in blenlib */ diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index d690251f503..1a3668813c8 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -78,6 +78,7 @@ typedef PointerRNA (*PropCollectionGetFunc)(struct CollectionPropertyIterator *i typedef int (*PropCollectionLengthFunc)(struct PointerRNA *ptr); typedef PointerRNA (*PropCollectionLookupIntFunc)(struct PointerRNA *ptr, int key); typedef PointerRNA (*PropCollectionLookupStringFunc)(struct PointerRNA *ptr, const char *key); +typedef void (*PropCollectionAddFunc)(PointerRNA *ptr, PointerRNA *item); /* Container - generic abstracted container of RNA properties */ typedef struct ContainerRNA { @@ -243,6 +244,7 @@ typedef struct CollectionPropertyRNA { PropCollectionLengthFunc length; /* optional */ PropCollectionLookupIntFunc lookupint; /* optional */ PropCollectionLookupStringFunc lookupstring; /* optional */ + PropCollectionAddFunc add; struct StructRNA *type; } CollectionPropertyRNA; diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c index ae03cca74c2..3070e0a9076 100644 --- a/source/blender/makesrna/intern/rna_key.c +++ b/source/blender/makesrna/intern/rna_key.c @@ -335,7 +335,7 @@ static void rna_def_keyblock(BlenderRNA *brna) RNA_def_property_collection_sdna(prop, NULL, "data", "totelem"); RNA_def_property_struct_type(prop, "UnknownType"); RNA_def_property_ui_text(prop, "Data", ""); - RNA_def_property_collection_funcs(prop, "rna_ShapeKey_data_begin", 0, 0, "rna_ShapeKey_data_get", "rna_ShapeKey_data_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_ShapeKey_data_begin", 0, 0, "rna_ShapeKey_data_get", "rna_ShapeKey_data_length", 0, 0, 0); } static void rna_def_key(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_lattice.c b/source/blender/makesrna/intern/rna_lattice.c index 26c4ebb7b23..de547eab617 100644 --- a/source/blender/makesrna/intern/rna_lattice.c +++ b/source/blender/makesrna/intern/rna_lattice.c @@ -99,7 +99,7 @@ static void rna_def_latticepoint(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Deformed Location", ""); prop= RNA_def_property(srna, "groups", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_funcs(prop, "rna_LatticePoint_groups_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, "rna_LatticePoint_groups_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", 0, 0, 0, 0); RNA_def_property_struct_type(prop, "VertexGroupElement"); RNA_def_property_ui_text(prop, "Groups", "Weights for the vertex groups this point is member of."); } @@ -159,7 +159,7 @@ static void rna_def_lattice(BlenderRNA *brna) prop= RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "LatticePoint"); - RNA_def_property_collection_funcs(prop, "rna_Lattice_points_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, "rna_Lattice_points_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", 0, 0, 0, 0); RNA_def_property_ui_text(prop, "Points", "Points of the lattice."); } diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index 347aef69a76..22f19d725cb 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -264,7 +264,7 @@ void RNA_def_main(BlenderRNA *brna) { prop= RNA_def_property(srna, lists[i][0], PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, lists[i][1]); - RNA_def_property_collection_funcs(prop, lists[i][2], "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, lists[i][2], "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", 0, 0, 0, 0); RNA_def_property_ui_text(prop, lists[i][3], lists[i][4]); } diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index a5dbb63adf2..1ab70154442 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -1048,7 +1048,7 @@ void rna_def_mtex_common(StructRNA *srna, const char *begin, const char *activeg /* mtex */ prop= RNA_def_property(srna, "textures", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, structname); - RNA_def_property_collection_funcs(prop, begin, "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_dereference_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, begin, "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_dereference_get", 0, 0, 0, 0); RNA_def_property_ui_text(prop, "Textures", "Texture slots defining the mapping and influence of textures."); prop= RNA_def_property(srna, "active_texture", PROP_POINTER, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 574bfee49f6..18a94d97296 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -646,7 +646,7 @@ static void rna_def_mvert(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Bevel Weight", "Weight used by the Bevel modifier 'Only Vertices' option"); prop= RNA_def_property(srna, "groups", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_funcs(prop, "rna_MeshVertex_groups_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, "rna_MeshVertex_groups_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", 0, 0, 0, 0); RNA_def_property_struct_type(prop, "VertexGroupElement"); RNA_def_property_ui_text(prop, "Groups", "Weights for the vertex groups this vertex is member of."); } @@ -761,7 +761,7 @@ static void rna_def_mtface(BlenderRNA *brna) prop= RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshTextureFace"); RNA_def_property_ui_text(prop, "Data", ""); - RNA_def_property_collection_funcs(prop, "rna_MeshTextureFaceLayer_data_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_MeshTextureFaceLayer_data_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_MeshTextureFaceLayer_data_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_MeshTextureFaceLayer_data_length", 0, 0, 0); srna= RNA_def_struct(brna, "MeshTextureFace", NULL); RNA_def_struct_sdna(srna, "MTFace"); @@ -902,7 +902,7 @@ static void rna_def_mcol(BlenderRNA *brna) prop= RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshColor"); RNA_def_property_ui_text(prop, "Data", ""); - RNA_def_property_collection_funcs(prop, "rna_MeshColorLayer_data_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_MeshColorLayer_data_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_MeshColorLayer_data_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_MeshColorLayer_data_length", 0, 0, 0); srna= RNA_def_struct(brna, "MeshColor", NULL); RNA_def_struct_sdna(srna, "MCol"); @@ -948,7 +948,7 @@ static void rna_def_mproperties(BlenderRNA *brna) prop= RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshFloatProperty"); RNA_def_property_ui_text(prop, "Data", ""); - RNA_def_property_collection_funcs(prop, "rna_MeshFloatPropertyLayer_data_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_MeshFloatPropertyLayer_data_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_MeshFloatPropertyLayer_data_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_MeshFloatPropertyLayer_data_length", 0, 0, 0); srna= RNA_def_struct(brna, "MeshFloatProperty", NULL); RNA_def_struct_sdna(srna, "MFloatProperty"); @@ -972,7 +972,7 @@ static void rna_def_mproperties(BlenderRNA *brna) prop= RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshIntProperty"); RNA_def_property_ui_text(prop, "Data", ""); - RNA_def_property_collection_funcs(prop, "rna_MeshIntPropertyLayer_data_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_MeshIntPropertyLayer_data_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_MeshIntPropertyLayer_data_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_MeshIntPropertyLayer_data_length", 0, 0, 0); srna= RNA_def_struct(brna, "MeshIntProperty", NULL); RNA_def_struct_sdna(srna, "MIntProperty"); @@ -996,7 +996,7 @@ static void rna_def_mproperties(BlenderRNA *brna) prop= RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshStringProperty"); RNA_def_property_ui_text(prop, "Data", ""); - RNA_def_property_collection_funcs(prop, "rna_MeshStringPropertyLayer_data_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_MeshStringPropertyLayer_data_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_MeshStringPropertyLayer_data_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_MeshStringPropertyLayer_data_length", 0, 0, 0); srna= RNA_def_struct(brna, "MeshStringProperty", NULL); RNA_def_struct_sdna(srna, "MStringProperty"); @@ -1072,31 +1072,31 @@ static void rna_def_mesh(BlenderRNA *brna) prop= RNA_def_property(srna, "uv_layers", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "fdata.layers", "fdata.totlayer"); - RNA_def_property_collection_funcs(prop, "rna_Mesh_uv_layers_begin", 0, 0, 0, "rna_Mesh_uv_layers_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_Mesh_uv_layers_begin", 0, 0, 0, "rna_Mesh_uv_layers_length", 0, 0, 0); RNA_def_property_struct_type(prop, "MeshTextureFaceLayer"); RNA_def_property_ui_text(prop, "UV Layers", ""); prop= RNA_def_property(srna, "vcol_layers", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "fdata.layers", "fdata.totlayer"); - RNA_def_property_collection_funcs(prop, "rna_Mesh_vcol_layers_begin", 0, 0, 0, "rna_Mesh_vcol_layers_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_Mesh_vcol_layers_begin", 0, 0, 0, "rna_Mesh_vcol_layers_length", 0, 0, 0); RNA_def_property_struct_type(prop, "MeshColorLayer"); RNA_def_property_ui_text(prop, "Vertex Color Layers", ""); prop= RNA_def_property(srna, "float_layers", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "fdata.layers", "fdata.totlayer"); - RNA_def_property_collection_funcs(prop, "rna_Mesh_float_layers_begin", 0, 0, 0, "rna_Mesh_float_layers_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_Mesh_float_layers_begin", 0, 0, 0, "rna_Mesh_float_layers_length", 0, 0, 0); RNA_def_property_struct_type(prop, "MeshFloatPropertyLayer"); RNA_def_property_ui_text(prop, "Float Property Layers", ""); prop= RNA_def_property(srna, "int_layers", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "fdata.layers", "fdata.totlayer"); - RNA_def_property_collection_funcs(prop, "rna_Mesh_int_layers_begin", 0, 0, 0, "rna_Mesh_int_layers_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_Mesh_int_layers_begin", 0, 0, 0, "rna_Mesh_int_layers_length", 0, 0, 0); RNA_def_property_struct_type(prop, "MeshIntPropertyLayer"); RNA_def_property_ui_text(prop, "Int Property Layers", ""); prop= RNA_def_property(srna, "string_layers", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "fdata.layers", "fdata.totlayer"); - RNA_def_property_collection_funcs(prop, "rna_Mesh_string_layers_begin", 0, 0, 0, "rna_Mesh_string_layers_length", 0, 0); + RNA_def_property_collection_funcs(prop, "rna_Mesh_string_layers_begin", 0, 0, 0, "rna_Mesh_string_layers_length", 0, 0, 0); RNA_def_property_struct_type(prop, "MeshStringPropertyLayer"); RNA_def_property_ui_text(prop, "String Property Layers", ""); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index fe5bd6ad727..56e16a8f21e 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1089,7 +1089,7 @@ static void rna_def_modifier_uvproject(BlenderRNA *brna) prop= RNA_def_property(srna, "projectors", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "Object"); - RNA_def_property_collection_funcs(prop, "rna_UVProject_projectors_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_dereference_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, "rna_UVProject_projectors_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_dereference_get", 0, 0, 0, 0); RNA_def_property_ui_text(prop, "Projectors", ""); prop= RNA_def_property(srna, "image", PROP_POINTER, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 86cf501295c..4d5cef123dc 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -739,7 +739,7 @@ static StructRNA *rna_def_object(BlenderRNA *brna) prop= RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol"); RNA_def_property_struct_type(prop, "MaterialSlot"); - RNA_def_property_collection_funcs(prop, NULL, NULL, NULL, "rna_iterator_array_get", 0, 0, 0); /* don't dereference pointer! */ + RNA_def_property_collection_funcs(prop, NULL, NULL, NULL, "rna_iterator_array_get", 0, 0, 0, 0); /* don't dereference pointer! */ RNA_def_property_ui_text(prop, "Materials", "Material slots in the object."); prop= RNA_def_property(srna, "active_material", PROP_POINTER, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index 8ea8844c65f..e0f670e8ded 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -611,13 +611,13 @@ static void rna_def_struct(BlenderRNA *brna) prop= RNA_def_property(srna, "properties", PROP_COLLECTION, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_struct_type(prop, "Property"); - RNA_def_property_collection_funcs(prop, "rna_Struct_properties_begin", "rna_Struct_properties_next", "rna_iterator_listbase_end", "rna_Struct_properties_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, "rna_Struct_properties_begin", "rna_Struct_properties_next", "rna_iterator_listbase_end", "rna_Struct_properties_get", 0, 0, 0, 0); RNA_def_property_ui_text(prop, "Properties", "Properties in the struct."); prop= RNA_def_property(srna, "functions", PROP_COLLECTION, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_struct_type(prop, "Function"); - RNA_def_property_collection_funcs(prop, "rna_Struct_functions_begin", "rna_Struct_functions_next", "rna_iterator_listbase_end", "rna_Struct_functions_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, "rna_Struct_functions_begin", "rna_Struct_functions_next", "rna_iterator_listbase_end", "rna_Struct_functions_get", 0, 0, 0, 0); RNA_def_property_ui_text(prop, "Functions", ""); } @@ -719,7 +719,7 @@ static void rna_def_function(BlenderRNA *brna) prop= RNA_def_property(srna, "parameters", PROP_COLLECTION, PROP_NONE); /*RNA_def_property_clear_flag(prop, PROP_EDITABLE);*/ RNA_def_property_struct_type(prop, "Property"); - RNA_def_property_collection_funcs(prop, "rna_Function_parameters_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, "rna_Function_parameters_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", 0, 0, 0, 0); RNA_def_property_ui_text(prop, "Parameters", "Parameters for the function."); prop= RNA_def_property(srna, "registered", PROP_BOOLEAN, PROP_NONE); @@ -800,7 +800,7 @@ static void rna_def_enum_property(BlenderRNA *brna, StructRNA *srna) prop= RNA_def_property(srna, "items", PROP_COLLECTION, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_struct_type(prop, "EnumPropertyItem"); - RNA_def_property_collection_funcs(prop, "rna_EnumProperty_items_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, "rna_EnumProperty_items_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", 0, 0, 0, 0); RNA_def_property_ui_text(prop, "Items", "Possible values for the property."); srna= RNA_def_struct(brna, "EnumPropertyItem", NULL); @@ -895,7 +895,7 @@ void RNA_def_rna(BlenderRNA *brna) prop= RNA_def_property(srna, "structs", PROP_COLLECTION, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_struct_type(prop, "Struct"); - RNA_def_property_collection_funcs(prop, "rna_BlenderRNA_structs_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, "rna_BlenderRNA_structs_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", 0, 0, 0, 0); RNA_def_property_ui_text(prop, "Structs", ""); } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 3907a633e3d..83b6df502a6 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -866,7 +866,7 @@ void RNA_def_scene(BlenderRNA *brna) RNA_def_property_collection_sdna(prop, NULL, "base", NULL); RNA_def_property_struct_type(prop, "Object"); RNA_def_property_ui_text(prop, "Objects", ""); - RNA_def_property_collection_funcs(prop, 0, 0, 0, "rna_Scene_objects_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, 0, 0, 0, "rna_Scene_objects_get", 0, 0, 0, 0); prop= RNA_def_property(srna, "visible_layers", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "lay", 1); diff --git a/source/blender/makesrna/intern/rna_sequence.c b/source/blender/makesrna/intern/rna_sequence.c index b21e08fcd1a..b3975e2469e 100644 --- a/source/blender/makesrna/intern/rna_sequence.c +++ b/source/blender/makesrna/intern/rna_sequence.c @@ -519,7 +519,7 @@ void rna_def_editor(BlenderRNA *brna) RNA_def_property_collection_sdna(prop, NULL, "metastack", NULL); RNA_def_property_struct_type(prop, "Sequence"); RNA_def_property_ui_text(prop, "Meta Stack", "Meta strip stack, last is currently edited meta strip."); - RNA_def_property_collection_funcs(prop, 0, 0, 0, "rna_SequenceEdtior_meta_stack_get", 0, 0, 0); + RNA_def_property_collection_funcs(prop, 0, 0, 0, "rna_SequenceEdtior_meta_stack_get", 0, 0, 0, 0); prop= RNA_def_property(srna, "active_strip", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "act_seq"); From bbec5c03c989fca91ba8be9f3c002c6cb2980415 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Tue, 16 Jun 2009 16:15:19 +0000 Subject: [PATCH 22/68] Updated makesrna to generate correct CollectionPropertyRNAs. --- source/blender/makesrna/intern/makesrna.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index ea587174f54..304a008229a 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1628,7 +1628,7 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr } case PROP_COLLECTION: { CollectionPropertyRNA *cprop= (CollectionPropertyRNA*)prop; - fprintf(f, "\t%s, %s, %s, %s, %s, %s, %s, ", rna_function_string(cprop->begin), rna_function_string(cprop->next), rna_function_string(cprop->end), rna_function_string(cprop->get), rna_function_string(cprop->length), rna_function_string(cprop->lookupint), rna_function_string(cprop->lookupstring)); + fprintf(f, "\t%s, %s, %s, %s, %s, %s, %s, %s, ", rna_function_string(cprop->begin), rna_function_string(cprop->next), rna_function_string(cprop->end), rna_function_string(cprop->get), rna_function_string(cprop->length), rna_function_string(cprop->lookupint), rna_function_string(cprop->lookupstring), rna_function_string(cprop->add)); if(cprop->type) fprintf(f, "&RNA_%s\n", (char*)cprop->type); else fprintf(f, "NULL\n"); break; From 449555315a45fe4e58cc20d5685c60063444543f Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Tue, 16 Jun 2009 18:02:38 +0000 Subject: [PATCH 23/68] RNA_property_collection_add should work now for non-ID properties. Since it's very limited, and has a doubtful interface, I need help on how improve and use it. I'll ask on ML. --- source/blender/makesrna/intern/rna_access.c | 8 ++++-- .../makesrna/intern/rna_internal_types.h | 2 +- source/blender/makesrna/intern/rna_mesh.c | 28 +++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 71044cbfe13..f114e72e990 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -1314,6 +1314,7 @@ int RNA_property_collection_length(PointerRNA *ptr, PropertyRNA *prop) void RNA_property_collection_add(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *r_ptr) { IDProperty *idprop; + CollectionPropertyRNA *cprop= (CollectionPropertyRNA*)prop; if((idprop=rna_idproperty_check(&prop, ptr))) { IDPropertyTemplate val = {0}; @@ -1339,10 +1340,11 @@ void RNA_property_collection_add(PointerRNA *ptr, PropertyRNA *prop, PointerRNA MEM_freeN(item); } } + else if(cprop->add){ + cprop->add(ptr, r_ptr); + } else - printf("RNA_property_collection_add %s.%s: only supported for id properties.\n", ptr->type->identifier, prop->identifier); - - /* TODO: call cprop->add on non-ID props here */ + printf("RNA_property_collection_add %s.%s: not implemented for this property.\n", ptr->type->identifier, prop->identifier); if(r_ptr) { if(idprop) { diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index 1a3668813c8..840b5745681 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -78,7 +78,7 @@ typedef PointerRNA (*PropCollectionGetFunc)(struct CollectionPropertyIterator *i typedef int (*PropCollectionLengthFunc)(struct PointerRNA *ptr); typedef PointerRNA (*PropCollectionLookupIntFunc)(struct PointerRNA *ptr, int key); typedef PointerRNA (*PropCollectionLookupStringFunc)(struct PointerRNA *ptr, const char *key); -typedef void (*PropCollectionAddFunc)(PointerRNA *ptr, PointerRNA *item); +typedef void (*PropCollectionAddFunc)(PointerRNA *ptr, PointerRNA *ptr_item); /* Container - generic abstracted container of RNA properties */ typedef struct ContainerRNA { diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 18a94d97296..ec58d695105 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -484,6 +484,33 @@ static void rna_TextureFace_image_set(PointerRNA *ptr, PointerRNA value) tf->tpage= (struct Image*)id; } +static void rna_Mesh_verts_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + Mesh *me= (Mesh*)ptr->data; + rna_iterator_array_begin(iter, me->mvert, sizeof(MVert), me->totvert, NULL); +} + +/* extern struct EditVert *addvertlist(EditMesh *em, float *vec, struct EditVert *example); */ + +static void rna_Mesh_verts_add(PointerRNA *ptr, PointerRNA *ptr_item) +{ + Mesh *me= (Mesh*)ptr->data; + + /* + // XXX if item is not MVert we fail silently + if (item->type == RNA_MeshVertex) + return; + + // XXX this must be slow... + EditMesh *em= BKE_mesh_get_editmesh(me); + + MVert *v = (MVert*)ptr_item->ptr->data; + addvertlist(em, v->co, NULL); + + BKE_mesh_end_editmesh(me, em); + */ +} + /* path construction */ static char *rna_VertexGroupElement_path(PointerRNA *ptr) @@ -1054,6 +1081,7 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_collection_sdna(prop, NULL, "mvert", "totvert"); RNA_def_property_struct_type(prop, "MeshVertex"); RNA_def_property_ui_text(prop, "Vertices", "Vertices of the mesh."); + RNA_def_property_collection_funcs(prop, "rna_Mesh_verts_begin", 0, 0, 0, 0, 0, 0, "rna_Mesh_verts_add"); prop= RNA_def_property(srna, "edges", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "medge", "totedge"); From 29f5694ab8d2021bb0b9c197f95b228809b40897 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 18 Jun 2009 09:50:34 +0000 Subject: [PATCH 24/68] API structuring improvements according to design guidelines by Brecht, for more info see http://lists.blender.org/pipermail/bf-taskforce25/2009-June/000954.html. Created *_api.c files in makesrna/intern. Among these only rna_api.c is compiled on preprocesssing step. It contains code declaring RNA struct functions, for example RNA_api_mesh declares all functions on Mesh. The rest *_api.c files contain functions themselves. Removed interface_api.c and moved its contents to rna_api.c. Added remove_mesh function on Main. Replaced copy and copy_mesh on Mesh with make_rendermesh which currently does the same as copy_applied did (grasping mesh-related stuff needs time). SConscript tweaking so it builds ok. --- source/blender/editors/interface/SConscript | 4 +- source/blender/editors/mesh/editmesh.c | 26 +------- source/blender/makesrna/intern/SConscript | 10 ++- source/blender/makesrna/intern/main_api.c | 26 ++++++++ source/blender/makesrna/intern/mesh_api.c | 42 +++++++++++++ .../intern/rna_api.c} | 61 ++++++++++++++++++- source/blender/makesrna/intern/rna_context.c | 5 -- source/blender/makesrna/intern/rna_internal.h | 24 ++++++-- source/blender/makesrna/intern/rna_main.c | 8 +-- source/blender/makesrna/intern/rna_mesh.c | 13 +--- source/blender/makesrna/intern/rna_wm.c | 2 + source/blender/makesrna/intern/wm_api.c | 9 +++ .../blender/python/intern/bpy_operator_wrap.c | 2 +- 13 files changed, 173 insertions(+), 59 deletions(-) create mode 100644 source/blender/makesrna/intern/main_api.c create mode 100644 source/blender/makesrna/intern/mesh_api.c rename source/blender/{editors/interface/interface_api.c => makesrna/intern/rna_api.c} (82%) create mode 100644 source/blender/makesrna/intern/wm_api.c diff --git a/source/blender/editors/interface/SConscript b/source/blender/editors/interface/SConscript index bac3742c12f..a05756ed9c6 100644 --- a/source/blender/editors/interface/SConscript +++ b/source/blender/editors/interface/SConscript @@ -3,8 +3,8 @@ Import ('env') sources = env.Glob('*.c') -for source in env.Glob('*_api.c'): - sources.remove(source) +# for source in env.Glob('*_api.c'): +# sources.remove(source) incs = '../include ../../blenlib ../../blenfont ../../blenkernel ../../makesdna ../../imbuf' incs += ' ../../makesrna ../../windowmanager #/intern/guardedalloc' diff --git a/source/blender/editors/mesh/editmesh.c b/source/blender/editors/mesh/editmesh.c index 33174e31df5..b6cc57990a3 100644 --- a/source/blender/editors/mesh/editmesh.c +++ b/source/blender/editors/mesh/editmesh.c @@ -2028,35 +2028,11 @@ void em_setup_viewcontext(bContext *C, ViewContext *vc) } } -/* Python API */ -void copy_mesh_data(Mesh *dest, Mesh *src); - -Mesh *RNA_api_add_mesh(Main *main, char *name) -{ - return add_mesh(name); -} - -void RNA_api_mesh_copy(Mesh *me, Mesh *from) -{ - copy_mesh_data(me, from); -} - -void RNA_api_mesh_copy_applied(Mesh *me, Scene *sce, Object *ob) -{ - DerivedMesh *dm= mesh_create_derived_view(sce, ob, CD_MASK_MESH); - DM_to_mesh(dm, me); - dm->release(dm); -} - -void RNA_api_mesh_transform(Mesh *me, float **mat) -{ -} - /* * This version of copy_mesh doesn't allocate a new mesh, * instead it copies data between two existing meshes. * - * XXX is this already possible with DerivedMesh? + * XXX not used anywhere... */ void copy_mesh_data(Mesh *dest, Mesh *src) { diff --git a/source/blender/makesrna/intern/SConscript b/source/blender/makesrna/intern/SConscript index 9c8e00da16d..0ad14553bfb 100644 --- a/source/blender/makesrna/intern/SConscript +++ b/source/blender/makesrna/intern/SConscript @@ -13,12 +13,17 @@ root_build_dir=normpath(env['BF_BUILDDIR']) source_files = env.Glob('*.c') source_files.remove('rna_access.c') +api_runtime_files = env.Glob('*_api.c') +for api_file in api_runtime_files: + source_files.remove(api_file) + generated_files = source_files[:] generated_files.remove('rna_define.c') generated_files.remove('makesrna.c') generated_files = [filename[:-2] + '_gen.c' for filename in generated_files] -source_files.extend(env.Glob('../../editors/*/*_api.c')) +# source_files.extend(env.Glob('../../editors/*/*_api.c')) +source_files.extend(env.Glob('rna_api.c')) makesrna_tool = env.Clone() rna = env.Clone() @@ -110,8 +115,9 @@ else: rna.Command (generated_files, '', root_build_dir+os.sep+"makesrna.exe " + build_dir) +api_runtime_files.remove('rna_api.c') obj = ['intern/rna_access.c'] -for generated_file in generated_files: +for generated_file in generated_files + api_runtime_files: obj += ['intern/' + generated_file] Return ('obj') diff --git a/source/blender/makesrna/intern/main_api.c b/source/blender/makesrna/intern/main_api.c new file mode 100644 index 00000000000..5d9e1f5e580 --- /dev/null +++ b/source/blender/makesrna/intern/main_api.c @@ -0,0 +1,26 @@ +#include + +#include "BKE_main.h" +#include "BKE_mesh.h" +#include "BKE_library.h" + +#include "BLI_listbase.h" + +#include "DNA_mesh_types.h" + +Mesh *RNA_api_main_add_mesh(Main *main, char *name) +{ + return add_mesh(name); +} + +void RNA_api_main_remove_mesh(Main *main, Mesh *me) +{ + if (BLI_findindex(&main->mesh, me) == -1) { + /* XXX report error */ + return; + } + + /* XXX correct ? */ + if (me->id.us == 1) + free_libblock(&main->mesh, me); +} diff --git a/source/blender/makesrna/intern/mesh_api.c b/source/blender/makesrna/intern/mesh_api.c new file mode 100644 index 00000000000..e9437e29f81 --- /dev/null +++ b/source/blender/makesrna/intern/mesh_api.c @@ -0,0 +1,42 @@ +#include + +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" + +#include "BLI_blenlib.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_mesh.h" + + +/* +void RNA_api_mesh_copy(Mesh *me, Mesh *from) +{ + copy_mesh_data(me, from); +} + +void RNA_api_mesh_copy_applied(Mesh *me, Scene *sce, Object *ob) +{ + DerivedMesh *dm= mesh_create_derived_view(sce, ob, CD_MASK_MESH); + DM_to_mesh(dm, me); + dm->release(dm); +} +*/ + +/* copied from init_render_mesh (render code) */ +void RNA_api_mesh_make_rendermesh(Mesh *me, Scene *sce, Object *ob) +{ + CustomDataMask mask = CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; + DerivedMesh *dm= mesh_create_derived_render(sce, ob, mask); + + /* XXX report reason */ + if(dm==NULL) return; + + DM_to_mesh(dm, me); + dm->release(dm); +} + +void RNA_api_mesh_transform(Mesh *me, float **mat) +{ +} diff --git a/source/blender/editors/interface/interface_api.c b/source/blender/makesrna/intern/rna_api.c similarity index 82% rename from source/blender/editors/interface/interface_api.c rename to source/blender/makesrna/intern/rna_api.c index b4e7dc03506..fa594c5929e 100644 --- a/source/blender/editors/interface/interface_api.c +++ b/source/blender/makesrna/intern/rna_api.c @@ -34,6 +34,66 @@ #include "UI_interface.h" + +void RNA_api_main(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *prop; + + func= RNA_def_function(srna, "add_mesh", "RNA_api_main_add_mesh"); + RNA_def_function_ui_description(func, "Add a new mesh."); + prop= RNA_def_string(func, "name", "", 0, "", "New name for the datablock."); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop= RNA_def_pointer(func, "mesh", "Mesh", "", "A new mesh."); + RNA_def_function_return(func, prop); + + func= RNA_def_function(srna, "remove_mesh", "RNA_api_main_remove_mesh"); + RNA_def_function_ui_description(func, "Remove a mesh if it has only one user."); + prop= RNA_def_pointer(func, "mesh", "Mesh", "", "A mesh to remove."); + RNA_def_property_flag(prop, PROP_REQUIRED); +} + +void RNA_api_mesh(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *prop; + + /* + func= RNA_def_function(srna, "copy", "RNA_api_mesh_copy"); + RNA_def_function_ui_description(func, "Copy mesh data."); + prop= RNA_def_pointer(func, "src", "Mesh", "", "A mesh to copy data from."); + RNA_def_property_flag(prop, PROP_REQUIRED);*/ + + func= RNA_def_function(srna, "make_rendermesh", "RNA_api_mesh_make_rendermesh"); + RNA_def_function_ui_description(func, "Copy mesh data from object with all modifiers applied."); + prop= RNA_def_pointer(func, "sce", "Scene", "", "Scene."); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop= RNA_def_pointer(func, "ob", "Object", "", "Object to copy data from."); + RNA_def_property_flag(prop, PROP_REQUIRED); + + /* + func= RNA_def_function(srna, "add_geom", "RNA_api_mesh_add_geom"); + RNA_def_function_ui_description(func, "Add geometry data to mesh."); + prop= RNA_def_collection(func, "verts", "?", "", "Vertices."); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop= RNA_def_collection(func, "faces", "?", "", "Faces."); + RNA_def_property_flag(prop, PROP_REQUIRED); + */ +} + +void RNA_api_wm(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *prop; + + func= RNA_def_function(srna, "add_fileselect", "RNA_api_wm_add_fileselect"); + RNA_def_function_ui_description(func, "Show up the file selector."); + prop= RNA_def_pointer(func, "context", "Context", "", "Context."); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop= RNA_def_pointer(func, "op", "Operator", "", "Operator to call."); + RNA_def_property_flag(prop, PROP_REQUIRED); +} + static void api_ui_item_common(FunctionRNA *func) { RNA_def_string(func, "text", "", 0, "", "Override automatic text of the item."); @@ -237,4 +297,3 @@ void RNA_api_ui_layout(StructRNA *srna) parm= RNA_def_string(func, "property", "", 0, "", "Identifier of pointer property in data."); RNA_def_property_flag(parm, PROP_REQUIRED); } - diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c index b1ecf2c3a86..6bab4206dc6 100644 --- a/source/blender/makesrna/intern/rna_context.c +++ b/source/blender/makesrna/intern/rna_context.c @@ -146,11 +146,6 @@ void RNA_def_context(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_struct_type(prop, "Scene"); RNA_def_property_pointer_funcs(prop, "rna_Context_scene_get", NULL, NULL); - - func= RNA_def_function(srna, "add_fileselect", "WM_event_add_fileselect"); - RNA_def_function_ui_description(func, "Show up the file selector."); - prop= RNA_def_pointer(func, "op", "Operator", "", "Operator to call."); - RNA_def_property_flag(prop, PROP_REQUIRED); } #endif diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 467e0dabb24..f38c3aac71d 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -188,15 +188,29 @@ void rna_object_vcollayer_name_set(struct PointerRNA *ptr, const char *value, ch /* API functions */ +void RNA_api_ui_layout(struct StructRNA *srna); +void RNA_api_mesh(struct StructRNA *srna); +void RNA_api_wm(struct StructRNA *srna); +void RNA_api_main(StructRNA *srna); + +#ifdef RNA_RUNTIME + +struct wmWindowManager; +struct bContext; +struct wmOperator; struct Main; +struct Mesh; struct Scene; struct Object; -struct Mesh; -void RNA_api_ui_layout(struct StructRNA *srna); -struct Mesh *RNA_api_add_mesh(struct Main *main, char *name); -void RNA_api_mesh_copy(struct Mesh *me, struct Mesh *from); -void RNA_api_mesh_copy_applied(struct Mesh *me, struct Scene *sce, struct Object *ob); +void RNA_api_wm_add_fileselect(struct wmWindowManager *self, struct bContext *C, struct wmOperator *op); + +struct Mesh *RNA_api_main_add_mesh(struct Main *main, char *name); +void RNA_api_main_remove_mesh(struct Main *main, struct Mesh *me); + +void RNA_api_mesh_make_rendermesh(struct Mesh *me, struct Scene *sce, struct Object *ob); + +#endif /* ID Properties */ diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index 22f19d725cb..78e1c7fb435 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -33,6 +33,7 @@ #ifdef RNA_RUNTIME #include "BKE_main.h" +#include "BKE_mesh.h" /* all the list begin functions are added manually here, Main is not in SDNA */ @@ -268,12 +269,7 @@ void RNA_def_main(BlenderRNA *brna) RNA_def_property_ui_text(prop, lists[i][3], lists[i][4]); } - func= RNA_def_function(srna, "add_mesh", "RNA_api_add_mesh"); - RNA_def_function_ui_description(func, "Add a new mesh."); - prop= RNA_def_string(func, "name", "", 0, "", "New name for the datablock."); - RNA_def_property_flag(prop, PROP_REQUIRED); - prop= RNA_def_pointer(func, "mesh", "Mesh", "", "A new mesh."); - RNA_def_function_return(func, prop); + RNA_api_main(srna); } #endif diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index ec58d695105..d7b17490826 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1071,7 +1071,6 @@ static void rna_def_mesh(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; - FunctionRNA *func; srna= RNA_def_struct(brna, "Mesh", "ID"); RNA_def_struct_ui_text(srna, "Mesh", "Mesh datablock to define geometric surfaces."); @@ -1156,17 +1155,7 @@ static void rna_def_mesh(BlenderRNA *brna) rna_def_texmat_common(srna, "rna_Mesh_texspace_editable"); - func= RNA_def_function(srna, "copy", "RNA_api_mesh_copy"); - RNA_def_function_ui_description(func, "Copy mesh data."); - prop= RNA_def_pointer(func, "src", "Mesh", "", "A mesh to copy data from."); - RNA_def_property_flag(prop, PROP_REQUIRED); - - func= RNA_def_function(srna, "copy_applied", "RNA_api_mesh_copy_applied"); - RNA_def_function_ui_description(func, "Copy mesh data from object with all modifiers applied."); - prop= RNA_def_pointer(func, "sce", "Scene", "", "Scene."); - RNA_def_property_flag(prop, PROP_REQUIRED); - prop= RNA_def_pointer(func, "ob", "Object", "", "Object to copy data from."); - RNA_def_property_flag(prop, PROP_REQUIRED); + RNA_api_mesh(srna); } void RNA_def_mesh(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 22ce207c6a9..df07e03850a 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -160,6 +160,8 @@ static void rna_def_windowmanager(BlenderRNA *brna) prop= RNA_def_property(srna, "operators", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "Operator"); RNA_def_property_ui_text(prop, "Operators", "Operator registry."); + + RNA_api_wm(srna); } void RNA_def_wm(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/wm_api.c b/source/blender/makesrna/intern/wm_api.c new file mode 100644 index 00000000000..a00c401b683 --- /dev/null +++ b/source/blender/makesrna/intern/wm_api.c @@ -0,0 +1,9 @@ +#include "WM_api.h" + +#include "BKE_context.h" + +void RNA_api_wm_add_fileselect(wmWindowManager *self, bContext *C, wmOperator *op) +{ + WM_event_add_fileselect(C, op); +} + diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index 4624fe764b3..c0fac17dd97 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -225,7 +225,7 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve PyObject *ret= NULL, *py_class_instance, *item= NULL; int ret_flag= (mode==PYOP_POLL ? 0:OPERATOR_CANCELLED); PointerRNA ptr_context; - PyObject ptr_operator; + PointerRNA ptr_operator; PyObject *py_operator; PyGILState_STATE gilstate = PyGILState_Ensure(); From 5d78f56c1fb1208329ba9c40eaff6ee4a8cd59bb Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Fri, 19 Jun 2009 10:40:18 +0000 Subject: [PATCH 25/68] - added Object.create_dupli_list, Object.free_dupli_list - attempted to RNA-wrap DupliObject, Object.create_dupli_list returns a collection of these. Build fails probably because DupliObject is not defined in one of DNA_*.h headers. --- source/blender/makesrna/intern/rna_object.c | 30 ++++++ .../blender/makesrna/intern/rna_object_api.c | 96 ++++++++++++++++++- 2 files changed, 121 insertions(+), 5 deletions(-) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index ff9777d283e..36e2ba2d8aa 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -1113,12 +1113,42 @@ static void rna_def_object(BlenderRNA *brna) RNA_api_object(srna); } +static void rna_def_dupli_object(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "DupliObject", NULL); + RNA_def_struct_sdna(srna, "DupliObject"); + RNA_def_struct_ui_text(srna, "Dupli Object", "Dupli Object data."); + /* RNA_def_struct_ui_icon(srna, ICON_OBJECT_DATA); */ + + prop= RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "ob"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Object", "Object this DupliObject represents."); + + prop= RNA_def_property(srna, "ob_matrix", PROP_FLOAT, PROP_MATRIX); + RNA_def_property_float_sdna(prop, NULL, "omat"); + RNA_def_property_array(prop, 16); + RNA_def_property_ui_text(prop, "Object Matrix", "Object transformation matrix."); + + prop= RNA_def_property(srna, "matrix", PROP_FLOAT, PROP_MATRIX); + RNA_def_property_float_sdna(prop, NULL, "mat"); + RNA_def_property_array(prop, 16); + RNA_def_property_ui_text(prop, "DupliObject Matrix", "DupliObject transformation matrix."); + + /* TODO: DupliObject has more properties that can be wrapped */ +} + void RNA_def_object(BlenderRNA *brna) { rna_def_object(brna); rna_def_object_game_settings(brna); rna_def_vertex_group(brna); rna_def_material_slot(brna); + rna_def_dupli_object(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 053ab115b3b..e54c8c712c1 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -32,13 +32,18 @@ #include "RNA_define.h" #include "RNA_types.h" +#define OBJECT_API_PROP_DUPLILIST "dupli_list" + #ifdef RNA_RUNTIME #include "BKE_customdata.h" #include "BKE_DerivedMesh.h" +#include "BKE_anim.h" +#include "BKE_report.h" #include "DNA_mesh_types.h" #include "DNA_scene_types.h" +#include "DNA_object_types.h" /* copied from init_render_mesh (render code) */ Mesh *rna_Object_create_render_mesh(Object *ob, Scene *scene) @@ -64,19 +69,100 @@ Mesh *rna_Object_create_render_mesh(Object *ob, Scene *scene) return me; } +/* When no longer needed, duplilist should be freed with Object.free_duplilist */ +void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *reports) +{ + PointerRNA obptr; + PointerRNA dobptr; + Scene *sce; + ListBase *lb; + DupliObject *dob; + PropertyRNA *prop; + + if (!(ob->transflag & OB_DUPLI)) { + BKE_report(reports, RPT_ERROR, "Object does not have duplis."); + return; + } + + sce= CTX_data_scene(C); + + RNA_id_pointer_create(&ob->id, &obptr); + + if (!(prop= RNA_struct_find_property(&obptr, OBJECT_API_PROP_DUPLILIST))) { + // hint: all Objects will now have this property defined + prop= RNA_def_collection_runtime(obptr->type, OBJECT_API_PROP_DUPLILIST, "DupliObject", "Dupli list", "List of object's duplis"); + } + + RNA_property_collection_clear(&obptr, prop); + lb= object_duplilist(sce, ob); + + for(dob= (DupliObject*)lb->first; dob; dob= dob->next) { + RNA_pointer_create(NULL, &RNA_Object, dob, &dobptr); + RNA_property_collection_add(&obptr, prop, &dobptr); + dob = dob->next; + } + + /* + Now we need to free duplilist with + + free_object_duplilist(lb); + + We can't to it here since DupliObjects are in use, + but we also can't do it in another function since lb + isn't stored... + + So we free lb, but not DupliObjects - these will have to be freed with Object.free_duplilist + */ + + MEM_freeN(lb); +} + +void rna_Object_free_duplilist(Object *ob, ReportList *reports) +{ + PointerRNA obptr; + PropertyRNA *prop; + CollectionPropertyIterator iter; + + RNA_id_pointer_create(&ob->id, &obptr); + + if (!(prop= RNA_struct_find_property(&obptr, OBJECT_API_PROP_DUPLILIST))) { + BKE_report(reports, RPT_ERROR, "Object has no duplilist property."); + return; + } + + /* free each allocated DupliObject */ + RNA_property_collection_begin(&obptr, prop, &iter); + for(; iter.valid; RNA_property_collection_next(&iter)) { + MEM_freeN(iter.ptr.data); + } + RNA_property_collection_end(&iter); + + RNA_property_collection_clear(&obptr, prop); +} + #else void RNA_api_object(StructRNA *srna) { FunctionRNA *func; - PropertyRNA *prop; + PropertyRNA *parm; func= RNA_def_function(srna, "create_render_mesh", "rna_Object_create_render_mesh"); RNA_def_function_ui_description(func, "Create a Mesh datablock with all modifiers applied."); - prop= RNA_def_pointer(func, "scene", "Scene", "", ""); - RNA_def_property_flag(prop, PROP_REQUIRED); - prop= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object, remove it if it is only used for export."); - RNA_def_function_return(func, prop); + parm= RNA_def_pointer(func, "scene", "Scene", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object, remove it if it is only used for export."); + RNA_def_function_return(func, parm); + + func= RNA_def_function(srna, "create_dupli_list", "rna_Object_create_duplilist"); + RNA_def_function_ui_description(func, "Create a list of dupli objects for this object. When no longer needed, it should be freed with free_dupli_list."); + RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); + parm= RNA_def_collection(func, OBJECT_API_PROP_DUPLILIST, "DupliObject", "Dupli list", "List of objects's duplis."); + RNA_def_function_return(func, parm); + + func= RNA_def_function(srna, "free_dupli_list", "rna_Object_free_duplilist"); + RNA_def_function_ui_description(func, "Free the list of dupli objects."); + RNA_def_function_flag(func, FUNC_USE_REPORTS); } #endif From f6267e2a26cac8c7b30c7b165df1df54e9cec474 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Fri, 19 Jun 2009 12:46:51 +0000 Subject: [PATCH 26/68] Moved struct DupliObject to DNA_object_types.h. Object.create_dupli_list now works ok. --- release/io/export_obj.py | 14 ++++++- source/blender/blenkernel/BKE_anim.h | 9 +---- source/blender/makesdna/DNA_object_types.h | 9 +++++ .../blender/makesrna/intern/rna_object_api.c | 38 +++++-------------- 4 files changed, 33 insertions(+), 37 deletions(-) diff --git a/release/io/export_obj.py b/release/io/export_obj.py index 4354f9f9bb9..8b3bcfb26b3 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -41,7 +41,7 @@ class SCRIPT_OT_export_obj(bpy.types.Operator): def debug(self, message): print("{0}: {1}".format(self.__class__.__name__, message)) - def execute(self, context): + def execute_(self, context): self.debug("exec") self.debug("filename = " + self.filename) @@ -56,6 +56,18 @@ class SCRIPT_OT_export_obj(bpy.types.Operator): # raise Exception("oops!") return ('FINISHED',) + + def execute(self, context): + self.debug("exec") + + act = context.active_object + + act.create_dupli_list() + print("{0} has {1} dupli objects".format(act.name, len(act.dupli_list))) + + act.free_dupli_list() + + return ('FINISHED',) def invoke(self, context, event): self.debug("invoke") diff --git a/source/blender/blenkernel/BKE_anim.h b/source/blender/blenkernel/BKE_anim.h index 5ea511738ad..091887a4eb7 100644 --- a/source/blender/blenkernel/BKE_anim.h +++ b/source/blender/blenkernel/BKE_anim.h @@ -39,14 +39,7 @@ struct PartEff; struct Scene; struct ListBase; -typedef struct DupliObject { - struct DupliObject *next, *prev; - struct Object *ob; - unsigned int origlay; - int index, no_draw, type, animated; - float mat[4][4], omat[4][4]; - float orco[3], uv[2]; -} DupliObject; +#include "DNA_object_types.h" void free_path(struct Path *path); void calc_curvepath(struct Object *ob); diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index febf2fe59cd..445a948c5cb 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -230,6 +230,7 @@ typedef struct Object { int pad2; ListBase gpulamp; /* runtime, for lamps only */ + ListBase *duplilist; /* only for internal use by RNA API functions. To get dupli list, use object_duplilist instead */ } Object; /* Warning, this is not used anymore because hooks are now modifiers */ @@ -250,6 +251,14 @@ typedef struct ObHook { float force; } ObHook; +typedef struct DupliObject { + struct DupliObject *next, *prev; + struct Object *ob; + unsigned int origlay; + int index, no_draw, type, animated; + float mat[4][4], omat[4][4]; + float orco[3], uv[2]; +} DupliObject; /* this work object is defined in object.c */ extern Object workob; diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index e54c8c712c1..3944dd72cec 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -32,8 +32,6 @@ #include "RNA_define.h" #include "RNA_types.h" -#define OBJECT_API_PROP_DUPLILIST "dupli_list" - #ifdef RNA_RUNTIME #include "BKE_customdata.h" @@ -45,6 +43,8 @@ #include "DNA_scene_types.h" #include "DNA_object_types.h" +#define OBJECT_API_PROP_DUPLILIST "dupli_list" + /* copied from init_render_mesh (render code) */ Mesh *rna_Object_create_render_mesh(Object *ob, Scene *scene) { @@ -75,7 +75,6 @@ void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *reports) PointerRNA obptr; PointerRNA dobptr; Scene *sce; - ListBase *lb; DupliObject *dob; PropertyRNA *prop; @@ -90,38 +89,27 @@ void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *reports) if (!(prop= RNA_struct_find_property(&obptr, OBJECT_API_PROP_DUPLILIST))) { // hint: all Objects will now have this property defined - prop= RNA_def_collection_runtime(obptr->type, OBJECT_API_PROP_DUPLILIST, "DupliObject", "Dupli list", "List of object's duplis"); + prop= RNA_def_collection_runtime(obptr.type, OBJECT_API_PROP_DUPLILIST, "DupliObject", "Dupli list", "List of object's duplis"); } RNA_property_collection_clear(&obptr, prop); - lb= object_duplilist(sce, ob); + ob->duplilist= object_duplilist(sce, ob); - for(dob= (DupliObject*)lb->first; dob; dob= dob->next) { + for(dob= (DupliObject*)ob->duplilist->first; dob; dob= dob->next) { RNA_pointer_create(NULL, &RNA_Object, dob, &dobptr); RNA_property_collection_add(&obptr, prop, &dobptr); dob = dob->next; } - /* - Now we need to free duplilist with + /* ob->duplilist should now be freed with Object.free_duplilist */ - free_object_duplilist(lb); - - We can't to it here since DupliObjects are in use, - but we also can't do it in another function since lb - isn't stored... - - So we free lb, but not DupliObjects - these will have to be freed with Object.free_duplilist - */ - - MEM_freeN(lb); + return *((CollectionPropertyRNA*)prop); } void rna_Object_free_duplilist(Object *ob, ReportList *reports) { PointerRNA obptr; PropertyRNA *prop; - CollectionPropertyIterator iter; RNA_id_pointer_create(&ob->id, &obptr); @@ -130,14 +118,10 @@ void rna_Object_free_duplilist(Object *ob, ReportList *reports) return; } - /* free each allocated DupliObject */ - RNA_property_collection_begin(&obptr, prop, &iter); - for(; iter.valid; RNA_property_collection_next(&iter)) { - MEM_freeN(iter.ptr.data); - } - RNA_property_collection_end(&iter); - RNA_property_collection_clear(&obptr, prop); + + free_object_duplilist(ob->duplilist); + ob->duplilist= NULL; } #else @@ -157,8 +141,6 @@ void RNA_api_object(StructRNA *srna) func= RNA_def_function(srna, "create_dupli_list", "rna_Object_create_duplilist"); RNA_def_function_ui_description(func, "Create a list of dupli objects for this object. When no longer needed, it should be freed with free_dupli_list."); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); - parm= RNA_def_collection(func, OBJECT_API_PROP_DUPLILIST, "DupliObject", "Dupli list", "List of objects's duplis."); - RNA_def_function_return(func, parm); func= RNA_def_function(srna, "free_dupli_list", "rna_Object_free_duplilist"); RNA_def_function_ui_description(func, "Free the list of dupli objects."); From 705fbec7688c88a801c41bb3c8889cd0527adc30 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Sat, 20 Jun 2009 16:32:52 +0000 Subject: [PATCH 27/68] * Added Main.add_object(type, name) * Added optional params to Object.create_render_mesh(apply_matrix, custom_matrix) apply_matrix is a boolean, True to transform mesh by ob->obmat or custom_matrix if given * Fix subtle error in Object.create_dupli_list * Make RNA struct funcs static, hopefully didn't miss any Ignore export_obj-2.5.py changes for now ;) --- release/scripts/export_obj-2.5.py | 11 ++++ source/blender/makesrna/intern/rna_main_api.c | 50 ++++++++++++++++--- .../blender/makesrna/intern/rna_object_api.c | 49 +++++++++++++++--- 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/release/scripts/export_obj-2.5.py b/release/scripts/export_obj-2.5.py index e0b417d4ce5..a9abba22944 100644 --- a/release/scripts/export_obj-2.5.py +++ b/release/scripts/export_obj-2.5.py @@ -347,6 +347,17 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): face_vert_index = 1 globalNormals = {} + + for ob_main in objects: + + if ob_main.dupli_type != 'NONE': + ob_main.create_dupli_list() + + if ob_main.parent: + pass + + if ob_main.dupli_type != 'NONE': + ob_main.free_dupli_list() # Get all meshes for ob_main in objects: diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 6d56b2b00f9..2c78884a027 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -32,22 +32,25 @@ #include "RNA_define.h" #include "RNA_types.h" +#include "DNA_object_types.h" + #ifdef RNA_RUNTIME #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_library.h" +#include "BKE_object.h" #include "DNA_mesh_types.h" -Mesh *rna_Main_add_mesh(Main *main, char *name) +static Mesh *rna_Main_add_mesh(Main *main, char *name) { Mesh *me= add_mesh(name); me->id.us--; return me; } -void rna_Main_remove_mesh(Main *main, ReportList *reports, Mesh *me) +static void rna_Main_remove_mesh(Main *main, ReportList *reports, Mesh *me) { if(me->id.us == 0) free_libblock(&main->mesh, me); @@ -57,24 +60,55 @@ void rna_Main_remove_mesh(Main *main, ReportList *reports, Mesh *me) /* XXX python now has invalid pointer? */ } +static Object* rna_Main_add_object(Main *main, int type, char *name) +{ + return add_only_object(type, name); +} + #else void RNA_api_main(StructRNA *srna) { FunctionRNA *func; - PropertyRNA *prop; + PropertyRNA *parm; + + /* copied from rna_def_object */ + static EnumPropertyItem object_type_items[] = { + {OB_EMPTY, "EMPTY", 0, "Empty", ""}, + {OB_MESH, "MESH", 0, "Mesh", ""}, + {OB_CURVE, "CURVE", 0, "Curve", ""}, + {OB_SURF, "SURFACE", 0, "Surface", ""}, + {OB_FONT, "TEXT", 0, "Text", ""}, + {OB_MBALL, "META", 0, "Meta", ""}, + {OB_LAMP, "LAMP", 0, "Lamp", ""}, + {OB_CAMERA, "CAMERA", 0, "Camera", ""}, + {OB_WAVE, "WAVE", 0, "Wave", ""}, + {OB_LATTICE, "LATTICE", 0, "Lattice", ""}, + {OB_ARMATURE, "ARMATURE", 0, "Armature", ""}, + {0, NULL, 0, NULL, NULL}}; + + func= RNA_def_function(srna, "add_object", "rna_Main_add_object"); + RNA_def_function_ui_description(func, "Add a new object."); + parm= RNA_def_enum(func, "type", object_type_items, 0, "Type", "Type of Object."); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_string(func, "name", "Object", 0, "", "New name for the datablock."); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_pointer(func, "object", "Object", "", "New object."); + RNA_def_function_return(func, parm); func= RNA_def_function(srna, "add_mesh", "rna_Main_add_mesh"); RNA_def_function_ui_description(func, "Add a new mesh."); - prop= RNA_def_string(func, "name", "Mesh", 0, "", "New name for the datablock."); - prop= RNA_def_pointer(func, "mesh", "Mesh", "", "New mesh."); - RNA_def_function_return(func, prop); + parm= RNA_def_string(func, "name", "Mesh", 0, "", "New name for the datablock."); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_pointer(func, "mesh", "Mesh", "", "New mesh."); + RNA_def_function_return(func, parm); func= RNA_def_function(srna, "remove_mesh", "rna_Main_remove_mesh"); RNA_def_function_flag(func, FUNC_USE_REPORTS); RNA_def_function_ui_description(func, "Remove a mesh if it has zero users."); - prop= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh to remove."); - RNA_def_property_flag(prop, PROP_REQUIRED); + parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh to remove."); + RNA_def_property_flag(parm, PROP_REQUIRED); + } #endif diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index d7f6f85eff4..61f074c00ab 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -32,6 +32,8 @@ #include "RNA_define.h" #include "RNA_types.h" +#include "DNA_object_types.h" + #ifdef RNA_RUNTIME #include "BKE_customdata.h" @@ -41,17 +43,19 @@ #include "DNA_mesh_types.h" #include "DNA_scene_types.h" -#include "DNA_object_types.h" +#include "DNA_meshdata_types.h" #define OBJECT_API_PROP_DUPLILIST "dupli_list" /* copied from init_render_mesh (render code) */ -Mesh *rna_Object_create_render_mesh(Object *ob, bContext *C, ReportList *reports) +static Mesh *rna_Object_create_render_mesh(Object *ob, bContext *C, ReportList *reports, int apply_matrix, float *matrix) { CustomDataMask mask = CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; DerivedMesh *dm; Mesh *me; Scene *sce; + int a; + MVert *mvert; sce= CTX_data_scene(C); @@ -73,11 +77,25 @@ Mesh *rna_Object_create_render_mesh(Object *ob, bContext *C, ReportList *reports DM_to_mesh(dm, me); dm->release(dm); + if (apply_matrix) { + float *mat = ob->obmat; + + if (matrix) { + /* apply custom matrix */ + mat = matrix; + } + + /* is this really that simple? :) */ + for(a= 0, mvert= me->mvert; a < me->totvert; a++, mvert++) { + Mat4MulVecfl(ob->obmat, mvert->co); + } + } + return me; } /* When no longer needed, duplilist should be freed with Object.free_duplilist */ -void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *reports) +static void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *reports) { PointerRNA obptr; PointerRNA dobptr; @@ -96,7 +114,7 @@ void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *reports) if (!(prop= RNA_struct_find_property(&obptr, OBJECT_API_PROP_DUPLILIST))) { // hint: all Objects will now have this property defined - prop= RNA_def_collection_runtime(obptr.type, OBJECT_API_PROP_DUPLILIST, "DupliObject", "Dupli list", "List of object's duplis"); + prop= RNA_def_collection_runtime(obptr.type, OBJECT_API_PROP_DUPLILIST, &RNA_DupliObject, "Dupli list", "List of object's duplis"); } RNA_property_collection_clear(&obptr, prop); @@ -109,11 +127,9 @@ void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *reports) } /* ob->duplilist should now be freed with Object.free_duplilist */ - - return *((CollectionPropertyRNA*)prop); } -void rna_Object_free_duplilist(Object *ob, ReportList *reports) +static void rna_Object_free_duplilist(Object *ob, ReportList *reports) { PointerRNA obptr; PropertyRNA *prop; @@ -138,9 +154,28 @@ void RNA_api_object(StructRNA *srna) FunctionRNA *func; PropertyRNA *parm; + /* copied from rna_def_object */ + static EnumPropertyItem object_type_items[] = { + {OB_EMPTY, "EMPTY", 0, "Empty", ""}, + {OB_MESH, "MESH", 0, "Mesh", ""}, + {OB_CURVE, "CURVE", 0, "Curve", ""}, + {OB_SURF, "SURFACE", 0, "Surface", ""}, + {OB_FONT, "TEXT", 0, "Text", ""}, + {OB_MBALL, "META", 0, "Meta", ""}, + {OB_LAMP, "LAMP", 0, "Lamp", ""}, + {OB_CAMERA, "CAMERA", 0, "Camera", ""}, + {OB_WAVE, "WAVE", 0, "Wave", ""}, + {OB_LATTICE, "LATTICE", 0, "Lattice", ""}, + {OB_ARMATURE, "ARMATURE", 0, "Armature", ""}, + {0, NULL, 0, NULL, NULL}}; + func= RNA_def_function(srna, "create_render_mesh", "rna_Object_create_render_mesh"); RNA_def_function_ui_description(func, "Create a Mesh datablock with all modifiers applied."); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); + parm= RNA_def_boolean(func, "apply_matrix", 0, "", "True if object matrix or custom matrix should be applied to geometry."); + RNA_def_property_clear_flag(parm, PROP_REQUIRED); + parm= RNA_def_float_matrix(func, "custom_matrix", 16, NULL, 0.0f, 0.0f, "", "Optional custom matrix to apply.", 0.0f, 0.0f); + RNA_def_property_clear_flag(parm, PROP_REQUIRED); parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object, remove it if it is only used for export."); RNA_def_function_return(func, parm); From 01da493a0af3130652151640f867cfe6b4bc376a Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Sat, 20 Jun 2009 20:08:11 +0000 Subject: [PATCH 28/68] * Object.create_dupli_list, Object.free_dupli_list tweaking * Defined custom "get" function for DupliObject.object Accessing Object.dupli_list[N].object produces a crash. --- source/blender/makesrna/intern/rna_object.c | 11 ++++++++++- source/blender/makesrna/intern/rna_object_api.c | 13 +++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 159362ef7e7..d566af1954c 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -364,6 +364,14 @@ static void rna_GameObjectSettings_state_set(PointerRNA *ptr, const int *values) } } +static PointerRNA rna_DupliObject_object_get(PointerRNA *ptr) +{ + DupliObject *dob= (DupliObject*)ptr->data; + PointerRNA newptr; + RNA_pointer_create(&dob->ob->id, &RNA_Object, dob->ob, &newptr); + return newptr; +} + #else static void rna_def_vertex_group(BlenderRNA *brna) @@ -1167,8 +1175,9 @@ static void rna_def_dupli_object(BlenderRNA *brna) /* RNA_def_struct_ui_icon(srna, ICON_OBJECT_DATA); */ prop= RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "ob"); RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_pointer_sdna(prop, NULL, "ob"); + RNA_def_property_pointer_funcs(prop, "rna_DupliObject_object_get", NULL, NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Object", "Object this DupliObject represents."); diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 61f074c00ab..c3740921f0f 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -108,20 +108,19 @@ static void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *rep return; } - sce= CTX_data_scene(C); - RNA_id_pointer_create(&ob->id, &obptr); if (!(prop= RNA_struct_find_property(&obptr, OBJECT_API_PROP_DUPLILIST))) { // hint: all Objects will now have this property defined - prop= RNA_def_collection_runtime(obptr.type, OBJECT_API_PROP_DUPLILIST, &RNA_DupliObject, "Dupli list", "List of object's duplis"); + prop= RNA_def_collection_runtime(obptr.type, OBJECT_API_PROP_DUPLILIST, &RNA_DupliObject, "Dupli list", ""); } RNA_property_collection_clear(&obptr, prop); + sce= CTX_data_scene(C); ob->duplilist= object_duplilist(sce, ob); for(dob= (DupliObject*)ob->duplilist->first; dob; dob= dob->next) { - RNA_pointer_create(NULL, &RNA_Object, dob, &dobptr); + RNA_pointer_create(NULL, &RNA_DupliObject, dob, &dobptr); RNA_property_collection_add(&obptr, prop, &dobptr); dob = dob->next; } @@ -143,8 +142,10 @@ static void rna_Object_free_duplilist(Object *ob, ReportList *reports) RNA_property_collection_clear(&obptr, prop); - free_object_duplilist(ob->duplilist); - ob->duplilist= NULL; + if (ob->duplilist) { + free_object_duplilist(ob->duplilist); + ob->duplilist= NULL; + } } #else From 3fce2f282644a632fe2768ae516a50e394c1179f Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Tue, 23 Jun 2009 17:52:58 +0000 Subject: [PATCH 29/68] - added Mesh.transform - fixed Object.create_dupli_list - continuing OBJ exporter conversion --- release/scripts/export_obj-2.5.py | 326 +++++++++++++++++- source/blender/makesrna/intern/rna_mesh_api.c | 22 +- source/blender/makesrna/intern/rna_object.c | 6 + .../blender/makesrna/intern/rna_object_api.c | 38 +- 4 files changed, 351 insertions(+), 41 deletions(-) diff --git a/release/scripts/export_obj-2.5.py b/release/scripts/export_obj-2.5.py index a9abba22944..4fa02ce2efe 100644 --- a/release/scripts/export_obj-2.5.py +++ b/release/scripts/export_obj-2.5.py @@ -186,7 +186,7 @@ def copy_images(dest_dir): def test_nurbs_compat(ob): - if ob.type != 'Curve': + if ob.type != 'CURVE': return False for nu in ob.data: @@ -353,8 +353,328 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if ob_main.dupli_type != 'NONE': ob_main.create_dupli_list() - if ob_main.parent: - pass + # ignore dupli children + if ob_main.parent.dupli_type != 'NONE': + continue + + obs = [] + if ob_main.dupli_type != 'NONE': + obs = [(dob.matrix, dob.object) for dob in ob_main.dupli_list] + else: + obs = [ob.matrix, ob] + + for ob, ob_mat in obs: + # XXX postponed +# # Nurbs curve support +# if EXPORT_CURVE_AS_NURBS and test_nurbs_compat(ob): +# if EXPORT_ROTX90: +# ob_mat = ob_mat * mat_xrot90 + +# totverts += write_nurb(file, ob, ob_mat) + +# continue +# end nurbs + +# # Will work for non meshes now! :) +# me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, EXPORT_POLYGROUPS, scn) +# if not me: +# continue + + if ob.type != 'MESH': + continue + + me = ob.data + + # XXX +# if EXPORT_UV: +# faceuv= me.faceUV +# else: +# faceuv = False + + convert_to_tri = False + + # We have a valid mesh + if EXPORT_TRI and me.faces: + # Add a dummy object to it. + has_quads = False + for f in me.faces: +# if len(f) == 4: + if len(f.verts) == 4: + has_quads = True + break + + convert_to_tri = has_quads +# oldmode = Mesh.Mode() +# Mesh.Mode(Mesh.SelectModes['FACE']) + +# me.sel = True +# tempob = scn.objects.new(me) +# me.quadToTriangle(0) # more=0 shortest length +# oldmode = Mesh.Mode(oldmode) +# scn.objects.unlink(tempob) + +# Mesh.Mode(oldmode) + + if EXPORT_ROTX90: + ob_mat *= mat_xrot90 + + me = ob.create_render_mesh(True, ob_mat, convert_to_tri) + + # Make our own list so it can be sorted to reduce context switching + faces = [ f for f in me.faces ] + + if EXPORT_EDGES: + edges = me.edges + else: + edges = [] + + if not (len(faces)+len(edges)+len(me.verts)): # Make sure there is somthing to write + continue # dont bother with this mesh. + + # done above ^ +# if EXPORT_ROTX90: +# me.transform(ob_mat*mat_xrot90) +# else: +# me.transform(ob_mat) + + # High Quality Normals + if EXPORT_NORMALS and faces: + if EXPORT_NORMALS_HQ: + BPyMesh.meshCalcNormals(me) + else: + # transforming normals is incorrect + # when the matrix is scaled, + # better to recalculate them + me.calcNormals() + + # # Crash Blender + #materials = me.getMaterials(1) # 1 == will return None in the list. + materials = me.materials + + materialNames = [] + materialItems = materials[:] + if materials: + for mat in materials: + if mat: # !=None + materialNames.append(mat.name) + else: + materialNames.append(None) + # Cant use LC because some materials are None. + # materialNames = map(lambda mat: mat.name, materials) # Bug Blender, dosent account for null materials, still broken. + + # Possible there null materials, will mess up indicies + # but at least it will export, wait until Blender gets fixed. + materialNames.extend((16-len(materialNames)) * [None]) + materialItems.extend((16-len(materialItems)) * [None]) + + # Sort by Material, then images + # so we dont over context switch in the obj file. + if EXPORT_KEEP_VERT_ORDER: + pass + elif faceuv: + try: faces.sort(key = lambda a: (a.mat, a.image, a.smooth)) + except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) + elif len(materials) > 1: + try: faces.sort(key = lambda a: (a.mat, a.smooth)) + except: faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth))) + else: + # no materials + try: faces.sort(key = lambda a: a.smooth) + except: faces.sort(lambda a,b: cmp(a.smooth, b.smooth)) + + # Set the default mat to no material and no image. + contextMat = (0, 0) # Can never be this, so we will label a new material teh first chance we get. + contextSmooth = None # Will either be true or false, set bad to force initialization switch. + + if EXPORT_BLEN_OBS or EXPORT_GROUP_BY_OB: + name1 = ob.name + name2 = ob.getData(1) + if name1 == name2: + obnamestring = fixName(name1) + else: + obnamestring = '%s_%s' % (fixName(name1), fixName(name2)) + + if EXPORT_BLEN_OBS: + file.write('o %s\n' % obnamestring) # Write Object name + else: # if EXPORT_GROUP_BY_OB: + file.write('g %s\n' % obnamestring) + + + # Vert + for v in me.verts: + file.write('v %.6f %.6f %.6f\n' % tuple(v.co)) + + # UV + if faceuv: + uv_face_mapping = [[0,0,0,0] for f in faces] # a bit of a waste for tri's :/ + + uv_dict = {} # could use a set() here + for f_index, f in enumerate(faces): + + for uv_index, uv in enumerate(f.uv): + uvkey = veckey2d(uv) + try: + uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] + except: + uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) + file.write('vt %.6f %.6f\n' % tuple(uv)) + + uv_unique_count = len(uv_dict) + del uv, uvkey, uv_dict, f_index, uv_index + # Only need uv_unique_count and uv_face_mapping + + # NORMAL, Smooth/Non smoothed. + if EXPORT_NORMALS: + for f in faces: + if f.smooth: + for v in f: + noKey = veckey3d(v.no) + if not globalNormals.has_key( noKey ): + globalNormals[noKey] = totno + totno +=1 + file.write('vn %.6f %.6f %.6f\n' % noKey) + else: + # Hard, 1 normal from the face. + noKey = veckey3d(f.no) + if not globalNormals.has_key( noKey ): + globalNormals[noKey] = totno + totno +=1 + file.write('vn %.6f %.6f %.6f\n' % noKey) + + if not faceuv: + f_image = None + + if EXPORT_POLYGROUPS: + # Retrieve the list of vertex groups + vertGroupNames = me.getVertGroupNames() + + currentVGroup = '' + # Create a dictionary keyed by face id and listing, for each vertex, the vertex groups it belongs to + vgroupsMap = [[] for _i in xrange(len(me.verts))] + for vertexGroupName in vertGroupNames: + for vIdx, vWeight in me.getVertsFromGroup(vertexGroupName, 1): + vgroupsMap[vIdx].append((vertexGroupName, vWeight)) + + for f_index, f in enumerate(faces): + f_v= f.v + f_smooth= f.smooth + f_mat = min(f.mat, len(materialNames)-1) + if faceuv: + f_image = f.image + f_uv= f.uv + + # MAKE KEY + if faceuv and f_image: # Object is always true. + key = materialNames[f_mat], f_image.name + else: + key = materialNames[f_mat], None # No image, use None instead. + + # Write the vertex group + if EXPORT_POLYGROUPS: + if vertGroupNames: + # find what vertext group the face belongs to + theVGroup = findVertexGroupName(f,vgroupsMap) + if theVGroup != currentVGroup: + currentVGroup = theVGroup + file.write('g %s\n' % theVGroup) + + # CHECK FOR CONTEXT SWITCH + if key == contextMat: + pass # Context alredy switched, dont do anything + else: + if key[0] == None and key[1] == None: + # Write a null material, since we know the context has changed. + if EXPORT_GROUP_BY_MAT: + file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.getData(1))) ) # can be mat_image or (null) + file.write('usemtl (null)\n') # mat, image + + else: + mat_data= MTL_DICT.get(key) + if not mat_data: + # First add to global dict so we can export to mtl + # Then write mtl + + # Make a new names from the mat and image name, + # converting any spaces to underscores with fixName. + + # If none image dont bother adding it to the name + if key[1] == None: + mat_data = MTL_DICT[key] = ('%s'%fixName(key[0])), materialItems[f_mat], f_image + else: + mat_data = MTL_DICT[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image + + if EXPORT_GROUP_BY_MAT: + file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.getData(1)), mat_data[0]) ) # can be mat_image or (null) + + file.write('usemtl %s\n' % mat_data[0]) # can be mat_image or (null) + + contextMat = key + if f_smooth != contextSmooth: + if f_smooth: # on now off + file.write('s 1\n') + contextSmooth = f_smooth + else: # was off now on + file.write('s off\n') + contextSmooth = f_smooth + + file.write('f') + if faceuv: + if EXPORT_NORMALS: + if f_smooth: # Smoothed, use vertex normals + for vi, v in enumerate(f_v): + file.write( ' %d/%d/%d' % (\ + v.index+totverts,\ + totuvco + uv_face_mapping[f_index][vi],\ + globalNormals[ veckey3d(v.no) ])) # vert, uv, normal + + else: # No smoothing, face normals + no = globalNormals[ veckey3d(f.no) ] + for vi, v in enumerate(f_v): + file.write( ' %d/%d/%d' % (\ + v.index+totverts,\ + totuvco + uv_face_mapping[f_index][vi],\ + no)) # vert, uv, normal + + else: # No Normals + for vi, v in enumerate(f_v): + file.write( ' %d/%d' % (\ + v.index+totverts,\ + totuvco + uv_face_mapping[f_index][vi])) # vert, uv + + face_vert_index += len(f_v) + + else: # No UV's + if EXPORT_NORMALS: + if f_smooth: # Smoothed, use vertex normals + for v in f_v: + file.write( ' %d//%d' % (\ + v.index+totverts,\ + globalNormals[ veckey3d(v.no) ])) + else: # No smoothing, face normals + no = globalNormals[ veckey3d(f.no) ] + for v in f_v: + file.write( ' %d//%d' % (\ + v.index+totverts,\ + no)) + else: # No Normals + for v in f_v: + file.write( ' %d' % (\ + v.index+totverts)) + + file.write('\n') + + # Write edges. + if EXPORT_EDGES: + LOOSE= Mesh.EdgeFlags.LOOSE + for ed in edges: + if ed.flag & LOOSE: + file.write('f %d %d\n' % (ed.v1.index+totverts, ed.v2.index+totverts)) + + # Make the indicies global rather then per mesh + totverts += len(me.verts) + if faceuv: + totuvco += uv_unique_count + me.verts= None if ob_main.dupli_type != 'NONE': ob_main.free_dupli_list() diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index 26fb77777d7..0aa4faeddd8 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -54,8 +54,15 @@ void rna_Mesh_copy_applied(Mesh *me, Scene *sce, Object *ob) } */ -void rna_Mesh_transform(Mesh *me, float **mat) +void rna_Mesh_transform(Mesh *me, float *mat) { + /* TODO: old API transform had recalc_normals option */ + int i; + MVert *mvert= me->mvert; + + for(i= 0; i < mesh->totvert; i++, mvert++) { + Mat4MulVecfl(mat, mvert->co); + } } #if 0 @@ -85,14 +92,13 @@ static void rna_Mesh_verts_add(PointerRNA *ptr, PointerRNA *ptr_item) void RNA_api_mesh(StructRNA *srna) { - /*FunctionRNA *func; - PropertyRNA *prop;*/ + FunctionRNA *func; + PropertyRNA *parm; - /* - func= RNA_def_function(srna, "copy", "rna_Mesh_copy"); - RNA_def_function_ui_description(func, "Copy mesh data."); - prop= RNA_def_pointer(func, "src", "Mesh", "", "A mesh to copy data from."); - RNA_def_property_flag(prop, PROP_REQUIRED);*/ + func= RNA_def_function(srna, "transform", "rna_Mesh_transform"); + RNA_def_function_ui_description(func, "Transform mesh vertices by a matrix."); + parm= RNA_def_float_matrix(func, "matrix", 16, NULL, 0.0f, 0.0f, "", "Matrix.", 0.0f, 0.0f); + RNA_def_property_flag(parm, PROP_REQUIRED); /* func= RNA_def_function(srna, "add_geom", "rna_Mesh_add_geom"); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index d566af1954c..14ce5954cb7 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -1028,6 +1028,12 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Dupli Frames Off", "Recurring frames to exclude from the Dupliframes."); RNA_def_property_update(prop, NC_OBJECT|ND_DRAW, "rna_Object_update"); + prop= RNA_def_property(srna, "dupli_list", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "duplilist", NULL); + RNA_def_property_struct_type(prop, "DupliObject"); + RNA_def_property_ui_text(prop, "Dupli list", "Object duplis."); + + /* time offset */ prop= RNA_def_property(srna, "time_offset", PROP_FLOAT, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index c3740921f0f..69d3f48761c 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -45,7 +45,7 @@ #include "DNA_scene_types.h" #include "DNA_meshdata_types.h" -#define OBJECT_API_PROP_DUPLILIST "dupli_list" +#include "BLI_arithb.h" /* copied from init_render_mesh (render code) */ static Mesh *rna_Object_create_render_mesh(Object *ob, bContext *C, ReportList *reports, int apply_matrix, float *matrix) @@ -78,7 +78,7 @@ static Mesh *rna_Object_create_render_mesh(Object *ob, bContext *C, ReportList * dm->release(dm); if (apply_matrix) { - float *mat = ob->obmat; + float *mat = (float*)ob->obmat; if (matrix) { /* apply custom matrix */ @@ -97,33 +97,20 @@ static Mesh *rna_Object_create_render_mesh(Object *ob, bContext *C, ReportList * /* When no longer needed, duplilist should be freed with Object.free_duplilist */ static void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *reports) { - PointerRNA obptr; - PointerRNA dobptr; - Scene *sce; - DupliObject *dob; - PropertyRNA *prop; - if (!(ob->transflag & OB_DUPLI)) { BKE_report(reports, RPT_ERROR, "Object does not have duplis."); return; } - RNA_id_pointer_create(&ob->id, &obptr); + /* free duplilist if a user forget to */ + if (ob->duplilist) { + BKE_report(reports, RPT_WARNING, "%s.dupli_list has not been freed.", RNA_struct_identifier(&RNA_Object)); - if (!(prop= RNA_struct_find_property(&obptr, OBJECT_API_PROP_DUPLILIST))) { - // hint: all Objects will now have this property defined - prop= RNA_def_collection_runtime(obptr.type, OBJECT_API_PROP_DUPLILIST, &RNA_DupliObject, "Dupli list", ""); + free_object_duplilist(ob->duplilist); + ob->duplilist= NULL; } - RNA_property_collection_clear(&obptr, prop); - sce= CTX_data_scene(C); - ob->duplilist= object_duplilist(sce, ob); - - for(dob= (DupliObject*)ob->duplilist->first; dob; dob= dob->next) { - RNA_pointer_create(NULL, &RNA_DupliObject, dob, &dobptr); - RNA_property_collection_add(&obptr, prop, &dobptr); - dob = dob->next; - } + ob->duplilist= object_duplilist(CTX_data_scene(C), ob); /* ob->duplilist should now be freed with Object.free_duplilist */ } @@ -133,15 +120,6 @@ static void rna_Object_free_duplilist(Object *ob, ReportList *reports) PointerRNA obptr; PropertyRNA *prop; - RNA_id_pointer_create(&ob->id, &obptr); - - if (!(prop= RNA_struct_find_property(&obptr, OBJECT_API_PROP_DUPLILIST))) { - BKE_report(reports, RPT_ERROR, "Object has no duplilist property."); - return; - } - - RNA_property_collection_clear(&obptr, prop); - if (ob->duplilist) { free_object_duplilist(ob->duplilist); ob->duplilist= NULL; From 3f2fef55c2bbeb1f837b48c76263e3e035d56792 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Wed, 24 Jun 2009 19:23:34 +0000 Subject: [PATCH 30/68] - added API functions: * Main.remove_object * Scene.add_object * Scene.remove_object * Object.convert_to_triface * Object.create_preview_mesh - a small tweak in set_mesh (blenkernel/inter/mesh.c) to make it work on objects having data == NULL --- release/scripts/export_obj-2.5.py | 20 ++-- source/blender/blenkernel/intern/mesh.c | 3 +- source/blender/makesrna/intern/makesrna.c | 2 +- source/blender/makesrna/intern/rna_main_api.c | 33 ++++++- source/blender/makesrna/intern/rna_mesh_api.c | 3 +- .../blender/makesrna/intern/rna_object_api.c | 93 +++++++++++++------ source/blender/makesrna/intern/rna_scene.c | 2 + .../blender/makesrna/intern/rna_scene_api.c | 91 ++++++++++++++++++ 8 files changed, 205 insertions(+), 42 deletions(-) create mode 100644 source/blender/makesrna/intern/rna_scene_api.c diff --git a/release/scripts/export_obj-2.5.py b/release/scripts/export_obj-2.5.py index 4fa02ce2efe..c0468f744b8 100644 --- a/release/scripts/export_obj-2.5.py +++ b/release/scripts/export_obj-2.5.py @@ -307,7 +307,8 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): temp_mesh_name = '~tmp-mesh' time1 = sys.time() - scn = Scene.GetCurrent() +# scn = Scene.GetCurrent() + scene = context.scene file = open(filename, "w") @@ -383,15 +384,15 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if ob.type != 'MESH': continue - me = ob.data - # XXX # if EXPORT_UV: # faceuv= me.faceUV # else: # faceuv = False - convert_to_tri = False + me = ob.create_render_mesh() + + newob = ob # We have a valid mesh if EXPORT_TRI and me.faces: @@ -403,7 +404,10 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): has_quads = True break - convert_to_tri = has_quads + if has_quads: + newob = bpy.data.add_object('MESH', 'temp_object') + scene.add_object(newob) + newob.convert_to_triface(scene) # oldmode = Mesh.Mode() # Mesh.Mode(Mesh.SelectModes['FACE']) @@ -418,8 +422,6 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if EXPORT_ROTX90: ob_mat *= mat_xrot90 - me = ob.create_render_mesh(True, ob_mat, convert_to_tri) - # Make our own list so it can be sorted to reduce context switching faces = [ f for f in me.faces ] @@ -429,6 +431,10 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): edges = [] if not (len(faces)+len(edges)+len(me.verts)): # Make sure there is somthing to write + + if newob != ob: + scene.remove_object(newob) + continue # dont bother with this mesh. # done above ^ diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 3facf975992..9fc8d0ed609 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -542,7 +542,8 @@ void set_mesh(Object *ob, Mesh *me) if(ob->type==OB_MESH) { old= ob->data; - old->id.us--; + if (old) /* to make set_mesh work on objects created with add_only_object, i.e. having ob->data == NULL */ + old->id.us--; ob->data= me; id_us_plus((ID *)me); } diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index e7ca3fc5932..f7bf176a325 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1853,7 +1853,7 @@ RNAProcessItem PROCESS_ITEMS[]= { {"rna_particle.c", NULL, RNA_def_particle}, {"rna_pose.c", NULL, RNA_def_pose}, {"rna_property.c", NULL, RNA_def_gameproperty}, - {"rna_scene.c", NULL, RNA_def_scene}, + {"rna_scene.c", "rna_scene_api.c", RNA_def_scene}, {"rna_screen.c", NULL, RNA_def_screen}, {"rna_scriptlink.c", NULL, RNA_def_scriptlink}, {"rna_sensor.c", NULL, RNA_def_sensor}, diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 2c78884a027..08a3b7cee25 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -62,7 +62,29 @@ static void rna_Main_remove_mesh(Main *main, ReportList *reports, Mesh *me) static Object* rna_Main_add_object(Main *main, int type, char *name) { - return add_only_object(type, name); + Object *ob= add_only_object(type, name); + ob->id.us--; + return ob; +} + +/* + WARNING: the following example shows when this function should not be called + + ob = bpy.data.add_object() + scene.add_object(ob) + + # ob is freed here + scene.remove_object(ob) + + # don't do this since ob is already freed! + bpy.data.remove_object(ob) +*/ +static void rna_Main_remove_object(Main *main, ReportList *reports, Object *ob) +{ + if(ob->id.us == 0) + free_libblock(&main->object, ob); + else + BKE_report(reports, RPT_ERROR, "Object must have zero users to be removed."); } #else @@ -89,13 +111,19 @@ void RNA_api_main(StructRNA *srna) func= RNA_def_function(srna, "add_object", "rna_Main_add_object"); RNA_def_function_ui_description(func, "Add a new object."); - parm= RNA_def_enum(func, "type", object_type_items, 0, "Type", "Type of Object."); + parm= RNA_def_enum(func, "type", object_type_items, 0, "", "Type of Object."); RNA_def_property_flag(parm, PROP_REQUIRED); parm= RNA_def_string(func, "name", "Object", 0, "", "New name for the datablock."); RNA_def_property_flag(parm, PROP_REQUIRED); parm= RNA_def_pointer(func, "object", "Object", "", "New object."); RNA_def_function_return(func, parm); + func= RNA_def_function(srna, "remove_object", "rna_Main_remove_object"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Remove an object if it has zero users."); + parm= RNA_def_pointer(func, "object", "Object", "", "Object to remove."); + RNA_def_property_flag(parm, PROP_REQUIRED); + func= RNA_def_function(srna, "add_mesh", "rna_Main_add_mesh"); RNA_def_function_ui_description(func, "Add a new mesh."); parm= RNA_def_string(func, "name", "Mesh", 0, "", "New name for the datablock."); @@ -108,7 +136,6 @@ void RNA_api_main(StructRNA *srna) RNA_def_function_ui_description(func, "Remove a mesh if it has zero users."); parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh to remove."); RNA_def_property_flag(parm, PROP_REQUIRED); - } #endif diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index 0aa4faeddd8..984e6a5d30f 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -36,6 +36,7 @@ #include "BKE_customdata.h" #include "BKE_DerivedMesh.h" +#include "BLI_arithb.h" #include "DNA_mesh_types.h" #include "DNA_scene_types.h" @@ -60,7 +61,7 @@ void rna_Mesh_transform(Mesh *me, float *mat) int i; MVert *mvert= me->mvert; - for(i= 0; i < mesh->totvert; i++, mvert++) { + for(i= 0; i < me->totvert; i++, mvert++) { Mat4MulVecfl(mat, mvert->co); } } diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 69d3f48761c..5af12b696c4 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -28,6 +28,8 @@ #include #include +#include +#include #include "RNA_define.h" #include "RNA_types.h" @@ -40,6 +42,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_anim.h" #include "BKE_report.h" +#include "BKE_depsgraph.h" #include "DNA_mesh_types.h" #include "DNA_scene_types.h" @@ -47,15 +50,17 @@ #include "BLI_arithb.h" +#include "BLO_sys_types.h" /* needed for intptr_t used in ED_mesh.h */ + +#include "ED_mesh.h" + /* copied from init_render_mesh (render code) */ -static Mesh *rna_Object_create_render_mesh(Object *ob, bContext *C, ReportList *reports, int apply_matrix, float *matrix) +static Mesh *create_mesh(Object *ob, bContext *C, ReportList *reports, int render_mesh) { CustomDataMask mask = CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; DerivedMesh *dm; Mesh *me; Scene *sce; - int a; - MVert *mvert; sce= CTX_data_scene(C); @@ -65,7 +70,7 @@ static Mesh *rna_Object_create_render_mesh(Object *ob, bContext *C, ReportList * return NULL; } - dm= mesh_create_derived_render(sce, ob, mask); + dm= render_mesh ? mesh_create_derived_render(sce, ob, mask) : mesh_create_derived_view(sce, ob, mask); if(!dm) { /* TODO: report */ @@ -77,23 +82,19 @@ static Mesh *rna_Object_create_render_mesh(Object *ob, bContext *C, ReportList * DM_to_mesh(dm, me); dm->release(dm); - if (apply_matrix) { - float *mat = (float*)ob->obmat; - - if (matrix) { - /* apply custom matrix */ - mat = matrix; - } - - /* is this really that simple? :) */ - for(a= 0, mvert= me->mvert; a < me->totvert; a++, mvert++) { - Mat4MulVecfl(ob->obmat, mvert->co); - } - } - return me; } +static Mesh *rna_Object_create_render_mesh(Object *ob, bContext *C, ReportList *reports) +{ + return create_mesh(ob, C, reports, 1); +} + +static Mesh *rna_Object_create_preview_mesh(Object *ob, bContext *C, ReportList *reports) +{ + return create_mesh(ob, C, reports, 0); +} + /* When no longer needed, duplilist should be freed with Object.free_duplilist */ static void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *reports) { @@ -102,9 +103,9 @@ static void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *rep return; } - /* free duplilist if a user forget to */ + /* free duplilist if a user forgets to */ if (ob->duplilist) { - BKE_report(reports, RPT_WARNING, "%s.dupli_list has not been freed.", RNA_struct_identifier(&RNA_Object)); + BKE_reportf(reports, RPT_WARNING, "%s.dupli_list has not been freed.", RNA_struct_identifier(&RNA_Object)); free_object_duplilist(ob->duplilist); ob->duplilist= NULL; @@ -117,15 +118,41 @@ static void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *rep static void rna_Object_free_duplilist(Object *ob, ReportList *reports) { - PointerRNA obptr; - PropertyRNA *prop; - if (ob->duplilist) { free_object_duplilist(ob->duplilist); ob->duplilist= NULL; } } +static void rna_Object_convert_to_triface(Object *ob, bContext *C, ReportList *reports, Scene *sce) +{ + Mesh *me; + int ob_editing = CTX_data_edit_object(C) == ob; + + if (ob->type != OB_MESH) { + BKE_report(reports, RPT_ERROR, "Object should be of type MESH."); + return; + } + + me= (Mesh*)ob->data; + + if (!ob_editing) + make_editMesh(sce, ob); + + /* select all */ + EM_set_flag_all(me->edit_mesh, SELECT); + + convert_to_triface(me->edit_mesh, 0); + + load_editMesh(sce, ob); + + if (!ob_editing) + free_editMesh(me->edit_mesh); + + DAG_object_flush_update(sce, ob, OB_RECALC_DATA); +} + + #else void RNA_api_object(StructRNA *srna) @@ -149,22 +176,30 @@ void RNA_api_object(StructRNA *srna) {0, NULL, 0, NULL, NULL}}; func= RNA_def_function(srna, "create_render_mesh", "rna_Object_create_render_mesh"); - RNA_def_function_ui_description(func, "Create a Mesh datablock with all modifiers applied."); + RNA_def_function_ui_description(func, "Create a Mesh datablock with all modifiers applied for rendering."); + RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); + parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object, remove it if it is only used for export."); + RNA_def_function_return(func, parm); + + func= RNA_def_function(srna, "create_preview_mesh", "rna_Object_create_preview_mesh"); + RNA_def_function_ui_description(func, "Create a Mesh datablock with all modifiers applied for preview."); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); - parm= RNA_def_boolean(func, "apply_matrix", 0, "", "True if object matrix or custom matrix should be applied to geometry."); - RNA_def_property_clear_flag(parm, PROP_REQUIRED); - parm= RNA_def_float_matrix(func, "custom_matrix", 16, NULL, 0.0f, 0.0f, "", "Optional custom matrix to apply.", 0.0f, 0.0f); - RNA_def_property_clear_flag(parm, PROP_REQUIRED); parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object, remove it if it is only used for export."); RNA_def_function_return(func, parm); func= RNA_def_function(srna, "create_dupli_list", "rna_Object_create_duplilist"); - RNA_def_function_ui_description(func, "Create a list of dupli objects for this object. When no longer needed, it should be freed with free_dupli_list."); + RNA_def_function_ui_description(func, "Create a list of dupli objects for this object, needs to be freed manually with free_dupli_list."); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); func= RNA_def_function(srna, "free_dupli_list", "rna_Object_free_duplilist"); RNA_def_function_ui_description(func, "Free the list of dupli objects."); RNA_def_function_flag(func, FUNC_USE_REPORTS); + + func= RNA_def_function(srna, "convert_to_triface", "rna_Object_convert_to_triface"); + RNA_def_function_ui_description(func, "Convert all mesh faces to triangles."); + RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); + parm= RNA_def_pointer(func, "scene", "Scene", "", "Scene where the object is."); + RNA_def_property_flag(parm, PROP_REQUIRED); } #endif diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 47c9025149a..1017703c56b 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1018,6 +1018,8 @@ void RNA_def_scene(BlenderRNA *brna) rna_def_tool_settings(brna); rna_def_scene_render_data(brna); + + RNA_api_scene(srna); } #endif diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c new file mode 100644 index 00000000000..a38622b48ae --- /dev/null +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -0,0 +1,91 @@ +/** + * $Id: rna_object_api.c 21115 2009-06-23 19:17:59Z kazanbas $ + * + * ***** 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) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include + +#include "RNA_define.h" +#include "RNA_types.h" + +#include "DNA_object_types.h" + +#ifdef RNA_RUNTIME + +#include "BKE_scene.h" +#include "ED_object.h" + +static void rna_Scene_add_object(Scene *sce, ReportList *reports, Object *ob) +{ + Base *base= object_in_scene(ob, sce); + if (base) { + BKE_report(reports, RPT_ERROR, "Object is already in this scene."); + return; + } + base= scene_add_base(sce, ob); + ob->id.us++; + + /* this is similar to what object_add_type and add_object do */ + ob->lay= base->lay= sce->lay; + ob->recalc |= OB_RECALC; + + DAG_scene_sort(sce); +} + +static void rna_Scene_remove_object(Scene *sce, ReportList *reports, Object *ob) +{ + Base *base= object_in_scene(ob, sce); + if (!base) { + BKE_report(reports, RPT_ERROR, "Object is not in this scene."); + return; + } + /* as long as ED_base_object_free_and_unlink calls free_libblock_us, we don't have to decrement ob->id.us */ + ED_base_object_free_and_unlink(sce, base); +} + +#else + +void RNA_api_scene(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *parm; + + func= RNA_def_function(srna, "add_object", "rna_Scene_add_object"); + RNA_def_function_ui_description(func, "Add object to scene."); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm= RNA_def_pointer(func, "object", "Object", "", "Object to add to scene."); + RNA_def_property_flag(parm, PROP_REQUIRED); + + func= RNA_def_function(srna, "remove_object", "rna_Scene_remove_object"); + RNA_def_function_ui_description(func, "Remove object from scene."); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm= RNA_def_pointer(func, "object", "Object", "", "Object to remove from scene."); + RNA_def_property_flag(parm, PROP_REQUIRED); +} + +#endif + From e2e139c0ba91f6218af04748ab08fcb8dd1a9425 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 25 Jun 2009 13:33:21 +0000 Subject: [PATCH 31/68] Added Mesh.active_uv_layer read-only property. --- source/blender/makesrna/intern/rna_mesh.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index da90b9f4c76..00af79890be 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -234,6 +234,23 @@ static int rna_Mesh_uv_layers_length(PointerRNA *ptr) return rna_CustomDataLayer_length(ptr, CD_MTFACE); } +static PointerRNA rna_Mesh_active_uv_layer_get(PointerRNA *ptr) +{ + CustomDataLayer *active_layer; + Mesh *me; + int layer_index; + + active_layer= NULL; + me= (Mesh*)ptr->data; + + if (CustomData_has_layer(&me->fdata, CD_MTFACE)) { + layer_index= CustomData_get_active_layer_index(&me->fdata, CD_MTFACE); + active_layer= &me->fdata.layers[layer_index]; + } + + return rna_pointer_inherit_refine(ptr, &RNA_MeshTextureFaceLayer, active_layer); +} + static void rna_MeshTextureFace_uv1_get(PointerRNA *ptr, float *values) { MTFace *mtface= (MTFace*)ptr->data; @@ -1081,6 +1098,11 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_struct_type(prop, "MeshTextureFaceLayer"); RNA_def_property_ui_text(prop, "UV Layers", ""); + prop= RNA_def_property(srna, "active_uv_layer", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "MeshTextureFaceLayer"); + RNA_def_property_pointer_funcs(prop, "rna_Mesh_active_uv_layer_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Active UV layer", "Active UV layer."); + prop= RNA_def_property(srna, "vcol_layers", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "fdata.layers", "fdata.totlayer"); RNA_def_property_collection_funcs(prop, "rna_Mesh_vcol_layers_begin", 0, 0, 0, "rna_Mesh_vcol_layers_length", 0, 0, 0, 0); From 18e7ec8463930947cfa9d76e7385be0b81482627 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 25 Jun 2009 18:04:32 +0000 Subject: [PATCH 32/68] - added MeshFace.normal property - continuing OBJ exporter conversion --- release/scripts/export_obj-2.5.py | 163 ++++++++++++------ source/blender/makesrna/intern/rna_mesh.c | 29 ++++ .../blender/makesrna/intern/rna_object_api.c | 2 +- 3 files changed, 140 insertions(+), 54 deletions(-) diff --git a/release/scripts/export_obj-2.5.py b/release/scripts/export_obj-2.5.py index c0468f744b8..de1657402b3 100644 --- a/release/scripts/export_obj-2.5.py +++ b/release/scripts/export_obj-2.5.py @@ -384,16 +384,13 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if ob.type != 'MESH': continue - # XXX -# if EXPORT_UV: -# faceuv= me.faceUV -# else: -# faceuv = False + if EXPORT_UV: + faceuv = len(me.uv_layers) > 0 + else: + faceuv = False me = ob.create_render_mesh() - newob = ob - # We have a valid mesh if EXPORT_TRI and me.faces: # Add a dummy object to it. @@ -408,6 +405,8 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): newob = bpy.data.add_object('MESH', 'temp_object') scene.add_object(newob) newob.convert_to_triface(scene) + # me will still be there + scene.remove_object(newob) # oldmode = Mesh.Mode() # Mesh.Mode(Mesh.SelectModes['FACE']) @@ -423,35 +422,38 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): ob_mat *= mat_xrot90 # Make our own list so it can be sorted to reduce context switching - faces = [ f for f in me.faces ] + face_index_pairs = [ (face, index) for index, face in enumerate(me.faces)] + # faces = [ f for f in me.faces ] if EXPORT_EDGES: edges = me.edges else: edges = [] - - if not (len(faces)+len(edges)+len(me.verts)): # Make sure there is somthing to write - if newob != ob: - scene.remove_object(newob) + if not (len(face_index_pairs)+len(edges)+len(me.verts)): # Make sure there is somthing to write +# if not (len(faces)+len(edges)+len(me.verts)): # Make sure there is somthing to write + + bpy.data.remove_mesh(me) continue # dont bother with this mesh. - # done above ^ -# if EXPORT_ROTX90: -# me.transform(ob_mat*mat_xrot90) -# else: -# me.transform(ob_mat) + if EXPORT_ROTX90: + me.transform(ob_mat*mat_xrot90) + else: + me.transform(ob_mat) # High Quality Normals - if EXPORT_NORMALS and faces: - if EXPORT_NORMALS_HQ: - BPyMesh.meshCalcNormals(me) - else: - # transforming normals is incorrect - # when the matrix is scaled, - # better to recalculate them - me.calcNormals() + if EXPORT_NORMALS and face_index_pairs: +# if EXPORT_NORMALS and faces: + # XXX + pass +# if EXPORT_NORMALS_HQ: +# BPyMesh.meshCalcNormals(me) +# else: +# # transforming normals is incorrect +# # when the matrix is scaled, +# # better to recalculate them +# me.calcNormals() # # Crash Blender #materials = me.getMaterials(1) # 1 == will return None in the list. @@ -478,15 +480,35 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if EXPORT_KEEP_VERT_ORDER: pass elif faceuv: - try: faces.sort(key = lambda a: (a.mat, a.image, a.smooth)) - except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) + # XXX update + tface = me.active_uv_layer.data + + # exception only raised if Python 2.3 or lower... + try: face_index_pairs.sort(key = lambda a: (a[0].material_index, tface[a[1]].image, a[0].smooth)) + except: face_index_pairs.sort(lambda a,b: cmp((a[0].material_index, tface[a[1]].image, a[0].smooth), + (b[0].material_index, tface[b[1]].image, b[0].smooth))) elif len(materials) > 1: - try: faces.sort(key = lambda a: (a.mat, a.smooth)) - except: faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth))) + try: face_index_pairs.sort(key = lambda a: (a[0].material_index, a[0].smooth)) + except: face_index_pairs.sort(lambda a,b: cmp((a[0].material_index, a[0].smooth), + (b[0].material_index, b[0].smooth))) else: # no materials - try: faces.sort(key = lambda a: a.smooth) - except: faces.sort(lambda a,b: cmp(a.smooth, b.smooth)) + try: face_index_pairs.sort(key = lambda a: a[0].smooth) + except: face_index_pairs.sort(lambda a,b: cmp(a[0].smooth, b[0].smooth)) +# if EXPORT_KEEP_VERT_ORDER: +# pass +# elif faceuv: +# try: faces.sort(key = lambda a: (a.mat, a.image, a.smooth)) +# except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) +# elif len(materials) > 1: +# try: faces.sort(key = lambda a: (a.mat, a.smooth)) +# except: faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth))) +# else: +# # no materials +# try: faces.sort(key = lambda a: a.smooth) +# except: faces.sort(lambda a,b: cmp(a.smooth, b.smooth)) + + faces = [pair[0] for pair in face_index_pairs] # Set the default mat to no material and no image. contextMat = (0, 0) # Can never be this, so we will label a new material teh first chance we get. @@ -494,7 +516,8 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if EXPORT_BLEN_OBS or EXPORT_GROUP_BY_OB: name1 = ob.name - name2 = ob.getData(1) + name2 = ob.data.name + # name2 = ob.getData(1) if name1 == name2: obnamestring = fixName(name1) else: @@ -513,17 +536,37 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): # UV if faceuv: uv_face_mapping = [[0,0,0,0] for f in faces] # a bit of a waste for tri's :/ - + uv_dict = {} # could use a set() here - for f_index, f in enumerate(faces): - - for uv_index, uv in enumerate(f.uv): + uv_layer = me.active_uv_layer + for f, f_index in face_index_pairs: + + tface = uv_layer.data[f_index] + + uvs = [tface.uv1, tface.uv2, tface.uv3] + + # add another UV if it's a quad + if tface.verts[3] != 0: + uvs.append(tface.uv4) + + for uv_index, uv in enumerate(uvs): uvkey = veckey2d(uv) try: uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] except: uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) file.write('vt %.6f %.6f\n' % tuple(uv)) + +# uv_dict = {} # could use a set() here +# for f_index, f in enumerate(faces): + +# for uv_index, uv in enumerate(f.uv): +# uvkey = veckey2d(uv) +# try: +# uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] +# except: +# uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) +# file.write('vt %.6f %.6f\n' % tuple(uv)) uv_unique_count = len(uv_dict) del uv, uvkey, uv_dict, f_index, uv_index @@ -534,14 +577,16 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): for f in faces: if f.smooth: for v in f: - noKey = veckey3d(v.no) + noKey = veckey3d(v.normal) +# noKey = veckey3d(v.no) if not globalNormals.has_key( noKey ): globalNormals[noKey] = totno totno +=1 file.write('vn %.6f %.6f %.6f\n' % noKey) else: # Hard, 1 normal from the face. - noKey = veckey3d(f.no) + noKey = veckey3d(f.normal) +# noKey = veckey3d(f.no) if not globalNormals.has_key( noKey ): globalNormals[noKey] = totno totno +=1 @@ -549,14 +594,17 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if not faceuv: f_image = None - + + # XXX if EXPORT_POLYGROUPS: # Retrieve the list of vertex groups - vertGroupNames = me.getVertGroupNames() + vertGroupNames = [g.name for g in ob.vertex_groups] +# vertGroupNames = me.getVertGroupNames() currentVGroup = '' # Create a dictionary keyed by face id and listing, for each vertex, the vertex groups it belongs to - vgroupsMap = [[] for _i in xrange(len(me.verts))] + vgroupsMap = [[] for _i in range(len(me.verts))] +# vgroupsMap = [[] for _i in xrange(len(me.verts))] for vertexGroupName in vertGroupNames: for vIdx, vWeight in me.getVertsFromGroup(vertexGroupName, 1): vgroupsMap[vIdx].append((vertexGroupName, vWeight)) @@ -564,25 +612,34 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): for f_index, f in enumerate(faces): f_v= f.v f_smooth= f.smooth - f_mat = min(f.mat, len(materialNames)-1) + f_mat = min(f.material_index, len(materialNames)-1) +# f_mat = min(f.mat, len(materialNames)-1) if faceuv: - f_image = f.image - f_uv= f.uv + + tface = me.active_uv_layer.data[face_index_pairs[f_index][1]] + + f_image = tface.image + f_uv= [tface.uv1, tface.uv2, tface.uv3] + if f.verts[4] != 0: + f_uv.append(tface.uv4) +# f_image = f.image +# f_uv= f.uv # MAKE KEY if faceuv and f_image: # Object is always true. key = materialNames[f_mat], f_image.name else: key = materialNames[f_mat], None # No image, use None instead. - - # Write the vertex group - if EXPORT_POLYGROUPS: - if vertGroupNames: - # find what vertext group the face belongs to - theVGroup = findVertexGroupName(f,vgroupsMap) - if theVGroup != currentVGroup: - currentVGroup = theVGroup - file.write('g %s\n' % theVGroup) + + # XXX +# # Write the vertex group +# if EXPORT_POLYGROUPS: +# if vertGroupNames: +# # find what vertext group the face belongs to +# theVGroup = findVertexGroupName(f,vgroupsMap) +# if theVGroup != currentVGroup: +# currentVGroup = theVGroup +# file.write('g %s\n' % theVGroup) # CHECK FOR CONTEXT SWITCH if key == contextMat: diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 00af79890be..3127c352f60 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -38,6 +38,9 @@ #ifdef RNA_RUNTIME +#include "BLI_arithb.h" /* CalcNormFloat */ + + static void rna_MeshVertex_normal_get(PointerRNA *ptr, float *value) { MVert *mvert= (MVert*)ptr->data; @@ -183,6 +186,26 @@ static void rna_MeshFace_material_index_range(PointerRNA *ptr, int *min, int *ma *max= me->totcol-1; } +static void rna_MeshFace_normal_get(PointerRNA *ptr, float *value) +{ + float *vert[4]; + MFace *face = (MFace*)ptr->data; + Mesh *me= (Mesh*)ptr->id.data; + + vert[0] = me->mvert[face->v1].co; + vert[1] = me->mvert[face->v2].co; + vert[2] = me->mvert[face->v3].co; + + /* copied from MFace_getNormal (old python API) */ + if (face->v4) { + vert[3] = me->mvert[face->v4].co; + CalcNormFloat4(vert[0], vert[1], vert[2], vert[3], value); + } + else { + CalcNormFloat(vert[0], vert[1], vert[2], value); + } +} + static int rna_CustomDataLayer_length(PointerRNA *ptr, int type) { Mesh *me= (Mesh*)ptr->id.data; @@ -749,6 +772,12 @@ static void rna_def_mface(BlenderRNA *brna) prop= RNA_def_property(srna, "smooth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_SMOOTH); RNA_def_property_ui_text(prop, "Smooth", ""); + + prop= RNA_def_property(srna, "normal", PROP_FLOAT, PROP_VECTOR); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_MeshFace_normal_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Normal", "Face normal"); } static void rna_def_mtface(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 5af12b696c4..40f29360e9c 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -198,7 +198,7 @@ void RNA_api_object(StructRNA *srna) func= RNA_def_function(srna, "convert_to_triface", "rna_Object_convert_to_triface"); RNA_def_function_ui_description(func, "Convert all mesh faces to triangles."); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); - parm= RNA_def_pointer(func, "scene", "Scene", "", "Scene where the object is."); + parm= RNA_def_pointer(func, "scene", "Scene", "", "Scene where the object belongs."); RNA_def_property_flag(parm, PROP_REQUIRED); } From acb590e4b065505117a78786e2627c43e65c499a Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Fri, 26 Jun 2009 12:33:07 +0000 Subject: [PATCH 33/68] * OBJ exporter almost converted * added MeshEdge.loose property * changed mask used in Object.create_*_mesh to CD_MASK_MESH. --- release/scripts/export_obj-2.5.py | 489 +++++------------- source/blender/makesrna/intern/rna_mesh.c | 5 + .../blender/makesrna/intern/rna_object_api.c | 4 +- 3 files changed, 135 insertions(+), 363 deletions(-) diff --git a/release/scripts/export_obj-2.5.py b/release/scripts/export_obj-2.5.py index de1657402b3..cd8e423ed07 100644 --- a/release/scripts/export_obj-2.5.py +++ b/release/scripts/export_obj-2.5.py @@ -47,7 +47,7 @@ will be exported as mesh data. import bpy -import BPySys +# import BPySys # import Blender # from Blender import Mesh, Scene, Window, sys, Image, Draw @@ -75,16 +75,20 @@ def fixName(name): # (material.name, image.name):matname_imagename # matname_imagename has gaps removed. MTL_DICT = {} -def write_mtl(filename): - - world = Blender.World.GetCurrent() - if world: - worldAmb = world.getAmb() - else: - worldAmb = (0,0,0) # Default value +def write_mtl(scene, filename): + + world = bpy.data.worlds[0] + worldAmb = world.ambient_color + +# world = Blender.World.GetCurrent() +# if world: +# worldAmb = world.getAmb() +# else: +# worldAmb = (0,0,0) # Default value file = open(filename, "w") - file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1]) + # XXX +# file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1]) file.write('# Material Count: %i\n' % len(MTL_DICT)) # Write material/image combinations we have used. for key, (mtl_mat_name, mat, img) in MTL_DICT.iteritems(): @@ -261,7 +265,7 @@ def write_nurb(file, ob, ob_mat): return tot_verts -def write(filename, objects,\ +def write(filename, objects, scene, \ EXPORT_TRI=False, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_NORMALS_HQ=False,\ EXPORT_UV=True, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False,\ EXPORT_APPLY_MODIFIERS=True, EXPORT_ROTX90=True, EXPORT_BLEN_OBS=True,\ @@ -290,8 +294,10 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): of vertices is the face's group """ weightDict = {} - for vert in face: - vWeights = vWeightMap[vert.index] + for vert_index in face.verts: +# for vert in face: + vWeights = vWeightMap[vert_index] +# vWeights = vWeightMap[vert] for vGroupName, weight in vWeights: weightDict[vGroupName] = weightDict.get(vGroupName, 0) + weight @@ -302,6 +308,17 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): else: return '(null)' + # TODO: implement this in C? dunno how it should be called... + def getVertsFromGroup(me, group_index): + ret = [] + + for i, v in enumerate(me.verts): + for g in v.groups: + if g.group == group.index: + ret.append((i, g.weight)) + + return ret + print 'OBJ Export path: "%s"' % filename temp_mesh_name = '~tmp-mesh' @@ -349,6 +366,7 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): globalNormals = {} + # Get all meshes for ob_main in objects: if ob_main.dupli_type != 'NONE': @@ -375,22 +393,25 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): # continue # end nurbs - + + if ob.type != 'MESH': + continue + + # XXX EXPORT_APPLY_MODIFIERS is not used (always true) + # we also need influences to be copied... for EXPORT_POLYGROUPS to work + # which create_preview_mesh presumably does (CD_MASK_MDEFORMVERT flag) + me = ob.create_preview_mesh() + # # Will work for non meshes now! :) # me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, EXPORT_POLYGROUPS, scn) # if not me: # continue - if ob.type != 'MESH': - continue - if EXPORT_UV: faceuv = len(me.uv_layers) > 0 else: faceuv = False - me = ob.create_render_mesh() - # We have a valid mesh if EXPORT_TRI and me.faces: # Add a dummy object to it. @@ -598,19 +619,21 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): # XXX if EXPORT_POLYGROUPS: # Retrieve the list of vertex groups - vertGroupNames = [g.name for g in ob.vertex_groups] # vertGroupNames = me.getVertGroupNames() currentVGroup = '' # Create a dictionary keyed by face id and listing, for each vertex, the vertex groups it belongs to vgroupsMap = [[] for _i in range(len(me.verts))] # vgroupsMap = [[] for _i in xrange(len(me.verts))] - for vertexGroupName in vertGroupNames: - for vIdx, vWeight in me.getVertsFromGroup(vertexGroupName, 1): - vgroupsMap[vIdx].append((vertexGroupName, vWeight)) + for g in ob.vertex_groups: +# for vertexGroupName in vertGroupNames: + for vIdx, vWeight in getVertsFromGroup(me, g.index) +# for vIdx, vWeight in me.getVertsFromGroup(vertexGroupName, 1): + vgroupsMap[vIdx].append((g.name, vWeight)) for f_index, f in enumerate(faces): - f_v= f.v + f_v = [{"index": index, "vertex": me.verts[index]} for index in f.verts] +# f_v= f.v f_smooth= f.smooth f_mat = min(f.material_index, len(materialNames)-1) # f_mat = min(f.mat, len(materialNames)-1) @@ -632,6 +655,14 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): key = materialNames[f_mat], None # No image, use None instead. # XXX + # Write the vertex group + if EXPORT_POLYGROUPS: + if len(ob.vertex_groups): + # find what vertext group the face belongs to + theVGroup = findVertexGroupName(f,vgroupsMap) + if theVGroup != currentVGroup: + currentVGroup = theVGroup + file.write('g %s\n' % theVGroup) # # Write the vertex group # if EXPORT_POLYGROUPS: # if vertGroupNames: @@ -648,7 +679,9 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if key[0] == None and key[1] == None: # Write a null material, since we know the context has changed. if EXPORT_GROUP_BY_MAT: - file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.getData(1))) ) # can be mat_image or (null) + # can be mat_image or (null) + file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.data.name)) ) +# file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.getData(1))) ) # can be mat_image or (null) file.write('usemtl (null)\n') # mat, image else: @@ -667,7 +700,8 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): mat_data = MTL_DICT[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image if EXPORT_GROUP_BY_MAT: - file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.getData(1)), mat_data[0]) ) # can be mat_image or (null) + file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.data.name), mat_data[0]) ) # can be mat_image or (null) +# file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.getData(1)), mat_data[0]) ) # can be mat_image or (null) file.write('usemtl %s\n' % mat_data[0]) # can be mat_image or (null) @@ -685,24 +719,36 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if EXPORT_NORMALS: if f_smooth: # Smoothed, use vertex normals for vi, v in enumerate(f_v): - file.write( ' %d/%d/%d' % (\ - v.index+totverts,\ - totuvco + uv_face_mapping[f_index][vi],\ - globalNormals[ veckey3d(v.no) ])) # vert, uv, normal + file.write( ' %d/%d/%d' % \ + (v["index"] + totverts, + totuvco + uv_face_mapping[f_index][vi], + globalNormals[ veckey3d(v["vertex"].normal) ]) ) # vert, uv, normal +# file.write( ' %d/%d/%d' % (\ +# v.index+totverts,\ +# totuvco + uv_face_mapping[f_index][vi],\ +# globalNormals[ veckey3d(v.no) ])) # vert, uv, normal else: # No smoothing, face normals - no = globalNormals[ veckey3d(f.no) ] + no = globalNormals[ veckey3d(f.normal) ] +# no = globalNormals[ veckey3d(f.no) ] for vi, v in enumerate(f_v): - file.write( ' %d/%d/%d' % (\ - v.index+totverts,\ - totuvco + uv_face_mapping[f_index][vi],\ - no)) # vert, uv, normal + file.write( ' %d/%d/%d' % \ + (v["index"] + totverts, + totuvco + uv_face_mapping[f_index][vi], + no) ) # vert, uv, normal +# file.write( ' %d/%d/%d' % (\ +# v.index+totverts,\ +# totuvco + uv_face_mapping[f_index][vi],\ +# no)) # vert, uv, normal else: # No Normals for vi, v in enumerate(f_v): file.write( ' %d/%d' % (\ - v.index+totverts,\ + v["index"] + totverts,\ totuvco + uv_face_mapping[f_index][vi])) # vert, uv +# file.write( ' %d/%d' % (\ +# v.index+totverts,\ +# totuvco + uv_face_mapping[f_index][vi])) # vert, uv face_vert_index += len(f_v) @@ -710,344 +756,56 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if EXPORT_NORMALS: if f_smooth: # Smoothed, use vertex normals for v in f_v: - file.write( ' %d//%d' % (\ - v.index+totverts,\ - globalNormals[ veckey3d(v.no) ])) + file.write( ' %d//%d' % + (v["index"] + totverts, globalNormals[ veckey3d(v["vertex"].normal) ]) ) + +# file.write( ' %d//%d' % (\ +# v.index+totverts,\ +# globalNormals[ veckey3d(v.no) ])) else: # No smoothing, face normals - no = globalNormals[ veckey3d(f.no) ] + no = globalNormals[ veckey3d(f.normal) ] +# no = globalNormals[ veckey3d(f.no) ] for v in f_v: - file.write( ' %d//%d' % (\ - v.index+totverts,\ - no)) + file.write( ' %d//%d' % (v["index"] + totverts, no) ) +# file.write( ' %d//%d' % (\ +# v.index+totverts,\ +# no)) else: # No Normals for v in f_v: - file.write( ' %d' % (\ - v.index+totverts)) + file.write( ' %d' % (v["index"] + totverts) ) +# file.write( ' %d' % (\ +# v.index+totverts)) file.write('\n') # Write edges. if EXPORT_EDGES: - LOOSE= Mesh.EdgeFlags.LOOSE for ed in edges: - if ed.flag & LOOSE: - file.write('f %d %d\n' % (ed.v1.index+totverts, ed.v2.index+totverts)) + if ed.loose: + file.write('f %d %d\n' % (ed.verts[0] + totverts, ed.verts[1] + totverts)) +# LOOSE= Mesh.EdgeFlags.LOOSE +# for ed in edges: +# if ed.flag & LOOSE: +# file.write('f %d %d\n' % (ed.v1.index+totverts, ed.v2.index+totverts)) # Make the indicies global rather then per mesh totverts += len(me.verts) if faceuv: totuvco += uv_unique_count - me.verts= None + + # clean up + bpy.data.remove_mesh(me) +# me.verts= None if ob_main.dupli_type != 'NONE': ob_main.free_dupli_list() - - # Get all meshes - for ob_main in objects: - for ob, ob_mat in BPyObject.getDerivedObjects(ob_main): - - # Nurbs curve support - if EXPORT_CURVE_AS_NURBS and test_nurbs_compat(ob): - if EXPORT_ROTX90: - ob_mat = ob_mat * mat_xrot90 - - totverts += write_nurb(file, ob, ob_mat) - - continue - # end nurbs - - # Will work for non meshes now! :) - # getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None) - me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, EXPORT_POLYGROUPS, scn) - if not me: - continue - - if EXPORT_UV: - faceuv= me.faceUV - else: - faceuv = False - - # We have a valid mesh - if EXPORT_TRI and me.faces: - # Add a dummy object to it. - has_quads = False - for f in me.faces: - if len(f) == 4: - has_quads = True - break - - if has_quads: - oldmode = Mesh.Mode() - Mesh.Mode(Mesh.SelectModes['FACE']) - - me.sel = True - tempob = scn.objects.new(me) - me.quadToTriangle(0) # more=0 shortest length - oldmode = Mesh.Mode(oldmode) - scn.objects.unlink(tempob) - - Mesh.Mode(oldmode) - - # Make our own list so it can be sorted to reduce context switching - faces = [ f for f in me.faces ] - - if EXPORT_EDGES: - edges = me.edges - else: - edges = [] - - if not (len(faces)+len(edges)+len(me.verts)): # Make sure there is somthing to write - continue # dont bother with this mesh. - - if EXPORT_ROTX90: - me.transform(ob_mat*mat_xrot90) - else: - me.transform(ob_mat) - - # High Quality Normals - if EXPORT_NORMALS and faces: - if EXPORT_NORMALS_HQ: - BPyMesh.meshCalcNormals(me) - else: - # transforming normals is incorrect - # when the matrix is scaled, - # better to recalculate them - me.calcNormals() - - # # Crash Blender - #materials = me.getMaterials(1) # 1 == will return None in the list. - materials = me.materials - - materialNames = [] - materialItems = materials[:] - if materials: - for mat in materials: - if mat: # !=None - materialNames.append(mat.name) - else: - materialNames.append(None) - # Cant use LC because some materials are None. - # materialNames = map(lambda mat: mat.name, materials) # Bug Blender, dosent account for null materials, still broken. - - # Possible there null materials, will mess up indicies - # but at least it will export, wait until Blender gets fixed. - materialNames.extend((16-len(materialNames)) * [None]) - materialItems.extend((16-len(materialItems)) * [None]) - - # Sort by Material, then images - # so we dont over context switch in the obj file. - if EXPORT_KEEP_VERT_ORDER: - pass - elif faceuv: - try: faces.sort(key = lambda a: (a.mat, a.image, a.smooth)) - except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) - elif len(materials) > 1: - try: faces.sort(key = lambda a: (a.mat, a.smooth)) - except: faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth))) - else: - # no materials - try: faces.sort(key = lambda a: a.smooth) - except: faces.sort(lambda a,b: cmp(a.smooth, b.smooth)) - - # Set the default mat to no material and no image. - contextMat = (0, 0) # Can never be this, so we will label a new material teh first chance we get. - contextSmooth = None # Will either be true or false, set bad to force initialization switch. - - if EXPORT_BLEN_OBS or EXPORT_GROUP_BY_OB: - name1 = ob.name - name2 = ob.getData(1) - if name1 == name2: - obnamestring = fixName(name1) - else: - obnamestring = '%s_%s' % (fixName(name1), fixName(name2)) - - if EXPORT_BLEN_OBS: - file.write('o %s\n' % obnamestring) # Write Object name - else: # if EXPORT_GROUP_BY_OB: - file.write('g %s\n' % obnamestring) - - - # Vert - for v in me.verts: - file.write('v %.6f %.6f %.6f\n' % tuple(v.co)) - - # UV - if faceuv: - uv_face_mapping = [[0,0,0,0] for f in faces] # a bit of a waste for tri's :/ - - uv_dict = {} # could use a set() here - for f_index, f in enumerate(faces): - - for uv_index, uv in enumerate(f.uv): - uvkey = veckey2d(uv) - try: - uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] - except: - uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) - file.write('vt %.6f %.6f\n' % tuple(uv)) - - uv_unique_count = len(uv_dict) - del uv, uvkey, uv_dict, f_index, uv_index - # Only need uv_unique_count and uv_face_mapping - - # NORMAL, Smooth/Non smoothed. - if EXPORT_NORMALS: - for f in faces: - if f.smooth: - for v in f: - noKey = veckey3d(v.no) - if not globalNormals.has_key( noKey ): - globalNormals[noKey] = totno - totno +=1 - file.write('vn %.6f %.6f %.6f\n' % noKey) - else: - # Hard, 1 normal from the face. - noKey = veckey3d(f.no) - if not globalNormals.has_key( noKey ): - globalNormals[noKey] = totno - totno +=1 - file.write('vn %.6f %.6f %.6f\n' % noKey) - - if not faceuv: - f_image = None - - if EXPORT_POLYGROUPS: - # Retrieve the list of vertex groups - vertGroupNames = me.getVertGroupNames() - currentVGroup = '' - # Create a dictionary keyed by face id and listing, for each vertex, the vertex groups it belongs to - vgroupsMap = [[] for _i in xrange(len(me.verts))] - for vertexGroupName in vertGroupNames: - for vIdx, vWeight in me.getVertsFromGroup(vertexGroupName, 1): - vgroupsMap[vIdx].append((vertexGroupName, vWeight)) - - for f_index, f in enumerate(faces): - f_v= f.v - f_smooth= f.smooth - f_mat = min(f.mat, len(materialNames)-1) - if faceuv: - f_image = f.image - f_uv= f.uv - - # MAKE KEY - if faceuv and f_image: # Object is always true. - key = materialNames[f_mat], f_image.name - else: - key = materialNames[f_mat], None # No image, use None instead. - - # Write the vertex group - if EXPORT_POLYGROUPS: - if vertGroupNames: - # find what vertext group the face belongs to - theVGroup = findVertexGroupName(f,vgroupsMap) - if theVGroup != currentVGroup: - currentVGroup = theVGroup - file.write('g %s\n' % theVGroup) - - # CHECK FOR CONTEXT SWITCH - if key == contextMat: - pass # Context alredy switched, dont do anything - else: - if key[0] == None and key[1] == None: - # Write a null material, since we know the context has changed. - if EXPORT_GROUP_BY_MAT: - file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.getData(1))) ) # can be mat_image or (null) - file.write('usemtl (null)\n') # mat, image - - else: - mat_data= MTL_DICT.get(key) - if not mat_data: - # First add to global dict so we can export to mtl - # Then write mtl - - # Make a new names from the mat and image name, - # converting any spaces to underscores with fixName. - - # If none image dont bother adding it to the name - if key[1] == None: - mat_data = MTL_DICT[key] = ('%s'%fixName(key[0])), materialItems[f_mat], f_image - else: - mat_data = MTL_DICT[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image - - if EXPORT_GROUP_BY_MAT: - file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.getData(1)), mat_data[0]) ) # can be mat_image or (null) - - file.write('usemtl %s\n' % mat_data[0]) # can be mat_image or (null) - - contextMat = key - if f_smooth != contextSmooth: - if f_smooth: # on now off - file.write('s 1\n') - contextSmooth = f_smooth - else: # was off now on - file.write('s off\n') - contextSmooth = f_smooth - - file.write('f') - if faceuv: - if EXPORT_NORMALS: - if f_smooth: # Smoothed, use vertex normals - for vi, v in enumerate(f_v): - file.write( ' %d/%d/%d' % (\ - v.index+totverts,\ - totuvco + uv_face_mapping[f_index][vi],\ - globalNormals[ veckey3d(v.no) ])) # vert, uv, normal - - else: # No smoothing, face normals - no = globalNormals[ veckey3d(f.no) ] - for vi, v in enumerate(f_v): - file.write( ' %d/%d/%d' % (\ - v.index+totverts,\ - totuvco + uv_face_mapping[f_index][vi],\ - no)) # vert, uv, normal - - else: # No Normals - for vi, v in enumerate(f_v): - file.write( ' %d/%d' % (\ - v.index+totverts,\ - totuvco + uv_face_mapping[f_index][vi])) # vert, uv - - face_vert_index += len(f_v) - - else: # No UV's - if EXPORT_NORMALS: - if f_smooth: # Smoothed, use vertex normals - for v in f_v: - file.write( ' %d//%d' % (\ - v.index+totverts,\ - globalNormals[ veckey3d(v.no) ])) - else: # No smoothing, face normals - no = globalNormals[ veckey3d(f.no) ] - for v in f_v: - file.write( ' %d//%d' % (\ - v.index+totverts,\ - no)) - else: # No Normals - for v in f_v: - file.write( ' %d' % (\ - v.index+totverts)) - - file.write('\n') - - # Write edges. - if EXPORT_EDGES: - LOOSE= Mesh.EdgeFlags.LOOSE - for ed in edges: - if ed.flag & LOOSE: - file.write('f %d %d\n' % (ed.v1.index+totverts, ed.v2.index+totverts)) - - # Make the indicies global rather then per mesh - totverts += len(me.verts) - if faceuv: - totuvco += uv_unique_count - me.verts= None file.close() # Now we have all our materials, save them if EXPORT_MTL: - write_mtl(mtlfilename) + write_mtl(scene, mtlfilename) if EXPORT_COPY_IMAGES: dest_dir = filename # Remove chars until we are just the path. @@ -1332,8 +1090,8 @@ def write_ui(filename): def do_export(filename, context): -# Window.EditMode(0) -# Window.WaitCursor(1) + # Window.EditMode(0) + # Window.WaitCursor(1) EXPORT_APPLY_MODIFIERS = True EXPORT_ROTX90 = True @@ -1351,6 +1109,8 @@ def do_export(filename, context): EXPORT_GROUP_BY_OB = False EXPORT_GROUP_BY_MAT = False EXPORT_KEEP_VERT_ORDER = False + EXPORT_POLYGROUPS = False + EXPORT_CURVE_AS_NURBS = True base_name, ext = splitExt(filename) context_name = [base_name, '', '', ext] # Base name, scene name, frame number, extension @@ -1370,8 +1130,8 @@ def do_export(filename, context): # Export all scenes. for scn in export_scenes: -# scn.makeCurrent() # If already current, this is not slow. -# context = scn.getRenderingContext() + # scn.makeCurrent() # If already current, this is not slow. + # context = scn.getRenderingContext() orig_frame = scn.current_frame if EXPORT_ALL_SCENES: # Add scene name into the context_name @@ -1398,12 +1158,14 @@ def do_export(filename, context): # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad. # EXPORT THE FILE. -# write(full_path, export_objects, -# EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS, -# EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL, -# EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS, -# EXPORT_ROTX90, EXPORT_BLEN_OBS, -# EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER) + write(full_path, export_objects, scn, + EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS, + EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL, + EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS, + EXPORT_ROTX90, EXPORT_BLEN_OBS, + EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER, + EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS) + scn.current_frame = orig_frame @@ -1412,11 +1174,14 @@ def do_export(filename, context): # Window.WaitCursor(0) -class SCRIPT_OT_export_obj(bpy.types.Operator): +class EXPORT_OT_obj(bpy.types.Operator): ''' - Operator documentatuon text, will be used for the operator tooltip and python docs. + Currently the exporter lacks these features: + * nurbs + * multiple scene export (only active scene is written) + * particles ''' - __label__ = 'My Operator' + __label__ = 'Export OBJ' # List of operator properties, the attributes will be assigned # to the class instance from the operator settings before calling. diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 3127c352f60..4b4c9b97871 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -736,6 +736,11 @@ static void rna_def_medge(BlenderRNA *brna) prop= RNA_def_property(srna, "sharp", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_SHARP); RNA_def_property_ui_text(prop, "Sharp", "Sharp edge for the EdgeSplit modifier"); + + prop= RNA_def_property(srna, "loose", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_LOOSEEDGE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Loose", "Loose edge"); } static void rna_def_mface(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 40f29360e9c..1b6c298eba4 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -57,7 +57,9 @@ /* copied from init_render_mesh (render code) */ static Mesh *create_mesh(Object *ob, bContext *C, ReportList *reports, int render_mesh) { - CustomDataMask mask = CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; + /* CustomDataMask mask = CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; */ + CustomDataMask mask = CD_MASK_MESH; /* this seems more suitable, exporter, + for example, needs CD_MASK_MDEFORMVERT */ DerivedMesh *dm; Mesh *me; Scene *sce; From 1557736756b41dfa8fff4d7c887a7d6da2b1f468 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Sun, 28 Jun 2009 13:29:03 +0000 Subject: [PATCH 34/68] OBJ exporter working (Python 3.0), but needs testing and fixing. Current issues: - NURBS - needs API additions - "all scenes" export - cannot switch scene in bpy - normal calculation, disabled - duplis - need testing, only dupliverts tested - matrix problem - UI, 18 options currently don't fit into filesel panel, will do manual lay out once it's available - probably others... BPY: - made operator "execute" method required to avoid crash - added bpy.sys module which replicates old "sys" module API: - replaced create_*_mesh with a single create_mesh accepting type parameter - added Mesh.create_copy to create a copy of a mesh with 0 users Ran `dos2unix` on source/blender/python/SConscript --- release/io/export_obj.py | 984 ++++++++++++- release/io/export_ply.py | 6 +- release/scripts/3ds_export.py | 24 +- release/scripts/export_obj-2.5.py | 1217 ----------------- release/ui/space_script.py | 4 +- source/blender/makesrna/intern/rna_mesh_api.c | 15 + .../blender/makesrna/intern/rna_object_api.c | 58 +- source/blender/python/intern/bpy_interface.c | 2 + .../blender/python/intern/bpy_operator_wrap.c | 2 +- source/blender/python/intern/bpy_rna.c | 8 +- source/blender/python/intern/bpy_sys.c | 460 +++++++ source/blender/python/intern/bpy_sys.h | 41 + 12 files changed, 1499 insertions(+), 1322 deletions(-) delete mode 100644 release/scripts/export_obj-2.5.py create mode 100644 source/blender/python/intern/bpy_sys.c create mode 100644 source/blender/python/intern/bpy_sys.h diff --git a/release/io/export_obj.py b/release/io/export_obj.py index 8b3bcfb26b3..d139e872251 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -1,83 +1,961 @@ +#!BPY + +""" +Name: 'Wavefront (.obj)...' +Blender: 248 +Group: 'Export' +Tooltip: 'Save a Wavefront OBJ File' +""" + +__author__ = "Campbell Barton, Jiri Hnidek, Paolo Ciccone" +__url__ = ['http://wiki.blender.org/index.php/Scripts/Manual/Export/wavefront_obj', 'www.blender.org', 'blenderartists.org'] +__version__ = "1.21" + +__bpydoc__ = """\ +This script is an exporter to OBJ file format. + +Usage: + +Select the objects you wish to export and run this script from "File->Export" menu. +Selecting the default options from the popup box will be good in most cases. +All objects that can be represented as a mesh (mesh, curve, metaball, surface, text3d) +will be exported as mesh data. +""" + + +# -------------------------------------------------------------------------- +# OBJ Export v1.1 by Campbell Barton (AKA Ideasman) +# -------------------------------------------------------------------------- +# ***** 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. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + + import bpy +import os # os.sep +import Mathutils -def write_obj(filepath, scene, ob): - out = open(filepath, 'w') +# Returns a tuple - path,extension. +# 'hello.obj' > ('hello', '.obj') +def splitExt(path): + dotidx = path.rfind('.') + if dotidx == -1: + return path, '' + else: + return path[:dotidx], path[dotidx:] - # create a temporary mesh - mesh = ob.create_render_mesh(scene) +def fixName(name): + if name == None: + return 'None' + else: + return name.replace(' ', '_') - # for vert in mesh.verts: - # ^ iterating that way doesn't work atm for some reason - for i in range(len(mesh.verts)): - vert = mesh.verts[i] - out.write('v {0} {1} {2}\n'.format(vert.co[0], vert.co[1], vert.co[2])) +# this used to be in BPySys module +# frankly, I don't understand how it works +def BPySys_cleanName(name): + + v = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,46,47,58,59,60,61,62,63,64,91,92,93,94,96,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254] + + invalid = ''.join([chr(i) for i in v]) + + for ch in invalid: + name = name.replace(ch, '_') + return name + +# A Dict of Materials +# (material.name, image.name):matname_imagename # matname_imagename has gaps removed. +MTL_DICT = {} + +def write_mtl(scene, filename): + + world = scene.world + worldAmb = world.ambient_color + + file = open(filename, "w") + # XXX +# file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1]) + file.write('# Material Count: %i\n' % len(MTL_DICT)) + # Write material/image combinations we have used. + for key, (mtl_mat_name, mat, img) in MTL_DICT.items(): + + # Get the Blender data for the material and the image. + # Having an image named None will make a bug, dont do it :) + + file.write('newmtl %s\n' % mtl_mat_name) # Define a new material: matname_imgname + + if mat: + file.write('Ns %.6f\n' % ((mat.specular_hardness-1) * 1.9607843137254901) ) # Hardness, convert blenders 1-511 to MTL's + file.write('Ka %.6f %.6f %.6f\n' % tuple([c*mat.ambient for c in worldAmb]) ) # Ambient, uses mirror colour, + file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.diffuse_reflection for c in mat.diffuse_color]) ) # Diffuse + file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.specular_reflection for c in mat.specular_color]) ) # Specular + file.write('Ni %.6f\n' % mat.ior) # Refraction index + file.write('d %.6f\n' % mat.alpha) # Alpha (obj uses 'd' for dissolve) + + # 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting. + if mat.shadeless: + file.write('illum 0\n') # ignore lighting + elif mat.specular_reflection == 0: + file.write('illum 1\n') # no specular. + else: + file.write('illum 2\n') # light normaly + + else: + #write a dummy material here? + file.write('Ns 0\n') + file.write('Ka %.6f %.6f %.6f\n' % tuple([c for c in worldAmb]) ) # Ambient, uses mirror colour, + file.write('Kd 0.8 0.8 0.8\n') + file.write('Ks 0.8 0.8 0.8\n') + file.write('d 1\n') # No alpha + file.write('illum 2\n') # light normaly + + # Write images! + if img: # We have an image on the face! + file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image + + elif mat: # No face image. if we havea material search for MTex image. + for mtex in mat.textures: + if mtex and mtex.texure.type == 'IMAGE': + try: + filename = mtex.texture.image.filename.split('\\')[-1].split('/')[-1] + file.write('map_Kd %s\n' % filename) # Diffuse mapping image + break + except: + # Texture has no image though its an image type, best ignore. + pass + + file.write('\n\n') - for i in range(len(mesh.faces)): - face = mesh.faces[i] - out.write('f') + file.close() - # but this works - for index in face.verts: - out.write(' {0}'.format(index + 1)) - out.write('\n') - - # delete mesh gain - bpy.data.remove_mesh(mesh) - - out.close() +def copy_file(source, dest): + file = open(source, 'rb') + data = file.read() + file.close() -class SCRIPT_OT_export_obj(bpy.types.Operator): - '''A very basic OBJ exporter, writes only active object's mesh.''' + file = open(dest, 'wb') + file.write(data) + file.close() + +def copy_images(dest_dir): + if dest_dir[-1] != os.sep: + dest_dir += os.sep +# if dest_dir[-1] != sys.sep: +# dest_dir += sys.sep + + # Get unique image names + uniqueImages = {} + for matname, mat, image in MTL_DICT.values(): # Only use image name + # Get Texface images + if image: + uniqueImages[image] = image # Should use sets here. wait until Python 2.4 is default. + + # Get MTex images + if mat: + for mtex in mat.textures: + if mtex and mtex.texture.type == 'IMAGE': + image_tex = mtex.texture.image + if image_tex: + try: + uniqueImages[image_tex] = image_tex + except: + pass + + # Now copy images + copyCount = 0 + + for bImage in uniqueImages.values(): + image_path = bpy.sys.expandpath(bImage.filename) + if bpy.sys.exists(image_path): + # Make a name for the target path. + dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] + if not bpy.sys.exists(dest_image_path): # Image isnt alredy there + print('\tCopying "%s" > "%s"' % (image_path, dest_image_path)) + copy_file(image_path, dest_image_path) + copyCount+=1 + + print('\tCopied %d images' % copyCount) + +# XXX not converted +def test_nurbs_compat(ob): + if ob.type != 'CURVE': + return False + + for nu in ob.data.curves: + if (not nu.knotsV) and nu.type != 1: # not a surface and not bezier + return True + +# for nu in ob.data: +# if (not nu.knotsV) and nu.type != 1: # not a surface and not bezier +# return True + + return False + +# XXX not converted +def write_nurb(file, ob, ob_mat): + tot_verts = 0 + cu = ob.data + + # use negative indices + Vector = Blender.Mathutils.Vector + for nu in cu: + + if nu.type==0: DEG_ORDER_U = 1 + else: DEG_ORDER_U = nu.orderU-1 # Tested to be correct + + if nu.type==1: + print("\tWarning, bezier curve:", ob.name, "only poly and nurbs curves supported") + continue + + if nu.knotsV: + print("\tWarning, surface:", ob.name, "only poly and nurbs curves supported") + continue + + if len(nu) <= DEG_ORDER_U: + print("\tWarning, orderU is lower then vert count, skipping:", ob.name) + continue + + pt_num = 0 + do_closed = (nu.flagU & 1) + do_endpoints = (do_closed==0) and (nu.flagU & 2) + + for pt in nu: + pt = Vector(pt[0], pt[1], pt[2]) * ob_mat + file.write('v %.6f %.6f %.6f\n' % (pt[0], pt[1], pt[2])) + pt_num += 1 + tot_verts += pt_num + + file.write('g %s\n' % (fixName(ob.name))) # fixName(ob.getData(1)) could use the data name too + file.write('cstype bspline\n') # not ideal, hard coded + file.write('deg %d\n' % DEG_ORDER_U) # not used for curves but most files have it still + + curve_ls = [-(i+1) for i in range(pt_num)] + + # 'curv' keyword + if do_closed: + if DEG_ORDER_U == 1: + pt_num += 1 + curve_ls.append(-1) + else: + pt_num += DEG_ORDER_U + curve_ls = curve_ls + curve_ls[0:DEG_ORDER_U] + + file.write('curv 0.0 1.0 %s\n' % (' '.join( [str(i) for i in curve_ls] ))) # Blender has no U and V values for the curve + + # 'parm' keyword + tot_parm = (DEG_ORDER_U + 1) + pt_num + tot_parm_div = float(tot_parm-1) + parm_ls = [(i/tot_parm_div) for i in range(tot_parm)] + + if do_endpoints: # end points, force param + for i in range(DEG_ORDER_U+1): + parm_ls[i] = 0.0 + parm_ls[-(1+i)] = 1.0 + + file.write('parm u %s\n' % ' '.join( [str(i) for i in parm_ls] )) + + file.write('end\n') + + return tot_verts + +def write(filename, objects, scene, + EXPORT_TRI=False, + EXPORT_EDGES=False, + EXPORT_NORMALS=False, + EXPORT_NORMALS_HQ=False, + EXPORT_UV=True, + EXPORT_MTL=True, + EXPORT_COPY_IMAGES=False, + EXPORT_APPLY_MODIFIERS=True, + EXPORT_ROTX90=True, + EXPORT_BLEN_OBS=True, + EXPORT_GROUP_BY_OB=False, + EXPORT_GROUP_BY_MAT=False, + EXPORT_KEEP_VERT_ORDER=False, + EXPORT_POLYGROUPS=False, + EXPORT_CURVE_AS_NURBS=True): + ''' + Basic write function. The context and options must be alredy set + This can be accessed externaly + eg. + write( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options. + ''' + + def veckey3d(v): + return round(v.x, 6), round(v.y, 6), round(v.z, 6) + + def veckey2d(v): + return round(v.x, 6), round(v.y, 6) + + def findVertexGroupName(face, vWeightMap): + """ + Searches the vertexDict to see what groups is assigned to a given face. + We use a frequency system in order to sort out the name because a given vetex can + belong to two or more groups at the same time. To find the right name for the face + we list all the possible vertex group names with their frequency and then sort by + frequency in descend order. The top element is the one shared by the highest number + of vertices is the face's group + """ + weightDict = {} + for vert_index in face.verts: +# for vert in face: + vWeights = vWeightMap[vert_index] +# vWeights = vWeightMap[vert] + for vGroupName, weight in vWeights: + weightDict[vGroupName] = weightDict.get(vGroupName, 0) + weight + + if weightDict: + alist = [(weight,vGroupName) for vGroupName, weight in weightDict.items()] # sort least to greatest amount of weight + alist.sort() + return(alist[-1][1]) # highest value last + else: + return '(null)' + + # TODO: implement this in C? dunno how it should be called... + def getVertsFromGroup(me, group_index): + ret = [] + + for i, v in enumerate(me.verts): + for g in v.groups: + if g.group == group_index: + ret.append((i, g.weight)) + + return ret + + + print('OBJ Export path: "%s"' % filename) + temp_mesh_name = '~tmp-mesh' + + time1 = bpy.sys.time() +# time1 = sys.time() +# scn = Scene.GetCurrent() + + file = open(filename, "w") + + # Write Header + version = "2.5" + file.write('# Blender3D v%s OBJ File: %s\n' % (version, bpy.data.filename.split('/')[-1].split('\\')[-1] )) + file.write('# www.blender3d.org\n') + + # Tell the obj file what material file to use. + if EXPORT_MTL: + mtlfilename = '%s.mtl' % '.'.join(filename.split('.')[:-1]) + file.write('mtllib %s\n' % ( mtlfilename.split('\\')[-1].split('/')[-1] )) + + if EXPORT_ROTX90: + mat_xrot90= Mathutils.RotationMatrix(-90, 4, 'x') + + # Initialize totals, these are updated each object + totverts = totuvco = totno = 1 + + face_vert_index = 1 + + globalNormals = {} + + # Get all meshes + for ob_main in objects: + + if ob_main.dupli_type != 'NONE': + # XXX + print('creating dupli_list on', ob_main.name) + ob_main.create_dupli_list() + + # ignore dupli children + if ob_main.parent and ob_main.parent.dupli_type != 'NONE': + # XXX + print(ob_main.name, 'is a dupli child - ignoring') + continue + + obs = [] + if ob_main.dupli_type != 'NONE': + obs = [(dob.object, dob.matrix) for dob in ob_main.dupli_list] + + # XXX + print(ob_main.name, 'has', len(obs), 'dupli children') + else: + obs = [(ob_main, ob_main.matrix)] + + for ob, ob_mat in obs: + + if EXPORT_ROTX90: + ob_mat = ob_mat * mat_xrot90 + + # XXX postponed +# # Nurbs curve support +# if EXPORT_CURVE_AS_NURBS and test_nurbs_compat(ob): +# if EXPORT_ROTX90: +# ob_mat = ob_mat * mat_xrot90 + +# totverts += write_nurb(file, ob, ob_mat) + +# continue +# end nurbs + + if ob.type != 'MESH': + continue + + if EXPORT_APPLY_MODIFIERS: + me = ob.create_mesh('PREVIEW') + else: + me = ob.data.create_copy() + + me.transform(ob_mat) + +# # Will work for non meshes now! :) +# me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, EXPORT_POLYGROUPS, scn) +# if not me: +# continue + + if EXPORT_UV: + faceuv = len(me.uv_layers) > 0 + else: + faceuv = False + + # We have a valid mesh + if EXPORT_TRI and me.faces: + # Add a dummy object to it. + has_quads = False + for f in me.faces: + if f.verts[3] != 0: + has_quads = True + break + + if has_quads: + newob = bpy.data.add_object('MESH', 'temp_object') + newob.data = me + # if we forget to set Object.data - crash + scene.add_object(newob) + newob.convert_to_triface(scene) + # mesh will still be there + scene.remove_object(newob) + + # Make our own list so it can be sorted to reduce context switching + face_index_pairs = [ (face, index) for index, face in enumerate(me.faces)] + # faces = [ f for f in me.faces ] + + if EXPORT_EDGES: + edges = me.edges + else: + edges = [] + + if not (len(face_index_pairs)+len(edges)+len(me.verts)): # Make sure there is somthing to write + + # clean up + bpy.data.remove_mesh(me) + + continue # dont bother with this mesh. + + # XXX + # High Quality Normals + if EXPORT_NORMALS and face_index_pairs: + pass +# if EXPORT_NORMALS_HQ: +# BPyMesh.meshCalcNormals(me) +# else: +# # transforming normals is incorrect +# # when the matrix is scaled, +# # better to recalculate them +# me.calcNormals() + + materials = me.materials + + materialNames = [] + materialItems = [m for m in materials] + if materials: + for mat in materials: + if mat: # !=None + materialNames.append(mat.name) + else: + materialNames.append(None) + # Cant use LC because some materials are None. + # materialNames = map(lambda mat: mat.name, materials) # Bug Blender, dosent account for null materials, still broken. + + # Possible there null materials, will mess up indicies + # but at least it will export, wait until Blender gets fixed. + materialNames.extend((16-len(materialNames)) * [None]) + materialItems.extend((16-len(materialItems)) * [None]) + + # Sort by Material, then images + # so we dont over context switch in the obj file. + if EXPORT_KEEP_VERT_ORDER: + pass + elif faceuv: + # XXX update + tface = me.active_uv_layer.data + + # exception only raised if Python 2.3 or lower... + try: + face_index_pairs.sort(key = lambda a: (a[0].material_index, tface[a[1]].image, a[0].smooth)) + except: + face_index_pairs.sort(lambda a,b: cmp((a[0].material_index, tface[a[1]].image, a[0].smooth), + (b[0].material_index, tface[b[1]].image, b[0].smooth))) + elif len(materials) > 1: + try: + face_index_pairs.sort(key = lambda a: (a[0].material_index, a[0].smooth)) + except: + face_index_pairs.sort(lambda a,b: cmp((a[0].material_index, a[0].smooth), + (b[0].material_index, b[0].smooth))) + else: + # no materials + try: + face_index_pairs.sort(key = lambda a: a[0].smooth) + except: + face_index_pairs.sort(lambda a,b: cmp(a[0].smooth, b[0].smooth)) +# if EXPORT_KEEP_VERT_ORDER: +# pass +# elif faceuv: +# try: faces.sort(key = lambda a: (a.mat, a.image, a.smooth)) +# except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) +# elif len(materials) > 1: +# try: faces.sort(key = lambda a: (a.mat, a.smooth)) +# except: faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth))) +# else: +# # no materials +# try: faces.sort(key = lambda a: a.smooth) +# except: faces.sort(lambda a,b: cmp(a.smooth, b.smooth)) + + faces = [pair[0] for pair in face_index_pairs] + + # Set the default mat to no material and no image. + contextMat = (0, 0) # Can never be this, so we will label a new material teh first chance we get. + contextSmooth = None # Will either be true or false, set bad to force initialization switch. + + if EXPORT_BLEN_OBS or EXPORT_GROUP_BY_OB: + name1 = ob.name + name2 = ob.data.name + if name1 == name2: + obnamestring = fixName(name1) + else: + obnamestring = '%s_%s' % (fixName(name1), fixName(name2)) + + if EXPORT_BLEN_OBS: + file.write('o %s\n' % obnamestring) # Write Object name + else: # if EXPORT_GROUP_BY_OB: + file.write('g %s\n' % obnamestring) + + + # Vert + for v in me.verts: + file.write('v %.6f %.6f %.6f\n' % tuple(v.co)) + + # UV + if faceuv: + uv_face_mapping = [[0,0,0,0] for f in faces] # a bit of a waste for tri's :/ + + uv_dict = {} # could use a set() here + uv_layer = me.active_uv_layer + for f, f_index in face_index_pairs: + + tface = uv_layer.data[f_index] + + uvs = [tface.uv1, tface.uv2, tface.uv3] + + # add another UV if it's a quad + if f.verts[3] != 0: + uvs.append(tface.uv4) + + for uv_index, uv in enumerate(uvs): + uvkey = veckey2d(uv) + try: + uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] + except: + uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) + file.write('vt %.6f %.6f\n' % tuple(uv)) + +# uv_dict = {} # could use a set() here +# for f_index, f in enumerate(faces): + +# for uv_index, uv in enumerate(f.uv): +# uvkey = veckey2d(uv) +# try: +# uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] +# except: +# uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) +# file.write('vt %.6f %.6f\n' % tuple(uv)) + + uv_unique_count = len(uv_dict) + del uv, uvkey, uv_dict, f_index, uv_index + # Only need uv_unique_count and uv_face_mapping + + # NORMAL, Smooth/Non smoothed. + if EXPORT_NORMALS: + for f in faces: + if f.smooth: + for v in f: + noKey = veckey3d(v.normal) + if noKey not in globalNormals: + globalNormals[noKey] = totno + totno +=1 + file.write('vn %.6f %.6f %.6f\n' % noKey) + else: + # Hard, 1 normal from the face. + noKey = veckey3d(f.normal) + if noKey not in globalNormals: + globalNormals[noKey] = totno + totno +=1 + file.write('vn %.6f %.6f %.6f\n' % noKey) + + if not faceuv: + f_image = None + + # XXX + if EXPORT_POLYGROUPS: + # Retrieve the list of vertex groups +# vertGroupNames = me.getVertGroupNames() + + currentVGroup = '' + # Create a dictionary keyed by face id and listing, for each vertex, the vertex groups it belongs to + vgroupsMap = [[] for _i in range(len(me.verts))] +# vgroupsMap = [[] for _i in xrange(len(me.verts))] + for g in ob.vertex_groups: +# for vertexGroupName in vertGroupNames: + for vIdx, vWeight in getVertsFromGroup(me, g.index): +# for vIdx, vWeight in me.getVertsFromGroup(vertexGroupName, 1): + vgroupsMap[vIdx].append((g.name, vWeight)) + + for f_index, f in enumerate(faces): + f_v = [{"index": index, "vertex": me.verts[index]} for index in f.verts] + + if f.verts[3] == 0: + f_v.pop() + +# f_v= f.v + f_smooth= f.smooth + f_mat = min(f.material_index, len(materialNames)-1) +# f_mat = min(f.mat, len(materialNames)-1) + if faceuv: + + tface = me.active_uv_layer.data[face_index_pairs[f_index][1]] + + f_image = tface.image + f_uv= [tface.uv1, tface.uv2, tface.uv3] + if f.verts[3] != 0: + f_uv.append(tface.uv4) +# f_image = f.image +# f_uv= f.uv + + # MAKE KEY + if faceuv and f_image: # Object is always true. + key = materialNames[f_mat], f_image.name + else: + key = materialNames[f_mat], None # No image, use None instead. + + # Write the vertex group + if EXPORT_POLYGROUPS: + if len(ob.vertex_groups): + # find what vertext group the face belongs to + theVGroup = findVertexGroupName(f,vgroupsMap) + if theVGroup != currentVGroup: + currentVGroup = theVGroup + file.write('g %s\n' % theVGroup) +# # Write the vertex group +# if EXPORT_POLYGROUPS: +# if vertGroupNames: +# # find what vertext group the face belongs to +# theVGroup = findVertexGroupName(f,vgroupsMap) +# if theVGroup != currentVGroup: +# currentVGroup = theVGroup +# file.write('g %s\n' % theVGroup) + + # CHECK FOR CONTEXT SWITCH + if key == contextMat: + pass # Context alredy switched, dont do anything + else: + if key[0] == None and key[1] == None: + # Write a null material, since we know the context has changed. + if EXPORT_GROUP_BY_MAT: + # can be mat_image or (null) + file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.data.name)) ) # can be mat_image or (null) + file.write('usemtl (null)\n') # mat, image + + else: + mat_data= MTL_DICT.get(key) + if not mat_data: + # First add to global dict so we can export to mtl + # Then write mtl + + # Make a new names from the mat and image name, + # converting any spaces to underscores with fixName. + + # If none image dont bother adding it to the name + if key[1] == None: + mat_data = MTL_DICT[key] = ('%s'%fixName(key[0])), materialItems[f_mat], f_image + else: + mat_data = MTL_DICT[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image + + if EXPORT_GROUP_BY_MAT: + file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.data.name), mat_data[0]) ) # can be mat_image or (null) + + file.write('usemtl %s\n' % mat_data[0]) # can be mat_image or (null) + + contextMat = key + if f_smooth != contextSmooth: + if f_smooth: # on now off + file.write('s 1\n') + contextSmooth = f_smooth + else: # was off now on + file.write('s off\n') + contextSmooth = f_smooth + + file.write('f') + if faceuv: + if EXPORT_NORMALS: + if f_smooth: # Smoothed, use vertex normals + for vi, v in enumerate(f_v): + file.write( ' %d/%d/%d' % \ + (v["index"] + totverts, + totuvco + uv_face_mapping[f_index][vi], + globalNormals[ veckey3d(v["vertex"].normal) ]) ) # vert, uv, normal + + else: # No smoothing, face normals + no = globalNormals[ veckey3d(f.normal) ] + for vi, v in enumerate(f_v): + file.write( ' %d/%d/%d' % \ + (v["index"] + totverts, + totuvco + uv_face_mapping[f_index][vi], + no) ) # vert, uv, normal + else: # No Normals + for vi, v in enumerate(f_v): + file.write( ' %d/%d' % (\ + v["index"] + totverts,\ + totuvco + uv_face_mapping[f_index][vi])) # vert, uv + + face_vert_index += len(f_v) + + else: # No UV's + if EXPORT_NORMALS: + if f_smooth: # Smoothed, use vertex normals + for v in f_v: + file.write( ' %d//%d' % + (v["index"] + totverts, globalNormals[ veckey3d(v["vertex"].normal) ]) ) + else: # No smoothing, face normals + no = globalNormals[ veckey3d(f.normal) ] + for v in f_v: + file.write( ' %d//%d' % (v["index"] + totverts, no) ) + else: # No Normals + for v in f_v: + file.write( ' %d' % (v["index"] + totverts) ) + + file.write('\n') + + # Write edges. + if EXPORT_EDGES: + for ed in edges: + if ed.loose: + file.write('f %d %d\n' % (ed.verts[0] + totverts, ed.verts[1] + totverts)) + + # Make the indicies global rather then per mesh + totverts += len(me.verts) + if faceuv: + totuvco += uv_unique_count + + # clean up + bpy.data.remove_mesh(me) + + if ob_main.dupli_type != 'NONE': + ob_main.free_dupli_list() + + file.close() + + + # Now we have all our materials, save them + if EXPORT_MTL: + write_mtl(scene, mtlfilename) + if EXPORT_COPY_IMAGES: + dest_dir = filename + # Remove chars until we are just the path. + while dest_dir and dest_dir[-1] not in '\\/': + dest_dir = dest_dir[:-1] + if dest_dir: + copy_images(dest_dir) + else: + print('\tError: "%s" could not be used as a base for an image path.' % filename) + + print("OBJ Export time: %.2f" % (bpy.sys.time() - time1)) +# print "OBJ Export time: %.2f" % (sys.time() - time1) + +def do_export(filename, context, + EXPORT_APPLY_MODIFIERS = True, # not used + EXPORT_ROTX90 = True, # wrong + EXPORT_TRI = False, # ok + EXPORT_EDGES = False, + EXPORT_NORMALS = False, # not yet + EXPORT_NORMALS_HQ = False, # not yet + EXPORT_UV = True, # ok + EXPORT_MTL = True, + EXPORT_SEL_ONLY = True, # ok + EXPORT_ALL_SCENES = False, # XXX not working atm + EXPORT_ANIMATION = False, + EXPORT_COPY_IMAGES = False, + EXPORT_BLEN_OBS = True, + EXPORT_GROUP_BY_OB = False, + EXPORT_GROUP_BY_MAT = False, + EXPORT_KEEP_VERT_ORDER = False, + EXPORT_POLYGROUPS = False, + EXPORT_CURVE_AS_NURBS = True): + # Window.EditMode(0) + # Window.WaitCursor(1) + + base_name, ext = splitExt(filename) + context_name = [base_name, '', '', ext] # Base name, scene name, frame number, extension + + orig_scene = context.scene + +# if EXPORT_ALL_SCENES: +# export_scenes = bpy.data.scenes +# else: +# export_scenes = [orig_scene] + + # XXX only exporting one scene atm since changing + # current scene is not possible. + # Brecht says that ideally in 2.5 we won't need such a function, + # allowing multiple scenes open at once. + export_scenes = [orig_scene] + + # Export all scenes. + for scn in export_scenes: + # scn.makeCurrent() # If already current, this is not slow. + # context = scn.getRenderingContext() + orig_frame = scn.current_frame + + if EXPORT_ALL_SCENES: # Add scene name into the context_name + context_name[1] = '_%s' % BPySys_cleanName(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied. + + # Export an animation? + if EXPORT_ANIMATION: + scene_frames = range(scn.start_frame, context.end_frame+1) # Up to and including the end frame. + else: + scene_frames = [orig_frame] # Dont export an animation. + + # Loop through all frames in the scene and export. + for frame in scene_frames: + if EXPORT_ANIMATION: # Add frame to the filename. + context_name[2] = '_%.6d' % frame + + scn.current_frame = frame + if EXPORT_SEL_ONLY: + export_objects = context.selected_objects + else: + export_objects = scn.objects + + full_path= ''.join(context_name) + + # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad. + # EXPORT THE FILE. + write(full_path, export_objects, scn, + EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS, + EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL, + EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS, + EXPORT_ROTX90, EXPORT_BLEN_OBS, + EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER, + EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS) + + + scn.current_frame = orig_frame + + # Restore old active scene. +# orig_scene.makeCurrent() +# Window.WaitCursor(0) + + +class EXPORT_OT_obj(bpy.types.Operator): + ''' + Currently the exporter lacks these features: + * nurbs + * multiple scene export (only active scene is written) + * particles + ''' __label__ = 'Export OBJ' # List of operator properties, the attributes will be assigned # to the class instance from the operator settings before calling. + __props__ = [ - bpy.props.StringProperty(attr="filename", name="filename") - ] + bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the PLY file", maxlen= 1024, default= ""), - def debug(self, message): - print("{0}: {1}".format(self.__class__.__name__, message)) + # context group + bpy.props.BoolProperty(attr="use_selection", name="Selection Only", description="", default= True), + bpy.props.BoolProperty(attr="use_all_scenes", name="All Scenes", description="", default= False), + bpy.props.BoolProperty(attr="use_animation", name="All Animation", description="", default= False), - def execute_(self, context): - self.debug("exec") - self.debug("filename = " + self.filename) + # object group + bpy.props.BoolProperty(attr="use_modifiers", name="Apply Modifiers", description="", default= True), + bpy.props.BoolProperty(attr="use_rotate90", name="Rotate X90", description="", default= True), - act = context.active_object - - if act.type == 'MESH': - write_obj(self.filename, context.scene, act) - else: - self.debug("Active object is not a MESH.") - - # XXX errors are silenced for some reason -# raise Exception("oops!") - - return ('FINISHED',) + # extra data group + bpy.props.BoolProperty(attr="use_edges", name="Edges", description="", default= True), + bpy.props.BoolProperty(attr="use_normals", name="Normals", description="", default= False), + bpy.props.BoolProperty(attr="use_hq_normals", name="High Quality Normals", description="", default= True), + bpy.props.BoolProperty(attr="use_uvs", name="UVs", description="", default= True), + bpy.props.BoolProperty(attr="use_materials", name="Materials", description="", default= True), + bpy.props.BoolProperty(attr="copy_images", name="Copy Images", description="", default= False), + bpy.props.BoolProperty(attr="use_triangles", name="Triangulate", description="", default= False), + bpy.props.BoolProperty(attr="use_vertex_groups", name="Polygroups", description="", default= False), + bpy.props.BoolProperty(attr="use_nurbs", name="Nurbs", description="", default= False), + # grouping group + bpy.props.BoolProperty(attr="use_blen_objects", name="Objects as OBJ Objects", description="", default= True), + bpy.props.BoolProperty(attr="group_by_object", name="Objects as OBJ Groups ", description="", default= False), + bpy.props.BoolProperty(attr="group_by_material", name="Material Groups", description="", default= False), + bpy.props.BoolProperty(attr="keep_vertex_order", name="Keep Vertex Order", description="", default= False) + ] + def execute(self, context): - self.debug("exec") - - act = context.active_object - act.create_dupli_list() - print("{0} has {1} dupli objects".format(act.name, len(act.dupli_list))) - - act.free_dupli_list() + do_export(self.filename, context, + EXPORT_TRI=self.use_triangles, + EXPORT_EDGES=self.use_edges, + EXPORT_NORMALS=self.use_normals, + EXPORT_NORMALS_HQ=self.use_hq_normals, + EXPORT_UV=self.use_uvs, + EXPORT_MTL=self.use_materials, + EXPORT_COPY_IMAGES=self.copy_images, + EXPORT_APPLY_MODIFIERS=self.use_modifiers, + EXPORT_ROTX90=self.use_rotate90, + EXPORT_BLEN_OBS=self.use_blen_objects, + EXPORT_GROUP_BY_OB=self.group_by_object, + EXPORT_GROUP_BY_MAT=self.group_by_material, + EXPORT_KEEP_VERT_ORDER=self.keep_vertex_order, + EXPORT_POLYGROUPS=self.use_vertex_groups, + EXPORT_CURVE_AS_NURBS=self.use_nurbs, + EXPORT_SEL_ONLY=self.use_selection, + EXPORT_ALL_SCENES=self.use_all_scenes) return ('FINISHED',) def invoke(self, context, event): - self.debug("invoke") wm = context.manager wm.add_fileselect(self.__operator__) return ('RUNNING_MODAL',) - def poll(self, context): # poll isnt working yet - self.debug("poll") - return True + def poll(self, context): # Poll isnt working yet + print("Poll") + return context.active_object != None -bpy.ops.add(SCRIPT_OT_export_obj) +bpy.ops.add(EXPORT_OT_obj) +if __name__ == "__main__": + bpy.ops.EXPORT_OT_obj(filename="/tmp/test.obj") + +# CONVERSION ISSUES +# - matrix problem +# - duplis - only tested dupliverts +# - NURBS - needs API additions +# - all scenes export +# - normals calculation diff --git a/release/io/export_ply.py b/release/io/export_ply.py index ed983c2b169..ce1cdc55d09 100644 --- a/release/io/export_ply.py +++ b/release/io/export_ply.py @@ -64,7 +64,7 @@ def write(filename, scene, ob, \ raise Exception("Error, Select 1 active object") return - file = open(filename, 'wb') + file = open(filename, 'w') #EXPORT_EDGES = Draw.Create(0) @@ -123,8 +123,8 @@ def write(filename, scene, ob, \ mesh_verts = mesh.verts # save a lookup ply_verts = [] # list of dictionaries # vdict = {} # (index, normal, uv) -> new index - vdict = [{} for i in xrange(len(mesh_verts))] - ply_faces = [[] for f in xrange(len(mesh.faces))] + vdict = [{} for i in range(len(mesh_verts))] + ply_faces = [[] for f in range(len(mesh.faces))] vert_count = 0 for i, f in enumerate(mesh.faces): diff --git a/release/scripts/3ds_export.py b/release/scripts/3ds_export.py index 87680bce1b0..69b4d00b4d8 100644 --- a/release/scripts/3ds_export.py +++ b/release/scripts/3ds_export.py @@ -863,19 +863,24 @@ def make_kf_obj_node(obj, name_to_id): """ import BPyMessages -def save_3ds(filename): +def save_3ds(filename, context): '''Save the Blender scene to a 3ds file.''' # Time the export if not filename.lower().endswith('.3ds'): filename += '.3ds' - - if not BPyMessages.Warning_SaveOver(filename): - return - - time1= Blender.sys.time() - Blender.Window.WaitCursor(1) - sce= bpy.data.scenes.active + + # XXX +# if not BPyMessages.Warning_SaveOver(filename): +# return + + # XXX + time1 = bpy.sys.time() +# time1= Blender.sys.time() +# Blender.Window.WaitCursor(1) + + sce = context.scene +# sce= bpy.data.scenes.active # Initialize the main chunk (primary): primary = _3ds_chunk(PRIMARY) @@ -901,7 +906,8 @@ def save_3ds(filename): # each material is added once): materialDict = {} mesh_objects = [] - for ob in sce.objects.context: + for ob in context.selected_objects: +# for ob in sce.objects.context: for ob_derived, mat in getDerivedObjects(ob, False): data = getMeshFromObject(ob_derived, None, True, False, sce) if data: diff --git a/release/scripts/export_obj-2.5.py b/release/scripts/export_obj-2.5.py deleted file mode 100644 index cd8e423ed07..00000000000 --- a/release/scripts/export_obj-2.5.py +++ /dev/null @@ -1,1217 +0,0 @@ -#!BPY - -""" -Name: 'Wavefront (.obj)...' -Blender: 248 -Group: 'Export' -Tooltip: 'Save a Wavefront OBJ File' -""" - -__author__ = "Campbell Barton, Jiri Hnidek, Paolo Ciccone" -__url__ = ['http://wiki.blender.org/index.php/Scripts/Manual/Export/wavefront_obj', 'www.blender.org', 'blenderartists.org'] -__version__ = "1.21" - -__bpydoc__ = """\ -This script is an exporter to OBJ file format. - -Usage: - -Select the objects you wish to export and run this script from "File->Export" menu. -Selecting the default options from the popup box will be good in most cases. -All objects that can be represented as a mesh (mesh, curve, metaball, surface, text3d) -will be exported as mesh data. -""" - - -# -------------------------------------------------------------------------- -# OBJ Export v1.1 by Campbell Barton (AKA Ideasman) -# -------------------------------------------------------------------------- -# ***** 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. -# -# ***** END GPL LICENCE BLOCK ***** -# -------------------------------------------------------------------------- - - -import bpy -# import BPySys - -# import Blender -# from Blender import Mesh, Scene, Window, sys, Image, Draw -# import BPyMesh -# import BPyObject -# import BPySys -# import BPyMessages - -# Returns a tuple - path,extension. -# 'hello.obj' > ('hello', '.obj') -def splitExt(path): - dotidx = path.rfind('.') - if dotidx == -1: - return path, '' - else: - return path[:dotidx], path[dotidx:] - -def fixName(name): - if name == None: - return 'None' - else: - return name.replace(' ', '_') - -# A Dict of Materials -# (material.name, image.name):matname_imagename # matname_imagename has gaps removed. -MTL_DICT = {} - -def write_mtl(scene, filename): - - world = bpy.data.worlds[0] - worldAmb = world.ambient_color - -# world = Blender.World.GetCurrent() -# if world: -# worldAmb = world.getAmb() -# else: -# worldAmb = (0,0,0) # Default value - - file = open(filename, "w") - # XXX -# file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1]) - file.write('# Material Count: %i\n' % len(MTL_DICT)) - # Write material/image combinations we have used. - for key, (mtl_mat_name, mat, img) in MTL_DICT.iteritems(): - - # Get the Blender data for the material and the image. - # Having an image named None will make a bug, dont do it :) - - file.write('newmtl %s\n' % mtl_mat_name) # Define a new material: matname_imgname - - if mat: - file.write('Ns %.6f\n' % ((mat.getHardness()-1) * 1.9607843137254901) ) # Hardness, convert blenders 1-511 to MTL's - file.write('Ka %.6f %.6f %.6f\n' % tuple([c*mat.amb for c in worldAmb]) ) # Ambient, uses mirror colour, - file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.ref for c in mat.rgbCol]) ) # Diffuse - file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.spec for c in mat.specCol]) ) # Specular - file.write('Ni %.6f\n' % mat.IOR) # Refraction index - file.write('d %.6f\n' % mat.alpha) # Alpha (obj uses 'd' for dissolve) - - # 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting. - if mat.getMode() & Blender.Material.Modes['SHADELESS']: - file.write('illum 0\n') # ignore lighting - elif mat.getSpec() == 0: - file.write('illum 1\n') # no specular. - else: - file.write('illum 2\n') # light normaly - - else: - #write a dummy material here? - file.write('Ns 0\n') - file.write('Ka %.6f %.6f %.6f\n' % tuple([c for c in worldAmb]) ) # Ambient, uses mirror colour, - file.write('Kd 0.8 0.8 0.8\n') - file.write('Ks 0.8 0.8 0.8\n') - file.write('d 1\n') # No alpha - file.write('illum 2\n') # light normaly - - # Write images! - if img: # We have an image on the face! - file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image - - elif mat: # No face image. if we havea material search for MTex image. - for mtex in mat.getTextures(): - if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: - try: - filename = mtex.tex.image.filename.split('\\')[-1].split('/')[-1] - file.write('map_Kd %s\n' % filename) # Diffuse mapping image - break - except: - # Texture has no image though its an image type, best ignore. - pass - - file.write('\n\n') - - file.close() - -def copy_file(source, dest): - file = open(source, 'rb') - data = file.read() - file.close() - - file = open(dest, 'wb') - file.write(data) - file.close() - - -def copy_images(dest_dir): - if dest_dir[-1] != sys.sep: - dest_dir += sys.sep - - # Get unique image names - uniqueImages = {} - for matname, mat, image in MTL_DICT.itervalues(): # Only use image name - # Get Texface images - if image: - uniqueImages[image] = image # Should use sets here. wait until Python 2.4 is default. - - # Get MTex images - if mat: - for mtex in mat.getTextures(): - if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: - image_tex = mtex.tex.image - if image_tex: - try: - uniqueImages[image_tex] = image_tex - except: - pass - - # Now copy images - copyCount = 0 - - for bImage in uniqueImages.itervalues(): - image_path = sys.expandpath(bImage.filename) - if sys.exists(image_path): - # Make a name for the target path. - dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] - if not sys.exists(dest_image_path): # Image isnt alredy there - print '\tCopying "%s" > "%s"' % (image_path, dest_image_path) - copy_file(image_path, dest_image_path) - copyCount+=1 - print '\tCopied %d images' % copyCount - - -def test_nurbs_compat(ob): - if ob.type != 'CURVE': - return False - - for nu in ob.data: - if (not nu.knotsV) and nu.type != 1: # not a surface and not bezier - return True - - return False - -def write_nurb(file, ob, ob_mat): - tot_verts = 0 - cu = ob.data - - # use negative indices - Vector = Blender.Mathutils.Vector - for nu in cu: - - if nu.type==0: DEG_ORDER_U = 1 - else: DEG_ORDER_U = nu.orderU-1 # Tested to be correct - - if nu.type==1: - print "\tWarning, bezier curve:", ob.name, "only poly and nurbs curves supported" - continue - - if nu.knotsV: - print "\tWarning, surface:", ob.name, "only poly and nurbs curves supported" - continue - - if len(nu) <= DEG_ORDER_U: - print "\tWarning, orderU is lower then vert count, skipping:", ob.name - continue - - pt_num = 0 - do_closed = (nu.flagU & 1) - do_endpoints = (do_closed==0) and (nu.flagU & 2) - - for pt in nu: - pt = Vector(pt[0], pt[1], pt[2]) * ob_mat - file.write('v %.6f %.6f %.6f\n' % (pt[0], pt[1], pt[2])) - pt_num += 1 - tot_verts += pt_num - - file.write('g %s\n' % (fixName(ob.name))) # fixName(ob.getData(1)) could use the data name too - file.write('cstype bspline\n') # not ideal, hard coded - file.write('deg %d\n' % DEG_ORDER_U) # not used for curves but most files have it still - - curve_ls = [-(i+1) for i in xrange(pt_num)] - - # 'curv' keyword - if do_closed: - if DEG_ORDER_U == 1: - pt_num += 1 - curve_ls.append(-1) - else: - pt_num += DEG_ORDER_U - curve_ls = curve_ls + curve_ls[0:DEG_ORDER_U] - - file.write('curv 0.0 1.0 %s\n' % (' '.join( [str(i) for i in curve_ls] ))) # Blender has no U and V values for the curve - - # 'parm' keyword - tot_parm = (DEG_ORDER_U + 1) + pt_num - tot_parm_div = float(tot_parm-1) - parm_ls = [(i/tot_parm_div) for i in xrange(tot_parm)] - - if do_endpoints: # end points, force param - for i in xrange(DEG_ORDER_U+1): - parm_ls[i] = 0.0 - parm_ls[-(1+i)] = 1.0 - - file.write('parm u %s\n' % ' '.join( [str(i) for i in parm_ls] )) - - file.write('end\n') - - return tot_verts - -def write(filename, objects, scene, \ -EXPORT_TRI=False, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_NORMALS_HQ=False,\ -EXPORT_UV=True, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False,\ -EXPORT_APPLY_MODIFIERS=True, EXPORT_ROTX90=True, EXPORT_BLEN_OBS=True,\ -EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_KEEP_VERT_ORDER=False,\ -EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): - ''' - Basic write function. The context and options must be alredy set - This can be accessed externaly - eg. - write( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options. - ''' - - def veckey3d(v): - return round(v.x, 6), round(v.y, 6), round(v.z, 6) - - def veckey2d(v): - return round(v.x, 6), round(v.y, 6) - - def findVertexGroupName(face, vWeightMap): - """ - Searches the vertexDict to see what groups is assigned to a given face. - We use a frequency system in order to sort out the name because a given vetex can - belong to two or more groups at the same time. To find the right name for the face - we list all the possible vertex group names with their frequency and then sort by - frequency in descend order. The top element is the one shared by the highest number - of vertices is the face's group - """ - weightDict = {} - for vert_index in face.verts: -# for vert in face: - vWeights = vWeightMap[vert_index] -# vWeights = vWeightMap[vert] - for vGroupName, weight in vWeights: - weightDict[vGroupName] = weightDict.get(vGroupName, 0) + weight - - if weightDict: - alist = [(weight,vGroupName) for vGroupName, weight in weightDict.iteritems()] # sort least to greatest amount of weight - alist.sort() - return(alist[-1][1]) # highest value last - else: - return '(null)' - - # TODO: implement this in C? dunno how it should be called... - def getVertsFromGroup(me, group_index): - ret = [] - - for i, v in enumerate(me.verts): - for g in v.groups: - if g.group == group.index: - ret.append((i, g.weight)) - - return ret - - - print 'OBJ Export path: "%s"' % filename - temp_mesh_name = '~tmp-mesh' - - time1 = sys.time() -# scn = Scene.GetCurrent() - scene = context.scene - - file = open(filename, "w") - - # Write Header - file.write('# Blender3D v%s OBJ File: %s\n' % (Blender.Get('version'), Blender.Get('filename').split('/')[-1].split('\\')[-1] )) - file.write('# www.blender3d.org\n') - - # Tell the obj file what material file to use. - if EXPORT_MTL: - mtlfilename = '%s.mtl' % '.'.join(filename.split('.')[:-1]) - file.write('mtllib %s\n' % ( mtlfilename.split('\\')[-1].split('/')[-1] )) - - # Get the container mesh. - used for applying modifiers and non mesh objects. - -# containerMesh = meshName = tempMesh = None -# for meshName in Blender.NMesh.GetNames(): -# if meshName.startswith(temp_mesh_name): -# tempMesh = Mesh.Get(meshName) -# if not tempMesh.users: -# containerMesh = tempMesh -# if not containerMesh: -# containerMesh = Mesh.New(temp_mesh_name) - - # XXX this mesh is not removed - # XXX this mesh should not be in database - containerMesh = bpy.data.add_mesh(temp_mesh_name) - - if EXPORT_ROTX90: - mat_xrot90= Blender.Mathutils.RotationMatrix(-90, 4, 'x') - -# del meshName -# del tempMesh - - # Initialize totals, these are updated each object - totverts = totuvco = totno = 1 - - face_vert_index = 1 - - globalNormals = {} - - # Get all meshes - for ob_main in objects: - - if ob_main.dupli_type != 'NONE': - ob_main.create_dupli_list() - - # ignore dupli children - if ob_main.parent.dupli_type != 'NONE': - continue - - obs = [] - if ob_main.dupli_type != 'NONE': - obs = [(dob.matrix, dob.object) for dob in ob_main.dupli_list] - else: - obs = [ob.matrix, ob] - - for ob, ob_mat in obs: - # XXX postponed -# # Nurbs curve support -# if EXPORT_CURVE_AS_NURBS and test_nurbs_compat(ob): -# if EXPORT_ROTX90: -# ob_mat = ob_mat * mat_xrot90 - -# totverts += write_nurb(file, ob, ob_mat) - -# continue -# end nurbs - - if ob.type != 'MESH': - continue - - # XXX EXPORT_APPLY_MODIFIERS is not used (always true) - # we also need influences to be copied... for EXPORT_POLYGROUPS to work - # which create_preview_mesh presumably does (CD_MASK_MDEFORMVERT flag) - me = ob.create_preview_mesh() - -# # Will work for non meshes now! :) -# me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, EXPORT_POLYGROUPS, scn) -# if not me: -# continue - - if EXPORT_UV: - faceuv = len(me.uv_layers) > 0 - else: - faceuv = False - - # We have a valid mesh - if EXPORT_TRI and me.faces: - # Add a dummy object to it. - has_quads = False - for f in me.faces: -# if len(f) == 4: - if len(f.verts) == 4: - has_quads = True - break - - if has_quads: - newob = bpy.data.add_object('MESH', 'temp_object') - scene.add_object(newob) - newob.convert_to_triface(scene) - # me will still be there - scene.remove_object(newob) -# oldmode = Mesh.Mode() -# Mesh.Mode(Mesh.SelectModes['FACE']) - -# me.sel = True -# tempob = scn.objects.new(me) -# me.quadToTriangle(0) # more=0 shortest length -# oldmode = Mesh.Mode(oldmode) -# scn.objects.unlink(tempob) - -# Mesh.Mode(oldmode) - - if EXPORT_ROTX90: - ob_mat *= mat_xrot90 - - # Make our own list so it can be sorted to reduce context switching - face_index_pairs = [ (face, index) for index, face in enumerate(me.faces)] - # faces = [ f for f in me.faces ] - - if EXPORT_EDGES: - edges = me.edges - else: - edges = [] - - if not (len(face_index_pairs)+len(edges)+len(me.verts)): # Make sure there is somthing to write -# if not (len(faces)+len(edges)+len(me.verts)): # Make sure there is somthing to write - - bpy.data.remove_mesh(me) - - continue # dont bother with this mesh. - - if EXPORT_ROTX90: - me.transform(ob_mat*mat_xrot90) - else: - me.transform(ob_mat) - - # High Quality Normals - if EXPORT_NORMALS and face_index_pairs: -# if EXPORT_NORMALS and faces: - # XXX - pass -# if EXPORT_NORMALS_HQ: -# BPyMesh.meshCalcNormals(me) -# else: -# # transforming normals is incorrect -# # when the matrix is scaled, -# # better to recalculate them -# me.calcNormals() - - # # Crash Blender - #materials = me.getMaterials(1) # 1 == will return None in the list. - materials = me.materials - - materialNames = [] - materialItems = materials[:] - if materials: - for mat in materials: - if mat: # !=None - materialNames.append(mat.name) - else: - materialNames.append(None) - # Cant use LC because some materials are None. - # materialNames = map(lambda mat: mat.name, materials) # Bug Blender, dosent account for null materials, still broken. - - # Possible there null materials, will mess up indicies - # but at least it will export, wait until Blender gets fixed. - materialNames.extend((16-len(materialNames)) * [None]) - materialItems.extend((16-len(materialItems)) * [None]) - - # Sort by Material, then images - # so we dont over context switch in the obj file. - if EXPORT_KEEP_VERT_ORDER: - pass - elif faceuv: - # XXX update - tface = me.active_uv_layer.data - - # exception only raised if Python 2.3 or lower... - try: face_index_pairs.sort(key = lambda a: (a[0].material_index, tface[a[1]].image, a[0].smooth)) - except: face_index_pairs.sort(lambda a,b: cmp((a[0].material_index, tface[a[1]].image, a[0].smooth), - (b[0].material_index, tface[b[1]].image, b[0].smooth))) - elif len(materials) > 1: - try: face_index_pairs.sort(key = lambda a: (a[0].material_index, a[0].smooth)) - except: face_index_pairs.sort(lambda a,b: cmp((a[0].material_index, a[0].smooth), - (b[0].material_index, b[0].smooth))) - else: - # no materials - try: face_index_pairs.sort(key = lambda a: a[0].smooth) - except: face_index_pairs.sort(lambda a,b: cmp(a[0].smooth, b[0].smooth)) -# if EXPORT_KEEP_VERT_ORDER: -# pass -# elif faceuv: -# try: faces.sort(key = lambda a: (a.mat, a.image, a.smooth)) -# except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) -# elif len(materials) > 1: -# try: faces.sort(key = lambda a: (a.mat, a.smooth)) -# except: faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth))) -# else: -# # no materials -# try: faces.sort(key = lambda a: a.smooth) -# except: faces.sort(lambda a,b: cmp(a.smooth, b.smooth)) - - faces = [pair[0] for pair in face_index_pairs] - - # Set the default mat to no material and no image. - contextMat = (0, 0) # Can never be this, so we will label a new material teh first chance we get. - contextSmooth = None # Will either be true or false, set bad to force initialization switch. - - if EXPORT_BLEN_OBS or EXPORT_GROUP_BY_OB: - name1 = ob.name - name2 = ob.data.name - # name2 = ob.getData(1) - if name1 == name2: - obnamestring = fixName(name1) - else: - obnamestring = '%s_%s' % (fixName(name1), fixName(name2)) - - if EXPORT_BLEN_OBS: - file.write('o %s\n' % obnamestring) # Write Object name - else: # if EXPORT_GROUP_BY_OB: - file.write('g %s\n' % obnamestring) - - - # Vert - for v in me.verts: - file.write('v %.6f %.6f %.6f\n' % tuple(v.co)) - - # UV - if faceuv: - uv_face_mapping = [[0,0,0,0] for f in faces] # a bit of a waste for tri's :/ - - uv_dict = {} # could use a set() here - uv_layer = me.active_uv_layer - for f, f_index in face_index_pairs: - - tface = uv_layer.data[f_index] - - uvs = [tface.uv1, tface.uv2, tface.uv3] - - # add another UV if it's a quad - if tface.verts[3] != 0: - uvs.append(tface.uv4) - - for uv_index, uv in enumerate(uvs): - uvkey = veckey2d(uv) - try: - uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] - except: - uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) - file.write('vt %.6f %.6f\n' % tuple(uv)) - -# uv_dict = {} # could use a set() here -# for f_index, f in enumerate(faces): - -# for uv_index, uv in enumerate(f.uv): -# uvkey = veckey2d(uv) -# try: -# uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] -# except: -# uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) -# file.write('vt %.6f %.6f\n' % tuple(uv)) - - uv_unique_count = len(uv_dict) - del uv, uvkey, uv_dict, f_index, uv_index - # Only need uv_unique_count and uv_face_mapping - - # NORMAL, Smooth/Non smoothed. - if EXPORT_NORMALS: - for f in faces: - if f.smooth: - for v in f: - noKey = veckey3d(v.normal) -# noKey = veckey3d(v.no) - if not globalNormals.has_key( noKey ): - globalNormals[noKey] = totno - totno +=1 - file.write('vn %.6f %.6f %.6f\n' % noKey) - else: - # Hard, 1 normal from the face. - noKey = veckey3d(f.normal) -# noKey = veckey3d(f.no) - if not globalNormals.has_key( noKey ): - globalNormals[noKey] = totno - totno +=1 - file.write('vn %.6f %.6f %.6f\n' % noKey) - - if not faceuv: - f_image = None - - # XXX - if EXPORT_POLYGROUPS: - # Retrieve the list of vertex groups -# vertGroupNames = me.getVertGroupNames() - - currentVGroup = '' - # Create a dictionary keyed by face id and listing, for each vertex, the vertex groups it belongs to - vgroupsMap = [[] for _i in range(len(me.verts))] -# vgroupsMap = [[] for _i in xrange(len(me.verts))] - for g in ob.vertex_groups: -# for vertexGroupName in vertGroupNames: - for vIdx, vWeight in getVertsFromGroup(me, g.index) -# for vIdx, vWeight in me.getVertsFromGroup(vertexGroupName, 1): - vgroupsMap[vIdx].append((g.name, vWeight)) - - for f_index, f in enumerate(faces): - f_v = [{"index": index, "vertex": me.verts[index]} for index in f.verts] -# f_v= f.v - f_smooth= f.smooth - f_mat = min(f.material_index, len(materialNames)-1) -# f_mat = min(f.mat, len(materialNames)-1) - if faceuv: - - tface = me.active_uv_layer.data[face_index_pairs[f_index][1]] - - f_image = tface.image - f_uv= [tface.uv1, tface.uv2, tface.uv3] - if f.verts[4] != 0: - f_uv.append(tface.uv4) -# f_image = f.image -# f_uv= f.uv - - # MAKE KEY - if faceuv and f_image: # Object is always true. - key = materialNames[f_mat], f_image.name - else: - key = materialNames[f_mat], None # No image, use None instead. - - # XXX - # Write the vertex group - if EXPORT_POLYGROUPS: - if len(ob.vertex_groups): - # find what vertext group the face belongs to - theVGroup = findVertexGroupName(f,vgroupsMap) - if theVGroup != currentVGroup: - currentVGroup = theVGroup - file.write('g %s\n' % theVGroup) -# # Write the vertex group -# if EXPORT_POLYGROUPS: -# if vertGroupNames: -# # find what vertext group the face belongs to -# theVGroup = findVertexGroupName(f,vgroupsMap) -# if theVGroup != currentVGroup: -# currentVGroup = theVGroup -# file.write('g %s\n' % theVGroup) - - # CHECK FOR CONTEXT SWITCH - if key == contextMat: - pass # Context alredy switched, dont do anything - else: - if key[0] == None and key[1] == None: - # Write a null material, since we know the context has changed. - if EXPORT_GROUP_BY_MAT: - # can be mat_image or (null) - file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.data.name)) ) -# file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.getData(1))) ) # can be mat_image or (null) - file.write('usemtl (null)\n') # mat, image - - else: - mat_data= MTL_DICT.get(key) - if not mat_data: - # First add to global dict so we can export to mtl - # Then write mtl - - # Make a new names from the mat and image name, - # converting any spaces to underscores with fixName. - - # If none image dont bother adding it to the name - if key[1] == None: - mat_data = MTL_DICT[key] = ('%s'%fixName(key[0])), materialItems[f_mat], f_image - else: - mat_data = MTL_DICT[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image - - if EXPORT_GROUP_BY_MAT: - file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.data.name), mat_data[0]) ) # can be mat_image or (null) -# file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.getData(1)), mat_data[0]) ) # can be mat_image or (null) - - file.write('usemtl %s\n' % mat_data[0]) # can be mat_image or (null) - - contextMat = key - if f_smooth != contextSmooth: - if f_smooth: # on now off - file.write('s 1\n') - contextSmooth = f_smooth - else: # was off now on - file.write('s off\n') - contextSmooth = f_smooth - - file.write('f') - if faceuv: - if EXPORT_NORMALS: - if f_smooth: # Smoothed, use vertex normals - for vi, v in enumerate(f_v): - file.write( ' %d/%d/%d' % \ - (v["index"] + totverts, - totuvco + uv_face_mapping[f_index][vi], - globalNormals[ veckey3d(v["vertex"].normal) ]) ) # vert, uv, normal -# file.write( ' %d/%d/%d' % (\ -# v.index+totverts,\ -# totuvco + uv_face_mapping[f_index][vi],\ -# globalNormals[ veckey3d(v.no) ])) # vert, uv, normal - - else: # No smoothing, face normals - no = globalNormals[ veckey3d(f.normal) ] -# no = globalNormals[ veckey3d(f.no) ] - for vi, v in enumerate(f_v): - file.write( ' %d/%d/%d' % \ - (v["index"] + totverts, - totuvco + uv_face_mapping[f_index][vi], - no) ) # vert, uv, normal -# file.write( ' %d/%d/%d' % (\ -# v.index+totverts,\ -# totuvco + uv_face_mapping[f_index][vi],\ -# no)) # vert, uv, normal - - else: # No Normals - for vi, v in enumerate(f_v): - file.write( ' %d/%d' % (\ - v["index"] + totverts,\ - totuvco + uv_face_mapping[f_index][vi])) # vert, uv -# file.write( ' %d/%d' % (\ -# v.index+totverts,\ -# totuvco + uv_face_mapping[f_index][vi])) # vert, uv - - face_vert_index += len(f_v) - - else: # No UV's - if EXPORT_NORMALS: - if f_smooth: # Smoothed, use vertex normals - for v in f_v: - file.write( ' %d//%d' % - (v["index"] + totverts, globalNormals[ veckey3d(v["vertex"].normal) ]) ) - -# file.write( ' %d//%d' % (\ -# v.index+totverts,\ -# globalNormals[ veckey3d(v.no) ])) - else: # No smoothing, face normals - no = globalNormals[ veckey3d(f.normal) ] -# no = globalNormals[ veckey3d(f.no) ] - for v in f_v: - file.write( ' %d//%d' % (v["index"] + totverts, no) ) -# file.write( ' %d//%d' % (\ -# v.index+totverts,\ -# no)) - else: # No Normals - for v in f_v: - file.write( ' %d' % (v["index"] + totverts) ) -# file.write( ' %d' % (\ -# v.index+totverts)) - - file.write('\n') - - # Write edges. - if EXPORT_EDGES: - for ed in edges: - if ed.loose: - file.write('f %d %d\n' % (ed.verts[0] + totverts, ed.verts[1] + totverts)) -# LOOSE= Mesh.EdgeFlags.LOOSE -# for ed in edges: -# if ed.flag & LOOSE: -# file.write('f %d %d\n' % (ed.v1.index+totverts, ed.v2.index+totverts)) - - # Make the indicies global rather then per mesh - totverts += len(me.verts) - if faceuv: - totuvco += uv_unique_count - - # clean up - bpy.data.remove_mesh(me) -# me.verts= None - - if ob_main.dupli_type != 'NONE': - ob_main.free_dupli_list() - - file.close() - - - # Now we have all our materials, save them - if EXPORT_MTL: - write_mtl(scene, mtlfilename) - if EXPORT_COPY_IMAGES: - dest_dir = filename - # Remove chars until we are just the path. - while dest_dir and dest_dir[-1] not in '\\/': - dest_dir = dest_dir[:-1] - if dest_dir: - copy_images(dest_dir) - else: - print '\tError: "%s" could not be used as a base for an image path.' % filename - - print "OBJ Export time: %.2f" % (sys.time() - time1) - - -# replaced by do_export -def write_ui(filename): - - if not filename.lower().endswith('.obj'): - filename += '.obj' - - if not BPyMessages.Warning_SaveOver(filename): - return - - global EXPORT_APPLY_MODIFIERS, EXPORT_ROTX90, EXPORT_TRI, EXPORT_EDGES,\ - EXPORT_NORMALS, EXPORT_NORMALS_HQ, EXPORT_UV,\ - EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES,\ - EXPORT_ANIMATION, EXPORT_COPY_IMAGES, EXPORT_BLEN_OBS,\ - EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER,\ - EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS - - EXPORT_APPLY_MODIFIERS = Draw.Create(0) - EXPORT_ROTX90 = Draw.Create(1) - EXPORT_TRI = Draw.Create(0) - EXPORT_EDGES = Draw.Create(1) - EXPORT_NORMALS = Draw.Create(0) - EXPORT_NORMALS_HQ = Draw.Create(0) - EXPORT_UV = Draw.Create(1) - EXPORT_MTL = Draw.Create(1) - EXPORT_SEL_ONLY = Draw.Create(1) - EXPORT_ALL_SCENES = Draw.Create(0) - EXPORT_ANIMATION = Draw.Create(0) - EXPORT_COPY_IMAGES = Draw.Create(0) - EXPORT_BLEN_OBS = Draw.Create(0) - EXPORT_GROUP_BY_OB = Draw.Create(0) - EXPORT_GROUP_BY_MAT = Draw.Create(0) - EXPORT_KEEP_VERT_ORDER = Draw.Create(1) - EXPORT_POLYGROUPS = Draw.Create(0) - EXPORT_CURVE_AS_NURBS = Draw.Create(1) - - - # Old UI - ''' - # removed too many options are bad! - - # Get USER Options - pup_block = [\ - ('Context...'),\ - ('Selection Only', EXPORT_SEL_ONLY, 'Only export objects in visible selection. Else export whole scene.'),\ - ('All Scenes', EXPORT_ALL_SCENES, 'Each scene as a separate OBJ file.'),\ - ('Animation', EXPORT_ANIMATION, 'Each frame as a numbered OBJ file.'),\ - ('Object Prefs...'),\ - ('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object. May break vert order for morph targets.'),\ - ('Rotate X90', EXPORT_ROTX90 , 'Rotate on export so Blenders UP is translated into OBJs UP'),\ - ('Keep Vert Order', EXPORT_KEEP_VERT_ORDER, 'Keep vert and face order, disables some other options.'),\ - ('Extra Data...'),\ - ('Edges', EXPORT_EDGES, 'Edges not connected to faces.'),\ - ('Normals', EXPORT_NORMALS, 'Export vertex normal data (Ignored on import).'),\ - ('High Quality Normals', EXPORT_NORMALS_HQ, 'Calculate high quality normals for rendering.'),\ - ('UVs', EXPORT_UV, 'Export texface UV coords.'),\ - ('Materials', EXPORT_MTL, 'Write a separate MTL file with the OBJ.'),\ - ('Copy Images', EXPORT_COPY_IMAGES, 'Copy image files to the export directory, never overwrite.'),\ - ('Triangulate', EXPORT_TRI, 'Triangulate quads.'),\ - ('Grouping...'),\ - ('Objects', EXPORT_BLEN_OBS, 'Export blender objects as "OBJ objects".'),\ - ('Object Groups', EXPORT_GROUP_BY_OB, 'Export blender objects as "OBJ Groups".'),\ - ('Material Groups', EXPORT_GROUP_BY_MAT, 'Group by materials.'),\ - ] - - if not Draw.PupBlock('Export...', pup_block): - return - ''' - - # BEGIN ALTERNATIVE UI ******************* - if True: - - EVENT_NONE = 0 - EVENT_EXIT = 1 - EVENT_REDRAW = 2 - EVENT_EXPORT = 3 - - GLOBALS = {} - GLOBALS['EVENT'] = EVENT_REDRAW - #GLOBALS['MOUSE'] = Window.GetMouseCoords() - GLOBALS['MOUSE'] = [i/2 for i in Window.GetScreenSize()] - - def obj_ui_set_event(e,v): - GLOBALS['EVENT'] = e - - def do_split(e,v): - global EXPORT_BLEN_OBS, EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_APPLY_MODIFIERS, KEEP_VERT_ORDER, EXPORT_POLYGROUPS - if EXPORT_BLEN_OBS.val or EXPORT_GROUP_BY_OB.val or EXPORT_GROUP_BY_MAT.val or EXPORT_APPLY_MODIFIERS.val: - EXPORT_KEEP_VERT_ORDER.val = 0 - else: - EXPORT_KEEP_VERT_ORDER.val = 1 - - def do_vertorder(e,v): - global EXPORT_BLEN_OBS, EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_APPLY_MODIFIERS, KEEP_VERT_ORDER - if EXPORT_KEEP_VERT_ORDER.val: - EXPORT_BLEN_OBS.val = EXPORT_GROUP_BY_OB.val = EXPORT_GROUP_BY_MAT.val = EXPORT_APPLY_MODIFIERS.val = 0 - else: - if not (EXPORT_BLEN_OBS.val or EXPORT_GROUP_BY_OB.val or EXPORT_GROUP_BY_MAT.val or EXPORT_APPLY_MODIFIERS.val): - EXPORT_KEEP_VERT_ORDER.val = 1 - - - def do_help(e,v): - url = __url__[0] - print 'Trying to open web browser with documentation at this address...' - print '\t' + url - - try: - import webbrowser - webbrowser.open(url) - except: - print '...could not open a browser window.' - - def obj_ui(): - ui_x, ui_y = GLOBALS['MOUSE'] - - # Center based on overall pup size - ui_x -= 165 - ui_y -= 140 - - global EXPORT_APPLY_MODIFIERS, EXPORT_ROTX90, EXPORT_TRI, EXPORT_EDGES,\ - EXPORT_NORMALS, EXPORT_NORMALS_HQ, EXPORT_UV,\ - EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES,\ - EXPORT_ANIMATION, EXPORT_COPY_IMAGES, EXPORT_BLEN_OBS,\ - EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER,\ - EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS - - Draw.Label('Context...', ui_x+9, ui_y+239, 220, 20) - Draw.BeginAlign() - EXPORT_SEL_ONLY = Draw.Toggle('Selection Only', EVENT_NONE, ui_x+9, ui_y+219, 110, 20, EXPORT_SEL_ONLY.val, 'Only export objects in visible selection. Else export whole scene.') - EXPORT_ALL_SCENES = Draw.Toggle('All Scenes', EVENT_NONE, ui_x+119, ui_y+219, 110, 20, EXPORT_ALL_SCENES.val, 'Each scene as a separate OBJ file.') - EXPORT_ANIMATION = Draw.Toggle('Animation', EVENT_NONE, ui_x+229, ui_y+219, 110, 20, EXPORT_ANIMATION.val, 'Each frame as a numbered OBJ file.') - Draw.EndAlign() - - - Draw.Label('Output Options...', ui_x+9, ui_y+189, 220, 20) - Draw.BeginAlign() - EXPORT_APPLY_MODIFIERS = Draw.Toggle('Apply Modifiers', EVENT_REDRAW, ui_x+9, ui_y+170, 110, 20, EXPORT_APPLY_MODIFIERS.val, 'Use transformed mesh data from each object. May break vert order for morph targets.', do_split) - EXPORT_ROTX90 = Draw.Toggle('Rotate X90', EVENT_NONE, ui_x+119, ui_y+170, 110, 20, EXPORT_ROTX90.val, 'Rotate on export so Blenders UP is translated into OBJs UP') - EXPORT_COPY_IMAGES = Draw.Toggle('Copy Images', EVENT_NONE, ui_x+229, ui_y+170, 110, 20, EXPORT_COPY_IMAGES.val, 'Copy image files to the export directory, never overwrite.') - Draw.EndAlign() - - - Draw.Label('Export...', ui_x+9, ui_y+139, 220, 20) - Draw.BeginAlign() - EXPORT_EDGES = Draw.Toggle('Edges', EVENT_NONE, ui_x+9, ui_y+120, 50, 20, EXPORT_EDGES.val, 'Edges not connected to faces.') - EXPORT_TRI = Draw.Toggle('Triangulate', EVENT_NONE, ui_x+59, ui_y+120, 70, 20, EXPORT_TRI.val, 'Triangulate quads.') - Draw.EndAlign() - Draw.BeginAlign() - EXPORT_MTL = Draw.Toggle('Materials', EVENT_NONE, ui_x+139, ui_y+120, 70, 20, EXPORT_MTL.val, 'Write a separate MTL file with the OBJ.') - EXPORT_UV = Draw.Toggle('UVs', EVENT_NONE, ui_x+209, ui_y+120, 31, 20, EXPORT_UV.val, 'Export texface UV coords.') - Draw.EndAlign() - Draw.BeginAlign() - EXPORT_NORMALS = Draw.Toggle('Normals', EVENT_NONE, ui_x+250, ui_y+120, 59, 20, EXPORT_NORMALS.val, 'Export vertex normal data (Ignored on import).') - EXPORT_NORMALS_HQ = Draw.Toggle('HQ', EVENT_NONE, ui_x+309, ui_y+120, 31, 20, EXPORT_NORMALS_HQ.val, 'Calculate high quality normals for rendering.') - Draw.EndAlign() - EXPORT_POLYGROUPS = Draw.Toggle('Polygroups', EVENT_REDRAW, ui_x+9, ui_y+95, 120, 20, EXPORT_POLYGROUPS.val, 'Export vertex groups as OBJ groups (one group per face approximation).') - - EXPORT_CURVE_AS_NURBS = Draw.Toggle('Nurbs', EVENT_NONE, ui_x+139, ui_y+95, 100, 20, EXPORT_CURVE_AS_NURBS.val, 'Export 3D nurbs curves and polylines as OBJ curves, (bezier not supported).') - - - Draw.Label('Blender Objects as OBJ:', ui_x+9, ui_y+59, 220, 20) - Draw.BeginAlign() - EXPORT_BLEN_OBS = Draw.Toggle('Objects', EVENT_REDRAW, ui_x+9, ui_y+39, 60, 20, EXPORT_BLEN_OBS.val, 'Export blender objects as "OBJ objects".', do_split) - EXPORT_GROUP_BY_OB = Draw.Toggle('Groups', EVENT_REDRAW, ui_x+69, ui_y+39, 60, 20, EXPORT_GROUP_BY_OB.val, 'Export blender objects as "OBJ Groups".', do_split) - EXPORT_GROUP_BY_MAT = Draw.Toggle('Material Groups', EVENT_REDRAW, ui_x+129, ui_y+39, 100, 20, EXPORT_GROUP_BY_MAT.val, 'Group by materials.', do_split) - Draw.EndAlign() - - EXPORT_KEEP_VERT_ORDER = Draw.Toggle('Keep Vert Order', EVENT_REDRAW, ui_x+239, ui_y+39, 100, 20, EXPORT_KEEP_VERT_ORDER.val, 'Keep vert and face order, disables some other options. Use for morph targets.', do_vertorder) - - Draw.BeginAlign() - Draw.PushButton('Online Help', EVENT_REDRAW, ui_x+9, ui_y+9, 110, 20, 'Load the wiki page for this script', do_help) - Draw.PushButton('Cancel', EVENT_EXIT, ui_x+119, ui_y+9, 110, 20, '', obj_ui_set_event) - Draw.PushButton('Export', EVENT_EXPORT, ui_x+229, ui_y+9, 110, 20, 'Export with these settings', obj_ui_set_event) - Draw.EndAlign() - - - # hack so the toggle buttons redraw. this is not nice at all - while GLOBALS['EVENT'] not in (EVENT_EXIT, EVENT_EXPORT): - Draw.UIBlock(obj_ui, 0) - - if GLOBALS['EVENT'] != EVENT_EXPORT: - return - - # END ALTERNATIVE UI ********************* - - - if EXPORT_KEEP_VERT_ORDER.val: - EXPORT_BLEN_OBS.val = False - EXPORT_GROUP_BY_OB.val = False - EXPORT_GROUP_BY_MAT.val = False - EXPORT_APPLY_MODIFIERS.val = False - - Window.EditMode(0) - Window.WaitCursor(1) - - EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val - EXPORT_ROTX90 = EXPORT_ROTX90.val - EXPORT_TRI = EXPORT_TRI.val - EXPORT_EDGES = EXPORT_EDGES.val - EXPORT_NORMALS = EXPORT_NORMALS.val - EXPORT_NORMALS_HQ = EXPORT_NORMALS_HQ.val - EXPORT_UV = EXPORT_UV.val - EXPORT_MTL = EXPORT_MTL.val - EXPORT_SEL_ONLY = EXPORT_SEL_ONLY.val - EXPORT_ALL_SCENES = EXPORT_ALL_SCENES.val - EXPORT_ANIMATION = EXPORT_ANIMATION.val - EXPORT_COPY_IMAGES = EXPORT_COPY_IMAGES.val - EXPORT_BLEN_OBS = EXPORT_BLEN_OBS.val - EXPORT_GROUP_BY_OB = EXPORT_GROUP_BY_OB.val - EXPORT_GROUP_BY_MAT = EXPORT_GROUP_BY_MAT.val - EXPORT_KEEP_VERT_ORDER = EXPORT_KEEP_VERT_ORDER.val - EXPORT_POLYGROUPS = EXPORT_POLYGROUPS.val - EXPORT_CURVE_AS_NURBS = EXPORT_CURVE_AS_NURBS.val - - - base_name, ext = splitExt(filename) - context_name = [base_name, '', '', ext] # basename, scene_name, framenumber, extension - - # Use the options to export the data using write() - # def write(filename, objects, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False, EXPORT_APPLY_MODIFIERS=True): - orig_scene = Scene.GetCurrent() - if EXPORT_ALL_SCENES: - export_scenes = Scene.Get() - else: - export_scenes = [orig_scene] - - # Export all scenes. - for scn in export_scenes: - scn.makeCurrent() # If alredy current, this is not slow. - context = scn.getRenderingContext() - orig_frame = Blender.Get('curframe') - - if EXPORT_ALL_SCENES: # Add scene name into the context_name - context_name[1] = '_%s' % BPySys.cleanName(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied. - - # Export an animation? - if EXPORT_ANIMATION: - scene_frames = xrange(context.startFrame(), context.endFrame()+1) # up to and including the end frame. - else: - scene_frames = [orig_frame] # Dont export an animation. - - # Loop through all frames in the scene and export. - for frame in scene_frames: - if EXPORT_ANIMATION: # Add frame to the filename. - context_name[2] = '_%.6d' % frame - - Blender.Set('curframe', frame) - if EXPORT_SEL_ONLY: - export_objects = scn.objects.context - else: - export_objects = scn.objects - - full_path= ''.join(context_name) - - # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad. - # EXPORT THE FILE. - write(full_path, export_objects,\ - EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS,\ - EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL,\ - EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS,\ - EXPORT_ROTX90, EXPORT_BLEN_OBS,\ - EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER,\ - EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS) - - Blender.Set('curframe', orig_frame) - - # Restore old active scene. - orig_scene.makeCurrent() - Window.WaitCursor(0) - - -def do_export(filename, context): - # Window.EditMode(0) - # Window.WaitCursor(1) - - EXPORT_APPLY_MODIFIERS = True - EXPORT_ROTX90 = True - EXPORT_TRI = False - EXPORT_EDGES = False - EXPORT_NORMALS = False - EXPORT_NORMALS_HQ = False - EXPORT_UV = True - EXPORT_MTL = True - EXPORT_SEL_ONLY = True - EXPORT_ALL_SCENES = False # XXX not working atm - EXPORT_ANIMATION = False - EXPORT_COPY_IMAGES = False - EXPORT_BLEN_OBS = True - EXPORT_GROUP_BY_OB = False - EXPORT_GROUP_BY_MAT = False - EXPORT_KEEP_VERT_ORDER = False - EXPORT_POLYGROUPS = False - EXPORT_CURVE_AS_NURBS = True - - base_name, ext = splitExt(filename) - context_name = [base_name, '', '', ext] # Base name, scene name, frame number, extension - - orig_scene = context.scene - -# if EXPORT_ALL_SCENES: -# export_scenes = bpy.data.scenes -# else: -# export_scenes = [orig_scene] - - # XXX only exporting one scene atm since changing - # current scene is not possible. - # Brecht says that ideally in 2.5 we won't need such a function, - # allowing multiple scenes open at once. - export_scenes = [orig_scene] - - # Export all scenes. - for scn in export_scenes: - # scn.makeCurrent() # If already current, this is not slow. - # context = scn.getRenderingContext() - orig_frame = scn.current_frame - - if EXPORT_ALL_SCENES: # Add scene name into the context_name - context_name[1] = '_%s' % BPySys.cleanName(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied. - - # Export an animation? - if EXPORT_ANIMATION: - scene_frames = xrange(scn.start_frame, context.end_frame+1) # Up to and including the end frame. - else: - scene_frames = [orig_frame] # Dont export an animation. - - # Loop through all frames in the scene and export. - for frame in scene_frames: - if EXPORT_ANIMATION: # Add frame to the filename. - context_name[2] = '_%.6d' % frame - - scn.current_frame = frame - if EXPORT_SEL_ONLY: - export_objects = context.selected_objects - else: - export_objects = scn.objects - - full_path= ''.join(context_name) - - # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad. - # EXPORT THE FILE. - write(full_path, export_objects, scn, - EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS, - EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL, - EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS, - EXPORT_ROTX90, EXPORT_BLEN_OBS, - EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER, - EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS) - - - scn.current_frame = orig_frame - - # Restore old active scene. -# orig_scene.makeCurrent() -# Window.WaitCursor(0) - - -class EXPORT_OT_obj(bpy.types.Operator): - ''' - Currently the exporter lacks these features: - * nurbs - * multiple scene export (only active scene is written) - * particles - ''' - __label__ = 'Export OBJ' - - # List of operator properties, the attributes will be assigned - # to the class instance from the operator settings before calling. - __props__ = [ - # bpy.types.FloatProperty(attr="setting_1", name="Example 1", - # default=10.0, min=0, max=10, description="Add info here"), - # bpy.types.IntProperty(attr="setting_2", default=2), - # bpy.types.BoolProperty(attr="toggle", default=True) - ] - - def execu(self, context): - print("Selected: " + context.active_object.name) - - do_export("/tmp/test.obj", context) - - return 'FINISHED' - - def invoke(self, event): - print("Invoke") - return 'FINISHED' - - def poll(self, context): # Poll isnt working yet - print("Poll") - return True - -if (hasattr(bpy.ops, "SCRIPT_OT_export_obj")): - bpy.ops.remove(bpy.ops.SCRIPT_OT_export_obj) - -bpy.ops.add(SCRIPT_OT_export_obj) - -bpy.ops.SCRIPT_OT_export_obj() - -bpy.ops.remove(bpy.ops.SCRIPT_OT_export_obj) diff --git a/release/ui/space_script.py b/release/ui/space_script.py index d35f2d389c8..9b0809ffc75 100644 --- a/release/ui/space_script.py +++ b/release/ui/space_script.py @@ -32,7 +32,7 @@ class SCRIPT_HT_header(bpy.types.Header): if context.area.show_menus: row = layout.row(align=True) - row.itemM(context, "SCRIPT_MT_scripts") + row.itemM("SCRIPT_MT_scripts") class SCRIPT_MT_scripts(bpy.types.Menu): __space_type__ = "SCRIPTS_WINDOW" @@ -41,7 +41,7 @@ class SCRIPT_MT_scripts(bpy.types.Menu): def draw(self, context): layout = self.layout layout.column() - layout.itemM(context, "SCRIPT_MT_export") + layout.itemM("SCRIPT_MT_export") layout.itemO("SCRIPT_OT_reload_scripts") class SCRIPT_MT_export(bpy.types.Menu): diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index 984e6a5d30f..f4bc52bc517 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -36,6 +36,8 @@ #include "BKE_customdata.h" #include "BKE_DerivedMesh.h" +#include "BKE_mesh.h" + #include "BLI_arithb.h" #include "DNA_mesh_types.h" @@ -66,6 +68,14 @@ void rna_Mesh_transform(Mesh *me, float *mat) } } +Mesh *rna_Mesh_create_copy(Mesh *me) +{ + Mesh *ret= copy_mesh(me); + ret->id.us--; + + return ret; +} + #if 0 /* extern struct EditVert *addvertlist(EditMesh *em, float *vec, struct EditVert *example); */ @@ -101,6 +111,11 @@ void RNA_api_mesh(StructRNA *srna) parm= RNA_def_float_matrix(func, "matrix", 16, NULL, 0.0f, 0.0f, "", "Matrix.", 0.0f, 0.0f); RNA_def_property_flag(parm, PROP_REQUIRED); + func= RNA_def_function(srna, "create_copy", "rna_Mesh_create_copy"); + RNA_def_function_ui_description(func, "Create a copy of this Mesh datablock."); + parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh, remove it if it is only used for export."); + RNA_def_function_return(func, parm); + /* func= RNA_def_function(srna, "add_geom", "rna_Mesh_add_geom"); RNA_def_function_ui_description(func, "Add geometry data to mesh."); diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 1b6c298eba4..b09acb51084 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -36,6 +36,12 @@ #include "DNA_object_types.h" +/* parameter to rna_Object_create_mesh */ +typedef enum CreateMeshType { + CREATE_MESH_PREVIEW = 0, + CREATE_MESH_RENDER = 1 +} CreateMeshType; + #ifdef RNA_RUNTIME #include "BKE_customdata.h" @@ -55,7 +61,7 @@ #include "ED_mesh.h" /* copied from init_render_mesh (render code) */ -static Mesh *create_mesh(Object *ob, bContext *C, ReportList *reports, int render_mesh) +static Mesh *rna_Object_create_mesh(Object *ob, bContext *C, ReportList *reports, int type) { /* CustomDataMask mask = CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; */ CustomDataMask mask = CD_MASK_MESH; /* this seems more suitable, exporter, @@ -71,8 +77,13 @@ static Mesh *create_mesh(Object *ob, bContext *C, ReportList *reports, int rende BKE_report(reports, RPT_ERROR, "Object should be of type MESH."); return NULL; } - - dm= render_mesh ? mesh_create_derived_render(sce, ob, mask) : mesh_create_derived_view(sce, ob, mask); + + if (type == CREATE_MESH_PREVIEW) { + dm= mesh_create_derived_view(sce, ob, mask); + } + else { + dm= mesh_create_derived_render(sce, ob, mask); + } if(!dm) { /* TODO: report */ @@ -87,16 +98,6 @@ static Mesh *create_mesh(Object *ob, bContext *C, ReportList *reports, int rende return me; } -static Mesh *rna_Object_create_render_mesh(Object *ob, bContext *C, ReportList *reports) -{ - return create_mesh(ob, C, reports, 1); -} - -static Mesh *rna_Object_create_preview_mesh(Object *ob, bContext *C, ReportList *reports) -{ - return create_mesh(ob, C, reports, 0); -} - /* When no longer needed, duplilist should be freed with Object.free_duplilist */ static void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *reports) { @@ -162,33 +163,20 @@ void RNA_api_object(StructRNA *srna) FunctionRNA *func; PropertyRNA *parm; - /* copied from rna_def_object */ - static EnumPropertyItem object_type_items[] = { - {OB_EMPTY, "EMPTY", 0, "Empty", ""}, - {OB_MESH, "MESH", 0, "Mesh", ""}, - {OB_CURVE, "CURVE", 0, "Curve", ""}, - {OB_SURF, "SURFACE", 0, "Surface", ""}, - {OB_FONT, "TEXT", 0, "Text", ""}, - {OB_MBALL, "META", 0, "Meta", ""}, - {OB_LAMP, "LAMP", 0, "Lamp", ""}, - {OB_CAMERA, "CAMERA", 0, "Camera", ""}, - {OB_WAVE, "WAVE", 0, "Wave", ""}, - {OB_LATTICE, "LATTICE", 0, "Lattice", ""}, - {OB_ARMATURE, "ARMATURE", 0, "Armature", ""}, - {0, NULL, 0, NULL, NULL}}; + static EnumPropertyItem mesh_type_items[] = { + {CREATE_MESH_PREVIEW, "PREVIEW", 0, "Preview", "Apply preview settings."}, + {CREATE_MESH_RENDER, "RENDER", 0, "Render", "Apply render settings."}, + {0, NULL, 0, NULL, NULL} + }; - func= RNA_def_function(srna, "create_render_mesh", "rna_Object_create_render_mesh"); - RNA_def_function_ui_description(func, "Create a Mesh datablock with all modifiers applied for rendering."); + func= RNA_def_function(srna, "create_mesh", "rna_Object_create_mesh"); + RNA_def_function_ui_description(func, "Create a Mesh datablock with all modifiers applied."); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); + parm= RNA_def_enum(func, "type", mesh_type_items, 0, "", "Type of mesh settings to apply."); + RNA_def_property_flag(parm, PROP_REQUIRED); parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object, remove it if it is only used for export."); RNA_def_function_return(func, parm); - func= RNA_def_function(srna, "create_preview_mesh", "rna_Object_create_preview_mesh"); - RNA_def_function_ui_description(func, "Create a Mesh datablock with all modifiers applied for preview."); - RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); - parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object, remove it if it is only used for export."); - RNA_def_function_return(func, parm); - func= RNA_def_function(srna, "create_dupli_list", "rna_Object_create_duplilist"); RNA_def_function_ui_description(func, "Create a list of dupli objects for this object, needs to be freed manually with free_dupli_list."); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 8b5ad36f349..96ef796839b 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -19,6 +19,7 @@ #include "bpy_rna.h" #include "bpy_operator.h" #include "bpy_ui.h" +#include "bpy_sys.h" #include "bpy_util.h" #include "DNA_anim_types.h" @@ -91,6 +92,7 @@ void BPY_update_modules( void ) PyObject *mod= PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0); PyModule_AddObject( mod, "data", BPY_rna_module() ); PyModule_AddObject( mod, "types", BPY_rna_types() ); + PyModule_AddObject( mod, "sys", BPY_sys_module() ); } /***************************************************************************** diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index b7e3c86dd91..4e5536c8552 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -331,7 +331,7 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class) {PYOP_ATTR_UINAME, 's', 0, BPY_CLASS_ATTR_OPTIONAL}, {PYOP_ATTR_PROP, 'l', 0, BPY_CLASS_ATTR_OPTIONAL}, {PYOP_ATTR_DESCRIPTION, 's', 0, BPY_CLASS_ATTR_NONE_OK}, - {"execute", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, + {"execute", 'f', 2, 0}, {"invoke", 'f', 3, BPY_CLASS_ATTR_OPTIONAL}, {"poll", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, {NULL, 0, 0, 0} diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 0b8a7df1ae1..90876b36ee0 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -1367,8 +1367,12 @@ PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *data) newptr= *(PointerRNA*)data; } else { - /* XXX this is missing the ID part! */ - RNA_pointer_create(NULL, type, *(void**)data, &newptr); + if (RNA_struct_is_ID(type)) { + RNA_id_pointer_create(*(void**)data, &newptr); + } + else { + RNA_pointer_create(NULL, type, *(void**)data, &newptr); + } } if (newptr.data) { diff --git a/source/blender/python/intern/bpy_sys.c b/source/blender/python/intern/bpy_sys.c new file mode 100644 index 00000000000..4d80d3bff93 --- /dev/null +++ b/source/blender/python/intern/bpy_sys.c @@ -0,0 +1,460 @@ +/* + * $Id: Sys.c 17889 2008-12-16 11:26:55Z campbellbarton $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * This is a new part of Blender. + * + * Contributor(s): Willian P. Germano, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** +*/ + +#include "bpy_sys.h" /*This must come first*/ +#include "bpy_util.h" + +#include "BKE_utildefines.h" +#include "BKE_global.h" +#include "BKE_context.h" + +#include "BLI_blenlib.h" + +#include "DNA_scene_types.h" /* G.scene-"r.cfra */ + +#include "PIL_time.h" +/* #include "gen_utils.h" */ + +#ifdef WIN32 +#define DIRSEP '\\' +#define DIRSEP_STR "\\" +#else +#define DIRSEP '/' +#define DIRSEP_STR "/" +#endif + + +/*****************************************************************************/ +/* Python API function prototypes for the sys module. */ +/*****************************************************************************/ +static PyObject *M_sys_basename( PyObject * self, PyObject * value ); +static PyObject *M_sys_dirname( PyObject * self, PyObject * value ); +static PyObject *M_sys_join( PyObject * self, PyObject * args ); +static PyObject *M_sys_splitext( PyObject * self, PyObject * value ); +static PyObject *M_sys_makename( PyObject * self, PyObject * args, + PyObject * kw ); +static PyObject *M_sys_exists( PyObject * self, PyObject * value ); +static PyObject *M_sys_time( PyObject * self ); +static PyObject *M_sys_sleep( PyObject * self, PyObject * args ); +static PyObject *M_sys_expandpath( PyObject *self, PyObject *value); +static PyObject *M_sys_cleanpath( PyObject *self, PyObject *value); +static PyObject *M_sys_relpath( PyObject *self, PyObject *args); + +/*****************************************************************************/ +/* The following string definitions are used for documentation strings. */ +/* In Python these will be written to the console when doing a */ +/* Blender.sys.__doc__ */ +/*****************************************************************************/ +static char M_sys_doc[] = "The Blender.sys submodule\n\ +\n\ +This is a minimal system module to supply simple functionality available\n\ +in the default Python module os."; + +static char M_sys_basename_doc[] = + "(path) - Split 'path' in dir and filename.\n\ +Return the filename."; + +static char M_sys_dirname_doc[] = + "(path) - Split 'path' in dir and filename.\n\ +Return the dir."; + +static char M_sys_join_doc[] = + "(dir, file) - Join dir and file to form a full filename.\n\ +Return the filename."; + +static char M_sys_splitext_doc[] = + "(path) - Split 'path' in root and extension:\n\ +/this/that/file.ext -> ('/this/that/file','.ext').\n\ +Return the pair (root, extension)."; + +static char M_sys_makename_doc[] = + "(path = Blender.Get('filename'), ext = \"\", strip = 0) -\n\ +Strip dir and extension from path, leaving only a name, then append 'ext'\n\ +to it (if given) and return the resulting string.\n\n\ +(path) - string: a pathname -- Blender.Get('filename') if 'path' isn't given;\n\ +(ext = \"\") - string: the extension to append.\n\ +(strip = 0) - int: strip dirname from 'path' if given and non-zero.\n\ +Ex: makename('/path/to/file/myfile.foo','-01.abc') returns 'myfile-01.abc'\n\ +Ex: makename(ext='.txt') returns 'untitled.txt' if Blender.Get('filename')\n\ +returns a path to the file 'untitled.blend'"; + +static char M_sys_time_doc[] = + "() - Return a float representing time elapsed in seconds.\n\ +Each successive call is garanteed to return values greater than or\n\ +equal to the previous call."; + +static char M_sys_sleep_doc[] = + "(milliseconds = 10) - Sleep for the specified time.\n\ +(milliseconds = 10) - the amount of time in milliseconds to sleep.\n\ +This function can be necessary in tight 'get event' loops."; + +static char M_sys_exists_doc[] = + "(path) - Check if the given pathname exists.\n\ +The return value is as follows:\n\ +\t 0: path doesn't exist;\n\ +\t 1: path is an existing filename;\n\ +\t 2: path is an existing dirname;\n\ +\t-1: path exists but is neither a regular file nor a dir."; + +static char M_sys_expandpath_doc[] = +"(path) - Expand this Blender internal path to a proper file system path.\n\ +(path) - the string path to convert.\n\n\ +Note: internally Blender paths can contain two special character sequences:\n\ +- '//' (at start) for base path directory (the current .blend's dir path);\n\ +- '#' characters in the filename will be replaced by the frame number.\n\n\ +This function expands these to their actual content, returning a valid path.\n\ +If the special chars are not found in the given path, it is simply returned."; + +static char M_sys_cleanpath_doc[] = +"(path) - Removes parts of a path that are not needed paths such as '../foo/../bar/' and '//./././'"; + +static char M_sys_relpath_doc[] = +"(path, start=\"//\") - Returns the path relative to the current blend file or start if spesified"; + +/*****************************************************************************/ +/* Python method structure definition for Blender.sys module: */ +/*****************************************************************************/ +struct PyMethodDef M_sys_methods[] = { + {"basename", M_sys_basename, METH_O, M_sys_basename_doc}, + {"dirname", M_sys_dirname, METH_O, M_sys_dirname_doc}, + {"join", M_sys_join, METH_VARARGS, M_sys_join_doc}, + {"splitext", M_sys_splitext, METH_O, M_sys_splitext_doc}, + {"makename", ( PyCFunction ) M_sys_makename, + METH_VARARGS | METH_KEYWORDS, + M_sys_makename_doc}, + {"exists", M_sys_exists, METH_O, M_sys_exists_doc}, + {"sleep", M_sys_sleep, METH_VARARGS, M_sys_sleep_doc}, + {"time", ( PyCFunction ) M_sys_time, METH_NOARGS, M_sys_time_doc}, + {"expandpath", M_sys_expandpath, METH_O, M_sys_expandpath_doc}, + {"cleanpath", M_sys_cleanpath, METH_O, M_sys_cleanpath_doc}, + {"relpath", M_sys_relpath, METH_VARARGS, M_sys_relpath_doc}, + {NULL, NULL, 0, NULL} +}; + +#if PY_VERSION_HEX >= 0x03000000 +static struct PyModuleDef sys_module = { + PyModuleDef_HEAD_INIT, + "bpysys", + M_sys_doc, + -1,/* multiple "initialization" just copies the module dict. */ + M_sys_methods, + NULL, NULL, NULL, NULL +}; +#endif + +/* Module Functions */ + +PyObject *BPY_sys_module( void ) +{ + PyObject *submodule, *dict; + +#if PY_VERSION_HEX >= 0x03000000 + submodule= PyModule_Create(&sys_module); +#else /* Py2.x */ + submodule= Py_InitModule3( "bpysys", M_sys_methods, M_sys_doc ); +#endif + + dict = PyModule_GetDict( submodule ); + + /* EXPP_dict_set_item_str( dict, "dirsep", PyString_FromString(DIRSEP_STR) ); */ + /* EXPP_dict_set_item_str( dict, "sep", PyString_FromString(DIRSEP_STR) ); */ + + return submodule; +} + +static PyObject *M_sys_basename( PyObject * self, PyObject * value ) +{ + char *name = _PyUnicode_AsString(value); + char *p, basename[FILE_MAXDIR + FILE_MAXFILE]; + int n, len; + + if( !name ) { + return PyErr_Format( PyExc_TypeError, "expected string argument" ); + } + + len = strlen( name ); + +#ifdef WIN32 + p = MAX2(strrchr( name, '/' ), strrchr( name, '\\' )); +#else + p = strrchr( name, DIRSEP ); +#endif + + if( p ) { + n = name + len - p - 1; /* - 1 because we don't want the sep */ + + if( n > FILE_MAXDIR + FILE_MAXFILE ) { + return PyErr_Format( PyExc_RuntimeError, "path too long" ); + } + + BLI_strncpy( basename, p + 1, n + 1 ); + return PyUnicode_FromString( basename ); + } + + return PyUnicode_FromString( name ); +} + +static PyObject *M_sys_dirname( PyObject * self, PyObject * value ) +{ + char *name = _PyUnicode_AsString(value); + char *p, dirname[FILE_MAXDIR + FILE_MAXFILE]; + int n; + + if( !name ) + return PyErr_Format( PyExc_TypeError, "expected string argument" ); + +#ifdef WIN32 + p = MAX2(strrchr( name, '/' ), strrchr( name, '\\' )); +#else + p = strrchr( name, DIRSEP ); +#endif + + if( p ) { + n = p - name; + + if( n > FILE_MAXDIR + FILE_MAXFILE ) + return PyErr_Format( PyExc_RuntimeError, "path too long" ); + + BLI_strncpy( dirname, name, n + 1 ); + return PyUnicode_FromString( dirname ); + } + + return PyUnicode_FromString( "." ); +} + +static PyObject *M_sys_join( PyObject * self, PyObject * args ) +{ + char *name = NULL, *path = NULL; + char filename[FILE_MAXDIR + FILE_MAXFILE]; + int pathlen = 0, namelen = 0; + + if( !PyArg_ParseTuple( args, "ss:Blender.sys.join", &path, &name ) ) + return NULL; + + pathlen = strlen( path ) + 1; + namelen = strlen( name ) + 1; /* + 1 to account for '\0' for BLI_strncpy */ + + if( pathlen + namelen > FILE_MAXDIR + FILE_MAXFILE - 1 ) + return PyErr_Format( PyExc_RuntimeError, "filename is too long." ); + + BLI_strncpy( filename, path, pathlen ); + + if( filename[pathlen - 2] != DIRSEP ) { + filename[pathlen - 1] = DIRSEP; + pathlen += 1; + } + + BLI_strncpy( filename + pathlen - 1, name, namelen ); + + return PyUnicode_FromString( filename ); +} + +static PyObject *M_sys_splitext( PyObject * self, PyObject * value ) +{ + char *name = _PyUnicode_AsString(value); + char *dot, *p, path[FILE_MAXDIR + FILE_MAXFILE], ext[FILE_MAXDIR + FILE_MAXFILE]; + int n, len; + + if( !name ) + return PyErr_Format( PyExc_TypeError, "expected string argument" ); + + len = strlen( name ); + dot = strrchr( name, '.' ); + + if( !dot ) + return Py_BuildValue( "ss", name, "" ); + + p = strrchr( name, DIRSEP ); + + if( p ) { + if( p > dot ) + return Py_BuildValue( "ss", name, "" ); + } + + n = name + len - dot; + + /* loong extensions are supported -- foolish, but Python's os.path.splitext + * supports them, so ... */ + + if( n >= FILE_MAXDIR + FILE_MAXFILE || ( len - n ) >= FILE_MAXDIR + FILE_MAXFILE ) + return PyErr_Format( PyExc_RuntimeError, "path too long" ); + + BLI_strncpy( ext, dot, n + 1 ); + BLI_strncpy( path, name, dot - name + 1 ); + + return Py_BuildValue( "ss", path, ext ); +} + +static PyObject *M_sys_makename( PyObject * self, PyObject * args, + PyObject * kw ) +{ + char *path = G.sce, *ext = NULL; + int strip = 0; + static char *kwlist[] = { "path", "ext", "strip", NULL }; + char *dot = NULL, *p = NULL, basename[FILE_MAXDIR + FILE_MAXFILE]; + int n, len, lenext = 0; + + if( !PyArg_ParseTupleAndKeywords( args, kw, "|ssi:Blender.sys.makename", kwlist, &path, &ext, &strip ) ) + return NULL; + + len = strlen( path ) + 1; /* + 1 to consider ending '\0' */ + if( ext ) + lenext = strlen( ext ) + 1; + + if( ( len + lenext ) > FILE_MAXDIR + FILE_MAXFILE ) + return PyErr_Format( PyExc_RuntimeError, "path too long" ); + + p = strrchr( path, DIRSEP ); + + if( p && strip ) { + n = path + len - p; + BLI_strncpy( basename, p + 1, n ); /* + 1 to skip the sep */ + } else + BLI_strncpy( basename, path, len ); + + dot = strrchr( basename, '.' ); + + /* now the extension: always remove the one in basename */ + if( dot || ext ) { + if( !ext ) + basename[dot - basename] = '\0'; + else { /* if user gave an ext, append it */ + + if( dot ) + n = dot - basename; + else + n = strlen( basename ); + + BLI_strncpy( basename + n, ext, lenext ); + } + } + + return PyUnicode_FromString( basename ); +} + +static PyObject *M_sys_time( PyObject * self ) +{ + return PyFloat_FromDouble( PIL_check_seconds_timer( ) ); +} + +static PyObject *M_sys_sleep( PyObject * self, PyObject * args ) +{ + int millisecs = 10; + + if( !PyArg_ParseTuple( args, "|i:Blender.sys.sleep", &millisecs ) ) + return NULL; + + PIL_sleep_ms( millisecs ); + + Py_RETURN_NONE; +} + +static PyObject *M_sys_exists( PyObject * self, PyObject * value ) +{ + char *fname = _PyUnicode_AsString(value); + + int mode = 0, i = -1; + + if( !fname ) + return PyErr_Format( PyExc_TypeError, "expected string (pathname) argument" ); + + mode = BLI_exist(fname); + + if( mode == 0 ) + i = 0; + else if( S_ISREG( mode ) ) + i = 1; + else if( S_ISDIR( mode ) ) + i = 2; + /* i stays as -1 if path exists but is neither a regular file nor a dir */ + + return PyLong_FromLong(i); +} + +static PyObject *M_sys_expandpath( PyObject * self, PyObject * value ) +{ + char *path = _PyUnicode_AsString(value); + char expanded[FILE_MAXDIR + FILE_MAXFILE]; + bContext *C = BPy_GetContext(); + Scene *scene = CTX_data_scene(C); + + if (!path) + return PyErr_Format( PyExc_TypeError, "expected string argument" ); + + BLI_strncpy(expanded, path, FILE_MAXDIR + FILE_MAXFILE); + BLI_convertstringcode(expanded, G.sce); + BLI_convertstringframe(expanded, scene->r.cfra); + + return PyUnicode_FromString(expanded); +} + +static PyObject *M_sys_cleanpath( PyObject * self, PyObject * value ) +{ + char *path = _PyUnicode_AsString(value); + char cleaned[FILE_MAXDIR + FILE_MAXFILE]; + int trailing_slash = 0, last; + if (!path) + return PyErr_Format( PyExc_TypeError, "expected string argument" ); + last = strlen(path)-1; + if ((last >= 0) && ((path[last]=='/') || (path[last]=='\\'))) { + trailing_slash = 1; + } + BLI_strncpy(cleaned, path, FILE_MAXDIR + FILE_MAXFILE); + BLI_cleanup_file(NULL, cleaned); + + if (trailing_slash) { + BLI_add_slash(cleaned); + } + + return PyUnicode_FromString(cleaned); +} + +static PyObject *M_sys_relpath( PyObject * self, PyObject * args ) +{ + char *base = G.sce; + char *path; + char relpath[FILE_MAXDIR + FILE_MAXFILE]; + + if( !PyArg_ParseTuple( args, "s|s:Blender.sys.relpath", &path, &base ) ) + return NULL; + + strncpy(relpath, path, sizeof(relpath)); + BLI_makestringcode(base, relpath); + + return PyUnicode_FromString(relpath); +} + +#if 0 + +static PyObject *bpy_sys_get_blender_version() +{ + return PyUnicode_FromString(G.version); +} + +#endif diff --git a/source/blender/python/intern/bpy_sys.h b/source/blender/python/intern/bpy_sys.h new file mode 100644 index 00000000000..a045ed0d537 --- /dev/null +++ b/source/blender/python/intern/bpy_sys.h @@ -0,0 +1,41 @@ +/* + * $Id: Sys.h 14444 2008-04-16 22:40:48Z hos $ + * + * ***** 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. + * + * Contributor(s): Willian P. Germano + * + * ***** END GPL LICENSE BLOCK ***** +*/ + +#ifndef BPY_SYS_H + +/* #include */ + +/* PyObject *sys_Init( void ); */ + +#include + +PyObject *BPY_sys_module( void ); + + +#endif /* BPY_SYS_H */ From 6ede28a05ae7cfbf8159277f3618b763d1fbb2c2 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Wed, 1 Jul 2009 18:23:11 +0000 Subject: [PATCH 35/68] - started OBJ importer conversion - added Mesh.add_uv_layer, Object.add_vertex_group --- release/io/import_obj.py | 1280 +++++++++++++++++ source/blender/makesrna/intern/rna_mesh_api.c | 14 + source/blender/makesrna/intern/rna_object.c | 6 +- .../blender/makesrna/intern/rna_object_api.c | 57 +- 4 files changed, 1350 insertions(+), 7 deletions(-) create mode 100644 release/io/import_obj.py diff --git a/release/io/import_obj.py b/release/io/import_obj.py new file mode 100644 index 00000000000..b81ada15f89 --- /dev/null +++ b/release/io/import_obj.py @@ -0,0 +1,1280 @@ +#!BPY + +""" +Name: 'Wavefront (.obj)...' +Blender: 249 +Group: 'Import' +Tooltip: 'Load a Wavefront OBJ File, Shift: batch import all dir.' +""" + +__author__= "Campbell Barton", "Jiri Hnidek", "Paolo Ciccone" +__url__= ['http://wiki.blender.org/index.php/Scripts/Manual/Import/wavefront_obj', 'blender.org', 'blenderartists.org'] +__version__= "2.11" + +__bpydoc__= """\ +This script imports a Wavefront OBJ files to Blender. + +Usage: +Run this script from "File->Import" menu and then load the desired OBJ file. +Note, This loads mesh objects and materials only, nurbs and curves are not supported. +""" + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Script copyright (C) Campbell J Barton 2007 +# +# 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. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + +from Blender import Mesh, Draw, Window, Texture, Material, sys +import bpy +import BPyMesh +import BPyImage +import BPyMessages + +try: import os +except: os= False + +# Generic path functions +def stripFile(path): + '''Return directory, where the file is''' + lastSlash= max(path.rfind('\\'), path.rfind('/')) + if lastSlash != -1: + path= path[:lastSlash] + return '%s%s' % (path, sys.sep) + +def stripPath(path): + '''Strips the slashes from the back of a string''' + return path.split('/')[-1].split('\\')[-1] + +def stripExt(name): # name is a string + '''Strips the prefix off the name before writing''' + index= name.rfind('.') + if index != -1: + return name[ : index ] + else: + return name +# end path funcs + +def unpack_list(list_of_tuples): + l = [] + for t in list_of_tuples: + l.extend(t) + return l + +# same as above except that it adds 0 for triangle faces +def unpack_face_list(list_of_tuples): + l = [] + for t in list_of_tuples: + if len(t) == 3: + t += [0] + l.extend(t) + return l + + +def line_value(line_split): + ''' + Returns 1 string represneting the value for this line + None will be returned if theres only 1 word + ''' + length= len(line_split) + if length == 1: + return None + + elif length == 2: + return line_split[1] + + elif length > 2: + return ' '.join( line_split[1:] ) + +def obj_image_load(imagepath, DIR, IMAGE_SEARCH): + ''' + Mainly uses comprehensiveImageLoad + but tries to replace '_' with ' ' for Max's exporter replaces spaces with underscores. + ''' + + if '_' in imagepath: + image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) + if image: return image + # Did the exporter rename the image? + image= BPyImage.comprehensiveImageLoad(imagepath.replace('_', ' '), DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) + if image: return image + + # Return an image, placeholder if it dosnt exist + image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= True, RECURSIVE= IMAGE_SEARCH) + return image + + +def create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH): + ''' + Create all the used materials in this obj, + assign colors and images to the materials from all referenced material libs + ''' + DIR= stripFile(filepath) + + #==================================================================================# + # This function sets textures defined in .mtl file # + #==================================================================================# + def load_material_image(blender_material, context_material_name, imagepath, type): + + texture= bpy.data.textures.new(type) + texture.setType('Image') + + # Absolute path - c:\.. etc would work here + image= obj_image_load(imagepath, DIR, IMAGE_SEARCH) + has_data = image.has_data + texture.image = image + + # Adds textures for materials (rendering) + if type == 'Kd': + if has_data and image.depth == 32: + # Image has alpha + blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL | Texture.MapTo.ALPHA) + texture.setImageFlags('MipMap', 'InterPol', 'UseAlpha') + blender_material.mode |= Material.Modes.ZTRANSP + blender_material.alpha = 0.0 + else: + blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL) + + # adds textures to faces (Textured/Alt-Z mode) + # Only apply the diffuse texture to the face if the image has not been set with the inline usemat func. + unique_material_images[context_material_name]= image, has_data # set the texface image + + elif type == 'Ka': + blender_material.setTexture(1, texture, Texture.TexCo.UV, Texture.MapTo.CMIR) # TODO- Add AMB to BPY API + + elif type == 'Ks': + blender_material.setTexture(2, texture, Texture.TexCo.UV, Texture.MapTo.SPEC) + + elif type == 'Bump': + blender_material.setTexture(3, texture, Texture.TexCo.UV, Texture.MapTo.NOR) + elif type == 'D': + blender_material.setTexture(4, texture, Texture.TexCo.UV, Texture.MapTo.ALPHA) + blender_material.mode |= Material.Modes.ZTRANSP + blender_material.alpha = 0.0 + # Todo, unset deffuse material alpha if it has an alpha channel + + elif type == 'refl': + blender_material.setTexture(5, texture, Texture.TexCo.UV, Texture.MapTo.REF) + + + # Add an MTL with the same name as the obj if no MTLs are spesified. + temp_mtl= stripExt(stripPath(filepath))+ '.mtl' + + if sys.exists(DIR + temp_mtl) and temp_mtl not in material_libs: + material_libs.append( temp_mtl ) + del temp_mtl + + #Create new materials + for name in unique_materials: # .keys() + if name != None: + unique_materials[name]= bpy.data.materials.new(name) + unique_material_images[name]= None, False # assign None to all material images to start with, add to later. + + unique_materials[None]= None + unique_material_images[None]= None, False + + for libname in material_libs: + mtlpath= DIR + libname + if not sys.exists(mtlpath): + #print '\tError Missing MTL: "%s"' % mtlpath + pass + else: + #print '\t\tloading mtl: "%s"' % mtlpath + context_material= None + mtl= open(mtlpath, 'rU') + for line in mtl: #.xreadlines(): + if line.startswith('newmtl'): + context_material_name= line_value(line.split()) + if unique_materials.has_key(context_material_name): + context_material = unique_materials[ context_material_name ] + else: + context_material = None + + elif context_material: + # we need to make a material to assign properties to it. + line_split= line.split() + line_lower= line.lower().lstrip() + if line_lower.startswith('ka'): + context_material.setMirCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) + elif line_lower.startswith('kd'): + context_material.setRGBCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) + elif line_lower.startswith('ks'): + context_material.setSpecCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) + elif line_lower.startswith('ns'): + context_material.setHardness( int((float(line_split[1])*0.51)) ) + elif line_lower.startswith('ni'): # Refraction index + context_material.setIOR( max(1, min(float(line_split[1]), 3))) # Between 1 and 3 + elif line_lower.startswith('d') or line_lower.startswith('tr'): + context_material.setAlpha(float(line_split[1])) + elif line_lower.startswith('map_ka'): + img_filepath= line_value(line.split()) + if img_filepath: + load_material_image(context_material, context_material_name, img_filepath, 'Ka') + elif line_lower.startswith('map_ks'): + img_filepath= line_value(line.split()) + if img_filepath: + load_material_image(context_material, context_material_name, img_filepath, 'Ks') + elif line_lower.startswith('map_kd'): + img_filepath= line_value(line.split()) + if img_filepath: + load_material_image(context_material, context_material_name, img_filepath, 'Kd') + elif line_lower.startswith('map_bump'): + img_filepath= line_value(line.split()) + if img_filepath: + load_material_image(context_material, context_material_name, img_filepath, 'Bump') + elif line_lower.startswith('map_d') or line_lower.startswith('map_tr'): # Alpha map - Dissolve + img_filepath= line_value(line.split()) + if img_filepath: + load_material_image(context_material, context_material_name, img_filepath, 'D') + + elif line_lower.startswith('refl'): # Reflectionmap + img_filepath= line_value(line.split()) + if img_filepath: + load_material_image(context_material, context_material_name, img_filepath, 'refl') + mtl.close() + + + + +def split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP, SPLIT_MATERIALS): + ''' + Takes vert_loc and faces, and seperates into multiple sets of + (verts_loc, faces, unique_materials, dataname) + This is done so objects do not overload the 16 material limit. + ''' + + filename = stripExt(stripPath(filepath)) + + if not SPLIT_OB_OR_GROUP and not SPLIT_MATERIALS: + # use the filename for the object name since we arnt chopping up the mesh. + return [(verts_loc, faces, unique_materials, filename)] + + + def key_to_name(key): + # if the key is a tuple, join it to make a string + if type(key) == tuple: + return '%s_%s' % key + elif not key: + return filename # assume its a string. make sure this is true if the splitting code is changed + else: + return key + + # Return a key that makes the faces unique. + if SPLIT_OB_OR_GROUP and not SPLIT_MATERIALS: + def face_key(face): + return face[4] # object + + elif not SPLIT_OB_OR_GROUP and SPLIT_MATERIALS: + def face_key(face): + return face[2] # material + + else: # Both + def face_key(face): + return face[4], face[2] # object,material + + + face_split_dict= {} + + oldkey= -1 # initialize to a value that will never match the key + + for face in faces: + + key= face_key(face) + + if oldkey != key: + # Check the key has changed. + try: + verts_split, faces_split, unique_materials_split, vert_remap= face_split_dict[key] + except KeyError: + faces_split= [] + verts_split= [] + unique_materials_split= {} + vert_remap= [-1]*len(verts_loc) + + face_split_dict[key]= (verts_split, faces_split, unique_materials_split, vert_remap) + + oldkey= key + + face_vert_loc_indicies= face[0] + + # Remap verts to new vert list and add where needed + for enum, i in enumerate(face_vert_loc_indicies): + if vert_remap[i] == -1: + new_index= len(verts_split) + vert_remap[i]= new_index # set the new remapped index so we only add once and can reference next time. + face_vert_loc_indicies[enum] = new_index # remap to the local index + verts_split.append( verts_loc[i] ) # add the vert to the local verts + + else: + face_vert_loc_indicies[enum] = vert_remap[i] # remap to the local index + + matname= face[2] + if matname and not unique_materials_split.has_key(matname): + unique_materials_split[matname] = unique_materials[matname] + + faces_split.append(face) + + + # remove one of the itemas and reorder + return [(value[0], value[1], value[2], key_to_name(key)) for key, value in face_split_dict.iteritems()] + + +def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc, verts_tex, faces, unique_materials, unique_material_images, unique_smooth_groups, vertex_groups, dataname): + ''' + Takes all the data gathered and generates a mesh, adding the new object to new_objects + deals with fgons, sharp edges and assigning materials + ''' + if not has_ngons: + CREATE_FGONS= False + + if unique_smooth_groups: + sharp_edges= {} + smooth_group_users= dict([ (context_smooth_group, {}) for context_smooth_group in unique_smooth_groups.iterkeys() ]) + context_smooth_group_old= -1 + + # Split fgons into tri's + fgon_edges= {} # Used for storing fgon keys + if CREATE_EDGES: + edges= [] + + context_object= None + + # reverse loop through face indicies + for f_idx in xrange(len(faces)-1, -1, -1): + + face_vert_loc_indicies,\ + face_vert_tex_indicies,\ + context_material,\ + context_smooth_group,\ + context_object= faces[f_idx] + + len_face_vert_loc_indicies = len(face_vert_loc_indicies) + + if len_face_vert_loc_indicies==1: + faces.pop(f_idx)# cant add single vert faces + + elif not face_vert_tex_indicies or len_face_vert_loc_indicies == 2: # faces that have no texture coords are lines + if CREATE_EDGES: + # generators are better in python 2.4+ but can't be used in 2.3 + # edges.extend( (face_vert_loc_indicies[i], face_vert_loc_indicies[i+1]) for i in xrange(len_face_vert_loc_indicies-1) ) + edges.extend( [(face_vert_loc_indicies[i], face_vert_loc_indicies[i+1]) for i in xrange(len_face_vert_loc_indicies-1)] ) + + faces.pop(f_idx) + else: + + # Smooth Group + if unique_smooth_groups and context_smooth_group: + # Is a part of of a smooth group and is a face + if context_smooth_group_old is not context_smooth_group: + edge_dict= smooth_group_users[context_smooth_group] + context_smooth_group_old= context_smooth_group + + for i in xrange(len_face_vert_loc_indicies): + i1= face_vert_loc_indicies[i] + i2= face_vert_loc_indicies[i-1] + if i1>i2: i1,i2= i2,i1 + + try: + edge_dict[i1,i2]+= 1 + except KeyError: + edge_dict[i1,i2]= 1 + + # FGons into triangles + if has_ngons and len_face_vert_loc_indicies > 4: + + ngon_face_indices= BPyMesh.ngon(verts_loc, face_vert_loc_indicies) + faces.extend(\ + [(\ + [face_vert_loc_indicies[ngon[0]], face_vert_loc_indicies[ngon[1]], face_vert_loc_indicies[ngon[2]] ],\ + [face_vert_tex_indicies[ngon[0]], face_vert_tex_indicies[ngon[1]], face_vert_tex_indicies[ngon[2]] ],\ + context_material,\ + context_smooth_group,\ + context_object)\ + for ngon in ngon_face_indices]\ + ) + + # edges to make fgons + if CREATE_FGONS: + edge_users= {} + for ngon in ngon_face_indices: + for i in (0,1,2): + i1= face_vert_loc_indicies[ngon[i ]] + i2= face_vert_loc_indicies[ngon[i-1]] + if i1>i2: i1,i2= i2,i1 + + try: + edge_users[i1,i2]+=1 + except KeyError: + edge_users[i1,i2]= 1 + + for key, users in edge_users.iteritems(): + if users>1: + fgon_edges[key]= None + + # remove all after 3, means we dont have to pop this one. + faces.pop(f_idx) + + + # Build sharp edges + if unique_smooth_groups: + for edge_dict in smooth_group_users.itervalues(): + for key, users in edge_dict.iteritems(): + if users==1: # This edge is on the boundry of a group + sharp_edges[key]= None + + + # map the material names to an index + material_mapping= dict([(name, i) for i, name in enumerate(unique_materials)]) # enumerate over unique_materials keys() + + materials= [None] * len(unique_materials) + + for name, index in material_mapping.iteritems(): + materials[index]= unique_materials[name] + + me= bpy.data.add_mesh(dataname) +# me= bpy.data.meshes.new(dataname) + + me.materials= materials[0:16] # make sure the list isnt too big. + #me.verts.extend([(0,0,0)]) # dummy vert + + me.add_geometry(len(verts_loc), 0, len(faces)) + + # verts_loc is a list of (x, y, z) tuples + me.verts.foreach_set("co", unpack_list(verts_loc)) +# me.verts.extend(verts_loc) + + # faces is a list of (vert_indices, texco_indices, ...) tuples + # XXX faces should not contain edges + # XXX no check for valid face indices + me.faces.foreach_set("verts", unpack_face_list([f[0] for f in faces])) +# face_mapping= me.faces.extend([f[0] for f in faces], indexList=True) + + if verts_tex and me.faces: + me.add_uv_layer() +# me.faceUV= 1 + # TEXMODE= Mesh.FaceModes['TEX'] + + context_material_old= -1 # avoid a dict lookup + mat= 0 # rare case it may be un-initialized. + me_faces= me.faces +# ALPHA= Mesh.FaceTranspModes.ALPHA + + for i, face in enumerate(faces): + if len(face[0]) < 2: + pass #raise "bad face" + elif len(face[0])==2: + if CREATE_EDGES: + edges.append(face[0]) + else: +# face_index_map= face_mapping[i] + + # since we use foreach_set to add faces, all of them are added + if 1: +# if face_index_map!=None: # None means the face wasnt added + + blender_face = me.faces[i] +# blender_face= me_faces[face_index_map] + + face_vert_loc_indicies,\ + face_vert_tex_indicies,\ + context_material,\ + context_smooth_group,\ + context_object= face + + + + if context_smooth_group: + blender_face.smooth= True + + if context_material: + if context_material_old is not context_material: + mat= material_mapping[context_material] + if mat>15: + mat= 15 + context_material_old= context_material + + blender_face.material_index= mat +# blender_face.mat= mat + + + if verts_tex: + + blender_tface= me.uv_layers[0].data[i] + + if context_material: + image, has_data= unique_material_images[context_material] + if image: # Can be none if the material dosnt have an image. + blender_tface.image= image +# blender_face.image= image + if has_data: +# if has_data and image.depth == 32: + blender_tface.transp = 'ALPHA' +# blender_face.transp |= ALPHA + + # BUG - Evil eekadoodle problem where faces that have vert index 0 location at 3 or 4 are shuffled. + if len(face_vert_loc_indicies)==4: + if face_vert_loc_indicies[2]==0 or face_vert_loc_indicies[3]==0: + face_vert_tex_indicies= face_vert_tex_indicies[2], face_vert_tex_indicies[3], face_vert_tex_indicies[0], face_vert_tex_indicies[1] + else: # length of 3 + if face_vert_loc_indicies[2]==0: + face_vert_tex_indicies= face_vert_tex_indicies[1], face_vert_tex_indicies[2], face_vert_tex_indicies[0] + # END EEEKADOODLE FIX + + # assign material, uv's and image + blender_tface.uv1= verts_tex[face_vert_tex_indicies[0]] + blender_tface.uv2= verts_tex[face_vert_tex_indicies[1]] + blender_tface.uv3= verts_tex[face_vert_tex_indicies[2]] + + if blender_face.verts[3] != 0: + blender_tface.uv4= verts_tex[face_vert_tex_indicies[3]] + +# for ii, uv in enumerate(blender_face.uv): +# uv.x, uv.y= verts_tex[face_vert_tex_indicies[ii]] + del me_faces +# del ALPHA + + # Add edge faces. + me_edges= me.edges +# if CREATE_FGONS and fgon_edges: +# FGON= Mesh.EdgeFlags.FGON +# for ed in me.findEdges( fgon_edges.keys() ): +# if ed!=None: +# me_edges[ed].flag |= FGON +# del FGON + +# if unique_smooth_groups and sharp_edges: +# SHARP= Mesh.EdgeFlags.SHARP +# for ed in me.findEdges( sharp_edges.keys() ): +# if ed!=None: +# me_edges[ed].flag |= SHARP +# del SHARP + +# if CREATE_EDGES: +# me_edges.extend( edges ) + +# del me_edges + +# me.calcNormals() + + ob= bpy.data.add_object("MESH", "Mesh") + ob.data= me +# ob= scn.objects.new(me) + new_objects.append(ob) + +# # Create the vertex groups. No need to have the flag passed here since we test for the +# # content of the vertex_groups. If the user selects to NOT have vertex groups saved then +# # the following test will never run +# for group_name, group_indicies in vertex_groups.iteritems(): +# i= ob.add_vertex_group(group_name) +# # me.addVertGroup(group_name) +# me.assign_verts_to_group(group_index, group_indicies, len(group_indicies), 1.0, 'REPLACE') +# # me.assignVertsToGroup(group_name, group_indicies, 1.00, Mesh.AssignModes.REPLACE) + + +class Mesh(bpy.types.Mesh): + + def assign_verts_to_group(self, group_index, vert_indices, weight): + + +def create_nurbs(scn, context_nurbs, vert_loc, new_objects): + ''' + Add nurbs object to blender, only support one type at the moment + ''' + deg = context_nurbs.get('deg', (3,)) + curv_range = context_nurbs.get('curv_range', None) + curv_idx = context_nurbs.get('curv_idx', []) + parm_u = context_nurbs.get('parm_u', []) + parm_v = context_nurbs.get('parm_v', []) + name = context_nurbs.get('name', 'ObjNurb') + cstype = context_nurbs.get('cstype', None) + + if cstype == None: + print '\tWarning, cstype not found' + return + if cstype != 'bspline': + print '\tWarning, cstype is not supported (only bspline)' + return + if not curv_idx: + print '\tWarning, curv argument empty or not set' + return + if len(deg) > 1 or parm_v: + print '\tWarning, surfaces not supported' + return + + cu = bpy.data.curves.new(name, 'Curve') + cu.flag |= 1 # 3D curve + + nu = None + for pt in curv_idx: + + pt = vert_loc[pt] + pt = (pt[0], pt[1], pt[2], 1.0) + + if nu == None: + nu = cu.appendNurb(pt) + else: + nu.append(pt) + + nu.orderU = deg[0]+1 + + # get for endpoint flag from the weighting + if curv_range and len(parm_u) > deg[0]+1: + do_endpoints = True + for i in xrange(deg[0]+1): + + if abs(parm_u[i]-curv_range[0]) > 0.0001: + do_endpoints = False + break + + if abs(parm_u[-(i+1)]-curv_range[1]) > 0.0001: + do_endpoints = False + break + + else: + do_endpoints = False + + if do_endpoints: + nu.flagU |= 2 + + + # close + ''' + do_closed = False + if len(parm_u) > deg[0]+1: + for i in xrange(deg[0]+1): + #print curv_idx[i], curv_idx[-(i+1)] + + if curv_idx[i]==curv_idx[-(i+1)]: + do_closed = True + break + + if do_closed: + nu.flagU |= 1 + ''' + + ob = scn.objects.new(cu) + new_objects.append(ob) + + +def strip_slash(line_split): + if line_split[-1][-1]== '\\': + if len(line_split[-1])==1: + line_split.pop() # remove the \ item + else: + line_split[-1]= line_split[-1][:-1] # remove the \ from the end last number + return True + return False + + + +def get_float_func(filepath): + ''' + find the float function for this obj file + - weather to replace commas or not + ''' + file= open(filepath, 'rU') + for line in file: #.xreadlines(): + line = line.lstrip() + if line.startswith('v'): # vn vt v + if ',' in line: + return lambda f: float(f.replace(',', '.')) + elif '.' in line: + return float + + # incase all vert values were ints + return float + +def load_obj(filepath, + CLAMP_SIZE= 0.0, + CREATE_FGONS= True, + CREATE_SMOOTH_GROUPS= True, + CREATE_EDGES= True, + SPLIT_OBJECTS= True, + SPLIT_GROUPS= True, + SPLIT_MATERIALS= True, + ROTATE_X90= True, + IMAGE_SEARCH=True, + POLYGROUPS=False): + ''' + Called by the user interface or another script. + load_obj(path) - should give acceptable results. + This function passes the file and sends the data off + to be split into objects and then converted into mesh objects + ''' + print '\nimporting obj "%s"' % filepath + + if SPLIT_OBJECTS or SPLIT_GROUPS or SPLIT_MATERIALS: + POLYGROUPS = False + + time_main= sys.time() + + verts_loc= [] + verts_tex= [] + faces= [] # tuples of the faces + material_libs= [] # filanems to material libs this uses + vertex_groups = {} # when POLYGROUPS is true + + # Get the string to float conversion func for this file- is 'float' for almost all files. + float_func= get_float_func(filepath) + + # Context variables + context_material= None + context_smooth_group= None + context_object= None + context_vgroup = None + + # Nurbs + context_nurbs = {} + nurbs = [] + context_parm = '' # used by nurbs too but could be used elsewhere + + has_ngons= False + # has_smoothgroups= False - is explicit with len(unique_smooth_groups) being > 0 + + # Until we can use sets + unique_materials= {} + unique_material_images= {} + unique_smooth_groups= {} + # unique_obects= {} - no use for this variable since the objects are stored in the face. + + # when there are faces that end with \ + # it means they are multiline- + # since we use xreadline we cant skip to the next line + # so we need to know weather + context_multi_line= '' + + print '\tparsing obj file "%s"...' % filepath, + time_sub= sys.time() + + file= open(filepath, 'rU') + for line in file: #.xreadlines(): + line = line.lstrip() # rare cases there is white space at the start of the line + + if line.startswith('v '): + line_split= line.split() + # rotate X90: (x,-z,y) + verts_loc.append( (float_func(line_split[1]), -float_func(line_split[3]), float_func(line_split[2])) ) + + elif line.startswith('vn '): + pass + + elif line.startswith('vt '): + line_split= line.split() + verts_tex.append( (float_func(line_split[1]), float_func(line_split[2])) ) + + # Handel faces lines (as faces) and the second+ lines of fa multiline face here + # use 'f' not 'f ' because some objs (very rare have 'fo ' for faces) + elif line.startswith('f') or context_multi_line == 'f': + + if context_multi_line: + # use face_vert_loc_indicies and face_vert_tex_indicies previously defined and used the obj_face + line_split= line.split() + + else: + line_split= line[2:].split() + face_vert_loc_indicies= [] + face_vert_tex_indicies= [] + + # Instance a face + faces.append((\ + face_vert_loc_indicies,\ + face_vert_tex_indicies,\ + context_material,\ + context_smooth_group,\ + context_object\ + )) + + if strip_slash(line_split): + context_multi_line = 'f' + else: + context_multi_line = '' + + for v in line_split: + obj_vert= v.split('/') + + vert_loc_index= int(obj_vert[0])-1 + # Add the vertex to the current group + # *warning*, this wont work for files that have groups defined around verts + if POLYGROUPS and context_vgroup: + vertex_groups[context_vgroup].append(vert_loc_index) + + # Make relative negative vert indicies absolute + if vert_loc_index < 0: + vert_loc_index= len(verts_loc) + vert_loc_index + 1 + + face_vert_loc_indicies.append(vert_loc_index) + + if len(obj_vert)>1 and obj_vert[1]: + # formatting for faces with normals and textures us + # loc_index/tex_index/nor_index + + vert_tex_index= int(obj_vert[1])-1 + # Make relative negative vert indicies absolute + if vert_tex_index < 0: + vert_tex_index= len(verts_tex) + vert_tex_index + 1 + + face_vert_tex_indicies.append(vert_tex_index) + else: + # dummy + face_vert_tex_indicies.append(0) + + if len(face_vert_loc_indicies) > 4: + has_ngons= True + + elif CREATE_EDGES and (line.startswith('l ') or context_multi_line == 'l'): + # very similar to the face load function above with some parts removed + + if context_multi_line: + # use face_vert_loc_indicies and face_vert_tex_indicies previously defined and used the obj_face + line_split= line.split() + + else: + line_split= line[2:].split() + face_vert_loc_indicies= [] + face_vert_tex_indicies= [] + + # Instance a face + faces.append((\ + face_vert_loc_indicies,\ + face_vert_tex_indicies,\ + context_material,\ + context_smooth_group,\ + context_object\ + )) + + if strip_slash(line_split): + context_multi_line = 'l' + else: + context_multi_line = '' + + isline= line.startswith('l') + + for v in line_split: + vert_loc_index= int(v)-1 + + # Make relative negative vert indicies absolute + if vert_loc_index < 0: + vert_loc_index= len(verts_loc) + vert_loc_index + 1 + + face_vert_loc_indicies.append(vert_loc_index) + + elif line.startswith('s'): + if CREATE_SMOOTH_GROUPS: + context_smooth_group= line_value(line.split()) + if context_smooth_group=='off': + context_smooth_group= None + elif context_smooth_group: # is not None + unique_smooth_groups[context_smooth_group]= None + + elif line.startswith('o'): + if SPLIT_OBJECTS: + context_object= line_value(line.split()) + # unique_obects[context_object]= None + + elif line.startswith('g'): + if SPLIT_GROUPS: + context_object= line_value(line.split()) + # print 'context_object', context_object + # unique_obects[context_object]= None + elif POLYGROUPS: + context_vgroup = line_value(line.split()) + if context_vgroup and context_vgroup != '(null)': + vertex_groups.setdefault(context_vgroup, []) + else: + context_vgroup = None # dont assign a vgroup + + elif line.startswith('usemtl'): + context_material= line_value(line.split()) + unique_materials[context_material]= None + elif line.startswith('mtllib'): # usemap or usemat + material_libs.extend( line.split()[1:] ) # can have multiple mtllib filenames per line + + + # Nurbs support + elif line.startswith('cstype '): + context_nurbs['cstype']= line_value(line.split()) # 'rat bspline' / 'bspline' + elif line.startswith('curv ') or context_multi_line == 'curv': + line_split= line.split() + + curv_idx = context_nurbs['curv_idx'] = context_nurbs.get('curv_idx', []) # incase were multiline + + if not context_multi_line: + context_nurbs['curv_range'] = float_func(line_split[1]), float_func(line_split[2]) + line_split[0:3] = [] # remove first 3 items + + if strip_slash(line_split): + context_multi_line = 'curv' + else: + context_multi_line = '' + + + for i in line_split: + vert_loc_index = int(i)-1 + + if vert_loc_index < 0: + vert_loc_index= len(verts_loc) + vert_loc_index + 1 + + curv_idx.append(vert_loc_index) + + elif line.startswith('parm') or context_multi_line == 'parm': + line_split= line.split() + + if context_multi_line: + context_multi_line = '' + else: + context_parm = line_split[1] + line_split[0:2] = [] # remove first 2 + + if strip_slash(line_split): + context_multi_line = 'parm' + else: + context_multi_line = '' + + if context_parm.lower() == 'u': + context_nurbs.setdefault('parm_u', []).extend( [float_func(f) for f in line_split] ) + elif context_parm.lower() == 'v': # surfaces not suported yet + context_nurbs.setdefault('parm_v', []).extend( [float_func(f) for f in line_split] ) + # else: # may want to support other parm's ? + + elif line.startswith('deg '): + context_nurbs['deg']= [int(i) for i in line.split()[1:]] + elif line.startswith('end'): + # Add the nurbs curve + if context_object: + context_nurbs['name'] = context_object + nurbs.append(context_nurbs) + context_nurbs = {} + context_parm = '' + + ''' # How to use usemap? depricated? + elif line.startswith('usema'): # usemap or usemat + context_image= line_value(line.split()) + ''' + + file.close() + time_new= sys.time() + print '%.4f sec' % (time_new-time_sub) + time_sub= time_new + + + print '\tloading materials and images...', + create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH) + + time_new= sys.time() + print '%.4f sec' % (time_new-time_sub) + time_sub= time_new + + if not ROTATE_X90: + verts_loc[:] = [(v[0], v[2], -v[1]) for v in verts_loc] + + # deselect all + scn = bpy.data.scenes.active + scn.objects.selected = [] + new_objects= [] # put new objects here + + print '\tbuilding geometry...\n\tverts:%i faces:%i materials: %i smoothgroups:%i ...' % ( len(verts_loc), len(faces), len(unique_materials), len(unique_smooth_groups) ), + # Split the mesh by objects/materials, may + if SPLIT_OBJECTS or SPLIT_GROUPS: SPLIT_OB_OR_GROUP = True + else: SPLIT_OB_OR_GROUP = False + + for verts_loc_split, faces_split, unique_materials_split, dataname in split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP, SPLIT_MATERIALS): + # Create meshes from the data, warning 'vertex_groups' wont support splitting + create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc_split, verts_tex, faces_split, unique_materials_split, unique_material_images, unique_smooth_groups, vertex_groups, dataname) + + # nurbs support + for context_nurbs in nurbs: + create_nurbs(scn, context_nurbs, verts_loc, new_objects) + + + axis_min= [ 1000000000]*3 + axis_max= [-1000000000]*3 + + if CLAMP_SIZE: + # Get all object bounds + for ob in new_objects: + for v in ob.getBoundBox(): + for axis, value in enumerate(v): + if axis_min[axis] > value: axis_min[axis]= value + if axis_max[axis] < value: axis_max[axis]= value + + # Scale objects + max_axis= max(axis_max[0]-axis_min[0], axis_max[1]-axis_min[1], axis_max[2]-axis_min[2]) + scale= 1.0 + + while CLAMP_SIZE < max_axis * scale: + scale= scale/10.0 + + for ob in new_objects: + ob.setSize(scale, scale, scale) + + # Better rotate the vert locations + #if not ROTATE_X90: + # for ob in new_objects: + # ob.RotX = -1.570796326794896558 + + time_new= sys.time() + + print '%.4f sec' % (time_new-time_sub) + print 'finished importing: "%s" in %.4f sec.' % (filepath, (time_new-time_main)) + + +DEBUG= True + + +def load_obj_ui(filepath, BATCH_LOAD= False): + if BPyMessages.Error_NoFile(filepath): + return + + global CREATE_SMOOTH_GROUPS, CREATE_FGONS, CREATE_EDGES, SPLIT_OBJECTS, SPLIT_GROUPS, SPLIT_MATERIALS, CLAMP_SIZE, IMAGE_SEARCH, POLYGROUPS, KEEP_VERT_ORDER, ROTATE_X90 + + CREATE_SMOOTH_GROUPS= Draw.Create(0) + CREATE_FGONS= Draw.Create(1) + CREATE_EDGES= Draw.Create(1) + SPLIT_OBJECTS= Draw.Create(0) + SPLIT_GROUPS= Draw.Create(0) + SPLIT_MATERIALS= Draw.Create(0) + CLAMP_SIZE= Draw.Create(10.0) + IMAGE_SEARCH= Draw.Create(1) + POLYGROUPS= Draw.Create(0) + KEEP_VERT_ORDER= Draw.Create(1) + ROTATE_X90= Draw.Create(1) + + + # Get USER Options + # Note, Works but not pretty, instead use a more complicated GUI + ''' + pup_block= [\ + 'Import...',\ + ('Smooth Groups', CREATE_SMOOTH_GROUPS, 'Surround smooth groups by sharp edges'),\ + ('Create FGons', CREATE_FGONS, 'Import faces with more then 4 verts as fgons.'),\ + ('Lines', CREATE_EDGES, 'Import lines and faces with 2 verts as edges'),\ + 'Separate objects from obj...',\ + ('Object', SPLIT_OBJECTS, 'Import OBJ Objects into Blender Objects'),\ + ('Group', SPLIT_GROUPS, 'Import OBJ Groups into Blender Objects'),\ + ('Material', SPLIT_MATERIALS, 'Import each material into a seperate mesh (Avoids > 16 per mesh error)'),\ + 'Options...',\ + ('Keep Vert Order', KEEP_VERT_ORDER, 'Keep vert and face order, disables some other options.'),\ + ('Clamp Scale:', CLAMP_SIZE, 0.0, 1000.0, 'Clamp the size to this maximum (Zero to Disable)'),\ + ('Image Search', IMAGE_SEARCH, 'Search subdirs for any assosiated images (Warning, may be slow)'),\ + ] + + if not Draw.PupBlock('Import OBJ...', pup_block): + return + + if KEEP_VERT_ORDER.val: + SPLIT_OBJECTS.val = False + SPLIT_GROUPS.val = False + SPLIT_MATERIALS.val = False + ''' + + + + # BEGIN ALTERNATIVE UI ******************* + if True: + + EVENT_NONE = 0 + EVENT_EXIT = 1 + EVENT_REDRAW = 2 + EVENT_IMPORT = 3 + + GLOBALS = {} + GLOBALS['EVENT'] = EVENT_REDRAW + #GLOBALS['MOUSE'] = Window.GetMouseCoords() + GLOBALS['MOUSE'] = [i/2 for i in Window.GetScreenSize()] + + def obj_ui_set_event(e,v): + GLOBALS['EVENT'] = e + + def do_split(e,v): + global SPLIT_OBJECTS, SPLIT_GROUPS, SPLIT_MATERIALS, KEEP_VERT_ORDER, POLYGROUPS + if SPLIT_OBJECTS.val or SPLIT_GROUPS.val or SPLIT_MATERIALS.val: + KEEP_VERT_ORDER.val = 0 + POLYGROUPS.val = 0 + else: + KEEP_VERT_ORDER.val = 1 + + def do_vertorder(e,v): + global SPLIT_OBJECTS, SPLIT_GROUPS, SPLIT_MATERIALS, KEEP_VERT_ORDER + if KEEP_VERT_ORDER.val: + SPLIT_OBJECTS.val = SPLIT_GROUPS.val = SPLIT_MATERIALS.val = 0 + else: + if not (SPLIT_OBJECTS.val or SPLIT_GROUPS.val or SPLIT_MATERIALS.val): + KEEP_VERT_ORDER.val = 1 + + def do_polygroups(e,v): + global SPLIT_OBJECTS, SPLIT_GROUPS, SPLIT_MATERIALS, KEEP_VERT_ORDER, POLYGROUPS + if POLYGROUPS.val: + SPLIT_OBJECTS.val = SPLIT_GROUPS.val = SPLIT_MATERIALS.val = 0 + + def do_help(e,v): + url = __url__[0] + print 'Trying to open web browser with documentation at this address...' + print '\t' + url + + try: + import webbrowser + webbrowser.open(url) + except: + print '...could not open a browser window.' + + def obj_ui(): + ui_x, ui_y = GLOBALS['MOUSE'] + + # Center based on overall pup size + ui_x -= 165 + ui_y -= 90 + + global CREATE_SMOOTH_GROUPS, CREATE_FGONS, CREATE_EDGES, SPLIT_OBJECTS, SPLIT_GROUPS, SPLIT_MATERIALS, CLAMP_SIZE, IMAGE_SEARCH, POLYGROUPS, KEEP_VERT_ORDER, ROTATE_X90 + + Draw.Label('Import...', ui_x+9, ui_y+159, 220, 21) + Draw.BeginAlign() + CREATE_SMOOTH_GROUPS = Draw.Toggle('Smooth Groups', EVENT_NONE, ui_x+9, ui_y+139, 110, 20, CREATE_SMOOTH_GROUPS.val, 'Surround smooth groups by sharp edges') + CREATE_FGONS = Draw.Toggle('NGons as FGons', EVENT_NONE, ui_x+119, ui_y+139, 110, 20, CREATE_FGONS.val, 'Import faces with more then 4 verts as fgons') + CREATE_EDGES = Draw.Toggle('Lines as Edges', EVENT_NONE, ui_x+229, ui_y+139, 110, 20, CREATE_EDGES.val, 'Import lines and faces with 2 verts as edges') + Draw.EndAlign() + + Draw.Label('Separate objects by OBJ...', ui_x+9, ui_y+110, 220, 20) + Draw.BeginAlign() + SPLIT_OBJECTS = Draw.Toggle('Object', EVENT_REDRAW, ui_x+9, ui_y+89, 55, 21, SPLIT_OBJECTS.val, 'Import OBJ Objects into Blender Objects', do_split) + SPLIT_GROUPS = Draw.Toggle('Group', EVENT_REDRAW, ui_x+64, ui_y+89, 55, 21, SPLIT_GROUPS.val, 'Import OBJ Groups into Blender Objects', do_split) + SPLIT_MATERIALS = Draw.Toggle('Material', EVENT_REDRAW, ui_x+119, ui_y+89, 60, 21, SPLIT_MATERIALS.val, 'Import each material into a seperate mesh (Avoids > 16 per mesh error)', do_split) + Draw.EndAlign() + + # Only used for user feedback + KEEP_VERT_ORDER = Draw.Toggle('Keep Vert Order', EVENT_REDRAW, ui_x+184, ui_y+89, 113, 21, KEEP_VERT_ORDER.val, 'Keep vert and face order, disables split options, enable for morph targets', do_vertorder) + + ROTATE_X90 = Draw.Toggle('-X90', EVENT_REDRAW, ui_x+302, ui_y+89, 38, 21, ROTATE_X90.val, 'Rotate X 90.') + + Draw.Label('Options...', ui_x+9, ui_y+60, 211, 20) + CLAMP_SIZE = Draw.Number('Clamp Scale: ', EVENT_NONE, ui_x+9, ui_y+39, 130, 21, CLAMP_SIZE.val, 0.0, 1000.0, 'Clamp the size to this maximum (Zero to Disable)') + POLYGROUPS = Draw.Toggle('Poly Groups', EVENT_REDRAW, ui_x+144, ui_y+39, 90, 21, POLYGROUPS.val, 'Import OBJ groups as vertex groups.', do_polygroups) + IMAGE_SEARCH = Draw.Toggle('Image Search', EVENT_NONE, ui_x+239, ui_y+39, 100, 21, IMAGE_SEARCH.val, 'Search subdirs for any assosiated images (Warning, may be slow)') + Draw.BeginAlign() + Draw.PushButton('Online Help', EVENT_REDRAW, ui_x+9, ui_y+9, 110, 21, 'Load the wiki page for this script', do_help) + Draw.PushButton('Cancel', EVENT_EXIT, ui_x+119, ui_y+9, 110, 21, '', obj_ui_set_event) + Draw.PushButton('Import', EVENT_IMPORT, ui_x+229, ui_y+9, 110, 21, 'Import with these settings', obj_ui_set_event) + Draw.EndAlign() + + + # hack so the toggle buttons redraw. this is not nice at all + while GLOBALS['EVENT'] not in (EVENT_EXIT, EVENT_IMPORT): + Draw.UIBlock(obj_ui, 0) + + if GLOBALS['EVENT'] != EVENT_IMPORT: + return + + # END ALTERNATIVE UI ********************* + + + + + + + + Window.WaitCursor(1) + + if BATCH_LOAD: # load the dir + try: + files= [ f for f in os.listdir(filepath) if f.lower().endswith('.obj') ] + except: + Window.WaitCursor(0) + Draw.PupMenu('Error%t|Could not open path ' + filepath) + return + + if not files: + Window.WaitCursor(0) + Draw.PupMenu('Error%t|No files at path ' + filepath) + return + + for f in files: + scn= bpy.data.scenes.new( stripExt(f) ) + scn.makeCurrent() + + load_obj(sys.join(filepath, f),\ + CLAMP_SIZE.val,\ + CREATE_FGONS.val,\ + CREATE_SMOOTH_GROUPS.val,\ + CREATE_EDGES.val,\ + SPLIT_OBJECTS.val,\ + SPLIT_GROUPS.val,\ + SPLIT_MATERIALS.val,\ + ROTATE_X90.val,\ + IMAGE_SEARCH.val,\ + POLYGROUPS.val + ) + + else: # Normal load + load_obj(filepath,\ + CLAMP_SIZE.val,\ + CREATE_FGONS.val,\ + CREATE_SMOOTH_GROUPS.val,\ + CREATE_EDGES.val,\ + SPLIT_OBJECTS.val,\ + SPLIT_GROUPS.val,\ + SPLIT_MATERIALS.val,\ + ROTATE_X90.val,\ + IMAGE_SEARCH.val,\ + POLYGROUPS.val + ) + + Window.WaitCursor(0) + + +def load_obj_ui_batch(file): + load_obj_ui(file, True) + +DEBUG= False + +if __name__=='__main__' and not DEBUG: + if os and Window.GetKeyQualifiers() & Window.Qual.SHIFT: + Window.FileSelector(load_obj_ui_batch, 'Import OBJ Dir', '') + else: + Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ', '*.obj') + + # For testing compatibility +''' +else: + # DEBUG ONLY + TIME= sys.time() + DIR = '/fe/obj' + import os + print 'Searching for files' + def fileList(path): + for dirpath, dirnames, filenames in os.walk(path): + for filename in filenames: + yield os.path.join(dirpath, filename) + + files = [f for f in fileList(DIR) if f.lower().endswith('.obj')] + files.sort() + + for i, obj_file in enumerate(files): + if 0 < i < 20: + print 'Importing', obj_file, '\nNUMBER', i, 'of', len(files) + newScn= bpy.data.scenes.new(os.path.basename(obj_file)) + newScn.makeCurrent() + load_obj(obj_file, False, IMAGE_SEARCH=0) + + print 'TOTAL TIME: %.6f' % (sys.time() - TIME) +''' +#load_obj('/test.obj') +#load_obj('/fe/obj/mba1.obj') + +# NOTES (all line numbers refer to 2.4x import_obj.py, not this file) +# check later: line 489 +# edge flags, edges, normals: lines 508-528 +# vertex groups: line 533 - cannot assign vertex groups diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index 00d5bacfef0..ecc32b23249 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -232,6 +232,12 @@ static void rna_Mesh_add_faces(Mesh *mesh, int len) mesh->totface= totface; } +/* +static void rna_Mesh_add_faces(Mesh *mesh) +{ +} +*/ + static void rna_Mesh_add_geometry(Mesh *mesh, int verts, int edges, int faces) { if(verts) @@ -242,6 +248,11 @@ static void rna_Mesh_add_geometry(Mesh *mesh, int verts, int edges, int faces) rna_Mesh_add_faces(mesh, faces); } +static void rna_Mesh_add_uv_layer(Mesh *me) +{ + me->mtface= CustomData_add_layer(&me->fdata, CD_MTFACE, CD_DEFAULT, NULL, me->totface); +} + #else void RNA_api_mesh(StructRNA *srna) @@ -267,6 +278,9 @@ void RNA_api_mesh(StructRNA *srna) parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh, remove it if it is only used for export."); RNA_def_function_return(func, parm); + func= RNA_def_function(srna, "add_uv_layer", "rna_Mesh_add_uv_layer"); + RNA_def_function_ui_description(func, "Add new UV layer to Mesh."); + /* func= RNA_def_function(srna, "add_geom", "rna_Mesh_add_geom"); RNA_def_function_ui_description(func, "Add geometry data to mesh."); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 4166eb9bc2c..e046f2ff73d 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -384,6 +384,7 @@ static void rna_GameObjectSettings_state_set(PointerRNA *ptr, const int *values) } } +/* static PointerRNA rna_DupliObject_object_get(PointerRNA *ptr) { DupliObject *dob= (DupliObject*)ptr->data; @@ -391,6 +392,7 @@ static PointerRNA rna_DupliObject_object_get(PointerRNA *ptr) RNA_pointer_create(&dob->ob->id, &RNA_Object, dob->ob, &newptr); return newptr; } +*/ #else @@ -1207,9 +1209,9 @@ static void rna_def_dupli_object(BlenderRNA *brna) /* RNA_def_struct_ui_icon(srna, ICON_OBJECT_DATA); */ prop= RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "Object"); + /* RNA_def_property_struct_type(prop, "Object"); */ RNA_def_property_pointer_sdna(prop, NULL, "ob"); - RNA_def_property_pointer_funcs(prop, "rna_DupliObject_object_get", NULL, NULL); + /* RNA_def_property_pointer_funcs(prop, "rna_DupliObject_object_get", NULL, NULL); */ RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Object", "Object this DupliObject represents."); diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index b09acb51084..9d1a6a39d51 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -36,6 +36,10 @@ #include "DNA_object_types.h" +#include "BLO_sys_types.h" /* needed for intptr_t used in ED_mesh.h */ + +#include "ED_mesh.h" + /* parameter to rna_Object_create_mesh */ typedef enum CreateMeshType { CREATE_MESH_PREVIEW = 0, @@ -56,10 +60,6 @@ typedef enum CreateMeshType { #include "BLI_arithb.h" -#include "BLO_sys_types.h" /* needed for intptr_t used in ED_mesh.h */ - -#include "ED_mesh.h" - /* copied from init_render_mesh (render code) */ static Mesh *rna_Object_create_mesh(Object *ob, bContext *C, ReportList *reports, int type) { @@ -108,7 +108,7 @@ static void rna_Object_create_duplilist(Object *ob, bContext *C, ReportList *rep /* free duplilist if a user forgets to */ if (ob->duplilist) { - BKE_reportf(reports, RPT_WARNING, "%s.dupli_list has not been freed.", RNA_struct_identifier(&RNA_Object)); + BKE_reportf(reports, RPT_WARNING, "Object.dupli_list has not been freed."); free_object_duplilist(ob->duplilist); ob->duplilist= NULL; @@ -155,6 +155,47 @@ static void rna_Object_convert_to_triface(Object *ob, bContext *C, ReportList *r DAG_object_flush_update(sce, ob, OB_RECALC_DATA); } +static int rna_Object_add_vertex_group(Object *ob, char *group_name) +{ + bDeformGroup *defgroup= add_defgroup_name(ob, group_name); + return BLI_findindex(&ob->defbase, defgroup); +} + +/* +static void rna_Mesh_assign_verts_to_group(Object *ob, bDeformGroup *group, int *indices, int totindex, float weight, int assignmode) +{ + if (ob->type != OB_MESH) { + BKE_report(reports, RPT_ERROR, "Object should be of MESH type."); + return; + } + + Mesh *me = (Mesh*)ob->data; + int group_index = get_defgroup_num(ob, group); + if (group_index == -1) { + BKE_report(reports, RPT_ERROR, "No deform groups assigned to mesh."); + return; + } + + if (assignmode != WEIGHT_REPLACE && assignmode != WEIGHT_ADD && assignmode != WEIGHT_SUBTRACT) { + BKE_report(reports, RPT_ERROR, "Bad assignment mode." ); + return; + } + + // makes a set of dVerts corresponding to the mVerts + if (!me->dvert) + create_dverts(&me->id); + + // loop list adding verts to group + for (i= 0; i < totindex; i++) { + if(i < 0 || i >= me->totvert) { + BKE_report(reports, RPT_ERROR, "Bad vertex index in list."); + return; + } + + add_vert_defnr(ob, group_index, i, weight, assignmode); + } +} +*/ #else @@ -190,6 +231,12 @@ void RNA_api_object(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); parm= RNA_def_pointer(func, "scene", "Scene", "", "Scene where the object belongs."); RNA_def_property_flag(parm, PROP_REQUIRED); + + func= RNA_def_function(srna, "add_vertex_group", "rna_Object_add_vertex_group"); + RNA_def_function_ui_description(func, "Add vertex group to object."); + parm= RNA_def_string(func, "name", "Group", 0, "", "Vertex group name."); + parm= RNA_def_int(func, "group_index", 0, 0, 0, "", "Index of the created vertex group.", 0, 0); + RNA_def_function_return(func, parm); } #endif From 617851bf21ac5da10bfd171816187e1336cd4a69 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 2 Jul 2009 20:46:35 +0000 Subject: [PATCH 36/68] - added API functions: - Mesh.calc_normals - Object.add_vertex_to_group - Main.add_material - Main.add_texture - Material.add_texture - OBJ importer conversion in progress --- release/io/export_obj.py | 2 +- release/io/import_obj.py | 184 ++++++++++-------- source/blender/makesrna/intern/makesrna.c | 2 +- source/blender/makesrna/intern/rna_internal.h | 1 + source/blender/makesrna/intern/rna_main_api.c | 30 ++- source/blender/makesrna/intern/rna_material.c | 2 + .../makesrna/intern/rna_material_api.c | 126 ++++++++++++ source/blender/makesrna/intern/rna_mesh_api.c | 8 + .../blender/makesrna/intern/rna_object_api.c | 33 +++- 9 files changed, 297 insertions(+), 91 deletions(-) create mode 100644 source/blender/makesrna/intern/rna_material_api.c diff --git a/release/io/export_obj.py b/release/io/export_obj.py index d139e872251..1ee685a52a3 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -464,7 +464,7 @@ def write(filename, objects, scene, # XXX # High Quality Normals if EXPORT_NORMALS and face_index_pairs: - pass + me.calc_normals() # if EXPORT_NORMALS_HQ: # BPyMesh.meshCalcNormals(me) # else: diff --git a/release/io/import_obj.py b/release/io/import_obj.py index b81ada15f89..e16780ce1d6 100644 --- a/release/io/import_obj.py +++ b/release/io/import_obj.py @@ -42,7 +42,7 @@ Note, This loads mesh objects and materials only, nurbs and curves are not suppo from Blender import Mesh, Draw, Window, Texture, Material, sys import bpy -import BPyMesh +# import BPyMesh import BPyImage import BPyMessages @@ -130,9 +130,11 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ # This function sets textures defined in .mtl file # #==================================================================================# def load_material_image(blender_material, context_material_name, imagepath, type): - - texture= bpy.data.textures.new(type) - texture.setType('Image') + + texture= bpy.data.add_texture(type) + texture.type= 'IMAGE' +# texture= bpy.data.textures.new(type) +# texture.setType('Image') # Absolute path - c:\.. etc would work here image= obj_image_load(imagepath, DIR, IMAGE_SEARCH) @@ -182,7 +184,8 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ #Create new materials for name in unique_materials: # .keys() if name != None: - unique_materials[name]= bpy.data.materials.new(name) + unique_materials[name]= bpy.data.add_material(name) +# unique_materials[name]= bpy.data.materials.new(name) unique_material_images[name]= None, False # assign None to all material images to start with, add to later. unique_materials[None]= None @@ -190,7 +193,8 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ for libname in material_libs: mtlpath= DIR + libname - if not sys.exists(mtlpath): + if not bpy.sys.exists(mtlpath): +# if not sys.exists(mtlpath): #print '\tError Missing MTL: "%s"' % mtlpath pass else: @@ -210,17 +214,23 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ line_split= line.split() line_lower= line.lower().lstrip() if line_lower.startswith('ka'): - context_material.setMirCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) + context_material.mirror_color = (float(line_split[1]), float(line_split[2]), float(line_split[3])) +# context_material.setMirCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) elif line_lower.startswith('kd'): - context_material.setRGBCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) + context_material.diffuse_color = (float(line_split[1]), float(line_split[2]), float(line_split[3])) +# context_material.setRGBCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) elif line_lower.startswith('ks'): - context_material.setSpecCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) + context_material.specular_color = (float(line_split[1]), float(line_split[2]), float(line_split[3])) +# context_material.setSpecCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) elif line_lower.startswith('ns'): - context_material.setHardness( int((float(line_split[1])*0.51)) ) + context_material.specular_hardness = int((float(line_split[1])*0.51)) +# context_material.setHardness( int((float(line_split[1])*0.51)) ) elif line_lower.startswith('ni'): # Refraction index - context_material.setIOR( max(1, min(float(line_split[1]), 3))) # Between 1 and 3 + context_material.ior = max(1, min(float(line_split[1]), 3)) +# context_material.setIOR( max(1, min(float(line_split[1]), 3))) # Between 1 and 3 elif line_lower.startswith('d') or line_lower.startswith('tr'): - context_material.setAlpha(float(line_split[1])) + context_material.alpha = float(line_split[1]) +# context_material.setAlpha(float(line_split[1])) elif line_lower.startswith('map_ka'): img_filepath= line_value(line.split()) if img_filepath: @@ -395,39 +405,39 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l edge_dict[i1,i2]= 1 # FGons into triangles - if has_ngons and len_face_vert_loc_indicies > 4: +# if has_ngons and len_face_vert_loc_indicies > 4: - ngon_face_indices= BPyMesh.ngon(verts_loc, face_vert_loc_indicies) - faces.extend(\ - [(\ - [face_vert_loc_indicies[ngon[0]], face_vert_loc_indicies[ngon[1]], face_vert_loc_indicies[ngon[2]] ],\ - [face_vert_tex_indicies[ngon[0]], face_vert_tex_indicies[ngon[1]], face_vert_tex_indicies[ngon[2]] ],\ - context_material,\ - context_smooth_group,\ - context_object)\ - for ngon in ngon_face_indices]\ - ) +# ngon_face_indices= BPyMesh.ngon(verts_loc, face_vert_loc_indicies) +# faces.extend(\ +# [(\ +# [face_vert_loc_indicies[ngon[0]], face_vert_loc_indicies[ngon[1]], face_vert_loc_indicies[ngon[2]] ],\ +# [face_vert_tex_indicies[ngon[0]], face_vert_tex_indicies[ngon[1]], face_vert_tex_indicies[ngon[2]] ],\ +# context_material,\ +# context_smooth_group,\ +# context_object)\ +# for ngon in ngon_face_indices]\ +# ) - # edges to make fgons - if CREATE_FGONS: - edge_users= {} - for ngon in ngon_face_indices: - for i in (0,1,2): - i1= face_vert_loc_indicies[ngon[i ]] - i2= face_vert_loc_indicies[ngon[i-1]] - if i1>i2: i1,i2= i2,i1 +# # edges to make fgons +# if CREATE_FGONS: +# edge_users= {} +# for ngon in ngon_face_indices: +# for i in (0,1,2): +# i1= face_vert_loc_indicies[ngon[i ]] +# i2= face_vert_loc_indicies[ngon[i-1]] +# if i1>i2: i1,i2= i2,i1 - try: - edge_users[i1,i2]+=1 - except KeyError: - edge_users[i1,i2]= 1 +# try: +# edge_users[i1,i2]+=1 +# except KeyError: +# edge_users[i1,i2]= 1 - for key, users in edge_users.iteritems(): - if users>1: - fgon_edges[key]= None +# for key, users in edge_users.iteritems(): +# if users>1: +# fgon_edges[key]= None - # remove all after 3, means we dont have to pop this one. - faces.pop(f_idx) +# # remove all after 3, means we dont have to pop this one. +# faces.pop(f_idx) # Build sharp edges @@ -564,33 +574,36 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l # me_edges[ed].flag |= SHARP # del SHARP -# if CREATE_EDGES: + if CREATE_EDGES: + + me.add_geometry(0, len(edges)) + + # edges is (should be) a list of (a, b) tuples + me.edges.foreach_set("verts", unpack_list(edges)) # me_edges.extend( edges ) # del me_edges - + + me.calc_normals() # me.calcNormals() ob= bpy.data.add_object("MESH", "Mesh") ob.data= me + scn.add_object(ob) # ob= scn.objects.new(me) new_objects.append(ob) -# # Create the vertex groups. No need to have the flag passed here since we test for the -# # content of the vertex_groups. If the user selects to NOT have vertex groups saved then -# # the following test will never run -# for group_name, group_indicies in vertex_groups.iteritems(): -# i= ob.add_vertex_group(group_name) -# # me.addVertGroup(group_name) -# me.assign_verts_to_group(group_index, group_indicies, len(group_indicies), 1.0, 'REPLACE') -# # me.assignVertsToGroup(group_name, group_indicies, 1.00, Mesh.AssignModes.REPLACE) + # Create the vertex groups. No need to have the flag passed here since we test for the + # content of the vertex_groups. If the user selects to NOT have vertex groups saved then + # the following test will never run + for group_name, group_indicies in vertex_groups.iteritems(): + group= ob.add_vertex_group(group_name) +# me.addVertGroup(group_name) + for vertex_index in group_indicies: + ob.add_vertex_to_group(vertex_index, group, 1.0, 'REPLACE') +# me.assignVertsToGroup(group_name, group_indicies, 1.00, Mesh.AssignModes.REPLACE) -class Mesh(bpy.types.Mesh): - - def assign_verts_to_group(self, group_index, vert_indices, weight): - - def create_nurbs(scn, context_nurbs, vert_loc, new_objects): ''' Add nurbs object to blender, only support one type at the moment @@ -700,16 +713,16 @@ def get_float_func(filepath): return float def load_obj(filepath, - CLAMP_SIZE= 0.0, - CREATE_FGONS= True, - CREATE_SMOOTH_GROUPS= True, - CREATE_EDGES= True, - SPLIT_OBJECTS= True, - SPLIT_GROUPS= True, - SPLIT_MATERIALS= True, - ROTATE_X90= True, - IMAGE_SEARCH=True, - POLYGROUPS=False): + CLAMP_SIZE= 0.0, + CREATE_FGONS= True, + CREATE_SMOOTH_GROUPS= True, + CREATE_EDGES= True, + SPLIT_OBJECTS= True, + SPLIT_GROUPS= True, + SPLIT_MATERIALS= True, + ROTATE_X90= True, + IMAGE_SEARCH=True, + POLYGROUPS=False): ''' Called by the user interface or another script. load_obj(path) - should give acceptable results. @@ -997,30 +1010,30 @@ def load_obj(filepath, create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc_split, verts_tex, faces_split, unique_materials_split, unique_material_images, unique_smooth_groups, vertex_groups, dataname) # nurbs support - for context_nurbs in nurbs: - create_nurbs(scn, context_nurbs, verts_loc, new_objects) +# for context_nurbs in nurbs: +# create_nurbs(scn, context_nurbs, verts_loc, new_objects) axis_min= [ 1000000000]*3 axis_max= [-1000000000]*3 - if CLAMP_SIZE: - # Get all object bounds - for ob in new_objects: - for v in ob.getBoundBox(): - for axis, value in enumerate(v): - if axis_min[axis] > value: axis_min[axis]= value - if axis_max[axis] < value: axis_max[axis]= value +# if CLAMP_SIZE: +# # Get all object bounds +# for ob in new_objects: +# for v in ob.getBoundBox(): +# for axis, value in enumerate(v): +# if axis_min[axis] > value: axis_min[axis]= value +# if axis_max[axis] < value: axis_max[axis]= value - # Scale objects - max_axis= max(axis_max[0]-axis_min[0], axis_max[1]-axis_min[1], axis_max[2]-axis_min[2]) - scale= 1.0 +# # Scale objects +# max_axis= max(axis_max[0]-axis_min[0], axis_max[1]-axis_min[1], axis_max[2]-axis_min[2]) +# scale= 1.0 - while CLAMP_SIZE < max_axis * scale: - scale= scale/10.0 +# while CLAMP_SIZE < max_axis * scale: +# scale= scale/10.0 - for ob in new_objects: - ob.setSize(scale, scale, scale) +# for ob in new_objects: +# ob.setSize(scale, scale, scale) # Better rotate the vert locations #if not ROTATE_X90: @@ -1276,5 +1289,10 @@ else: # NOTES (all line numbers refer to 2.4x import_obj.py, not this file) # check later: line 489 -# edge flags, edges, normals: lines 508-528 -# vertex groups: line 533 - cannot assign vertex groups +# can convert now: edge flags, edges: lines 508-528 +# ngon (uses python module BPyMesh): 384-414 +# nurbs: 947- +# clamp size: cannot get bound box with RNA - neither I can write RNA struct function that returns it - +# again, RNA limitation +# warning: uses bpy.sys.exists +# get back to l 140 (here) diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index e779f901b3c..8a9fdb8531d 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1911,7 +1911,7 @@ RNAProcessItem PROCESS_ITEMS[]= { {"rna_lamp.c", NULL, RNA_def_lamp}, {"rna_lattice.c", NULL, RNA_def_lattice}, {"rna_main.c", "rna_main_api.c", RNA_def_main}, - {"rna_material.c", NULL, RNA_def_material}, + {"rna_material.c", "rna_material_api.c", RNA_def_material}, {"rna_mesh.c", "rna_mesh_api.c", RNA_def_mesh}, {"rna_meta.c", NULL, RNA_def_meta}, {"rna_modifier.c", NULL, RNA_def_modifier}, diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index f465e733d68..7bf33760011 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -193,6 +193,7 @@ void RNA_api_object(struct StructRNA *srna); void RNA_api_ui_layout(struct StructRNA *srna); void RNA_api_wm(struct StructRNA *srna); void RNA_api_scene(struct StructRNA *srna); +void RNA_api_material(StructRNA *srna); /* ID Properties */ diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 08a3b7cee25..9dc32acff6f 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -40,6 +40,7 @@ #include "BKE_mesh.h" #include "BKE_library.h" #include "BKE_object.h" +#include "BKE_material.h" #include "DNA_mesh_types.h" @@ -68,7 +69,7 @@ static Object* rna_Main_add_object(Main *main, int type, char *name) } /* - WARNING: the following example shows when this function should not be called + NOTE: the following example shows when this function should _not_ be called ob = bpy.data.add_object() scene.add_object(ob) @@ -87,6 +88,20 @@ static void rna_Main_remove_object(Main *main, ReportList *reports, Object *ob) BKE_report(reports, RPT_ERROR, "Object must have zero users to be removed."); } +static Material *rna_Main_add_material(Main *main, char *name) +{ + return add_material(name); +} + +/* TODO: remove material? */ + +struct Tex *rna_Main_add_texture(Main *main, char *name) +{ + return add_texture(name); +} + +/* TODO: remove texture? */ + #else void RNA_api_main(StructRNA *srna) @@ -136,6 +151,19 @@ void RNA_api_main(StructRNA *srna) RNA_def_function_ui_description(func, "Remove a mesh if it has zero users."); parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh to remove."); RNA_def_property_flag(parm, PROP_REQUIRED); + + func= RNA_def_function(srna, "add_material", "rna_Main_add_material"); + RNA_def_function_ui_description(func, "Add a new material."); + parm= RNA_def_string(func, "name", "Material", 0, "", "New name for the datablock."); /* optional */ + parm= RNA_def_pointer(func, "material", "Material", "", "New material."); + RNA_def_function_return(func, parm); + + func= RNA_def_function(srna, "add_texture", "rna_Main_add_texture"); + RNA_def_function_ui_description(func, "Add a new texture."); + parm= RNA_def_string(func, "name", "Tex", 0, "", "New name for the datablock."); /* optional */ + parm= RNA_def_pointer(func, "texture", "Texture", "", "New texture."); + RNA_def_function_return(func, parm); + } #endif diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 41f31594f6e..6de13dbd440 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -1134,6 +1134,8 @@ void RNA_def_material(BlenderRNA *brna) rna_def_material_sss(brna); rna_def_material_mtex(brna); rna_def_material_strand(brna); + + RNA_api_material(srna); } void rna_def_mtex_common(StructRNA *srna, const char *begin, const char *activeget, const char *structname) diff --git a/source/blender/makesrna/intern/rna_material_api.c b/source/blender/makesrna/intern/rna_material_api.c new file mode 100644 index 00000000000..e2b47460fdb --- /dev/null +++ b/source/blender/makesrna/intern/rna_material_api.c @@ -0,0 +1,126 @@ +/** + * + * + * ***** 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) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include + +#include "RNA_define.h" +#include "RNA_types.h" + +#include "DNA_material_types.h" + +#ifdef RNA_RUNTIME + +#include "BKE_material.h" +#include "BKE_texture.h" + +/* + Adds material to the first free texture slot. + If all slots are busy, replaces the first. +*/ +static void rna_Material_add_texture(Material *ma, Tex *tex, int mapto, int texco) +{ + int i; + MTex *mtex; + int slot= -1; + + for (i= 0; i < MAX_MTEX; i++) { + if (!ma->mtex[i]) { + slot= i; + break; + } + } + + if (slot == -1) + slot= 0; + + if (ma->mtex[slot]) { + ma->mtex[slot]->tex->id.us--; + } + else { + ma->mtex[slot]= add_mtex(); + } + + mtex= ma->mtex[slot]; + + mtex->tex= tex; + id_us_plus(&tex->id); + + mtex->texco= mapto; + mtex->mapto= texco; +} + +#else + +void RNA_api_material(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *parm; + + /* copied from rna_def_material_mtex (rna_material.c) */ + static EnumPropertyItem prop_texture_coordinates_items[] = { + {TEXCO_GLOB, "GLOBAL", 0, "Global", "Uses global coordinates for the texture coordinates."}, + {TEXCO_OBJECT, "OBJECT", 0, "Object", "Uses linked object's coordinates for texture coordinates."}, + {TEXCO_UV, "UV", 0, "UV", "Uses UV coordinates for texture coordinates."}, + {TEXCO_ORCO, "ORCO", 0, "Generated", "Uses the original undeformed coordinates of the object."}, + {TEXCO_STRAND, "STRAND", 0, "Strand", "Uses normalized strand texture coordinate (1D)."}, + {TEXCO_STICKY, "STICKY", 0, "Sticky", "Uses mesh's sticky coordinates for the texture coordinates."}, + {TEXCO_WINDOW, "WINDOW", 0, "Window", "Uses screen coordinates as texture coordinates."}, + {TEXCO_NORM, "NORMAL", 0, "Normal", "Uses normal vector as texture coordinates."}, + {TEXCO_REFL, "REFLECTION", 0, "Reflection", "Uses reflection vector as texture coordinates."}, + {TEXCO_STRESS, "STRESS", 0, "Stress", "Uses the difference of edge lengths compared to original coordinates of the mesh."}, + {TEXCO_TANGENT, "TANGENT", 0, "Tangent", "Uses the optional tangent vector as texture coordinates."}, + + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem prop_texture_mapto_items[] = { + {MAP_COL, "COLOR", 0, "Color", "Causes the texture to affect basic color of the material"}, + {MAP_NORM, "NORMAL", 0, "Normal", "Causes the texture to affect the rendered normal"}, + {MAP_COLSPEC, "SPEC_COLOR", 0, "Specularity Color", "Causes the texture to affect the specularity color"}, + {MAP_COLMIR, "MIRROR", 0, "Mirror", "Causes the texture to affect the mirror color"}, + {MAP_REF, "REFLECTION", 0, "Reflection", "Causes the texture to affect the value of the materials reflectivity"}, + {MAP_SPEC, "SPECULARITY", 0, "Specularity", "Causes the texture to affect the value of specularity"}, + {MAP_EMIT, "EMIT", 0, "Emit", "Causes the texture to affect the emit value"}, + {MAP_ALPHA, "ALPHA", 0, "Alpha", "Causes the texture to affect the alpha value"}, + {MAP_HAR, "HARDNESS", 0, "Hardness", "Causes the texture to affect the hardness value"}, + {MAP_RAYMIRR, "RAY_MIRROR", 0, "Ray-Mirror", "Causes the texture to affect the ray-mirror value"}, + {MAP_TRANSLU, "TRANSLUCENCY", 0, "Translucency", "Causes the texture to affect the translucency value"}, + {MAP_AMB, "AMBIENT", 0, "Ambient", "Causes the texture to affect the value of ambient"}, + {MAP_DISPLACE, "DISPLACEMENT", 0, "Displacement", "Let the texture displace the surface"}, + {MAP_WARP, "WARP", 0, "Warp", "Let the texture warp texture coordinates of next channels"}, + {0, NULL, 0, NULL, NULL}}; + + func= RNA_def_function(srna, "add_texture", "rna_Material_add_texture"); + RNA_def_function_ui_description(func, "Add a texture to material's free texture slot."); + parm= RNA_def_pointer(func, "texture", "Texture", "", "Texture to add."); + parm= RNA_def_enum(func, "texture_coordinates", prop_texture_coordinates_items, TEXCO_UV, "", "Source of texture coordinate information."); /* optional */ + parm= RNA_def_enum(func, "map_to", prop_texture_mapto_items, MAP_COL, "", "Controls which material property the texture affects."); /* optional */ +} + +#endif + diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index ecc32b23249..b2157340207 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -253,6 +253,11 @@ static void rna_Mesh_add_uv_layer(Mesh *me) me->mtface= CustomData_add_layer(&me->fdata, CD_MTFACE, CD_DEFAULT, NULL, me->totface); } +static void rna_Mesh_calc_normals(Mesh *me) +{ + mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL); +} + #else void RNA_api_mesh(StructRNA *srna) @@ -281,6 +286,9 @@ void RNA_api_mesh(StructRNA *srna) func= RNA_def_function(srna, "add_uv_layer", "rna_Mesh_add_uv_layer"); RNA_def_function_ui_description(func, "Add new UV layer to Mesh."); + func= RNA_def_function(srna, "calc_normals", "rna_Mesh_calc_normals"); + RNA_def_function_ui_description(func, "Calculate vertex normals."); + /* func= RNA_def_function(srna, "add_geom", "rna_Mesh_add_geom"); RNA_def_function_ui_description(func, "Add geometry data to mesh."); diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 9d1a6a39d51..02db7e83062 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -155,10 +155,15 @@ static void rna_Object_convert_to_triface(Object *ob, bContext *C, ReportList *r DAG_object_flush_update(sce, ob, OB_RECALC_DATA); } -static int rna_Object_add_vertex_group(Object *ob, char *group_name) +static bDeformGroup *rna_Object_add_vertex_group(Object *ob, char *group_name) { - bDeformGroup *defgroup= add_defgroup_name(ob, group_name); - return BLI_findindex(&ob->defbase, defgroup); + return add_defgroup_name(ob, group_name); +} + +static void rna_Object_add_vertex_to_group(Object *ob, int vertex_index, bDeformGroup *def, float weight, int assignmode) +{ + /* creates dverts if needed */ + add_vert_to_defgroup(ob, def, vertex_index, weight, assignmode); } /* @@ -210,6 +215,13 @@ void RNA_api_object(StructRNA *srna) {0, NULL, 0, NULL, NULL} }; + static EnumPropertyItem assign_mode_items[] = { + {WEIGHT_REPLACE, "REPLACE", 0, "Replace", "Replace."}, /* TODO: more meaningful descriptions */ + {WEIGHT_ADD, "ADD", 0, "Add", "Add."}, + {WEIGHT_SUBTRACT, "SUBTRACT", 0, "Subtract", "Subtract."}, + {0, NULL, 0, NULL, NULL} + }; + func= RNA_def_function(srna, "create_mesh", "rna_Object_create_mesh"); RNA_def_function_ui_description(func, "Create a Mesh datablock with all modifiers applied."); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); @@ -234,9 +246,20 @@ void RNA_api_object(StructRNA *srna) func= RNA_def_function(srna, "add_vertex_group", "rna_Object_add_vertex_group"); RNA_def_function_ui_description(func, "Add vertex group to object."); - parm= RNA_def_string(func, "name", "Group", 0, "", "Vertex group name."); - parm= RNA_def_int(func, "group_index", 0, 0, 0, "", "Index of the created vertex group.", 0, 0); + parm= RNA_def_string(func, "name", "Group", 0, "", "Vertex group name."); /* optional */ + parm= RNA_def_pointer(func, "group", "VertexGroup", "", "New vertex group."); RNA_def_function_return(func, parm); + + func= RNA_def_function(srna, "add_vertex_to_group", "rna_Object_add_vertex_to_group"); + RNA_def_function_ui_description(func, "Add vertex to a vertex group."); + parm= RNA_def_int(func, "vertex_index", 0, 0, 0, "", "Vertex index.", 0, 0); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_pointer(func, "group", "VertexGroup", "", "Vertex group to add vertex to."); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_float(func, "weight", 0, 0.0f, 1.0f, "", "Vertex weight.", 0.0f, 1.0f); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_enum(func, "type", assign_mode_items, 0, "", "Vertex assign mode."); + RNA_def_property_flag(parm, PROP_REQUIRED); } #endif From 2a23fda9d55034e524be32bd8cb196ae4cd93920 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Fri, 3 Jul 2009 17:44:20 +0000 Subject: [PATCH 37/68] OBJ importer almost converted, except a few features (NURBS, NGON, FGON and sharp edges). Added to API: - Main.add_image - Material.z_transparency - two temporary properties: Image.depth and Image.has_data --- release/io/import_obj.py | 146 ++++++++++++------ source/blender/makesrna/intern/rna_image.c | 37 +++++ source/blender/makesrna/intern/rna_main_api.c | 17 +- source/blender/makesrna/intern/rna_material.c | 5 + 4 files changed, 158 insertions(+), 47 deletions(-) diff --git a/release/io/import_obj.py b/release/io/import_obj.py index e16780ce1d6..659d5207261 100644 --- a/release/io/import_obj.py +++ b/release/io/import_obj.py @@ -40,14 +40,16 @@ Note, This loads mesh objects and materials only, nurbs and curves are not suppo # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- -from Blender import Mesh, Draw, Window, Texture, Material, sys import bpy -# import BPyMesh -import BPyImage -import BPyMessages +import os -try: import os -except: os= False +# from Blender import Mesh, Draw, Window, Texture, Material, sys +# # import BPyMesh +# import BPyImage +# import BPyMessages + +# try: import os +# except: os= False # Generic path functions def stripFile(path): @@ -101,22 +103,44 @@ def line_value(line_split): elif length > 2: return ' '.join( line_split[1:] ) +# limited replacement for BPyImage.comprehensiveImageLoad +def load_image(imagepath, direc): + + if os.path.exists(imagepath): + return bpy.data.add_image(imagepath) + + im_base = os.path.basename(imagepath) + + tmp = os.path.join(direc, im_base) + if os.path.exists(tmp): + return bpy.data.add_image(tmp) + + # TODO comprehensiveImageLoad also searched in bpy.config.textureDir + def obj_image_load(imagepath, DIR, IMAGE_SEARCH): - ''' - Mainly uses comprehensiveImageLoad - but tries to replace '_' with ' ' for Max's exporter replaces spaces with underscores. - ''' - + if '_' in imagepath: - image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) - if image: return image - # Did the exporter rename the image? - image= BPyImage.comprehensiveImageLoad(imagepath.replace('_', ' '), DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) + image= load_image(imagepath.replace('_', ' '), DIR) if image: return image + + return load_image(imagepath, DIR) + +# def obj_image_load(imagepath, DIR, IMAGE_SEARCH): +# ''' +# Mainly uses comprehensiveImageLoad +# but tries to replace '_' with ' ' for Max's exporter replaces spaces with underscores. +# ''' - # Return an image, placeholder if it dosnt exist - image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= True, RECURSIVE= IMAGE_SEARCH) - return image +# if '_' in imagepath: +# image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) +# if image: return image +# # Did the exporter rename the image? +# image= BPyImage.comprehensiveImageLoad(imagepath.replace('_', ' '), DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) +# if image: return image + +# # Return an image, placeholder if it dosnt exist +# image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= True, RECURSIVE= IMAGE_SEARCH) +# return image def create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH): @@ -139,46 +163,65 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ # Absolute path - c:\.. etc would work here image= obj_image_load(imagepath, DIR, IMAGE_SEARCH) has_data = image.has_data - texture.image = image + + if image: + texture.image = image # Adds textures for materials (rendering) if type == 'Kd': if has_data and image.depth == 32: # Image has alpha - blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL | Texture.MapTo.ALPHA) - texture.setImageFlags('MipMap', 'InterPol', 'UseAlpha') - blender_material.mode |= Material.Modes.ZTRANSP + + # XXX bitmask won't work? + blender_material.add_texture(texture, "UV", ("COLOR", "ALPHA")) + texture.mipmap = True + texture.interpolation = True + texture.use_alpha = True + blender_material.z_transparency = True blender_material.alpha = 0.0 + +# blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL | Texture.MapTo.ALPHA) +# texture.setImageFlags('MipMap', 'InterPol', 'UseAlpha') +# blender_material.mode |= Material.Modes.ZTRANSP +# blender_material.alpha = 0.0 else: - blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL) + blender_material.add_texture(texture, "UV", "COLOR") +# blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL) # adds textures to faces (Textured/Alt-Z mode) # Only apply the diffuse texture to the face if the image has not been set with the inline usemat func. unique_material_images[context_material_name]= image, has_data # set the texface image elif type == 'Ka': - blender_material.setTexture(1, texture, Texture.TexCo.UV, Texture.MapTo.CMIR) # TODO- Add AMB to BPY API + blender_material.add_texture(texture, "UV", "AMBIENT") +# blender_material.setTexture(1, texture, Texture.TexCo.UV, Texture.MapTo.CMIR) # TODO- Add AMB to BPY API elif type == 'Ks': - blender_material.setTexture(2, texture, Texture.TexCo.UV, Texture.MapTo.SPEC) + blender_material.add_texture(texture, "UV", "SPECULAR") +# blender_material.setTexture(2, texture, Texture.TexCo.UV, Texture.MapTo.SPEC) elif type == 'Bump': - blender_material.setTexture(3, texture, Texture.TexCo.UV, Texture.MapTo.NOR) + blender_material.add_texture(texture, "UV", "NORMAL") +# blender_material.setTexture(3, texture, Texture.TexCo.UV, Texture.MapTo.NOR) elif type == 'D': - blender_material.setTexture(4, texture, Texture.TexCo.UV, Texture.MapTo.ALPHA) - blender_material.mode |= Material.Modes.ZTRANSP + blender_material.add_texture(texture, "UV", "ALPHA") + blender_material.z_transparency = True blender_material.alpha = 0.0 +# blender_material.setTexture(4, texture, Texture.TexCo.UV, Texture.MapTo.ALPHA) +# blender_material.mode |= Material.Modes.ZTRANSP +# blender_material.alpha = 0.0 # Todo, unset deffuse material alpha if it has an alpha channel elif type == 'refl': - blender_material.setTexture(5, texture, Texture.TexCo.UV, Texture.MapTo.REF) + blender_material.add_texture(texture, "UV", "REFLECTION") +# blender_material.setTexture(5, texture, Texture.TexCo.UV, Texture.MapTo.REF) # Add an MTL with the same name as the obj if no MTLs are spesified. temp_mtl= stripExt(stripPath(filepath))+ '.mtl' if sys.exists(DIR + temp_mtl) and temp_mtl not in material_libs: - material_libs.append( temp_mtl ) + material_libs.append( temp_mtl ) del temp_mtl #Create new materials @@ -578,7 +621,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l me.add_geometry(0, len(edges)) - # edges is (should be) a list of (a, b) tuples + # edges should be a list of (a, b) tuples me.edges.foreach_set("verts", unpack_list(edges)) # me_edges.extend( edges ) @@ -713,6 +756,7 @@ def get_float_func(filepath): return float def load_obj(filepath, + context, CLAMP_SIZE= 0.0, CREATE_FGONS= True, CREATE_SMOOTH_GROUPS= True, @@ -733,8 +777,9 @@ def load_obj(filepath, if SPLIT_OBJECTS or SPLIT_GROUPS or SPLIT_MATERIALS: POLYGROUPS = False - - time_main= sys.time() + + time_main= bpy.sys.time() +# time_main= sys.time() verts_loc= [] verts_tex= [] @@ -772,7 +817,8 @@ def load_obj(filepath, context_multi_line= '' print '\tparsing obj file "%s"...' % filepath, - time_sub= sys.time() + time_sub= bpy.sys.time() +# time_sub= sys.time() file= open(filepath, 'rU') for line in file: #.xreadlines(): @@ -980,15 +1026,17 @@ def load_obj(filepath, ''' file.close() - time_new= sys.time() + time_new= bpy.sys.time() +# time_new= sys.time() print '%.4f sec' % (time_new-time_sub) time_sub= time_new print '\tloading materials and images...', create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH) - - time_new= sys.time() + + time_new= bpy.sys.time() +# time_new= sys.time() print '%.4f sec' % (time_new-time_sub) time_sub= time_new @@ -996,8 +1044,12 @@ def load_obj(filepath, verts_loc[:] = [(v[0], v[2], -v[1]) for v in verts_loc] # deselect all - scn = bpy.data.scenes.active - scn.objects.selected = [] + if context.selected_objects: + bpy.ops.OBJECT_OT_select_all_toggle() + + scene = context.scene +# scn = bpy.data.scenes.active +# scn.objects.selected = [] new_objects= [] # put new objects here print '\tbuilding geometry...\n\tverts:%i faces:%i materials: %i smoothgroups:%i ...' % ( len(verts_loc), len(faces), len(unique_materials), len(unique_smooth_groups) ), @@ -1007,7 +1059,7 @@ def load_obj(filepath, for verts_loc_split, faces_split, unique_materials_split, dataname in split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP, SPLIT_MATERIALS): # Create meshes from the data, warning 'vertex_groups' wont support splitting - create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc_split, verts_tex, faces_split, unique_materials_split, unique_material_images, unique_smooth_groups, vertex_groups, dataname) + create_mesh(scene, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc_split, verts_tex, faces_split, unique_materials_split, unique_material_images, unique_smooth_groups, vertex_groups, dataname) # nurbs support # for context_nurbs in nurbs: @@ -1039,8 +1091,9 @@ def load_obj(filepath, #if not ROTATE_X90: # for ob in new_objects: # ob.RotX = -1.570796326794896558 - - time_new= sys.time() + + time_new= bpy.sys.time() +# time_new= sys.time() print '%.4f sec' % (time_new-time_sub) print 'finished importing: "%s" in %.4f sec.' % (filepath, (time_new-time_main)) @@ -1292,7 +1345,10 @@ else: # can convert now: edge flags, edges: lines 508-528 # ngon (uses python module BPyMesh): 384-414 # nurbs: 947- -# clamp size: cannot get bound box with RNA - neither I can write RNA struct function that returns it - -# again, RNA limitation -# warning: uses bpy.sys.exists +# NEXT clamp size: get bound box with RNA # get back to l 140 (here) +# search image in bpy.config.textureDir - load_image +# replaced BPyImage.comprehensiveImageLoad with a simplified version that only checks additional directory specified, but doesn't search dirs recursively (obj_image_load) +# bitmask won't work? - 132 +# uses operator bpy.ops.OBJECT_OT_select_all_toggle() to deselect all (not necessary?) +# uses bpy.sys.exists and bpy.sys.time() diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index c74e46c17da..fbef838d06c 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -66,6 +66,29 @@ static int rna_Image_dirty_get(PointerRNA *ptr) return 0; } +static int rna_Image_has_data_get(PointerRNA *ptr) +{ + Image *im= (Image*)ptr->data; + + if (im->ibufs.first) + return 1; + + return 0; +} + +static int rna_Image_depth_get(PointerRNA *ptr) +{ + Image *im= (Image*)ptr->data; + ImBuf *ibuf= BKE_image_get_ibuf(im, NULL); + + if (!ibuf) return 0; + + if (ibuf->rect_float) + return 128; + + return ibuf->depth; +} + #else static void rna_def_imageuser(BlenderRNA *brna) @@ -275,6 +298,20 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "tpageflag", IMA_CLAMP_V); RNA_def_property_ui_text(prop, "Clamp Y", "Disable texture repeating vertically."); RNA_def_property_update(prop, NC_IMAGE|ND_DISPLAY, NULL); + + /* + Image.has_data and Image.depth are temporary, + Update import_obj.py when they are replaced (Arystan) + */ + prop= RNA_def_property(srna, "has_data", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_Image_has_data_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Has data", "True if this image has data."); + + prop= RNA_def_property(srna, "depth", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs(prop, "rna_Image_depth_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Depth", "Image bit depth."); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); } void RNA_def_image(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 9dc32acff6f..17f3800e10e 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -33,6 +33,8 @@ #include "RNA_types.h" #include "DNA_object_types.h" +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" #ifdef RNA_RUNTIME @@ -41,8 +43,7 @@ #include "BKE_library.h" #include "BKE_object.h" #include "BKE_material.h" - -#include "DNA_mesh_types.h" +#include "BKE_image.h" static Mesh *rna_Main_add_mesh(Main *main, char *name) { @@ -102,6 +103,11 @@ struct Tex *rna_Main_add_texture(Main *main, char *name) /* TODO: remove texture? */ +struct Image *rna_Main_add_image(Main *main, char *filename) +{ + return BKE_add_image_file(filename, 0); +} + #else void RNA_api_main(StructRNA *srna) @@ -164,6 +170,13 @@ void RNA_api_main(StructRNA *srna) parm= RNA_def_pointer(func, "texture", "Texture", "", "New texture."); RNA_def_function_return(func, parm); + func= RNA_def_function(srna, "add_image", "rna_Main_add_image"); + RNA_def_function_ui_description(func, "Add a new image."); + parm= RNA_def_string(func, "filename", "", 0, "", "Filename to load image from."); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_pointer(func, "image", "Image", "", "New image."); + RNA_def_function_return(func, parm); + } #endif diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 6de13dbd440..326c37d8597 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -1082,6 +1082,11 @@ void RNA_def_material(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "mode", MA_TANGENT_V); RNA_def_property_ui_text(prop, "Tangent Shading", "Use the material's tangent vector instead of the normal for shading - for anisotropic shading effects"); RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "z_transparency", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mode", MA_ZTRA); + RNA_def_property_ui_text(prop, "ZTransp", "Z-buffer transparency"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); /* nested structs */ prop= RNA_def_property(srna, "raytrace_mirror", PROP_POINTER, PROP_NEVER_NULL); From ad0b2c87d5c86b56f073c81f25df5c3988911d14 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Tue, 7 Jul 2009 08:38:18 +0000 Subject: [PATCH 38/68] Integrated unit testing framework with scons on Linux. I needed this to make sure that BKE_copy_images works properly, probably will be useful in future. Using Check framework (http://check.sourceforge.net/doc/check.html/index.html). WITH_BF_UNIT_TEST option builds 'alltest' program under [BUILDDIR]/bin, which, when executed, runs unit tests, currently only 1. Example output: ---------------------------------------------------------------------- Running suite(s): Image 0%: Checks: 1, Failures: 1, Errors: 0 tests/alltest.c:74:F:Core:test_copy_images:0: Expected //bar/image.png to be translated to /tmp/bar/image.png, got /tmp/bar/image.pn. ---------------------------------------------------------------------- Spent lots of time (a couple of days actually :) to figure out how to link the test program with Blender libraries. As it turned out there are circular dependencies among Blender libraries. GCC by default doesn't expect circular dependencies - dependant libs should precede libs they depend on. The magical --start-group linker option helped to solve this (http://stephane.carrez.free.fr/doc/ld_2.html#IDX122). Also: - added bpy.util module. bpy.sys.* functions will move here later - added bpy.util.copy_images that uses BKE_copy_images - export_obj.py uses bpy.util.copy_images --- SConstruct | 11 +- release/io/export_obj.py | 23 ++-- source/blender/blenkernel/BKE_image.h | 6 + source/blender/blenkernel/SConscript | 3 + source/blender/blenkernel/intern/image.c | 117 ++++++++++++++++ source/blender/python/intern/bpy_interface.c | 3 + source/blender/python/intern/bpy_util.c | 135 ++++++++++++++++++- source/blender/python/intern/bpy_util.h | 2 + tools/btools.py | 6 +- 9 files changed, 291 insertions(+), 15 deletions(-) diff --git a/SConstruct b/SConstruct index b85bc799ea5..2d1daba98b6 100644 --- a/SConstruct +++ b/SConstruct @@ -407,7 +407,16 @@ if env['WITH_BF_PLAYER']: if 'blender' in B.targets or not env['WITH_BF_NOBLENDER']: #env.BlenderProg(B.root_build_dir, "blender", dobj , [], mainlist + thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender') - env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist, [], thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender') + blen = env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist, [], thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender') + + build_data = {"lib": thestatlibs + thesyslibs, "libpath": thelibincs, "blen": blen} + + Export('env') + Export('build_data') + + BuildDir(B.root_build_dir+'/tests', 'tests', duplicate=0) + SConscript(B.root_build_dir+'/tests/SConscript') + if env['WITH_BF_PLAYER']: playerlist = B.create_blender_liblist(env, 'player') env.BlenderProg(B.root_build_dir, "blenderplayer", dobj + playerlist, [], thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blenderplayer') diff --git a/release/io/export_obj.py b/release/io/export_obj.py index 1ee685a52a3..ee045053dd3 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -178,19 +178,22 @@ def copy_images(dest_dir): pass # Now copy images - copyCount = 0 +# copyCount = 0 - for bImage in uniqueImages.values(): - image_path = bpy.sys.expandpath(bImage.filename) - if bpy.sys.exists(image_path): - # Make a name for the target path. - dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] - if not bpy.sys.exists(dest_image_path): # Image isnt alredy there - print('\tCopying "%s" > "%s"' % (image_path, dest_image_path)) - copy_file(image_path, dest_image_path) - copyCount+=1 +# for bImage in uniqueImages.values(): +# image_path = bpy.sys.expandpath(bImage.filename) +# if bpy.sys.exists(image_path): +# # Make a name for the target path. +# dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] +# if not bpy.sys.exists(dest_image_path): # Image isnt alredy there +# print('\tCopying "%s" > "%s"' % (image_path, dest_image_path)) +# copy_file(image_path, dest_image_path) +# copyCount+=1 + + paths= bpy.util.copy_images(uniqueImages.values(), dest_dir) print('\tCopied %d images' % copyCount) +# print('\tCopied %d images' % copyCount) # XXX not converted def test_nurbs_compat(ob): diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 816baa20467..f3f5266189a 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -40,6 +40,7 @@ struct ImBuf; struct Tex; struct anim; struct Scene; +struct ListBase; /* call from library */ void free_image(struct Image *me); @@ -154,6 +155,11 @@ struct Image *BKE_image_copy(struct Image *ima); /* merge source into dest, and free source */ void BKE_image_merge(struct Image *dest, struct Image *source); +/* ********************************** FOR EXPORTERS *********************** */ + +/* copy images into dest_dir */ +void BKE_copy_images(struct ListBase *images, char *dest_dir, struct ListBase *filenames); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/SConscript b/source/blender/blenkernel/SConscript index dbc990d0613..9aaea7e8aff 100644 --- a/source/blender/blenkernel/SConscript +++ b/source/blender/blenkernel/SConscript @@ -57,6 +57,9 @@ if env['BF_NO_ELBEEM']: if env['WITH_BF_LCMS']: defs.append('WITH_LCMS') + +if env['WITH_BF_UNIT_TEST']: + defs.append('WITH_UNIT_TEST') if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'): incs += ' ' + env['BF_PTHREADS_INC'] diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index ef0984bf93d..039c2d07bce 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -2109,3 +2109,120 @@ void BKE_image_user_calc_imanr(ImageUser *iuser, int cfra, int fieldnr) } } +/* + Copy list of images to dest_dir. + + paths is optional, if given, image paths for each image will be written in it. + If an image file doesn't exist, NULL is added in paths. + + Logic: + + For each image if it's "below" current .blend file directory, + rebuild the same dir structure in dest_dir. + + For example //textures/foo/bar.png becomes + [dest_dir]/textures/foo/bar.png. + + If an image is not "below" current .blend file directory, disregard + it's path and copy it in the same directory where 3D file goes. + + For example //../foo/bar.png becomes [dest_dir]/bar.png. + + This logic will help ensure that all image paths are relative and + that a user gets his images in one place. It'll also provide + consistent behaviour across exporters. +*/ +void BKE_copy_images(ListBase *images, char *dest_dir, ListBase *paths) +{ + char path[FILE_MAX]; + char dir[FILE_MAX]; + char base[FILE_MAX]; + char blend_dir[FILE_MAX]; /* directory, where current .blend file resides */ + char dest_path[FILE_MAX]; + int len; + Image *im; + LinkData *link; + + if (paths) { + memset(paths, 0, sizeof(*paths)); + } + + BLI_split_dirfile_basic(G.sce, blend_dir, NULL); + + link= images->first; + + while (link) { + im= link->data; + + BLI_strncpy(path, im->name, sizeof(path)); + + /* expand "//" in filename and get absolute path */ + BLI_convertstringcode(path, G.sce); + + /* in unit tests, we don't want to modify the filesystem */ +#ifndef WITH_UNIT_TEST + /* proceed only if image file exists */ + if (!BLI_exists(path)) { + + if (paths) { + LinkData *ld = MEM_callocN(sizeof(LinkData), "PathLinkData"); + ld->data= NULL; + BLI_addtail(paths, ld); + } + + continue; + } +#endif + + /* get the directory part */ + BLI_split_dirfile_basic(path, dir, base); + + len= strlen(blend_dir); + + /* if image is "below" current .blend file directory */ + if (!strncmp(path, blend_dir, len)) { + + /* if image is _in_ current .blend file directory */ + if (!strcmp(dir, blend_dir)) { + /* copy to dest_dir */ + BLI_join_dirfile(dest_path, dest_dir, base); + } + /* "below" */ + else { + char rel[FILE_MAX]; + + /* rel = image_path_dir - blend_dir */ + BLI_strncpy(rel, dir + len, sizeof(rel)); + + BLI_join_dirfile(dest_path, dest_dir, rel); + +#ifndef WITH_UNIT_TEST + /* build identical directory structure under dest_dir */ + BLI_make_existing_file(dest_path); +#endif + + BLI_join_dirfile(dest_path, dest_path, base); + } + + } + /* image is out of current directory */ + else { + /* copy to dest_dir */ + BLI_join_dirfile(dest_path, dest_dir, base); + } + +#ifndef WITH_UNIT_TEST + BLI_copy_fileops(path, dest_path); +#endif + + if (paths) { + LinkData *ld = MEM_callocN(sizeof(LinkData), "PathLinkData"); + len= strlen(dest_path) + 1; + ld->data= MEM_callocN(len, "PathLinkData"); + BLI_strncpy(ld->data, dest_path, len); + BLI_addtail(paths, ld); + } + + link= link->next; + } +} diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 96ef796839b..c895a41637d 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -92,6 +92,9 @@ void BPY_update_modules( void ) PyObject *mod= PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0); PyModule_AddObject( mod, "data", BPY_rna_module() ); PyModule_AddObject( mod, "types", BPY_rna_types() ); + PyModule_AddObject( mod, "util", BPY_util_module() ); + + /* XXX this will move to bpy.util */ PyModule_AddObject( mod, "sys", BPY_sys_module() ); } diff --git a/source/blender/python/intern/bpy_util.c b/source/blender/python/intern/bpy_util.c index bce73b903c0..5f235b91337 100644 --- a/source/blender/python/intern/bpy_util.c +++ b/source/blender/python/intern/bpy_util.c @@ -25,12 +25,17 @@ #include "DNA_listBase.h" #include "RNA_access.h" #include "bpy_util.h" -#include "BLI_dynstr.h" +#include "bpy_rna.h" + #include "MEM_guardedalloc.h" + +#include "BLI_dynstr.h" +#include "BLI_listbase.h" + #include "BKE_report.h" - - +#include "BKE_image.h" #include "BKE_context.h" + bContext* __py_context = NULL; bContext* BPy_GetContext(void) { return __py_context; }; void BPy_SetContext(bContext *C) { __py_context= C; }; @@ -464,3 +469,127 @@ int BPy_errors_to_report(ReportList *reports) Py_DECREF(pystring); return 1; } + + +/* bpy.util module */ +static PyObject *bpy_util_copy_images(PyObject *self, PyObject *args); + +struct PyMethodDef bpy_util_methods[] = { + {"copy_images", bpy_util_copy_images, METH_VARARGS, NULL}, + {NULL, NULL, 0, NULL} +}; + +#if PY_VERSION_HEX >= 0x03000000 +static struct PyModuleDef bpy_util_module = { + PyModuleDef_HEAD_INIT, + "bpyutil", + NULL, + -1, + bpy_util_methods, + NULL, NULL, NULL, NULL +}; +#endif + +PyObject *BPY_util_module( void ) +{ + PyObject *submodule, *dict; + +#if PY_VERSION_HEX >= 0x03000000 + submodule= PyModule_Create(&bpy_util_module); +#else /* Py2.x */ + submodule= Py_InitModule3("bpyutil", bpy_util_methods, NULL); +#endif + + dict = PyModule_GetDict(submodule); + + return submodule; +} + +/* + copy_images(images, dest_dir) + return filenames +*/ +static PyObject *bpy_util_copy_images(PyObject *self, PyObject *args) +{ + const char *dest_dir; + ListBase *images; + ListBase *paths; + LinkData *link; + PyObject *seq; + PyObject *ret; + PyObject *item; + int i; + int len; + + /* check args/types */ + if (!PyArg_ParseTuple(args, "Os", &seq, &dest_dir)) { + PyErr_SetString(PyExc_TypeError, "Invalid arguments."); + return NULL; + } + + /* expecting a sequence of Image objects */ + if (!PySequence_Check(seq)) { + PyErr_SetString(PyExc_TypeError, "Expected a sequence of images."); + return NULL; + } + + /* create image list */ + len= PySequence_Size(seq); + + if (!len) { + PyErr_SetString(PyExc_TypeError, "At least one image should be specified."); + return NULL; + } + + /* make sure all sequence items are Image */ + for(i= 0; i < len; i++) { + item= PySequence_GetItem(seq, i); + + if (!BPy_StructRNA_Check(item) || ((BPy_StructRNA*)item)->ptr.type != &RNA_Image) { + PyErr_SetString(PyExc_TypeError, "Expected a sequence of Image objects."); + return NULL; + } + } + + images= MEM_callocN(sizeof(*images), "ListBase of images"); + + for(i= 0; i < len; i++) { + BPy_StructRNA* srna; + + item= PySequence_GetItem(seq, i); + srna= (BPy_StructRNA*)item; + + link= MEM_callocN(sizeof(LinkData), "LinkData image"); + link->data= srna->ptr.data; + BLI_addtail(images, link); + + Py_DECREF(item); + } + + paths= MEM_callocN(sizeof(*paths), "ListBase of image paths"); + + /* call BKE_copy_images */ + BKE_copy_images(images, dest_dir, paths); + + /* convert filenames */ + ret= PyList_New(0); + len= BLI_countlist(paths); + + for(link= paths->first, i= 0; link; link++, i++) { + if (link->data) { + item= PyUnicode_FromString(link->data); + PyList_Append(ret, item); + Py_DECREF(item); + } + else { + PyList_Append(ret, Py_None); + } + } + + /* free memory */ + BLI_freelistN(images); + BLI_freelistN(paths); + + /* return filenames */ + return ret; +} diff --git a/source/blender/python/intern/bpy_util.h b/source/blender/python/intern/bpy_util.h index 6429af67eb0..89d27ba8325 100644 --- a/source/blender/python/intern/bpy_util.h +++ b/source/blender/python/intern/bpy_util.h @@ -81,4 +81,6 @@ int BPy_errors_to_report(struct ReportList *reports); struct bContext *BPy_GetContext(void); void BPy_SetContext(struct bContext *C); +PyObject *BPY_util_module(void); + #endif diff --git a/tools/btools.py b/tools/btools.py index 9603022deaa..f4d79b1bb24 100755 --- a/tools/btools.py +++ b/tools/btools.py @@ -65,6 +65,8 @@ def validate_arguments(args, bc): 'WITH_BF_DOCS', 'BF_NUMJOBS', 'BF_MSVS', + + 'WITH_BF_UNIT_TEST' ] # Have options here that scons expects to be lists @@ -356,7 +358,9 @@ def read_opts(cfg, args): ('BF_CONFIG', 'SCons python config file used to set default options', 'user_config.py'), ('BF_NUMJOBS', 'Number of build processes to spawn', '1'), - ('BF_MSVS', 'Generate MSVS project files and solution', False) + ('BF_MSVS', 'Generate MSVS project files and solution', False), + + (BoolVariable('WITH_BF_UNIT_TEST', 'Build unit tests', False)) ) # end of opts.AddOptions() From f7438ff77a206049cb11873e15360215064c1961 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Tue, 7 Jul 2009 19:13:05 +0000 Subject: [PATCH 39/68] - slowly starting FBX exporter conversion - added Bone.matrix and Bone.armature_matrix --- release/io/export_fbx.py | 3112 +++++++++++++++++ release/io/export_obj.py | 1 + source/blender/makesrna/intern/rna_armature.c | 10 + source/blender/makesrna/intern/rna_pose_api.c | 56 + 4 files changed, 3179 insertions(+) create mode 100644 release/io/export_fbx.py create mode 100644 source/blender/makesrna/intern/rna_pose_api.c diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py new file mode 100644 index 00000000000..cea9f500533 --- /dev/null +++ b/release/io/export_fbx.py @@ -0,0 +1,3112 @@ +#!BPY +""" +Name: 'Autodesk FBX (.fbx)...' +Blender: 249 +Group: 'Export' +Tooltip: 'Selection to an ASCII Autodesk FBX ' +""" +__author__ = "Campbell Barton" +__url__ = ['www.blender.org', 'blenderartists.org'] +__version__ = "1.2" + +__bpydoc__ = """\ +This script is an exporter to the FBX file format. + +http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx +""" +# -------------------------------------------------------------------------- +# FBX Export v0.1 by Campbell Barton (AKA Ideasman) +# -------------------------------------------------------------------------- +# ***** 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. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + +import os + +try: + import time + # import os # only needed for batch export, nbot used yet +except: + time = None # use this to check if they have python modules installed + +# for python 2.3 support +try: + set() +except: + try: + from sets import Set as set + except: + set = None # so it complains you dont have a ! + +# # os is only needed for batch 'own dir' option +# try: +# import os +# except: +# os = None + +import Blender +import bpy +from Blender.Mathutils import Matrix, Vector, RotationMatrix + +import BPyObject +import BPyMesh +import BPySys +import BPyMessages + +## This was used to make V, but faster not to do all that +##valid = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_,.()[]{}' +##v = range(255) +##for c in valid: v.remove(ord(c)) +v = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,42,43,47,58,59,60,61,62,63,64,92,94,96,124,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254] +invalid = ''.join([chr(i) for i in v]) +def cleanName(name): + for ch in invalid: name = name.replace(ch, '_') + return name +del v, i + + +def copy_file(source, dest): + file = open(source, 'rb') + data = file.read() + file.close() + + file = open(dest, 'wb') + file.write(data) + file.close() + + +def copy_images(dest_dir, textures): + if not dest_dir.endswith(os.sep): + dest_dir += os.sep + + image_paths = set() + for tex in textures: + image_paths.add(Blender.sys.expandpath(tex.filename)) + + # Now copy images + copyCount = 0 + for image_path in image_paths: + if Blender.sys.exists(image_path): + # Make a name for the target path. + dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] + if not Blender.sys.exists(dest_image_path): # Image isnt alredy there + print '\tCopying "%s" > "%s"' % (image_path, dest_image_path) + try: + copy_file(image_path, dest_image_path) + copyCount+=1 + except: + print '\t\tWarning, file failed to copy, skipping.' + + print '\tCopied %d images' % copyCount + +mtx4_identity = Matrix() + +# testing +mtx_x90 = RotationMatrix( 90, 3, 'x') # used +#mtx_x90n = RotationMatrix(-90, 3, 'x') +#mtx_y90 = RotationMatrix( 90, 3, 'y') +#mtx_y90n = RotationMatrix(-90, 3, 'y') +#mtx_z90 = RotationMatrix( 90, 3, 'z') +#mtx_z90n = RotationMatrix(-90, 3, 'z') + +#mtx4_x90 = RotationMatrix( 90, 4, 'x') +mtx4_x90n = RotationMatrix(-90, 4, 'x') # used +#mtx4_y90 = RotationMatrix( 90, 4, 'y') +mtx4_y90n = RotationMatrix(-90, 4, 'y') # used +mtx4_z90 = RotationMatrix( 90, 4, 'z') # used +mtx4_z90n = RotationMatrix(-90, 4, 'z') # used + +def strip_path(p): + return p.split('\\')[-1].split('/')[-1] + +# Used to add the scene name into the filename without using odd chars +sane_name_mapping_ob = {} +sane_name_mapping_mat = {} +sane_name_mapping_tex = {} +sane_name_mapping_take = {} +sane_name_mapping_group = {} + +# Make sure reserved names are not used +sane_name_mapping_ob['Scene'] = 'Scene_' +sane_name_mapping_ob['blend_root'] = 'blend_root_' + +def increment_string(t): + name = t + num = '' + while name and name[-1].isdigit(): + num = name[-1] + num + name = name[:-1] + if num: return '%s%d' % (name, int(num)+1) + else: return name + '_0' + + + +# todo - Disallow the name 'Scene' and 'blend_root' - it will bugger things up. +def sane_name(data, dct): + #if not data: return None + + if type(data)==tuple: # materials are paired up with images + data, other = data + use_other = True + else: + other = None + use_other = False + + if data: name = data.name + else: name = None + orig_name = name + + if other: + orig_name_other = other.name + name = '%s #%s' % (name, orig_name_other) + else: + orig_name_other = None + + # dont cache, only ever call once for each data type now, + # so as to avoid namespace collision between types - like with objects <-> bones + #try: return dct[name] + #except: pass + + if not name: + name = 'unnamed' # blank string, ASKING FOR TROUBLE! + else: + #name = BPySys.cleanName(name) + name = cleanName(name) # use our own + + while name in dct.itervalues(): name = increment_string(name) + + if use_other: # even if other is None - orig_name_other will be a string or None + dct[orig_name, orig_name_other] = name + else: + dct[orig_name] = name + + return name + +def sane_obname(data): return sane_name(data, sane_name_mapping_ob) +def sane_matname(data): return sane_name(data, sane_name_mapping_mat) +def sane_texname(data): return sane_name(data, sane_name_mapping_tex) +def sane_takename(data): return sane_name(data, sane_name_mapping_take) +def sane_groupname(data): return sane_name(data, sane_name_mapping_group) + +def derived_paths(fname_orig, basepath, FORCE_CWD=False): + ''' + fname_orig - blender path, can be relative + basepath - fname_rel will be relative to this + FORCE_CWD - dont use the basepath, just add a ./ to the filename. + use when we know the file will be in the basepath. + ''' + fname = Blender.sys.expandpath(fname_orig) + fname_strip = strip_path(fname) + if FORCE_CWD: fname_rel = '.' + os.sep + fname_strip + else: fname_rel = Blender.sys.relpath(fname, basepath) + if fname_rel.startswith('//'): fname_rel = '.' + os.sep + fname_rel[2:] + return fname, fname_strip, fname_rel + + +def mat4x4str(mat): + return '%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f' % tuple([ f for v in mat for f in v ]) + +def meshNormalizedWeights(me): + try: # account for old bad BPyMesh + groupNames, vWeightList = BPyMesh.meshWeight2List(me) + except: + return [],[] + + if not groupNames: + return [],[] + + for i, vWeights in enumerate(vWeightList): + tot = 0.0 + for w in vWeights: + tot+=w + + if tot: + for j, w in enumerate(vWeights): + vWeights[j] = w/tot + + return groupNames, vWeightList + +header_comment = \ +'''; FBX 6.1.0 project file +; Created by Blender FBX Exporter +; for support mail: ideasman42@gmail.com +; ---------------------------------------------------- + +''' + +# This func can be called with just the filename +def write(filename, batch_objects = None, \ + context = None, + EXP_OBS_SELECTED = True, + EXP_MESH = True, + EXP_MESH_APPLY_MOD = True, + EXP_MESH_HQ_NORMALS = False, + EXP_ARMATURE = True, + EXP_LAMP = True, + EXP_CAMERA = True, + EXP_EMPTY = True, + EXP_IMAGE_COPY = False, + GLOBAL_MATRIX = Matrix(), + ANIM_ENABLE = True, + ANIM_OPTIMIZE = True, + ANIM_OPTIMIZE_PRECISSION = 6, + ANIM_ACTION_ALL = False, + BATCH_ENABLE = False, + BATCH_GROUP = True, + BATCH_SCENE = False, + BATCH_FILE_PREFIX = '', + BATCH_OWN_DIR = False + ): + + # ----------------- Batch support! + if BATCH_ENABLE: + if os == None: BATCH_OWN_DIR = False + + fbxpath = filename + + # get the path component of filename + tmp_exists = bpy.sys.exists(fbxpath) +# tmp_exists = Blender.sys.exists(fbxpath) + + if tmp_exists != 2: # a file, we want a path + while fbxpath and fbxpath[-1] not in ('/', '\\'): + fbxpath = fbxpath[:-1] + if not filename: + # XXX + print('Error%t|Directory does not exist!') +# Draw.PupMenu('Error%t|Directory does not exist!') + return + + tmp_exists = bpy.sys.exists(fbxpath) +# tmp_exists = Blender.sys.exists(fbxpath) + + if tmp_exists != 2: + # XXX + print('Error%t|Directory does not exist!') +# Draw.PupMenu('Error%t|Directory does not exist!') + return + + if not fbxpath.endswith(os.sep): + fbxpath += os.sep + del tmp_exists + + + if BATCH_GROUP: + data_seq = bpy.data.groups + else: + data_seq = bpy.data.scenes + + # call this function within a loop with BATCH_ENABLE == False + orig_sce = context.scene +# orig_sce = bpy.data.scenes.active + + + new_fbxpath = fbxpath # own dir option modifies, we need to keep an original + for data in data_seq: # scene or group + newname = BATCH_FILE_PREFIX + cleanName(data.name) +# newname = BATCH_FILE_PREFIX + BPySys.cleanName(data.name) + + + if BATCH_OWN_DIR: + new_fbxpath = fbxpath + newname + os.sep + # path may alredy exist + # TODO - might exist but be a file. unlikely but should probably account for it. + + if bpy.sys.exists(new_fbxpath) == 0: +# if Blender.sys.exists(new_fbxpath) == 0: + os.mkdir(new_fbxpath) + + + filename = new_fbxpath + newname + '.fbx' + + print '\nBatch exporting %s as...\n\t"%s"' % (data, filename) + + # XXX don't know what to do with this, probably do the same? (Arystan) + if BATCH_GROUP: #group + # group, so objects update properly, add a dummy scene. + sce = bpy.data.scenes.new() + sce.Layers = (1<<20) -1 + bpy.data.scenes.active = sce + for ob_base in data.objects: + sce.objects.link(ob_base) + + sce.update(1) + + # TODO - BUMMER! Armatures not in the group wont animate the mesh + + else:# scene + + + data_seq.active = data + + + # Call self with modified args + # Dont pass batch options since we alredy usedt them + write(filename, data.objects, + context, + False, + EXP_MESH, + EXP_MESH_APPLY_MOD, + EXP_MESH_HQ_NORMALS, + EXP_ARMATURE, + EXP_LAMP, + EXP_CAMERA, + EXP_EMPTY, + EXP_IMAGE_COPY, + GLOBAL_MATRIX, + ANIM_ENABLE, + ANIM_OPTIMIZE, + ANIM_OPTIMIZE_PRECISSION, + ANIM_ACTION_ALL + ) + + if BATCH_GROUP: + # remove temp group scene + bpy.data.remove_scene(sce) +# bpy.data.scenes.unlink(sce) + + bpy.data.scenes.active = orig_sce + + return # so the script wont run after we have batch exported. + + # end batch support + + # Use this for working out paths relative to the export location + basepath = Blender.sys.dirname(filename) + + # ---------------------------------------------- + # storage classes + class my_bone_class: + __slots__ =(\ + 'blenName',\ + 'blenBone',\ + 'blenMeshes',\ + 'restMatrix',\ + 'parent',\ + 'blenName',\ + 'fbxName',\ + 'fbxArm',\ + '__pose_bone',\ + '__anim_poselist') + + def __init__(self, blenBone, fbxArm): + + # This is so 2 armatures dont have naming conflicts since FBX bones use object namespace + self.fbxName = sane_obname(blenBone) + + self.blenName = blenBone.name + self.blenBone = blenBone + self.blenMeshes = {} # fbxMeshObName : mesh + self.fbxArm = fbxArm + self.restMatrix = blenBone.armature_matrix + # self.restMatrix = blenBone.matrix['ARMATURESPACE'] + + # not used yet + # self.restMatrixInv = self.restMatrix.copy().invert() + # self.restMatrixLocal = None # set later, need parent matrix + + self.parent = None + + # not public + pose = fbxArm.blenObject.pose +# pose = fbxArm.blenObject.getPose() + self.__pose_bone = pose.bones[self.blenName] + + # store a list if matricies here, (poseMatrix, head, tail) + # {frame:posematrix, frame:posematrix, ...} + self.__anim_poselist = {} + + ''' + def calcRestMatrixLocal(self): + if self.parent: + self.restMatrixLocal = self.restMatrix * self.parent.restMatrix.copy().invert() + else: + self.restMatrixLocal = self.restMatrix.copy() + ''' + def setPoseFrame(self, f): + # cache pose info here, frame must be set beforehand + + # Didnt end up needing head or tail, if we do - here it is. + ''' + self.__anim_poselist[f] = (\ + self.__pose_bone.poseMatrix.copy(),\ + self.__pose_bone.head.copy(),\ + self.__pose_bone.tail.copy() ) + ''' + + self.__anim_poselist[f] = self.__pose_bone.poseMatrix.copy() + + # get pose from frame. + def getPoseMatrix(self, f):# ---------------------------------------------- + return self.__anim_poselist[f] + ''' + def getPoseHead(self, f): + #return self.__pose_bone.head.copy() + return self.__anim_poselist[f][1].copy() + def getPoseTail(self, f): + #return self.__pose_bone.tail.copy() + return self.__anim_poselist[f][2].copy() + ''' + # end + + def getAnimParRelMatrix(self, frame): + #arm_mat = self.fbxArm.matrixWorld + #arm_mat = self.fbxArm.parRelMatrix() + if not self.parent: + #return mtx4_z90 * (self.getPoseMatrix(frame) * arm_mat) # dont apply arm matrix anymore + return mtx4_z90 * self.getPoseMatrix(frame) + else: + #return (mtx4_z90 * ((self.getPoseMatrix(frame) * arm_mat))) * (mtx4_z90 * (self.parent.getPoseMatrix(frame) * arm_mat)).invert() + return (mtx4_z90 * (self.getPoseMatrix(frame))) * (mtx4_z90 * self.parent.getPoseMatrix(frame)).invert() + + # we need thes because cameras and lights modified rotations + def getAnimParRelMatrixRot(self, frame): + return self.getAnimParRelMatrix(frame) + + def flushAnimData(self): + self.__anim_poselist.clear() + + + class my_object_generic: + # Other settings can be applied for each type - mesh, armature etc. + def __init__(self, ob, matrixWorld = None): + self.fbxName = sane_obname(ob) + self.blenObject = ob + self.fbxGroupNames = [] + self.fbxParent = None # set later on IF the parent is in the selection. + if matrixWorld: self.matrixWorld = matrixWorld * GLOBAL_MATRIX + else: self.matrixWorld = ob.matrixWorld * GLOBAL_MATRIX + self.__anim_poselist = {} # we should only access this + + def parRelMatrix(self): + if self.fbxParent: + return self.matrixWorld * self.fbxParent.matrixWorld.copy().invert() + else: + return self.matrixWorld + + def setPoseFrame(self, f): + self.__anim_poselist[f] = self.blenObject.matrixWorld.copy() + + def getAnimParRelMatrix(self, frame): + if self.fbxParent: + #return (self.__anim_poselist[frame] * self.fbxParent.__anim_poselist[frame].copy().invert() ) * GLOBAL_MATRIX + return (self.__anim_poselist[frame] * GLOBAL_MATRIX) * (self.fbxParent.__anim_poselist[frame] * GLOBAL_MATRIX).invert() + else: + return self.__anim_poselist[frame] * GLOBAL_MATRIX + + def getAnimParRelMatrixRot(self, frame): + type = self.blenObject.type + if self.fbxParent: + matrix_rot = (((self.__anim_poselist[frame] * GLOBAL_MATRIX) * (self.fbxParent.__anim_poselist[frame] * GLOBAL_MATRIX).invert())).rotationPart() + else: + matrix_rot = (self.__anim_poselist[frame] * GLOBAL_MATRIX).rotationPart() + + # Lamps need to be rotated + if type =='Lamp': + matrix_rot = mtx_x90 * matrix_rot + elif ob and type =='Camera': + y = Vector(0,1,0) * matrix_rot + matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y) + + return matrix_rot + + # ---------------------------------------------- + + + + + + print '\nFBX export starting...', filename + start_time = Blender.sys.time() + try: + file = open(filename, 'w') + except: + return False + + sce = bpy.data.scenes.active + world = sce.world + + + # ---------------------------- Write the header first + file.write(header_comment) + if time: + curtime = time.localtime()[0:6] + else: + curtime = (0,0,0,0,0,0) + # + file.write(\ +'''FBXHeaderExtension: { + FBXHeaderVersion: 1003 + FBXVersion: 6100 + CreationTimeStamp: { + Version: 1000 + Year: %.4i + Month: %.2i + Day: %.2i + Hour: %.2i + Minute: %.2i + Second: %.2i + Millisecond: 0 + } + Creator: "FBX SDK/FBX Plugins build 20070228" + OtherFlags: { + FlagPLE: 0 + } +}''' % (curtime)) + + file.write('\nCreationTime: "%.4i-%.2i-%.2i %.2i:%.2i:%.2i:000"' % curtime) + file.write('\nCreator: "Blender3D version %.2f"' % Blender.Get('version')) + + pose_items = [] # list of (fbxName, matrix) to write pose data for, easier to collect allong the way + + # --------------- funcs for exporting + def object_tx(ob, loc, matrix, matrix_mod = None): + ''' + Matrix mod is so armature objects can modify their bone matricies + ''' + if isinstance(ob, Blender.Types.BoneType): + + # we know we have a matrix + # matrix = mtx4_z90 * (ob.matrix['ARMATURESPACE'] * matrix_mod) + matrix = mtx4_z90 * ob.matrix['ARMATURESPACE'] # dont apply armature matrix anymore + + parent = ob.parent + if parent: + #par_matrix = mtx4_z90 * (parent.matrix['ARMATURESPACE'] * matrix_mod) + par_matrix = mtx4_z90 * parent.matrix['ARMATURESPACE'] # dont apply armature matrix anymore + matrix = matrix * par_matrix.copy().invert() + + matrix_rot = matrix.rotationPart() + + loc = tuple(matrix.translationPart()) + scale = tuple(matrix.scalePart()) + rot = tuple(matrix_rot.toEuler()) + + else: + # This is bad because we need the parent relative matrix from the fbx parent (if we have one), dont use anymore + #if ob and not matrix: matrix = ob.matrixWorld * GLOBAL_MATRIX + if ob and not matrix: raise "error: this should never happen!" + + matrix_rot = matrix + #if matrix: + # matrix = matrix_scale * matrix + + if matrix: + loc = tuple(matrix.translationPart()) + scale = tuple(matrix.scalePart()) + + matrix_rot = matrix.rotationPart() + # Lamps need to be rotated + if ob and ob.type =='Lamp': + matrix_rot = mtx_x90 * matrix_rot + rot = tuple(matrix_rot.toEuler()) + elif ob and ob.type =='Camera': + y = Vector(0,1,0) * matrix_rot + matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y) + rot = tuple(matrix_rot.toEuler()) + else: + rot = tuple(matrix_rot.toEuler()) + else: + if not loc: + loc = 0,0,0 + scale = 1,1,1 + rot = 0,0,0 + + return loc, rot, scale, matrix, matrix_rot + + def write_object_tx(ob, loc, matrix, matrix_mod= None): + ''' + We have loc to set the location if non blender objects that have a location + + matrix_mod is only used for bones at the moment + ''' + loc, rot, scale, matrix, matrix_rot = object_tx(ob, loc, matrix, matrix_mod) + + file.write('\n\t\t\tProperty: "Lcl Translation", "Lcl Translation", "A+",%.15f,%.15f,%.15f' % loc) + file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % rot) + file.write('\n\t\t\tProperty: "Lcl Scaling", "Lcl Scaling", "A+",%.15f,%.15f,%.15f' % scale) + return loc, rot, scale, matrix, matrix_rot + + def write_object_props(ob=None, loc=None, matrix=None, matrix_mod=None): + # if the type is 0 its an empty otherwise its a mesh + # only difference at the moment is one has a color + file.write(''' + Properties60: { + Property: "QuaternionInterpolate", "bool", "",0 + Property: "Visibility", "Visibility", "A+",1''') + + loc, rot, scale, matrix, matrix_rot = write_object_tx(ob, loc, matrix, matrix_mod) + + # Rotation order, note, for FBX files Iv loaded normal order is 1 + # setting to zero. + # eEULER_XYZ = 0 + # eEULER_XZY + # eEULER_YZX + # eEULER_YXZ + # eEULER_ZXY + # eEULER_ZYX + + file.write(''' + Property: "RotationOffset", "Vector3D", "",0,0,0 + Property: "RotationPivot", "Vector3D", "",0,0,0 + Property: "ScalingOffset", "Vector3D", "",0,0,0 + Property: "ScalingPivot", "Vector3D", "",0,0,0 + Property: "TranslationActive", "bool", "",0 + Property: "TranslationMin", "Vector3D", "",0,0,0 + Property: "TranslationMax", "Vector3D", "",0,0,0 + Property: "TranslationMinX", "bool", "",0 + Property: "TranslationMinY", "bool", "",0 + Property: "TranslationMinZ", "bool", "",0 + Property: "TranslationMaxX", "bool", "",0 + Property: "TranslationMaxY", "bool", "",0 + Property: "TranslationMaxZ", "bool", "",0 + Property: "RotationOrder", "enum", "",0 + Property: "RotationSpaceForLimitOnly", "bool", "",0 + Property: "AxisLen", "double", "",10 + Property: "PreRotation", "Vector3D", "",0,0,0 + Property: "PostRotation", "Vector3D", "",0,0,0 + Property: "RotationActive", "bool", "",0 + Property: "RotationMin", "Vector3D", "",0,0,0 + Property: "RotationMax", "Vector3D", "",0,0,0 + Property: "RotationMinX", "bool", "",0 + Property: "RotationMinY", "bool", "",0 + Property: "RotationMinZ", "bool", "",0 + Property: "RotationMaxX", "bool", "",0 + Property: "RotationMaxY", "bool", "",0 + Property: "RotationMaxZ", "bool", "",0 + Property: "RotationStiffnessX", "double", "",0 + Property: "RotationStiffnessY", "double", "",0 + Property: "RotationStiffnessZ", "double", "",0 + Property: "MinDampRangeX", "double", "",0 + Property: "MinDampRangeY", "double", "",0 + Property: "MinDampRangeZ", "double", "",0 + Property: "MaxDampRangeX", "double", "",0 + Property: "MaxDampRangeY", "double", "",0 + Property: "MaxDampRangeZ", "double", "",0 + Property: "MinDampStrengthX", "double", "",0 + Property: "MinDampStrengthY", "double", "",0 + Property: "MinDampStrengthZ", "double", "",0 + Property: "MaxDampStrengthX", "double", "",0 + Property: "MaxDampStrengthY", "double", "",0 + Property: "MaxDampStrengthZ", "double", "",0 + Property: "PreferedAngleX", "double", "",0 + Property: "PreferedAngleY", "double", "",0 + Property: "PreferedAngleZ", "double", "",0 + Property: "InheritType", "enum", "",0 + Property: "ScalingActive", "bool", "",0 + Property: "ScalingMin", "Vector3D", "",1,1,1 + Property: "ScalingMax", "Vector3D", "",1,1,1 + Property: "ScalingMinX", "bool", "",0 + Property: "ScalingMinY", "bool", "",0 + Property: "ScalingMinZ", "bool", "",0 + Property: "ScalingMaxX", "bool", "",0 + Property: "ScalingMaxY", "bool", "",0 + Property: "ScalingMaxZ", "bool", "",0 + Property: "GeometricTranslation", "Vector3D", "",0,0,0 + Property: "GeometricRotation", "Vector3D", "",0,0,0 + Property: "GeometricScaling", "Vector3D", "",1,1,1 + Property: "LookAtProperty", "object", "" + Property: "UpVectorProperty", "object", "" + Property: "Show", "bool", "",1 + Property: "NegativePercentShapeSupport", "bool", "",1 + Property: "DefaultAttributeIndex", "int", "",0''') + if ob and type(ob) != Blender.Types.BoneType: + # Only mesh objects have color + file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8') + file.write('\n\t\t\tProperty: "Size", "double", "",100') + file.write('\n\t\t\tProperty: "Look", "enum", "",1') + + return loc, rot, scale, matrix, matrix_rot + + + # -------------------------------------------- Armatures + #def write_bone(bone, name, matrix_mod): + def write_bone(my_bone): + file.write('\n\tModel: "Model::%s", "Limb" {' % my_bone.fbxName) + file.write('\n\t\tVersion: 232') + + #poseMatrix = write_object_props(my_bone.blenBone, None, None, my_bone.fbxArm.parRelMatrix())[3] + poseMatrix = write_object_props(my_bone.blenBone)[3] # dont apply bone matricies anymore + pose_items.append( (my_bone.fbxName, poseMatrix) ) + + + # file.write('\n\t\t\tProperty: "Size", "double", "",%.6f' % ((my_bone.blenData.head['ARMATURESPACE'] - my_bone.blenData.tail['ARMATURESPACE']) * my_bone.fbxArm.parRelMatrix()).length) + file.write('\n\t\t\tProperty: "Size", "double", "",1') + + #((my_bone.blenData.head['ARMATURESPACE'] * my_bone.fbxArm.matrixWorld) - (my_bone.blenData.tail['ARMATURESPACE'] * my_bone.fbxArm.parRelMatrix())).length) + + """ + file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %\ + ((my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']) * my_bone.fbxArm.parRelMatrix()).length) + """ + + file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %\ + (my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']).length) + + #file.write('\n\t\t\tProperty: "LimbLength", "double", "",1') + file.write('\n\t\t\tProperty: "Color", "ColorRGB", "",0.8,0.8,0.8') + file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8') + file.write('\n\t\t}') + file.write('\n\t\tMultiLayer: 0') + file.write('\n\t\tMultiTake: 1') + file.write('\n\t\tShading: Y') + file.write('\n\t\tCulling: "CullingOff"') + file.write('\n\t\tTypeFlags: "Skeleton"') + file.write('\n\t}') + + def write_camera_switch(): + file.write(''' + Model: "Model::Camera Switcher", "CameraSwitcher" { + Version: 232''') + + write_object_props() + file.write(''' + Property: "Color", "Color", "A",0.8,0.8,0.8 + Property: "Camera Index", "Integer", "A+",100 + } + MultiLayer: 0 + MultiTake: 1 + Hidden: "True" + Shading: W + Culling: "CullingOff" + Version: 101 + Name: "Model::Camera Switcher" + CameraId: 0 + CameraName: 100 + CameraIndexName: + }''') + + def write_camera_dummy(name, loc, near, far, proj_type, up): + file.write('\n\tModel: "Model::%s", "Camera" {' % name ) + file.write('\n\t\tVersion: 232') + write_object_props(None, loc) + + file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8') + file.write('\n\t\t\tProperty: "Roll", "Roll", "A+",0') + file.write('\n\t\t\tProperty: "FieldOfView", "FieldOfView", "A+",40') + file.write('\n\t\t\tProperty: "FieldOfViewX", "FieldOfView", "A+",1') + file.write('\n\t\t\tProperty: "FieldOfViewY", "FieldOfView", "A+",1') + file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",0') + file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",0') + file.write('\n\t\t\tProperty: "BackgroundColor", "Color", "A+",0.63,0.63,0.63') + file.write('\n\t\t\tProperty: "TurnTable", "Real", "A+",0') + file.write('\n\t\t\tProperty: "DisplayTurnTableIcon", "bool", "",1') + file.write('\n\t\t\tProperty: "Motion Blur Intensity", "Real", "A+",1') + file.write('\n\t\t\tProperty: "UseMotionBlur", "bool", "",0') + file.write('\n\t\t\tProperty: "UseRealTimeMotionBlur", "bool", "",1') + file.write('\n\t\t\tProperty: "ResolutionMode", "enum", "",0') + file.write('\n\t\t\tProperty: "ApertureMode", "enum", "",2') + file.write('\n\t\t\tProperty: "GateFit", "enum", "",0') + file.write('\n\t\t\tProperty: "FocalLength", "Real", "A+",21.3544940948486') + file.write('\n\t\t\tProperty: "CameraFormat", "enum", "",0') + file.write('\n\t\t\tProperty: "AspectW", "double", "",320') + file.write('\n\t\t\tProperty: "AspectH", "double", "",200') + file.write('\n\t\t\tProperty: "PixelAspectRatio", "double", "",1') + file.write('\n\t\t\tProperty: "UseFrameColor", "bool", "",0') + file.write('\n\t\t\tProperty: "FrameColor", "ColorRGB", "",0.3,0.3,0.3') + file.write('\n\t\t\tProperty: "ShowName", "bool", "",1') + file.write('\n\t\t\tProperty: "ShowGrid", "bool", "",1') + file.write('\n\t\t\tProperty: "ShowOpticalCenter", "bool", "",0') + file.write('\n\t\t\tProperty: "ShowAzimut", "bool", "",1') + file.write('\n\t\t\tProperty: "ShowTimeCode", "bool", "",0') + file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % near) + file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % far) + file.write('\n\t\t\tProperty: "FilmWidth", "double", "",0.816') + file.write('\n\t\t\tProperty: "FilmHeight", "double", "",0.612') + file.write('\n\t\t\tProperty: "FilmAspectRatio", "double", "",1.33333333333333') + file.write('\n\t\t\tProperty: "FilmSqueezeRatio", "double", "",1') + file.write('\n\t\t\tProperty: "FilmFormatIndex", "enum", "",4') + file.write('\n\t\t\tProperty: "ViewFrustum", "bool", "",1') + file.write('\n\t\t\tProperty: "ViewFrustumNearFarPlane", "bool", "",0') + file.write('\n\t\t\tProperty: "ViewFrustumBackPlaneMode", "enum", "",2') + file.write('\n\t\t\tProperty: "BackPlaneDistance", "double", "",100') + file.write('\n\t\t\tProperty: "BackPlaneDistanceMode", "enum", "",0') + file.write('\n\t\t\tProperty: "ViewCameraToLookAt", "bool", "",1') + file.write('\n\t\t\tProperty: "LockMode", "bool", "",0') + file.write('\n\t\t\tProperty: "LockInterestNavigation", "bool", "",0') + file.write('\n\t\t\tProperty: "FitImage", "bool", "",0') + file.write('\n\t\t\tProperty: "Crop", "bool", "",0') + file.write('\n\t\t\tProperty: "Center", "bool", "",1') + file.write('\n\t\t\tProperty: "KeepRatio", "bool", "",1') + file.write('\n\t\t\tProperty: "BackgroundMode", "enum", "",0') + file.write('\n\t\t\tProperty: "BackgroundAlphaTreshold", "double", "",0.5') + file.write('\n\t\t\tProperty: "ForegroundTransparent", "bool", "",1') + file.write('\n\t\t\tProperty: "DisplaySafeArea", "bool", "",0') + file.write('\n\t\t\tProperty: "SafeAreaDisplayStyle", "enum", "",1') + file.write('\n\t\t\tProperty: "SafeAreaAspectRatio", "double", "",1.33333333333333') + file.write('\n\t\t\tProperty: "Use2DMagnifierZoom", "bool", "",0') + file.write('\n\t\t\tProperty: "2D Magnifier Zoom", "Real", "A+",100') + file.write('\n\t\t\tProperty: "2D Magnifier X", "Real", "A+",50') + file.write('\n\t\t\tProperty: "2D Magnifier Y", "Real", "A+",50') + file.write('\n\t\t\tProperty: "CameraProjectionType", "enum", "",%i' % proj_type) + file.write('\n\t\t\tProperty: "UseRealTimeDOFAndAA", "bool", "",0') + file.write('\n\t\t\tProperty: "UseDepthOfField", "bool", "",0') + file.write('\n\t\t\tProperty: "FocusSource", "enum", "",0') + file.write('\n\t\t\tProperty: "FocusAngle", "double", "",3.5') + file.write('\n\t\t\tProperty: "FocusDistance", "double", "",200') + file.write('\n\t\t\tProperty: "UseAntialiasing", "bool", "",0') + file.write('\n\t\t\tProperty: "AntialiasingIntensity", "double", "",0.77777') + file.write('\n\t\t\tProperty: "UseAccumulationBuffer", "bool", "",0') + file.write('\n\t\t\tProperty: "FrameSamplingCount", "int", "",7') + file.write('\n\t\t}') + file.write('\n\t\tMultiLayer: 0') + file.write('\n\t\tMultiTake: 0') + file.write('\n\t\tHidden: "True"') + file.write('\n\t\tShading: Y') + file.write('\n\t\tCulling: "CullingOff"') + file.write('\n\t\tTypeFlags: "Camera"') + file.write('\n\t\tGeometryVersion: 124') + file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc) + file.write('\n\t\tUp: %i,%i,%i' % up) + file.write('\n\t\tLookAt: 0,0,0') + file.write('\n\t\tShowInfoOnMoving: 1') + file.write('\n\t\tShowAudio: 0') + file.write('\n\t\tAudioColor: 0,1,0') + file.write('\n\t\tCameraOrthoZoom: 1') + file.write('\n\t}') + + def write_camera_default(): + # This sucks but to match FBX converter its easier to + # write the cameras though they are not needed. + write_camera_dummy('Producer Perspective', (0,71.3,287.5), 10, 4000, 0, (0,1,0)) + write_camera_dummy('Producer Top', (0,4000,0), 1, 30000, 1, (0,0,-1)) + write_camera_dummy('Producer Bottom', (0,-4000,0), 1, 30000, 1, (0,0,-1)) + write_camera_dummy('Producer Front', (0,0,4000), 1, 30000, 1, (0,1,0)) + write_camera_dummy('Producer Back', (0,0,-4000), 1, 30000, 1, (0,1,0)) + write_camera_dummy('Producer Right', (4000,0,0), 1, 30000, 1, (0,1,0)) + write_camera_dummy('Producer Left', (-4000,0,0), 1, 30000, 1, (0,1,0)) + + def write_camera(my_cam): + ''' + Write a blender camera + ''' + render = sce.render + width = render.sizeX + height = render.sizeY + aspect = float(width)/height + + data = my_cam.blenObject.data + + file.write('\n\tModel: "Model::%s", "Camera" {' % my_cam.fbxName ) + file.write('\n\t\tVersion: 232') + loc, rot, scale, matrix, matrix_rot = write_object_props(my_cam.blenObject, None, my_cam.parRelMatrix()) + + file.write('\n\t\t\tProperty: "Roll", "Roll", "A+",0') + file.write('\n\t\t\tProperty: "FieldOfView", "FieldOfView", "A+",%.6f' % data.angle) + file.write('\n\t\t\tProperty: "FieldOfViewX", "FieldOfView", "A+",1') + file.write('\n\t\t\tProperty: "FieldOfViewY", "FieldOfView", "A+",1') + file.write('\n\t\t\tProperty: "FocalLength", "Real", "A+",14.0323972702026') + file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",%.6f' % data.shiftX) # not sure if this is in the correct units? + file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",%.6f' % data.shiftY) # ditto + file.write('\n\t\t\tProperty: "BackgroundColor", "Color", "A+",0,0,0') + file.write('\n\t\t\tProperty: "TurnTable", "Real", "A+",0') + file.write('\n\t\t\tProperty: "DisplayTurnTableIcon", "bool", "",1') + file.write('\n\t\t\tProperty: "Motion Blur Intensity", "Real", "A+",1') + file.write('\n\t\t\tProperty: "UseMotionBlur", "bool", "",0') + file.write('\n\t\t\tProperty: "UseRealTimeMotionBlur", "bool", "",1') + file.write('\n\t\t\tProperty: "ResolutionMode", "enum", "",0') + file.write('\n\t\t\tProperty: "ApertureMode", "enum", "",2') + file.write('\n\t\t\tProperty: "GateFit", "enum", "",0') + file.write('\n\t\t\tProperty: "CameraFormat", "enum", "",0') + file.write('\n\t\t\tProperty: "AspectW", "double", "",%i' % width) + file.write('\n\t\t\tProperty: "AspectH", "double", "",%i' % height) + + '''Camera aspect ratio modes. + 0 If the ratio mode is eWINDOW_SIZE, both width and height values aren't relevant. + 1 If the ratio mode is eFIXED_RATIO, the height value is set to 1.0 and the width value is relative to the height value. + 2 If the ratio mode is eFIXED_RESOLUTION, both width and height values are in pixels. + 3 If the ratio mode is eFIXED_WIDTH, the width value is in pixels and the height value is relative to the width value. + 4 If the ratio mode is eFIXED_HEIGHT, the height value is in pixels and the width value is relative to the height value. + + Definition at line 234 of file kfbxcamera.h. ''' + + file.write('\n\t\t\tProperty: "PixelAspectRatio", "double", "",2') + + file.write('\n\t\t\tProperty: "UseFrameColor", "bool", "",0') + file.write('\n\t\t\tProperty: "FrameColor", "ColorRGB", "",0.3,0.3,0.3') + file.write('\n\t\t\tProperty: "ShowName", "bool", "",1') + file.write('\n\t\t\tProperty: "ShowGrid", "bool", "",1') + file.write('\n\t\t\tProperty: "ShowOpticalCenter", "bool", "",0') + file.write('\n\t\t\tProperty: "ShowAzimut", "bool", "",1') + file.write('\n\t\t\tProperty: "ShowTimeCode", "bool", "",0') + file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % data.clipStart) + file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % data.clipStart) + file.write('\n\t\t\tProperty: "FilmWidth", "double", "",1.0') + file.write('\n\t\t\tProperty: "FilmHeight", "double", "",1.0') + file.write('\n\t\t\tProperty: "FilmAspectRatio", "double", "",%.6f' % aspect) + file.write('\n\t\t\tProperty: "FilmSqueezeRatio", "double", "",1') + file.write('\n\t\t\tProperty: "FilmFormatIndex", "enum", "",0') + file.write('\n\t\t\tProperty: "ViewFrustum", "bool", "",1') + file.write('\n\t\t\tProperty: "ViewFrustumNearFarPlane", "bool", "",0') + file.write('\n\t\t\tProperty: "ViewFrustumBackPlaneMode", "enum", "",2') + file.write('\n\t\t\tProperty: "BackPlaneDistance", "double", "",100') + file.write('\n\t\t\tProperty: "BackPlaneDistanceMode", "enum", "",0') + file.write('\n\t\t\tProperty: "ViewCameraToLookAt", "bool", "",1') + file.write('\n\t\t\tProperty: "LockMode", "bool", "",0') + file.write('\n\t\t\tProperty: "LockInterestNavigation", "bool", "",0') + file.write('\n\t\t\tProperty: "FitImage", "bool", "",0') + file.write('\n\t\t\tProperty: "Crop", "bool", "",0') + file.write('\n\t\t\tProperty: "Center", "bool", "",1') + file.write('\n\t\t\tProperty: "KeepRatio", "bool", "",1') + file.write('\n\t\t\tProperty: "BackgroundMode", "enum", "",0') + file.write('\n\t\t\tProperty: "BackgroundAlphaTreshold", "double", "",0.5') + file.write('\n\t\t\tProperty: "ForegroundTransparent", "bool", "",1') + file.write('\n\t\t\tProperty: "DisplaySafeArea", "bool", "",0') + file.write('\n\t\t\tProperty: "SafeAreaDisplayStyle", "enum", "",1') + file.write('\n\t\t\tProperty: "SafeAreaAspectRatio", "double", "",%.6f' % aspect) + file.write('\n\t\t\tProperty: "Use2DMagnifierZoom", "bool", "",0') + file.write('\n\t\t\tProperty: "2D Magnifier Zoom", "Real", "A+",100') + file.write('\n\t\t\tProperty: "2D Magnifier X", "Real", "A+",50') + file.write('\n\t\t\tProperty: "2D Magnifier Y", "Real", "A+",50') + file.write('\n\t\t\tProperty: "CameraProjectionType", "enum", "",0') + file.write('\n\t\t\tProperty: "UseRealTimeDOFAndAA", "bool", "",0') + file.write('\n\t\t\tProperty: "UseDepthOfField", "bool", "",0') + file.write('\n\t\t\tProperty: "FocusSource", "enum", "",0') + file.write('\n\t\t\tProperty: "FocusAngle", "double", "",3.5') + file.write('\n\t\t\tProperty: "FocusDistance", "double", "",200') + file.write('\n\t\t\tProperty: "UseAntialiasing", "bool", "",0') + file.write('\n\t\t\tProperty: "AntialiasingIntensity", "double", "",0.77777') + file.write('\n\t\t\tProperty: "UseAccumulationBuffer", "bool", "",0') + file.write('\n\t\t\tProperty: "FrameSamplingCount", "int", "",7') + + file.write('\n\t\t}') + file.write('\n\t\tMultiLayer: 0') + file.write('\n\t\tMultiTake: 0') + file.write('\n\t\tShading: Y') + file.write('\n\t\tCulling: "CullingOff"') + file.write('\n\t\tTypeFlags: "Camera"') + file.write('\n\t\tGeometryVersion: 124') + file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc) + file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(Vector(0,1,0) * matrix_rot) ) + file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Vector(0,0,-1)*matrix_rot) ) + + #file.write('\n\t\tUp: 0,0,0' ) + #file.write('\n\t\tLookAt: 0,0,0' ) + + file.write('\n\t\tShowInfoOnMoving: 1') + file.write('\n\t\tShowAudio: 0') + file.write('\n\t\tAudioColor: 0,1,0') + file.write('\n\t\tCameraOrthoZoom: 1') + file.write('\n\t}') + + def write_light(my_light): + light = my_light.blenObject.data + file.write('\n\tModel: "Model::%s", "Light" {' % my_light.fbxName) + file.write('\n\t\tVersion: 232') + + write_object_props(my_light.blenObject, None, my_light.parRelMatrix()) + + # Why are these values here twice?????? - oh well, follow the holy sdk's output + + # Blender light types match FBX's, funny coincidence, we just need to + # be sure that all unsupported types are made into a point light + #ePOINT, + #eDIRECTIONAL + #eSPOT + light_type = light.type + if light_type > 2: light_type = 1 # hemi and area lights become directional + + mode = light.mode + if mode & Blender.Lamp.Modes.RayShadow or mode & Blender.Lamp.Modes.Shadows: + do_shadow = 1 + else: + do_shadow = 0 + + if mode & Blender.Lamp.Modes.OnlyShadow or (mode & Blender.Lamp.Modes.NoDiffuse and mode & Blender.Lamp.Modes.NoSpecular): + do_light = 0 + else: + do_light = 1 + + scale = abs(GLOBAL_MATRIX.scalePart()[0]) # scale is always uniform in this case + + file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type) + file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",1') + file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1') + file.write('\n\t\t\tProperty: "DrawGroundProjection", "bool", "",1') + file.write('\n\t\t\tProperty: "DrawFrontFacingVolumetricLight", "bool", "",0') + file.write('\n\t\t\tProperty: "GoboProperty", "object", ""') + file.write('\n\t\t\tProperty: "Color", "Color", "A+",1,1,1') + file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy*100, 200))) # clamp below 200 + file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale)) + file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50') + file.write('\n\t\t\tProperty: "Color", "Color", "A",%.2f,%.2f,%.2f' % tuple(light.col)) + file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy*100, 200))) # clamp below 200 + file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale)) + file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50') + file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type) + file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",%i' % do_light) + file.write('\n\t\t\tProperty: "DrawGroundProjection", "bool", "",1') + file.write('\n\t\t\tProperty: "DrawFrontFacingVolumetricLight", "bool", "",0') + file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1') + file.write('\n\t\t\tProperty: "GoboProperty", "object", ""') + file.write('\n\t\t\tProperty: "DecayType", "enum", "",0') + file.write('\n\t\t\tProperty: "DecayStart", "double", "",%.2f' % light.dist) + file.write('\n\t\t\tProperty: "EnableNearAttenuation", "bool", "",0') + file.write('\n\t\t\tProperty: "NearAttenuationStart", "double", "",0') + file.write('\n\t\t\tProperty: "NearAttenuationEnd", "double", "",0') + file.write('\n\t\t\tProperty: "EnableFarAttenuation", "bool", "",0') + file.write('\n\t\t\tProperty: "FarAttenuationStart", "double", "",0') + file.write('\n\t\t\tProperty: "FarAttenuationEnd", "double", "",0') + file.write('\n\t\t\tProperty: "CastShadows", "bool", "",%i' % do_shadow) + file.write('\n\t\t\tProperty: "ShadowColor", "ColorRGBA", "",0,0,0,1') + file.write('\n\t\t}') + file.write('\n\t\tMultiLayer: 0') + file.write('\n\t\tMultiTake: 0') + file.write('\n\t\tShading: Y') + file.write('\n\t\tCulling: "CullingOff"') + file.write('\n\t\tTypeFlags: "Light"') + file.write('\n\t\tGeometryVersion: 124') + file.write('\n\t}') + + # matrixOnly is not used at the moment + def write_null(my_null = None, fbxName = None, matrixOnly = None): + # ob can be null + if not fbxName: fbxName = my_null.fbxName + + file.write('\n\tModel: "Model::%s", "Null" {' % fbxName) + file.write('\n\t\tVersion: 232') + + # only use this for the root matrix at the moment + if matrixOnly: + poseMatrix = write_object_props(None, None, matrixOnly)[3] + + else: # all other Null's + if my_null: poseMatrix = write_object_props(my_null.blenObject, None, my_null.parRelMatrix())[3] + else: poseMatrix = write_object_props()[3] + + pose_items.append((fbxName, poseMatrix)) + + file.write(''' + } + MultiLayer: 0 + MultiTake: 1 + Shading: Y + Culling: "CullingOff" + TypeFlags: "Null" + }''') + + # Material Settings + if world: world_amb = world.getAmb() + else: world_amb = (0,0,0) # Default value + + def write_material(matname, mat): + file.write('\n\tMaterial: "Material::%s", "" {' % matname) + + # Todo, add more material Properties. + if mat: + mat_cold = tuple(mat.rgbCol) + mat_cols = tuple(mat.specCol) + #mat_colm = tuple(mat.mirCol) # we wont use the mirror color + mat_colamb = tuple([c for c in world_amb]) + + mat_dif = mat.ref + mat_amb = mat.amb + mat_hard = (float(mat.hard)-1)/5.10 + mat_spec = mat.spec/2.0 + mat_alpha = mat.alpha + mat_emit = mat.emit + mat_shadeless = mat.mode & Blender.Material.Modes.SHADELESS + if mat_shadeless: + mat_shader = 'Lambert' + else: + if mat.diffuseShader == Blender.Material.Shaders.DIFFUSE_LAMBERT: + mat_shader = 'Lambert' + else: + mat_shader = 'Phong' + else: + mat_cols = mat_cold = 0.8, 0.8, 0.8 + mat_colamb = 0.0,0.0,0.0 + # mat_colm + mat_dif = 1.0 + mat_amb = 0.5 + mat_hard = 20.0 + mat_spec = 0.2 + mat_alpha = 1.0 + mat_emit = 0.0 + mat_shadeless = False + mat_shader = 'Phong' + + file.write('\n\t\tVersion: 102') + file.write('\n\t\tShadingModel: "%s"' % mat_shader.lower()) + file.write('\n\t\tMultiLayer: 0') + + file.write('\n\t\tProperties60: {') + file.write('\n\t\t\tProperty: "ShadingModel", "KString", "", "%s"' % mat_shader) + file.write('\n\t\t\tProperty: "MultiLayer", "bool", "",0') + file.write('\n\t\t\tProperty: "EmissiveColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cold) # emit and diffuse color are he same in blender + file.write('\n\t\t\tProperty: "EmissiveFactor", "double", "",%.4f' % mat_emit) + + file.write('\n\t\t\tProperty: "AmbientColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_colamb) + file.write('\n\t\t\tProperty: "AmbientFactor", "double", "",%.4f' % mat_amb) + file.write('\n\t\t\tProperty: "DiffuseColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cold) + file.write('\n\t\t\tProperty: "DiffuseFactor", "double", "",%.4f' % mat_dif) + file.write('\n\t\t\tProperty: "Bump", "Vector3D", "",0,0,0') + file.write('\n\t\t\tProperty: "TransparentColor", "ColorRGB", "",1,1,1') + file.write('\n\t\t\tProperty: "TransparencyFactor", "double", "",%.4f' % (1.0 - mat_alpha)) + if not mat_shadeless: + file.write('\n\t\t\tProperty: "SpecularColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cols) + file.write('\n\t\t\tProperty: "SpecularFactor", "double", "",%.4f' % mat_spec) + file.write('\n\t\t\tProperty: "ShininessExponent", "double", "",80.0') + file.write('\n\t\t\tProperty: "ReflectionColor", "ColorRGB", "",0,0,0') + file.write('\n\t\t\tProperty: "ReflectionFactor", "double", "",1') + file.write('\n\t\t\tProperty: "Emissive", "ColorRGB", "",0,0,0') + file.write('\n\t\t\tProperty: "Ambient", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_colamb) + file.write('\n\t\t\tProperty: "Diffuse", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cold) + if not mat_shadeless: + file.write('\n\t\t\tProperty: "Specular", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cols) + file.write('\n\t\t\tProperty: "Shininess", "double", "",%.1f' % mat_hard) + file.write('\n\t\t\tProperty: "Opacity", "double", "",%.1f' % mat_alpha) + if not mat_shadeless: + file.write('\n\t\t\tProperty: "Reflectivity", "double", "",0') + + file.write('\n\t\t}') + file.write('\n\t}') + + def write_video(texname, tex): + # Same as texture really! + file.write('\n\tVideo: "Video::%s", "Clip" {' % texname) + + file.write(''' + Type: "Clip" + Properties60: { + Property: "FrameRate", "double", "",0 + Property: "LastFrame", "int", "",0 + Property: "Width", "int", "",0 + Property: "Height", "int", "",0''') + if tex: + fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) + else: + fname = fname_strip = fname_rel = '' + + file.write('\n\t\t\tProperty: "Path", "charptr", "", "%s"' % fname_strip) + + + file.write(''' + Property: "StartFrame", "int", "",0 + Property: "StopFrame", "int", "",0 + Property: "PlaySpeed", "double", "",1 + Property: "Offset", "KTime", "",0 + Property: "InterlaceMode", "enum", "",0 + Property: "FreeRunning", "bool", "",0 + Property: "Loop", "bool", "",0 + Property: "AccessMode", "enum", "",0 + } + UseMipMap: 0''') + + file.write('\n\t\tFilename: "%s"' % fname_strip) + if fname_strip: fname_strip = '/' + fname_strip + file.write('\n\t\tRelativeFilename: "%s"' % fname_rel) # make relative + file.write('\n\t}') + + + def write_texture(texname, tex, num): + # if tex == None then this is a dummy tex + file.write('\n\tTexture: "Texture::%s", "TextureVideoClip" {' % texname) + file.write('\n\t\tType: "TextureVideoClip"') + file.write('\n\t\tVersion: 202') + # TODO, rare case _empty_ exists as a name. + file.write('\n\t\tTextureName: "Texture::%s"' % texname) + + file.write(''' + Properties60: { + Property: "Translation", "Vector", "A+",0,0,0 + Property: "Rotation", "Vector", "A+",0,0,0 + Property: "Scaling", "Vector", "A+",1,1,1''') + file.write('\n\t\t\tProperty: "Texture alpha", "Number", "A+",%i' % num) + + + # WrapModeU/V 0==rep, 1==clamp, TODO add support + file.write(''' + Property: "TextureTypeUse", "enum", "",0 + Property: "CurrentTextureBlendMode", "enum", "",1 + Property: "UseMaterial", "bool", "",0 + Property: "UseMipMap", "bool", "",0 + Property: "CurrentMappingType", "enum", "",0 + Property: "UVSwap", "bool", "",0''') + + file.write('\n\t\t\tProperty: "WrapModeU", "enum", "",%i' % tex.clampX) + file.write('\n\t\t\tProperty: "WrapModeV", "enum", "",%i' % tex.clampY) + + file.write(''' + Property: "TextureRotationPivot", "Vector3D", "",0,0,0 + Property: "TextureScalingPivot", "Vector3D", "",0,0,0 + Property: "VideoProperty", "object", "" + }''') + + file.write('\n\t\tMedia: "Video::%s"' % texname) + + if tex: + fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) + else: + fname = fname_strip = fname_rel = '' + + file.write('\n\t\tFileName: "%s"' % fname_strip) + file.write('\n\t\tRelativeFilename: "%s"' % fname_rel) # need some make relative command + + file.write(''' + ModelUVTranslation: 0,0 + ModelUVScaling: 1,1 + Texture_Alpha_Source: "None" + Cropping: 0,0,0,0 + }''') + + def write_deformer_skin(obname): + ''' + Each mesh has its own deformer + ''' + file.write('\n\tDeformer: "Deformer::Skin %s", "Skin" {' % obname) + file.write(''' + Version: 100 + MultiLayer: 0 + Type: "Skin" + Properties60: { + } + Link_DeformAcuracy: 50 + }''') + + # in the example was 'Bip01 L Thigh_2' + def write_sub_deformer_skin(my_mesh, my_bone, weights): + + ''' + Each subdeformer is spesific to a mesh, but the bone it links to can be used by many sub-deformers + So the SubDeformer needs the mesh-object name as a prefix to make it unique + + Its possible that there is no matching vgroup in this mesh, in that case no verts are in the subdeformer, + a but silly but dosnt really matter + ''' + file.write('\n\tDeformer: "SubDeformer::Cluster %s %s", "Cluster" {' % (my_mesh.fbxName, my_bone.fbxName)) + + file.write(''' + Version: 100 + MultiLayer: 0 + Type: "Cluster" + Properties60: { + Property: "SrcModel", "object", "" + Property: "SrcModelReference", "object", "" + } + UserData: "", ""''') + + # Support for bone parents + if my_mesh.fbxBoneParent: + if my_mesh.fbxBoneParent == my_bone: + # TODO - this is a bit lazy, we could have a simple write loop + # for this case because all weights are 1.0 but for now this is ok + # Parent Bones arent used all that much anyway. + vgroup_data = [(j, 1.0) for j in xrange(len(my_mesh.blenData.verts))] + else: + # This bone is not a parent of this mesh object, no weights + vgroup_data = [] + + else: + # Normal weight painted mesh + if my_bone.blenName in weights[0]: + # Before we used normalized wright list + #vgroup_data = me.getVertsFromGroup(bone.name, 1) + group_index = weights[0].index(my_bone.blenName) + vgroup_data = [(j, weight[group_index]) for j, weight in enumerate(weights[1]) if weight[group_index]] + else: + vgroup_data = [] + + file.write('\n\t\tIndexes: ') + + i = -1 + for vg in vgroup_data: + if i == -1: + file.write('%i' % vg[0]) + i=0 + else: + if i==23: + file.write('\n\t\t') + i=0 + file.write(',%i' % vg[0]) + i+=1 + + file.write('\n\t\tWeights: ') + i = -1 + for vg in vgroup_data: + if i == -1: + file.write('%.8f' % vg[1]) + i=0 + else: + if i==38: + file.write('\n\t\t') + i=0 + file.write(',%.8f' % vg[1]) + i+=1 + + if my_mesh.fbxParent: + # TODO FIXME, this case is broken in some cases. skinned meshes just shouldnt have parents where possible! + m = mtx4_z90 * (my_bone.restMatrix * my_bone.fbxArm.matrixWorld.copy() * my_mesh.matrixWorld.copy().invert() ) + else: + # Yes! this is it... - but dosnt work when the mesh is a. + m = mtx4_z90 * (my_bone.restMatrix * my_bone.fbxArm.matrixWorld.copy() * my_mesh.matrixWorld.copy().invert() ) + + #m = mtx4_z90 * my_bone.restMatrix + matstr = mat4x4str(m) + matstr_i = mat4x4str(m.invert()) + + file.write('\n\t\tTransform: %s' % matstr_i) # THIS IS __NOT__ THE GLOBAL MATRIX AS DOCUMENTED :/ + file.write('\n\t\tTransformLink: %s' % matstr) + file.write('\n\t}') + + def write_mesh(my_mesh): + + me = my_mesh.blenData + + # if there are non NULL materials on this mesh + if my_mesh.blenMaterials: do_materials = True + else: do_materials = False + + if my_mesh.blenTextures: do_textures = True + else: do_textures = False + + do_uvs = me.faceUV + + + file.write('\n\tModel: "Model::%s", "Mesh" {' % my_mesh.fbxName) + file.write('\n\t\tVersion: 232') # newline is added in write_object_props + + poseMatrix = write_object_props(my_mesh.blenObject, None, my_mesh.parRelMatrix())[3] + pose_items.append((my_mesh.fbxName, poseMatrix)) + + file.write('\n\t\t}') + file.write('\n\t\tMultiLayer: 0') + file.write('\n\t\tMultiTake: 1') + file.write('\n\t\tShading: Y') + file.write('\n\t\tCulling: "CullingOff"') + + + # Write the Real Mesh data here + file.write('\n\t\tVertices: ') + i=-1 + + for v in me.verts: + if i==-1: + file.write('%.6f,%.6f,%.6f' % tuple(v.co)); i=0 + else: + if i==7: + file.write('\n\t\t'); i=0 + file.write(',%.6f,%.6f,%.6f'% tuple(v.co)) + i+=1 + + file.write('\n\t\tPolygonVertexIndex: ') + i=-1 + for f in me.faces: + fi = [v.index for v in f] + # flip the last index, odd but it looks like + # this is how fbx tells one face from another + fi[-1] = -(fi[-1]+1) + fi = tuple(fi) + if i==-1: + if len(f) == 3: file.write('%i,%i,%i' % fi ) + else: file.write('%i,%i,%i,%i' % fi ) + i=0 + else: + if i==13: + file.write('\n\t\t') + i=0 + if len(f) == 3: file.write(',%i,%i,%i' % fi ) + else: file.write(',%i,%i,%i,%i' % fi ) + i+=1 + + file.write('\n\t\tEdges: ') + i=-1 + for ed in me.edges: + if i==-1: + file.write('%i,%i' % (ed.v1.index, ed.v2.index)) + i=0 + else: + if i==13: + file.write('\n\t\t') + i=0 + file.write(',%i,%i' % (ed.v1.index, ed.v2.index)) + i+=1 + + file.write('\n\t\tGeometryVersion: 124') + + file.write(''' + LayerElementNormal: 0 { + Version: 101 + Name: "" + MappingInformationType: "ByVertice" + ReferenceInformationType: "Direct" + Normals: ''') + + i=-1 + for v in me.verts: + if i==-1: + file.write('%.15f,%.15f,%.15f' % tuple(v.no)); i=0 + else: + if i==2: + file.write('\n '); i=0 + file.write(',%.15f,%.15f,%.15f' % tuple(v.no)) + i+=1 + file.write('\n\t\t}') + + # Write Face Smoothing + file.write(''' + LayerElementSmoothing: 0 { + Version: 102 + Name: "" + MappingInformationType: "ByPolygon" + ReferenceInformationType: "Direct" + Smoothing: ''') + + i=-1 + for f in me.faces: + if i==-1: + file.write('%i' % f.smooth); i=0 + else: + if i==54: + file.write('\n '); i=0 + file.write(',%i' % f.smooth) + i+=1 + + file.write('\n\t\t}') + + # Write Edge Smoothing + file.write(''' + LayerElementSmoothing: 0 { + Version: 101 + Name: "" + MappingInformationType: "ByEdge" + ReferenceInformationType: "Direct" + Smoothing: ''') + + SHARP = Blender.Mesh.EdgeFlags.SHARP + i=-1 + for ed in me.edges: + if i==-1: + file.write('%i' % ((ed.flag&SHARP)!=0)); i=0 + else: + if i==54: + file.write('\n '); i=0 + file.write(',%i' % ((ed.flag&SHARP)!=0)) + i+=1 + + file.write('\n\t\t}') + del SHARP + + + # Write VertexColor Layers + # note, no programs seem to use this info :/ + collayers = [] + if me.vertexColors: + collayers = me.getColorLayerNames() + collayer_orig = me.activeColorLayer + for colindex, collayer in enumerate(collayers): + me.activeColorLayer = collayer + file.write('\n\t\tLayerElementColor: %i {' % colindex) + file.write('\n\t\t\tVersion: 101') + file.write('\n\t\t\tName: "%s"' % collayer) + + file.write(''' + MappingInformationType: "ByPolygonVertex" + ReferenceInformationType: "IndexToDirect" + Colors: ''') + + i = -1 + ii = 0 # Count how many Colors we write + + for f in me.faces: + for col in f.col: + if i==-1: + file.write('%.4f,%.4f,%.4f,1' % (col[0]/255.0, col[1]/255.0, col[2]/255.0)) + i=0 + else: + if i==7: + file.write('\n\t\t\t\t') + i=0 + file.write(',%.4f,%.4f,%.4f,1' % (col[0]/255.0, col[1]/255.0, col[2]/255.0)) + i+=1 + ii+=1 # One more Color + + file.write('\n\t\t\tColorIndex: ') + i = -1 + for j in xrange(ii): + if i == -1: + file.write('%i' % j) + i=0 + else: + if i==55: + file.write('\n\t\t\t\t') + i=0 + file.write(',%i' % j) + i+=1 + + file.write('\n\t\t}') + + + + # Write UV and texture layers. + uvlayers = [] + if do_uvs: + uvlayers = me.getUVLayerNames() + uvlayer_orig = me.activeUVLayer + for uvindex, uvlayer in enumerate(uvlayers): + me.activeUVLayer = uvlayer + file.write('\n\t\tLayerElementUV: %i {' % uvindex) + file.write('\n\t\t\tVersion: 101') + file.write('\n\t\t\tName: "%s"' % uvlayer) + + file.write(''' + MappingInformationType: "ByPolygonVertex" + ReferenceInformationType: "IndexToDirect" + UV: ''') + + i = -1 + ii = 0 # Count how many UVs we write + + for f in me.faces: + for uv in f.uv: + if i==-1: + file.write('%.6f,%.6f' % tuple(uv)) + i=0 + else: + if i==7: + file.write('\n ') + i=0 + file.write(',%.6f,%.6f' % tuple(uv)) + i+=1 + ii+=1 # One more UV + + file.write('\n\t\t\tUVIndex: ') + i = -1 + for j in xrange(ii): + if i == -1: + file.write('%i' % j) + i=0 + else: + if i==55: + file.write('\n\t\t\t\t') + i=0 + file.write(',%i' % j) + i+=1 + + file.write('\n\t\t}') + + if do_textures: + file.write('\n\t\tLayerElementTexture: %i {' % uvindex) + file.write('\n\t\t\tVersion: 101') + file.write('\n\t\t\tName: "%s"' % uvlayer) + + if len(my_mesh.blenTextures) == 1: + file.write('\n\t\t\tMappingInformationType: "AllSame"') + else: + file.write('\n\t\t\tMappingInformationType: "ByPolygon"') + + file.write('\n\t\t\tReferenceInformationType: "IndexToDirect"') + file.write('\n\t\t\tBlendMode: "Translucent"') + file.write('\n\t\t\tTextureAlpha: 1') + file.write('\n\t\t\tTextureId: ') + + if len(my_mesh.blenTextures) == 1: + file.write('0') + else: + texture_mapping_local = {None:-1} + + i = 0 # 1 for dummy + for tex in my_mesh.blenTextures: + if tex: # None is set above + texture_mapping_local[tex] = i + i+=1 + + i=-1 + for f in me.faces: + img_key = f.image + + if i==-1: + i=0 + file.write( '%s' % texture_mapping_local[img_key]) + else: + if i==55: + file.write('\n ') + i=0 + + file.write(',%s' % texture_mapping_local[img_key]) + i+=1 + + else: + file.write(''' + LayerElementTexture: 0 { + Version: 101 + Name: "" + MappingInformationType: "NoMappingInformation" + ReferenceInformationType: "IndexToDirect" + BlendMode: "Translucent" + TextureAlpha: 1 + TextureId: ''') + file.write('\n\t\t}') + + me.activeUVLayer = uvlayer_orig + + # Done with UV/textures. + + if do_materials: + file.write('\n\t\tLayerElementMaterial: 0 {') + file.write('\n\t\t\tVersion: 101') + file.write('\n\t\t\tName: ""') + + if len(my_mesh.blenMaterials) == 1: + file.write('\n\t\t\tMappingInformationType: "AllSame"') + else: + file.write('\n\t\t\tMappingInformationType: "ByPolygon"') + + file.write('\n\t\t\tReferenceInformationType: "IndexToDirect"') + file.write('\n\t\t\tMaterials: ') + + if len(my_mesh.blenMaterials) == 1: + file.write('0') + else: + # Build a material mapping for this + material_mapping_local = {} # local-mat & tex : global index. + + for j, mat_tex_pair in enumerate(my_mesh.blenMaterials): + material_mapping_local[mat_tex_pair] = j + + len_material_mapping_local = len(material_mapping_local) + + mats = my_mesh.blenMaterialList + + i=-1 + for f in me.faces: + try: mat = mats[f.mat] + except:mat = None + + if do_uvs: tex = f.image # WARNING - MULTI UV LAYER IMAGES NOT SUPPORTED :/ + else: tex = None + + if i==-1: + i=0 + file.write( '%s' % (material_mapping_local[mat, tex])) # None for mat or tex is ok + else: + if i==55: + file.write('\n\t\t\t\t') + i=0 + + file.write(',%s' % (material_mapping_local[mat, tex])) + i+=1 + + file.write('\n\t\t}') + + file.write(''' + Layer: 0 { + Version: 100 + LayerElement: { + Type: "LayerElementNormal" + TypedIndex: 0 + }''') + + if do_materials: + file.write(''' + LayerElement: { + Type: "LayerElementMaterial" + TypedIndex: 0 + }''') + + # Always write this + if do_textures: + file.write(''' + LayerElement: { + Type: "LayerElementTexture" + TypedIndex: 0 + }''') + + if me.vertexColors: + file.write(''' + LayerElement: { + Type: "LayerElementColor" + TypedIndex: 0 + }''') + + if do_uvs: # same as me.faceUV + file.write(''' + LayerElement: { + Type: "LayerElementUV" + TypedIndex: 0 + }''') + + + file.write('\n\t\t}') + + if len(uvlayers) > 1: + for i in xrange(1, len(uvlayers)): + + file.write('\n\t\tLayer: %i {' % i) + file.write('\n\t\t\tVersion: 100') + + file.write(''' + LayerElement: { + Type: "LayerElementUV"''') + + file.write('\n\t\t\t\tTypedIndex: %i' % i) + file.write('\n\t\t\t}') + + if do_textures: + + file.write(''' + LayerElement: { + Type: "LayerElementTexture"''') + + file.write('\n\t\t\t\tTypedIndex: %i' % i) + file.write('\n\t\t\t}') + + file.write('\n\t\t}') + + if len(collayers) > 1: + # Take into account any UV layers + layer_offset = 0 + if uvlayers: layer_offset = len(uvlayers)-1 + + for i in xrange(layer_offset, len(collayers)+layer_offset): + file.write('\n\t\tLayer: %i {' % i) + file.write('\n\t\t\tVersion: 100') + + file.write(''' + LayerElement: { + Type: "LayerElementColor"''') + + file.write('\n\t\t\t\tTypedIndex: %i' % i) + file.write('\n\t\t\t}') + file.write('\n\t\t}') + file.write('\n\t}') + + def write_group(name): + file.write('\n\tGroupSelection: "GroupSelection::%s", "Default" {' % name) + + file.write(''' + Properties60: { + Property: "MultiLayer", "bool", "",0 + Property: "Pickable", "bool", "",1 + Property: "Transformable", "bool", "",1 + Property: "Show", "bool", "",1 + } + MultiLayer: 0 + }''') + + + # add meshes here to clear because they are not used anywhere. + meshes_to_clear = [] + + ob_meshes = [] + ob_lights = [] + ob_cameras = [] + # in fbx we export bones as children of the mesh + # armatures not a part of a mesh, will be added to ob_arms + ob_bones = [] + ob_arms = [] + ob_null = [] # emptys + + # List of types that have blender objects (not bones) + ob_all_typegroups = [ob_meshes, ob_lights, ob_cameras, ob_arms, ob_null] + + groups = [] # blender groups, only add ones that have objects in the selections + materials = {} # (mat, image) keys, should be a set() + textures = {} # should be a set() + + tmp_ob_type = ob_type = None # incase no objects are exported, so as not to raise an error + + # if EXP_OBS_SELECTED is false, use sceens objects + if not batch_objects: + if EXP_OBS_SELECTED: tmp_objects = sce.objects.context + else: tmp_objects = sce.objects + else: + tmp_objects = batch_objects + + if EXP_ARMATURE: + # This is needed so applying modifiers dosnt apply the armature deformation, its also needed + # ...so mesh objects return their rest worldspace matrix when bone-parents are exported as weighted meshes. + # set every armature to its rest, backup the original values so we done mess up the scene + ob_arms_orig_rest = [arm.restPosition for arm in bpy.data.armatures] + + for arm in bpy.data.armatures: + arm.restPosition = True + + if ob_arms_orig_rest: + for ob_base in bpy.data.objects: + #if ob_base.type == 'Armature': + ob_base.makeDisplayList() + + # This causes the makeDisplayList command to effect the mesh + Blender.Set('curframe', Blender.Get('curframe')) + + + for ob_base in tmp_objects: + for ob, mtx in BPyObject.getDerivedObjects(ob_base): + #for ob in [ob_base,]: + tmp_ob_type = ob.type + if tmp_ob_type == 'Camera': + if EXP_CAMERA: + ob_cameras.append(my_object_generic(ob, mtx)) + elif tmp_ob_type == 'Lamp': + if EXP_LAMP: + ob_lights.append(my_object_generic(ob, mtx)) + elif tmp_ob_type == 'Armature': + if EXP_ARMATURE: + # TODO - armatures dont work in dupligroups! + if ob not in ob_arms: ob_arms.append(ob) + # ob_arms.append(ob) # replace later. was "ob_arms.append(sane_obname(ob), ob)" + elif tmp_ob_type == 'Empty': + if EXP_EMPTY: + ob_null.append(my_object_generic(ob, mtx)) + elif EXP_MESH: + origData = True + if tmp_ob_type != 'Mesh': + me = bpy.data.meshes.new() + try: me.getFromObject(ob) + except: me = None + if me: + meshes_to_clear.append( me ) + mats = me.materials + origData = False + else: + # Mesh Type! + if EXP_MESH_APPLY_MOD: + me = bpy.data.meshes.new() + me.getFromObject(ob) + + # so we keep the vert groups + if EXP_ARMATURE: + orig_mesh = ob.getData(mesh=1) + if orig_mesh.getVertGroupNames(): + ob.copy().link(me) + # If new mesh has no vgroups we can try add if verts are teh same + if not me.getVertGroupNames(): # vgroups were not kept by the modifier + if len(me.verts) == len(orig_mesh.verts): + groupNames, vWeightDict = BPyMesh.meshWeight2Dict(orig_mesh) + BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict) + + # print ob, me, me.getVertGroupNames() + meshes_to_clear.append( me ) + origData = False + mats = me.materials + else: + me = ob.getData(mesh=1) + mats = me.materials + + # Support object colors + tmp_colbits = ob.colbits + if tmp_colbits: + tmp_ob_mats = ob.getMaterials(1) # 1 so we get None's too. + for i in xrange(16): + if tmp_colbits & (1< fbxObject mapping + # this is needed for groups as well as fbxParenting + bpy.data.objects.tag = False + tmp_obmapping = {} + for ob_generic in ob_all_typegroups: + for ob_base in ob_generic: + ob_base.blenObject.tag = True + tmp_obmapping[ob_base.blenObject] = ob_base + + # Build Groups from objects we export + for blenGroup in bpy.data.groups: + fbxGroupName = None + for ob in blenGroup.objects: + if ob.tag: + if fbxGroupName == None: + fbxGroupName = sane_groupname(blenGroup) + groups.append((fbxGroupName, blenGroup)) + + tmp_obmapping[ob].fbxGroupNames.append(fbxGroupName) # also adds to the objects fbxGroupNames + + groups.sort() # not really needed + + # Assign parents using this mapping + for ob_generic in ob_all_typegroups: + for my_ob in ob_generic: + parent = my_ob.blenObject.parent + if parent and parent.tag: # does it exist and is it in the mapping + my_ob.fbxParent = tmp_obmapping[parent] + + + del tmp_obmapping + # Finished finding groups we use + + + materials = [(sane_matname(mat_tex_pair), mat_tex_pair) for mat_tex_pair in materials.iterkeys()] + textures = [(sane_texname(tex), tex) for tex in textures.iterkeys() if tex] + materials.sort() # sort by name + textures.sort() + + camera_count = 8 + file.write(''' + +; Object definitions +;------------------------------------------------------------------ + +Definitions: { + Version: 100 + Count: %i''' % (\ + 1+1+camera_count+\ + len(ob_meshes)+\ + len(ob_lights)+\ + len(ob_cameras)+\ + len(ob_arms)+\ + len(ob_null)+\ + len(ob_bones)+\ + bone_deformer_count+\ + len(materials)+\ + (len(textures)*2))) # add 1 for the root model 1 for global settings + + del bone_deformer_count + + file.write(''' + ObjectType: "Model" { + Count: %i + }''' % (\ + 1+camera_count+\ + len(ob_meshes)+\ + len(ob_lights)+\ + len(ob_cameras)+\ + len(ob_arms)+\ + len(ob_null)+\ + len(ob_bones))) # add 1 for the root model + + file.write(''' + ObjectType: "Geometry" { + Count: %i + }''' % len(ob_meshes)) + + if materials: + file.write(''' + ObjectType: "Material" { + Count: %i + }''' % len(materials)) + + if textures: + file.write(''' + ObjectType: "Texture" { + Count: %i + }''' % len(textures)) # add 1 for an empty tex + file.write(''' + ObjectType: "Video" { + Count: %i + }''' % len(textures)) # add 1 for an empty tex + + tmp = 0 + # Add deformer nodes + for my_mesh in ob_meshes: + if my_mesh.fbxArm: + tmp+=1 + + # Add subdeformers + for my_bone in ob_bones: + tmp += len(my_bone.blenMeshes) + + if tmp: + file.write(''' + ObjectType: "Deformer" { + Count: %i + }''' % tmp) + del tmp + + # we could avoid writing this possibly but for now just write it + + file.write(''' + ObjectType: "Pose" { + Count: 1 + }''') + + if groups: + file.write(''' + ObjectType: "GroupSelection" { + Count: %i + }''' % len(groups)) + + file.write(''' + ObjectType: "GlobalSettings" { + Count: 1 + } +}''') + + file.write(''' + +; Object properties +;------------------------------------------------------------------ + +Objects: {''') + + # To comply with other FBX FILES + write_camera_switch() + + # Write the null object + write_null(None, 'blend_root')# , GLOBAL_MATRIX) + + for my_null in ob_null: + write_null(my_null) + + for my_arm in ob_arms: + write_null(my_arm) + + for my_cam in ob_cameras: + write_camera(my_cam) + + for my_light in ob_lights: + write_light(my_light) + + for my_mesh in ob_meshes: + write_mesh(my_mesh) + + #for bonename, bone, obname, me, armob in ob_bones: + for my_bone in ob_bones: + write_bone(my_bone) + + write_camera_default() + + for matname, (mat, tex) in materials: + write_material(matname, mat) # We only need to have a material per image pair, but no need to write any image info into the material (dumb fbx standard) + + # each texture uses a video, odd + for texname, tex in textures: + write_video(texname, tex) + i = 0 + for texname, tex in textures: + write_texture(texname, tex, i) + i+=1 + + for groupname, group in groups: + write_group(groupname) + + # NOTE - c4d and motionbuilder dont need normalized weights, but deep-exploration 5 does and (max?) do. + + # Write armature modifiers + # TODO - add another MODEL? - because of this skin definition. + for my_mesh in ob_meshes: + if my_mesh.fbxArm: + write_deformer_skin(my_mesh.fbxName) + + # Get normalized weights for temorary use + if my_mesh.fbxBoneParent: + weights = None + else: + weights = meshNormalizedWeights(my_mesh.blenData) + + #for bonename, bone, obname, bone_mesh, armob in ob_bones: + for my_bone in ob_bones: + if me in my_bone.blenMeshes.itervalues(): + write_sub_deformer_skin(my_mesh, my_bone, weights) + + # Write pose's really weired, only needed when an armature and mesh are used together + # each by themselves dont need pose data. for now only pose meshes and bones + + file.write(''' + Pose: "Pose::BIND_POSES", "BindPose" { + Type: "BindPose" + Version: 100 + Properties60: { + } + NbPoseNodes: ''') + file.write(str(len(pose_items))) + + + for fbxName, matrix in pose_items: + file.write('\n\t\tPoseNode: {') + file.write('\n\t\t\tNode: "Model::%s"' % fbxName ) + if matrix: file.write('\n\t\t\tMatrix: %s' % mat4x4str(matrix)) + else: file.write('\n\t\t\tMatrix: %s' % mat4x4str(mtx4_identity)) + file.write('\n\t\t}') + + file.write('\n\t}') + + + # Finish Writing Objects + # Write global settings + file.write(''' + GlobalSettings: { + Version: 1000 + Properties60: { + Property: "UpAxis", "int", "",1 + Property: "UpAxisSign", "int", "",1 + Property: "FrontAxis", "int", "",2 + Property: "FrontAxisSign", "int", "",1 + Property: "CoordAxis", "int", "",0 + Property: "CoordAxisSign", "int", "",1 + Property: "UnitScaleFactor", "double", "",100 + } + } +''') + file.write('}') + + file.write(''' + +; Object relations +;------------------------------------------------------------------ + +Relations: {''') + + file.write('\n\tModel: "Model::blend_root", "Null" {\n\t}') + + for my_null in ob_null: + file.write('\n\tModel: "Model::%s", "Null" {\n\t}' % my_null.fbxName) + + for my_arm in ob_arms: + file.write('\n\tModel: "Model::%s", "Null" {\n\t}' % my_arm.fbxName) + + for my_mesh in ob_meshes: + file.write('\n\tModel: "Model::%s", "Mesh" {\n\t}' % my_mesh.fbxName) + + # TODO - limbs can have the same name for multiple armatures, should prefix. + #for bonename, bone, obname, me, armob in ob_bones: + for my_bone in ob_bones: + file.write('\n\tModel: "Model::%s", "Limb" {\n\t}' % my_bone.fbxName) + + for my_cam in ob_cameras: + file.write('\n\tModel: "Model::%s", "Camera" {\n\t}' % my_cam.fbxName) + + for my_light in ob_lights: + file.write('\n\tModel: "Model::%s", "Light" {\n\t}' % my_light.fbxName) + + file.write(''' + Model: "Model::Producer Perspective", "Camera" { + } + Model: "Model::Producer Top", "Camera" { + } + Model: "Model::Producer Bottom", "Camera" { + } + Model: "Model::Producer Front", "Camera" { + } + Model: "Model::Producer Back", "Camera" { + } + Model: "Model::Producer Right", "Camera" { + } + Model: "Model::Producer Left", "Camera" { + } + Model: "Model::Camera Switcher", "CameraSwitcher" { + }''') + + for matname, (mat, tex) in materials: + file.write('\n\tMaterial: "Material::%s", "" {\n\t}' % matname) + + if textures: + for texname, tex in textures: + file.write('\n\tTexture: "Texture::%s", "TextureVideoClip" {\n\t}' % texname) + for texname, tex in textures: + file.write('\n\tVideo: "Video::%s", "Clip" {\n\t}' % texname) + + # deformers - modifiers + for my_mesh in ob_meshes: + if my_mesh.fbxArm: + file.write('\n\tDeformer: "Deformer::Skin %s", "Skin" {\n\t}' % my_mesh.fbxName) + + #for bonename, bone, obname, me, armob in ob_bones: + for my_bone in ob_bones: + for fbxMeshObName in my_bone.blenMeshes: # .keys() - fbxMeshObName + # is this bone effecting a mesh? + file.write('\n\tDeformer: "SubDeformer::Cluster %s %s", "Cluster" {\n\t}' % (fbxMeshObName, my_bone.fbxName)) + + # This should be at the end + # file.write('\n\tPose: "Pose::BIND_POSES", "BindPose" {\n\t}') + + for groupname, group in groups: + file.write('\n\tGroupSelection: "GroupSelection::%s", "Default" {\n\t}' % groupname) + + file.write('\n}') + file.write(''' + +; Object connections +;------------------------------------------------------------------ + +Connections: {''') + + # NOTE - The FBX SDK dosnt care about the order but some importers DO! + # for instance, defining the material->mesh connection + # before the mesh->blend_root crashes cinema4d + + + # write the fake root node + file.write('\n\tConnect: "OO", "Model::blend_root", "Model::Scene"') + + for ob_generic in ob_all_typegroups: # all blender 'Object's we support + for my_ob in ob_generic: + if my_ob.fbxParent: + file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_ob.fbxName, my_ob.fbxParent.fbxName)) + else: + file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % my_ob.fbxName) + + if materials: + for my_mesh in ob_meshes: + # Connect all materials to all objects, not good form but ok for now. + for mat, tex in my_mesh.blenMaterials: + if mat: mat_name = mat.name + else: mat_name = None + + if tex: tex_name = tex.name + else: tex_name = None + + file.write('\n\tConnect: "OO", "Material::%s", "Model::%s"' % (sane_name_mapping_mat[mat_name, tex_name], my_mesh.fbxName)) + + if textures: + for my_mesh in ob_meshes: + if my_mesh.blenTextures: + # file.write('\n\tConnect: "OO", "Texture::_empty_", "Model::%s"' % my_mesh.fbxName) + for tex in my_mesh.blenTextures: + if tex: + file.write('\n\tConnect: "OO", "Texture::%s", "Model::%s"' % (sane_name_mapping_tex[tex.name], my_mesh.fbxName)) + + for texname, tex in textures: + file.write('\n\tConnect: "OO", "Video::%s", "Texture::%s"' % (texname, texname)) + + for my_mesh in ob_meshes: + if my_mesh.fbxArm: + file.write('\n\tConnect: "OO", "Deformer::Skin %s", "Model::%s"' % (my_mesh.fbxName, my_mesh.fbxName)) + + #for bonename, bone, obname, me, armob in ob_bones: + for my_bone in ob_bones: + for fbxMeshObName in my_bone.blenMeshes: # .keys() + file.write('\n\tConnect: "OO", "SubDeformer::Cluster %s %s", "Deformer::Skin %s"' % (fbxMeshObName, my_bone.fbxName, fbxMeshObName)) + + # limbs -> deformers + # for bonename, bone, obname, me, armob in ob_bones: + for my_bone in ob_bones: + for fbxMeshObName in my_bone.blenMeshes: # .keys() + file.write('\n\tConnect: "OO", "Model::%s", "SubDeformer::Cluster %s %s"' % (my_bone.fbxName, fbxMeshObName, my_bone.fbxName)) + + + #for bonename, bone, obname, me, armob in ob_bones: + for my_bone in ob_bones: + # Always parent to armature now + if my_bone.parent: + file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_bone.fbxName, my_bone.parent.fbxName) ) + else: + # the armature object is written as an empty and all root level bones connect to it + file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_bone.fbxName, my_bone.fbxArm.fbxName) ) + + # groups + if groups: + for ob_generic in ob_all_typegroups: + for ob_base in ob_generic: + for fbxGroupName in ob_base.fbxGroupNames: + file.write('\n\tConnect: "OO", "Model::%s", "GroupSelection::%s"' % (ob_base.fbxName, fbxGroupName)) + + for my_arm in ob_arms: + file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % my_arm.fbxName) + + file.write('\n}') + + + # Needed for scene footer as well as animation + render = sce.render + + # from the FBX sdk + #define KTIME_ONE_SECOND KTime (K_LONGLONG(46186158000)) + def fbx_time(t): + # 0.5 + val is the same as rounding. + return int(0.5 + ((t/fps) * 46186158000)) + + fps = float(render.fps) + start = render.sFrame + end = render.eFrame + if end < start: start, end = end, start + if start==end: ANIM_ENABLE = False + + # animations for these object types + ob_anim_lists = ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights, ob_arms + + if ANIM_ENABLE and [tmp for tmp in ob_anim_lists if tmp]: + + frame_orig = Blender.Get('curframe') + + if ANIM_OPTIMIZE: + ANIM_OPTIMIZE_PRECISSION_FLOAT = 0.1 ** ANIM_OPTIMIZE_PRECISSION + + # default action, when no actions are avaioable + tmp_actions = [None] # None is the default action + blenActionDefault = None + action_lastcompat = None + + if ANIM_ACTION_ALL: + bpy.data.actions.tag = False + tmp_actions = list(bpy.data.actions) + + + # find which actions are compatible with the armatures + # blenActions is not yet initialized so do it now. + tmp_act_count = 0 + for my_arm in ob_arms: + + # get the default name + if not blenActionDefault: + blenActionDefault = my_arm.blenAction + + arm_bone_names = set([my_bone.blenName for my_bone in my_arm.fbxBones]) + + for action in tmp_actions: + + action_chan_names = arm_bone_names.intersection( set(action.getChannelNames()) ) + + if action_chan_names: # at least one channel matches. + my_arm.blenActionList.append(action) + action.tag = True + tmp_act_count += 1 + + # incase there is no actions applied to armatures + action_lastcompat = action + + if tmp_act_count: + # unlikely to ever happen but if no actions applied to armatures, just use the last compatible armature. + if not blenActionDefault: + blenActionDefault = action_lastcompat + + del action_lastcompat + + file.write(''' +;Takes and animation section +;---------------------------------------------------- + +Takes: {''') + + if blenActionDefault: + file.write('\n\tCurrent: "%s"' % sane_takename(blenActionDefault)) + else: + file.write('\n\tCurrent: "Default Take"') + + for blenAction in tmp_actions: + # we have tagged all actious that are used be selected armatures + if blenAction: + if blenAction.tag: + print '\taction: "%s" exporting...' % blenAction.name + else: + print '\taction: "%s" has no armature using it, skipping' % blenAction.name + continue + + if blenAction == None: + # Warning, this only accounts for tmp_actions being [None] + file.write('\n\tTake: "Default Take" {') + act_start = start + act_end = end + else: + # use existing name + if blenAction == blenActionDefault: # have we alredy got the name + file.write('\n\tTake: "%s" {' % sane_name_mapping_take[blenAction.name]) + else: + file.write('\n\tTake: "%s" {' % sane_takename(blenAction)) + + tmp = blenAction.getFrameNumbers() + if tmp: + act_start = min(tmp) + act_end = max(tmp) + del tmp + else: + # Fallback on this, theres not much else we can do? :/ + # when an action has no length + act_start = start + act_end = end + + # Set the action active + for my_bone in ob_arms: + if blenAction in my_bone.blenActionList: + ob.action = blenAction + # print '\t\tSetting Action!', blenAction + # sce.update(1) + + file.write('\n\t\tFileName: "Default_Take.tak"') # ??? - not sure why this is needed + file.write('\n\t\tLocalTime: %i,%i' % (fbx_time(act_start-1), fbx_time(act_end-1))) # ??? - not sure why this is needed + file.write('\n\t\tReferenceTime: %i,%i' % (fbx_time(act_start-1), fbx_time(act_end-1))) # ??? - not sure why this is needed + + file.write(''' + + ;Models animation + ;----------------------------------------------------''') + + + # set pose data for all bones + # do this here incase the action changes + ''' + for my_bone in ob_bones: + my_bone.flushAnimData() + ''' + i = act_start + while i <= act_end: + Blender.Set('curframe', i) + for ob_generic in ob_anim_lists: + for my_ob in ob_generic: + #Blender.Window.RedrawAll() + if ob_generic == ob_meshes and my_ob.fbxArm: + # We cant animate armature meshes! + pass + else: + my_ob.setPoseFrame(i) + + i+=1 + + + #for bonename, bone, obname, me, armob in ob_bones: + for ob_generic in (ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights, ob_arms): + + for my_ob in ob_generic: + + if ob_generic == ob_meshes and my_ob.fbxArm: + # do nothing, + pass + else: + + file.write('\n\t\tModel: "Model::%s" {' % my_ob.fbxName) # ??? - not sure why this is needed + file.write('\n\t\t\tVersion: 1.1') + file.write('\n\t\t\tChannel: "Transform" {') + + context_bone_anim_mats = [ (my_ob.getAnimParRelMatrix(frame), my_ob.getAnimParRelMatrixRot(frame)) for frame in xrange(act_start, act_end+1) ] + + # ---------------- + # ---------------- + for TX_LAYER, TX_CHAN in enumerate('TRS'): # transform, rotate, scale + + if TX_CHAN=='T': context_bone_anim_vecs = [mtx[0].translationPart() for mtx in context_bone_anim_mats] + elif TX_CHAN=='S': context_bone_anim_vecs = [mtx[0].scalePart() for mtx in context_bone_anim_mats] + elif TX_CHAN=='R': + # Was.... + # elif TX_CHAN=='R': context_bone_anim_vecs = [mtx[1].toEuler() for mtx in context_bone_anim_mats] + # + # ...but we need to use the previous euler for compatible conversion. + context_bone_anim_vecs = [] + prev_eul = None + for mtx in context_bone_anim_mats: + if prev_eul: prev_eul = mtx[1].toEuler(prev_eul) + else: prev_eul = mtx[1].toEuler() + context_bone_anim_vecs.append(prev_eul) + + file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation + + for i in xrange(3): + # Loop on each axis of the bone + file.write('\n\t\t\t\t\tChannel: "%s" {'% ('XYZ'[i])) # translation + file.write('\n\t\t\t\t\t\tDefault: %.15f' % context_bone_anim_vecs[0][i] ) + file.write('\n\t\t\t\t\t\tKeyVer: 4005') + + if not ANIM_OPTIMIZE: + # Just write all frames, simple but in-eficient + file.write('\n\t\t\t\t\t\tKeyCount: %i' % (1 + act_end - act_start)) + file.write('\n\t\t\t\t\t\tKey: ') + frame = act_start + while frame <= act_end: + if frame!=act_start: + file.write(',') + + # Curve types are 'C,n' for constant, 'L' for linear + # C,n is for bezier? - linear is best for now so we can do simple keyframe removal + file.write('\n\t\t\t\t\t\t\t%i,%.15f,L' % (fbx_time(frame-1), context_bone_anim_vecs[frame-act_start][i] )) + frame+=1 + else: + # remove unneeded keys, j is the frame, needed when some frames are removed. + context_bone_anim_keys = [ (vec[i], j) for j, vec in enumerate(context_bone_anim_vecs) ] + + # last frame to fisrt frame, missing 1 frame on either side. + # removeing in a backwards loop is faster + #for j in xrange( (act_end-act_start)-1, 0, -1 ): + # j = (act_end-act_start)-1 + j = len(context_bone_anim_keys)-2 + while j > 0 and len(context_bone_anim_keys) > 2: + # print j, len(context_bone_anim_keys) + # Is this key the same as the ones next to it? + + # co-linear horizontal... + if abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j-1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT and\ + abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j+1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT: + + del context_bone_anim_keys[j] + + else: + frame_range = float(context_bone_anim_keys[j+1][1] - context_bone_anim_keys[j-1][1]) + frame_range_fac1 = (context_bone_anim_keys[j+1][1] - context_bone_anim_keys[j][1]) / frame_range + frame_range_fac2 = 1.0 - frame_range_fac1 + + if abs(((context_bone_anim_keys[j-1][0]*frame_range_fac1 + context_bone_anim_keys[j+1][0]*frame_range_fac2)) - context_bone_anim_keys[j][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT: + del context_bone_anim_keys[j] + else: + j-=1 + + # keep the index below the list length + if j > len(context_bone_anim_keys)-2: + j = len(context_bone_anim_keys)-2 + + if len(context_bone_anim_keys) == 2 and context_bone_anim_keys[0][0] == context_bone_anim_keys[1][0]: + # This axis has no moton, its okay to skip KeyCount and Keys in this case + pass + else: + # We only need to write these if there is at least one + file.write('\n\t\t\t\t\t\tKeyCount: %i' % len(context_bone_anim_keys)) + file.write('\n\t\t\t\t\t\tKey: ') + for val, frame in context_bone_anim_keys: + if frame != context_bone_anim_keys[0][1]: # not the first + file.write(',') + # frame is alredy one less then blenders frame + file.write('\n\t\t\t\t\t\t\t%i,%.15f,L' % (fbx_time(frame), val )) + + if i==0: file.write('\n\t\t\t\t\t\tColor: 1,0,0') + elif i==1: file.write('\n\t\t\t\t\t\tColor: 0,1,0') + elif i==2: file.write('\n\t\t\t\t\t\tColor: 0,0,1') + + file.write('\n\t\t\t\t\t}') + file.write('\n\t\t\t\t\tLayerType: %i' % (TX_LAYER+1) ) + file.write('\n\t\t\t\t}') + + # --------------- + + file.write('\n\t\t\t}') + file.write('\n\t\t}') + + # end the take + file.write('\n\t}') + + # end action loop. set original actions + # do this after every loop incase actions effect eachother. + for my_bone in ob_arms: + my_bone.blenObject.action = my_bone.blenAction + + file.write('\n}') + + Blender.Set('curframe', frame_orig) + + else: + # no animation + file.write('\n;Takes and animation section') + file.write('\n;----------------------------------------------------') + file.write('\n') + file.write('\nTakes: {') + file.write('\n\tCurrent: ""') + file.write('\n}') + + + # write meshes animation + #for obname, ob, mtx, me, mats, arm, armname in ob_meshes: + + + # Clear mesh data Only when writing with modifiers applied + for me in meshes_to_clear: + me.verts = None + + + + # --------------------------- Footer + if world: + has_mist = world.mode & 1 + mist_intense, mist_start, mist_end, mist_height = world.mist + world_hor = world.hor + else: + has_mist = mist_intense = mist_start = mist_end = mist_height = 0 + world_hor = 0,0,0 + + file.write('\n;Version 5 settings') + file.write('\n;------------------------------------------------------------------') + file.write('\n') + file.write('\nVersion5: {') + file.write('\n\tAmbientRenderSettings: {') + file.write('\n\t\tVersion: 101') + file.write('\n\t\tAmbientLightColor: %.1f,%.1f,%.1f,0' % tuple(world_amb)) + file.write('\n\t}') + file.write('\n\tFogOptions: {') + file.write('\n\t\tFlogEnable: %i' % has_mist) + file.write('\n\t\tFogMode: 0') + file.write('\n\t\tFogDensity: %.3f' % mist_intense) + file.write('\n\t\tFogStart: %.3f' % mist_start) + file.write('\n\t\tFogEnd: %.3f' % mist_end) + file.write('\n\t\tFogColor: %.1f,%.1f,%.1f,1' % tuple(world_hor)) + file.write('\n\t}') + file.write('\n\tSettings: {') + file.write('\n\t\tFrameRate: "%i"' % int(fps)) + file.write('\n\t\tTimeFormat: 1') + file.write('\n\t\tSnapOnFrames: 0') + file.write('\n\t\tReferenceTimeIndex: -1') + file.write('\n\t\tTimeLineStartTime: %i' % fbx_time(start-1)) + file.write('\n\t\tTimeLineStopTime: %i' % fbx_time(end-1)) + file.write('\n\t}') + file.write('\n\tRendererSetting: {') + file.write('\n\t\tDefaultCamera: "Producer Perspective"') + file.write('\n\t\tDefaultViewingMode: 0') + file.write('\n\t}') + file.write('\n}') + file.write('\n') + + # Incase sombody imports this, clean up by clearing global dicts + sane_name_mapping_ob.clear() + sane_name_mapping_mat.clear() + sane_name_mapping_tex.clear() + + ob_arms[:] = [] + ob_bones[:] = [] + ob_cameras[:] = [] + ob_lights[:] = [] + ob_meshes[:] = [] + ob_null[:] = [] + + + # copy images if enabled + if EXP_IMAGE_COPY: + copy_images( basepath, [ tex[1] for tex in textures if tex[1] != None ]) + + print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time) + return True + + +# -------------------------------------------- +# UI Function - not a part of the exporter. +# this is to seperate the user interface from the rest of the exporter. +from Blender import Draw, Window +EVENT_NONE = 0 +EVENT_EXIT = 1 +EVENT_REDRAW = 2 +EVENT_FILESEL = 3 + +GLOBALS = {} + +# export opts + +def do_redraw(e,v): GLOBALS['EVENT'] = e + +# toggle between these 2, only allow one on at once +def do_obs_sel(e,v): + GLOBALS['EVENT'] = e + GLOBALS['EXP_OBS_SCENE'].val = 0 + GLOBALS['EXP_OBS_SELECTED'].val = 1 + +def do_obs_sce(e,v): + GLOBALS['EVENT'] = e + GLOBALS['EXP_OBS_SCENE'].val = 1 + GLOBALS['EXP_OBS_SELECTED'].val = 0 + +def do_obs_sce(e,v): + GLOBALS['EVENT'] = e + GLOBALS['EXP_OBS_SCENE'].val = 1 + GLOBALS['EXP_OBS_SELECTED'].val = 0 + +def do_batch_type_grp(e,v): + GLOBALS['EVENT'] = e + GLOBALS['BATCH_GROUP'].val = 1 + GLOBALS['BATCH_SCENE'].val = 0 + +def do_batch_type_sce(e,v): + GLOBALS['EVENT'] = e + GLOBALS['BATCH_GROUP'].val = 0 + GLOBALS['BATCH_SCENE'].val = 1 + +def do_anim_act_all(e,v): + GLOBALS['EVENT'] = e + GLOBALS['ANIM_ACTION_ALL'][0].val = 1 + GLOBALS['ANIM_ACTION_ALL'][1].val = 0 + +def do_anim_act_cur(e,v): + if GLOBALS['BATCH_ENABLE'].val and GLOBALS['BATCH_GROUP'].val: + Draw.PupMenu('Warning%t|Cant use this with batch export group option') + else: + GLOBALS['EVENT'] = e + GLOBALS['ANIM_ACTION_ALL'][0].val = 0 + GLOBALS['ANIM_ACTION_ALL'][1].val = 1 + +def fbx_ui_exit(e,v): + GLOBALS['EVENT'] = e + +def do_help(e,v): + url = 'http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx' + print 'Trying to open web browser with documentation at this address...' + print '\t' + url + + try: + import webbrowser + webbrowser.open(url) + except: + Blender.Draw.PupMenu("Error%t|Opening a webbrowser requires a full python installation") + print '...could not open a browser window.' + + + +# run when export is pressed +#def fbx_ui_write(e,v): +def fbx_ui_write(filename, context): + + # Dont allow overwriting files when saving normally + if not GLOBALS['BATCH_ENABLE'].val: + if not BPyMessages.Warning_SaveOver(filename): + return + + GLOBALS['EVENT'] = EVENT_EXIT + + # Keep the order the same as above for simplicity + # the [] is a dummy arg used for objects + + Blender.Window.WaitCursor(1) + + # Make the matrix + GLOBAL_MATRIX = mtx4_identity + GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = GLOBALS['_SCALE'].val + if GLOBALS['_XROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_x90n + if GLOBALS['_YROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_y90n + if GLOBALS['_ZROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_z90n + + ret = write(\ + filename, None,\ + context, + GLOBALS['EXP_OBS_SELECTED'].val,\ + GLOBALS['EXP_MESH'].val,\ + GLOBALS['EXP_MESH_APPLY_MOD'].val,\ + GLOBALS['EXP_MESH_HQ_NORMALS'].val,\ + GLOBALS['EXP_ARMATURE'].val,\ + GLOBALS['EXP_LAMP'].val,\ + GLOBALS['EXP_CAMERA'].val,\ + GLOBALS['EXP_EMPTY'].val,\ + GLOBALS['EXP_IMAGE_COPY'].val,\ + GLOBAL_MATRIX,\ + GLOBALS['ANIM_ENABLE'].val,\ + GLOBALS['ANIM_OPTIMIZE'].val,\ + GLOBALS['ANIM_OPTIMIZE_PRECISSION'].val,\ + GLOBALS['ANIM_ACTION_ALL'][0].val,\ + GLOBALS['BATCH_ENABLE'].val,\ + GLOBALS['BATCH_GROUP'].val,\ + GLOBALS['BATCH_SCENE'].val,\ + GLOBALS['BATCH_FILE_PREFIX'].val,\ + GLOBALS['BATCH_OWN_DIR'].val,\ + ) + + Blender.Window.WaitCursor(0) + GLOBALS.clear() + + if ret == False: + Draw.PupMenu('Error%t|Path cannot be written to!') + + +def fbx_ui(): + # Only to center the UI + x,y = GLOBALS['MOUSE'] + x-=180; y-=0 # offset... just to get it centered + + Draw.Label('Export Objects...', x+20,y+165, 200, 20) + + if not GLOBALS['BATCH_ENABLE'].val: + Draw.BeginAlign() + GLOBALS['EXP_OBS_SELECTED'] = Draw.Toggle('Selected Objects', EVENT_REDRAW, x+20, y+145, 160, 20, GLOBALS['EXP_OBS_SELECTED'].val, 'Export selected objects on visible layers', do_obs_sel) + GLOBALS['EXP_OBS_SCENE'] = Draw.Toggle('Scene Objects', EVENT_REDRAW, x+180, y+145, 160, 20, GLOBALS['EXP_OBS_SCENE'].val, 'Export all objects in this scene', do_obs_sce) + Draw.EndAlign() + + Draw.BeginAlign() + GLOBALS['_SCALE'] = Draw.Number('Scale:', EVENT_NONE, x+20, y+120, 140, 20, GLOBALS['_SCALE'].val, 0.01, 1000.0, 'Scale all data, (Note! some imports dont support scaled armatures)') + GLOBALS['_XROT90'] = Draw.Toggle('Rot X90', EVENT_NONE, x+160, y+120, 60, 20, GLOBALS['_XROT90'].val, 'Rotate all objects 90 degrese about the X axis') + GLOBALS['_YROT90'] = Draw.Toggle('Rot Y90', EVENT_NONE, x+220, y+120, 60, 20, GLOBALS['_YROT90'].val, 'Rotate all objects 90 degrese about the Y axis') + GLOBALS['_ZROT90'] = Draw.Toggle('Rot Z90', EVENT_NONE, x+280, y+120, 60, 20, GLOBALS['_ZROT90'].val, 'Rotate all objects 90 degrese about the Z axis') + Draw.EndAlign() + + y -= 35 + + Draw.BeginAlign() + GLOBALS['EXP_EMPTY'] = Draw.Toggle('Empty', EVENT_NONE, x+20, y+120, 60, 20, GLOBALS['EXP_EMPTY'].val, 'Export empty objects') + GLOBALS['EXP_CAMERA'] = Draw.Toggle('Camera', EVENT_NONE, x+80, y+120, 60, 20, GLOBALS['EXP_CAMERA'].val, 'Export camera objects') + GLOBALS['EXP_LAMP'] = Draw.Toggle('Lamp', EVENT_NONE, x+140, y+120, 60, 20, GLOBALS['EXP_LAMP'].val, 'Export lamp objects') + GLOBALS['EXP_ARMATURE'] = Draw.Toggle('Armature', EVENT_NONE, x+200, y+120, 60, 20, GLOBALS['EXP_ARMATURE'].val, 'Export armature objects') + GLOBALS['EXP_MESH'] = Draw.Toggle('Mesh', EVENT_REDRAW, x+260, y+120, 80, 20, GLOBALS['EXP_MESH'].val, 'Export mesh objects', do_redraw) #, do_axis_z) + Draw.EndAlign() + + if GLOBALS['EXP_MESH'].val: + # below mesh but + Draw.BeginAlign() + GLOBALS['EXP_MESH_APPLY_MOD'] = Draw.Toggle('Modifiers', EVENT_NONE, x+260, y+100, 80, 20, GLOBALS['EXP_MESH_APPLY_MOD'].val, 'Apply modifiers to mesh objects') #, do_axis_z) + GLOBALS['EXP_MESH_HQ_NORMALS'] = Draw.Toggle('HQ Normals', EVENT_NONE, x+260, y+80, 80, 20, GLOBALS['EXP_MESH_HQ_NORMALS'].val, 'Generate high quality normals') #, do_axis_z) + Draw.EndAlign() + + GLOBALS['EXP_IMAGE_COPY'] = Draw.Toggle('Copy Image Files', EVENT_NONE, x+20, y+80, 160, 20, GLOBALS['EXP_IMAGE_COPY'].val, 'Copy image files to the destination path') #, do_axis_z) + + + Draw.Label('Export Armature Animation...', x+20,y+45, 300, 20) + + GLOBALS['ANIM_ENABLE'] = Draw.Toggle('Enable Animation', EVENT_REDRAW, x+20, y+25, 160, 20, GLOBALS['ANIM_ENABLE'].val, 'Export keyframe animation', do_redraw) + if GLOBALS['ANIM_ENABLE'].val: + Draw.BeginAlign() + GLOBALS['ANIM_OPTIMIZE'] = Draw.Toggle('Optimize Keyframes', EVENT_REDRAW, x+20, y+0, 160, 20, GLOBALS['ANIM_OPTIMIZE'].val, 'Remove double keyframes', do_redraw) + if GLOBALS['ANIM_OPTIMIZE'].val: + GLOBALS['ANIM_OPTIMIZE_PRECISSION'] = Draw.Number('Precission: ', EVENT_NONE, x+180, y+0, 160, 20, GLOBALS['ANIM_OPTIMIZE_PRECISSION'].val, 1, 16, 'Tolerence for comparing double keyframes (higher for greater accuracy)') + Draw.EndAlign() + + Draw.BeginAlign() + GLOBALS['ANIM_ACTION_ALL'][1] = Draw.Toggle('Current Action', EVENT_REDRAW, x+20, y-25, 160, 20, GLOBALS['ANIM_ACTION_ALL'][1].val, 'Use actions currently applied to the armatures (use scene start/end frame)', do_anim_act_cur) + GLOBALS['ANIM_ACTION_ALL'][0] = Draw.Toggle('All Actions', EVENT_REDRAW, x+180,y-25, 160, 20, GLOBALS['ANIM_ACTION_ALL'][0].val, 'Use all actions for armatures', do_anim_act_all) + Draw.EndAlign() + + + Draw.Label('Export Batch...', x+20,y-60, 300, 20) + GLOBALS['BATCH_ENABLE'] = Draw.Toggle('Enable Batch', EVENT_REDRAW, x+20, y-80, 160, 20, GLOBALS['BATCH_ENABLE'].val, 'Automate exporting multiple scenes or groups to files', do_redraw) + + if GLOBALS['BATCH_ENABLE'].val: + Draw.BeginAlign() + GLOBALS['BATCH_GROUP'] = Draw.Toggle('Group > File', EVENT_REDRAW, x+20, y-105, 160, 20, GLOBALS['BATCH_GROUP'].val, 'Export each group as an FBX file', do_batch_type_grp) + GLOBALS['BATCH_SCENE'] = Draw.Toggle('Scene > File', EVENT_REDRAW, x+180, y-105, 160, 20, GLOBALS['BATCH_SCENE'].val, 'Export each scene as an FBX file', do_batch_type_sce) + + # Own dir requires OS module + if os: + GLOBALS['BATCH_OWN_DIR'] = Draw.Toggle('Own Dir', EVENT_NONE, x+20, y-125, 80, 20, GLOBALS['BATCH_OWN_DIR'].val, 'Create a dir for each exported file') + GLOBALS['BATCH_FILE_PREFIX'] = Draw.String('Prefix: ', EVENT_NONE, x+100, y-125, 240, 20, GLOBALS['BATCH_FILE_PREFIX'].val, 64, 'Prefix each file with this name ') + else: + GLOBALS['BATCH_FILE_PREFIX'] = Draw.String('Prefix: ', EVENT_NONE, x+20, y-125, 320, 20, GLOBALS['BATCH_FILE_PREFIX'].val, 64, 'Prefix each file with this name ') + + + Draw.EndAlign() + + #y+=80 + + ''' + Draw.BeginAlign() + GLOBALS['FILENAME'] = Draw.String('path: ', EVENT_NONE, x+20, y-170, 300, 20, GLOBALS['FILENAME'].val, 64, 'Prefix each file with this name ') + Draw.PushButton('..', EVENT_FILESEL, x+320, y-170, 20, 20, 'Select the path', do_redraw) + ''' + # Until batch is added + # + + + #Draw.BeginAlign() + Draw.PushButton('Online Help', EVENT_REDRAW, x+20, y-160, 100, 20, 'Open online help in a browser window', do_help) + Draw.PushButton('Cancel', EVENT_EXIT, x+130, y-160, 100, 20, 'Exit the exporter', fbx_ui_exit) + Draw.PushButton('Export', EVENT_FILESEL, x+240, y-160, 100, 20, 'Export the fbx file', do_redraw) + + #Draw.PushButton('Export', EVENT_EXIT, x+180, y-160, 160, 20, 'Export the fbx file', fbx_ui_write) + #Draw.EndAlign() + + # exit when mouse out of the view? + # GLOBALS['EVENT'] = EVENT_EXIT + +#def write_ui(filename): +def write_ui(): + + # globals + GLOBALS['EVENT'] = EVENT_REDRAW + #GLOBALS['MOUSE'] = Window.GetMouseCoords() + GLOBALS['MOUSE'] = [i/2 for i in Window.GetScreenSize()] + GLOBALS['FILENAME'] = '' + ''' + # IF called from the fileselector + if filename == None: + GLOBALS['FILENAME'] = filename # Draw.Create(Blender.sys.makename(ext='.fbx')) + else: + GLOBALS['FILENAME'].val = filename + ''' + GLOBALS['EXP_OBS_SELECTED'] = Draw.Create(1) # dont need 2 variables but just do this for clarity + GLOBALS['EXP_OBS_SCENE'] = Draw.Create(0) + + GLOBALS['EXP_MESH'] = Draw.Create(1) + GLOBALS['EXP_MESH_APPLY_MOD'] = Draw.Create(1) + GLOBALS['EXP_MESH_HQ_NORMALS'] = Draw.Create(0) + GLOBALS['EXP_ARMATURE'] = Draw.Create(1) + GLOBALS['EXP_LAMP'] = Draw.Create(1) + GLOBALS['EXP_CAMERA'] = Draw.Create(1) + GLOBALS['EXP_EMPTY'] = Draw.Create(1) + GLOBALS['EXP_IMAGE_COPY'] = Draw.Create(0) + # animation opts + GLOBALS['ANIM_ENABLE'] = Draw.Create(1) + GLOBALS['ANIM_OPTIMIZE'] = Draw.Create(1) + GLOBALS['ANIM_OPTIMIZE_PRECISSION'] = Draw.Create(4) # decimal places + GLOBALS['ANIM_ACTION_ALL'] = [Draw.Create(0), Draw.Create(1)] # not just the current action + + # batch export options + GLOBALS['BATCH_ENABLE'] = Draw.Create(0) + GLOBALS['BATCH_GROUP'] = Draw.Create(1) # cant have both of these enabled at once. + GLOBALS['BATCH_SCENE'] = Draw.Create(0) # see above + GLOBALS['BATCH_FILE_PREFIX'] = Draw.Create(Blender.sys.makename(ext='_').split('\\')[-1].split('/')[-1]) + GLOBALS['BATCH_OWN_DIR'] = Draw.Create(0) + # done setting globals + + # Used by the user interface + GLOBALS['_SCALE'] = Draw.Create(1.0) + GLOBALS['_XROT90'] = Draw.Create(True) + GLOBALS['_YROT90'] = Draw.Create(False) + GLOBALS['_ZROT90'] = Draw.Create(False) + + # best not do move the cursor + # Window.SetMouseCoords(*[i/2 for i in Window.GetScreenSize()]) + + # hack so the toggle buttons redraw. this is not nice at all + while GLOBALS['EVENT'] != EVENT_EXIT: + + if GLOBALS['BATCH_ENABLE'].val and GLOBALS['BATCH_GROUP'].val and GLOBALS['ANIM_ACTION_ALL'][1].val: + #Draw.PupMenu("Warning%t|Cant batch export groups with 'Current Action' ") + GLOBALS['ANIM_ACTION_ALL'][0].val = 1 + GLOBALS['ANIM_ACTION_ALL'][1].val = 0 + + if GLOBALS['EVENT'] == EVENT_FILESEL: + if GLOBALS['BATCH_ENABLE'].val: + txt = 'Batch FBX Dir' + name = Blender.sys.expandpath('//') + else: + txt = 'Export FBX' + name = Blender.sys.makename(ext='.fbx') + + Blender.Window.FileSelector(fbx_ui_write, txt, name) + #fbx_ui_write('/test.fbx') + break + + Draw.UIBlock(fbx_ui, 0) + + + # GLOBALS.clear() +#test = [write_ui] +if __name__ == '__main__': + # Cant call the file selector first because of a bug in the interface that crashes it. + # Blender.Window.FileSelector(write_ui, 'Export FBX', Blender.sys.makename(ext='.fbx')) + #write('/scratch/test.fbx') + #write_ui('/scratch/test.fbx') + + if not set: + Draw.PupMenu('Error%t|A full install of python2.3 or python 2.4+ is needed to run this script.') + else: + write_ui() + +# NOTES (all line numbers correspond to original export_fbx.py (under release/scripts) +# +# - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print +# - get rid of cleanName somehow +# - new scene creation, activation: lines 327-342, 368 + +# TODO + +# bpy.data.remove_scene: line 366 diff --git a/release/io/export_obj.py b/release/io/export_obj.py index ee045053dd3..26e6e248a45 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -962,3 +962,4 @@ if __name__ == "__main__": # - NURBS - needs API additions # - all scenes export # - normals calculation +# - get rid of cleanName somehow diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index caa970eff57..dc519e9ec6e 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -609,6 +609,16 @@ static void rna_def_bone(BlenderRNA *brna) prop= RNA_def_property(srna, "selected", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_SELECTED); RNA_def_property_ui_text(prop, "Selected", ""); + + prop= RNA_def_property(srna, "matrix", PROP_FLOAT, PROP_MATRIX); + RNA_def_property_float_sdna(prop, NULL, "bone_mat"); + RNA_def_property_array(prop, 9); + RNA_def_property_ui_text(prop, "Bone Matrix", "3x3 bone matrix."); + + prop= RNA_def_property(srna, "armature_matrix", PROP_FLOAT, PROP_MATRIX); + RNA_def_property_float_sdna(prop, NULL, "arm_mat"); + RNA_def_property_array(prop, 16); + RNA_def_property_ui_text(prop, "Bone Armature Matrix", "4x4 bone matrix relative to armature."); } static void rna_def_edit_bone(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_pose_api.c b/source/blender/makesrna/intern/rna_pose_api.c new file mode 100644 index 00000000000..c0bf3339f4f --- /dev/null +++ b/source/blender/makesrna/intern/rna_pose_api.c @@ -0,0 +1,56 @@ +/** + * + * + * ***** 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) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include "RNA_define.h" +#include "RNA_types.h" + +#include "DNA_object_types.h" + +/* #include "BLO_sys_types.h" */ + +#ifdef RNA_RUNTIME + +/* #include "DNA_anim_types.h" */ +#include "DNA_action_types.h" /* bPose */ + +#else + +void RNA_api_pose(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *parm; + +} + +#endif + From 797c6a2295f47f1c3a71f9094c710316cdf2ecf3 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Fri, 10 Jul 2009 14:46:52 +0000 Subject: [PATCH 40/68] - RNA-wrapped Bone props: head, armature_head, tail, armature_tail. - more FBX conversion --- release/io/export_fbx.py | 131 ++++++++++++------ release/io/export_obj.py | 12 +- source/blender/makesrna/intern/makesrna.c | 2 +- source/blender/makesrna/intern/rna_armature.c | 23 ++- 4 files changed, 116 insertions(+), 52 deletions(-) diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index cea9f500533..59480c4d455 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -210,10 +210,14 @@ def derived_paths(fname_orig, basepath, FORCE_CWD=False): FORCE_CWD - dont use the basepath, just add a ./ to the filename. use when we know the file will be in the basepath. ''' - fname = Blender.sys.expandpath(fname_orig) + fname = bpy.sys.expandpath(fname_orig) +# fname = Blender.sys.expandpath(fname_orig) fname_strip = strip_path(fname) - if FORCE_CWD: fname_rel = '.' + os.sep + fname_strip - else: fname_rel = Blender.sys.relpath(fname, basepath) + if FORCE_CWD: + fname_rel = '.' + os.sep + fname_strip + else: + fname_rel = bpy.sys.relpath(fname, basepath) +# fname_rel = Blender.sys.relpath(fname, basepath) if fname_rel.startswith('//'): fname_rel = '.' + os.sep + fname_rel[2:] return fname, fname_strip, fname_rel @@ -414,7 +418,7 @@ def write(filename, batch_objects = None, \ self.blenMeshes = {} # fbxMeshObName : mesh self.fbxArm = fbxArm self.restMatrix = blenBone.armature_matrix - # self.restMatrix = blenBone.matrix['ARMATURESPACE'] +# self.restMatrix = blenBone.matrix['ARMATURESPACE'] # not used yet # self.restMatrixInv = self.restMatrix.copy().invert() @@ -532,13 +536,15 @@ def write(filename, batch_objects = None, \ print '\nFBX export starting...', filename - start_time = Blender.sys.time() + start_time = bpy.sys.time() +# start_time = Blender.sys.time() try: file = open(filename, 'w') except: return False - - sce = bpy.data.scenes.active + + sce = context.scene +# sce = bpy.data.scenes.active world = sce.world @@ -570,7 +576,8 @@ def write(filename, batch_objects = None, \ }''' % (curtime)) file.write('\nCreationTime: "%.4i-%.2i-%.2i %.2i:%.2i:%.2i:000"' % curtime) - file.write('\nCreator: "Blender3D version %.2f"' % Blender.Get('version')) + file.write('\nCreator: "Blender3D version 2.5"') +# file.write('\nCreator: "Blender3D version %.2f"' % Blender.Get('version')) pose_items = [] # list of (fbxName, matrix) to write pose data for, easier to collect allong the way @@ -579,16 +586,19 @@ def write(filename, batch_objects = None, \ ''' Matrix mod is so armature objects can modify their bone matricies ''' - if isinstance(ob, Blender.Types.BoneType): + if isinstance(ob, bpy.types.Bone): +# if isinstance(ob, Blender.Types.BoneType): # we know we have a matrix # matrix = mtx4_z90 * (ob.matrix['ARMATURESPACE'] * matrix_mod) - matrix = mtx4_z90 * ob.matrix['ARMATURESPACE'] # dont apply armature matrix anymore + matrix = mtx4_z90 * ob.armature_matrix # dont apply armature matrix anymore +# matrix = mtx4_z90 * ob.matrix['ARMATURESPACE'] # dont apply armature matrix anymore parent = ob.parent if parent: #par_matrix = mtx4_z90 * (parent.matrix['ARMATURESPACE'] * matrix_mod) - par_matrix = mtx4_z90 * parent.matrix['ARMATURESPACE'] # dont apply armature matrix anymore + par_matrix = mtx4_z90 * parent.armature_matrix # dont apply armature matrix anymore +# par_matrix = mtx4_z90 * parent.matrix['ARMATURESPACE'] # dont apply armature matrix anymore matrix = matrix * par_matrix.copy().invert() matrix_rot = matrix.rotationPart() @@ -755,8 +765,9 @@ def write(filename, batch_objects = None, \ ((my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']) * my_bone.fbxArm.parRelMatrix()).length) """ - file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %\ - (my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']).length) + file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' % + (my_bone.blenBone.armature_head - my_bone.blenBone.armature_tail).length) +# (my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']).length) #file.write('\n\t\t\tProperty: "LimbLength", "double", "",1') file.write('\n\t\t\tProperty: "Color", "ColorRGB", "",0.8,0.8,0.8') @@ -895,9 +906,12 @@ def write(filename, batch_objects = None, \ ''' Write a blender camera ''' - render = sce.render - width = render.sizeX - height = render.sizeY + render = sce.render_data + width = render.resolution_x + height = render.resolution_y +# render = sce.render +# width = render.sizeX +# height = render.sizeY aspect = float(width)/height data = my_cam.blenObject.data @@ -911,8 +925,10 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t\tProperty: "FieldOfViewX", "FieldOfView", "A+",1') file.write('\n\t\t\tProperty: "FieldOfViewY", "FieldOfView", "A+",1') file.write('\n\t\t\tProperty: "FocalLength", "Real", "A+",14.0323972702026') - file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",%.6f' % data.shiftX) # not sure if this is in the correct units? - file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",%.6f' % data.shiftY) # ditto + file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",%.6f' % data.shift_x) # not sure if this is in the correct units? +# file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",%.6f' % data.shiftX) # not sure if this is in the correct units? + file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",%.6f' % data.shift_y) # ditto +# file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",%.6f' % data.shiftY) # ditto file.write('\n\t\t\tProperty: "BackgroundColor", "Color", "A+",0,0,0') file.write('\n\t\t\tProperty: "TurnTable", "Real", "A+",0') file.write('\n\t\t\tProperty: "DisplayTurnTableIcon", "bool", "",1') @@ -944,8 +960,10 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t\tProperty: "ShowOpticalCenter", "bool", "",0') file.write('\n\t\t\tProperty: "ShowAzimut", "bool", "",1') file.write('\n\t\t\tProperty: "ShowTimeCode", "bool", "",0') - file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % data.clipStart) - file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % data.clipStart) + file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % data.clip_start) +# file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % data.clipStart) + file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % data.clip_end) +# file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % data.clipStart) file.write('\n\t\t\tProperty: "FilmWidth", "double", "",1.0') file.write('\n\t\t\tProperty: "FilmHeight", "double", "",1.0') file.write('\n\t\t\tProperty: "FilmAspectRatio", "double", "",%.6f' % aspect) @@ -1018,16 +1036,20 @@ def write(filename, batch_objects = None, \ #ePOINT, #eDIRECTIONAL #eSPOT - light_type = light.type + light_type_items = {'POINT': 0, 'SUN': 1, 'SPOT': 2, 'HEMI': 3, 'AREA': 4} + light_type = light_type_items[light.type] +# light_type = light.type if light_type > 2: light_type = 1 # hemi and area lights become directional - - mode = light.mode - if mode & Blender.Lamp.Modes.RayShadow or mode & Blender.Lamp.Modes.Shadows: + +# mode = light.mode + if light.shadow_method == 'RAY_SHADOW' or light.shadow_method == 'BUFFER_SHADOW': +# if mode & Blender.Lamp.Modes.RayShadow or mode & Blender.Lamp.Modes.Shadows: do_shadow = 1 else: do_shadow = 0 - - if mode & Blender.Lamp.Modes.OnlyShadow or (mode & Blender.Lamp.Modes.NoDiffuse and mode & Blender.Lamp.Modes.NoSpecular): + + if light.only_shadow or (not light.diffuse and not light.specular): +# if mode & Blender.Lamp.Modes.OnlyShadow or (mode & Blender.Lamp.Modes.NoDiffuse and mode & Blender.Lamp.Modes.NoSpecular): do_light = 0 else: do_light = 1 @@ -1042,11 +1064,16 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t\tProperty: "GoboProperty", "object", ""') file.write('\n\t\t\tProperty: "Color", "Color", "A+",1,1,1') file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy*100, 200))) # clamp below 200 - file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale)) + if light.type == 'SPOT': + file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spot_size * scale)) +# file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale)) file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50') - file.write('\n\t\t\tProperty: "Color", "Color", "A",%.2f,%.2f,%.2f' % tuple(light.col)) + file.write('\n\t\t\tProperty: "Color", "Color", "A",%.2f,%.2f,%.2f' % tuple(light.color)) +# file.write('\n\t\t\tProperty: "Color", "Color", "A",%.2f,%.2f,%.2f' % tuple(light.col)) file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy*100, 200))) # clamp below 200 - file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale)) +# + # duplication? see ^ (Arystan) +# file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale)) file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50') file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type) file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",%i' % do_light) @@ -1055,7 +1082,8 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1') file.write('\n\t\t\tProperty: "GoboProperty", "object", ""') file.write('\n\t\t\tProperty: "DecayType", "enum", "",0') - file.write('\n\t\t\tProperty: "DecayStart", "double", "",%.2f' % light.dist) + file.write('\n\t\t\tProperty: "DecayStart", "double", "",%.2f' % light.distance) +# file.write('\n\t\t\tProperty: "DecayStart", "double", "",%.2f' % light.dist) file.write('\n\t\t\tProperty: "EnableNearAttenuation", "bool", "",0') file.write('\n\t\t\tProperty: "NearAttenuationStart", "double", "",0') file.write('\n\t\t\tProperty: "NearAttenuationEnd", "double", "",0') @@ -1101,7 +1129,8 @@ def write(filename, batch_objects = None, \ }''') # Material Settings - if world: world_amb = world.getAmb() + if world: world_amb = tuple(world.ambient_color) +# if world: world_amb = world.getAmb() else: world_amb = (0,0,0) # Default value def write_material(matname, mat): @@ -1109,22 +1138,31 @@ def write(filename, batch_objects = None, \ # Todo, add more material Properties. if mat: - mat_cold = tuple(mat.rgbCol) - mat_cols = tuple(mat.specCol) + mat_cold = tuple(mat.diffuse_color) +# mat_cold = tuple(mat.rgbCol) + mat_cols = tuple(mat.specular_color) +# mat_cols = tuple(mat.specCol) #mat_colm = tuple(mat.mirCol) # we wont use the mirror color - mat_colamb = tuple([c for c in world_amb]) - - mat_dif = mat.ref - mat_amb = mat.amb - mat_hard = (float(mat.hard)-1)/5.10 - mat_spec = mat.spec/2.0 + mat_colamb = world_amb +# mat_colamb = tuple([c for c in world_amb]) + + mat_dif = mat.diffuse_reflection +# mat_dif = mat.ref + mat_amb = mat.ambient +# mat_amb = mat.amb + mat_hard = (float(mat.specular_hardness)-1)/5.10 +# mat_hard = (float(mat.hard)-1)/5.10 + mat_spec = mat.specular_reflection/2.0 +# mat_spec = mat.spec/2.0 mat_alpha = mat.alpha mat_emit = mat.emit - mat_shadeless = mat.mode & Blender.Material.Modes.SHADELESS + mat_shadeless = mat.shadeless +# mat_shadeless = mat.mode & Blender.Material.Modes.SHADELESS if mat_shadeless: mat_shader = 'Lambert' else: - if mat.diffuseShader == Blender.Material.Shaders.DIFFUSE_LAMBERT: + if mat.diffuse_shader == 'LAMBERT': +# if mat.diffuseShader == Blender.Material.Shaders.DIFFUSE_LAMBERT: mat_shader = 'Lambert' else: mat_shader = 'Phong' @@ -3102,11 +3140,16 @@ if __name__ == '__main__': write_ui() # NOTES (all line numbers correspond to original export_fbx.py (under release/scripts) -# # - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print # - get rid of cleanName somehow -# - new scene creation, activation: lines 327-342, 368 +# - isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565 # TODO -# bpy.data.remove_scene: line 366 +# - bpy.data.remove_scene: line 366 +# - bpy.sys.time move to bpy.sys.util? +# - new scene creation, activation: lines 327-342, 368 +# - uses bpy.sys.expandpath, *.relpath - replace at least relpath + +# SMALL or COSMETICAL +# - find a way to get blender version, and put it in bpy.util?, old was Blender.Get('version') diff --git a/release/io/export_obj.py b/release/io/export_obj.py index 26e6e248a45..51d0403cdae 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -389,16 +389,13 @@ def write(filename, objects, scene, if ob_main.dupli_type != 'NONE': obs = [(dob.object, dob.matrix) for dob in ob_main.dupli_list] - # XXX + # XXX debug print print(ob_main.name, 'has', len(obs), 'dupli children') else: obs = [(ob_main, ob_main.matrix)] for ob, ob_mat in obs: - if EXPORT_ROTX90: - ob_mat = ob_mat * mat_xrot90 - # XXX postponed # # Nurbs curve support # if EXPORT_CURVE_AS_NURBS and test_nurbs_compat(ob): @@ -418,7 +415,10 @@ def write(filename, objects, scene, else: me = ob.data.create_copy() - me.transform(ob_mat) + if EXPORT_ROTX90: + me.transform(ob_mat * mat_xrot90) + else: + me.transform(ob_mat) # # Will work for non meshes now! :) # me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, EXPORT_POLYGROUPS, scn) @@ -961,5 +961,5 @@ if __name__ == "__main__": # - duplis - only tested dupliverts # - NURBS - needs API additions # - all scenes export -# - normals calculation +# + normals calculation # - get rid of cleanName somehow diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 8a9fdb8531d..fb2d052e241 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1920,7 +1920,7 @@ RNAProcessItem PROCESS_ITEMS[]= { {"rna_object_force.c", NULL, RNA_def_object_force}, {"rna_packedfile.c", NULL, RNA_def_packedfile}, {"rna_particle.c", NULL, RNA_def_particle}, - {"rna_pose.c", NULL, RNA_def_pose}, + {"rna_pose.c", "rna_pose_api.c", RNA_def_pose}, {"rna_property.c", NULL, RNA_def_gameproperty}, {"rna_scene.c", "rna_scene_api.c", RNA_def_scene}, {"rna_screen.c", NULL, RNA_def_screen}, diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index dc519e9ec6e..7d8391132b1 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -610,6 +610,7 @@ static void rna_def_bone(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_SELECTED); RNA_def_property_ui_text(prop, "Selected", ""); + /* XXX better matrix descriptions possible (Arystan) */ prop= RNA_def_property(srna, "matrix", PROP_FLOAT, PROP_MATRIX); RNA_def_property_float_sdna(prop, NULL, "bone_mat"); RNA_def_property_array(prop, 9); @@ -618,7 +619,27 @@ static void rna_def_bone(BlenderRNA *brna) prop= RNA_def_property(srna, "armature_matrix", PROP_FLOAT, PROP_MATRIX); RNA_def_property_float_sdna(prop, NULL, "arm_mat"); RNA_def_property_array(prop, 16); - RNA_def_property_ui_text(prop, "Bone Armature Matrix", "4x4 bone matrix relative to armature."); + RNA_def_property_ui_text(prop, "Bone Armature-Relative Matrix", "4x4 bone matrix relative to armature."); + + prop= RNA_def_property(srna, "tail", PROP_FLOAT, PROP_VECTOR); + RNA_def_property_float_sdna(prop, NULL, "tail"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Tail", "Location of tail end of the bone."); + + prop= RNA_def_property(srna, "armature_tail", PROP_FLOAT, PROP_VECTOR); + RNA_def_property_float_sdna(prop, NULL, "arm_tail"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Armature-Relative Tail", "Location of tail end of the bone relative to armature."); + + prop= RNA_def_property(srna, "head", PROP_FLOAT, PROP_VECTOR); + RNA_def_property_float_sdna(prop, NULL, "head"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Head", "Location of head end of the bone."); + + prop= RNA_def_property(srna, "armature_head", PROP_FLOAT, PROP_VECTOR); + RNA_def_property_float_sdna(prop, NULL, "arm_head"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Armature-Relative Head", "Location of head end of the bone relative to armature."); } static void rna_def_edit_bone(BlenderRNA *brna) From f221d9128ddf593f7977251ed4214cfe682d7c40 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Sat, 11 Jul 2009 11:58:50 +0000 Subject: [PATCH 41/68] Added Scene.set_frame RNA function to get immediate scene update. Changing Scene.current_frame doesn't work in this case because it adds a notifier. Also added RNA_property_update call in pyrna_struct_setattro so that notifiers are added when a property changes. --- release/io/export_fbx.py | 6 ++- release/scripts/export_fbx.py | 3 +- source/blender/makesrna/intern/makesrna.c | 2 +- source/blender/makesrna/intern/rna_action.c | 2 + .../blender/makesrna/intern/rna_action_api.c | 48 +++++++++++++++++++ source/blender/makesrna/intern/rna_pose_api.c | 2 - .../blender/makesrna/intern/rna_scene_api.c | 16 +++++++ source/blender/python/intern/bpy_rna.c | 7 ++- 8 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 source/blender/makesrna/intern/rna_action_api.c diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index 59480c4d455..62c8e0daaaf 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -2500,7 +2500,8 @@ Connections: {''') if ANIM_ENABLE and [tmp for tmp in ob_anim_lists if tmp]: - frame_orig = Blender.Get('curframe') + frame_orig = sce.current_frame +# frame_orig = Blender.Get('curframe') if ANIM_OPTIMIZE: ANIM_OPTIMIZE_PRECISSION_FLOAT = 0.1 ** ANIM_OPTIMIZE_PRECISSION @@ -2613,7 +2614,8 @@ Takes: {''') ''' i = act_start while i <= act_end: - Blender.Set('curframe', i) + sce.set_frame(i) +# Blender.Set('curframe', i) for ob_generic in ob_anim_lists: for my_ob in ob_generic: #Blender.Window.RedrawAll() diff --git a/release/scripts/export_fbx.py b/release/scripts/export_fbx.py index 4115140a852..bac4c6cbcc4 100644 --- a/release/scripts/export_fbx.py +++ b/release/scripts/export_fbx.py @@ -2445,7 +2445,8 @@ Connections: {''') if ANIM_ENABLE and [tmp for tmp in ob_anim_lists if tmp]: - frame_orig = Blender.Get('curframe') + frame_orig = sce.current_frame +# frame_orig = Blender.Get('curframe') if ANIM_OPTIMIZE: ANIM_OPTIMIZE_PRECISSION_FLOAT = 0.1 ** ANIM_OPTIMIZE_PRECISSION diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index d7c9ffe9800..7a8dfe78ca1 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1897,7 +1897,7 @@ RNAProcessItem PROCESS_ITEMS[]= { {"rna_rna.c", NULL, RNA_def_rna}, {"rna_ID.c", NULL, RNA_def_ID}, {"rna_texture.c", NULL, RNA_def_texture}, - {"rna_action.c", NULL, RNA_def_action}, + {"rna_action.c", "rna_action_api.c", RNA_def_action}, {"rna_animation.c", NULL, RNA_def_animation}, {"rna_actuator.c", NULL, RNA_def_actuator}, {"rna_armature.c", NULL, RNA_def_armature}, diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c index 3639d6d3fff..bc59e06a978 100644 --- a/source/blender/makesrna/intern/rna_action.c +++ b/source/blender/makesrna/intern/rna_action.c @@ -100,6 +100,8 @@ void rna_def_action(BlenderRNA *brna) RNA_def_property_collection_sdna(prop, NULL, "markers", NULL); RNA_def_property_struct_type(prop, "TimelineMarker"); RNA_def_property_ui_text(prop, "Pose Markers", "Markers specific to this Action, for labeling poses."); + + RNA_api_action(srna); } /* --------- */ diff --git a/source/blender/makesrna/intern/rna_action_api.c b/source/blender/makesrna/intern/rna_action_api.c new file mode 100644 index 00000000000..a758a2c56d0 --- /dev/null +++ b/source/blender/makesrna/intern/rna_action_api.c @@ -0,0 +1,48 @@ +/** + * ***** 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) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include "RNA_define.h" +#include "RNA_types.h" + +#include "DNA_action_types.h" + +#ifdef RNA_RUNTIME + +#else + +void RNA_api_action(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *parm; + +} + +#endif diff --git a/source/blender/makesrna/intern/rna_pose_api.c b/source/blender/makesrna/intern/rna_pose_api.c index c0bf3339f4f..9ac7713b2e9 100644 --- a/source/blender/makesrna/intern/rna_pose_api.c +++ b/source/blender/makesrna/intern/rna_pose_api.c @@ -1,6 +1,4 @@ /** - * - * * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index a38622b48ae..f3cbb630df5 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -33,6 +33,7 @@ #include "RNA_types.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" #ifdef RNA_RUNTIME @@ -67,6 +68,15 @@ static void rna_Scene_remove_object(Scene *sce, ReportList *reports, Object *ob) ED_base_object_free_and_unlink(sce, base); } +static void rna_Scene_set_frame(Scene *sce, bContext *C, int frame) +{ + sce->r.cfra= frame; + CLAMP(sce->r.cfra, MINAFRAME, MAXFRAME); + scene_update_for_newframe(sce, (1<<20) - 1); + + WM_event_add_notifier(C, NC_SCENE|ND_FRAME, sce); +} + #else void RNA_api_scene(StructRNA *srna) @@ -85,6 +95,12 @@ void RNA_api_scene(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_REPORTS); parm= RNA_def_pointer(func, "object", "Object", "", "Object to remove from scene."); RNA_def_property_flag(parm, PROP_REQUIRED); + + func= RNA_def_function(srna, "set_frame", "rna_Scene_set_frame"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_ui_description(func, "Set scene frame updating all objects immediately."); + parm= RNA_def_int(func, "frame", 0, MINAFRAME, MAXFRAME, "", "Frame number to set.", MINAFRAME, MAXFRAME); + RNA_def_property_flag(parm, PROP_REQUIRED); } #endif diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index ef504b6fbd8..9619d6fbbbb 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -1311,6 +1311,7 @@ static int pyrna_struct_setattro( BPy_StructRNA * self, PyObject *pyname, PyObje { char *name = _PyUnicode_AsString(pyname); PropertyRNA *prop = RNA_struct_find_property(&self->ptr, name); + int ret; if (prop==NULL) { if (!BPy_StructRNA_CheckExact(self) && PyObject_GenericSetAttr((PyObject *)self, pyname, value) >= 0) { @@ -1328,7 +1329,11 @@ static int pyrna_struct_setattro( BPy_StructRNA * self, PyObject *pyname, PyObje } /* pyrna_py_to_prop sets its own exceptions */ - return pyrna_py_to_prop(&self->ptr, prop, NULL, value); + ret= pyrna_py_to_prop(&self->ptr, prop, NULL, value); + + RNA_property_update(BPy_GetContext(), &self->ptr, prop); + + return ret; } static PyObject *pyrna_prop_keys(BPy_PropertyRNA *self) From 870a9d61042d8badb02673678dd1ed32a6ee994d Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Sat, 11 Jul 2009 16:11:26 +0000 Subject: [PATCH 42/68] RNA functions can now return dynamic arrays (float, int/boolean). RNA handles freeing memory. Example code: http://pastebin.mozilla.org/662154. --- source/blender/makesrna/RNA_types.h | 3 +++ source/blender/makesrna/intern/makesrna.c | 24 ++++++++++++++++--- source/blender/makesrna/intern/rna_access.c | 7 +++++- .../blender/makesrna/intern/rna_action_api.c | 15 ++++++++++++ source/blender/makesrna/intern/rna_define.c | 2 ++ source/blender/python/SConscript | 2 +- source/blender/python/intern/bpy_rna.c | 6 +++++ 7 files changed, 54 insertions(+), 5 deletions(-) diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index dc2a2a1a1de..ed6f12681cc 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -106,6 +106,9 @@ typedef enum PropertyFlag { /* pointers */ PROP_ID_REFCOUNT = 64, + /* arrays */ + PROP_DYNAMIC_ARRAY = 32768, + /* internal flags */ PROP_BUILTIN = 128, PROP_EXPORT = 256, diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 7a8dfe78ca1..5462c005bcf 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1200,6 +1200,7 @@ static void rna_def_function_funcs(FILE *f, StructDefRNA *dsrna, FunctionDefRNA fprintf(f, "\tchar *_data"); if(func->ret) fprintf(f, ", *_retdata"); fprintf(f, ";\n"); + if(func->ret && (func->ret->flag & PROP_DYNAMIC_ARRAY)) fprintf(f, "\tint _ret_array_length;\n"); fprintf(f, "\t\n"); /* assign self */ @@ -1254,6 +1255,12 @@ static void rna_def_function_funcs(FILE *f, StructDefRNA *dsrna, FunctionDefRNA fprintf(f, "reports"); } + if(func->ret && (func->ret->flag & PROP_DYNAMIC_ARRAY)) { + if(!first) fprintf(f, ", "); + first= 0; + fprintf(f, "&_ret_array_length"); + } + dparm= dfunc->cont.properties.first; for(; dparm; dparm= dparm->next) { if(dparm->prop==func->ret) @@ -1271,6 +1278,10 @@ static void rna_def_function_funcs(FILE *f, StructDefRNA *dsrna, FunctionDefRNA dparm= rna_find_parameter_def(func->ret); ptrstr= dparm->prop->type == PROP_POINTER || dparm->prop->arraylength > 0 ? "*" : ""; fprintf(f, "\t*((%s%s%s*)_retdata)= %s;\n", rna_type_struct(dparm->prop), rna_parameter_type_name(dparm->prop), ptrstr, func->ret->identifier); + + if(func->ret && (func->ret->flag & PROP_DYNAMIC_ARRAY)) { + fprintf(f, "\t_parms->func->ret->arraylength= _ret_array_length;\n"); + } } } @@ -1476,9 +1487,9 @@ static void rna_generate_static_parameter_prototypes(BlenderRNA *brna, StructRNA /* return type */ for(dparm= dfunc->cont.properties.first; dparm; dparm= dparm->next) { if(dparm->prop==func->ret) { - if(dparm->prop->arraylength) - fprintf(f, "XXX no array return types yet"); /* XXX not supported */ - else if(dparm->prop->type == PROP_POINTER) + if(dparm->prop->arraylength && !(dparm->prop->flag & PROP_DYNAMIC_ARRAY)) + fprintf(f, "\"XXX array return types only allowed with PROP_DYNAMIC_ARRAY flag.\""); /* XXX not supported */ + else if(dparm->prop->type == PROP_POINTER || (dparm->prop->flag & PROP_DYNAMIC_ARRAY)) fprintf(f, "%s%s *", rna_type_struct(dparm->prop), rna_parameter_type_name(dparm->prop)); else fprintf(f, "%s%s ", rna_type_struct(dparm->prop), rna_parameter_type_name(dparm->prop)); @@ -1515,6 +1526,13 @@ static void rna_generate_static_parameter_prototypes(BlenderRNA *brna, StructRNA fprintf(f, "ReportList *reports"); } + /* dynamic array length paramter */ + if(func->ret && (func->ret->flag & PROP_DYNAMIC_ARRAY)) { + if(!first) fprintf(f, ", "); + first= 0; + fprintf(f, "int *array_length"); + } + /* defined parameters */ for(dparm= dfunc->cont.properties.first; dparm; dparm= dparm->next) { if(dparm->prop==func->ret) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index f20df81d6ad..b312b0b8e51 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -2799,8 +2799,13 @@ void RNA_parameter_list_free(ParameterList *parms) parm= parms->func->cont.properties.first; for(tot= 0; parm; parm= parm->next) { - if(parm->type == PROP_COLLECTION) + if(parm->type == PROP_COLLECTION) { BLI_freelistN((ListBase*)((char*)parms->data+tot)); + } + else if(parm->flag & PROP_DYNAMIC_ARRAY) { + /* for dynamic arrays, data is a pointer to an array */ + MEM_freeN(*(char**)parms->data+tot); + } tot+= rna_parameter_size(parm); } diff --git a/source/blender/makesrna/intern/rna_action_api.c b/source/blender/makesrna/intern/rna_action_api.c index a758a2c56d0..b989d2d66a9 100644 --- a/source/blender/makesrna/intern/rna_action_api.c +++ b/source/blender/makesrna/intern/rna_action_api.c @@ -36,6 +36,16 @@ #ifdef RNA_RUNTIME +int *rna_Action_get_frames(bAction *act, int *ret_length) +{ + *ret_length= 3; + int *ret= MEM_callocN(*ret_length * sizeof(int), "action frames"); + ret[0] = 1; + ret[1] = 2; + ret[2] = 3; + return ret; +} + #else void RNA_api_action(StructRNA *srna) @@ -43,6 +53,11 @@ void RNA_api_action(StructRNA *srna) FunctionRNA *func; PropertyRNA *parm; + func= RNA_def_function(srna, "get_frames", "rna_Action_get_frames"); + RNA_def_function_ui_description(func, "Get action frames."); /* XXX describe better */ + parm= RNA_def_int_array(func, "frames", 1, NULL, 0, 0, "", "", 0, 0); + RNA_def_property_flag(parm, PROP_DYNAMIC_ARRAY); + RNA_def_function_return(func, parm); } #endif diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 07515d3ad56..d0a2de515ed 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -2220,6 +2220,8 @@ int rna_parameter_size(PropertyRNA *parm) PropertyType ptype= parm->type; int len= parm->arraylength; + if (parm->flag & PROP_DYNAMIC_ARRAY) return sizeof(void*); + if(len > 0) { switch (ptype) { case PROP_BOOLEAN: diff --git a/source/blender/python/SConscript b/source/blender/python/SConscript index 73dc171fc3e..0ef6689de7e 100644 --- a/source/blender/python/SConscript +++ b/source/blender/python/SConscript @@ -3,7 +3,7 @@ Import ('env') sources = env.Glob('intern/*.c') -incs = '. ../editors/include ../makesdna ../makesrna ../blenlib ../blenkernel ../nodes' +incs = '. ../editors/include ../makesdna ../makesrna ../makesrna/intern ../blenlib ../blenkernel ../nodes' incs += ' ../imbuf ../blenloader ../render/extern/include ../windowmanager' incs += ' #intern/guardedalloc #intern/memutil #extern/glew/include' incs += ' ' + env['BF_PYTHON_INC'] diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 9619d6fbbbb..fe68436c07f 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -44,6 +44,8 @@ #include "DNA_scene_types.h" #include "ED_keyframing.h" +#include "rna_internal_types.h" /* PropertyRNA */ + #define USE_MATHUTILS #ifdef USE_MATHUTILS @@ -1792,6 +1794,10 @@ PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *data) /* resolve the array from a new pytype */ ret = PyTuple_New(len); + /* for return values, data is a pointer to an array, not first element pointer */ + if (prop->flag & PROP_DYNAMIC_ARRAY) + data = *((char**)data); + switch (type) { case PROP_BOOLEAN: for(a=0; a Date: Sun, 12 Jul 2009 06:59:57 +0000 Subject: [PATCH 43/68] Added Action.get_frame_range returning a (min, max) pair or (0, 0) if there are no keys. --- release/io/export_fbx.py | 23 +++++----- .../blender/makesrna/intern/rna_action_api.c | 45 +++++++++++++++---- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index 62c8e0daaaf..063a52b2f7d 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -2577,17 +2577,18 @@ Takes: {''') file.write('\n\tTake: "%s" {' % sane_name_mapping_take[blenAction.name]) else: file.write('\n\tTake: "%s" {' % sane_takename(blenAction)) - - tmp = blenAction.getFrameNumbers() - if tmp: - act_start = min(tmp) - act_end = max(tmp) - del tmp - else: - # Fallback on this, theres not much else we can do? :/ - # when an action has no length - act_start = start - act_end = end + + act_start, act_end = blenAction.get_frame_range() +# tmp = blenAction.getFrameNumbers() +# if tmp: +# act_start = min(tmp) +# act_end = max(tmp) +# del tmp +# else: +# # Fallback on this, theres not much else we can do? :/ +# # when an action has no length +# act_start = start +# act_end = end # Set the action active for my_bone in ob_arms: diff --git a/source/blender/makesrna/intern/rna_action_api.c b/source/blender/makesrna/intern/rna_action_api.c index b989d2d66a9..9f63d44f8ef 100644 --- a/source/blender/makesrna/intern/rna_action_api.c +++ b/source/blender/makesrna/intern/rna_action_api.c @@ -36,13 +36,40 @@ #ifdef RNA_RUNTIME -int *rna_Action_get_frames(bAction *act, int *ret_length) +#include "DNA_anim_types.h" +#include "DNA_curve_types.h" + +/* return frame range of all curves (min, max) or (0, 0) if there are no keys */ +int *rna_Action_get_frame_range(bAction *act, int *ret_length) { - *ret_length= 3; - int *ret= MEM_callocN(*ret_length * sizeof(int), "action frames"); - ret[0] = 1; - ret[1] = 2; - ret[2] = 3; + FCurve *cu; + int *ret; + + *ret_length= 2; + ret= MEM_callocN(*ret_length * sizeof(int), "rna_Action_get_frame_range"); + + ret[0]= 0; + ret[1]= 0; + + for (cu= act->curves.first; cu; cu= cu->next) { + int verts= cu->totvert; + BezTriple *bezt= cu->bezt; + while (verts) { + int frame= (int)bezt->vec[1][0]; + + if (cu == act->curves.first && verts == cu->totvert) + ret[0]= ret[1]= frame; + + if (frame < ret[0]) + ret[0]= frame; + if (frame > ret[1]) + ret[1]= frame; + + bezt++; + verts--; + } + } + return ret; } @@ -53,9 +80,9 @@ void RNA_api_action(StructRNA *srna) FunctionRNA *func; PropertyRNA *parm; - func= RNA_def_function(srna, "get_frames", "rna_Action_get_frames"); - RNA_def_function_ui_description(func, "Get action frames."); /* XXX describe better */ - parm= RNA_def_int_array(func, "frames", 1, NULL, 0, 0, "", "", 0, 0); + func= RNA_def_function(srna, "get_frame_range", "rna_Action_get_frame_range"); + RNA_def_function_ui_description(func, "Get action frame range as a (min, max) tuple."); + parm= RNA_def_int_array(func, "frame_range", 1, NULL, 0, 0, "", "Action frame range.", 0, 0); RNA_def_property_flag(parm, PROP_DYNAMIC_ARRAY); RNA_def_function_return(func, parm); } From ffa5636a8b348d47d08b76644ec567d90cc9d3c5 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Sun, 12 Jul 2009 07:20:49 +0000 Subject: [PATCH 44/68] Reusing existing blenkernel function calc_action_range for Action.get_frame_range. --- .../blender/makesrna/intern/rna_action_api.c | 29 ++++--------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/source/blender/makesrna/intern/rna_action_api.c b/source/blender/makesrna/intern/rna_action_api.c index 9f63d44f8ef..2b51a424fcf 100644 --- a/source/blender/makesrna/intern/rna_action_api.c +++ b/source/blender/makesrna/intern/rna_action_api.c @@ -39,36 +39,19 @@ #include "DNA_anim_types.h" #include "DNA_curve_types.h" -/* return frame range of all curves (min, max) or (0, 0) if there are no keys */ +/* return frame range of all curves (min, max) or (0, 1) if there are no keys */ int *rna_Action_get_frame_range(bAction *act, int *ret_length) { - FCurve *cu; int *ret; + float start, end; + + calc_action_range(act, &start, &end, 1); *ret_length= 2; ret= MEM_callocN(*ret_length * sizeof(int), "rna_Action_get_frame_range"); - ret[0]= 0; - ret[1]= 0; - - for (cu= act->curves.first; cu; cu= cu->next) { - int verts= cu->totvert; - BezTriple *bezt= cu->bezt; - while (verts) { - int frame= (int)bezt->vec[1][0]; - - if (cu == act->curves.first && verts == cu->totvert) - ret[0]= ret[1]= frame; - - if (frame < ret[0]) - ret[0]= frame; - if (frame > ret[1]) - ret[1]= frame; - - bezt++; - verts--; - } - } + ret[0]= (int)start; + ret[1]= (int)end; return ret; } From fb150e12c560acd1418c106dbbe94f757731db09 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Mon, 13 Jul 2009 11:04:18 +0000 Subject: [PATCH 45/68] - FBX exporter conversion continued - added (uncommented actually) PoseChannel.pose_matrix - added Object.dag_update that calls DAG_object_flush_update. I'm not sure if it's needed, but FBX exporter uses it. --- release/io/export_fbx.py | 174 ++++++++++++------ .../blender/makesrna/intern/rna_object_api.c | 25 ++- source/blender/makesrna/intern/rna_pose.c | 9 +- 3 files changed, 147 insertions(+), 61 deletions(-) diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index 063a52b2f7d..0a6588c380b 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -37,12 +37,13 @@ http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx # -------------------------------------------------------------------------- import os +import time -try: - import time - # import os # only needed for batch export, nbot used yet -except: - time = None # use this to check if they have python modules installed +# try: +# import time +# # import os # only needed for batch export, nbot used yet +# except: +# time = None # use this to check if they have python modules installed # for python 2.3 support try: @@ -114,10 +115,41 @@ def copy_images(dest_dir, textures): print '\tCopied %d images' % copyCount -mtx4_identity = Matrix() +def BPyObject_getObjectArmature(ob): + ''' + This returns the first armature the mesh uses. + remember there can be more then 1 armature but most people dont do that. + ''' + if ob.type != 'MESH': + return None + + arm = ob.parent + if arm and arm.type == 'ARMATURE' and ob.parent_type == 'ARMATURE': + return arm + + for m in ob.modifiers: + if m.type== 'ARMATURE': + arm = m.object + if arm: + return arm + + return None + +# I guess FBX uses degrees instead of radians (Arystan). +# Call this function just before writing to FBX. +def eulerRadToDeg(eul): + ret = Mathutils.Euler() + + ret.x = 180 / math.pi * eul.x + ret.y = 180 / math.pi * eul.y + ret.z = 180 / math.pi * eul.z + + return ret + +mtx4_identity = Mathutils.Matrix() # testing -mtx_x90 = RotationMatrix( 90, 3, 'x') # used +mtx_x90 = Mathutils.RotationMatrix( math.pi/2, 3, 'x') # used #mtx_x90n = RotationMatrix(-90, 3, 'x') #mtx_y90 = RotationMatrix( 90, 3, 'y') #mtx_y90n = RotationMatrix(-90, 3, 'y') @@ -125,14 +157,14 @@ mtx_x90 = RotationMatrix( 90, 3, 'x') # used #mtx_z90n = RotationMatrix(-90, 3, 'z') #mtx4_x90 = RotationMatrix( 90, 4, 'x') -mtx4_x90n = RotationMatrix(-90, 4, 'x') # used +mtx4_x90n = Mathutils.RotationMatrix(-math.pi/2, 4, 'x') # used #mtx4_y90 = RotationMatrix( 90, 4, 'y') -mtx4_y90n = RotationMatrix(-90, 4, 'y') # used -mtx4_z90 = RotationMatrix( 90, 4, 'z') # used -mtx4_z90n = RotationMatrix(-90, 4, 'z') # used +mtx4_y90n = Mathutils.RotationMatrix(-math.pi/2, 4, 'y') # used +mtx4_z90 = Mathutils.RotationMatrix( math.pi/2, 4, 'z') # used +mtx4_z90n = Mathutils.RotationMatrix(-math.pi/2, 4, 'z') # used -def strip_path(p): - return p.split('\\')[-1].split('/')[-1] +# def strip_path(p): +# return p.split('\\')[-1].split('/')[-1] # Used to add the scene name into the filename without using odd chars sane_name_mapping_ob = {} @@ -212,7 +244,8 @@ def derived_paths(fname_orig, basepath, FORCE_CWD=False): ''' fname = bpy.sys.expandpath(fname_orig) # fname = Blender.sys.expandpath(fname_orig) - fname_strip = strip_path(fname) + fname_strip = os.path.basename(fname) +# fname_strip = strip_path(fname) if FORCE_CWD: fname_rel = '.' + os.sep + fname_strip else: @@ -265,7 +298,7 @@ def write(filename, batch_objects = None, \ EXP_CAMERA = True, EXP_EMPTY = True, EXP_IMAGE_COPY = False, - GLOBAL_MATRIX = Matrix(), + GLOBAL_MATRIX = Mathutils.Matrix(), ANIM_ENABLE = True, ANIM_OPTIMIZE = True, ANIM_OPTIMIZE_PRECISSION = 6, @@ -288,9 +321,11 @@ def write(filename, batch_objects = None, \ # tmp_exists = Blender.sys.exists(fbxpath) if tmp_exists != 2: # a file, we want a path - while fbxpath and fbxpath[-1] not in ('/', '\\'): - fbxpath = fbxpath[:-1] - if not filename: + fbxpath = os.path.dirname(fbxpath) +# while fbxpath and fbxpath[-1] not in ('/', '\\'): +# fbxpath = fbxpath[:-1] + if not fbxpath: +# if not filename: # XXX print('Error%t|Directory does not exist!') # Draw.PupMenu('Error%t|Directory does not exist!') @@ -391,7 +426,9 @@ def write(filename, batch_objects = None, \ # end batch support # Use this for working out paths relative to the export location - basepath = Blender.sys.dirname(filename) + basepath = os.path.dirname(filename) or '.' + basepath += os.sep +# basepath = Blender.sys.dirname(filename) # ---------------------------------------------- # storage classes @@ -429,7 +466,8 @@ def write(filename, batch_objects = None, \ # not public pose = fbxArm.blenObject.pose # pose = fbxArm.blenObject.getPose() - self.__pose_bone = pose.bones[self.blenName] + self.__pose_bone = pose.pose_channels[self.blenName] +# self.__pose_bone = pose.bones[self.blenName] # store a list if matricies here, (poseMatrix, head, tail) # {frame:posematrix, frame:posematrix, ...} @@ -452,8 +490,9 @@ def write(filename, batch_objects = None, \ self.__pose_bone.head.copy(),\ self.__pose_bone.tail.copy() ) ''' - - self.__anim_poselist[f] = self.__pose_bone.poseMatrix.copy() + + self.__anim_poselist[f] = self.__pose_bone.pose_matrix.copy() +# self.__anim_poselist[f] = self.__pose_bone.poseMatrix.copy() # get pose from frame. def getPoseMatrix(self, f):# ---------------------------------------------- @@ -521,11 +560,12 @@ def write(filename, batch_objects = None, \ matrix_rot = (self.__anim_poselist[frame] * GLOBAL_MATRIX).rotationPart() # Lamps need to be rotated - if type =='Lamp': + if type =='LAMP': matrix_rot = mtx_x90 * matrix_rot - elif ob and type =='Camera': + elif type =='CAMERA': +# elif ob and type =='Camera': y = Vector(0,1,0) * matrix_rot - matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y) + matrix_rot = matrix_rot * Mathutils.RotationMatrix(math.pi/2, 3, 'r', y) return matrix_rot @@ -627,7 +667,7 @@ def write(filename, batch_objects = None, \ rot = tuple(matrix_rot.toEuler()) elif ob and ob.type =='Camera': y = Vector(0,1,0) * matrix_rot - matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y) + matrix_rot = matrix_rot * Mathutils.RotationMatrix(math.pi/2, 3, 'r', y) rot = tuple(matrix_rot.toEuler()) else: rot = tuple(matrix_rot.toEuler()) @@ -648,7 +688,8 @@ def write(filename, batch_objects = None, \ loc, rot, scale, matrix, matrix_rot = object_tx(ob, loc, matrix, matrix_mod) file.write('\n\t\t\tProperty: "Lcl Translation", "Lcl Translation", "A+",%.15f,%.15f,%.15f' % loc) - file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % rot) + file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % eulerRadToDeg(rot)) +# file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % rot) file.write('\n\t\t\tProperty: "Lcl Scaling", "Lcl Scaling", "A+",%.15f,%.15f,%.15f' % scale) return loc, rot, scale, matrix, matrix_rot @@ -735,7 +776,8 @@ def write(filename, batch_objects = None, \ Property: "Show", "bool", "",1 Property: "NegativePercentShapeSupport", "bool", "",1 Property: "DefaultAttributeIndex", "int", "",0''') - if ob and type(ob) != Blender.Types.BoneType: + if ob and not isinstance(ob, bpy.types.Bone): +# if ob and type(ob) != Blender.Types.BoneType: # Only mesh objects have color file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8') file.write('\n\t\t\tProperty: "Size", "double", "",100') @@ -1214,7 +1256,8 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t}') file.write('\n\t}') - + + # tex is an Image (Arystan) def write_video(texname, tex): # Same as texture really! file.write('\n\tVideo: "Video::%s", "Clip" {' % texname) @@ -1276,9 +1319,11 @@ def write(filename, batch_objects = None, \ Property: "UseMipMap", "bool", "",0 Property: "CurrentMappingType", "enum", "",0 Property: "UVSwap", "bool", "",0''') - - file.write('\n\t\t\tProperty: "WrapModeU", "enum", "",%i' % tex.clampX) - file.write('\n\t\t\tProperty: "WrapModeV", "enum", "",%i' % tex.clampY) + + file.write('\n\t\t\tProperty: "WrapModeU", "enum", "",%i' % tex.clamp_x) +# file.write('\n\t\t\tProperty: "WrapModeU", "enum", "",%i' % tex.clampX) + file.write('\n\t\t\tProperty: "WrapModeV", "enum", "",%i' % tex.clamp_y) +# file.write('\n\t\t\tProperty: "WrapModeV", "enum", "",%i' % tex.clampY) file.write(''' Property: "TextureRotationPivot", "Vector3D", "",0,0,0 @@ -1915,13 +1960,16 @@ def write(filename, batch_objects = None, \ else: # Mesh Type! if EXP_MESH_APPLY_MOD: - me = bpy.data.meshes.new() - me.getFromObject(ob) +# me = bpy.data.meshes.new() + me = ob.create_mesh('PREVIEW') +# me.getFromObject(ob) # so we keep the vert groups if EXP_ARMATURE: - orig_mesh = ob.getData(mesh=1) - if orig_mesh.getVertGroupNames(): + orig_mesh = ob.data +# orig_mesh = ob.getData(mesh=1) + if len(ob.vertex_groups): +# if orig_mesh.getVertGroupNames(): ob.copy().link(me) # If new mesh has no vgroups we can try add if verts are teh same if not me.getVertGroupNames(): # vgroups were not kept by the modifier @@ -1980,7 +2028,7 @@ def write(filename, batch_objects = None, \ materials[None, None] = None if EXP_ARMATURE: - armob = BPyObject.getObjectArmature(ob) + armob = BPyObject_getObjectArmature(ob) blenParentBoneName = None # parent bone - special case @@ -2014,14 +2062,17 @@ def write(filename, batch_objects = None, \ if EXP_ARMATURE: # now we have the meshes, restore the rest arm position for i, arm in enumerate(bpy.data.armatures): - arm.restPosition = ob_arms_orig_rest[i] + arm.rest_position = ob_arms_orig_rest[i] +# arm.restPosition = ob_arms_orig_rest[i] if ob_arms_orig_rest: for ob_base in bpy.data.objects: if ob_base.type == 'Armature': - ob_base.makeDisplayList() + ob_base.dag_update() +# ob_base.makeDisplayList() # This causes the makeDisplayList command to effect the mesh - Blender.Set('curframe', Blender.Get('curframe')) + sce.set_frame(sce.current_frame) +# Blender.Set('curframe', Blender.Get('curframe')) del tmp_ob_type, tmp_objects @@ -2032,7 +2083,11 @@ def write(filename, batch_objects = None, \ my_arm.fbxBones = [] my_arm.blenData = ob.data - my_arm.blenAction = ob.action + if ob.animation_data: + my_arm.blenAction = ob.animation_data.action + else: + my_arm.blenAction = None +# my_arm.blenAction = ob.action my_arm.blenActionList = [] # fbxName, blenderObject, my_bones, blenderActions @@ -2087,7 +2142,8 @@ def write(filename, batch_objects = None, \ # Build blenObject -> fbxObject mapping # this is needed for groups as well as fbxParenting - bpy.data.objects.tag = False + for ob in bpy.data.objects: ob.tag = False +# bpy.data.objects.tag = False tmp_obmapping = {} for ob_generic in ob_all_typegroups: for ob_base in ob_generic: @@ -2512,7 +2568,8 @@ Connections: {''') action_lastcompat = None if ANIM_ACTION_ALL: - bpy.data.actions.tag = False + for a in bpy.data.actions: a.tag = False +# bpy.data.actions.tag = False tmp_actions = list(bpy.data.actions) @@ -2528,8 +2585,9 @@ Connections: {''') arm_bone_names = set([my_bone.blenName for my_bone in my_arm.fbxBones]) for action in tmp_actions: - - action_chan_names = arm_bone_names.intersection( set(action.getChannelNames()) ) + + action_chan_names = arm_bone_names.intersection( set([g.name for g in action.groups]) ) +# action_chan_names = arm_bone_names.intersection( set(action.getChannelNames()) ) if action_chan_names: # at least one channel matches. my_arm.blenActionList.append(action) @@ -2661,7 +2719,8 @@ Takes: {''') for mtx in context_bone_anim_mats: if prev_eul: prev_eul = mtx[1].toEuler(prev_eul) else: prev_eul = mtx[1].toEuler() - context_bone_anim_vecs.append(prev_eul) + context_bone_anim_vecs.append(eulerRadToDeg(prev_eul)) +# context_bone_anim_vecs.append(prev_eul) file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation @@ -2752,8 +2811,9 @@ Takes: {''') my_bone.blenObject.action = my_bone.blenAction file.write('\n}') - - Blender.Set('curframe', frame_orig) + + sce.set_frame(frame_orig) +# Blender.Set('curframe', frame_orig) else: # no animation @@ -2771,15 +2831,23 @@ Takes: {''') # Clear mesh data Only when writing with modifiers applied for me in meshes_to_clear: - me.verts = None + bpy.data.remove_mesh(me) +# me.verts = None # --------------------------- Footer if world: - has_mist = world.mode & 1 - mist_intense, mist_start, mist_end, mist_height = world.mist - world_hor = world.hor + m = world.mist + has_mist = m.enabled +# has_mist = world.mode & 1 + mist_intense = m.intensity + mist_start = m.start + mist_end = m.depth + mist_height = m.height +# mist_intense, mist_start, mist_end, mist_height = world.mist + world_hor = world.horizon_color +# world_hor = world.hor else: has_mist = mist_intense = mist_start = mist_end = mist_height = 0 world_hor = 0,0,0 @@ -3146,6 +3214,8 @@ if __name__ == '__main__': # - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print # - get rid of cleanName somehow # - isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565 +# - get rid of BPyObject_getObjectArmature, move it in RNA? +# - BATCH_ENABLE and BATCH_GROUP options: line 327 # TODO diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 02db7e83062..bf1fb6d2b6a 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -166,6 +166,11 @@ static void rna_Object_add_vertex_to_group(Object *ob, int vertex_index, bDeform add_vert_to_defgroup(ob, def, vertex_index, weight, assignmode); } +static void rna_Object_dag_update(Object *ob, bContext *C) +{ + DAG_object_flush_update(CTX_data_scene(C), ob, OB_RECALC_DATA); +} + /* static void rna_Mesh_assign_verts_to_group(Object *ob, bDeformGroup *group, int *indices, int totindex, float weight, int assignmode) { @@ -222,6 +227,7 @@ void RNA_api_object(StructRNA *srna) {0, NULL, 0, NULL, NULL} }; + /* mesh */ func= RNA_def_function(srna, "create_mesh", "rna_Object_create_mesh"); RNA_def_function_ui_description(func, "Create a Mesh datablock with all modifiers applied."); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); @@ -230,6 +236,13 @@ void RNA_api_object(StructRNA *srna) parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object, remove it if it is only used for export."); RNA_def_function_return(func, parm); + func= RNA_def_function(srna, "convert_to_triface", "rna_Object_convert_to_triface"); + RNA_def_function_ui_description(func, "Convert all mesh faces to triangles."); + RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); + parm= RNA_def_pointer(func, "scene", "Scene", "", "Scene where the object belongs."); + RNA_def_property_flag(parm, PROP_REQUIRED); + + /* duplis */ func= RNA_def_function(srna, "create_dupli_list", "rna_Object_create_duplilist"); RNA_def_function_ui_description(func, "Create a list of dupli objects for this object, needs to be freed manually with free_dupli_list."); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); @@ -238,12 +251,7 @@ void RNA_api_object(StructRNA *srna) RNA_def_function_ui_description(func, "Free the list of dupli objects."); RNA_def_function_flag(func, FUNC_USE_REPORTS); - func= RNA_def_function(srna, "convert_to_triface", "rna_Object_convert_to_triface"); - RNA_def_function_ui_description(func, "Convert all mesh faces to triangles."); - RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); - parm= RNA_def_pointer(func, "scene", "Scene", "", "Scene where the object belongs."); - RNA_def_property_flag(parm, PROP_REQUIRED); - + /* vertex groups */ func= RNA_def_function(srna, "add_vertex_group", "rna_Object_add_vertex_group"); RNA_def_function_ui_description(func, "Add vertex group to object."); parm= RNA_def_string(func, "name", "Group", 0, "", "Vertex group name."); /* optional */ @@ -260,6 +268,11 @@ void RNA_api_object(StructRNA *srna) RNA_def_property_flag(parm, PROP_REQUIRED); parm= RNA_def_enum(func, "type", assign_mode_items, 0, "", "Vertex assign mode."); RNA_def_property_flag(parm, PROP_REQUIRED); + + /* DAG */ + func= RNA_def_function(srna, "dag_update", "rna_Object_dag_update"); + RNA_def_function_ui_description(func, "DAG update."); /* XXX describe better */ + RNA_def_function_flag(func, FUNC_USE_CONTEXT); } #endif diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index b8863540bdf..2561322b9a2 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -181,15 +181,18 @@ static void rna_def_pose_channel(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Channel Matrix", "4x4 matrix, before constraints.");*/ /* kaito says this should be not user-editable; I disagree; power users should be able to force this in python; he's the boss. */ -/* prop= RNA_def_property(srna, "pose_matrix", PROP_FLOAT, PROP_MATRIX); - RNA_def_property_struct_type(prop, "pose_mat"); + prop= RNA_def_property(srna, "pose_matrix", PROP_FLOAT, PROP_MATRIX); + RNA_def_property_float_sdna(prop, "pose_mat"); + RNA_def_property_array(prop, 16); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Pose Matrix", "Final 4x4 matrix for this channel."); + /* prop= RNA_def_property(srna, "constraint_inverse_matrix", PROP_FLOAT, PROP_MATRIX); RNA_def_property_struct_type(prop, "constinv"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Constraint Inverse Matrix", "4x4 matrix, defines transform from final position to unconstrained position."); */ + RNA_def_property_ui_text(prop, "Constraint Inverse Matrix", "4x4 matrix, defines transform from final position to unconstrained position."); + */ prop= RNA_def_property(srna, "pose_head", PROP_FLOAT, PROP_VECTOR); RNA_def_property_clear_flag(prop, PROP_EDITABLE); From 73ad2d320cca8c34d7901630dd8c6223ba4cd4a3 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 16 Jul 2009 08:20:15 +0000 Subject: [PATCH 46/68] More FBX exporter conversion. Experimenting with cross-compiling my branch with MinGW on Linux: - tweaked config/linuxcross.py, source/blender/makesdna/intern/SConscript and tools/Blender.py So far linking fails. --- config/linuxcross-config.py | 47 ++++-- release/io/export_fbx.py | 196 +++++++++++++++++----- release/io/export_obj.py | 8 +- source/blender/makesdna/intern/SConscript | 2 +- source/blender/makesrna/intern/rna_pose.c | 2 +- tools/Blender.py | 6 +- 6 files changed, 198 insertions(+), 63 deletions(-) diff --git a/config/linuxcross-config.py b/config/linuxcross-config.py index 5e5c44ecd69..5253110be66 100644 --- a/config/linuxcross-config.py +++ b/config/linuxcross-config.py @@ -2,13 +2,13 @@ LCGDIR = '#../lib/windows' LIBDIR = '${LCGDIR}' BF_PYTHON = LIBDIR + '/python' -BF_PYTHON_VERSION = '2.5' +BF_PYTHON_VERSION = '2.6' BF_PYTHON_INC = '${BF_PYTHON}/include/python${BF_PYTHON_VERSION}' BF_PYTHON_BINARY = 'python' -BF_PYTHON_LIB = 'python25' +BF_PYTHON_LIB = 'python26' BF_PYTHON_LIBPATH = '${BF_PYTHON}/lib' -WITH_BF_OPENAL = True +WITH_BF_OPENAL = False # XXX (Arystan) WITH_BF_STATICOPENAL = False BF_OPENAL = LIBDIR + '/openal' BF_OPENAL_INC = '${BF_OPENAL}/include' @@ -17,6 +17,12 @@ BF_OPENAL_LIBPATH = '${BF_OPENAL}/lib' # Warning, this static lib configuration is untested! users of this OS please confirm. BF_OPENAL_LIB_STATIC = '${BF_OPENAL}/lib/libopenal.a' +# copied from win32-mingw-config.py (Arystan) +WITH_BF_FFMPEG = False +BF_FFMPEG_LIB = 'avformat swscale avcodec avutil avdevice xvidcore x264' +BF_FFMPEG_LIBPATH = LIBDIR + '/gcc/ffmpeg/lib' +BF_FFMPEG_INC = LIBDIR + '/gcc/ffmpeg/include' + # Warning, this static lib configuration is untested! users of this OS please confirm. BF_CXX = '/usr' WITH_BF_STATICCXX = False @@ -33,7 +39,7 @@ BF_PTHREADS_INC = '${BF_PTHREADS}/include' BF_PTHREADS_LIB = 'pthreadGC2' BF_PTHREADS_LIBPATH = '${BF_PTHREADS}/lib' -WITH_BF_OPENEXR = True +WITH_BF_OPENEXR = False WITH_BF_STATICOPENEXR = False BF_OPENEXR = LIBDIR + '/gcc/openexr' BF_OPENEXR_INC = '${BF_OPENEXR}/include ${BF_OPENEXR}/include/OpenEXR' @@ -58,6 +64,8 @@ BF_PNG_LIBPATH = '${BF_PNG}/lib' BF_TIFF = LIBDIR + '/tiff' BF_TIFF_INC = '${BF_TIFF}/include' +BF_TIFF_LIB = 'libtiff' +BF_TIFF_LIBPATH = '${BF_TIFF}/lib' WITH_BF_ZLIB = True BF_ZLIB = LIBDIR + '/zlib' @@ -69,16 +77,26 @@ WITH_BF_INTERNATIONAL = True BF_GETTEXT = LIBDIR + '/gettext' BF_GETTEXT_INC = '${BF_GETTEXT}/include' -BF_GETTEXT_LIB = 'gnu_gettext' +BF_GETTEXT_LIB = 'gettextlib' +# BF_GETTEXT_LIB = 'gnu_gettext' BF_GETTEXT_LIBPATH = '${BF_GETTEXT}/lib' WITH_BF_GAMEENGINE = False +WITH_BF_ODE = True +BF_ODE = LIBDIR + '/ode' +BF_ODE_INC = BF_ODE + '/include' +BF_ODE_LIB = BF_ODE + '/lib/libode.a' + WITH_BF_BULLET = True BF_BULLET = '#extern/bullet2/src' BF_BULLET_INC = '${BF_BULLET}' BF_BULLET_LIB = 'extern_bullet' +BF_SOLID = '#extern/solid' +BF_SOLID_INC = '${BF_SOLID}' +BF_SOLID_LIB = 'extern_solid' + BF_WINTAB = LIBDIR + '/wintab' BF_WINTAB_INC = '${BF_WINTAB}/INCLUDE' @@ -100,9 +118,12 @@ BF_ICONV_LIBPATH = '${BF_ICONV}/lib' # Mesa Libs should go here if your using them as well.... WITH_BF_STATICOPENGL = False -BF_OPENGL = 'C:\\MingW' -BF_OPENGL_INC = '${BF_OPENGL}/include' -BF_OPENGL_LIBINC = '${BF_OPENGL}/lib' +BF_OPENGL = '' +# BF_OPENGL = 'C:\\MingW' +BF_OPENGL_INC = '' +# BF_OPENGL_INC = '${BF_OPENGL}/include' +BF_OPENGL_LIBINC = '' +# BF_OPENGL_LIBINC = '${BF_OPENGL}/lib' BF_OPENGL_LIB = 'opengl32 glu32' BF_OPENGL_LIB_STATIC = [ '${BF_OPENGL}/lib/libGL.a', '${BF_OPENGL}/lib/libGLU.a', '${BF_OPENGL}/lib/libXmu.a', '${BF_OPENGL}/lib/libXext.a', @@ -111,9 +132,13 @@ BF_OPENGL_LIB_STATIC = [ '${BF_OPENGL}/lib/libGL.a', '${BF_OPENGL}/lib/libGLU.a' CC = 'i586-mingw32msvc-gcc' CXX = 'i586-mingw32msvc-g++' +# Custom built MinGW (Arystan) +# CC = 'i586-pc-mingw32-gcc' +# CXX = 'i586-pc-mingw32-g++' + CCFLAGS = [ '-pipe', '-funsigned-char', '-fno-strict-aliasing' ] -CPPFLAGS = [ '-DXP_UNIX', '-DWIN32', '-DFREE_WINDOWS' ] +CPPFLAGS = [ '-DXP_UNIX', '-DWIN32', '-DFREE_WINDOWS', '-DLINUX_CROSS' ] CXXFLAGS = ['-pipe', '-mwindows', '-funsigned-char', '-fno-strict-aliasing' ] REL_CFLAGS = [ '-O2' ] REL_CCFLAGS = [ '-O2' ] @@ -122,7 +147,7 @@ C_WARN = [ '-Wall' , '-Wno-char-subscripts', '-Wdeclaration-after-statement' ] CC_WARN = [ '-Wall' ] -LLIBS = [ '-ldxguid', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz'] #'-lutil', '-lc', '-lm', '-ldl', '-lpthread' ] +LLIBS = [ '-ldxguid', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++'] #'-lutil', '-lc', '-lm', '-ldl', '-lpthread' ] BF_DEBUG = False BF_DEBUG_CCFLAGS= [] @@ -134,3 +159,5 @@ BF_PROFILE_LINKFLAGS = ['-pg'] BF_BUILDDIR = '../build/linuxcross' BF_INSTALLDIR='../install/linuxcross' BF_DOCDIR='../install/doc' + +# LINKFLAGS = ['-Wl,--start-group'] diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index 0a6588c380b..c4f56b472dc 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -60,9 +60,10 @@ except: # except: # os = None -import Blender +# import Blender import bpy -from Blender.Mathutils import Matrix, Vector, RotationMatrix +import Mathutils +# from Blender.Mathutils import Matrix, Vector, RotationMatrix import BPyObject import BPyMesh @@ -258,9 +259,48 @@ def derived_paths(fname_orig, basepath, FORCE_CWD=False): def mat4x4str(mat): return '%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f' % tuple([ f for v in mat for f in v ]) +# XXX not used +# duplicated in OBJ exporter +def getVertsFromGroup(me, group_index): + ret = [] + + for i, v in enumerate(me.verts): + for g in v.groups: + if g.group == group_index: + ret.append((i, g.weight)) + + return ret + +# ob must be OB_MESH +def BPyMesh_meshWeight2List(ob): + ''' Takes a mesh and return its group names and a list of lists, one list per vertex. + aligning the each vert list with the group names, each list contains float value for the weight. + These 2 lists can be modified and then used with list2MeshWeight to apply the changes. + ''' + + me = ob.data + + # Clear the vert group. + groupNames= [g.name for g in ob.vertex_groups] + len_groupNames= len(groupNames) + + if not len_groupNames: + # no verts? return a vert aligned empty list + return [[] for i in xrange(len(me.verts))], [] + else: + vWeightList= [[0.0]*len_groupNames for i in xrange(len(me.verts))] + + for i, v in enumerate(me.verts): + for g in v.groups: + vWeightList[i][g.group] = g.weight + + return groupNames, vWeightList + + def meshNormalizedWeights(me): try: # account for old bad BPyMesh - groupNames, vWeightList = BPyMesh.meshWeight2List(me) + groupNames, vWeightList = BPyMesh_meshWeight2List(me) +# groupNames, vWeightList = BPyMesh.meshWeight2List(me) except: return [],[] @@ -564,7 +604,7 @@ def write(filename, batch_objects = None, \ matrix_rot = mtx_x90 * matrix_rot elif type =='CAMERA': # elif ob and type =='Camera': - y = Vector(0,1,0) * matrix_rot + y = Mathutils.Vector(0,1,0) * matrix_rot matrix_rot = matrix_rot * Mathutils.RotationMatrix(math.pi/2, 3, 'r', y) return matrix_rot @@ -666,7 +706,7 @@ def write(filename, batch_objects = None, \ matrix_rot = mtx_x90 * matrix_rot rot = tuple(matrix_rot.toEuler()) elif ob and ob.type =='Camera': - y = Vector(0,1,0) * matrix_rot + y = Mathutils.Vector(0,1,0) * matrix_rot matrix_rot = matrix_rot * Mathutils.RotationMatrix(math.pi/2, 3, 'r', y) rot = tuple(matrix_rot.toEuler()) else: @@ -1052,8 +1092,8 @@ def write(filename, batch_objects = None, \ file.write('\n\t\tTypeFlags: "Camera"') file.write('\n\t\tGeometryVersion: 124') file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc) - file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(Vector(0,1,0) * matrix_rot) ) - file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Vector(0,0,-1)*matrix_rot) ) + file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(Mathutils.Vector(0,1,0) * matrix_rot) ) + file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Mathutils.Vector(0,0,-1)*matrix_rot) ) #file.write('\n\t\tUp: 0,0,0' ) #file.write('\n\t\tLookAt: 0,0,0' ) @@ -1458,7 +1498,8 @@ def write(filename, batch_objects = None, \ if my_mesh.blenTextures: do_textures = True else: do_textures = False - do_uvs = me.faceUV + do_uvs = len(me.uv_layers) > 0 +# do_uvs = me.faceUV file.write('\n\tModel: "Model::%s", "Mesh" {' % my_mesh.fbxName) @@ -1490,20 +1531,24 @@ def write(filename, batch_objects = None, \ file.write('\n\t\tPolygonVertexIndex: ') i=-1 for f in me.faces: - fi = [v.index for v in f] + fi = [v_index for j, v_index in enumerate(f.verts) if v_index != 0 or j != 3] +# fi = [v.index for v in f] + # flip the last index, odd but it looks like # this is how fbx tells one face from another fi[-1] = -(fi[-1]+1) fi = tuple(fi) if i==-1: - if len(f) == 3: file.write('%i,%i,%i' % fi ) + if len(fi) == 3: file.write('%i,%i,%i' % fi ) +# if len(f) == 3: file.write('%i,%i,%i' % fi ) else: file.write('%i,%i,%i,%i' % fi ) i=0 else: if i==13: file.write('\n\t\t') i=0 - if len(f) == 3: file.write(',%i,%i,%i' % fi ) + if len(fi) == 3: file.write(',%i,%i,%i' % fi ) +# if len(f) == 3: file.write(',%i,%i,%i' % fi ) else: file.write(',%i,%i,%i,%i' % fi ) i+=1 @@ -1511,13 +1556,15 @@ def write(filename, batch_objects = None, \ i=-1 for ed in me.edges: if i==-1: - file.write('%i,%i' % (ed.v1.index, ed.v2.index)) + file.write('%i,%i' % (ed.verts[0], ed.verts[1])) +# file.write('%i,%i' % (ed.v1.index, ed.v2.index)) i=0 else: if i==13: file.write('\n\t\t') i=0 - file.write(',%i,%i' % (ed.v1.index, ed.v2.index)) + file.write(',%i,%i' % (ed.verts[0], ed.verts[1])) +# file.write(',%i,%i' % (ed.v1.index, ed.v2.index)) i+=1 file.write('\n\t\tGeometryVersion: 124') @@ -1533,11 +1580,13 @@ def write(filename, batch_objects = None, \ i=-1 for v in me.verts: if i==-1: - file.write('%.15f,%.15f,%.15f' % tuple(v.no)); i=0 + file.write('%.15f,%.15f,%.15f' % tuple(v.normal)); i=0 +# file.write('%.15f,%.15f,%.15f' % tuple(v.no)); i=0 else: if i==2: file.write('\n '); i=0 - file.write(',%.15f,%.15f,%.15f' % tuple(v.no)) + file.write(',%.15f,%.15f,%.15f' % tuple(v.normal)) +# file.write(',%.15f,%.15f,%.15f' % tuple(v.no)) i+=1 file.write('\n\t\t}') @@ -1571,32 +1620,49 @@ def write(filename, batch_objects = None, \ ReferenceInformationType: "Direct" Smoothing: ''') - SHARP = Blender.Mesh.EdgeFlags.SHARP +# SHARP = Blender.Mesh.EdgeFlags.SHARP i=-1 for ed in me.edges: if i==-1: - file.write('%i' % ((ed.flag&SHARP)!=0)); i=0 + file.write('%i' % (ed.sharp)); i=0 +# file.write('%i' % ((ed.flag&SHARP)!=0)); i=0 else: if i==54: file.write('\n '); i=0 - file.write(',%i' % ((ed.flag&SHARP)!=0)) + file.write(',%i' % (ed.sharp)) +# file.write(',%i' % ((ed.flag&SHARP)!=0)) i+=1 file.write('\n\t\t}') - del SHARP - +# del SHARP + + # small utility function + # returns a slice of data depending on number of face verts + # data is either a MeshTextureFace or MeshColor + def face_data(data, face): + if f.verts[3] == 0: + totvert = 3 + else: + totvert = 4 + + return data[:totvert] + # Write VertexColor Layers # note, no programs seem to use this info :/ collayers = [] - if me.vertexColors: - collayers = me.getColorLayerNames() - collayer_orig = me.activeColorLayer + if len(me.vertex_colors): +# if me.vertexColors: + collayers = me.vertex_colors +# collayers = me.getColorLayerNames() + collayer_orig = me.active_vertex_color +# collayer_orig = me.activeColorLayer for colindex, collayer in enumerate(collayers): - me.activeColorLayer = collayer +# me.activeColorLayer = collayer file.write('\n\t\tLayerElementColor: %i {' % colindex) file.write('\n\t\t\tVersion: 101') - file.write('\n\t\t\tName: "%s"' % collayer) + file.write('\n\t\t\tName: "%s"' % collayer.name) +# file.write('\n\t\t\tName: "%s"' % collayer) file.write(''' MappingInformationType: "ByPolygonVertex" @@ -1605,19 +1671,37 @@ def write(filename, batch_objects = None, \ i = -1 ii = 0 # Count how many Colors we write - - for f in me.faces: - for col in f.col: + + for f, cf in zip(me.faces, collayer.data): + colors = [cf.color1, cf.color2, cf.color3, cf.color4] + + # determine number of verts + colors = face_data(colors, f) + + for col in colors: if i==-1: - file.write('%.4f,%.4f,%.4f,1' % (col[0]/255.0, col[1]/255.0, col[2]/255.0)) + file.write('%.4f,%.4f,%.4f,1' % tuple(col)) i=0 else: if i==7: file.write('\n\t\t\t\t') i=0 - file.write(',%.4f,%.4f,%.4f,1' % (col[0]/255.0, col[1]/255.0, col[2]/255.0)) + file.write(',%.4f,%.4f,%.4f,1' % tuple(col)) i+=1 ii+=1 # One more Color + +# for f in me.faces: +# for col in f.col: +# if i==-1: +# file.write('%.4f,%.4f,%.4f,1' % (col[0]/255.0, col[1]/255.0, col[2]/255.0)) +# i=0 +# else: +# if i==7: +# file.write('\n\t\t\t\t') +# i=0 +# file.write(',%.4f,%.4f,%.4f,1' % (col[0]/255.0, col[1]/255.0, col[2]/255.0)) +# i+=1 +# ii+=1 # One more Color file.write('\n\t\t\tColorIndex: ') i = -1 @@ -1639,13 +1723,17 @@ def write(filename, batch_objects = None, \ # Write UV and texture layers. uvlayers = [] if do_uvs: - uvlayers = me.getUVLayerNames() - uvlayer_orig = me.activeUVLayer - for uvindex, uvlayer in enumerate(uvlayers): - me.activeUVLayer = uvlayer + uvlayers = me.uv_textures +# uvlayers = me.getUVLayerNames() + uvlayer_orig = me.active_uv_texture +# uvlayer_orig = me.activeUVLayer + for uvindex, uvlayer in enumerate(me.uv_textures): +# for uvindex, uvlayer in enumerate(uvlayers): +# me.activeUVLayer = uvlayer file.write('\n\t\tLayerElementUV: %i {' % uvindex) file.write('\n\t\t\tVersion: 101') - file.write('\n\t\t\tName: "%s"' % uvlayer) + file.write('\n\t\t\tName: "%s"' % uvlayer.name) +# file.write('\n\t\t\tName: "%s"' % uvlayer) file.write(''' MappingInformationType: "ByPolygonVertex" @@ -1655,8 +1743,13 @@ def write(filename, batch_objects = None, \ i = -1 ii = 0 # Count how many UVs we write - for f in me.faces: - for uv in f.uv: + for f, uf in zip(me.faces, uvlayer.data): +# for f in me.faces: + uvs = [uf.uv1, uf.uv2, uf.uv3, uf.uv4] + uvs = face_data(uvs, f) + + for uv in uvs: +# for uv in f.uv: if i==-1: file.write('%.6f,%.6f' % tuple(uv)) i=0 @@ -1686,7 +1779,8 @@ def write(filename, batch_objects = None, \ if do_textures: file.write('\n\t\tLayerElementTexture: %i {' % uvindex) file.write('\n\t\t\tVersion: 101') - file.write('\n\t\t\tName: "%s"' % uvlayer) + file.write('\n\t\t\tName: "%s"' % uvlayer.name) +# file.write('\n\t\t\tName: "%s"' % uvlayer) if len(my_mesh.blenTextures) == 1: file.write('\n\t\t\tMappingInformationType: "AllSame"') @@ -1710,7 +1804,8 @@ def write(filename, batch_objects = None, \ i+=1 i=-1 - for f in me.faces: + for f in uvlayer.data: +# for f in me.faces: img_key = f.image if i==-1: @@ -1736,7 +1831,7 @@ def write(filename, batch_objects = None, \ TextureId: ''') file.write('\n\t\t}') - me.activeUVLayer = uvlayer_orig +# me.activeUVLayer = uvlayer_orig # Done with UV/textures. @@ -1765,13 +1860,21 @@ def write(filename, batch_objects = None, \ len_material_mapping_local = len(material_mapping_local) mats = my_mesh.blenMaterialList + + if me.active_uv_texture: + uv_faces = me.active_uv_texture.data + else: + uv_faces = [None] * len(me.faces) i=-1 - for f in me.faces: - try: mat = mats[f.mat] + for f, uf in zip(me.faces, uv_faces) +# for f in me.faces: + try: mat = mats[f.material_index] +# try: mat = mats[f.mat] except:mat = None - if do_uvs: tex = f.image # WARNING - MULTI UV LAYER IMAGES NOT SUPPORTED :/ + if do_uvs: tex = uf.image # WARNING - MULTI UV LAYER IMAGES NOT SUPPORTED :/ +# if do_uvs: tex = f.image # WARNING - MULTI UV LAYER IMAGES NOT SUPPORTED :/ else: tex = None if i==-1: @@ -1810,7 +1913,8 @@ def write(filename, batch_objects = None, \ TypedIndex: 0 }''') - if me.vertexColors: + if me.vertex_colors: +# if me.vertexColors: file.write(''' LayerElement: { Type: "LayerElementColor" @@ -2331,7 +2435,8 @@ Objects: {''') if my_mesh.fbxBoneParent: weights = None else: - weights = meshNormalizedWeights(my_mesh.blenData) + weights = meshNormalizedWeights(my_mesh.blenObject) +# weights = meshNormalizedWeights(my_mesh.blenData) #for bonename, bone, obname, bone_mesh, armob in ob_bones: for my_bone in ob_bones: @@ -3216,6 +3321,7 @@ if __name__ == '__main__': # - isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565 # - get rid of BPyObject_getObjectArmature, move it in RNA? # - BATCH_ENABLE and BATCH_GROUP options: line 327 +# - implement all BPyMesh_* used here with RNA # TODO diff --git a/release/io/export_obj.py b/release/io/export_obj.py index 466fb2f2362..70be6bb541b 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -434,7 +434,7 @@ def write(filename, objects, scene, # continue if EXPORT_UV: - faceuv = len(me.uv_layers) > 0 + faceuv = len(me.uv_textures) > 0 else: faceuv = False @@ -508,7 +508,7 @@ def write(filename, objects, scene, pass elif faceuv: # XXX update - tface = me.active_uv_layer.data + tface = me.active_uv_texture.data # exception only raised if Python 2.3 or lower... try: @@ -570,7 +570,7 @@ def write(filename, objects, scene, uv_face_mapping = [[0,0,0,0] for f in faces] # a bit of a waste for tri's :/ uv_dict = {} # could use a set() here - uv_layer = me.active_uv_layer + uv_layer = me.active_uv_texture for f, f_index in face_index_pairs: tface = uv_layer.data[f_index] @@ -652,7 +652,7 @@ def write(filename, objects, scene, # f_mat = min(f.mat, len(materialNames)-1) if faceuv: - tface = me.active_uv_layer.data[face_index_pairs[f_index][1]] + tface = me.active_uv_texture.data[face_index_pairs[f_index][1]] f_image = tface.image f_uv= [tface.uv1, tface.uv2, tface.uv3] diff --git a/source/blender/makesdna/intern/SConscript b/source/blender/makesdna/intern/SConscript index 1c716019e80..9089718da83 100644 --- a/source/blender/makesdna/intern/SConscript +++ b/source/blender/makesdna/intern/SConscript @@ -61,6 +61,6 @@ if env['OURPLATFORM'] != 'linuxcross': else: dna.Command ('dna.c', '', root_build_dir+os.sep+"makesdna $TARGET") else: - dna.Command ('dna.c', '', root_build_dir+os.sep+"makesdna.exe $TARGET") + dna.Command ('dna.c', '', 'wine ' + root_build_dir+os.sep+"makesdna.exe $TARGET") obj = ['intern/dna.c', 'intern/dna_genfile.c'] Return ('obj') diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index 2561322b9a2..12822024654 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -182,7 +182,7 @@ static void rna_def_pose_channel(BlenderRNA *brna) /* kaito says this should be not user-editable; I disagree; power users should be able to force this in python; he's the boss. */ prop= RNA_def_property(srna, "pose_matrix", PROP_FLOAT, PROP_MATRIX); - RNA_def_property_float_sdna(prop, "pose_mat"); + RNA_def_property_float_sdna(prop, NULL, "pose_mat"); RNA_def_property_array(prop, 16); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Pose Matrix", "Final 4x4 matrix for this channel."); diff --git a/tools/Blender.py b/tools/Blender.py index 164a9d097e6..8866df2c0ae 100644 --- a/tools/Blender.py +++ b/tools/Blender.py @@ -112,7 +112,6 @@ def setup_staticlibs(lenv): #here libs for static linking ] libincs = [ - '/usr/lib', lenv['BF_OPENGL_LIBPATH'], lenv['BF_JPEG_LIBPATH'], lenv['BF_PNG_LIBPATH'], @@ -120,6 +119,9 @@ def setup_staticlibs(lenv): lenv['BF_ICONV_LIBPATH'] ] + if lenv['OURPLATFORM'] != 'linuxcross': + libincs = ['/usr/lib'] + libincs + libincs += Split(lenv['BF_FREETYPE_LIBPATH']) if lenv['WITH_BF_PYTHON']: libincs += Split(lenv['BF_PYTHON_LIBPATH']) @@ -223,7 +225,7 @@ def buildinfo(lenv, build_type): obj = [] if lenv['BF_BUILDINFO']: - if sys.platform=='win32': + if sys.platform=='win32' or lenv['OURPLATFORM']=='linuxcross': build_info_file = open("source/creator/winbuildinfo.h", 'w') build_info_file.write("char *build_date=\"%s\";\n"%build_date) build_info_file.write("char *build_time=\"%s\";\n"%build_time) From aede14d4e08b953e0e300c9992776f191e9fd94d Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 16 Jul 2009 13:19:43 +0000 Subject: [PATCH 47/68] - replaced Object.create_mesh body with 2.4x API's Mesh.createFromObject code to also support curves, surfaces and metaballs. - replaced Object.dag_update with Object.make_display_list, which copies old API's Object.makeDisplayList --- release/io/export_fbx.py | 47 +++- release/io/export_obj.py | 9 +- .../blender/makesrna/intern/rna_object_api.c | 233 ++++++++++++++---- 3 files changed, 224 insertions(+), 65 deletions(-) diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index c4f56b472dc..d74e6246104 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -2019,41 +2019,60 @@ def write(filename, batch_objects = None, \ # This is needed so applying modifiers dosnt apply the armature deformation, its also needed # ...so mesh objects return their rest worldspace matrix when bone-parents are exported as weighted meshes. # set every armature to its rest, backup the original values so we done mess up the scene - ob_arms_orig_rest = [arm.restPosition for arm in bpy.data.armatures] + ob_arms_orig_rest = [arm.rest_position for arm in bpy.data.armatures] +# ob_arms_orig_rest = [arm.restPosition for arm in bpy.data.armatures] for arm in bpy.data.armatures: - arm.restPosition = True + arm.rest_position = True +# arm.restPosition = True if ob_arms_orig_rest: for ob_base in bpy.data.objects: #if ob_base.type == 'Armature': - ob_base.makeDisplayList() + ob_base.make_display_list() +# ob_base.makeDisplayList() # This causes the makeDisplayList command to effect the mesh - Blender.Set('curframe', Blender.Get('curframe')) + sce.set_frame(sce.current_frame) +# Blender.Set('curframe', Blender.Get('curframe')) for ob_base in tmp_objects: - for ob, mtx in BPyObject.getDerivedObjects(ob_base): - #for ob in [ob_base,]: + + # ignore dupli children + if ob_base.parent and ob_base.parent.dupli_type != 'NONE': + continue + + obs = [(ob_base, ob_base.matrix)] + if ob_base.dupli_type != 'NONE': + ob_base.create_dupli_list() + obs = [(dob.object, dob.matrix) for dob in ob_base.dupli_list] + + for ob, mtx in obs: +# for ob, mtx in BPyObject.getDerivedObjects(ob_base): tmp_ob_type = ob.type - if tmp_ob_type == 'Camera': + if tmp_ob_type == 'CAMERA': +# if tmp_ob_type == 'Camera': if EXP_CAMERA: ob_cameras.append(my_object_generic(ob, mtx)) - elif tmp_ob_type == 'Lamp': + elif tmp_ob_type == 'LAMP': +# elif tmp_ob_type == 'Lamp': if EXP_LAMP: ob_lights.append(my_object_generic(ob, mtx)) - elif tmp_ob_type == 'Armature': + elif tmp_ob_type == 'ARMATURE': +# elif tmp_ob_type == 'Armature': if EXP_ARMATURE: # TODO - armatures dont work in dupligroups! if ob not in ob_arms: ob_arms.append(ob) # ob_arms.append(ob) # replace later. was "ob_arms.append(sane_obname(ob), ob)" - elif tmp_ob_type == 'Empty': + elif tmp_ob_type == 'EMPTY': +# elif tmp_ob_type == 'Empty': if EXP_EMPTY: ob_null.append(my_object_generic(ob, mtx)) elif EXP_MESH: origData = True - if tmp_ob_type != 'Mesh': + if tmp_ob_type != 'MESH': +# if tmp_ob_type != 'Mesh': me = bpy.data.meshes.new() try: me.getFromObject(ob) except: me = None @@ -2171,8 +2190,9 @@ def write(filename, batch_objects = None, \ if ob_arms_orig_rest: for ob_base in bpy.data.objects: - if ob_base.type == 'Armature': - ob_base.dag_update() + if ob_base.type == 'ARMATURE': +# if ob_base.type == 'Armature': + ob_base.make_display_list() # ob_base.makeDisplayList() # This causes the makeDisplayList command to effect the mesh sce.set_frame(sce.current_frame) @@ -3322,6 +3342,7 @@ if __name__ == '__main__': # - get rid of BPyObject_getObjectArmature, move it in RNA? # - BATCH_ENABLE and BATCH_GROUP options: line 327 # - implement all BPyMesh_* used here with RNA +# - getDerivedObjects is not fully replicated with .dupli* funcs # TODO diff --git a/release/io/export_obj.py b/release/io/export_obj.py index 70be6bb541b..251fdfcf113 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -380,11 +380,6 @@ def write(filename, objects, scene, # Get all meshes for ob_main in objects: - if ob_main.dupli_type != 'NONE': - # XXX - print('creating dupli_list on', ob_main.name) - ob_main.create_dupli_list() - # ignore dupli children if ob_main.parent and ob_main.parent.dupli_type != 'NONE': # XXX @@ -393,6 +388,10 @@ def write(filename, objects, scene, obs = [] if ob_main.dupli_type != 'NONE': + # XXX + print('creating dupli_list on', ob_main.name) + ob_main.create_dupli_list() + obs = [(dob.object, dob.matrix) for dob in ob_main.dupli_list] # XXX debug print diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index bf1fb6d2b6a..bae651bf806 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -40,62 +40,190 @@ #include "ED_mesh.h" -/* parameter to rna_Object_create_mesh */ -typedef enum CreateMeshType { - CREATE_MESH_PREVIEW = 0, - CREATE_MESH_RENDER = 1 -} CreateMeshType; - #ifdef RNA_RUNTIME -#include "BKE_customdata.h" -#include "BKE_DerivedMesh.h" -#include "BKE_anim.h" +#include "BKE_main.h" +#include "BKE_global.h" +#include "BKE_context.h" #include "BKE_report.h" +#include "BKE_object.h" +#include "BKE_mesh.h" +#include "BKE_DerivedMesh.h" +#include "BKE_customdata.h" +#include "BKE_anim.h" #include "BKE_depsgraph.h" +#include "BKE_displist.h" +#include "BKE_font.h" +#include "BKE_mball.h" + +#include "BLI_arithb.h" #include "DNA_mesh_types.h" #include "DNA_scene_types.h" #include "DNA_meshdata_types.h" +#include "DNA_curve_types.h" -#include "BLI_arithb.h" +#include "MEM_guardedalloc.h" -/* copied from init_render_mesh (render code) */ -static Mesh *rna_Object_create_mesh(Object *ob, bContext *C, ReportList *reports, int type) +/* copied from Mesh_getFromObject and adapted to RNA interface */ +/* settings: 0 - preview, 1 - render */ +static Mesh *rna_Object_create_mesh(Object *ob, bContext *C, ReportList *reports, int apply_modifiers, int settings) { - /* CustomDataMask mask = CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; */ - CustomDataMask mask = CD_MASK_MESH; /* this seems more suitable, exporter, - for example, needs CD_MASK_MDEFORMVERT */ - DerivedMesh *dm; - Mesh *me; - Scene *sce; + Mesh *tmpmesh; + Curve *tmpcu = NULL; + Object *tmpobj = NULL; + int render = settings, i; + int cage = !apply_modifiers; + Scene *sce = CTX_data_scene(C); - sce= CTX_data_scene(C); + /* perform the mesh extraction based on type */ + switch (ob->type) { + case OB_FONT: + case OB_CURVE: + case OB_SURF: + + /* copies object and modifiers (but not the data) */ + tmpobj= copy_object(ob); + tmpcu = (Curve *)tmpobj->data; + tmpcu->id.us--; + + /* if getting the original caged mesh, delete object modifiers */ + if( cage ) + object_free_modifiers(tmpobj); + + /* copies the data */ + tmpobj->data = copy_curve( (Curve *) ob->data ); + +#if 0 + /* copy_curve() sets disp.first null, so currently not need */ + { + Curve *cu; + cu = (Curve *)tmpobj->data; + if( cu->disp.first ) + MEM_freeN( cu->disp.first ); + cu->disp.first = NULL; + } - /* TODO: other types */ - if(ob->type != OB_MESH) { - BKE_report(reports, RPT_ERROR, "Object should be of type MESH."); - return NULL; - } +#endif - if (type == CREATE_MESH_PREVIEW) { - dm= mesh_create_derived_view(sce, ob, mask); - } - else { - dm= mesh_create_derived_render(sce, ob, mask); - } + /* get updated display list, and convert to a mesh */ + makeDispListCurveTypes( sce, tmpobj, 0 ); + nurbs_to_mesh( tmpobj ); + + /* nurbs_to_mesh changes the type to a mesh, check it worked */ + if (tmpobj->type != OB_MESH) { + free_libblock_us( &G.main->object, tmpobj ); + BKE_report(reports, RPT_ERROR, "cant convert curve to mesh. Does the curve have any segments?"); + return NULL; + } + tmpmesh = tmpobj->data; + free_libblock_us( &G.main->object, tmpobj ); + break; - if(!dm) { - /* TODO: report */ - return NULL; - } + case OB_MBALL: + /* metaballs don't have modifiers, so just convert to mesh */ + ob = find_basis_mball( sce, ob ); + tmpmesh = add_mesh("Mesh"); + mball_to_mesh( &ob->disp, tmpmesh ); + break; - me= add_mesh("tmp_render_mesh"); - me->id.us--; /* we don't assign it to anything */ - DM_to_mesh(dm, me); - dm->release(dm); + case OB_MESH: + /* copies object and modifiers (but not the data) */ + if (cage) { + /* copies the data */ + tmpmesh = copy_mesh( ob->data ); + /* if not getting the original caged mesh, get final derived mesh */ + } else { + /* Make a dummy mesh, saves copying */ + DerivedMesh *dm; + /* CustomDataMask mask = CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; */ + CustomDataMask mask = CD_MASK_MESH; /* this seems more suitable, exporter, + for example, needs CD_MASK_MDEFORMVERT */ + + /* Write the display mesh into the dummy mesh */ + if (render) + dm = mesh_create_derived_render( sce, ob, mask ); + else + dm = mesh_create_derived_view( sce, ob, mask ); + + tmpmesh = add_mesh( "Mesh" ); + DM_to_mesh( dm, tmpmesh ); + dm->release( dm ); + } + + break; + default: + BKE_report(reports, RPT_ERROR, "Object does not have geometry data"); + return NULL; + } - return me; + /* Copy materials to new mesh */ + switch (ob->type) { + case OB_SURF: + tmpmesh->totcol = tmpcu->totcol; + + /* free old material list (if it exists) and adjust user counts */ + if( tmpcu->mat ) { + for( i = tmpcu->totcol; i-- > 0; ) { + /* are we an object material or data based? */ + if (ob->colbits & 1<mat[i] = ob->mat[i]; + else + tmpmesh->mat[i] = tmpcu->mat[i]; + + if (tmpmesh->mat[i]) + tmpmesh->mat[i]->id.us++; + } + } + break; + +#if 0 + /* Crashes when assigning the new material, not sure why */ + case OB_MBALL: + tmpmb = (MetaBall *)ob->data; + tmpmesh->totcol = tmpmb->totcol; + + /* free old material list (if it exists) and adjust user counts */ + if( tmpmb->mat ) { + for( i = tmpmb->totcol; i-- > 0; ) { + tmpmesh->mat[i] = tmpmb->mat[i]; /* CRASH HERE ??? */ + if (tmpmesh->mat[i]) { + tmpmb->mat[i]->id.us++; + } + } + } + break; +#endif + + case OB_MESH: + if (!cage) { + Mesh *origmesh= ob->data; + tmpmesh->flag= origmesh->flag; + tmpmesh->mat = MEM_dupallocN(origmesh->mat); + tmpmesh->totcol = origmesh->totcol; + tmpmesh->smoothresh= origmesh->smoothresh; + if( origmesh->mat ) { + for( i = origmesh->totcol; i-- > 0; ) { + /* are we an object material or data based? */ + if (ob->colbits & 1<mat[i] = ob->mat[i]; + else + tmpmesh->mat[i] = origmesh->mat[i]; + if (tmpmesh->mat[i]) + tmpmesh->mat[i]->id.us++; + } + } + } + break; + } /* end copy materials */ + + /* we don't assign it to anything */ + tmpmesh->id.us--; + + /* make sure materials get updated in objects */ + test_object_materials( ( ID * ) tmpmesh ); + + return tmpmesh; } /* When no longer needed, duplilist should be freed with Object.free_duplilist */ @@ -166,9 +294,18 @@ static void rna_Object_add_vertex_to_group(Object *ob, int vertex_index, bDeform add_vert_to_defgroup(ob, def, vertex_index, weight, assignmode); } -static void rna_Object_dag_update(Object *ob, bContext *C) +/* copied from old API Object.makeDisplayList (Object.c) */ +static void rna_Object_make_display_list(Object *ob, bContext *C) { - DAG_object_flush_update(CTX_data_scene(C), ob, OB_RECALC_DATA); + Scene *sce= CTX_data_scene(C); + + if (ob->type == OB_FONT) { + Curve *cu = ob->data; + freedisplist(&cu->disp); + BKE_text_to_curve(sce, ob, CU_LEFT); + } + + DAG_object_flush_update(sce, ob, OB_RECALC_DATA); } /* @@ -215,8 +352,8 @@ void RNA_api_object(StructRNA *srna) PropertyRNA *parm; static EnumPropertyItem mesh_type_items[] = { - {CREATE_MESH_PREVIEW, "PREVIEW", 0, "Preview", "Apply preview settings."}, - {CREATE_MESH_RENDER, "RENDER", 0, "Render", "Apply render settings."}, + {0, "PREVIEW", 0, "Preview", "Apply modifier preview settings."}, + {1, "RENDER", 0, "Render", "Apply modifier render settings."}, {0, NULL, 0, NULL, NULL} }; @@ -229,9 +366,11 @@ void RNA_api_object(StructRNA *srna) /* mesh */ func= RNA_def_function(srna, "create_mesh", "rna_Object_create_mesh"); - RNA_def_function_ui_description(func, "Create a Mesh datablock with all modifiers applied."); + RNA_def_function_ui_description(func, "Create a Mesh datablock with modifiers applied."); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); - parm= RNA_def_enum(func, "type", mesh_type_items, 0, "", "Type of mesh settings to apply."); + RNA_def_boolean(func, "apply_modifiers", 0, "", "Apply modifiers."); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_enum(func, "settings", mesh_type_items, 0, "", "Mesh settings to apply."); RNA_def_property_flag(parm, PROP_REQUIRED); parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object, remove it if it is only used for export."); RNA_def_function_return(func, parm); @@ -270,8 +409,8 @@ void RNA_api_object(StructRNA *srna) RNA_def_property_flag(parm, PROP_REQUIRED); /* DAG */ - func= RNA_def_function(srna, "dag_update", "rna_Object_dag_update"); - RNA_def_function_ui_description(func, "DAG update."); /* XXX describe better */ + func= RNA_def_function(srna, "make_display_list", "rna_Object_make_display_list"); + RNA_def_function_ui_description(func, "Update object's display data."); /* XXX describe better */ RNA_def_function_flag(func, FUNC_USE_CONTEXT); } From 140f2b154f0a68cfe0ff6c42de8102883cd0958a Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 16 Jul 2009 19:07:54 +0000 Subject: [PATCH 48/68] Reverted BF_GETTEXT_LIB in config/linuxcross-config.py to previous value. Cross-compiling works! Started a wiki page describing the process here: http://wiki.blender.org/index.php/User:Kazanbas/Cross-compiling_Blender_2.5_on_Linux TODO: - make scons copy DLLs to install directory - deal with python: which version, what to install, etc. - package up into .msi? --- config/linuxcross-config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/linuxcross-config.py b/config/linuxcross-config.py index 5253110be66..8bc1391db6d 100644 --- a/config/linuxcross-config.py +++ b/config/linuxcross-config.py @@ -77,7 +77,7 @@ WITH_BF_INTERNATIONAL = True BF_GETTEXT = LIBDIR + '/gettext' BF_GETTEXT_INC = '${BF_GETTEXT}/include' -BF_GETTEXT_LIB = 'gettextlib' +BF_GETTEXT_LIB = 'gnu_gettext' # BF_GETTEXT_LIB = 'gnu_gettext' BF_GETTEXT_LIBPATH = '${BF_GETTEXT}/lib' From 122104b3bb1a69549b0bd016c29aa01dc71f3704 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Fri, 17 Jul 2009 10:09:07 +0000 Subject: [PATCH 49/68] FBX exporter conversion almost done. Unit tests: - add a check that BKE_copy_images produces NULL filepath for images having type other than IMA_TYPE_IMAGE - also expect NULL filepath for images having empty filename Enhanced BKE_copy_images so the tests pass. --- release/io/export_fbx.py | 136 ++++++++++++------ source/blender/blenkernel/intern/image.c | 26 ++-- .../blender/makesrna/intern/rna_object_api.c | 2 +- source/blender/python/intern/bpy_util.c | 3 +- 4 files changed, 104 insertions(+), 63 deletions(-) diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index d74e6246104..c19a914d22e 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -297,6 +297,29 @@ def BPyMesh_meshWeight2List(ob): return groupNames, vWeightList +def BPyMesh_meshWeight2Dict(me, ob): + ''' Takes a mesh and return its group names and a list of dicts, one dict per vertex. + using the group as a key and a float value for the weight. + These 2 lists can be modified and then used with dict2MeshWeight to apply the changes. + ''' + + vWeightDict= [dict() for i in xrange(len(me.verts))] # Sync with vertlist. + + # Clear the vert group. + groupNames= [g.name for g in ob.vertex_groups] +# groupNames= me.getVertGroupNames() + + for group in groupNames: + for vert_index, weight in me.getVertsFromGroup(group, 1): # (i,w) tuples. + vWeightDict[vert_index][group]= weight + + # removed this because me may be copying teh vertex groups. + #for group in groupNames: + # me.removeVertGroup(group) + + return groupNames, vWeightDict + + def meshNormalizedWeights(me): try: # account for old bad BPyMesh groupNames, vWeightList = BPyMesh_meshWeight2List(me) @@ -1498,7 +1521,7 @@ def write(filename, batch_objects = None, \ if my_mesh.blenTextures: do_textures = True else: do_textures = False - do_uvs = len(me.uv_layers) > 0 + do_uvs = len(me.uv_textures) > 0 # do_uvs = me.faceUV @@ -2073,8 +2096,9 @@ def write(filename, batch_objects = None, \ origData = True if tmp_ob_type != 'MESH': # if tmp_ob_type != 'Mesh': - me = bpy.data.meshes.new() - try: me.getFromObject(ob) +# me = bpy.data.meshes.new() + try: me = ob.create_mesh(True, 'PREVIEW') +# try: me.getFromObject(ob) except: me = None if me: meshes_to_clear.append( me ) @@ -2084,65 +2108,70 @@ def write(filename, batch_objects = None, \ # Mesh Type! if EXP_MESH_APPLY_MOD: # me = bpy.data.meshes.new() - me = ob.create_mesh('PREVIEW') + me = ob.create_mesh(True, 'PREVIEW') # me.getFromObject(ob) # so we keep the vert groups - if EXP_ARMATURE: - orig_mesh = ob.data +# if EXP_ARMATURE: # orig_mesh = ob.getData(mesh=1) - if len(ob.vertex_groups): # if orig_mesh.getVertGroupNames(): - ob.copy().link(me) - # If new mesh has no vgroups we can try add if verts are teh same - if not me.getVertGroupNames(): # vgroups were not kept by the modifier - if len(me.verts) == len(orig_mesh.verts): - groupNames, vWeightDict = BPyMesh.meshWeight2Dict(orig_mesh) - BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict) +# ob.copy().link(me) +# # If new mesh has no vgroups we can try add if verts are teh same +# if not me.getVertGroupNames(): # vgroups were not kept by the modifier +# if len(me.verts) == len(orig_mesh.verts): +# groupNames, vWeightDict = BPyMesh.meshWeight2Dict(orig_mesh) +# BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict) # print ob, me, me.getVertGroupNames() meshes_to_clear.append( me ) origData = False mats = me.materials else: - me = ob.getData(mesh=1) + me = ob.data +# me = ob.getData(mesh=1) mats = me.materials - # Support object colors - tmp_colbits = ob.colbits - if tmp_colbits: - tmp_ob_mats = ob.getMaterials(1) # 1 so we get None's too. - for i in xrange(16): - if tmp_colbits & (1< 0: +# if me.faceUV: + uvlayer_orig = me.active_uv_texture +# uvlayer_orig = me.activeUVLayer + for uvlayer in me.uv_textures: +# for uvlayer in me.getUVLayerNames(): +# me.activeUVLayer = uvlayer + for f, uf in zip(me.faces, uvlayer.data): +# for f in me.faces: + tex = uf.image +# tex = f.image textures[tex] = texture_mapping_local[tex] = None - try: mat = mats[f.mat] + try: mat = mats[f.material_index] +# try: mat = mats[f.mat] except: mat = None materials[mat, tex] = material_mapping_local[mat, tex] = None # should use sets, wait for blender 2.5 - me.activeUVLayer = uvlayer_orig +# me.activeUVLayer = uvlayer_orig else: for mat in mats: # 2.44 use mat.lib too for uniqueness @@ -2155,9 +2184,12 @@ def write(filename, batch_objects = None, \ blenParentBoneName = None # parent bone - special case - if (not armob) and ob.parent and ob.parent.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.BONE: + if (not armob) and ob.parent and ob.parent.type == 'ARMATURE' and \ + ob.parent_type == 'BONE': +# if (not armob) and ob.parent and ob.parent.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.BONE: armob = ob.parent - blenParentBoneName = ob.parentbonename + blenParentBoneName = ob.parent_bone +# blenParentBoneName = ob.parentbonename if armob and armob not in ob_arms: @@ -2181,7 +2213,11 @@ def write(filename, batch_objects = None, \ my_mesh.fbxBoneParent = blenParentBoneName # replace with my_bone instance later ob_meshes.append( my_mesh ) - + + # not forgetting to free dupli_list + if ob_base.dupli_list: ob_base.free_dupli_list() + + if EXP_ARMATURE: # now we have the meshes, restore the rest arm position for i, arm in enumerate(bpy.data.armatures): @@ -2217,7 +2253,8 @@ def write(filename, batch_objects = None, \ # fbxName, blenderObject, my_bones, blenderActions #ob_arms[i] = fbxArmObName, ob, arm_my_bones, (ob.action, []) - for bone in my_arm.blenData.bones.values(): + for bone in my_arm.blenData.bones: +# for bone in my_arm.blenData.bones.values(): my_bone = my_bone_class(bone, my_arm) my_arm.fbxBones.append( my_bone ) ob_bones.append( my_bone ) @@ -2662,7 +2699,8 @@ Connections: {''') # Needed for scene footer as well as animation - render = sce.render + render = sce.render_data +# render = sce.render # from the FBX sdk #define KTIME_ONE_SECOND KTime (K_LONGLONG(46186158000)) @@ -2671,8 +2709,10 @@ Connections: {''') return int(0.5 + ((t/fps) * 46186158000)) fps = float(render.fps) - start = render.sFrame - end = render.eFrame + start = sce.start_frame +# start = render.sFrame + end = sce.end_frame +# end = render.eFrame if end < start: start, end = end, start if start==end: ANIM_ENABLE = False @@ -2959,8 +2999,6 @@ Takes: {''') bpy.data.remove_mesh(me) # me.verts = None - - # --------------------------- Footer if world: m = world.mist @@ -3025,7 +3063,8 @@ Takes: {''') if EXP_IMAGE_COPY: copy_images( basepath, [ tex[1] for tex in textures if tex[1] != None ]) - print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time) + print 'export finished in %.4f sec.' % (bpy.sys.time() - start_time) +# print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time) return True @@ -3338,11 +3377,14 @@ if __name__ == '__main__': # NOTES (all line numbers correspond to original export_fbx.py (under release/scripts) # - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print # - get rid of cleanName somehow -# - isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565 +# + fixed: isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565 # - get rid of BPyObject_getObjectArmature, move it in RNA? # - BATCH_ENABLE and BATCH_GROUP options: line 327 # - implement all BPyMesh_* used here with RNA # - getDerivedObjects is not fully replicated with .dupli* funcs +# - talk to Campbell, this code won't work? lines 1867-1875 +# - don't know what those colbits are, do we need them? they're said to be deprecated in DNA_object_types.h: 1886-1893 +# - no hq normals: 1900-1901 # TODO diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 495dd3ec523..833d5ac1a90 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -2120,6 +2120,7 @@ void BKE_image_user_calc_imanr(ImageUser *iuser, int cfra, int fieldnr) Copy list of images to dest_dir. paths is optional, if given, image paths for each image will be written in it. + It will also contain NULLs for images that cannot be copied. If an image file doesn't exist, NULL is added in paths. Logic: @@ -2161,6 +2162,13 @@ void BKE_copy_images(ListBase *images, char *dest_dir, ListBase *paths) while (link) { im= link->data; + LinkData *ld = MEM_callocN(sizeof(LinkData), "PathLinkData"); + ld->data= NULL; + BLI_addtail(paths, ld); + + if (!strcmp(im->name, "") || im->type != IMA_TYPE_IMAGE) + goto next; + BLI_strncpy(path, im->name, sizeof(path)); /* expand "//" in filename and get absolute path */ @@ -2169,16 +2177,8 @@ void BKE_copy_images(ListBase *images, char *dest_dir, ListBase *paths) /* in unit tests, we don't want to modify the filesystem */ #ifndef WITH_UNIT_TEST /* proceed only if image file exists */ - if (!BLI_exists(path)) { - - if (paths) { - LinkData *ld = MEM_callocN(sizeof(LinkData), "PathLinkData"); - ld->data= NULL; - BLI_addtail(paths, ld); - } - - continue; - } + if (!BLI_exists(path)) + goto next; #endif /* get the directory part */ @@ -2219,17 +2219,17 @@ void BKE_copy_images(ListBase *images, char *dest_dir, ListBase *paths) } #ifndef WITH_UNIT_TEST - BLI_copy_fileops(path, dest_path); + if (BLI_copy_fileops(path, dest_path) != 0) + goto next; #endif if (paths) { - LinkData *ld = MEM_callocN(sizeof(LinkData), "PathLinkData"); len= strlen(dest_path) + 1; ld->data= MEM_callocN(len, "PathLinkData"); BLI_strncpy(ld->data, dest_path, len); - BLI_addtail(paths, ld); } + next: link= link->next; } } diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index bae651bf806..27a0ceb21c1 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -370,7 +370,7 @@ void RNA_api_object(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); RNA_def_boolean(func, "apply_modifiers", 0, "", "Apply modifiers."); RNA_def_property_flag(parm, PROP_REQUIRED); - parm= RNA_def_enum(func, "settings", mesh_type_items, 0, "", "Mesh settings to apply."); + parm= RNA_def_enum(func, "settings", mesh_type_items, 0, "", "Modifier settings to apply."); RNA_def_property_flag(parm, PROP_REQUIRED); parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object, remove it if it is only used for export."); RNA_def_function_return(func, parm); diff --git a/source/blender/python/intern/bpy_util.c b/source/blender/python/intern/bpy_util.c index 3295bdd91c0..c5fd030cdd6 100644 --- a/source/blender/python/intern/bpy_util.c +++ b/source/blender/python/intern/bpy_util.c @@ -583,9 +583,8 @@ static PyObject *bpy_util_copy_images(PyObject *self, PyObject *args) /* convert filenames */ ret= PyList_New(0); - len= BLI_countlist(paths); - for(link= paths->first, i= 0; link; link++, i++) { + for(link= paths->first; link; link= link->next) { if (link->data) { item= PyUnicode_FromString(link->data); PyList_Append(ret, item); From 00e219d8e97afcf3767a6d2b28a6d05bcc984279 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Wed, 22 Jul 2009 13:35:02 +0000 Subject: [PATCH 50/68] FBX exporter: - made an operator with props for UI. UI is still "raw" - ran 2to3 on export_fbx.py to make it python 3-compatible Next: testing/fixing. --- release/io/export_fbx.py | 187 +++++++++++++----- release/io/export_obj.py | 1 + release/io/export_ply.py | 2 +- .../blender/makesrna/intern/rna_object_api.c | 2 +- 4 files changed, 137 insertions(+), 55 deletions(-) diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index 64a8870bb3d..a861436d132 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -38,6 +38,7 @@ http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx import os import time +import math # math.pi # try: # import time @@ -65,10 +66,10 @@ import bpy import Mathutils # from Blender.Mathutils import Matrix, Vector, RotationMatrix -import BPyObject -import BPyMesh -import BPySys -import BPyMessages +# import BPyObject +# import BPyMesh +# import BPySys +# import BPyMessages ## This was used to make V, but faster not to do all that ##valid = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_,.()[]{}' @@ -79,7 +80,7 @@ invalid = ''.join([chr(i) for i in v]) def cleanName(name): for ch in invalid: name = name.replace(ch, '_') return name -del v, i +# del v, i def copy_file(source, dest): @@ -107,14 +108,14 @@ def copy_images(dest_dir, textures): # Make a name for the target path. dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] if not Blender.sys.exists(dest_image_path): # Image isnt alredy there - print '\tCopying "%s" > "%s"' % (image_path, dest_image_path) + print('\tCopying "%s" > "%s"' % (image_path, dest_image_path)) try: copy_file(image_path, dest_image_path) copyCount+=1 except: - print '\t\tWarning, file failed to copy, skipping.' + print('\t\tWarning, file failed to copy, skipping.') - print '\tCopied %d images' % copyCount + print('\tCopied %d images' % copyCount) def BPyObject_getObjectArmature(ob): ''' @@ -141,9 +142,9 @@ def BPyObject_getObjectArmature(ob): def eulerRadToDeg(eul): ret = Mathutils.Euler() - ret.x = 180 / math.pi * eul.x - ret.y = 180 / math.pi * eul.y - ret.z = 180 / math.pi * eul.z + ret.x = 180 / math.pi * eul[0] + ret.y = 180 / math.pi * eul[1] + ret.z = 180 / math.pi * eul[2] return ret @@ -221,7 +222,7 @@ def sane_name(data, dct): #name = BPySys.cleanName(name) name = cleanName(name) # use our own - while name in dct.itervalues(): name = increment_string(name) + while name in iter(dct.values()): name = increment_string(name) if use_other: # even if other is None - orig_name_other will be a string or None dct[orig_name, orig_name_other] = name @@ -286,9 +287,9 @@ def BPyMesh_meshWeight2List(ob): if not len_groupNames: # no verts? return a vert aligned empty list - return [[] for i in xrange(len(me.verts))], [] + return [[] for i in range(len(me.verts))], [] else: - vWeightList= [[0.0]*len_groupNames for i in xrange(len(me.verts))] + vWeightList= [[0.0]*len_groupNames for i in range(len(me.verts))] for i, v in enumerate(me.verts): for g in v.groups: @@ -303,7 +304,7 @@ def BPyMesh_meshWeight2Dict(me, ob): These 2 lists can be modified and then used with dict2MeshWeight to apply the changes. ''' - vWeightDict= [dict() for i in xrange(len(me.verts))] # Sync with vertlist. + vWeightDict= [dict() for i in range(len(me.verts))] # Sync with vertlist. # Clear the vert group. groupNames= [g.name for g in ob.vertex_groups] @@ -368,7 +369,6 @@ def write(filename, batch_objects = None, \ ANIM_ACTION_ALL = False, BATCH_ENABLE = False, BATCH_GROUP = True, - BATCH_SCENE = False, BATCH_FILE_PREFIX = '', BATCH_OWN_DIR = False ): @@ -436,7 +436,7 @@ def write(filename, batch_objects = None, \ filename = new_fbxpath + newname + '.fbx' - print '\nBatch exporting %s as...\n\t"%s"' % (data, filename) + print('\nBatch exporting %s as...\n\t"%s"' % (data, filename)) # XXX don't know what to do with this, probably do the same? (Arystan) if BATCH_GROUP: #group @@ -596,7 +596,8 @@ def write(filename, batch_objects = None, \ self.fbxGroupNames = [] self.fbxParent = None # set later on IF the parent is in the selection. if matrixWorld: self.matrixWorld = matrixWorld * GLOBAL_MATRIX - else: self.matrixWorld = ob.matrixWorld * GLOBAL_MATRIX + else: self.matrixWorld = ob.matrix * GLOBAL_MATRIX +# else: self.matrixWorld = ob.matrixWorld * GLOBAL_MATRIX self.__anim_poselist = {} # we should only access this def parRelMatrix(self): @@ -606,7 +607,8 @@ def write(filename, batch_objects = None, \ return self.matrixWorld def setPoseFrame(self, f): - self.__anim_poselist[f] = self.blenObject.matrixWorld.copy() + self.__anim_poselist[f] = self.blenObject.matrix.copy() +# self.__anim_poselist[f] = self.blenObject.matrixWorld.copy() def getAnimParRelMatrix(self, frame): if self.fbxParent: @@ -638,7 +640,7 @@ def write(filename, batch_objects = None, \ - print '\nFBX export starting...', filename + print('\nFBX export starting...', filename) start_time = bpy.sys.time() # start_time = Blender.sys.time() try: @@ -713,7 +715,7 @@ def write(filename, batch_objects = None, \ else: # This is bad because we need the parent relative matrix from the fbx parent (if we have one), dont use anymore #if ob and not matrix: matrix = ob.matrixWorld * GLOBAL_MATRIX - if ob and not matrix: raise "error: this should never happen!" + if ob and not matrix: raise Exception("error: this should never happen!") matrix_rot = matrix #if matrix: @@ -751,7 +753,7 @@ def write(filename, batch_objects = None, \ loc, rot, scale, matrix, matrix_rot = object_tx(ob, loc, matrix, matrix_mod) file.write('\n\t\t\tProperty: "Lcl Translation", "Lcl Translation", "A+",%.15f,%.15f,%.15f' % loc) - file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % eulerRadToDeg(rot)) + file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % tuple(eulerRadToDeg(rot))) # file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % rot) file.write('\n\t\t\tProperty: "Lcl Scaling", "Lcl Scaling", "A+",%.15f,%.15f,%.15f' % scale) return loc, rot, scale, matrix, matrix_rot @@ -1453,7 +1455,7 @@ def write(filename, batch_objects = None, \ # TODO - this is a bit lazy, we could have a simple write loop # for this case because all weights are 1.0 but for now this is ok # Parent Bones arent used all that much anyway. - vgroup_data = [(j, 1.0) for j in xrange(len(my_mesh.blenData.verts))] + vgroup_data = [(j, 1.0) for j in range(len(my_mesh.blenData.verts))] else: # This bone is not a parent of this mesh object, no weights vgroup_data = [] @@ -1728,7 +1730,7 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t\tColorIndex: ') i = -1 - for j in xrange(ii): + for j in range(ii): if i == -1: file.write('%i' % j) i=0 @@ -1786,7 +1788,7 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t\tUVIndex: ') i = -1 - for j in xrange(ii): + for j in range(ii): if i == -1: file.write('%i' % j) i=0 @@ -1890,7 +1892,7 @@ def write(filename, batch_objects = None, \ uv_faces = [None] * len(me.faces) i=-1 - for f, uf in zip(me.faces, uv_faces) + for f, uf in zip(me.faces, uv_faces): # for f in me.faces: try: mat = mats[f.material_index] # try: mat = mats[f.mat] @@ -1955,7 +1957,7 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t}') if len(uvlayers) > 1: - for i in xrange(1, len(uvlayers)): + for i in range(1, len(uvlayers)): file.write('\n\t\tLayer: %i {' % i) file.write('\n\t\t\tVersion: 100') @@ -1983,7 +1985,7 @@ def write(filename, batch_objects = None, \ layer_offset = 0 if uvlayers: layer_offset = len(uvlayers)-1 - for i in xrange(layer_offset, len(collayers)+layer_offset): + for i in range(layer_offset, len(collayers)+layer_offset): file.write('\n\t\tLayer: %i {' % i) file.write('\n\t\t\tVersion: 100') @@ -2033,7 +2035,8 @@ def write(filename, batch_objects = None, \ # if EXP_OBS_SELECTED is false, use sceens objects if not batch_objects: - if EXP_OBS_SELECTED: tmp_objects = sce.objects.context + if EXP_OBS_SELECTED: tmp_objects = context.selected_objects +# if EXP_OBS_SELECTED: tmp_objects = sce.objects.context else: tmp_objects = sce.objects else: tmp_objects = batch_objects @@ -2201,9 +2204,9 @@ def write(filename, batch_objects = None, \ my_mesh = my_object_generic(ob, mtx) my_mesh.blenData = me my_mesh.origData = origData - my_mesh.blenMaterials = material_mapping_local.keys() + my_mesh.blenMaterials = list(material_mapping_local.keys()) my_mesh.blenMaterialList = mats - my_mesh.blenTextures = texture_mapping_local.keys() + my_mesh.blenTextures = list(texture_mapping_local.keys()) # if only 1 null texture then empty the list if len(my_mesh.blenTextures) == 1 and my_mesh.blenTextures[0] == None: @@ -2336,8 +2339,8 @@ def write(filename, batch_objects = None, \ # Finished finding groups we use - materials = [(sane_matname(mat_tex_pair), mat_tex_pair) for mat_tex_pair in materials.iterkeys()] - textures = [(sane_texname(tex), tex) for tex in textures.iterkeys() if tex] + materials = [(sane_matname(mat_tex_pair), mat_tex_pair) for mat_tex_pair in materials.keys()] + textures = [(sane_texname(tex), tex) for tex in textures.keys() if tex] materials.sort() # sort by name textures.sort() @@ -2497,7 +2500,7 @@ Objects: {''') #for bonename, bone, obname, bone_mesh, armob in ob_bones: for my_bone in ob_bones: - if me in my_bone.blenMeshes.itervalues(): + if me in iter(my_bone.blenMeshes.values()): write_sub_deformer_skin(my_mesh, my_bone, weights) # Write pose's really weired, only needed when an armature and mesh are used together @@ -2784,9 +2787,9 @@ Takes: {''') # we have tagged all actious that are used be selected armatures if blenAction: if blenAction.tag: - print '\taction: "%s" exporting...' % blenAction.name + print('\taction: "%s" exporting...' % blenAction.name) else: - print '\taction: "%s" has no armature using it, skipping' % blenAction.name + print('\taction: "%s" has no armature using it, skipping' % blenAction.name) continue if blenAction == None: @@ -2866,7 +2869,7 @@ Takes: {''') file.write('\n\t\t\tVersion: 1.1') file.write('\n\t\t\tChannel: "Transform" {') - context_bone_anim_mats = [ (my_ob.getAnimParRelMatrix(frame), my_ob.getAnimParRelMatrixRot(frame)) for frame in xrange(act_start, act_end+1) ] + context_bone_anim_mats = [ (my_ob.getAnimParRelMatrix(frame), my_ob.getAnimParRelMatrixRot(frame)) for frame in range(act_start, act_end+1) ] # ---------------- # ---------------- @@ -2889,7 +2892,7 @@ Takes: {''') file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation - for i in xrange(3): + for i in range(3): # Loop on each axis of the bone file.write('\n\t\t\t\t\tChannel: "%s" {'% ('XYZ'[i])) # translation file.write('\n\t\t\t\t\t\tDefault: %.15f' % context_bone_anim_vecs[0][i] ) @@ -3064,7 +3067,7 @@ Takes: {''') # copy_images( basepath, [ tex[1] for tex in textures if tex[1] != None ]) bpy.util.copy_images( basepath, [ tex[1] for tex in textures if tex[1] != None ]) - print 'export finished in %.4f sec.' % (bpy.sys.time() - start_time) + print('export finished in %.4f sec.' % (bpy.sys.time() - start_time)) # print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time) return True @@ -3072,7 +3075,7 @@ Takes: {''') # -------------------------------------------- # UI Function - not a part of the exporter. # this is to seperate the user interface from the rest of the exporter. -from Blender import Draw, Window +# from Blender import Draw, Window EVENT_NONE = 0 EVENT_EXIT = 1 EVENT_REDRAW = 2 @@ -3123,15 +3126,15 @@ def fbx_ui_exit(e,v): def do_help(e,v): url = 'http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx' - print 'Trying to open web browser with documentation at this address...' - print '\t' + url + print('Trying to open web browser with documentation at this address...') + print('\t' + url) try: import webbrowser webbrowser.open(url) except: Blender.Draw.PupMenu("Error%t|Opening a webbrowser requires a full python installation") - print '...could not open a browser window.' + print('...could not open a browser window.') @@ -3358,17 +3361,95 @@ def write_ui(): # GLOBALS.clear() -#test = [write_ui] -if __name__ == '__main__': - # Cant call the file selector first because of a bug in the interface that crashes it. - # Blender.Window.FileSelector(write_ui, 'Export FBX', Blender.sys.makename(ext='.fbx')) - #write('/scratch/test.fbx') - #write_ui('/scratch/test.fbx') + +class EXPORT_OT_fbx(bpy.types.Operator): + ''' + Operator documentation text, will be used for the operator tooltip and python docs. + ''' + __idname__ = "export.fbx" + __label__ = "Export FBX" - if not set: - Draw.PupMenu('Error%t|A full install of python2.3 or python 2.4+ is needed to run this script.') - else: - write_ui() + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + + __props__ = [ + bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the PLY file", maxlen= 1024, default=""), + bpy.props.BoolProperty(attr="EXP_OBS_SELECTED", name="Selected Objects", description="Export selected objects on visible layers", default=True), +# bpy.props.BoolProperty(attr="EXP_OBS_SCENE", name="Scene Objects", description="Export all objects in this scene", default=True), + bpy.props.FloatProperty(attr="_SCALE", name="Scale", description="Scale all data, (Note! some imports dont support scaled armatures)", min=0.01, max=1000.0, soft_min=0.01, soft_max=1000.0, default=1.0), + bpy.props.BoolProperty(attr="_XROT90", name="Rot X90", description="Rotate all objects 90 degrese about the X axis", default=True), + bpy.props.BoolProperty(attr="_YROT90", name="Rot Y90", description="Rotate all objects 90 degrese about the Y axis", default=False), + bpy.props.BoolProperty(attr="_ZROT90", name="Rot Z90", description="Rotate all objects 90 degrese about the Z axis", default=False), + bpy.props.BoolProperty(attr="EXP_EMPTY", name="Empties", description="Export empty objects", default=True), + bpy.props.BoolProperty(attr="EXP_CAMERA", name="Cameras", description="Export camera objects", default=True), + bpy.props.BoolProperty(attr="EXP_LAMP", name="Lamps", description="Export lamp objects", default=True), + bpy.props.BoolProperty(attr="EXP_ARMATURE", name="Armatures", description="Export armature objects", default=True), + bpy.props.BoolProperty(attr="EXP_MESH", name="Meshes", description="Export mesh objects", default=True), + bpy.props.BoolProperty(attr="EXP_MESH_APPLY_MOD", name="Modifiers", description="Apply modifiers to mesh objects", default=True), + bpy.props.BoolProperty(attr="EXP_MESH_HQ_NORMALS", name="HQ Normals", description="Generate high quality normals", default=True), + bpy.props.BoolProperty(attr="EXP_IMAGE_COPY", name="Copy Image Files", description="Copy image files to the destination path", default=False), + # armature animation + bpy.props.BoolProperty(attr="ANIM_ENABLE", name="Enable Animation", description="Export keyframe animation", default=True), + bpy.props.BoolProperty(attr="ANIM_OPTIMIZE", name="Optimize Keyframes", description="Remove double keyframes", default=True), + bpy.props.FloatProperty(attr="ANIM_OPTIMIZE_PRECISSION", name="Precision", description="Tolerence for comparing double keyframes (higher for greater accuracy)", min=1, max=16, soft_min=1, soft_max=16, default=6.0), +# bpy.props.BoolProperty(attr="ANIM_ACTION_ALL", name="Current Action", description="Use actions currently applied to the armatures (use scene start/end frame)", default=True), + bpy.props.BoolProperty(attr="ANIM_ACTION_ALL", name="All Actions", description="Use all actions for armatures", default=False), + # batch + bpy.props.BoolProperty(attr="BATCH_ENABLE", name="Enable Batch", description="Automate exporting multiple scenes or groups to files", default=False), + bpy.props.BoolProperty(attr="BATCH_GROUP", name="Group > File", description="Export each group as an FBX file, if false, export each scene as an FBX file", default=False), + bpy.props.BoolProperty(attr="BATCH_OWN_DIR", name="Own Dir", description="Create a dir for each exported file", default=True), + bpy.props.StringProperty(attr="BATCH_FILE_PREFIX", name="Prefix", description="Prefix each file with this name", maxlen= 1024, default=""), + ] + + def poll(self, context): + print("Poll") + return context.active_object != None + + def execute(self, context): + if not self.filename: + raise Exception("filename not set") + + GLOBAL_MATRIX = mtx4_identity + GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = self._SCALE + if self._XROT90: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_x90n + if self._YROT90: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_y90n + if self._ZROT90: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_z90n + + write(self.filename, + None, # XXX + context, + self.EXP_OBS_SELECTED, + self.EXP_MESH, + self.EXP_MESH_APPLY_MOD, + self.EXP_MESH_HQ_NORMALS, + self.EXP_ARMATURE, + self.EXP_LAMP, + self.EXP_CAMERA, + self.EXP_EMPTY, + self.EXP_IMAGE_COPY, + GLOBAL_MATRIX, + self.ANIM_ENABLE, + self.ANIM_OPTIMIZE, + self.ANIM_OPTIMIZE_PRECISSION, + self.ANIM_ACTION_ALL, + self.BATCH_ENABLE, + self.BATCH_GROUP, + self.BATCH_FILE_PREFIX, + self.BATCH_OWN_DIR) + + return ('FINISHED',) + + def invoke(self, context, event): + wm = context.manager + wm.add_fileselect(self.__operator__) + return ('RUNNING_MODAL',) + + +bpy.ops.add(EXPORT_OT_fbx) + +# if __name__ == "__main__": +# bpy.ops.EXPORT_OT_ply(filename="/tmp/test.ply") + # NOTES (all line numbers correspond to original export_fbx.py (under release/scripts) # - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print diff --git a/release/io/export_obj.py b/release/io/export_obj.py index 251fdfcf113..bf3aa4ae819 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -891,6 +891,7 @@ class EXPORT_OT_obj(bpy.types.Operator): * multiple scene export (only active scene is written) * particles ''' + __idname__ = "export.obj" __label__ = 'Export OBJ' # List of operator properties, the attributes will be assigned diff --git a/release/io/export_ply.py b/release/io/export_ply.py index 2028358ff3b..8cbb2d5605a 100644 --- a/release/io/export_ply.py +++ b/release/io/export_ply.py @@ -238,7 +238,7 @@ def write(filename, scene, ob, \ class EXPORT_OT_ply(bpy.types.Operator): ''' - Operator documentatuon text, will be used for the operator tooltip and python docs. + Operator documentation text, will be used for the operator tooltip and python docs. ''' __idname__ = "export.ply" __label__ = "Export PLY" diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 27a0ceb21c1..bc031c4e5f2 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -368,7 +368,7 @@ void RNA_api_object(StructRNA *srna) func= RNA_def_function(srna, "create_mesh", "rna_Object_create_mesh"); RNA_def_function_ui_description(func, "Create a Mesh datablock with modifiers applied."); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); - RNA_def_boolean(func, "apply_modifiers", 0, "", "Apply modifiers."); + parm= RNA_def_boolean(func, "apply_modifiers", 0, "", "Apply modifiers."); RNA_def_property_flag(parm, PROP_REQUIRED); parm= RNA_def_enum(func, "settings", mesh_type_items, 0, "", "Modifier settings to apply."); RNA_def_property_flag(parm, PROP_REQUIRED); From c96041628e141b7bfc1da28be09510e7e0e5cd0a Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 23 Jul 2009 12:55:26 +0000 Subject: [PATCH 51/68] API: - replaced BKE_copy_images with BKE_export_image, now it handles only one image at a time, this is better since for exporters it is easier to export one image at a time writing new image path to a file - exposing BKE_export_image in RNA as Image.export, interestingly, RNA allowed me to define a function with PROP_STRING return type although it doesn't free memory, will fix that in the next commit - removed bpy.util.copy_images Unit tests: - re-wrote a test for BKE_export_image, it's more compact now - moved unit tests to the creator module to avoid another executable, now running tests with `blender --test` - as before, unit tests are built only if WITH_BF_UNIT_TEST is non 0 --- SConstruct | 8 -- release/io/export_fbx.py | 35 +++-- source/blender/blenkernel/BKE_image.h | 5 +- source/blender/blenkernel/intern/image.c | 136 +++++++++---------- source/blender/makesrna/intern/makesrna.c | 2 +- source/blender/makesrna/intern/rna_image.c | 2 + source/blender/python/intern/bpy_interface.c | 2 +- source/blender/python/intern/bpy_util.c | 123 ----------------- source/creator/SConscript | 11 +- source/creator/creator.c | 6 + source/creator/tests/alltest.c | 114 ++++++++++++++++ tools/Blender.py | 2 + tools/btools.py | 6 +- 13 files changed, 225 insertions(+), 227 deletions(-) create mode 100644 source/creator/tests/alltest.c diff --git a/SConstruct b/SConstruct index 2d1daba98b6..c1b4f8da174 100644 --- a/SConstruct +++ b/SConstruct @@ -409,14 +409,6 @@ if 'blender' in B.targets or not env['WITH_BF_NOBLENDER']: #env.BlenderProg(B.root_build_dir, "blender", dobj , [], mainlist + thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender') blen = env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist, [], thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender') - build_data = {"lib": thestatlibs + thesyslibs, "libpath": thelibincs, "blen": blen} - - Export('env') - Export('build_data') - - BuildDir(B.root_build_dir+'/tests', 'tests', duplicate=0) - SConscript(B.root_build_dir+'/tests/SConscript') - if env['WITH_BF_PLAYER']: playerlist = B.create_blender_liblist(env, 'player') env.BlenderProg(B.root_build_dir, "blenderplayer", dobj + playerlist, [], thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blenderplayer') diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index a861436d132..f98cefb076e 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -356,7 +356,7 @@ def write(filename, batch_objects = None, \ EXP_OBS_SELECTED = True, EXP_MESH = True, EXP_MESH_APPLY_MOD = True, - EXP_MESH_HQ_NORMALS = False, +# EXP_MESH_HQ_NORMALS = False, EXP_ARMATURE = True, EXP_LAMP = True, EXP_CAMERA = True, @@ -464,7 +464,7 @@ def write(filename, batch_objects = None, \ False, EXP_MESH, EXP_MESH_APPLY_MOD, - EXP_MESH_HQ_NORMALS, +# EXP_MESH_HQ_NORMALS, EXP_ARMATURE, EXP_LAMP, EXP_CAMERA, @@ -2306,19 +2306,25 @@ def write(filename, batch_objects = None, \ # Build blenObject -> fbxObject mapping # this is needed for groups as well as fbxParenting - for ob in bpy.data.objects: ob.tag = False +# for ob in bpy.data.objects: ob.tag = False # bpy.data.objects.tag = False + + # using a list of object names for tagging (Arystan) + tagged_objects = [] + tmp_obmapping = {} for ob_generic in ob_all_typegroups: for ob_base in ob_generic: - ob_base.blenObject.tag = True + tagged_objects.append(ob_base.blenObject.name) +# ob_base.blenObject.tag = True tmp_obmapping[ob_base.blenObject] = ob_base # Build Groups from objects we export for blenGroup in bpy.data.groups: fbxGroupName = None for ob in blenGroup.objects: - if ob.tag: + if ob.name in tagged_objects: +# if ob.tag: if fbxGroupName == None: fbxGroupName = sane_groupname(blenGroup) groups.append((fbxGroupName, blenGroup)) @@ -2331,7 +2337,8 @@ def write(filename, batch_objects = None, \ for ob_generic in ob_all_typegroups: for my_ob in ob_generic: parent = my_ob.blenObject.parent - if parent and parent.tag: # does it exist and is it in the mapping + if parent and parent.name in tagged_objects: # does it exist and is it in the mapping +# if parent and parent.tag: # does it exist and is it in the mapping my_ob.fbxParent = tmp_obmapping[parent] @@ -2734,9 +2741,11 @@ Connections: {''') tmp_actions = [None] # None is the default action blenActionDefault = None action_lastcompat = None + + # instead of tagging + tagged_actions = [] if ANIM_ACTION_ALL: - for a in bpy.data.actions: a.tag = False # bpy.data.actions.tag = False tmp_actions = list(bpy.data.actions) @@ -2759,7 +2768,8 @@ Connections: {''') if action_chan_names: # at least one channel matches. my_arm.blenActionList.append(action) - action.tag = True + tagged_actions.append(action.name) +# action.tag = True tmp_act_count += 1 # incase there is no actions applied to armatures @@ -2786,7 +2796,8 @@ Takes: {''') for blenAction in tmp_actions: # we have tagged all actious that are used be selected armatures if blenAction: - if blenAction.tag: + if blenAction.name in tagged_actions: +# if blenAction.tag: print('\taction: "%s" exporting...' % blenAction.name) else: print('\taction: "%s" has no armature using it, skipping' % blenAction.name) @@ -3065,7 +3076,7 @@ Takes: {''') # copy images if enabled if EXP_IMAGE_COPY: # copy_images( basepath, [ tex[1] for tex in textures if tex[1] != None ]) - bpy.util.copy_images( basepath, [ tex[1] for tex in textures if tex[1] != None ]) + bpy.util.copy_images( [ tex[1] for tex in textures if tex[1] != None ], basepath) print('export finished in %.4f sec.' % (bpy.sys.time() - start_time)) # print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time) @@ -3393,7 +3404,7 @@ class EXPORT_OT_fbx(bpy.types.Operator): bpy.props.BoolProperty(attr="ANIM_OPTIMIZE", name="Optimize Keyframes", description="Remove double keyframes", default=True), bpy.props.FloatProperty(attr="ANIM_OPTIMIZE_PRECISSION", name="Precision", description="Tolerence for comparing double keyframes (higher for greater accuracy)", min=1, max=16, soft_min=1, soft_max=16, default=6.0), # bpy.props.BoolProperty(attr="ANIM_ACTION_ALL", name="Current Action", description="Use actions currently applied to the armatures (use scene start/end frame)", default=True), - bpy.props.BoolProperty(attr="ANIM_ACTION_ALL", name="All Actions", description="Use all actions for armatures", default=False), + bpy.props.BoolProperty(attr="ANIM_ACTION_ALL", name="All Actions", description="Use all actions for armatures, if false, use current action", default=False), # batch bpy.props.BoolProperty(attr="BATCH_ENABLE", name="Enable Batch", description="Automate exporting multiple scenes or groups to files", default=False), bpy.props.BoolProperty(attr="BATCH_GROUP", name="Group > File", description="Export each group as an FBX file, if false, export each scene as an FBX file", default=False), @@ -3421,7 +3432,7 @@ class EXPORT_OT_fbx(bpy.types.Operator): self.EXP_OBS_SELECTED, self.EXP_MESH, self.EXP_MESH_APPLY_MOD, - self.EXP_MESH_HQ_NORMALS, +# self.EXP_MESH_HQ_NORMALS, self.EXP_ARMATURE, self.EXP_LAMP, self.EXP_CAMERA, diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index f3f5266189a..052f7738f2b 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -155,10 +155,9 @@ struct Image *BKE_image_copy(struct Image *ima); /* merge source into dest, and free source */ void BKE_image_merge(struct Image *dest, struct Image *source); -/* ********************************** FOR EXPORTERS *********************** */ +/* copy image file to a directory rebuilding subdirectory structure */ +int BKE_export_image(struct Image *im, const char *dest_dir, char *out_path, int out_path_len); -/* copy images into dest_dir */ -void BKE_copy_images(struct ListBase *images, char *dest_dir, struct ListBase *filenames); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 833d5ac1a90..a469752602e 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -2117,30 +2117,27 @@ void BKE_image_user_calc_imanr(ImageUser *iuser, int cfra, int fieldnr) } /* - Copy list of images to dest_dir. - - paths is optional, if given, image paths for each image will be written in it. - It will also contain NULLs for images that cannot be copied. - If an image file doesn't exist, NULL is added in paths. + Copy an image to destination directory rebuilding subdirectory structure if needed. + Target image path is written to out_path. + Returns 1 on success, 0 otherwise. Logic: - For each image if it's "below" current .blend file directory, - rebuild the same dir structure in dest_dir. + - if an image is "below" current .blend file directory, rebuild the same dir structure in dest_dir - For example //textures/foo/bar.png becomes - [dest_dir]/textures/foo/bar.png. + For example //textures/foo/bar.png becomes [dest_dir]/textures/foo/bar.png. - If an image is not "below" current .blend file directory, disregard - it's path and copy it in the same directory where 3D file goes. + - if an image is not "below" current .blend file directory, disregard it's path and copy it in the + same directory where 3D file goes. For example //../foo/bar.png becomes [dest_dir]/bar.png. This logic will help ensure that all image paths are relative and that a user gets his images in one place. It'll also provide consistent behaviour across exporters. -*/ -void BKE_copy_images(ListBase *images, char *dest_dir, ListBase *paths) + + */ +int BKE_export_image(Image *im, const char *dest_dir, char *out_path, int out_path_len) { char path[FILE_MAX]; char dir[FILE_MAX]; @@ -2148,88 +2145,77 @@ void BKE_copy_images(ListBase *images, char *dest_dir, ListBase *paths) char blend_dir[FILE_MAX]; /* directory, where current .blend file resides */ char dest_path[FILE_MAX]; int len; - Image *im; - LinkData *link; - if (paths) { - memset(paths, 0, sizeof(*paths)); - } + out_path[0]= 0; BLI_split_dirfile_basic(G.sce, blend_dir, NULL); - - link= images->first; - while (link) { - im= link->data; + if (!strcmp(im->name, "") || im->type != IMA_TYPE_IMAGE) { + if (G.f & G_DEBUG) printf("invalid image type\n"); + return 0; + } - LinkData *ld = MEM_callocN(sizeof(LinkData), "PathLinkData"); - ld->data= NULL; - BLI_addtail(paths, ld); + BLI_strncpy(path, im->name, sizeof(path)); - if (!strcmp(im->name, "") || im->type != IMA_TYPE_IMAGE) - goto next; + /* expand "//" in filename and get absolute path */ + BLI_convertstringcode(path, G.sce); - BLI_strncpy(path, im->name, sizeof(path)); - - /* expand "//" in filename and get absolute path */ - BLI_convertstringcode(path, G.sce); - - /* in unit tests, we don't want to modify the filesystem */ + /* in unit tests, we don't want to modify the filesystem */ #ifndef WITH_UNIT_TEST - /* proceed only if image file exists */ - if (!BLI_exists(path)) - goto next; + /* proceed only if image file exists */ + if (!BLI_exists(path)) { + if (G.f & G_DEBUG) printf("%s doesn't exist\n", path); + goto next; + } #endif - /* get the directory part */ - BLI_split_dirfile_basic(path, dir, base); + /* get the directory part */ + BLI_split_dirfile_basic(path, dir, base); - len= strlen(blend_dir); + len= strlen(blend_dir); - /* if image is "below" current .blend file directory */ - if (!strncmp(path, blend_dir, len)) { + /* if image is "below" current .blend file directory */ + if (!strncmp(path, blend_dir, len)) { - /* if image is _in_ current .blend file directory */ - if (!strcmp(dir, blend_dir)) { - /* copy to dest_dir */ - BLI_join_dirfile(dest_path, dest_dir, base); - } - /* "below" */ - else { - char rel[FILE_MAX]; - - /* rel = image_path_dir - blend_dir */ - BLI_strncpy(rel, dir + len, sizeof(rel)); - - BLI_join_dirfile(dest_path, dest_dir, rel); - -#ifndef WITH_UNIT_TEST - /* build identical directory structure under dest_dir */ - BLI_make_existing_file(dest_path); -#endif - - BLI_join_dirfile(dest_path, dest_path, base); - } - - } - /* image is out of current directory */ - else { + /* if image is _in_ current .blend file directory */ + if (!strcmp(dir, blend_dir)) { /* copy to dest_dir */ BLI_join_dirfile(dest_path, dest_dir, base); } + /* "below" */ + else { + char rel[FILE_MAX]; + + /* rel = image_path_dir - blend_dir */ + BLI_strncpy(rel, dir + len, sizeof(rel)); + + BLI_join_dirfile(dest_path, dest_dir, rel); #ifndef WITH_UNIT_TEST - if (BLI_copy_fileops(path, dest_path) != 0) - goto next; + /* build identical directory structure under dest_dir */ + BLI_make_existing_file(dest_path); #endif - if (paths) { - len= strlen(dest_path) + 1; - ld->data= MEM_callocN(len, "PathLinkData"); - BLI_strncpy(ld->data, dest_path, len); + BLI_join_dirfile(dest_path, dest_path, base); } - - next: - link= link->next; + } + /* image is out of current directory */ + else { + /* copy to dest_dir */ + BLI_join_dirfile(dest_path, dest_dir, base); + } + +#ifndef WITH_UNIT_TEST + if (G.f & G_DEBUG) printf("copying %s to %s\n", path, dest_path); + + if (BLI_copy_fileops(path, dest_path) != 0) { + if (G.f & G_DEBUG) printf("couldn't copy %s to %s\n", path, dest_path); + return 0; + } +#endif + + BLI_strncpy(out_path, dest_path, out_path_len); + + return 1; } diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 63ce5786ae3..03d8edde4b3 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1936,7 +1936,7 @@ RNAProcessItem PROCESS_ITEMS[]= { {"rna_fcurve.c", NULL, RNA_def_fcurve}, {"rna_fluidsim.c", NULL, RNA_def_fluidsim}, {"rna_group.c", NULL, RNA_def_group}, - {"rna_image.c", NULL, RNA_def_image}, + {"rna_image.c", "rna_image_api.c", RNA_def_image}, {"rna_key.c", NULL, RNA_def_key}, {"rna_lamp.c", NULL, RNA_def_lamp}, {"rna_lattice.c", NULL, RNA_def_lattice}, diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index fbef838d06c..5366296a6b8 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -312,6 +312,8 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_int_funcs(prop, "rna_Image_depth_get", NULL, NULL); RNA_def_property_ui_text(prop, "Depth", "Image bit depth."); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + RNA_api_image(srna); } void RNA_def_image(BlenderRNA *brna) diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index f045e977394..4ab79dcf074 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -93,7 +93,7 @@ void BPY_update_modules( void ) PyObject *mod= PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0); PyModule_AddObject( mod, "data", BPY_rna_module() ); PyModule_AddObject( mod, "types", BPY_rna_types() ); - PyModule_AddObject( mod, "util", BPY_util_module() ); + /* PyModule_AddObject( mod, "util", BPY_util_module() ); */ /* XXX this will move to bpy.util */ PyModule_AddObject( mod, "sys", BPY_sys_module() ); diff --git a/source/blender/python/intern/bpy_util.c b/source/blender/python/intern/bpy_util.c index dc3132814b4..89a68ba576c 100644 --- a/source/blender/python/intern/bpy_util.c +++ b/source/blender/python/intern/bpy_util.c @@ -488,126 +488,3 @@ int BPy_errors_to_report(ReportList *reports) Py_DECREF(pystring); return 1; } - - -/* bpy.util module */ -static PyObject *bpy_util_copy_images(PyObject *self, PyObject *args); - -struct PyMethodDef bpy_util_methods[] = { - {"copy_images", bpy_util_copy_images, METH_VARARGS, NULL}, - {NULL, NULL, 0, NULL} -}; - -#if PY_VERSION_HEX >= 0x03000000 -static struct PyModuleDef bpy_util_module = { - PyModuleDef_HEAD_INIT, - "bpyutil", - NULL, - -1, - bpy_util_methods, - NULL, NULL, NULL, NULL -}; -#endif - -PyObject *BPY_util_module( void ) -{ - PyObject *submodule, *dict; - -#if PY_VERSION_HEX >= 0x03000000 - submodule= PyModule_Create(&bpy_util_module); -#else /* Py2.x */ - submodule= Py_InitModule3("bpyutil", bpy_util_methods, NULL); -#endif - - dict = PyModule_GetDict(submodule); - - return submodule; -} - -/* - copy_images(images, dest_dir) - return filenames -*/ -static PyObject *bpy_util_copy_images(PyObject *self, PyObject *args) -{ - const char *dest_dir; - ListBase *images; - ListBase *paths; - LinkData *link; - PyObject *seq; - PyObject *ret; - PyObject *item; - int i; - int len; - - /* check args/types */ - if (!PyArg_ParseTuple(args, "Os", &seq, &dest_dir)) { - PyErr_SetString(PyExc_TypeError, "Invalid arguments."); - return NULL; - } - - /* expecting a sequence of Image objects */ - if (!PySequence_Check(seq)) { - PyErr_SetString(PyExc_TypeError, "Expected a sequence of images."); - return NULL; - } - - /* create image list */ - len= PySequence_Size(seq); - - if (!len) { - PyErr_SetString(PyExc_TypeError, "At least one image should be specified."); - return NULL; - } - - /* make sure all sequence items are Image */ - for(i= 0; i < len; i++) { - item= PySequence_GetItem(seq, i); - - if (!BPy_StructRNA_Check(item) || ((BPy_StructRNA*)item)->ptr.type != &RNA_Image) { - PyErr_SetString(PyExc_TypeError, "Expected a sequence of Image objects."); - return NULL; - } - } - - images= MEM_callocN(sizeof(*images), "ListBase of images"); - - for(i= 0; i < len; i++) { - BPy_StructRNA* srna; - - item= PySequence_GetItem(seq, i); - srna= (BPy_StructRNA*)item; - - link= MEM_callocN(sizeof(LinkData), "LinkData image"); - link->data= srna->ptr.data; - BLI_addtail(images, link); - - Py_DECREF(item); - } - - paths= MEM_callocN(sizeof(*paths), "ListBase of image paths"); - - /* call BKE_copy_images */ - BKE_copy_images(images, dest_dir, paths); - - /* convert filenames */ - ret= PyList_New(0); - - for(link= paths->first; link; link= link->next) { - if (link->data) { - item= PyUnicode_FromString(link->data); - PyList_Append(ret, item); - Py_DECREF(item); - } - else { - PyList_Append(ret, Py_None); - } - } - - /* free memory */ - BLI_freelistN(images); - BLI_freelistN(paths); - - /* return filenames */ - return ret; -} diff --git a/source/creator/SConscript b/source/creator/SConscript index 75e7494ebb5..82059c846e6 100644 --- a/source/creator/SConscript +++ b/source/creator/SConscript @@ -1,7 +1,10 @@ #!/usr/bin/python Import ('env') -sources = 'creator.c' +sources = ['creator.c'] + +if env['WITH_BF_UNIT_TEST']: + sources += env.Glob('tests/*.c') incs = '#/intern/guardedalloc ../blender/blenlib ../blender/blenkernel' incs += ' ../blender/editors/include ../blender/blenloader ../blender/imbuf' @@ -11,6 +14,10 @@ incs += ' ../kernel/gen_system #/extern/glew/include ../blender/gpu' incs += ' ' + env['BF_OPENGL_INC'] defs = [] + +if env['WITH_BF_UNIT_TEST']: + defs.append('WITH_UNIT_TEST') + if env['WITH_BF_QUICKTIME']: incs += ' ' + env['BF_QUICKTIME_INC'] defs.append('WITH_QUICKTIME') @@ -32,4 +39,4 @@ if env['WITH_BF_PYTHON']: else: defs.append('DISABLE_PYTHON') -env.BlenderLib ( libname = 'bf_creator', sources = Split(sources), includes = Split(incs), defines = defs, libtype='core', priority = 0 ) +env.BlenderLib ( libname = 'bf_creator', sources = sources, includes = Split(incs), defines = defs, libtype='core', priority = 0 ) diff --git a/source/creator/creator.c b/source/creator/creator.c index 8e0152b5e63..3a2a0ff32bc 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -338,6 +338,12 @@ int main(int argc, char **argv) exit(0); } +#ifdef WITH_UNIT_TEST + if (!strcmp(argv[a], "--test")) { + exit(run_tests()); + } +#endif + /* end argument processing after -- */ if (!strcmp( argv[a], "--")){ a = argc; diff --git a/source/creator/tests/alltest.c b/source/creator/tests/alltest.c new file mode 100644 index 00000000000..2173ecd224f --- /dev/null +++ b/source/creator/tests/alltest.c @@ -0,0 +1,114 @@ +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BKE_blender.h" +#include "BKE_image.h" +#include "BKE_utildefines.h" +#include "BKE_global.h" + +#include "BLI_listbase.h" + +#include "DNA_image_types.h" + +char bprogname[FILE_MAXDIR+FILE_MAXFILE]; +char btempdir[FILE_MAXDIR+FILE_MAXFILE]; + +typedef struct ImageTestData { + char *path; /* image filename */ + char *expect_path; /* file path that we expect */ + int type; /* image type */ + int ret; /* expected function return value */ +} ImageTestData; + +/* check that BKE_copy_images manipulates paths correctly */ +START_TEST(test_copy_images) +{ + char *dest_dir[] = {"/tmp/", "/tmp", NULL}; + char **dir; + ImageTestData *test; + + /* XXX Windows not tested */ +#ifdef WIN32 + static ImageTestData test_data[] = { + {"//bar/image.png", "C:\\Temp\\bar\\image.png"}, + /* TODO add more */ + {NULL, NULL}, + }; + + BLI_strncpy(G.sce, "C:\\Temp\untitled.blend", sizeof(G.sce)); +#else + /* + XXX are these paths possible in image->name?: + + ./foo/image.png + ../foo/image.png + + if so, BKE_copy_images currently doesn't support them! + */ + static ImageTestData test_data[] = { + {"//bar/image.png", "/tmp/bar/image.png", IMA_TYPE_IMAGE, 1}, + {"/foo/bar/image.png", "/tmp/image.png", IMA_TYPE_IMAGE, 1}, + {"//image.png", "/tmp/image.png", IMA_TYPE_IMAGE, 1}, + {"//../../../foo/bar/image.png", "/tmp/image.png", IMA_TYPE_IMAGE, 1}, + {"//./foo/bar/image.png", "/tmp/foo/bar/image.png", IMA_TYPE_IMAGE, 1}, + {"/tmp/image.png", "/tmp/image.png", IMA_TYPE_IMAGE, 1}, + {"//textures/test/foo/bar/image.png", "/tmp/textures/test/foo/bar/image.png", IMA_TYPE_IMAGE, 1}, + {"//textures/test/foo/bar/image.png", "", IMA_TYPE_MULTILAYER, 0}, + {"", "", IMA_TYPE_IMAGE, 0}, + {NULL, NULL}, + }; + + /* substitute G.sce */ + BLI_strncpy(G.sce, "/tmp/foo/bar/untitled.blend", sizeof(G.sce)); +#endif + + for (dir = dest_dir; *dir; dir++) { + for (test= &test_data[0]; test->path; test++) { + Image image; + char path[FILE_MAX]; + int ret; + + BLI_strncpy(image.name, test->path, sizeof(image.name)); + image.type= test->type; + + ret= BKE_export_image(&image, *dest_dir, path, sizeof(path)); + + /* check if we got correct output */ + fail_if(ret != test->ret, "For image with filename %s and type %d, expected %d as return value got %d.", + test->path, test->type, test->ret, ret); + fail_if(strcmp(path, test->expect_path), "For image with filename %s and type %d, expected path '%s' got '%s'.", + test->path, test->type, test->expect_path, path); + } + } +} +END_TEST + +static Suite *image_suite(void) +{ + Suite *s = suite_create("Image"); + + /* Core test case */ + TCase *tc_core = tcase_create("Core"); + tcase_add_test(tc_core, test_copy_images); + suite_add_tcase(s, tc_core); + + return s; +} + +int run_tests() +{ + int totfail; + Suite *s = image_suite(); + SRunner *sr = srunner_create(s); + + /* run tests */ + srunner_run_all(sr, CK_VERBOSE); + + totfail= srunner_ntests_failed(sr); + srunner_free(sr); + + return !totfail ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/tools/Blender.py b/tools/Blender.py index 8d213166a4b..c3ba9b2e76d 100644 --- a/tools/Blender.py +++ b/tools/Blender.py @@ -161,6 +161,8 @@ def setup_syslibs(lenv): ] syslibs += Split(lenv['BF_FREETYPE_LIB']) + if lenv['WITH_BF_UNIT_TEST']: + syslibs.append(lenv['BF_CHECK_LIB']) if lenv['WITH_BF_PYTHON'] and not lenv['WITH_BF_STATICPYTHON']: if lenv['BF_DEBUG'] and lenv['OURPLATFORM'] in ('win32-vc', 'win64-vc'): syslibs.append(lenv['BF_PYTHON_LIB']+'_d') diff --git a/tools/btools.py b/tools/btools.py index f4d79b1bb24..521596eca97 100755 --- a/tools/btools.py +++ b/tools/btools.py @@ -66,7 +66,8 @@ def validate_arguments(args, bc): 'BF_NUMJOBS', 'BF_MSVS', - 'WITH_BF_UNIT_TEST' + 'WITH_BF_UNIT_TEST', + 'BF_CHECK_LIB', ] # Have options here that scons expects to be lists @@ -360,7 +361,8 @@ def read_opts(cfg, args): ('BF_NUMJOBS', 'Number of build processes to spawn', '1'), ('BF_MSVS', 'Generate MSVS project files and solution', False), - (BoolVariable('WITH_BF_UNIT_TEST', 'Build unit tests', False)) + (BoolVariable('WITH_BF_UNIT_TEST', 'Build unit tests', False)), + ('BF_CHECK_LIB', 'Check unit testing framework library', 'check'), ) # end of opts.AddOptions() From d8f4ab2d599956fa4511d4cb8a6b68e613cf239f Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 23 Jul 2009 15:57:30 +0000 Subject: [PATCH 52/68] API: - freeing strings returned by RNA struct functions in RNA_parameter_list_free Unit tests: - check that BKE_export_image actually creates a file. This test is becoming dangerous: it creates and deletes files under /tmp. Having written this complicated test function I now realize it's much easier to write tests in a scripted language, which gives more freedom in expressions and need not be compiled. --- release/io/export_fbx.py | 56 ++++++++----- source/blender/blenkernel/intern/image.c | 23 +++-- source/blender/makesrna/intern/rna_access.c | 9 +- source/blender/python/intern/bpy_rna.c | 3 +- source/creator/tests/alltest.c | 93 +++++++++++++++++---- 5 files changed, 129 insertions(+), 55 deletions(-) diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index f98cefb076e..b7caf2b24dc 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -237,24 +237,24 @@ def sane_texname(data): return sane_name(data, sane_name_mapping_tex) def sane_takename(data): return sane_name(data, sane_name_mapping_take) def sane_groupname(data): return sane_name(data, sane_name_mapping_group) -def derived_paths(fname_orig, basepath, FORCE_CWD=False): - ''' - fname_orig - blender path, can be relative - basepath - fname_rel will be relative to this - FORCE_CWD - dont use the basepath, just add a ./ to the filename. - use when we know the file will be in the basepath. - ''' - fname = bpy.sys.expandpath(fname_orig) -# fname = Blender.sys.expandpath(fname_orig) - fname_strip = os.path.basename(fname) -# fname_strip = strip_path(fname) - if FORCE_CWD: - fname_rel = '.' + os.sep + fname_strip - else: - fname_rel = bpy.sys.relpath(fname, basepath) -# fname_rel = Blender.sys.relpath(fname, basepath) - if fname_rel.startswith('//'): fname_rel = '.' + os.sep + fname_rel[2:] - return fname, fname_strip, fname_rel +# def derived_paths(fname_orig, basepath, FORCE_CWD=False): +# ''' +# fname_orig - blender path, can be relative +# basepath - fname_rel will be relative to this +# FORCE_CWD - dont use the basepath, just add a ./ to the filename. +# use when we know the file will be in the basepath. +# ''' +# fname = bpy.sys.expandpath(fname_orig) +# # fname = Blender.sys.expandpath(fname_orig) +# fname_strip = os.path.basename(fname) +# # fname_strip = strip_path(fname) +# if FORCE_CWD: +# fname_rel = '.' + os.sep + fname_strip +# else: +# fname_rel = bpy.sys.relpath(fname, basepath) +# # fname_rel = Blender.sys.relpath(fname, basepath) +# if fname_rel.startswith('//'): fname_rel = '.' + os.sep + fname_rel[2:] +# return fname, fname_strip, fname_rel def mat4x4str(mat): @@ -1324,6 +1324,8 @@ def write(filename, batch_objects = None, \ # tex is an Image (Arystan) def write_video(texname, tex): + if not EXP_IMAGE_COPY: return + # Same as texture really! file.write('\n\tVideo: "Video::%s", "Clip" {' % texname) @@ -1335,7 +1337,10 @@ def write(filename, batch_objects = None, \ Property: "Width", "int", "",0 Property: "Height", "int", "",0''') if tex: - fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) + abspath = tex.export(basepath) + fname_rel = os.path.relpath(abspath, basepath) + fname_strip = os.path.basename(abspath) +# fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) else: fname = fname_strip = fname_rel = '' @@ -1361,6 +1366,8 @@ def write(filename, batch_objects = None, \ def write_texture(texname, tex, num): + if not EXP_IMAGE_COPY: return + # if tex == None then this is a dummy tex file.write('\n\tTexture: "Texture::%s", "TextureVideoClip" {' % texname) file.write('\n\t\tType: "TextureVideoClip"') @@ -1399,7 +1406,10 @@ def write(filename, batch_objects = None, \ file.write('\n\t\tMedia: "Video::%s"' % texname) if tex: - fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) + abspath = tex.export(basepath) + fname_rel = os.path.relpath(abspath, basepath) + fname_strip = os.path.basename(abspath) +# fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) else: fname = fname_strip = fname_rel = '' @@ -3074,9 +3084,9 @@ Takes: {''') # copy images if enabled - if EXP_IMAGE_COPY: -# copy_images( basepath, [ tex[1] for tex in textures if tex[1] != None ]) - bpy.util.copy_images( [ tex[1] for tex in textures if tex[1] != None ], basepath) +# if EXP_IMAGE_COPY: +# # copy_images( basepath, [ tex[1] for tex in textures if tex[1] != None ]) +# bpy.util.copy_images( [ tex[1] for tex in textures if tex[1] != None ], basepath) print('export finished in %.4f sec.' % (bpy.sys.time() - start_time)) # print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time) diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index a469752602e..f0b29f766ec 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -2160,14 +2160,11 @@ int BKE_export_image(Image *im, const char *dest_dir, char *out_path, int out_pa /* expand "//" in filename and get absolute path */ BLI_convertstringcode(path, G.sce); - /* in unit tests, we don't want to modify the filesystem */ -#ifndef WITH_UNIT_TEST /* proceed only if image file exists */ if (!BLI_exists(path)) { if (G.f & G_DEBUG) printf("%s doesn't exist\n", path); - goto next; + return 0; } -#endif /* get the directory part */ BLI_split_dirfile_basic(path, dir, base); @@ -2191,10 +2188,8 @@ int BKE_export_image(Image *im, const char *dest_dir, char *out_path, int out_pa BLI_join_dirfile(dest_path, dest_dir, rel); -#ifndef WITH_UNIT_TEST /* build identical directory structure under dest_dir */ - BLI_make_existing_file(dest_path); -#endif + BLI_recurdir_fileops(dest_path); BLI_join_dirfile(dest_path, dest_path, base); } @@ -2206,14 +2201,18 @@ int BKE_export_image(Image *im, const char *dest_dir, char *out_path, int out_pa BLI_join_dirfile(dest_path, dest_dir, base); } -#ifndef WITH_UNIT_TEST if (G.f & G_DEBUG) printf("copying %s to %s\n", path, dest_path); - if (BLI_copy_fileops(path, dest_path) != 0) { - if (G.f & G_DEBUG) printf("couldn't copy %s to %s\n", path, dest_path); - return 0; + /* only copy if paths differ */ + if (strcmp(path, dest_path)) { + if (BLI_copy_fileops(path, dest_path) != 0) { + if (G.f & G_DEBUG) printf("couldn't copy %s to %s\n", path, dest_path); + return 0; + } + } + else if (G.f & G_DEBUG){ + printf("%s and %s are the same file\n", path, dest_path); } -#endif BLI_strncpy(out_path, dest_path, out_path_len); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 9c133605f96..c9bf97fd274 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -2820,9 +2820,12 @@ void RNA_parameter_list_free(ParameterList *parms) if(parm->type == PROP_COLLECTION) { BLI_freelistN((ListBase*)((char*)parms->data+tot)); } - else if(parm->flag & PROP_DYNAMIC_ARRAY) { - /* for dynamic arrays, data is a pointer to an array */ - MEM_freeN(*(char**)parms->data+tot); + else if(parm == parms->func->ret) { + /* for dynamic arrays and strings, data is a pointer to an array */ + char *ptr= *(char**)((char*)parms->data+tot); + if((parm->flag & PROP_DYNAMIC_ARRAY || parm->type == PROP_STRING) && ptr) { + MEM_freeN(ptr); + } } tot+= rna_parameter_size(parm); diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index c7767c6c08e..780c58e2877 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -1844,7 +1844,8 @@ PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *data) break; case PROP_STRING: { - ret = PyUnicode_FromString( *(char**)data ); + char *ptr = *(char**)data; + ret = ptr ? PyUnicode_FromString( ptr ) : Py_None; break; } case PROP_ENUM: diff --git a/source/creator/tests/alltest.c b/source/creator/tests/alltest.c index 2173ecd224f..8bb2b1a9bb0 100644 --- a/source/creator/tests/alltest.c +++ b/source/creator/tests/alltest.c @@ -10,6 +10,9 @@ #include "BKE_global.h" #include "BLI_listbase.h" +#include "BLI_util.h" +#include "BLI_fileops.h" +#include "BLI_string.h" #include "DNA_image_types.h" @@ -21,12 +24,31 @@ typedef struct ImageTestData { char *expect_path; /* file path that we expect */ int type; /* image type */ int ret; /* expected function return value */ + int create_file; /* whether the file should be created */ } ImageTestData; +/* recursively deletes a directory only if it is under /tmp */ +static void delete_only_tmp(char *path, int dir) { +#ifdef WIN32 +#else + if (!strncmp(path, "/tmp/", 5) && BLI_exists(path)) { + BLI_delete(path, dir, 1); + } +#endif +} + +static void touch_only_tmp(char *path) { +#ifdef WIN32 +#else + if (!strncmp(path, "/tmp/", 5)) { + BLI_touch(path); + } +#endif +} + /* check that BKE_copy_images manipulates paths correctly */ START_TEST(test_copy_images) { - char *dest_dir[] = {"/tmp/", "/tmp", NULL}; char **dir; ImageTestData *test; @@ -49,38 +71,77 @@ START_TEST(test_copy_images) if so, BKE_copy_images currently doesn't support them! */ static ImageTestData test_data[] = { - {"//bar/image.png", "/tmp/bar/image.png", IMA_TYPE_IMAGE, 1}, - {"/foo/bar/image.png", "/tmp/image.png", IMA_TYPE_IMAGE, 1}, - {"//image.png", "/tmp/image.png", IMA_TYPE_IMAGE, 1}, - {"//../../../foo/bar/image.png", "/tmp/image.png", IMA_TYPE_IMAGE, 1}, - {"//./foo/bar/image.png", "/tmp/foo/bar/image.png", IMA_TYPE_IMAGE, 1}, - {"/tmp/image.png", "/tmp/image.png", IMA_TYPE_IMAGE, 1}, - {"//textures/test/foo/bar/image.png", "/tmp/textures/test/foo/bar/image.png", IMA_TYPE_IMAGE, 1}, - {"//textures/test/foo/bar/image.png", "", IMA_TYPE_MULTILAYER, 0}, - {"", "", IMA_TYPE_IMAGE, 0}, + {"//bar/image.png", "/tmp/blender/dest/bar/image.png", IMA_TYPE_IMAGE, 1, 1}, + {"//image.png", "/tmp/blender/dest/image.png", IMA_TYPE_IMAGE, 1, 1}, + {"//textures/test/foo/bar/image.png", "/tmp/blender/dest/textures/test/foo/bar/image.png", IMA_TYPE_IMAGE, 1, 1}, + {"//textures/test/foo/bar/image.png", "", IMA_TYPE_MULTILAYER, 0, 1}, + {"//./foo/bar/image.png", "/tmp/blender/dest/foo/bar/image.png", IMA_TYPE_IMAGE, 1, 1}, + {"//../foo/bar/image.png", "/tmp/blender/dest/image.png", IMA_TYPE_IMAGE, 1, 1}, + {"/tmp/blender/image.png", "/tmp/blender/dest/image.png", IMA_TYPE_IMAGE, 1, 1}, + /* expecting it to return 1 when src and dest are the same file */ + {"/tmp/blender/foo/bar/image.png", "/tmp/blender/dest/image.png", IMA_TYPE_IMAGE, 1, 1}, + {"/tmp/blender/dest/image.png", "/tmp/blender/dest/image.png", IMA_TYPE_IMAGE, 1, 1}, + /* expecting empty path and 0 return value for non-existing files */ + {"/tmp/blender/src/file-not-created", "", IMA_TYPE_IMAGE, 0, 0}, + {"", "", IMA_TYPE_IMAGE, 0, 0}, {NULL, NULL}, }; + char *dest_dir[] = {"/tmp/blender/dest/", "/tmp/blender/dest", NULL}; + const char *blend_dir = "/tmp/blender/src"; + /* substitute G.sce */ - BLI_strncpy(G.sce, "/tmp/foo/bar/untitled.blend", sizeof(G.sce)); + BLI_snprintf(G.sce, sizeof(G.sce), "%s/untitled.blend", blend_dir); #endif + /* only delete files/directories under /tmp/ ! */ + delete_only_tmp(blend_dir, 1); + + for (dir = dest_dir; *dir; dir++) { + delete_only_tmp(*dir, 1); + } + + /* create files */ + BLI_recurdir_fileops(blend_dir); + + /* create fake empty source files */ + for (test= &test_data[0]; test->path; test++) { + char dir[FILE_MAX]; + char path[FILE_MAX]; + + if (!test->create_file) continue; + + /* expand "//" */ + BLI_strncpy(path, test->path, sizeof(path)); + BLI_convertstringcode(path, G.sce); + + /* create a directory */ + BLI_split_dirfile_basic(path, dir, NULL); + BLI_recurdir_fileops(dir); + + /* create a file */ + touch_only_tmp(path); + } + for (dir = dest_dir; *dir; dir++) { for (test= &test_data[0]; test->path; test++) { Image image; char path[FILE_MAX]; + char part[200]; int ret; BLI_strncpy(image.name, test->path, sizeof(image.name)); image.type= test->type; - ret= BKE_export_image(&image, *dest_dir, path, sizeof(path)); + ret= BKE_export_image(&image, *dir, path, sizeof(path)); /* check if we got correct output */ - fail_if(ret != test->ret, "For image with filename %s and type %d, expected %d as return value got %d.", - test->path, test->type, test->ret, ret); - fail_if(strcmp(path, test->expect_path), "For image with filename %s and type %d, expected path '%s' got '%s'.", - test->path, test->type, test->expect_path, path); + BLI_snprintf(part, sizeof(part), "For image with filename %s and type %d", test->path, test->type); + fail_if(ret != test->ret, "%s, expected %d as return value got %d.", part, test->ret, ret); + fail_if(strcmp(path, test->expect_path), "%s, expected path %s got \"%s\".", part, test->expect_path, path); + if (test->ret == ret && ret == 1) { + fail_if(!BLI_exists(test->expect_path), "%s, expected %s to be created.", part, test->expect_path); + } } } } From d63d703842f62916f6242ac7c9331edd4943dc5e Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Fri, 24 Jul 2009 14:26:47 +0000 Subject: [PATCH 53/68] - added operator for OBJ importer plus some python 3 conversions --- release/io/export_obj.py | 9 +- release/io/import_obj.py | 144 ++++++++++++++++------ source/blender/makesrna/intern/rna_pose.c | 7 +- 3 files changed, 112 insertions(+), 48 deletions(-) diff --git a/release/io/export_obj.py b/release/io/export_obj.py index bf3aa4ae819..4d67903c343 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -200,19 +200,16 @@ def copy_images(dest_dir): # XXX not converted def test_nurbs_compat(ob): - if ob.type != 'CURVE': + if ob.type != 'Curve': return False - for nu in ob.data.curves: + for nu in ob.data: if (not nu.knotsV) and nu.type != 1: # not a surface and not bezier return True - -# for nu in ob.data: -# if (not nu.knotsV) and nu.type != 1: # not a surface and not bezier -# return True return False + # XXX not converted def write_nurb(file, ob, ob_mat): tot_verts = 0 diff --git a/release/io/import_obj.py b/release/io/import_obj.py index 659d5207261..a4778ac7790 100644 --- a/release/io/import_obj.py +++ b/release/io/import_obj.py @@ -57,7 +57,8 @@ def stripFile(path): lastSlash= max(path.rfind('\\'), path.rfind('/')) if lastSlash != -1: path= path[:lastSlash] - return '%s%s' % (path, sys.sep) + return '%s%s' % (path, os.sep) +# return '%s%s' % (path, sys.sep) def stripPath(path): '''Strips the slashes from the back of a string''' @@ -219,8 +220,9 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ # Add an MTL with the same name as the obj if no MTLs are spesified. temp_mtl= stripExt(stripPath(filepath))+ '.mtl' - - if sys.exists(DIR + temp_mtl) and temp_mtl not in material_libs: + + if os.path.exists(DIR + temp_mtl) and temp_mtl not in material_libs: +# if sys.exists(DIR + temp_mtl) and temp_mtl not in material_libs: material_libs.append( temp_mtl ) del temp_mtl @@ -236,7 +238,7 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ for libname in material_libs: mtlpath= DIR + libname - if not bpy.sys.exists(mtlpath): + if not os.path.exists(mtlpath): # if not sys.exists(mtlpath): #print '\tError Missing MTL: "%s"' % mtlpath pass @@ -247,7 +249,7 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ for line in mtl: #.xreadlines(): if line.startswith('newmtl'): context_material_name= line_value(line.split()) - if unique_materials.has_key(context_material_name): + if context_material_name in unique_materials: context_material = unique_materials[ context_material_name ] else: context_material = None @@ -377,14 +379,14 @@ def split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP, face_vert_loc_indicies[enum] = vert_remap[i] # remap to the local index matname= face[2] - if matname and not unique_materials_split.has_key(matname): + if matname and matname not in unique_materials_split: unique_materials_split[matname] = unique_materials[matname] faces_split.append(face) # remove one of the itemas and reorder - return [(value[0], value[1], value[2], key_to_name(key)) for key, value in face_split_dict.iteritems()] + return [(value[0], value[1], value[2], key_to_name(key)) for key, value in face_split_dict.items()] def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc, verts_tex, faces, unique_materials, unique_material_images, unique_smooth_groups, vertex_groups, dataname): @@ -397,7 +399,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l if unique_smooth_groups: sharp_edges= {} - smooth_group_users= dict([ (context_smooth_group, {}) for context_smooth_group in unique_smooth_groups.iterkeys() ]) + smooth_group_users= dict([ (context_smooth_group, {}) for context_smooth_group in unique_smooth_groups.keys() ]) context_smooth_group_old= -1 # Split fgons into tri's @@ -408,7 +410,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l context_object= None # reverse loop through face indicies - for f_idx in xrange(len(faces)-1, -1, -1): + for f_idx in range(len(faces)-1, -1, -1): face_vert_loc_indicies,\ face_vert_tex_indicies,\ @@ -425,7 +427,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l if CREATE_EDGES: # generators are better in python 2.4+ but can't be used in 2.3 # edges.extend( (face_vert_loc_indicies[i], face_vert_loc_indicies[i+1]) for i in xrange(len_face_vert_loc_indicies-1) ) - edges.extend( [(face_vert_loc_indicies[i], face_vert_loc_indicies[i+1]) for i in xrange(len_face_vert_loc_indicies-1)] ) + edges.extend( [(face_vert_loc_indicies[i], face_vert_loc_indicies[i+1]) for i in range(len_face_vert_loc_indicies-1)] ) faces.pop(f_idx) else: @@ -437,7 +439,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l edge_dict= smooth_group_users[context_smooth_group] context_smooth_group_old= context_smooth_group - for i in xrange(len_face_vert_loc_indicies): + for i in range(len_face_vert_loc_indicies): i1= face_vert_loc_indicies[i] i2= face_vert_loc_indicies[i-1] if i1>i2: i1,i2= i2,i1 @@ -485,8 +487,8 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l # Build sharp edges if unique_smooth_groups: - for edge_dict in smooth_group_users.itervalues(): - for key, users in edge_dict.iteritems(): + for edge_dict in smooth_group_users.values(): + for key, users in edge_dict.items(): if users==1: # This edge is on the boundry of a group sharp_edges[key]= None @@ -496,7 +498,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l materials= [None] * len(unique_materials) - for name, index in material_mapping.iteritems(): + for name, index in material_mapping.items(): materials[index]= unique_materials[name] me= bpy.data.add_mesh(dataname) @@ -639,7 +641,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l # Create the vertex groups. No need to have the flag passed here since we test for the # content of the vertex_groups. If the user selects to NOT have vertex groups saved then # the following test will never run - for group_name, group_indicies in vertex_groups.iteritems(): + for group_name, group_indicies in vertex_groups.items(): group= ob.add_vertex_group(group_name) # me.addVertGroup(group_name) for vertex_index in group_indicies: @@ -660,16 +662,16 @@ def create_nurbs(scn, context_nurbs, vert_loc, new_objects): cstype = context_nurbs.get('cstype', None) if cstype == None: - print '\tWarning, cstype not found' + print('\tWarning, cstype not found') return if cstype != 'bspline': - print '\tWarning, cstype is not supported (only bspline)' + print('\tWarning, cstype is not supported (only bspline)') return if not curv_idx: - print '\tWarning, curv argument empty or not set' + print('\tWarning, curv argument empty or not set') return if len(deg) > 1 or parm_v: - print '\tWarning, surfaces not supported' + print('\tWarning, surfaces not supported') return cu = bpy.data.curves.new(name, 'Curve') @@ -691,7 +693,7 @@ def create_nurbs(scn, context_nurbs, vert_loc, new_objects): # get for endpoint flag from the weighting if curv_range and len(parm_u) > deg[0]+1: do_endpoints = True - for i in xrange(deg[0]+1): + for i in range(deg[0]+1): if abs(parm_u[i]-curv_range[0]) > 0.0001: do_endpoints = False @@ -773,7 +775,7 @@ def load_obj(filepath, This function passes the file and sends the data off to be split into objects and then converted into mesh objects ''' - print '\nimporting obj "%s"' % filepath + print('\nimporting obj "%s"' % filepath) if SPLIT_OBJECTS or SPLIT_GROUPS or SPLIT_MATERIALS: POLYGROUPS = False @@ -816,7 +818,7 @@ def load_obj(filepath, # so we need to know weather context_multi_line= '' - print '\tparsing obj file "%s"...' % filepath, + print('\tparsing obj file "%s"...' % filepath, end=' ') time_sub= bpy.sys.time() # time_sub= sys.time() @@ -1028,31 +1030,31 @@ def load_obj(filepath, file.close() time_new= bpy.sys.time() # time_new= sys.time() - print '%.4f sec' % (time_new-time_sub) + print('%.4f sec' % (time_new-time_sub)) time_sub= time_new - print '\tloading materials and images...', + print('\tloading materials and images...', end=' ') create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH) time_new= bpy.sys.time() # time_new= sys.time() - print '%.4f sec' % (time_new-time_sub) + print('%.4f sec' % (time_new-time_sub)) time_sub= time_new if not ROTATE_X90: verts_loc[:] = [(v[0], v[2], -v[1]) for v in verts_loc] # deselect all - if context.selected_objects: - bpy.ops.OBJECT_OT_select_all_toggle() +# if context.selected_objects: +# bpy.ops.OBJECT_OT_select_all_toggle() scene = context.scene # scn = bpy.data.scenes.active # scn.objects.selected = [] new_objects= [] # put new objects here - print '\tbuilding geometry...\n\tverts:%i faces:%i materials: %i smoothgroups:%i ...' % ( len(verts_loc), len(faces), len(unique_materials), len(unique_smooth_groups) ), + print('\tbuilding geometry...\n\tverts:%i faces:%i materials: %i smoothgroups:%i ...' % ( len(verts_loc), len(faces), len(unique_materials), len(unique_smooth_groups) ), end=' ') # Split the mesh by objects/materials, may if SPLIT_OBJECTS or SPLIT_GROUPS: SPLIT_OB_OR_GROUP = True else: SPLIT_OB_OR_GROUP = False @@ -1095,8 +1097,8 @@ def load_obj(filepath, time_new= bpy.sys.time() # time_new= sys.time() - print '%.4f sec' % (time_new-time_sub) - print 'finished importing: "%s" in %.4f sec.' % (filepath, (time_new-time_main)) + print('%.4f sec' % (time_new-time_sub)) + print('finished importing: "%s" in %.4f sec.' % (filepath, (time_new-time_main))) DEBUG= True @@ -1189,14 +1191,14 @@ def load_obj_ui(filepath, BATCH_LOAD= False): def do_help(e,v): url = __url__[0] - print 'Trying to open web browser with documentation at this address...' - print '\t' + url + print('Trying to open web browser with documentation at this address...') + print('\t' + url) try: import webbrowser webbrowser.open(url) except: - print '...could not open a browser window.' + print('...could not open a browser window.') def obj_ui(): ui_x, ui_y = GLOBALS['MOUSE'] @@ -1306,11 +1308,11 @@ def load_obj_ui_batch(file): DEBUG= False -if __name__=='__main__' and not DEBUG: - if os and Window.GetKeyQualifiers() & Window.Qual.SHIFT: - Window.FileSelector(load_obj_ui_batch, 'Import OBJ Dir', '') - else: - Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ', '*.obj') +# if __name__=='__main__' and not DEBUG: +# if os and Window.GetKeyQualifiers() & Window.Qual.SHIFT: +# Window.FileSelector(load_obj_ui_batch, 'Import OBJ Dir', '') +# else: +# Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ', '*.obj') # For testing compatibility ''' @@ -1340,6 +1342,70 @@ else: #load_obj('/test.obj') #load_obj('/fe/obj/mba1.obj') + + +class IMPORT_OT_obj(bpy.types.Operator): + ''' + Operator documentation text, will be used for the operator tooltip and python docs. + ''' + __idname__ = "import.obj" + __label__ = "Import OBJ" + + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + + __props__ = [ + bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the PLY file", maxlen= 1024, default= ""), + + bpy.props.BoolProperty(attr="CREATE_SMOOTH_GROUPS", name="Smooth Groups", description="Surround smooth groups by sharp edges", default= True), + bpy.props.BoolProperty(attr="CREATE_FGONS", name="NGons as FGons", description="Import faces with more then 4 verts as fgons", default= True), + bpy.props.BoolProperty(attr="CREATE_EDGES", name="Lines as Edges", description="Import lines and faces with 2 verts as edge", default= True), + bpy.props.BoolProperty(attr="SPLIT_OBJECTS", name="Object", description="Import OBJ Objects into Blender Objects", default= True), + bpy.props.BoolProperty(attr="SPLIT_GROUPS", name="Group", description="Import OBJ Groups into Blender Objects", default= True), + bpy.props.BoolProperty(attr="SPLIT_MATERIALS", name="Material", description="Import each material into a seperate mesh (Avoids > 16 per mesh error)", default= True), + # old comment: only used for user feedback + # disabled this option because in old code a handler for it disabled SPLIT* params, it's not passed to load_obj + # bpy.props.BoolProperty(attr="KEEP_VERT_ORDER", name="Keep Vert Order", description="Keep vert and face order, disables split options, enable for morph targets", default= True), + bpy.props.BoolProperty(attr="ROTATE_X90", name="-X90", description="Rotate X 90.", default= True), + bpy.props.FloatProperty(attr="CLAMP_SIZE", name="Clamp Scale", description="Clamp the size to this maximum (Zero to Disable)", min=0.01, max=1000.0, soft_min=0.0, soft_max=1000.0, default=0.0), + bpy.props.BoolProperty(attr="POLYGROUPS", name="Poly Groups", description="Import OBJ groups as vertex groups.", default= True), + bpy.props.BoolProperty(attr="IMAGE_SEARCH", name="Image Search", description="Search subdirs for any assosiated images (Warning, may be slow)", default= True), + ] + + def poll(self, context): + print("Poll") + return context.active_object != None + + def execute(self, context): + # print("Selected: " + context.active_object.name) + + if not self.filename: + raise Exception("filename not set") + + load_obj(self.filename, + context, + self.CLAMP_SIZE, + self.CREATE_FGONS, + self.CREATE_SMOOTH_GROUPS, + self.CREATE_EDGES, + self.SPLIT_OBJECTS, + self.SPLIT_GROUPS, + self.SPLIT_MATERIALS, + self.ROTATE_X90, + self.IMAGE_SEARCH, + self.POLYGROUPS) + + return ('FINISHED',) + + def invoke(self, context, event): + wm = context.manager + wm.add_fileselect(self.__operator__) + return ('RUNNING_MODAL',) + + +bpy.ops.add(IMPORT_OT_obj) + + # NOTES (all line numbers refer to 2.4x import_obj.py, not this file) # check later: line 489 # can convert now: edge flags, edges: lines 508-528 @@ -1351,4 +1417,4 @@ else: # replaced BPyImage.comprehensiveImageLoad with a simplified version that only checks additional directory specified, but doesn't search dirs recursively (obj_image_load) # bitmask won't work? - 132 # uses operator bpy.ops.OBJECT_OT_select_all_toggle() to deselect all (not necessary?) -# uses bpy.sys.exists and bpy.sys.time() +# uses bpy.sys.time() diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index c6f4f430983..ce803aae78a 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -332,10 +332,11 @@ static void rna_def_pose_channel(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT|ND_POSE|ND_TRANSFORM, "rna_Pose_update"); /* These three matrix properties await an implementation of the PROP_MATRIX subtype, which currently doesn't exist. */ -/* prop= RNA_def_property(srna, "channel_matrix", PROP_FLOAT, PROP_MATRIX); - RNA_def_property_struct_type(prop, "chan_mat"); + prop= RNA_def_property(srna, "channel_matrix", PROP_FLOAT, PROP_MATRIX); + RNA_def_property_float_sdna(prop, NULL, "chan_mat"); + RNA_def_property_array(prop, 16); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Channel Matrix", "4x4 matrix, before constraints.");*/ + RNA_def_property_ui_text(prop, "Channel Matrix", "4x4 matrix, before constraints."); /* kaito says this should be not user-editable; I disagree; power users should be able to force this in python; he's the boss. */ prop= RNA_def_property(srna, "pose_matrix", PROP_FLOAT, PROP_MATRIX); From d1e42cc5f85a6665e85a388763696fd291b86db8 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Fri, 24 Jul 2009 15:27:59 +0000 Subject: [PATCH 54/68] Forgot to add rna_image_api.c. --- .../blender/makesrna/intern/rna_image_api.c | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 source/blender/makesrna/intern/rna_image_api.c diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c new file mode 100644 index 00000000000..42ee2b64c7a --- /dev/null +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -0,0 +1,73 @@ +/** + * + * + * ***** 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) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include "RNA_define.h" +#include "RNA_types.h" + +#include "DNA_object_types.h" + +#ifdef RNA_RUNTIME + +#include "BKE_utildefines.h" +#include "BKE_image.h" + +static char *rna_Image_export(Image *image, char *dest_dir) +{ + int length = FILE_MAX; + char *path= MEM_callocN(length, "image file path"); + + if (!BKE_export_image(image, dest_dir, path, length)) { + MEM_freeN(path); + return NULL; + } + + return path; +} + +#else + +void RNA_api_image(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *parm; + + func= RNA_def_function(srna, "export", "rna_Image_export"); + RNA_def_function_ui_description(func, "Copy image file to a directory rebuilding subdirectory structure."); + parm= RNA_def_string(func, "dest_dir", "", 0, "", "Destination directory."); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_string(func, "path", "", 0, "", "Absolute file path of copied image."); + RNA_def_function_return(func, parm); +} + +#endif + From 4086ca58e275816a49950f6ac0cbffa6cb36a8ad Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Mon, 3 Aug 2009 12:02:40 +0000 Subject: [PATCH 55/68] - re-wrote image exporting function renaming it from BKE_export_image to BKE_get_image_export_path because now it doesn't copy files but only manipulates paths. It produces both ablsolute and relative paths. COLLADA exporter can now use it. - re-wrote unit test for it, this is now more compact and readable - RNA API Image.get_export_path takes a boolean arg to indicate whether a relative or absolute path should be returned. Python scripts can now use it. --- source/blender/blenkernel/BKE_image.h | 3 +- source/blender/blenkernel/intern/image.c | 77 ++++---- .../blender/makesrna/intern/rna_image_api.c | 12 +- source/creator/tests/alltest.c | 183 +++++++++--------- 4 files changed, 137 insertions(+), 138 deletions(-) diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 052f7738f2b..56c58cdc6bf 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -156,8 +156,7 @@ struct Image *BKE_image_copy(struct Image *ima); void BKE_image_merge(struct Image *dest, struct Image *source); /* copy image file to a directory rebuilding subdirectory structure */ -int BKE_export_image(struct Image *im, const char *dest_dir, char *out_path, int out_path_len); - +int BKE_get_image_export_path(struct Image *im, const char *dest_dir, char *abs, int abs_size, char *rel, int rel_size); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index f0b29f766ec..276d79b7e32 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -2117,41 +2117,51 @@ void BKE_image_user_calc_imanr(ImageUser *iuser, int cfra, int fieldnr) } /* - Copy an image to destination directory rebuilding subdirectory structure if needed. - Target image path is written to out_path. - Returns 1 on success, 0 otherwise. + Produce image export path. + + Fails returning 0 if image filename is empty or if destination path + matches image path (i.e. both are the same file). + + Trailing slash in dest_dir is optional. Logic: - - if an image is "below" current .blend file directory, rebuild the same dir structure in dest_dir + - if an image is "below" current .blend file directory, rebuild the + same dir structure in dest_dir - For example //textures/foo/bar.png becomes [dest_dir]/textures/foo/bar.png. + For example //textures/foo/bar.png becomes + [dest_dir]/textures/foo/bar.png. - - if an image is not "below" current .blend file directory, disregard it's path and copy it in the - same directory where 3D file goes. + - if an image is not "below" current .blend file directory, + disregard it's path and copy it in the same directory where 3D file + goes. For example //../foo/bar.png becomes [dest_dir]/bar.png. This logic will help ensure that all image paths are relative and that a user gets his images in one place. It'll also provide consistent behaviour across exporters. - */ -int BKE_export_image(Image *im, const char *dest_dir, char *out_path, int out_path_len) +int BKE_get_image_export_path(struct Image *im, const char *dest_dir, char *abs, int abs_size, char *rel, int rel_size) { char path[FILE_MAX]; char dir[FILE_MAX]; char base[FILE_MAX]; char blend_dir[FILE_MAX]; /* directory, where current .blend file resides */ char dest_path[FILE_MAX]; + char rel_dir[FILE_MAX]; int len; - out_path[0]= 0; + if (abs) + abs[0]= 0; + + if (rel) + rel[0]= 0; BLI_split_dirfile_basic(G.sce, blend_dir, NULL); - if (!strcmp(im->name, "") || im->type != IMA_TYPE_IMAGE) { - if (G.f & G_DEBUG) printf("invalid image type\n"); + if (!strlen(im->name)) { + if (G.f & G_DEBUG) printf("Invalid image type.\n"); return 0; } @@ -2160,61 +2170,48 @@ int BKE_export_image(Image *im, const char *dest_dir, char *out_path, int out_pa /* expand "//" in filename and get absolute path */ BLI_convertstringcode(path, G.sce); - /* proceed only if image file exists */ - if (!BLI_exists(path)) { - if (G.f & G_DEBUG) printf("%s doesn't exist\n", path); - return 0; - } - /* get the directory part */ BLI_split_dirfile_basic(path, dir, base); len= strlen(blend_dir); + rel_dir[0] = 0; + /* if image is "below" current .blend file directory */ if (!strncmp(path, blend_dir, len)) { /* if image is _in_ current .blend file directory */ if (!strcmp(dir, blend_dir)) { - /* copy to dest_dir */ BLI_join_dirfile(dest_path, dest_dir, base); } /* "below" */ else { - char rel[FILE_MAX]; - /* rel = image_path_dir - blend_dir */ - BLI_strncpy(rel, dir + len, sizeof(rel)); - - BLI_join_dirfile(dest_path, dest_dir, rel); - - /* build identical directory structure under dest_dir */ - BLI_recurdir_fileops(dest_path); + BLI_strncpy(rel_dir, dir + len, sizeof(rel_dir)); + BLI_join_dirfile(dest_path, dest_dir, rel_dir); BLI_join_dirfile(dest_path, dest_path, base); } } /* image is out of current directory */ else { - /* copy to dest_dir */ BLI_join_dirfile(dest_path, dest_dir, base); } - if (G.f & G_DEBUG) printf("copying %s to %s\n", path, dest_path); - - /* only copy if paths differ */ - if (strcmp(path, dest_path)) { - if (BLI_copy_fileops(path, dest_path) != 0) { - if (G.f & G_DEBUG) printf("couldn't copy %s to %s\n", path, dest_path); - return 0; - } - } - else if (G.f & G_DEBUG){ - printf("%s and %s are the same file\n", path, dest_path); + /* only return 1 if paths differ */ + if (!strcmp(path, dest_path)) { + if (G.f & G_DEBUG) printf("%s and %s are the same file\n", path, dest_path); + return 0; } - BLI_strncpy(out_path, dest_path, out_path_len); + if (abs) + BLI_strncpy(abs, dest_path, abs_size); + + if (rel) { + strncat(rel, rel_dir, rel_size); + strncat(rel, base, rel_size); + } return 1; } diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 42ee2b64c7a..20770ef2957 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -41,12 +41,12 @@ #include "BKE_utildefines.h" #include "BKE_image.h" -static char *rna_Image_export(Image *image, char *dest_dir) +static char *rna_Image_get_export_path(Image *image, char *dest_dir, int rel) { int length = FILE_MAX; char *path= MEM_callocN(length, "image file path"); - if (!BKE_export_image(image, dest_dir, path, length)) { + if (!BKE_get_image_export_path(image, dest_dir, rel ? NULL : path, length, rel ? path : NULL, length )) { MEM_freeN(path); return NULL; } @@ -61,11 +61,13 @@ void RNA_api_image(StructRNA *srna) FunctionRNA *func; PropertyRNA *parm; - func= RNA_def_function(srna, "export", "rna_Image_export"); - RNA_def_function_ui_description(func, "Copy image file to a directory rebuilding subdirectory structure."); + func= RNA_def_function(srna, "export", "rna_Image_get_export_path"); + RNA_def_function_ui_description(func, "Produce image export path."); parm= RNA_def_string(func, "dest_dir", "", 0, "", "Destination directory."); RNA_def_property_flag(parm, PROP_REQUIRED); - parm= RNA_def_string(func, "path", "", 0, "", "Absolute file path of copied image."); + parm= RNA_def_boolean(func, "get_rel_path", 1, "", "Return relative path if True."); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_string(func, "path", "", 0, "", "Absolute export path."); RNA_def_function_return(func, parm); } diff --git a/source/creator/tests/alltest.c b/source/creator/tests/alltest.c index 8bb2b1a9bb0..99e0d2f5b26 100644 --- a/source/creator/tests/alltest.c +++ b/source/creator/tests/alltest.c @@ -19,48 +19,26 @@ char bprogname[FILE_MAXDIR+FILE_MAXFILE]; char btempdir[FILE_MAXDIR+FILE_MAXFILE]; +typedef struct ImageTestResult { + char *path; + char *rel; + int ret; +} ImageTestResult; + typedef struct ImageTestData { char *path; /* image filename */ - char *expect_path; /* file path that we expect */ - int type; /* image type */ - int ret; /* expected function return value */ - int create_file; /* whether the file should be created */ + ImageTestResult result[10]; } ImageTestData; -/* recursively deletes a directory only if it is under /tmp */ -static void delete_only_tmp(char *path, int dir) { -#ifdef WIN32 -#else - if (!strncmp(path, "/tmp/", 5) && BLI_exists(path)) { - BLI_delete(path, dir, 1); - } -#endif -} - -static void touch_only_tmp(char *path) { -#ifdef WIN32 -#else - if (!strncmp(path, "/tmp/", 5)) { - BLI_touch(path); - } -#endif -} - /* check that BKE_copy_images manipulates paths correctly */ START_TEST(test_copy_images) { char **dir; ImageTestData *test; + int i,j; - /* XXX Windows not tested */ #ifdef WIN32 - static ImageTestData test_data[] = { - {"//bar/image.png", "C:\\Temp\\bar\\image.png"}, - /* TODO add more */ - {NULL, NULL}, - }; - - BLI_strncpy(G.sce, "C:\\Temp\untitled.blend", sizeof(G.sce)); + /* TBD... */ #else /* XXX are these paths possible in image->name?: @@ -70,77 +48,100 @@ START_TEST(test_copy_images) if so, BKE_copy_images currently doesn't support them! */ - static ImageTestData test_data[] = { - {"//bar/image.png", "/tmp/blender/dest/bar/image.png", IMA_TYPE_IMAGE, 1, 1}, - {"//image.png", "/tmp/blender/dest/image.png", IMA_TYPE_IMAGE, 1, 1}, - {"//textures/test/foo/bar/image.png", "/tmp/blender/dest/textures/test/foo/bar/image.png", IMA_TYPE_IMAGE, 1, 1}, - {"//textures/test/foo/bar/image.png", "", IMA_TYPE_MULTILAYER, 0, 1}, - {"//./foo/bar/image.png", "/tmp/blender/dest/foo/bar/image.png", IMA_TYPE_IMAGE, 1, 1}, - {"//../foo/bar/image.png", "/tmp/blender/dest/image.png", IMA_TYPE_IMAGE, 1, 1}, - {"/tmp/blender/image.png", "/tmp/blender/dest/image.png", IMA_TYPE_IMAGE, 1, 1}, - /* expecting it to return 1 when src and dest are the same file */ - {"/tmp/blender/foo/bar/image.png", "/tmp/blender/dest/image.png", IMA_TYPE_IMAGE, 1, 1}, - {"/tmp/blender/dest/image.png", "/tmp/blender/dest/image.png", IMA_TYPE_IMAGE, 1, 1}, - /* expecting empty path and 0 return value for non-existing files */ - {"/tmp/blender/src/file-not-created", "", IMA_TYPE_IMAGE, 0, 0}, - {"", "", IMA_TYPE_IMAGE, 0, 0}, - {NULL, NULL}, - }; - char *dest_dir[] = {"/tmp/blender/dest/", "/tmp/blender/dest", NULL}; - const char *blend_dir = "/tmp/blender/src"; + const char *blend_dir = "/home/user/foo"; + char *dest_dir[] = {"/home/user/", "/home/user", "/home/user/export/", "/home/user/foo/", NULL}; + + static ImageTestData test_data[] = { + + /* image path | [expected output path | corresponding relative path | expected return value] */ + + /* relative, 0 level deep */ + {"//image.png", {{"/home/user/image.png", "image.png", 1}, + {"/home/user/image.png", "image.png", 1}, + {"/home/user/export/image.png", "image.png", 1}, + {"", "", 0},}}, + + /* relative, 1 level deep */ + {"//bar/image.png", {{"/home/user/bar/image.png", "bar/image.png", 1}, + {"/home/user/bar/image.png", "bar/image.png", 1}, + {"/home/user/export/bar/image.png", "bar/image.png", 1}, + {"", "", 0},}}, + + /* relative, 2 level deep */ + {"//bar/foo/image.png", {{"/home/user/bar/foo/image.png", "bar/foo/image.png", 1}, + {"/home/user/bar/foo/image.png", "bar/foo/image.png", 1}, + {"/home/user/export/bar/foo/image.png", "bar/foo/image.png", 1}, + {"", "", 0},}}, + + /* absolute, not under .blend dir */ + {"/home/user/bar/image.png", {{"/home/user/image.png", "image.png", 1}, + {"/home/user/image.png", "image.png", 1}, + {"/home/user/export/image.png", "image.png", 1}, + {"/home/user/foo/image.png", "image.png", 1},}}, + + /* absolute, under .blend dir, 0 level deep */ + {"/home/user/foo/image.png", {{"/home/user/image.png", "image.png", 1}, + {"/home/user/image.png", "image.png", 1}, + {"/home/user/export/image.png", "image.png", 1}, + {"", "", 0},}}, + + /* absolute, under .blend dir, 1 level deep */ + {"/home/user/foo/bar/image.png", {{"/home/user/bar/image.png", "bar/image.png", 1}, + {"/home/user/bar/image.png", "bar/image.png", 1}, + {"/home/user/export/bar/image.png", "bar/image.png", 1}, + {"", "", 0},}}, + + /* absolute, under .blend dir, 2 level deep */ + {"/home/user/foo/bar/foo/image.png", {{"/home/user/bar/foo/image.png", "bar/foo/image.png", 1}, + {"/home/user/bar/foo/image.png", "bar/foo/image.png", 1}, + {"/home/user/export/bar/foo/image.png", "bar/foo/image.png", 1}, + {"", "", 0},}}, + + /* empty image path, don't let these pass! */ + {"", {{"", 0}, + {"", 0}, + {"", 0}}}, + + {NULL}, + }; /* substitute G.sce */ BLI_snprintf(G.sce, sizeof(G.sce), "%s/untitled.blend", blend_dir); #endif - /* only delete files/directories under /tmp/ ! */ - delete_only_tmp(blend_dir, 1); - - for (dir = dest_dir; *dir; dir++) { - delete_only_tmp(*dir, 1); - } - - /* create files */ - BLI_recurdir_fileops(blend_dir); - - /* create fake empty source files */ - for (test= &test_data[0]; test->path; test++) { - char dir[FILE_MAX]; - char path[FILE_MAX]; - - if (!test->create_file) continue; - - /* expand "//" */ - BLI_strncpy(path, test->path, sizeof(path)); - BLI_convertstringcode(path, G.sce); - - /* create a directory */ - BLI_split_dirfile_basic(path, dir, NULL); - BLI_recurdir_fileops(dir); - - /* create a file */ - touch_only_tmp(path); - } - - for (dir = dest_dir; *dir; dir++) { + for (dir= dest_dir, i= 0; *dir; dir++, i++) { for (test= &test_data[0]; test->path; test++) { Image image; char path[FILE_MAX]; + char rel[FILE_MAX]; char part[200]; int ret; BLI_strncpy(image.name, test->path, sizeof(image.name)); - image.type= test->type; - ret= BKE_export_image(&image, *dir, path, sizeof(path)); + /* passing NULL as abs path or rel path or both shouldn't break it */ + int abs_rel_null[][2]= {{0, 0}, {1, 0}, {0, 1}, {1, 1}, {-1}}; - /* check if we got correct output */ - BLI_snprintf(part, sizeof(part), "For image with filename %s and type %d", test->path, test->type); - fail_if(ret != test->ret, "%s, expected %d as return value got %d.", part, test->ret, ret); - fail_if(strcmp(path, test->expect_path), "%s, expected path %s got \"%s\".", part, test->expect_path, path); - if (test->ret == ret && ret == 1) { - fail_if(!BLI_exists(test->expect_path), "%s, expected %s to be created.", part, test->expect_path); + for (j= 0; abs_rel_null[j][0] != -1; j++) { + + int *is_null= abs_rel_null[j]; + + ret= BKE_get_image_export_path(&image, *dir, + is_null[0] ? NULL : path, sizeof(path), + is_null[1] ? NULL : rel, sizeof(rel)); + + BLI_snprintf(part, sizeof(part), "For image at %s (output abs path is %s, rel path is %s)", + test->path, is_null[0] ? "NULL" : "non-NULL", is_null[1] ? "NULL" : "non-NULL"); + + /* we should get what we expect */ + ImageTestResult *res= &test->result[i]; + fail_if(ret != res->ret, "%s, expected to return %d got %d.", part, res->ret, ret); + + if (!is_null[0] && res->path) + fail_if(strcmp(path, res->path), "%s, expected absolute path \"%s\" got \"%s\".", part, res->path, path); + if (!is_null[1] && res->rel) + fail_if(strcmp(rel, res->rel), "%s, expected relative path \"%s\" got \"%s\".", part, res->rel, rel); } } } @@ -149,10 +150,10 @@ END_TEST static Suite *image_suite(void) { - Suite *s = suite_create("Image"); + Suite *s= suite_create("Image"); /* Core test case */ - TCase *tc_core = tcase_create("Core"); + TCase *tc_core= tcase_create("Core"); tcase_add_test(tc_core, test_copy_images); suite_add_tcase(s, tc_core); @@ -162,8 +163,8 @@ static Suite *image_suite(void) int run_tests() { int totfail; - Suite *s = image_suite(); - SRunner *sr = srunner_create(s); + Suite *s= image_suite(); + SRunner *sr= srunner_create(s); /* run tests */ srunner_run_all(sr, CK_VERBOSE); From 7586990ace3a988e720397f47bf95b7bc9d7124b Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Wed, 5 Aug 2009 07:59:49 +0000 Subject: [PATCH 56/68] - modified BKE_get_image_export_path so that it writes relative path if src file = dest file returning 2 in this case - modified unit tests for ^ firstly - incorporated Image.get_export_path into FBX exporter script --- release/io/export_fbx.py | 22 +++++++++++++------ source/blender/blenkernel/intern/image.c | 12 +++++----- .../blender/makesrna/intern/rna_image_api.c | 5 +++++ source/creator/tests/alltest.c | 12 +++++----- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index a81dea4d909..c50b60fd97e 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -39,6 +39,7 @@ http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx import os import time import math # math.pi +import shutil # for file copying # try: # import time @@ -1324,8 +1325,6 @@ def write(filename, batch_objects = None, \ # tex is an Image (Arystan) def write_video(texname, tex): - if not EXP_IMAGE_COPY: return - # Same as texture really! file.write('\n\tVideo: "Video::%s", "Clip" {' % texname) @@ -1337,9 +1336,15 @@ def write(filename, batch_objects = None, \ Property: "Width", "int", "",0 Property: "Height", "int", "",0''') if tex: - abspath = tex.export(basepath) - fname_rel = os.path.relpath(abspath, basepath) - fname_strip = os.path.basename(abspath) + src = bpy.sys.expandpath(tex.filename) + fname_rel = tex.get_export_path(basepath, True) + fname_abs = tex.get_export_path(basepath, False) + fname_strip = os.path.basename(fname_rel) + + if EXP_IMAGE_COPY: + if !os.path.exists(fname_abs): + shutil.copy(src, fname_abs) + # fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) else: fname = fname_strip = fname_rel = '' @@ -1404,11 +1409,14 @@ def write(filename, batch_objects = None, \ file.write('\n\t\tMedia: "Video::%s"' % texname) if tex: - fname_rel = tex.get_export_path(relpath, True) + src = bpy.sys.expandpath(tex.filename) + fname_rel = tex.get_export_path(basepath, True) + fname_abs = tex.get_export_path(basepath, False) fname_strip = os.path.basename(fname_rel) if EXP_IMAGE_COPY: - + if !os.path.exists(fname_abs): + shutil.copy(src, fname_abs) # fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) else: diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index b66213ffbc2..4a355640d8b 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -2206,12 +2206,6 @@ int BKE_get_image_export_path(struct Image *im, const char *dest_dir, char *abs, BLI_join_dirfile(dest_path, dest_dir, base); } - /* only return 1 if paths differ */ - if (!strcmp(path, dest_path)) { - if (G.f & G_DEBUG) printf("%s and %s are the same file\n", path, dest_path); - return 0; - } - if (abs) BLI_strncpy(abs, dest_path, abs_size); @@ -2220,5 +2214,11 @@ int BKE_get_image_export_path(struct Image *im, const char *dest_dir, char *abs, strncat(rel, base, rel_size); } + /* return 2 if src=dest */ + if (!strcmp(path, dest_path)) { + if (G.f & G_DEBUG) printf("%s and %s are the same file\n", path, dest_path); + return 2; + } + return 1; } diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 20770ef2957..af059b435b3 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -41,6 +41,11 @@ #include "BKE_utildefines.h" #include "BKE_image.h" +/* + User should check if returned path exists before copying a file there. + + TODO: it would be better to return a (abs, rel) tuple. +*/ static char *rna_Image_get_export_path(Image *image, char *dest_dir, int rel) { int length = FILE_MAX; diff --git a/source/creator/tests/alltest.c b/source/creator/tests/alltest.c index 99e0d2f5b26..89a58a08dfd 100644 --- a/source/creator/tests/alltest.c +++ b/source/creator/tests/alltest.c @@ -60,19 +60,19 @@ START_TEST(test_copy_images) {"//image.png", {{"/home/user/image.png", "image.png", 1}, {"/home/user/image.png", "image.png", 1}, {"/home/user/export/image.png", "image.png", 1}, - {"", "", 0},}}, + {"/home/user/foo/image.png", "image.png", 2},}}, /* relative, 1 level deep */ {"//bar/image.png", {{"/home/user/bar/image.png", "bar/image.png", 1}, {"/home/user/bar/image.png", "bar/image.png", 1}, {"/home/user/export/bar/image.png", "bar/image.png", 1}, - {"", "", 0},}}, + {"/home/user/foo/bar/image.png", "bar/image.png", 2},}}, /* relative, 2 level deep */ {"//bar/foo/image.png", {{"/home/user/bar/foo/image.png", "bar/foo/image.png", 1}, {"/home/user/bar/foo/image.png", "bar/foo/image.png", 1}, {"/home/user/export/bar/foo/image.png", "bar/foo/image.png", 1}, - {"", "", 0},}}, + {"/home/user/foo/bar/foo/image.png", "bar/foo/image.png", 2},}}, /* absolute, not under .blend dir */ {"/home/user/bar/image.png", {{"/home/user/image.png", "image.png", 1}, @@ -84,19 +84,19 @@ START_TEST(test_copy_images) {"/home/user/foo/image.png", {{"/home/user/image.png", "image.png", 1}, {"/home/user/image.png", "image.png", 1}, {"/home/user/export/image.png", "image.png", 1}, - {"", "", 0},}}, + {"/home/user/foo/image.png", "image.png", 2},}}, /* absolute, under .blend dir, 1 level deep */ {"/home/user/foo/bar/image.png", {{"/home/user/bar/image.png", "bar/image.png", 1}, {"/home/user/bar/image.png", "bar/image.png", 1}, {"/home/user/export/bar/image.png", "bar/image.png", 1}, - {"", "", 0},}}, + {"/home/user/foo/bar/image.png", "bar/image.png", 2},}}, /* absolute, under .blend dir, 2 level deep */ {"/home/user/foo/bar/foo/image.png", {{"/home/user/bar/foo/image.png", "bar/foo/image.png", 1}, {"/home/user/bar/foo/image.png", "bar/foo/image.png", 1}, {"/home/user/export/bar/foo/image.png", "bar/foo/image.png", 1}, - {"", "", 0},}}, + {"/home/user/foo/bar/foo/image.png", "bar/foo/image.png", 2},}}, /* empty image path, don't let these pass! */ {"", {{"", 0}, From bf90970eca5471b86d900daffb2f8eebdf85a673 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Wed, 5 Aug 2009 09:49:59 +0000 Subject: [PATCH 57/68] - OBJ exporter now copies images - added OBJ, FBX scripts to File -> Export, File -> Import menus --- release/io/export_fbx.py | 33 ++++++++++---------- release/io/export_obj.py | 65 ++++++++++++++++++++++++---------------- release/ui/space_info.py | 3 ++ 3 files changed, 58 insertions(+), 43 deletions(-) diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index c50b60fd97e..ad9b3caa13a 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -1323,6 +1323,19 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t}') file.write('\n\t}') + def copy_image(image): + + rel = image.get_export_path(basepath, True) + base = os.path.basename(fname_rel) + + if EXP_IMAGE_COPY: + src = bpy.sys.expandpath(image.filename) + absp = image.get_export_path(basepath, False) + if not os.path.exists(absp): + shutil.copy(src, absp) + + return (rel, base) + # tex is an Image (Arystan) def write_video(texname, tex): # Same as texture really! @@ -1336,15 +1349,7 @@ def write(filename, batch_objects = None, \ Property: "Width", "int", "",0 Property: "Height", "int", "",0''') if tex: - src = bpy.sys.expandpath(tex.filename) - fname_rel = tex.get_export_path(basepath, True) - fname_abs = tex.get_export_path(basepath, False) - fname_strip = os.path.basename(fname_rel) - - if EXP_IMAGE_COPY: - if !os.path.exists(fname_abs): - shutil.copy(src, fname_abs) - + fname_rel, fname_strip = copy_image(tex) # fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) else: fname = fname_strip = fname_rel = '' @@ -1409,15 +1414,7 @@ def write(filename, batch_objects = None, \ file.write('\n\t\tMedia: "Video::%s"' % texname) if tex: - src = bpy.sys.expandpath(tex.filename) - fname_rel = tex.get_export_path(basepath, True) - fname_abs = tex.get_export_path(basepath, False) - fname_strip = os.path.basename(fname_rel) - - if EXP_IMAGE_COPY: - if !os.path.exists(fname_abs): - shutil.copy(src, fname_abs) - + fname_rel, fname_strip = copy_image(tex) # fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) else: fname = fname_strip = fname_rel = '' diff --git a/release/io/export_obj.py b/release/io/export_obj.py index 4d67903c343..1209e10afe3 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -47,7 +47,7 @@ will be exported as mesh data. # import math and other in functions that use them for the sake of fast Blender startup # import math -import os # os.sep +import os import bpy import Mathutils @@ -83,13 +83,26 @@ def BPySys_cleanName(name): # A Dict of Materials # (material.name, image.name):matname_imagename # matname_imagename has gaps removed. -MTL_DICT = {} +MTL_DICT = {} -def write_mtl(scene, filename): +def write_mtl(scene, filename, copy_images): world = scene.world worldAmb = world.ambient_color + dest_dir = os.path.dirname(filename) + + def copy_image(image): + rel = image.get_export_path(dest_dir, True) + + if copy_images: + abspath = image.get_export_path(dest_dir, False) + if not os.path.exists(abs_path): + shutil.copy(bpy.sys.expandpath(image.filename), abs_path) + + return rel + + file = open(filename, "w") # XXX # file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1]) @@ -129,13 +142,17 @@ def write_mtl(scene, filename): # Write images! if img: # We have an image on the face! - file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image + # write relative image path + rel = copy_image(img) + file.write('map_Kd %s\n' % rel) # Diffuse mapping image +# file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image elif mat: # No face image. if we havea material search for MTex image. for mtex in mat.textures: if mtex and mtex.texure.type == 'IMAGE': try: - filename = mtex.texture.image.filename.split('\\')[-1].split('/')[-1] + filename = copy_image(mtex.texture.image) +# filename = mtex.texture.image.filename.split('\\')[-1].split('/')[-1] file.write('map_Kd %s\n' % filename) # Diffuse mapping image break except: @@ -146,6 +163,7 @@ def write_mtl(scene, filename): file.close() +# XXX not used def copy_file(source, dest): file = open(source, 'rb') data = file.read() @@ -156,6 +174,7 @@ def copy_file(source, dest): file.close() +# XXX not used def copy_images(dest_dir): if dest_dir[-1] != os.sep: dest_dir += os.sep @@ -181,7 +200,7 @@ def copy_images(dest_dir): pass # Now copy images -# copyCount = 0 + copyCount = 0 # for bImage in uniqueImages.values(): # image_path = bpy.sys.expandpath(bImage.filename) @@ -193,7 +212,7 @@ def copy_images(dest_dir): # copy_file(image_path, dest_image_path) # copyCount+=1 - paths= bpy.util.copy_images(uniqueImages.values(), dest_dir) +# paths= bpy.util.copy_images(uniqueImages.values(), dest_dir) print('\tCopied %d images' % copyCount) # print('\tCopied %d images' % copyCount) @@ -412,16 +431,11 @@ def write(filename, objects, scene, if ob.type != 'MESH': continue - if EXPORT_APPLY_MODIFIERS: - me = ob.create_mesh('PREVIEW') - else: - me = ob.data.create_copy() + me = ob.create_mesh(EXPORT_APPLY_MODIFIERS, 'PREVIEW') if EXPORT_ROTX90: - print(ob_mat * mat_xrot90) me.transform(ob_mat * mat_xrot90) else: - print(ob_mat) me.transform(ob_mat) # # Will work for non meshes now! :) @@ -597,7 +611,7 @@ def write(filename, objects, scene, # file.write('vt %.6f %.6f\n' % tuple(uv)) uv_unique_count = len(uv_dict) - del uv, uvkey, uv_dict, f_index, uv_index +# del uv, uvkey, uv_dict, f_index, uv_index # Only need uv_unique_count and uv_face_mapping # NORMAL, Smooth/Non smoothed. @@ -783,16 +797,17 @@ def write(filename, objects, scene, # Now we have all our materials, save them if EXPORT_MTL: - write_mtl(scene, mtlfilename) - if EXPORT_COPY_IMAGES: - dest_dir = filename - # Remove chars until we are just the path. - while dest_dir and dest_dir[-1] not in '\\/': - dest_dir = dest_dir[:-1] - if dest_dir: - copy_images(dest_dir) - else: - print('\tError: "%s" could not be used as a base for an image path.' % filename) + write_mtl(scene, mtlfilename, EXPORT_COPY_IMAGES) +# if EXPORT_COPY_IMAGES: +# dest_dir = os.path.basename(filename) +# # dest_dir = filename +# # # Remove chars until we are just the path. +# # while dest_dir and dest_dir[-1] not in '\\/': +# # dest_dir = dest_dir[:-1] +# if dest_dir: +# copy_images(dest_dir) +# else: +# print('\tError: "%s" could not be used as a base for an image path.' % filename) print("OBJ Export time: %.2f" % (bpy.sys.time() - time1)) # print "OBJ Export time: %.2f" % (sys.time() - time1) @@ -898,7 +913,7 @@ class EXPORT_OT_obj(bpy.types.Operator): bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the PLY file", maxlen= 1024, default= ""), # context group - bpy.props.BoolProperty(attr="use_selection", name="Selection Only", description="", default= True), + bpy.props.BoolProperty(attr="use_selection", name="Selection Only", description="", default= False), bpy.props.BoolProperty(attr="use_all_scenes", name="All Scenes", description="", default= False), bpy.props.BoolProperty(attr="use_animation", name="All Animation", description="", default= False), diff --git a/release/ui/space_info.py b/release/ui/space_info.py index 855ce0b4f8f..3ec3bce3d91 100644 --- a/release/ui/space_info.py +++ b/release/ui/space_info.py @@ -74,6 +74,7 @@ class INFO_MT_file_import(bpy.types.Menu): def draw(self, context): layout = self.layout + layout.itemO("import.obj", text="OBJ") class INFO_MT_file_export(bpy.types.Menu): __space_type__ = "USER_PREFERENCES" @@ -82,6 +83,8 @@ class INFO_MT_file_export(bpy.types.Menu): def draw(self, context): layout = self.layout + layout.itemO("export.fbx", text="FBX") + layout.itemO("export.obj", text="OBJ") layout.itemO("export.ply", text="PLY") class INFO_MT_file_external_data(bpy.types.Menu): From 59abddc2024080b3d243d14710d3fb3340766737 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Wed, 5 Aug 2009 12:01:42 +0000 Subject: [PATCH 58/68] - added Mesh.add_material function - tweaked OBJ importer. It reads primitive files (cube, cone, etc.) --- release/io/import_obj.py | 23 +++++++----- source/blender/makesrna/intern/rna_mesh_api.c | 37 ++++++++++++++----- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/release/io/import_obj.py b/release/io/import_obj.py index a4778ac7790..2a5d92969af 100644 --- a/release/io/import_obj.py +++ b/release/io/import_obj.py @@ -83,9 +83,11 @@ def unpack_list(list_of_tuples): def unpack_face_list(list_of_tuples): l = [] for t in list_of_tuples: - if len(t) == 3: - t += [0] - l.extend(t) + face = [i for i in t] + if len(face) > 4: + raise RuntimeError("More than 4 vertices per face.") + if len(face) == 3: face.append(0) + l.extend(face) return l @@ -163,7 +165,7 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ # Absolute path - c:\.. etc would work here image= obj_image_load(imagepath, DIR, IMAGE_SEARCH) - has_data = image.has_data + has_data = image.has_data if image else False if image: texture.image = image @@ -503,8 +505,11 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l me= bpy.data.add_mesh(dataname) # me= bpy.data.meshes.new(dataname) - - me.materials= materials[0:16] # make sure the list isnt too big. + + # make sure the list isnt too big + for material in materials[0:16]: + me.add_material(material) +# me.materials= materials[0:16] # make sure the list isnt too big. #me.verts.extend([(0,0,0)]) # dummy vert me.add_geometry(len(verts_loc), 0, len(faces)) @@ -569,7 +574,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l if verts_tex: - blender_tface= me.uv_layers[0].data[i] + blender_tface= me.uv_textures[0].data[i] if context_material: image, has_data= unique_material_images[context_material] @@ -621,7 +626,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l if CREATE_EDGES: - me.add_geometry(0, len(edges)) + me.add_geometry(0, len(edges), 0) # edges should be a list of (a, b) tuples me.edges.foreach_set("verts", unpack_list(edges)) @@ -629,7 +634,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l # del me_edges - me.calc_normals() + me.update() # me.calcNormals() ob= bpy.data.add_object("MESH", "Mesh") diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index 9a527948b0f..b50cc678f4f 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -258,6 +258,29 @@ static void rna_Mesh_calc_normals(Mesh *me) mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL); } +static void rna_Mesh_add_material(Mesh *me, Material *ma) +{ + int i; + int totcol = me->totcol + 1; + Material **mat; + + /* don't add if mesh already has it */ + for (i = 0; i < me->totcol; i++) + if (me->mat[i] == ma) + return; + + mat= MEM_callocN(sizeof(void*) * totcol, "newmatar"); + + if (me->totcol) memcpy(mat, me->mat, sizeof(void*) * me->totcol); + if (me->mat) MEM_freeN(me->mat); + + me->mat = mat; + me->mat[me->totcol++] = ma; + ma->id.us++; + + test_object_materials((ID*)me); +} + #else void RNA_api_mesh(StructRNA *srna) @@ -289,17 +312,13 @@ void RNA_api_mesh(StructRNA *srna) func= RNA_def_function(srna, "calc_normals", "rna_Mesh_calc_normals"); RNA_def_function_ui_description(func, "Calculate vertex normals."); - /* - func= RNA_def_function(srna, "add_geom", "rna_Mesh_add_geom"); - RNA_def_function_ui_description(func, "Add geometry data to mesh."); - prop= RNA_def_collection(func, "verts", "?", "", "Vertices."); - RNA_def_property_flag(prop, PROP_REQUIRED); - prop= RNA_def_collection(func, "faces", "?", "", "Faces."); - RNA_def_property_flag(prop, PROP_REQUIRED); - */ - func= RNA_def_function(srna, "update", "rna_Mesh_update"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); + + func= RNA_def_function(srna, "add_material", "rna_Mesh_add_material"); + RNA_def_function_ui_description(func, "Add a new material to Mesh."); + parm= RNA_def_pointer(func, "material", "Material", "", "Material to add."); + RNA_def_property_flag(parm, PROP_REQUIRED); } #endif From 01eedc504656b065dc9d17751016913906afe4d5 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 6 Aug 2009 12:22:50 +0000 Subject: [PATCH 59/68] - OBJ importer now reads most of the test files. Had to copy BPyMesh.ngon function to OBJ import code - added MeshEdge.fgon property, not using it yet - fixed in bpy_rna.c to correctly read arrays returned from RNA functions --- release/io/export_fbx.py | 2 +- release/io/export_obj.py | 5 +- release/io/import_obj.py | 321 ++++++++++++++---- .../blender/makesrna/intern/rna_image_api.c | 2 +- source/blender/makesrna/intern/rna_mesh.c | 5 + source/blender/python/intern/bpy_rna.c | 4 + 6 files changed, 278 insertions(+), 61 deletions(-) diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index ad9b3caa13a..5bb169a9849 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -1326,7 +1326,7 @@ def write(filename, batch_objects = None, \ def copy_image(image): rel = image.get_export_path(basepath, True) - base = os.path.basename(fname_rel) + base = os.path.basename(rel) if EXP_IMAGE_COPY: src = bpy.sys.expandpath(image.filename) diff --git a/release/io/export_obj.py b/release/io/export_obj.py index 1209e10afe3..fa07c5408a7 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -120,7 +120,10 @@ def write_mtl(scene, filename, copy_images): file.write('Ka %.6f %.6f %.6f\n' % tuple([c*mat.ambient for c in worldAmb]) ) # Ambient, uses mirror colour, file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.diffuse_reflection for c in mat.diffuse_color]) ) # Diffuse file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.specular_reflection for c in mat.specular_color]) ) # Specular - file.write('Ni %.6f\n' % mat.ior) # Refraction index + if hasattr(mat, "ior"): + file.write('Ni %.6f\n' % mat.ior) # Refraction index + else: + file.write('Ni %.6f\n' % 1.0) file.write('d %.6f\n' % mat.alpha) # Alpha (obj uses 'd' for dissolve) # 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting. diff --git a/release/io/import_obj.py b/release/io/import_obj.py index 2a5d92969af..f064561a512 100644 --- a/release/io/import_obj.py +++ b/release/io/import_obj.py @@ -40,9 +40,12 @@ Note, This loads mesh objects and materials only, nurbs and curves are not suppo # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- -import bpy import os +import bpy +import Mathutils +import Geometry + # from Blender import Mesh, Draw, Window, Texture, Material, sys # # import BPyMesh # import BPyImage @@ -84,12 +87,196 @@ def unpack_face_list(list_of_tuples): l = [] for t in list_of_tuples: face = [i for i in t] - if len(face) > 4: - raise RuntimeError("More than 4 vertices per face.") - if len(face) == 3: face.append(0) + if len(face) != 3 and len(face) != 4: + raise RuntimeError("{0} vertices in face.".format(len(face))) + if len(face) == 3: + face.append(0) l.extend(face) return l +def BPyMesh_ngon(from_data, indices, PREF_FIX_LOOPS= True): + ''' + Takes a polyline of indices (fgon) + and returns a list of face indicie lists. + Designed to be used for importers that need indices for an fgon to create from existing verts. + + from_data: either a mesh, or a list/tuple of vectors. + indices: a list of indicies to use this list is the ordered closed polyline to fill, and can be a subset of the data given. + PREF_FIX_LOOPS: If this is enabled polylines that use loops to make multiple polylines are delt with correctly. + ''' + + if not set: # Need sets for this, otherwise do a normal fill. + PREF_FIX_LOOPS= False + + Vector= Mathutils.Vector + if not indices: + return [] + + # return [] + def rvec(co): return round(co.x, 6), round(co.y, 6), round(co.z, 6) + def mlen(co): return abs(co[0])+abs(co[1])+abs(co[2]) # manhatten length of a vector, faster then length + + def vert_treplet(v, i): + return v, rvec(v), i, mlen(v) + + def ed_key_mlen(v1, v2): + if v1[3] > v2[3]: + return v2[1], v1[1] + else: + return v1[1], v2[1] + + + if not PREF_FIX_LOOPS: + ''' + Normal single concave loop filling + ''' + if type(from_data) in (tuple, list): + verts= [Vector(from_data[i]) for ii, i in enumerate(indices)] + else: + verts= [from_data.verts[i].co for ii, i in enumerate(indices)] + + for i in range(len(verts)-1, 0, -1): # same as reversed(xrange(1, len(verts))): + if verts[i][1]==verts[i-1][0]: + verts.pop(i-1) + + fill= Geometry.PolyFill([verts]) + + else: + ''' + Seperate this loop into multiple loops be finding edges that are used twice + This is used by lightwave LWO files a lot + ''' + + if type(from_data) in (tuple, list): + verts= [vert_treplet(Vector(from_data[i]), ii) for ii, i in enumerate(indices)] + else: + verts= [vert_treplet(from_data.verts[i].co, ii) for ii, i in enumerate(indices)] + + edges= [(i, i-1) for i in range(len(verts))] + if edges: + edges[0]= (0,len(verts)-1) + + if not verts: + return [] + + + edges_used= set() + edges_doubles= set() + # We need to check if any edges are used twice location based. + for ed in edges: + edkey= ed_key_mlen(verts[ed[0]], verts[ed[1]]) + if edkey in edges_used: + edges_doubles.add(edkey) + else: + edges_used.add(edkey) + + # Store a list of unconnected loop segments split by double edges. + # will join later + loop_segments= [] + + v_prev= verts[0] + context_loop= [v_prev] + loop_segments= [context_loop] + + for v in verts: + if v!=v_prev: + # Are we crossing an edge we removed? + if ed_key_mlen(v, v_prev) in edges_doubles: + context_loop= [v] + loop_segments.append(context_loop) + else: + if context_loop and context_loop[-1][1]==v[1]: + #raise "as" + pass + else: + context_loop.append(v) + + v_prev= v + # Now join loop segments + + def join_seg(s1,s2): + if s2[-1][1]==s1[0][1]: # + s1,s2= s2,s1 + elif s1[-1][1]==s2[0][1]: + pass + else: + return False + + # If were stuill here s1 and s2 are 2 segments in the same polyline + s1.pop() # remove the last vert from s1 + s1.extend(s2) # add segment 2 to segment 1 + + if s1[0][1]==s1[-1][1]: # remove endpoints double + s1.pop() + + s2[:]= [] # Empty this segment s2 so we dont use it again. + return True + + joining_segments= True + while joining_segments: + joining_segments= False + segcount= len(loop_segments) + + for j in range(segcount-1, -1, -1): #reversed(range(segcount)): + seg_j= loop_segments[j] + if seg_j: + for k in range(j-1, -1, -1): # reversed(range(j)): + if not seg_j: + break + seg_k= loop_segments[k] + + if seg_k and join_seg(seg_j, seg_k): + joining_segments= True + + loop_list= loop_segments + + for verts in loop_list: + while verts and verts[0][1]==verts[-1][1]: + verts.pop() + + loop_list= [verts for verts in loop_list if len(verts)>2] + # DONE DEALING WITH LOOP FIXING + + + # vert mapping + vert_map= [None]*len(indices) + ii=0 + for verts in loop_list: + if len(verts)>2: + for i, vert in enumerate(verts): + vert_map[i+ii]= vert[2] + ii+=len(verts) + + fill= Geometry.PolyFill([ [v[0] for v in loop] for loop in loop_list ]) + #draw_loops(loop_list) + #raise 'done loop' + # map to original indicies + fill= [[vert_map[i] for i in reversed(f)] for f in fill] + + + if not fill: + print('Warning Cannot scanfill, fallback on a triangle fan.') + fill= [ [0, i-1, i] for i in range(2, len(indices)) ] + else: + # Use real scanfill. + # See if its flipped the wrong way. + flip= None + for fi in fill: + if flip != None: + break + for i, vi in enumerate(fi): + if vi==0 and fi[i-1]==1: + flip= False + break + elif vi==1 and fi[i-1]==0: + flip= True + break + + if not flip: + for i, fi in enumerate(fill): + fill[i]= tuple([ii for ii in reversed(fi)]) + + return fill def line_value(line_split): ''' @@ -388,7 +575,7 @@ def split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP, # remove one of the itemas and reorder - return [(value[0], value[1], value[2], key_to_name(key)) for key, value in face_split_dict.items()] + return [(value[0], value[1], value[2], key_to_name(key)) for key, value in list(face_split_dict.items())] def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc, verts_tex, faces, unique_materials, unique_material_images, unique_smooth_groups, vertex_groups, dataname): @@ -401,7 +588,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l if unique_smooth_groups: sharp_edges= {} - smooth_group_users= dict([ (context_smooth_group, {}) for context_smooth_group in unique_smooth_groups.keys() ]) + smooth_group_users= dict([ (context_smooth_group, {}) for context_smooth_group in list(unique_smooth_groups.keys()) ]) context_smooth_group_old= -1 # Split fgons into tri's @@ -452,45 +639,45 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l edge_dict[i1,i2]= 1 # FGons into triangles -# if has_ngons and len_face_vert_loc_indicies > 4: + if has_ngons and len_face_vert_loc_indicies > 4: -# ngon_face_indices= BPyMesh.ngon(verts_loc, face_vert_loc_indicies) -# faces.extend(\ -# [(\ -# [face_vert_loc_indicies[ngon[0]], face_vert_loc_indicies[ngon[1]], face_vert_loc_indicies[ngon[2]] ],\ -# [face_vert_tex_indicies[ngon[0]], face_vert_tex_indicies[ngon[1]], face_vert_tex_indicies[ngon[2]] ],\ -# context_material,\ -# context_smooth_group,\ -# context_object)\ -# for ngon in ngon_face_indices]\ -# ) + ngon_face_indices= BPyMesh_ngon(verts_loc, face_vert_loc_indicies) + faces.extend(\ + [(\ + [face_vert_loc_indicies[ngon[0]], face_vert_loc_indicies[ngon[1]], face_vert_loc_indicies[ngon[2]] ],\ + [face_vert_tex_indicies[ngon[0]], face_vert_tex_indicies[ngon[1]], face_vert_tex_indicies[ngon[2]] ],\ + context_material,\ + context_smooth_group,\ + context_object)\ + for ngon in ngon_face_indices]\ + ) -# # edges to make fgons -# if CREATE_FGONS: -# edge_users= {} -# for ngon in ngon_face_indices: -# for i in (0,1,2): -# i1= face_vert_loc_indicies[ngon[i ]] -# i2= face_vert_loc_indicies[ngon[i-1]] -# if i1>i2: i1,i2= i2,i1 + # edges to make fgons + if CREATE_FGONS: + edge_users= {} + for ngon in ngon_face_indices: + for i in (0,1,2): + i1= face_vert_loc_indicies[ngon[i ]] + i2= face_vert_loc_indicies[ngon[i-1]] + if i1>i2: i1,i2= i2,i1 -# try: -# edge_users[i1,i2]+=1 -# except KeyError: -# edge_users[i1,i2]= 1 + try: + edge_users[i1,i2]+=1 + except KeyError: + edge_users[i1,i2]= 1 -# for key, users in edge_users.iteritems(): -# if users>1: -# fgon_edges[key]= None + for key, users in edge_users.items(): + if users>1: + fgon_edges[key]= None -# # remove all after 3, means we dont have to pop this one. -# faces.pop(f_idx) + # remove all after 3, means we dont have to pop this one. + faces.pop(f_idx) # Build sharp edges if unique_smooth_groups: - for edge_dict in smooth_group_users.values(): - for key, users in edge_dict.items(): + for edge_dict in list(smooth_group_users.values()): + for key, users in list(edge_dict.items()): if users==1: # This edge is on the boundry of a group sharp_edges[key]= None @@ -500,7 +687,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l materials= [None] * len(unique_materials) - for name, index in material_mapping.items(): + for name, index in list(material_mapping.items()): materials[index]= unique_materials[name] me= bpy.data.add_mesh(dataname) @@ -607,23 +794,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l # uv.x, uv.y= verts_tex[face_vert_tex_indicies[ii]] del me_faces # del ALPHA - - # Add edge faces. - me_edges= me.edges -# if CREATE_FGONS and fgon_edges: -# FGON= Mesh.EdgeFlags.FGON -# for ed in me.findEdges( fgon_edges.keys() ): -# if ed!=None: -# me_edges[ed].flag |= FGON -# del FGON - -# if unique_smooth_groups and sharp_edges: -# SHARP= Mesh.EdgeFlags.SHARP -# for ed in me.findEdges( sharp_edges.keys() ): -# if ed!=None: -# me_edges[ed].flag |= SHARP -# del SHARP - + if CREATE_EDGES: me.add_geometry(0, len(edges), 0) @@ -633,10 +804,44 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l # me_edges.extend( edges ) # del me_edges + + # Add edge faces. +# me_edges= me.edges + + def edges_match(e1, e2): + return (e1[0] == e2[0] and e1[1] == e2[1]) or (e1[0] == e2[1] and e1[1] == e2[0]) + + # XXX slow +# if CREATE_FGONS and fgon_edges: +# for fgon_edge in fgon_edges.keys(): +# for ed in me.edges: +# if edges_match(fgon_edge, ed.verts): +# ed.fgon = True + +# if CREATE_FGONS and fgon_edges: +# FGON= Mesh.EdgeFlags.FGON +# for ed in me.findEdges( fgon_edges.keys() ): +# if ed!=None: +# me_edges[ed].flag |= FGON +# del FGON + + # XXX slow +# if unique_smooth_groups and sharp_edges: +# for sharp_edge in sharp_edges.keys(): +# for ed in me.edges: +# if edges_match(sharp_edge, ed.verts): +# ed.sharp = True + +# if unique_smooth_groups and sharp_edges: +# SHARP= Mesh.EdgeFlags.SHARP +# for ed in me.findEdges( sharp_edges.keys() ): +# if ed!=None: +# me_edges[ed].flag |= SHARP +# del SHARP me.update() # me.calcNormals() - + ob= bpy.data.add_object("MESH", "Mesh") ob.data= me scn.add_object(ob) @@ -823,7 +1028,7 @@ def load_obj(filepath, # so we need to know weather context_multi_line= '' - print('\tparsing obj file "%s"...' % filepath, end=' ') + print('\tparsing obj file "%s"...' % filepath) time_sub= bpy.sys.time() # time_sub= sys.time() @@ -1039,7 +1244,7 @@ def load_obj(filepath, time_sub= time_new - print('\tloading materials and images...', end=' ') + print('\tloading materials and images...') create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH) time_new= bpy.sys.time() @@ -1059,7 +1264,7 @@ def load_obj(filepath, # scn.objects.selected = [] new_objects= [] # put new objects here - print('\tbuilding geometry...\n\tverts:%i faces:%i materials: %i smoothgroups:%i ...' % ( len(verts_loc), len(faces), len(unique_materials), len(unique_smooth_groups) ), end=' ') + print('\tbuilding geometry...\n\tverts:%i faces:%i materials: %i smoothgroups:%i ...' % ( len(verts_loc), len(faces), len(unique_materials), len(unique_smooth_groups) )) # Split the mesh by objects/materials, may if SPLIT_OBJECTS or SPLIT_GROUPS: SPLIT_OB_OR_GROUP = True else: SPLIT_OB_OR_GROUP = False diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index af059b435b3..ccc9846600d 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -66,7 +66,7 @@ void RNA_api_image(StructRNA *srna) FunctionRNA *func; PropertyRNA *parm; - func= RNA_def_function(srna, "export", "rna_Image_get_export_path"); + func= RNA_def_function(srna, "get_export_path", "rna_Image_get_export_path"); RNA_def_function_ui_description(func, "Produce image export path."); parm= RNA_def_string(func, "dest_dir", "", 0, "", "Destination directory."); RNA_def_property_flag(parm, PROP_REQUIRED); diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index dba5a0622c3..dc953f80804 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -877,6 +877,11 @@ static void rna_def_medge(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_LOOSEEDGE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Loose", "Loose edge"); + + prop= RNA_def_property(srna, "fgon", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_FGON); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Fgon", "Fgon edge"); } static void rna_def_mface(BlenderRNA *brna) diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 27bfb89b963..ded07d545ef 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -1801,6 +1801,10 @@ PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *data) /* resolve the array from a new pytype */ ret = PyTuple_New(len); + /* for return values, data is a pointer to an array, not first element pointer */ + if (prop->flag & PROP_DYNAMIC_ARRAY) + data = *((char**)data) + switch (type) { case PROP_BOOLEAN: for(a=0; a Date: Fri, 7 Aug 2009 13:00:39 +0000 Subject: [PATCH 60/68] bpyrna: interpret a tuple of enum items (e1, e2, ...) as a bitmask (e1 | e2 | ...). --- source/blender/python/intern/bpy_rna.c | 65 +++++++++++++++++++------- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index ded07d545ef..54d7b812fe1 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -33,6 +33,7 @@ #include "RNA_access.h" #include "RNA_define.h" /* for defining our own rna */ +#include "rna_internal_types.h" #include "MEM_guardedalloc.h" #include "BKE_utildefines.h" @@ -241,6 +242,27 @@ static char *pyrna_enum_as_string(PointerRNA *ptr, PropertyRNA *prop) return result; } +static int pyrna_string_to_enum(PyObject *item, PointerRNA *ptr, PropertyRNA *prop, int *val, const char *error_prefix) +{ + char *param= _PyUnicode_AsString(item); + + if (param==NULL) { + char *enum_str= pyrna_enum_as_string(ptr, prop); + PyErr_Format(PyExc_TypeError, "%.200s expected a string enum type in (%.200s)", error_prefix, enum_str); + MEM_freeN(enum_str); + return 0; + } else { + if (!RNA_property_enum_value(BPy_GetContext(), ptr, prop, param, val)) { + char *enum_str= pyrna_enum_as_string(ptr, prop); + PyErr_Format(PyExc_TypeError, "%.200s enum \"%.200s\" not found in (%.200s)", error_prefix, param, enum_str); + MEM_freeN(enum_str); + return 0; + } + } + + return 1; +} + PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop) { PyObject *ret; @@ -641,25 +663,34 @@ int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, void *data, PyObject *v } case PROP_ENUM: { - char *param = _PyUnicode_AsString(value); - - if (param==NULL) { - char *enum_str= pyrna_enum_as_string(ptr, prop); - PyErr_Format(PyExc_TypeError, "%.200s expected a string enum type in (%.200s)", error_prefix, enum_str); - MEM_freeN(enum_str); - return -1; - } else { - int val; - if (RNA_property_enum_value(BPy_GetContext(), ptr, prop, param, &val)) { - if(data) *((int*)data)= val; - else RNA_property_enum_set(ptr, prop, val); - } else { - char *enum_str= pyrna_enum_as_string(ptr, prop); - PyErr_Format(PyExc_TypeError, "%.200s enum \"%.200s\" not found in (%.200s)", error_prefix, param, enum_str); - MEM_freeN(enum_str); + int val, i; + + if (PyUnicode_Check(value)) { + if (!pyrna_string_to_enum(value, ptr, prop, &val, error_prefix)) return -1; + } + else if (PyTuple_Check(value)) { + /* tuple of enum items, concatenate all values with OR */ + val= 0; + for (i= 0; i < PyTuple_Size(value); i++) { + int tmpval; + + /* PyTuple_GET_ITEM returns a borrowed reference */ + if (!pyrna_string_to_enum(PyTuple_GET_ITEM(value, i), ptr, prop, &tmpval, error_prefix)) + return -1; + + val |= tmpval; } } + else { + char *enum_str= pyrna_enum_as_string(ptr, prop); + PyErr_Format(PyExc_TypeError, "%.200s expected a string enum or a tuple of strings in (%.200s)", error_prefix, enum_str); + MEM_freeN(enum_str); + return -1; + } + + if(data) *((int*)data)= val; + else RNA_property_enum_set(ptr, prop, val); break; } @@ -1803,7 +1834,7 @@ PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *data) /* for return values, data is a pointer to an array, not first element pointer */ if (prop->flag & PROP_DYNAMIC_ARRAY) - data = *((char**)data) + data = *(char**)(char*)data; switch (type) { case PROP_BOOLEAN: From 8fa528cef8faa4f5ce2f6ff4f6e2f5b08709d08e Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Sat, 8 Aug 2009 11:33:34 +0000 Subject: [PATCH 61/68] Added Object.find_armature() to find armature connected to object. Previously this was BPyObject.getObjectArmature() --- release/io/export_fbx.py | 48 +------------------ source/blender/editors/include/ED_mesh.h | 3 ++ .../blender/makesrna/intern/rna_object_api.c | 30 ++++++++++++ 3 files changed, 35 insertions(+), 46 deletions(-) diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py index 5bb169a9849..2a3ea5fc30e 100644 --- a/release/io/export_fbx.py +++ b/release/io/export_fbx.py @@ -118,26 +118,6 @@ def copy_images(dest_dir, textures): print('\tCopied %d images' % copyCount) -def BPyObject_getObjectArmature(ob): - ''' - This returns the first armature the mesh uses. - remember there can be more then 1 armature but most people dont do that. - ''' - if ob.type != 'MESH': - return None - - arm = ob.parent - if arm and arm.type == 'ARMATURE' and ob.parent_type == 'ARMATURE': - return arm - - for m in ob.modifiers: - if m.type== 'ARMATURE': - arm = m.object - if arm: - return arm - - return None - # I guess FBX uses degrees instead of radians (Arystan). # Call this function just before writing to FBX. def eulerRadToDeg(eul): @@ -298,30 +278,6 @@ def BPyMesh_meshWeight2List(ob): return groupNames, vWeightList - -def BPyMesh_meshWeight2Dict(me, ob): - ''' Takes a mesh and return its group names and a list of dicts, one dict per vertex. - using the group as a key and a float value for the weight. - These 2 lists can be modified and then used with dict2MeshWeight to apply the changes. - ''' - - vWeightDict= [dict() for i in range(len(me.verts))] # Sync with vertlist. - - # Clear the vert group. - groupNames= [g.name for g in ob.vertex_groups] -# groupNames= me.getVertGroupNames() - - for group in groupNames: - for vert_index, weight in me.getVertsFromGroup(group, 1): # (i,w) tuples. - vWeightDict[vert_index][group]= weight - - # removed this because me may be copying teh vertex groups. - #for group in groupNames: - # me.removeVertGroup(group) - - return groupNames, vWeightDict - - def meshNormalizedWeights(me): try: # account for old bad BPyMesh groupNames, vWeightList = BPyMesh_meshWeight2List(me) @@ -2199,7 +2155,7 @@ def write(filename, batch_objects = None, \ materials[None, None] = None if EXP_ARMATURE: - armob = BPyObject_getObjectArmature(ob) + armob = ob.find_armature() blenParentBoneName = None # parent bone - special case @@ -3482,7 +3438,7 @@ bpy.ops.add(EXPORT_OT_fbx) # - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print # - get rid of cleanName somehow # + fixed: isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565 -# - get rid of BPyObject_getObjectArmature, move it in RNA? +# + get rid of BPyObject_getObjectArmature, move it in RNA? # - BATCH_ENABLE and BATCH_GROUP options: line 327 # - implement all BPyMesh_* used here with RNA # - getDerivedObjects is not fully replicated with .dupli* funcs diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 0face00f82b..cceebeadc96 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -154,6 +154,9 @@ void EM_reveal_mesh(struct EditMesh *em); void EM_select_by_material(struct EditMesh *em, int index); void EM_deselect_by_material(struct EditMesh *em, int index); +/* editmesh_tools.c */ +void convert_to_triface(struct EditMesh *em, int direction); + /* editface.c */ struct MTFace *EM_get_active_mtface(struct EditMesh *em, struct EditFace **act_efa, struct MCol **mcol, int sloppy); diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 881c2bdf549..8192801d9fb 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -63,6 +63,7 @@ #include "DNA_scene_types.h" #include "DNA_meshdata_types.h" #include "DNA_curve_types.h" +#include "DNA_modifier_types.h" #include "MEM_guardedalloc.h" @@ -309,6 +310,29 @@ static void rna_Object_make_display_list(Object *ob, bContext *C) DAG_object_flush_update(sce, ob, OB_RECALC_DATA); } +static Object *rna_Object_find_armature(Object *ob) +{ + Object *ob_arm = NULL; + + if (ob->type != OB_MESH) return NULL; + + if (ob->parent && ob->partype == PARSKEL && ob->parent->type == OB_ARMATURE) { + ob_arm = ob->parent; + } + else { + ModifierData *mod = (ModifierData*)ob->modifiers.first; + while (mod) { + if (mod->type == eModifierType_Armature) { + ob_arm = ((ArmatureModifierData*)mod)->object; + } + + mod = mod->next; + } + } + + return ob_arm; +} + /* static void rna_Mesh_assign_verts_to_group(Object *ob, bDeformGroup *group, int *indices, int totindex, float weight, int assignmode) { @@ -409,6 +433,12 @@ void RNA_api_object(StructRNA *srna) parm= RNA_def_enum(func, "type", assign_mode_items, 0, "", "Vertex assign mode."); RNA_def_property_flag(parm, PROP_REQUIRED); + /* Armature */ + func= RNA_def_function(srna, "find_armature", "rna_Object_find_armature"); + RNA_def_function_ui_description(func, "Find armature influencing this object as a parent or via a modifier."); + parm= RNA_def_pointer(func, "ob_arm", "Object", "", "Armature object influencing this object or NULL."); + RNA_def_function_return(func, parm); + /* DAG */ func= RNA_def_function(srna, "make_display_list", "rna_Object_make_display_list"); RNA_def_function_ui_description(func, "Update object's display data."); /* XXX describe better */ From d1f22f6ce324e465697765221f2f4edf6af65147 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Sat, 8 Aug 2009 11:44:56 +0000 Subject: [PATCH 62/68] Copied 3DS, X3D scripts to release/io naming them after [import|export]_[format].py scheme. --- release/io/export_3ds.py | 1025 +++++++++++++++++++++++++++++++++++++ release/io/export_x3d.py | 1051 ++++++++++++++++++++++++++++++++++++++ release/io/import_3ds.py | 1007 ++++++++++++++++++++++++++++++++++++ 3 files changed, 3083 insertions(+) create mode 100644 release/io/export_3ds.py create mode 100644 release/io/export_x3d.py create mode 100644 release/io/import_3ds.py diff --git a/release/io/export_3ds.py b/release/io/export_3ds.py new file mode 100644 index 00000000000..69b4d00b4d8 --- /dev/null +++ b/release/io/export_3ds.py @@ -0,0 +1,1025 @@ +#!BPY +# coding: utf-8 +""" +Name: '3D Studio (.3ds)...' +Blender: 243 +Group: 'Export' +Tooltip: 'Export to 3DS file format (.3ds).' +""" + +__author__ = ["Campbell Barton", "Bob Holcomb", "Richard Lärkäng", "Damien McGinnes", "Mark Stijnman"] +__url__ = ("blenderartists.org", "www.blender.org", "www.gametutorials.com", "lib3ds.sourceforge.net/") +__version__ = "0.90a" +__bpydoc__ = """\ + +3ds Exporter + +This script Exports a 3ds file. + +Exporting is based on 3ds loader from www.gametutorials.com(Thanks DigiBen) and using information +from the lib3ds project (http://lib3ds.sourceforge.net/) sourcecode. +""" + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Script copyright (C) Bob Holcomb +# +# 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. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + + +###################################################### +# Importing modules +###################################################### + +import Blender +import bpy +from BPyMesh import getMeshFromObject +from BPyObject import getDerivedObjects +try: + import struct +except: + struct = None + +# So 3ds max can open files, limit names to 12 in length +# this is verry annoying for filenames! +name_unique = [] +name_mapping = {} +def sane_name(name): + name_fixed = name_mapping.get(name) + if name_fixed != None: + return name_fixed + + if len(name) > 12: + new_name = name[:12] + else: + new_name = name + + i = 0 + + while new_name in name_unique: + new_name = new_name[:-4] + '.%.3d' % i + i+=1 + + name_unique.append(new_name) + name_mapping[name] = new_name + return new_name + +###################################################### +# Data Structures +###################################################### + +#Some of the chunks that we will export +#----- Primary Chunk, at the beginning of each file +PRIMARY= long("0x4D4D",16) + +#------ Main Chunks +OBJECTINFO = long("0x3D3D",16); #This gives the version of the mesh and is found right before the material and object information +VERSION = long("0x0002",16); #This gives the version of the .3ds file +KFDATA = long("0xB000",16); #This is the header for all of the key frame info + +#------ sub defines of OBJECTINFO +MATERIAL=45055 #0xAFFF // This stored the texture info +OBJECT=16384 #0x4000 // This stores the faces, vertices, etc... + +#>------ sub defines of MATERIAL +MATNAME = long("0xA000",16); # This holds the material name +MATAMBIENT = long("0xA010",16); # Ambient color of the object/material +MATDIFFUSE = long("0xA020",16); # This holds the color of the object/material +MATSPECULAR = long("0xA030",16); # SPecular color of the object/material +MATSHINESS = long("0xA040",16); # ?? +MATMAP = long("0xA200",16); # This is a header for a new material +MATMAPFILE = long("0xA300",16); # This holds the file name of the texture + +RGB1= long("0x0011",16) +RGB2= long("0x0012",16) + +#>------ sub defines of OBJECT +OBJECT_MESH = long("0x4100",16); # This lets us know that we are reading a new object +OBJECT_LIGHT = long("0x4600",16); # This lets un know we are reading a light object +OBJECT_CAMERA= long("0x4700",16); # This lets un know we are reading a camera object + +#>------ sub defines of CAMERA +OBJECT_CAM_RANGES= long("0x4720",16); # The camera range values + +#>------ sub defines of OBJECT_MESH +OBJECT_VERTICES = long("0x4110",16); # The objects vertices +OBJECT_FACES = long("0x4120",16); # The objects faces +OBJECT_MATERIAL = long("0x4130",16); # This is found if the object has a material, either texture map or color +OBJECT_UV = long("0x4140",16); # The UV texture coordinates +OBJECT_TRANS_MATRIX = long("0x4160",16); # The Object Matrix + +#>------ sub defines of KFDATA +KFDATA_KFHDR = long("0xB00A",16); +KFDATA_KFSEG = long("0xB008",16); +KFDATA_KFCURTIME = long("0xB009",16); +KFDATA_OBJECT_NODE_TAG = long("0xB002",16); + +#>------ sub defines of OBJECT_NODE_TAG +OBJECT_NODE_ID = long("0xB030",16); +OBJECT_NODE_HDR = long("0xB010",16); +OBJECT_PIVOT = long("0xB013",16); +OBJECT_INSTANCE_NAME = long("0xB011",16); +POS_TRACK_TAG = long("0xB020",16); +ROT_TRACK_TAG = long("0xB021",16); +SCL_TRACK_TAG = long("0xB022",16); + +def uv_key(uv): + return round(uv.x, 6), round(uv.y, 6) + +# size defines: +SZ_SHORT = 2 +SZ_INT = 4 +SZ_FLOAT = 4 + +class _3ds_short(object): + '''Class representing a short (2-byte integer) for a 3ds file. + *** This looks like an unsigned short H is unsigned from the struct docs - Cam***''' + __slots__ = 'value' + def __init__(self, val=0): + self.value=val + + def get_size(self): + return SZ_SHORT + + def write(self,file): + file.write(struct.pack("= mat_ls_len: + mat_index = f.mat = 0 + mat = mat_ls[mat_index] + if mat: mat_name = mat.name + else: mat_name = None + # else there alredy set to none + + img = f.image + if img: img_name = img.name + else: img_name = None + + materialDict.setdefault((mat_name, img_name), (mat, img) ) + + + else: + for mat in mat_ls: + if mat: # material may be None so check its not. + materialDict.setdefault((mat.name, None), (mat, None) ) + + # Why 0 Why! + for f in data.faces: + if f.mat >= mat_ls_len: + f.mat = 0 + + # Make material chunks for all materials used in the meshes: + for mat_and_image in materialDict.itervalues(): + object_info.add_subchunk(make_material_chunk(mat_and_image[0], mat_and_image[1])) + + # Give all objects a unique ID and build a dictionary from object name to object id: + """ + name_to_id = {} + for ob, data in mesh_objects: + name_to_id[ob.name]= len(name_to_id) + #for ob in empty_objects: + # name_to_id[ob.name]= len(name_to_id) + """ + + # Create object chunks for all meshes: + i = 0 + for ob, blender_mesh in mesh_objects: + # create a new object chunk + object_chunk = _3ds_chunk(OBJECT) + + # set the object name + object_chunk.add_variable("name", _3ds_string(sane_name(ob.name))) + + # make a mesh chunk out of the mesh: + object_chunk.add_subchunk(make_mesh_chunk(blender_mesh, materialDict)) + object_info.add_subchunk(object_chunk) + + ''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX + # make a kf object node for the object: + kfdata.add_subchunk(make_kf_obj_node(ob, name_to_id)) + ''' + blender_mesh.verts = None + i+=i + + # Create chunks for all empties: + ''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX + for ob in empty_objects: + # Empties only require a kf object node: + kfdata.add_subchunk(make_kf_obj_node(ob, name_to_id)) + pass + ''' + + # Add main object info chunk to primary chunk: + primary.add_subchunk(object_info) + + ''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX + # Add main keyframe data chunk to primary chunk: + primary.add_subchunk(kfdata) + ''' + + # At this point, the chunk hierarchy is completely built. + + # Check the size: + primary.get_size() + # Open the file for writing: + file = open( filename, 'wb' ) + + # Recursively write the chunks to file: + primary.write(file) + + # Close the file: + file.close() + + # Debugging only: report the exporting time: + Blender.Window.WaitCursor(0) + print "3ds export time: %.2f" % (Blender.sys.time() - time1) + + # Debugging only: dump the chunk hierarchy: + #primary.dump() + + +if __name__=='__main__': + if struct: + Blender.Window.FileSelector(save_3ds, "Export 3DS", Blender.sys.makename(ext='.3ds')) + else: + Blender.Draw.PupMenu("Error%t|This script requires a full python installation") +# save_3ds('/test_b.3ds') diff --git a/release/io/export_x3d.py b/release/io/export_x3d.py new file mode 100644 index 00000000000..b12ff67d8a6 --- /dev/null +++ b/release/io/export_x3d.py @@ -0,0 +1,1051 @@ +#!BPY +""" Registration info for Blender menus: +Name: 'X3D Extensible 3D (.x3d)...' +Blender: 245 +Group: 'Export' +Tooltip: 'Export selection to Extensible 3D file (.x3d)' +""" + +__author__ = ("Bart", "Campbell Barton") +__email__ = ["Bart, bart:neeneenee*de"] +__url__ = ["Author's (Bart) homepage, http://www.neeneenee.de/vrml"] +__version__ = "2006/01/17" +__bpydoc__ = """\ +This script exports to X3D format. + +Usage: + +Run this script from "File->Export" menu. A pop-up will ask whether you +want to export only selected or all relevant objects. + +Known issues:
+ Doesn't handle multiple materials (don't use material indices);
+ Doesn't handle multiple UV textures on a single mesh (create a mesh for each texture);
+ Can't get the texture array associated with material * not the UV ones; +""" + + +# $Id$ +# +#------------------------------------------------------------------------ +# X3D exporter for blender 2.36 or above +# +# ***** 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. +# +# ***** END GPL LICENCE BLOCK ***** +# + +#################################### +# Library dependancies +#################################### + +import Blender +from Blender import Object, Lamp, Draw, Image, Text, sys, Mesh +from Blender.Scene import Render +import math +import BPyObject +import BPyMesh + +# +DEG2RAD=0.017453292519943295 +MATWORLD= Blender.Mathutils.RotationMatrix(-90, 4, 'x') + +#################################### +# Global Variables +#################################### + +filename = Blender.Get('filename') +_safeOverwrite = True + +extension = '' + +########################################################## +# Functions for writing output file +########################################################## + +class x3d_class: + + def __init__(self, filename): + #--- public you can change these --- + self.writingcolor = 0 + self.writingtexture = 0 + self.writingcoords = 0 + self.proto = 1 + self.matonly = 0 + self.share = 0 + self.billnode = 0 + self.halonode = 0 + self.collnode = 0 + self.tilenode = 0 + self.verbose=2 # level of verbosity in console 0-none, 1-some, 2-most + self.cp=3 # decimals for material color values 0.000 - 1.000 + self.vp=3 # decimals for vertex coordinate values 0.000 - n.000 + self.tp=3 # decimals for texture coordinate values 0.000 - 1.000 + self.it=3 + + #--- class private don't touch --- + self.texNames={} # dictionary of textureNames + self.matNames={} # dictionary of materiaNames + self.meshNames={} # dictionary of meshNames + self.indentLevel=0 # keeps track of current indenting + self.filename=filename + self.file = None + if filename.lower().endswith('.x3dz'): + try: + import gzip + self.file = gzip.open(filename, "w") + except: + print "failed to import compression modules, exporting uncompressed" + self.filename = filename[:-1] # remove trailing z + + if self.file == None: + self.file = open(self.filename, "w") + + self.bNav=0 + self.nodeID=0 + self.namesReserved=[ "Anchor","Appearance","Arc2D","ArcClose2D","AudioClip","Background","Billboard", + "BooleanFilter","BooleanSequencer","BooleanToggle","BooleanTrigger","Box","Circle2D", + "Collision","Color","ColorInterpolator","ColorRGBA","component","Cone","connect", + "Contour2D","ContourPolyline2D","Coordinate","CoordinateDouble","CoordinateInterpolator", + "CoordinateInterpolator2D","Cylinder","CylinderSensor","DirectionalLight","Disk2D", + "ElevationGrid","EspduTransform","EXPORT","ExternProtoDeclare","Extrusion","field", + "fieldValue","FillProperties","Fog","FontStyle","GeoCoordinate","GeoElevationGrid", + "GeoLocationLocation","GeoLOD","GeoMetadata","GeoOrigin","GeoPositionInterpolator", + "GeoTouchSensor","GeoViewpoint","Group","HAnimDisplacer","HAnimHumanoid","HAnimJoint", + "HAnimSegment","HAnimSite","head","ImageTexture","IMPORT","IndexedFaceSet", + "IndexedLineSet","IndexedTriangleFanSet","IndexedTriangleSet","IndexedTriangleStripSet", + "Inline","IntegerSequencer","IntegerTrigger","IS","KeySensor","LineProperties","LineSet", + "LoadSensor","LOD","Material","meta","MetadataDouble","MetadataFloat","MetadataInteger", + "MetadataSet","MetadataString","MovieTexture","MultiTexture","MultiTextureCoordinate", + "MultiTextureTransform","NavigationInfo","Normal","NormalInterpolator","NurbsCurve", + "NurbsCurve2D","NurbsOrientationInterpolator","NurbsPatchSurface", + "NurbsPositionInterpolator","NurbsSet","NurbsSurfaceInterpolator","NurbsSweptSurface", + "NurbsSwungSurface","NurbsTextureCoordinate","NurbsTrimmedSurface","OrientationInterpolator", + "PixelTexture","PlaneSensor","PointLight","PointSet","Polyline2D","Polypoint2D", + "PositionInterpolator","PositionInterpolator2D","ProtoBody","ProtoDeclare","ProtoInstance", + "ProtoInterface","ProximitySensor","ReceiverPdu","Rectangle2D","ROUTE","ScalarInterpolator", + "Scene","Script","Shape","SignalPdu","Sound","Sphere","SphereSensor","SpotLight","StaticGroup", + "StringSensor","Switch","Text","TextureBackground","TextureCoordinate","TextureCoordinateGenerator", + "TextureTransform","TimeSensor","TimeTrigger","TouchSensor","Transform","TransmitterPdu", + "TriangleFanSet","TriangleSet","TriangleSet2D","TriangleStripSet","Viewpoint","VisibilitySensor", + "WorldInfo","X3D","XvlShell","VertexShader","FragmentShader","MultiShaderAppearance","ShaderAppearance" ] + self.namesStandard=[ "Empty","Empty.000","Empty.001","Empty.002","Empty.003","Empty.004","Empty.005", + "Empty.006","Empty.007","Empty.008","Empty.009","Empty.010","Empty.011","Empty.012", + "Scene.001","Scene.002","Scene.003","Scene.004","Scene.005","Scene.06","Scene.013", + "Scene.006","Scene.007","Scene.008","Scene.009","Scene.010","Scene.011","Scene.012", + "World","World.000","World.001","World.002","World.003","World.004","World.005" ] + self.namesFog=[ "","LINEAR","EXPONENTIAL","" ] + +########################################################## +# Writing nodes routines +########################################################## + + def writeHeader(self): + #bfile = sys.expandpath( Blender.Get('filename') ).replace('<', '<').replace('>', '>') + bfile = self.filename.replace('<', '<').replace('>', '>') # use outfile name + self.file.write("\n") + self.file.write("\n") + self.file.write("\n") + self.file.write("\n") + self.file.write("\t\n" % sys.basename(bfile)) + self.file.write("\t\n" % Blender.Get('version')) + self.file.write("\t\n") + self.file.write("\n") + self.file.write("\n") + + # This functionality is poorly defined, disabling for now - campbell + ''' + def writeInline(self): + inlines = Blender.Scene.Get() + allinlines = len(inlines) + if scene != inlines[0]: + return + else: + for i in xrange(allinlines): + nameinline=inlines[i].name + if (nameinline not in self.namesStandard) and (i > 0): + self.file.write("" % nameinline) + self.file.write("\n\n") + + + def writeScript(self): + textEditor = Blender.Text.Get() + alltext = len(textEditor) + for i in xrange(alltext): + nametext = textEditor[i].name + nlines = textEditor[i].getNLines() + if (self.proto == 1): + if (nametext == "proto" or nametext == "proto.js" or nametext == "proto.txt") and (nlines != None): + nalllines = len(textEditor[i].asLines()) + alllines = textEditor[i].asLines() + for j in xrange(nalllines): + self.writeIndented(alllines[j] + "\n") + elif (self.proto == 0): + if (nametext == "route" or nametext == "route.js" or nametext == "route.txt") and (nlines != None): + nalllines = len(textEditor[i].asLines()) + alllines = textEditor[i].asLines() + for j in xrange(nalllines): + self.writeIndented(alllines[j] + "\n") + self.writeIndented("\n") + ''' + + def writeViewpoint(self, ob, mat, scene): + context = scene.render + ratio = float(context.imageSizeY())/float(context.imageSizeX()) + lens = (360* (math.atan(ratio *16 / ob.data.getLens()) / math.pi))*(math.pi/180) + lens = min(lens, math.pi) + + # get the camera location, subtract 90 degress from X to orient like X3D does + # mat = ob.matrixWorld - mat is now passed! + + loc = self.rotatePointForVRML(mat.translationPart()) + rot = mat.toEuler() + rot = (((rot[0]-90)*DEG2RAD), rot[1]*DEG2RAD, rot[2]*DEG2RAD) + nRot = self.rotatePointForVRML( rot ) + # convert to Quaternion and to Angle Axis + Q = self.eulerToQuaternions(nRot[0], nRot[1], nRot[2]) + Q1 = self.multiplyQuaternions(Q[0], Q[1]) + Qf = self.multiplyQuaternions(Q1, Q[2]) + angleAxis = self.quaternionToAngleAxis(Qf) + self.file.write("\n\n" % (lens)) + + def writeFog(self, world): + if world: + mtype = world.getMistype() + mparam = world.getMist() + grd = world.getHor() + grd0, grd1, grd2 = grd[0], grd[1], grd[2] + else: + return + if (mtype == 1 or mtype == 2): + self.file.write("\n\n" % round(mparam[2],self.cp)) + else: + return + + def writeNavigationInfo(self, scene): + self.file.write('\n') + + def writeSpotLight(self, ob, mtx, lamp, world): + safeName = self.cleanStr(ob.name) + if world: + ambi = world.amb + ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5 + else: + ambi = 0 + ambientIntensity = 0 + + # compute cutoff and beamwidth + intensity=min(lamp.energy/1.75,1.0) + beamWidth=((lamp.spotSize*math.pi)/180.0)*.37; + cutOffAngle=beamWidth*1.3 + + dx,dy,dz=self.computeDirection(mtx) + # note -dx seems to equal om[3][0] + # note -dz seems to equal om[3][1] + # note dy seems to equal om[3][2] + + #location=(ob.matrixWorld*MATWORLD).translationPart() # now passed + location=(mtx*MATWORLD).translationPart() + + radius = lamp.dist*math.cos(beamWidth) + self.file.write("\n\n" % (round(location[0],3), round(location[1],3), round(location[2],3))) + + + def writeDirectionalLight(self, ob, mtx, lamp, world): + safeName = self.cleanStr(ob.name) + if world: + ambi = world.amb + ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5 + else: + ambi = 0 + ambientIntensity = 0 + + intensity=min(lamp.energy/1.75,1.0) + (dx,dy,dz)=self.computeDirection(mtx) + self.file.write("\n\n" % (round(dx,4),round(dy,4),round(dz,4))) + + def writePointLight(self, ob, mtx, lamp, world): + safeName = self.cleanStr(ob.name) + if world: + ambi = world.amb + ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5 + else: + ambi = 0 + ambientIntensity = 0 + + # location=(ob.matrixWorld*MATWORLD).translationPart() # now passed + location= (mtx*MATWORLD).translationPart() + + self.file.write("\n\n" % (round(location[0],3), round(location[1],3), round(location[2],3))) + ''' + def writeNode(self, ob, mtx): + obname=str(ob.name) + if obname in self.namesStandard: + return + else: + dx,dy,dz = self.computeDirection(mtx) + # location=(ob.matrixWorld*MATWORLD).translationPart() + location=(mtx*MATWORLD).translationPart() + self.writeIndented("<%s\n" % obname,1) + self.writeIndented("direction=\"%s %s %s\"\n" % (round(dx,3),round(dy,3),round(dz,3))) + self.writeIndented("location=\"%s %s %s\"\n" % (round(location[0],3), round(location[1],3), round(location[2],3))) + self.writeIndented("/>\n",-1) + self.writeIndented("\n") + ''' + def secureName(self, name): + name = name + str(self.nodeID) + self.nodeID=self.nodeID+1 + if len(name) <= 3: + newname = "_" + str(self.nodeID) + return "%s" % (newname) + else: + for bad in ['"','#',"'",',','.','[','\\',']','{','}']: + name=name.replace(bad,'_') + if name in self.namesReserved: + newname = name[0:3] + "_" + str(self.nodeID) + return "%s" % (newname) + elif name[0].isdigit(): + newname = "_" + name + str(self.nodeID) + return "%s" % (newname) + else: + newname = name + return "%s" % (newname) + + def writeIndexedFaceSet(self, ob, mesh, mtx, world, EXPORT_TRI = False): + imageMap={} # set of used images + sided={} # 'one':cnt , 'two':cnt + vColors={} # 'multi':1 + meshName = self.cleanStr(ob.name) + + meshME = self.cleanStr(ob.getData(mesh=1).name) # We dont care if its the mesh name or not + if len(mesh.faces) == 0: return + mode = 0 + if mesh.faceUV: + for face in mesh.faces: + mode |= face.mode + + if mode & Mesh.FaceModes.HALO and self.halonode == 0: + self.writeIndented("\n",1) + self.halonode = 1 + elif mode & Mesh.FaceModes.BILLBOARD and self.billnode == 0: + self.writeIndented("\n",1) + self.billnode = 1 + elif mode & Mesh.FaceModes.OBCOL and self.matonly == 0: + self.matonly = 1 + elif mode & Mesh.FaceModes.TILES and self.tilenode == 0: + self.tilenode = 1 + elif not mode & Mesh.FaceModes.DYNAMIC and self.collnode == 0: + self.writeIndented("\n",1) + self.collnode = 1 + + nIFSCnt=self.countIFSSetsNeeded(mesh, imageMap, sided, vColors) + + if nIFSCnt > 1: + self.writeIndented("\n" % ("G_", meshName),1) + + if sided.has_key('two') and sided['two'] > 0: + bTwoSided=1 + else: + bTwoSided=0 + + # mtx = ob.matrixWorld * MATWORLD # mtx is now passed + mtx = mtx * MATWORLD + + loc= mtx.translationPart() + sca= mtx.scalePart() + quat = mtx.toQuat() + rot= quat.axis + + # self.writeIndented('\n' % (rot[0], rot[1], rot[2], rot[3])) + self.writeIndented('\n' % \ + (meshName, loc[0], loc[1], loc[2], sca[0], sca[1], sca[2], rot[0], rot[1], rot[2], quat.angle*DEG2RAD) ) + + self.writeIndented("\n",1) + maters=mesh.materials + hasImageTexture=0 + issmooth=0 + + if len(maters) > 0 or mesh.faceUV: + self.writeIndented("\n", 1) + # right now this script can only handle a single material per mesh. + if len(maters) >= 1: + mat=maters[0] + matFlags = mat.getMode() + if not matFlags & Blender.Material.Modes['TEXFACE']: + self.writeMaterial(mat, self.cleanStr(maters[0].name,''), world) + if len(maters) > 1: + print "Warning: mesh named %s has multiple materials" % meshName + print "Warning: only one material per object handled" + + #-- textures + if mesh.faceUV: + for face in mesh.faces: + if (hasImageTexture == 0) and (face.image): + self.writeImageTexture(face.image) + hasImageTexture=1 # keep track of face texture + if self.tilenode == 1: + self.writeIndented("\n" % (face.image.xrep, face.image.yrep)) + self.tilenode = 0 + self.writeIndented("\n", -1) + + #-- IndexedFaceSet or IndexedLineSet + + # user selected BOUNDS=1, SOLID=3, SHARED=4, or TEXTURE=5 + ifStyle="IndexedFaceSet" + # look up mesh name, use it if available + if self.meshNames.has_key(meshME): + self.writeIndented("<%s USE=\"ME_%s\">" % (ifStyle, meshME), 1) + self.meshNames[meshME]+=1 + else: + if int(mesh.users) > 1: + self.writeIndented("<%s DEF=\"ME_%s\" " % (ifStyle, meshME), 1) + self.meshNames[meshME]=1 + else: + self.writeIndented("<%s " % ifStyle, 1) + + if bTwoSided == 1: + self.file.write("solid=\"false\" ") + else: + self.file.write("solid=\"true\" ") + + for face in mesh.faces: + if face.smooth: + issmooth=1 + break + if issmooth==1: + creaseAngle=(mesh.degr)*(math.pi/180.0) + self.file.write("creaseAngle=\"%s\" " % (round(creaseAngle,self.cp))) + + #--- output textureCoordinates if UV texture used + if mesh.faceUV: + if self.matonly == 1 and self.share == 1: + self.writeFaceColors(mesh) + elif hasImageTexture == 1: + self.writeTextureCoordinates(mesh) + #--- output coordinates + self.writeCoordinates(ob, mesh, meshName, EXPORT_TRI) + + self.writingcoords = 1 + self.writingtexture = 1 + self.writingcolor = 1 + self.writeCoordinates(ob, mesh, meshName, EXPORT_TRI) + + #--- output textureCoordinates if UV texture used + if mesh.faceUV: + if hasImageTexture == 1: + self.writeTextureCoordinates(mesh) + elif self.matonly == 1 and self.share == 1: + self.writeFaceColors(mesh) + #--- output vertexColors + self.matonly = 0 + self.share = 0 + + self.writingcoords = 0 + self.writingtexture = 0 + self.writingcolor = 0 + #--- output closing braces + self.writeIndented("\n" % ifStyle, -1) + self.writeIndented("\n", -1) + self.writeIndented("\n", -1) + + if self.halonode == 1: + self.writeIndented("\n", -1) + self.halonode = 0 + + if self.billnode == 1: + self.writeIndented("\n", -1) + self.billnode = 0 + + if self.collnode == 1: + self.writeIndented("\n", -1) + self.collnode = 0 + + if nIFSCnt > 1: + self.writeIndented("\n", -1) + + self.file.write("\n") + + def writeCoordinates(self, ob, mesh, meshName, EXPORT_TRI = False): + # create vertex list and pre rotate -90 degrees X for VRML + + if self.writingcoords == 0: + self.file.write('coordIndex="') + for face in mesh.faces: + fv = face.v + + if len(face)==3: + self.file.write("%i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index)) + else: + if EXPORT_TRI: + self.file.write("%i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index)) + self.file.write("%i %i %i -1, " % (fv[0].index, fv[2].index, fv[3].index)) + else: + self.file.write("%i %i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index, fv[3].index)) + + self.file.write("\">\n") + else: + #-- vertices + # mesh.transform(ob.matrixWorld) + self.writeIndented("") + self.writeIndented("\n", -1) + + def writeTextureCoordinates(self, mesh): + texCoordList=[] + texIndexList=[] + j=0 + + for face in mesh.faces: + for uv in face.uv: + texIndexList.append(j) + texCoordList.append(uv) + j=j+1 + texIndexList.append(-1) + if self.writingtexture == 0: + self.file.write("\n\t\t\ttexCoordIndex=\"") + texIndxStr="" + for i in xrange(len(texIndexList)): + texIndxStr = texIndxStr + "%d, " % texIndexList[i] + if texIndexList[i]==-1: + self.file.write(texIndxStr) + texIndxStr="" + self.file.write("\"\n\t\t\t") + else: + self.writeIndented("") + self.writeIndented("\n", -1) + + def writeFaceColors(self, mesh): + if self.writingcolor == 0: + self.file.write("colorPerVertex=\"false\" ") + else: + self.writeIndented(" 2: + print "Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b) + aColor = self.rgbToFS(c) + self.file.write("%s, " % aColor) + self.file.write("\" />") + self.writeIndented("\n",-1) + + def writeMaterial(self, mat, matName, world): + # look up material name, use it if available + if self.matNames.has_key(matName): + self.writeIndented("\n" % matName) + self.matNames[matName]+=1 + return; + + self.matNames[matName]=1 + + ambient = mat.amb/3 + diffuseR, diffuseG, diffuseB = mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2] + if world: + ambi = world.getAmb() + ambi0, ambi1, ambi2 = (ambi[0]*mat.amb)*2, (ambi[1]*mat.amb)*2, (ambi[2]*mat.amb)*2 + else: + ambi0, ambi1, ambi2 = 0, 0, 0 + emisR, emisG, emisB = (diffuseR*mat.emit+ambi0)/2, (diffuseG*mat.emit+ambi1)/2, (diffuseB*mat.emit+ambi2)/2 + + shininess = mat.hard/512.0 + specR = (mat.specCol[0]+0.001)/(1.25/(mat.spec+0.001)) + specG = (mat.specCol[1]+0.001)/(1.25/(mat.spec+0.001)) + specB = (mat.specCol[2]+0.001)/(1.25/(mat.spec+0.001)) + transp = 1-mat.alpha + matFlags = mat.getMode() + if matFlags & Blender.Material.Modes['SHADELESS']: + ambient = 1 + shine = 1 + specR = emitR = diffuseR + specG = emitG = diffuseG + specB = emitB = diffuseB + self.writeIndented("" % (round(transp,self.cp))) + self.writeIndented("\n",-1) + + def writeImageTexture(self, image): + name = image.name + filename = image.filename.split('/')[-1].split('\\')[-1] + if self.texNames.has_key(name): + self.writeIndented("\n" % self.cleanStr(name)) + self.texNames[name] += 1 + return + else: + self.writeIndented("" % name) + self.writeIndented("\n",-1) + self.texNames[name] = 1 + + def writeBackground(self, world, alltextures): + if world: worldname = world.name + else: return + blending = world.getSkytype() + grd = world.getHor() + grd0, grd1, grd2 = grd[0], grd[1], grd[2] + sky = world.getZen() + sky0, sky1, sky2 = sky[0], sky[1], sky[2] + mix0, mix1, mix2 = grd[0]+sky[0], grd[1]+sky[1], grd[2]+sky[2] + mix0, mix1, mix2 = mix0/2, mix1/2, mix2/2 + self.file.write("\n\n") + +########################################################## +# export routine +########################################################## + + def export(self, scene, world, alltextures,\ + EXPORT_APPLY_MODIFIERS = False,\ + EXPORT_TRI= False,\ + ): + + print "Info: starting X3D export to " + self.filename + "..." + self.writeHeader() + # self.writeScript() + self.writeNavigationInfo(scene) + self.writeBackground(world, alltextures) + self.writeFog(world) + self.proto = 0 + + + # COPIED FROM OBJ EXPORTER + if EXPORT_APPLY_MODIFIERS: + temp_mesh_name = '~tmp-mesh' + + # Get the container mesh. - used for applying modifiers and non mesh objects. + containerMesh = meshName = tempMesh = None + for meshName in Blender.NMesh.GetNames(): + if meshName.startswith(temp_mesh_name): + tempMesh = Mesh.Get(meshName) + if not tempMesh.users: + containerMesh = tempMesh + if not containerMesh: + containerMesh = Mesh.New(temp_mesh_name) + # -------------------------- + + + for ob_main in scene.objects.context: + for ob, ob_mat in BPyObject.getDerivedObjects(ob_main): + objType=ob.type + objName=ob.name + self.matonly = 0 + if objType == "Camera": + self.writeViewpoint(ob, ob_mat, scene) + elif objType in ("Mesh", "Curve", "Surf", "Text") : + if EXPORT_APPLY_MODIFIERS or objType != 'Mesh': + me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, False, scene) + else: + me = ob.getData(mesh=1) + + self.writeIndexedFaceSet(ob, me, ob_mat, world, EXPORT_TRI = EXPORT_TRI) + elif objType == "Lamp": + data= ob.data + datatype=data.type + if datatype == Lamp.Types.Lamp: + self.writePointLight(ob, ob_mat, data, world) + elif datatype == Lamp.Types.Spot: + self.writeSpotLight(ob, ob_mat, data, world) + elif datatype == Lamp.Types.Sun: + self.writeDirectionalLight(ob, ob_mat, data, world) + else: + self.writeDirectionalLight(ob, ob_mat, data, world) + # do you think x3d could document what to do with dummy objects? + #elif objType == "Empty" and objName != "Empty": + # self.writeNode(ob, ob_mat) + else: + #print "Info: Ignoring [%s], object type [%s] not handle yet" % (object.name,object.getType) + pass + + self.file.write("\n\n") + + if EXPORT_APPLY_MODIFIERS: + if containerMesh: + containerMesh.verts = None + + self.cleanup() + +########################################################## +# Utility methods +########################################################## + + def cleanup(self): + self.file.close() + self.texNames={} + self.matNames={} + self.indentLevel=0 + print "Info: finished X3D export to %s\n" % self.filename + + def cleanStr(self, name, prefix='rsvd_'): + """cleanStr(name,prefix) - try to create a valid VRML DEF name from object name""" + + newName=name[:] + if len(newName) == 0: + self.nNodeID+=1 + return "%s%d" % (prefix, self.nNodeID) + + if newName in self.namesReserved: + newName='%s%s' % (prefix,newName) + + if newName[0].isdigit(): + newName='%s%s' % ('_',newName) + + for bad in [' ','"','#',"'",',','.','[','\\',']','{','}']: + newName=newName.replace(bad,'_') + return newName + + def countIFSSetsNeeded(self, mesh, imageMap, sided, vColors): + """ + countIFFSetsNeeded() - should look at a blender mesh to determine + how many VRML IndexFaceSets or IndexLineSets are needed. A + new mesh created under the following conditions: + + o - split by UV Textures / one per mesh + o - split by face, one sided and two sided + o - split by smooth and flat faces + o - split when faces only have 2 vertices * needs to be an IndexLineSet + """ + + imageNameMap={} + faceMap={} + nFaceIndx=0 + + if mesh.faceUV: + for face in mesh.faces: + sidename=''; + if face.mode & Mesh.FaceModes.TWOSIDE: + sidename='two' + else: + sidename='one' + + if sided.has_key(sidename): + sided[sidename]+=1 + else: + sided[sidename]=1 + + image = face.image + if image: + faceName="%s_%s" % (face.image.name, sidename); + try: + imageMap[faceName].append(face) + except: + imageMap[faceName]=[face.image.name,sidename,face] + + if self.verbose > 2: + for faceName in imageMap.iterkeys(): + ifs=imageMap[faceName] + print "Debug: faceName=%s image=%s, solid=%s facecnt=%d" % \ + (faceName, ifs[0], ifs[1], len(ifs)-2) + + return len(imageMap) + + def faceToString(self,face): + + print "Debug: face.flag=0x%x (bitflags)" % face.flag + if face.sel: + print "Debug: face.sel=true" + + print "Debug: face.mode=0x%x (bitflags)" % face.mode + if face.mode & Mesh.FaceModes.TWOSIDE: + print "Debug: face.mode twosided" + + print "Debug: face.transp=0x%x (enum)" % face.transp + if face.transp == Mesh.FaceTranspModes.SOLID: + print "Debug: face.transp.SOLID" + + if face.image: + print "Debug: face.image=%s" % face.image.name + print "Debug: face.materialIndex=%d" % face.materialIndex + + def getVertexColorByIndx(self, mesh, indx): + c = None + for face in mesh.faces: + j=0 + for vertex in face.v: + if vertex.index == indx: + c=face.col[j] + break + j=j+1 + if c: break + return c + + def meshToString(self,mesh): + print "Debug: mesh.hasVertexUV=%d" % mesh.vertexColors + print "Debug: mesh.faceUV=%d" % mesh.faceUV + print "Debug: mesh.hasVertexColours=%d" % mesh.hasVertexColours() + print "Debug: mesh.verts=%d" % len(mesh.verts) + print "Debug: mesh.faces=%d" % len(mesh.faces) + print "Debug: mesh.materials=%d" % len(mesh.materials) + + def rgbToFS(self, c): + s="%s %s %s" % ( + round(c.r/255.0,self.cp), + round(c.g/255.0,self.cp), + round(c.b/255.0,self.cp)) + return s + + def computeDirection(self, mtx): + x,y,z=(0,-1.0,0) # point down + + ax,ay,az = (mtx*MATWORLD).toEuler() + + ax *= DEG2RAD + ay *= DEG2RAD + az *= DEG2RAD + # rot X + x1=x + y1=y*math.cos(ax)-z*math.sin(ax) + z1=y*math.sin(ax)+z*math.cos(ax) + + # rot Y + x2=x1*math.cos(ay)+z1*math.sin(ay) + y2=y1 + z2=z1*math.cos(ay)-x1*math.sin(ay) + + # rot Z + x3=x2*math.cos(az)-y2*math.sin(az) + y3=x2*math.sin(az)+y2*math.cos(az) + z3=z2 + + return [x3,y3,z3] + + + # swap Y and Z to handle axis difference between Blender and VRML + #------------------------------------------------------------------------ + def rotatePointForVRML(self, v): + x = v[0] + y = v[2] + z = -v[1] + + vrmlPoint=[x, y, z] + return vrmlPoint + + # For writing well formed VRML code + #------------------------------------------------------------------------ + def writeIndented(self, s, inc=0): + if inc < 1: + self.indentLevel = self.indentLevel + inc + + spaces="" + for x in xrange(self.indentLevel): + spaces = spaces + "\t" + self.file.write(spaces + s) + + if inc > 0: + self.indentLevel = self.indentLevel + inc + + # Converts a Euler to three new Quaternions + # Angles of Euler are passed in as radians + #------------------------------------------------------------------------ + def eulerToQuaternions(self, x, y, z): + Qx = [math.cos(x/2), math.sin(x/2), 0, 0] + Qy = [math.cos(y/2), 0, math.sin(y/2), 0] + Qz = [math.cos(z/2), 0, 0, math.sin(z/2)] + + quaternionVec=[Qx,Qy,Qz] + return quaternionVec + + # Multiply two Quaternions together to get a new Quaternion + #------------------------------------------------------------------------ + def multiplyQuaternions(self, Q1, Q2): + result = [((Q1[0] * Q2[0]) - (Q1[1] * Q2[1]) - (Q1[2] * Q2[2]) - (Q1[3] * Q2[3])), + ((Q1[0] * Q2[1]) + (Q1[1] * Q2[0]) + (Q1[2] * Q2[3]) - (Q1[3] * Q2[2])), + ((Q1[0] * Q2[2]) + (Q1[2] * Q2[0]) + (Q1[3] * Q2[1]) - (Q1[1] * Q2[3])), + ((Q1[0] * Q2[3]) + (Q1[3] * Q2[0]) + (Q1[1] * Q2[2]) - (Q1[2] * Q2[1]))] + + return result + + # Convert a Quaternion to an Angle Axis (ax, ay, az, angle) + # angle is in radians + #------------------------------------------------------------------------ + def quaternionToAngleAxis(self, Qf): + scale = math.pow(Qf[1],2) + math.pow(Qf[2],2) + math.pow(Qf[3],2) + ax = Qf[1] + ay = Qf[2] + az = Qf[3] + + if scale > .0001: + ax/=scale + ay/=scale + az/=scale + + angle = 2 * math.acos(Qf[0]) + + result = [ax, ay, az, angle] + return result + +########################################################## +# Callbacks, needed before Main +########################################################## + +def x3d_export(filename, \ + EXPORT_APPLY_MODIFIERS= False,\ + EXPORT_TRI= False,\ + EXPORT_GZIP= False,\ + ): + + if EXPORT_GZIP: + if not filename.lower().endswith('.x3dz'): + filename = '.'.join(filename.split('.')[:-1]) + '.x3dz' + else: + if not filename.lower().endswith('.x3d'): + filename = '.'.join(filename.split('.')[:-1]) + '.x3d' + + + scene = Blender.Scene.GetCurrent() + world = scene.world + alltextures = Blender.Texture.Get() + + wrlexport=x3d_class(filename) + wrlexport.export(\ + scene,\ + world,\ + alltextures,\ + \ + EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS,\ + EXPORT_TRI = EXPORT_TRI,\ + ) + + +def x3d_export_ui(filename): + if not filename.endswith(extension): + filename += extension + #if _safeOverwrite and sys.exists(filename): + # result = Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0") + #if(result != 1): + # return + + # Get user options + EXPORT_APPLY_MODIFIERS = Draw.Create(1) + EXPORT_TRI = Draw.Create(0) + EXPORT_GZIP = Draw.Create( filename.lower().endswith('.x3dz') ) + + # Get USER Options + pup_block = [\ + ('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object.'),\ + ('Triangulate', EXPORT_TRI, 'Triangulate quads.'),\ + ('Compress', EXPORT_GZIP, 'GZip the resulting file, requires a full python install'),\ + ] + + if not Draw.PupBlock('Export...', pup_block): + return + + Blender.Window.EditMode(0) + Blender.Window.WaitCursor(1) + + x3d_export(filename,\ + EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val,\ + EXPORT_TRI = EXPORT_TRI.val,\ + EXPORT_GZIP = EXPORT_GZIP.val\ + ) + + Blender.Window.WaitCursor(0) + + + +######################################################### +# main routine +######################################################### + + +if __name__ == '__main__': + Blender.Window.FileSelector(x3d_export_ui,"Export X3D", Blender.Get('filename').replace('.blend', '.x3d')) + + diff --git a/release/io/import_3ds.py b/release/io/import_3ds.py new file mode 100644 index 00000000000..bcde82c4869 --- /dev/null +++ b/release/io/import_3ds.py @@ -0,0 +1,1007 @@ +#!BPY +""" +Name: '3D Studio (.3ds)...' +Blender: 244 +Group: 'Import' +Tooltip: 'Import from 3DS file format (.3ds)' +""" + +__author__= ['Bob Holcomb', 'Richard L?rk?ng', 'Damien McGinnes', 'Campbell Barton', 'Mario Lapin'] +__url__ = ("blenderartists.org", "www.blender.org", "www.gametutorials.com", "lib3ds.sourceforge.net/") +__version__= '0.996' +__bpydoc__= '''\ + +3ds Importer + +This script imports a 3ds file and the materials into Blender for editing. + +Loader is based on 3ds loader from www.gametutorials.com (Thanks DigiBen). + +0.996 by Mario Lapin (mario.lapin@gmail.com) 13/04/200
+ - Implemented workaround to correct association between name, geometry and materials of + imported meshes. + + Without this patch, version 0.995 of this importer would associate to each mesh object the + geometry and the materials of the previously parsed mesh object. By so, the name of the + first mesh object would be thrown away, and the name of the last mesh object would be + automatically merged with a '.001' at the end. No object would desappear, however object's + names and materials would be completely jumbled. + +0.995 by Campbell Barton
+- workaround for buggy mesh vert delete +- minor tweaks + +0.99 by Bob Holcomb
+- added support for floating point color values that previously broke on import. + +0.98 by Campbell Barton
+- import faces and verts to lists instead of a mesh, convert to a mesh later +- use new index mapping feature of mesh to re-map faces that were not added. + +0.97 by Campbell Barton
+- Strip material names of spaces +- Added import as instance to import the 3ds into its own + scene and add a group instance to the current scene +- New option to scale down imported objects so they are within a limited bounding area. + +0.96 by Campbell Barton
+- Added workaround for bug in setting UV's for Zero vert index UV faces. +- Removed unique name function, let blender make the names unique. + +0.95 by Campbell Barton
+- Removed workarounds for Blender 2.41 +- Mesh objects split by material- many 3ds objects used more then 16 per mesh. +- Removed a lot of unneeded variable creation. + +0.94 by Campbell Barton
+- Face import tested to be about overall 16x speedup over 0.93. +- Material importing speedup. +- Tested with more models. +- Support some corrupt models. + +0.93 by Campbell Barton
+- Tested with 400 3ds files from turbosquid and samples. +- Tactfully ignore faces that used the same verts twice. +- Rollback to 0.83 sloppy un-reorganized code, this broke UV coord loading. +- Converted from NMesh to Mesh. +- Faster and cleaner new names. +- Use external comprehensive image loader. +- Re intergrated 0.92 and 0.9 changes +- Fixes for 2.41 compat. +- Non textured faces do not use a texture flag. + +0.92
+- Added support for diffuse, alpha, spec, bump maps in a single material + +0.9
+- Reorganized code into object/material block functions
+- Use of Matrix() to copy matrix data
+- added support for material transparency
+ +0.83 2005-08-07: Campell Barton +- Aggressive image finding and case insensitivy for posisx systems. + +0.82a 2005-07-22 +- image texture loading (both for face uv and renderer) + +0.82 - image texture loading (for face uv) + +0.81a (fork- not 0.9) Campbell Barton 2005-06-08 +- Simplified import code +- Never overwrite data +- Faster list handling +- Leaves import selected + +0.81 Damien McGinnes 2005-01-09 +- handle missing images better + +0.8 Damien McGinnes 2005-01-08 +- copies sticky UV coords to face ones +- handles images better +- Recommend that you run 'RemoveDoubles' on each imported mesh after using this script + +''' + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Script copyright (C) Bob Holcomb +# +# 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. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + +# Importing modules + +import Blender +import bpy +from Blender import Mesh, Object, Material, Image, Texture, Lamp, Mathutils +from Blender.Mathutils import Vector +import BPyImage + +import BPyMessages + +try: + from struct import calcsize, unpack +except: + calcsize= unpack= None + + + +# If python version is less than 2.4, try to get set stuff from module +try: + set +except: + from sets import Set as set + +BOUNDS_3DS= [] + + +#this script imports uvcoords as sticky vertex coords +#this parameter enables copying these to face uv coords +#which shold be more useful. + +def createBlenderTexture(material, name, image): + texture= bpy.data.textures.new(name) + texture.setType('Image') + texture.image= image + material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL) + + + +###################################################### +# Data Structures +###################################################### + +#Some of the chunks that we will see +#----- Primary Chunk, at the beginning of each file +PRIMARY= long('0x4D4D',16) + +#------ Main Chunks +OBJECTINFO = long('0x3D3D',16); #This gives the version of the mesh and is found right before the material and object information +VERSION = long('0x0002',16); #This gives the version of the .3ds file +EDITKEYFRAME= long('0xB000',16); #This is the header for all of the key frame info + +#------ sub defines of OBJECTINFO +MATERIAL=45055 #0xAFFF // This stored the texture info +OBJECT=16384 #0x4000 // This stores the faces, vertices, etc... + +#>------ sub defines of MATERIAL +#------ sub defines of MATERIAL_BLOCK +MAT_NAME = long('0xA000',16) # This holds the material name +MAT_AMBIENT = long('0xA010',16) # Ambient color of the object/material +MAT_DIFFUSE = long('0xA020',16) # This holds the color of the object/material +MAT_SPECULAR = long('0xA030',16) # SPecular color of the object/material +MAT_SHINESS = long('0xA040',16) # ?? +MAT_TRANSPARENCY= long('0xA050',16) # Transparency value of material +MAT_SELF_ILLUM = long('0xA080',16) # Self Illumination value of material +MAT_WIRE = long('0xA085',16) # Only render's wireframe + +MAT_TEXTURE_MAP = long('0xA200',16) # This is a header for a new texture map +MAT_SPECULAR_MAP= long('0xA204',16) # This is a header for a new specular map +MAT_OPACITY_MAP = long('0xA210',16) # This is a header for a new opacity map +MAT_REFLECTION_MAP= long('0xA220',16) # This is a header for a new reflection map +MAT_BUMP_MAP = long('0xA230',16) # This is a header for a new bump map +MAT_MAP_FILENAME = long('0xA300',16) # This holds the file name of the texture + +MAT_FLOAT_COLOR = long ('0x0010', 16) #color defined as 3 floats +MAT_24BIT_COLOR = long ('0x0011', 16) #color defined as 3 bytes + +#>------ sub defines of OBJECT +OBJECT_MESH = long('0x4100',16); # This lets us know that we are reading a new object +OBJECT_LAMP = long('0x4600',16); # This lets un know we are reading a light object +OBJECT_LAMP_SPOT = long('0x4610',16); # The light is a spotloght. +OBJECT_LAMP_OFF = long('0x4620',16); # The light off. +OBJECT_LAMP_ATTENUATE = long('0x4625',16); +OBJECT_LAMP_RAYSHADE = long('0x4627',16); +OBJECT_LAMP_SHADOWED = long('0x4630',16); +OBJECT_LAMP_LOCAL_SHADOW = long('0x4640',16); +OBJECT_LAMP_LOCAL_SHADOW2 = long('0x4641',16); +OBJECT_LAMP_SEE_CONE = long('0x4650',16); +OBJECT_LAMP_SPOT_RECTANGULAR= long('0x4651',16); +OBJECT_LAMP_SPOT_OVERSHOOT= long('0x4652',16); +OBJECT_LAMP_SPOT_PROJECTOR= long('0x4653',16); +OBJECT_LAMP_EXCLUDE= long('0x4654',16); +OBJECT_LAMP_RANGE= long('0x4655',16); +OBJECT_LAMP_ROLL= long('0x4656',16); +OBJECT_LAMP_SPOT_ASPECT= long('0x4657',16); +OBJECT_LAMP_RAY_BIAS= long('0x4658',16); +OBJECT_LAMP_INNER_RANGE= long('0x4659',16); +OBJECT_LAMP_OUTER_RANGE= long('0x465A',16); +OBJECT_LAMP_MULTIPLIER = long('0x465B',16); +OBJECT_LAMP_AMBIENT_LIGHT = long('0x4680',16); + + + +OBJECT_CAMERA= long('0x4700',16); # This lets un know we are reading a camera object + +#>------ sub defines of CAMERA +OBJECT_CAM_RANGES= long('0x4720',16); # The camera range values + +#>------ sub defines of OBJECT_MESH +OBJECT_VERTICES = long('0x4110',16); # The objects vertices +OBJECT_FACES = long('0x4120',16); # The objects faces +OBJECT_MATERIAL = long('0x4130',16); # This is found if the object has a material, either texture map or color +OBJECT_UV = long('0x4140',16); # The UV texture coordinates +OBJECT_TRANS_MATRIX = long('0x4160',16); # The Object Matrix + +global scn +scn= None + +#the chunk class +class chunk: + ID=0 + length=0 + bytes_read=0 + + #we don't read in the bytes_read, we compute that + binary_format='3): + print '\tNon-Fatal Error: Version greater than 3, may not load correctly: ', version + + #is it an object info chunk? + elif (new_chunk.ID==OBJECTINFO): + #print 'elif (new_chunk.ID==OBJECTINFO):' + # print 'found an OBJECTINFO chunk' + process_next_chunk(file, new_chunk, importedObjects, IMAGE_SEARCH) + + #keep track of how much we read in the main chunk + new_chunk.bytes_read+=temp_chunk.bytes_read + + #is it an object chunk? + elif (new_chunk.ID==OBJECT): + + if CreateBlenderObject: + putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials) + contextMesh_vertls= []; contextMesh_facels= [] + + ## preparando para receber o proximo objeto + contextMeshMaterials= {} # matname:[face_idxs] + contextMeshUV= None + #contextMesh.vertexUV= 1 # Make sticky coords. + # Reset matrix + contextMatrix_rot= None + #contextMatrix_tx= None + + CreateBlenderObject= True + tempName= read_string(file) + contextObName= tempName + new_chunk.bytes_read += len(tempName)+1 + + #is it a material chunk? + elif (new_chunk.ID==MATERIAL): + #print 'elif (new_chunk.ID==MATERIAL):' + contextMaterial= bpy.data.materials.new('Material') + + elif (new_chunk.ID==MAT_NAME): + #print 'elif (new_chunk.ID==MAT_NAME):' + material_name= read_string(file) + + #plus one for the null character that ended the string + new_chunk.bytes_read+= len(material_name)+1 + + contextMaterial.name= material_name.rstrip() # remove trailing whitespace + MATDICT[material_name]= (contextMaterial.name, contextMaterial) + + elif (new_chunk.ID==MAT_AMBIENT): + #print 'elif (new_chunk.ID==MAT_AMBIENT):' + read_chunk(file, temp_chunk) + if (temp_chunk.ID==MAT_FLOAT_COLOR): + temp_data=file.read(calcsize('3f')) + temp_chunk.bytes_read+=12 + contextMaterial.mirCol=[float(col) for col in unpack('<3f', temp_data)] + elif (temp_chunk.ID==MAT_24BIT_COLOR): + temp_data=file.read(calcsize('3B')) + temp_chunk.bytes_read+= 3 + contextMaterial.mirCol= [float(col)/255 for col in unpack('<3B', temp_data)] # data [0,1,2] == rgb + else: + skip_to_end(file, temp_chunk) + new_chunk.bytes_read+= temp_chunk.bytes_read + + elif (new_chunk.ID==MAT_DIFFUSE): + #print 'elif (new_chunk.ID==MAT_DIFFUSE):' + read_chunk(file, temp_chunk) + if (temp_chunk.ID==MAT_FLOAT_COLOR): + temp_data=file.read(calcsize('3f')) + temp_chunk.bytes_read+=12 + contextMaterial.rgbCol=[float(col) for col in unpack('<3f', temp_data)] + elif (temp_chunk.ID==MAT_24BIT_COLOR): + temp_data=file.read(calcsize('3B')) + temp_chunk.bytes_read+= 3 + contextMaterial.rgbCol= [float(col)/255 for col in unpack('<3B', temp_data)] # data [0,1,2] == rgb + else: + skip_to_end(file, temp_chunk) + new_chunk.bytes_read+= temp_chunk.bytes_read + + elif (new_chunk.ID==MAT_SPECULAR): + #print 'elif (new_chunk.ID==MAT_SPECULAR):' + read_chunk(file, temp_chunk) + if (temp_chunk.ID==MAT_FLOAT_COLOR): + temp_data=file.read(calcsize('3f')) + temp_chunk.bytes_read+=12 + contextMaterial.mirCol=[float(col) for col in unpack('<3f', temp_data)] + elif (temp_chunk.ID==MAT_24BIT_COLOR): + temp_data=file.read(calcsize('3B')) + temp_chunk.bytes_read+= 3 + contextMaterial.mirCol= [float(col)/255 for col in unpack('<3B', temp_data)] # data [0,1,2] == rgb + else: + skip_to_end(file, temp_chunk) + new_chunk.bytes_read+= temp_chunk.bytes_read + + elif (new_chunk.ID==MAT_TEXTURE_MAP): + #print 'elif (new_chunk.ID==MAT_TEXTURE_MAP):' + new_texture= bpy.data.textures.new('Diffuse') + new_texture.setType('Image') + img = None + while (new_chunk.bytes_read BOUNDS_3DS[i+3]: + BOUNDS_3DS[i+3]= v[i] # min + + # Get the max axis x/y/z + max_axis= max(BOUNDS_3DS[3]-BOUNDS_3DS[0], BOUNDS_3DS[4]-BOUNDS_3DS[1], BOUNDS_3DS[5]-BOUNDS_3DS[2]) + # print max_axis + if max_axis < 1<<30: # Should never be false but just make sure. + + # Get a new scale factor if set as an option + SCALE=1.0 + while (max_axis*SCALE) > IMPORT_CONSTRAIN_BOUNDS: + SCALE/=10 + + # SCALE Matrix + SCALE_MAT= Blender.Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1]) + + for ob in importedObjects: + ob.setMatrix(ob.matrixWorld*SCALE_MAT) + + # Done constraining to bounds. + + # Select all new objects. + print 'finished importing: "%s" in %.4f sec.' % (filename, (Blender.sys.time()-time1)) + file.close() + Blender.Window.WaitCursor(0) + + +DEBUG= False +if __name__=='__main__' and not DEBUG: + if calcsize==None: + Blender.Draw.PupMenu('Error%t|a full python installation not found') + else: + Blender.Window.FileSelector(load_3ds, 'Import 3DS', '*.3ds') + +# For testing compatibility +#load_3ds('/metavr/convert/vehicle/truck_002/TruckTanker1.3DS', False) +#load_3ds('/metavr/archive/convert/old/arranged_3ds_to_hpx-2/only-need-engine-trains/Engine2.3DS', False) +''' + +else: + import os + # DEBUG ONLY + TIME= Blender.sys.time() + import os + print 'Searching for files' + os.system('find /metavr/ -iname "*.3ds" > /tmp/temp3ds_list') + # os.system('find /storage/ -iname "*.3ds" > /tmp/temp3ds_list') + print '...Done' + file= open('/tmp/temp3ds_list', 'r') + lines= file.readlines() + file.close() + # sort by filesize for faster testing + lines_size = [(os.path.getsize(f[:-1]), f[:-1]) for f in lines] + lines_size.sort() + lines = [f[1] for f in lines_size] + + + def between(v,a,b): + if v <= max(a,b) and v >= min(a,b): + return True + return False + + for i, _3ds in enumerate(lines): + if between(i, 650,800): + #_3ds= _3ds[:-1] + print 'Importing', _3ds, '\nNUMBER', i, 'of', len(lines) + _3ds_file= _3ds.split('/')[-1].split('\\')[-1] + newScn= Blender.Scene.New(_3ds_file) + newScn.makeCurrent() + load_3ds(_3ds, False) + + print 'TOTAL TIME: %.6f' % (Blender.sys.time() - TIME) + +''' From 675936b42cc9aa3ea15c1cf4af06790e5b41ecae Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Mon, 10 Aug 2009 13:49:55 +0000 Subject: [PATCH 63/68] - ran 2to3 on export_3ds.py, import_3ds.py and export_x3d.py - added operators and menu items for ^ --- release/io/export_3ds.py | 164 +++++++++++++++++++------------- release/io/export_obj.py | 2 +- release/io/export_x3d.py | 114 ++++++++++++++-------- release/io/import_3ds.py | 200 +++++++++++++++++++++++---------------- release/ui/space_info.py | 3 + 5 files changed, 291 insertions(+), 192 deletions(-) diff --git a/release/io/export_3ds.py b/release/io/export_3ds.py index 69b4d00b4d8..c4a73a44b0c 100644 --- a/release/io/export_3ds.py +++ b/release/io/export_3ds.py @@ -46,14 +46,17 @@ from the lib3ds project (http://lib3ds.sourceforge.net/) sourcecode. # Importing modules ###################################################### -import Blender +import struct + import bpy -from BPyMesh import getMeshFromObject -from BPyObject import getDerivedObjects -try: - import struct -except: - struct = None + +# import Blender +# from BPyMesh import getMeshFromObject +# from BPyObject import getDerivedObjects +# try: +# import struct +# except: +# struct = None # So 3ds max can open files, limit names to 12 in length # this is verry annoying for filenames! @@ -85,58 +88,58 @@ def sane_name(name): #Some of the chunks that we will export #----- Primary Chunk, at the beginning of each file -PRIMARY= long("0x4D4D",16) +PRIMARY= int("0x4D4D",16) #------ Main Chunks -OBJECTINFO = long("0x3D3D",16); #This gives the version of the mesh and is found right before the material and object information -VERSION = long("0x0002",16); #This gives the version of the .3ds file -KFDATA = long("0xB000",16); #This is the header for all of the key frame info +OBJECTINFO = int("0x3D3D",16); #This gives the version of the mesh and is found right before the material and object information +VERSION = int("0x0002",16); #This gives the version of the .3ds file +KFDATA = int("0xB000",16); #This is the header for all of the key frame info #------ sub defines of OBJECTINFO MATERIAL=45055 #0xAFFF // This stored the texture info OBJECT=16384 #0x4000 // This stores the faces, vertices, etc... #>------ sub defines of MATERIAL -MATNAME = long("0xA000",16); # This holds the material name -MATAMBIENT = long("0xA010",16); # Ambient color of the object/material -MATDIFFUSE = long("0xA020",16); # This holds the color of the object/material -MATSPECULAR = long("0xA030",16); # SPecular color of the object/material -MATSHINESS = long("0xA040",16); # ?? -MATMAP = long("0xA200",16); # This is a header for a new material -MATMAPFILE = long("0xA300",16); # This holds the file name of the texture +MATNAME = int("0xA000",16); # This holds the material name +MATAMBIENT = int("0xA010",16); # Ambient color of the object/material +MATDIFFUSE = int("0xA020",16); # This holds the color of the object/material +MATSPECULAR = int("0xA030",16); # SPecular color of the object/material +MATSHINESS = int("0xA040",16); # ?? +MATMAP = int("0xA200",16); # This is a header for a new material +MATMAPFILE = int("0xA300",16); # This holds the file name of the texture -RGB1= long("0x0011",16) -RGB2= long("0x0012",16) +RGB1= int("0x0011",16) +RGB2= int("0x0012",16) #>------ sub defines of OBJECT -OBJECT_MESH = long("0x4100",16); # This lets us know that we are reading a new object -OBJECT_LIGHT = long("0x4600",16); # This lets un know we are reading a light object -OBJECT_CAMERA= long("0x4700",16); # This lets un know we are reading a camera object +OBJECT_MESH = int("0x4100",16); # This lets us know that we are reading a new object +OBJECT_LIGHT = int("0x4600",16); # This lets un know we are reading a light object +OBJECT_CAMERA= int("0x4700",16); # This lets un know we are reading a camera object #>------ sub defines of CAMERA -OBJECT_CAM_RANGES= long("0x4720",16); # The camera range values +OBJECT_CAM_RANGES= int("0x4720",16); # The camera range values #>------ sub defines of OBJECT_MESH -OBJECT_VERTICES = long("0x4110",16); # The objects vertices -OBJECT_FACES = long("0x4120",16); # The objects faces -OBJECT_MATERIAL = long("0x4130",16); # This is found if the object has a material, either texture map or color -OBJECT_UV = long("0x4140",16); # The UV texture coordinates -OBJECT_TRANS_MATRIX = long("0x4160",16); # The Object Matrix +OBJECT_VERTICES = int("0x4110",16); # The objects vertices +OBJECT_FACES = int("0x4120",16); # The objects faces +OBJECT_MATERIAL = int("0x4130",16); # This is found if the object has a material, either texture map or color +OBJECT_UV = int("0x4140",16); # The UV texture coordinates +OBJECT_TRANS_MATRIX = int("0x4160",16); # The Object Matrix #>------ sub defines of KFDATA -KFDATA_KFHDR = long("0xB00A",16); -KFDATA_KFSEG = long("0xB008",16); -KFDATA_KFCURTIME = long("0xB009",16); -KFDATA_OBJECT_NODE_TAG = long("0xB002",16); +KFDATA_KFHDR = int("0xB00A",16); +KFDATA_KFSEG = int("0xB008",16); +KFDATA_KFCURTIME = int("0xB009",16); +KFDATA_OBJECT_NODE_TAG = int("0xB002",16); #>------ sub defines of OBJECT_NODE_TAG -OBJECT_NODE_ID = long("0xB030",16); -OBJECT_NODE_HDR = long("0xB010",16); -OBJECT_PIVOT = long("0xB013",16); -OBJECT_INSTANCE_NAME = long("0xB011",16); -POS_TRACK_TAG = long("0xB020",16); -ROT_TRACK_TAG = long("0xB021",16); -SCL_TRACK_TAG = long("0xB022",16); +OBJECT_NODE_ID = int("0xB030",16); +OBJECT_NODE_HDR = int("0xB010",16); +OBJECT_PIVOT = int("0xB013",16); +OBJECT_INSTANCE_NAME = int("0xB011",16); +POS_TRACK_TAG = int("0xB020",16); +ROT_TRACK_TAG = int("0xB021",16); +SCL_TRACK_TAG = int("0xB022",16); def uv_key(uv): return round(uv.x, 6), round(uv.y, 6) @@ -343,12 +346,12 @@ class _3ds_named_variable(object): def dump(self,indent): if (self.value!=None): spaces="" - for i in xrange(indent): + for i in range(indent): spaces+=" "; if (self.name!=""): - print spaces, self.name, " = ", self.value + print(spaces, self.name, " = ", self.value) else: - print spaces, "[unnamed]", " = ", self.value + print(spaces, "[unnamed]", " = ", self.value) #the chunk class @@ -408,9 +411,9 @@ class _3ds_chunk(object): Dump is used for debugging purposes, to dump the contents of a chunk to the standard output. Uses the dump function of the named variables and the subchunks to do the actual work.''' spaces="" - for i in xrange(indent): + for i in range(indent): spaces+=" "; - print spaces, "ID=", hex(self.ID.value), "size=", self.get_size() + print(spaces, "ID=", hex(self.ID.value), "size=", self.get_size()) for variable in self.variables: variable.dump(indent+1) for subchunk in self.subchunks: @@ -555,11 +558,11 @@ def remove_face_uv(verts, tri_list): # initialize a list of UniqueLists, one per vertex: #uv_list = [UniqueList() for i in xrange(len(verts))] - unique_uvs= [{} for i in xrange(len(verts))] + unique_uvs= [{} for i in range(len(verts))] # for each face uv coordinate, add it to the UniqueList of the vertex for tri in tri_list: - for i in xrange(3): + for i in range(3): # store the index into the UniqueList for future reference: # offset.append(uv_list[tri.vertex_index[i]].add(_3ds_point_uv(tri.faceuvs[i]))) @@ -589,7 +592,7 @@ def remove_face_uv(verts, tri_list): pt = _3ds_point_3d(vert.co) # reuse, should be ok uvmap = [None] * len(unique_uvs[i]) - for ii, uv_3ds in unique_uvs[i].itervalues(): + for ii, uv_3ds in unique_uvs[i].values(): # add a vertex duplicate to the vertex_array for every uv associated with this vertex: vert_array.add(pt) # add the uv coordinate to the uv array: @@ -607,7 +610,7 @@ def remove_face_uv(verts, tri_list): # Make sure the triangle vertex indices now refer to the new vertex list: for tri in tri_list: - for i in xrange(3): + for i in range(3): tri.offset[i]+=index_list[tri.vertex_index[i]] tri.vertex_index= tri.offset @@ -655,7 +658,7 @@ def make_faces_chunk(tri_list, mesh, materialDict): # obj_material_faces[tri.mat].add(_3ds_short(i)) face_chunk.add_variable("faces", face_list) - for mat_name, mat_faces in unique_mats.itervalues(): + for mat_name, mat_faces in unique_mats.values(): obj_material_chunk=_3ds_chunk(OBJECT_MATERIAL) obj_material_chunk.add_variable("name", mat_name) obj_material_chunk.add_variable("face_list", mat_faces) @@ -677,7 +680,7 @@ def make_faces_chunk(tri_list, mesh, materialDict): obj_material_faces[tri.mat].add(_3ds_short(i)) face_chunk.add_variable("faces", face_list) - for i in xrange(n_materials): + for i in range(n_materials): obj_material_chunk=_3ds_chunk(OBJECT_MATERIAL) obj_material_chunk.add_variable("name", obj_material_names[i]) obj_material_chunk.add_variable("face_list", obj_material_faces[i]) @@ -862,7 +865,7 @@ def make_kf_obj_node(obj, name_to_id): return kf_obj_node """ -import BPyMessages +# import BPyMessages def save_3ds(filename, context): '''Save the Blender scene to a 3ds file.''' # Time the export @@ -871,16 +874,16 @@ def save_3ds(filename, context): filename += '.3ds' # XXX -# if not BPyMessages.Warning_SaveOver(filename): -# return +# if not BPyMessages.Warning_SaveOver(filename): +# return # XXX - time1 = bpy.sys.time() -# time1= Blender.sys.time() -# Blender.Window.WaitCursor(1) + time1 = bpy.sys.time() +# time1= Blender.sys.time() +# Blender.Window.WaitCursor(1) sce = context.scene -# sce= bpy.data.scenes.active +# sce= bpy.data.scenes.active # Initialize the main chunk (primary): primary = _3ds_chunk(PRIMARY) @@ -948,7 +951,7 @@ def save_3ds(filename, context): f.mat = 0 # Make material chunks for all materials used in the meshes: - for mat_and_image in materialDict.itervalues(): + for mat_and_image in materialDict.values(): object_info.add_subchunk(make_material_chunk(mat_and_image[0], mat_and_image[1])) # Give all objects a unique ID and build a dictionary from object name to object id: @@ -1011,15 +1014,44 @@ def save_3ds(filename, context): # Debugging only: report the exporting time: Blender.Window.WaitCursor(0) - print "3ds export time: %.2f" % (Blender.sys.time() - time1) + print("3ds export time: %.2f" % (Blender.sys.time() - time1)) # Debugging only: dump the chunk hierarchy: #primary.dump() -if __name__=='__main__': - if struct: - Blender.Window.FileSelector(save_3ds, "Export 3DS", Blender.sys.makename(ext='.3ds')) - else: - Blender.Draw.PupMenu("Error%t|This script requires a full python installation") -# save_3ds('/test_b.3ds') +# if __name__=='__main__': +# if struct: +# Blender.Window.FileSelector(save_3ds, "Export 3DS", Blender.sys.makename(ext='.3ds')) +# else: +# Blender.Draw.PupMenu("Error%t|This script requires a full python installation") +# # save_3ds('/test_b.3ds') + +class EXPORT_OT_3ds(bpy.types.Operator): + ''' + 3DS Exporter + ''' + __idname__ = "export.3ds" + __label__ = 'Export 3DS' + + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + + __props__ = [ + bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the 3DS file", maxlen= 1024, default= ""), + ] + + def execute(self, context): + raise Exception("Not doing anything yet.") + return ('FINISHED',) + + def invoke(self, context, event): + wm = context.manager + wm.add_fileselect(self.__operator__) + return ('RUNNING_MODAL',) + + def poll(self, context): # Poll isnt working yet + print("Poll") + return context.active_object != None + +bpy.ops.add(EXPORT_OT_3ds) diff --git a/release/io/export_obj.py b/release/io/export_obj.py index fa07c5408a7..b2d4af7c517 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -913,7 +913,7 @@ class EXPORT_OT_obj(bpy.types.Operator): # to the class instance from the operator settings before calling. __props__ = [ - bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the PLY file", maxlen= 1024, default= ""), + bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the OBJ file", maxlen= 1024, default= ""), # context group bpy.props.BoolProperty(attr="use_selection", name="Selection Only", description="", default= False), diff --git a/release/io/export_x3d.py b/release/io/export_x3d.py index b12ff67d8a6..b57be2286e9 100644 --- a/release/io/export_x3d.py +++ b/release/io/export_x3d.py @@ -53,22 +53,27 @@ Known issues:
# Library dependancies #################################### -import Blender -from Blender import Object, Lamp, Draw, Image, Text, sys, Mesh -from Blender.Scene import Render import math -import BPyObject -import BPyMesh + +import bpy +import Mathutils + +# import Blender +# from Blender import Object, Lamp, Draw, Image, Text, sys, Mesh +# from Blender.Scene import Render +# import BPyObject +# import BPyMesh # DEG2RAD=0.017453292519943295 -MATWORLD= Blender.Mathutils.RotationMatrix(-90, 4, 'x') +MATWORLD= Mathutils.RotationMatrix(-90, 4, 'x') #################################### # Global Variables #################################### -filename = Blender.Get('filename') +filename = "" +# filename = Blender.Get('filename') _safeOverwrite = True extension = '' @@ -109,7 +114,7 @@ class x3d_class: import gzip self.file = gzip.open(filename, "w") except: - print "failed to import compression modules, exporting uncompressed" + print("failed to import compression modules, exporting uncompressed") self.filename = filename[:-1] # remove trailing z if self.file == None: @@ -383,7 +388,7 @@ class x3d_class: if nIFSCnt > 1: self.writeIndented("\n" % ("G_", meshName),1) - if sided.has_key('two') and sided['two'] > 0: + if 'two' in sided and sided['two'] > 0: bTwoSided=1 else: bTwoSided=0 @@ -414,8 +419,8 @@ class x3d_class: if not matFlags & Blender.Material.Modes['TEXFACE']: self.writeMaterial(mat, self.cleanStr(maters[0].name,''), world) if len(maters) > 1: - print "Warning: mesh named %s has multiple materials" % meshName - print "Warning: only one material per object handled" + print("Warning: mesh named %s has multiple materials" % meshName) + print("Warning: only one material per object handled") #-- textures if mesh.faceUV: @@ -433,7 +438,7 @@ class x3d_class: # user selected BOUNDS=1, SOLID=3, SHARED=4, or TEXTURE=5 ifStyle="IndexedFaceSet" # look up mesh name, use it if available - if self.meshNames.has_key(meshME): + if meshME in self.meshNames: self.writeIndented("<%s USE=\"ME_%s\">" % (ifStyle, meshME), 1) self.meshNames[meshME]+=1 else: @@ -547,7 +552,7 @@ class x3d_class: if self.writingtexture == 0: self.file.write("\n\t\t\ttexCoordIndex=\"") texIndxStr="" - for i in xrange(len(texIndexList)): + for i in range(len(texIndexList)): texIndxStr = texIndxStr + "%d, " % texIndexList[i] if texIndexList[i]==-1: self.file.write(texIndxStr) @@ -555,7 +560,7 @@ class x3d_class: self.file.write("\"\n\t\t\t") else: self.writeIndented("") self.writeIndented("\n", -1) @@ -569,7 +574,7 @@ class x3d_class: if face.col: c=face.col[0] if self.verbose > 2: - print "Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b) + print("Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b)) aColor = self.rgbToFS(c) self.file.write("%s, " % aColor) self.file.write("\" />") @@ -577,7 +582,7 @@ class x3d_class: def writeMaterial(self, mat, matName, world): # look up material name, use it if available - if self.matNames.has_key(matName): + if matName in self.matNames: self.writeIndented("\n" % matName) self.matNames[matName]+=1 return; @@ -617,7 +622,7 @@ class x3d_class: def writeImageTexture(self, image): name = image.name filename = image.filename.split('/')[-1].split('\\')[-1] - if self.texNames.has_key(name): + if name in self.texNames: self.writeIndented("\n" % self.cleanStr(name)) self.texNames[name] += 1 return @@ -671,7 +676,7 @@ class x3d_class: self.file.write("groundColor=\"%s %s %s\" " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) self.file.write("skyColor=\"%s %s %s\" " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) alltexture = len(alltextures) - for i in xrange(alltexture): + for i in range(alltexture): namemat = alltextures[i].name pic = alltextures[i].getImage() if (namemat == "back") and (pic != None): @@ -697,7 +702,7 @@ class x3d_class: EXPORT_TRI= False,\ ): - print "Info: starting X3D export to " + self.filename + "..." + print("Info: starting X3D export to " + self.filename + "...") self.writeHeader() # self.writeScript() self.writeNavigationInfo(scene) @@ -771,7 +776,7 @@ class x3d_class: self.texNames={} self.matNames={} self.indentLevel=0 - print "Info: finished X3D export to %s\n" % self.filename + print("Info: finished X3D export to %s\n" % self.filename) def cleanStr(self, name, prefix='rsvd_'): """cleanStr(name,prefix) - try to create a valid VRML DEF name from object name""" @@ -815,7 +820,7 @@ class x3d_class: else: sidename='one' - if sided.has_key(sidename): + if sidename in sided: sided[sidename]+=1 else: sided[sidename]=1 @@ -829,30 +834,30 @@ class x3d_class: imageMap[faceName]=[face.image.name,sidename,face] if self.verbose > 2: - for faceName in imageMap.iterkeys(): + for faceName in imageMap.keys(): ifs=imageMap[faceName] - print "Debug: faceName=%s image=%s, solid=%s facecnt=%d" % \ - (faceName, ifs[0], ifs[1], len(ifs)-2) + print("Debug: faceName=%s image=%s, solid=%s facecnt=%d" % \ + (faceName, ifs[0], ifs[1], len(ifs)-2)) return len(imageMap) def faceToString(self,face): - print "Debug: face.flag=0x%x (bitflags)" % face.flag + print("Debug: face.flag=0x%x (bitflags)" % face.flag) if face.sel: - print "Debug: face.sel=true" + print("Debug: face.sel=true") - print "Debug: face.mode=0x%x (bitflags)" % face.mode + print("Debug: face.mode=0x%x (bitflags)" % face.mode) if face.mode & Mesh.FaceModes.TWOSIDE: - print "Debug: face.mode twosided" + print("Debug: face.mode twosided") - print "Debug: face.transp=0x%x (enum)" % face.transp + print("Debug: face.transp=0x%x (enum)" % face.transp) if face.transp == Mesh.FaceTranspModes.SOLID: - print "Debug: face.transp.SOLID" + print("Debug: face.transp.SOLID") if face.image: - print "Debug: face.image=%s" % face.image.name - print "Debug: face.materialIndex=%d" % face.materialIndex + print("Debug: face.image=%s" % face.image.name) + print("Debug: face.materialIndex=%d" % face.materialIndex) def getVertexColorByIndx(self, mesh, indx): c = None @@ -867,12 +872,12 @@ class x3d_class: return c def meshToString(self,mesh): - print "Debug: mesh.hasVertexUV=%d" % mesh.vertexColors - print "Debug: mesh.faceUV=%d" % mesh.faceUV - print "Debug: mesh.hasVertexColours=%d" % mesh.hasVertexColours() - print "Debug: mesh.verts=%d" % len(mesh.verts) - print "Debug: mesh.faces=%d" % len(mesh.faces) - print "Debug: mesh.materials=%d" % len(mesh.materials) + print("Debug: mesh.hasVertexUV=%d" % mesh.vertexColors) + print("Debug: mesh.faceUV=%d" % mesh.faceUV) + print("Debug: mesh.hasVertexColours=%d" % mesh.hasVertexColours()) + print("Debug: mesh.verts=%d" % len(mesh.verts)) + print("Debug: mesh.faces=%d" % len(mesh.faces)) + print("Debug: mesh.materials=%d" % len(mesh.materials)) def rgbToFS(self, c): s="%s %s %s" % ( @@ -924,7 +929,7 @@ class x3d_class: self.indentLevel = self.indentLevel + inc spaces="" - for x in xrange(self.indentLevel): + for x in range(self.indentLevel): spaces = spaces + "\t" self.file.write(spaces + s) @@ -1045,7 +1050,34 @@ def x3d_export_ui(filename): ######################################################### -if __name__ == '__main__': - Blender.Window.FileSelector(x3d_export_ui,"Export X3D", Blender.Get('filename').replace('.blend', '.x3d')) +# if __name__ == '__main__': +# Blender.Window.FileSelector(x3d_export_ui,"Export X3D", Blender.Get('filename').replace('.blend', '.x3d')) +class EXPORT_OT_x3d(bpy.types.Operator): + ''' + X3D Exporter + ''' + __idname__ = "export.x3d" + __label__ = 'Export X3D' + + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + __props__ = [ + bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the X3D file", maxlen= 1024, default= ""), + ] + + def execute(self, context): + raise Exception("Not doing anything yet.") + return ('FINISHED',) + + def invoke(self, context, event): + wm = context.manager + wm.add_fileselect(self.__operator__) + return ('RUNNING_MODAL',) + + def poll(self, context): # Poll isnt working yet + print("Poll") + return context.active_object != None + +bpy.ops.add(EXPORT_OT_x3d) diff --git a/release/io/import_3ds.py b/release/io/import_3ds.py index bcde82c4869..a08fdb8fc85 100644 --- a/release/io/import_3ds.py +++ b/release/io/import_3ds.py @@ -125,26 +125,29 @@ Loader is based on 3ds loader from www.gametutorials.com (Thanks DigiBen). # Importing modules -import Blender +from struct import calcsize, unpack + import bpy -from Blender import Mesh, Object, Material, Image, Texture, Lamp, Mathutils -from Blender.Mathutils import Vector -import BPyImage -import BPyMessages +# import Blender +# from Blender import Mesh, Object, Material, Image, Texture, Lamp, Mathutils +# from Blender.Mathutils import Vector +# import BPyImage -try: - from struct import calcsize, unpack -except: - calcsize= unpack= None +# import BPyMessages + +# try: +# from struct import calcsize, unpack +# except: +# calcsize= unpack= None -# If python version is less than 2.4, try to get set stuff from module -try: - set -except: - from sets import Set as set +# # If python version is less than 2.4, try to get set stuff from module +# try: +# set +# except: +# from sets import Set as set BOUNDS_3DS= [] @@ -167,12 +170,12 @@ def createBlenderTexture(material, name, image): #Some of the chunks that we will see #----- Primary Chunk, at the beginning of each file -PRIMARY= long('0x4D4D',16) +PRIMARY= int('0x4D4D',16) #------ Main Chunks -OBJECTINFO = long('0x3D3D',16); #This gives the version of the mesh and is found right before the material and object information -VERSION = long('0x0002',16); #This gives the version of the .3ds file -EDITKEYFRAME= long('0xB000',16); #This is the header for all of the key frame info +OBJECTINFO = int('0x3D3D',16); #This gives the version of the mesh and is found right before the material and object information +VERSION = int('0x0002',16); #This gives the version of the .3ds file +EDITKEYFRAME= int('0xB000',16); #This is the header for all of the key frame info #------ sub defines of OBJECTINFO MATERIAL=45055 #0xAFFF // This stored the texture info @@ -180,62 +183,62 @@ OBJECT=16384 #0x4000 // This stores the faces, vertices, etc... #>------ sub defines of MATERIAL #------ sub defines of MATERIAL_BLOCK -MAT_NAME = long('0xA000',16) # This holds the material name -MAT_AMBIENT = long('0xA010',16) # Ambient color of the object/material -MAT_DIFFUSE = long('0xA020',16) # This holds the color of the object/material -MAT_SPECULAR = long('0xA030',16) # SPecular color of the object/material -MAT_SHINESS = long('0xA040',16) # ?? -MAT_TRANSPARENCY= long('0xA050',16) # Transparency value of material -MAT_SELF_ILLUM = long('0xA080',16) # Self Illumination value of material -MAT_WIRE = long('0xA085',16) # Only render's wireframe +MAT_NAME = int('0xA000',16) # This holds the material name +MAT_AMBIENT = int('0xA010',16) # Ambient color of the object/material +MAT_DIFFUSE = int('0xA020',16) # This holds the color of the object/material +MAT_SPECULAR = int('0xA030',16) # SPecular color of the object/material +MAT_SHINESS = int('0xA040',16) # ?? +MAT_TRANSPARENCY= int('0xA050',16) # Transparency value of material +MAT_SELF_ILLUM = int('0xA080',16) # Self Illumination value of material +MAT_WIRE = int('0xA085',16) # Only render's wireframe -MAT_TEXTURE_MAP = long('0xA200',16) # This is a header for a new texture map -MAT_SPECULAR_MAP= long('0xA204',16) # This is a header for a new specular map -MAT_OPACITY_MAP = long('0xA210',16) # This is a header for a new opacity map -MAT_REFLECTION_MAP= long('0xA220',16) # This is a header for a new reflection map -MAT_BUMP_MAP = long('0xA230',16) # This is a header for a new bump map -MAT_MAP_FILENAME = long('0xA300',16) # This holds the file name of the texture +MAT_TEXTURE_MAP = int('0xA200',16) # This is a header for a new texture map +MAT_SPECULAR_MAP= int('0xA204',16) # This is a header for a new specular map +MAT_OPACITY_MAP = int('0xA210',16) # This is a header for a new opacity map +MAT_REFLECTION_MAP= int('0xA220',16) # This is a header for a new reflection map +MAT_BUMP_MAP = int('0xA230',16) # This is a header for a new bump map +MAT_MAP_FILENAME = int('0xA300',16) # This holds the file name of the texture -MAT_FLOAT_COLOR = long ('0x0010', 16) #color defined as 3 floats -MAT_24BIT_COLOR = long ('0x0011', 16) #color defined as 3 bytes +MAT_FLOAT_COLOR = int ('0x0010', 16) #color defined as 3 floats +MAT_24BIT_COLOR = int ('0x0011', 16) #color defined as 3 bytes #>------ sub defines of OBJECT -OBJECT_MESH = long('0x4100',16); # This lets us know that we are reading a new object -OBJECT_LAMP = long('0x4600',16); # This lets un know we are reading a light object -OBJECT_LAMP_SPOT = long('0x4610',16); # The light is a spotloght. -OBJECT_LAMP_OFF = long('0x4620',16); # The light off. -OBJECT_LAMP_ATTENUATE = long('0x4625',16); -OBJECT_LAMP_RAYSHADE = long('0x4627',16); -OBJECT_LAMP_SHADOWED = long('0x4630',16); -OBJECT_LAMP_LOCAL_SHADOW = long('0x4640',16); -OBJECT_LAMP_LOCAL_SHADOW2 = long('0x4641',16); -OBJECT_LAMP_SEE_CONE = long('0x4650',16); -OBJECT_LAMP_SPOT_RECTANGULAR= long('0x4651',16); -OBJECT_LAMP_SPOT_OVERSHOOT= long('0x4652',16); -OBJECT_LAMP_SPOT_PROJECTOR= long('0x4653',16); -OBJECT_LAMP_EXCLUDE= long('0x4654',16); -OBJECT_LAMP_RANGE= long('0x4655',16); -OBJECT_LAMP_ROLL= long('0x4656',16); -OBJECT_LAMP_SPOT_ASPECT= long('0x4657',16); -OBJECT_LAMP_RAY_BIAS= long('0x4658',16); -OBJECT_LAMP_INNER_RANGE= long('0x4659',16); -OBJECT_LAMP_OUTER_RANGE= long('0x465A',16); -OBJECT_LAMP_MULTIPLIER = long('0x465B',16); -OBJECT_LAMP_AMBIENT_LIGHT = long('0x4680',16); +OBJECT_MESH = int('0x4100',16); # This lets us know that we are reading a new object +OBJECT_LAMP = int('0x4600',16); # This lets un know we are reading a light object +OBJECT_LAMP_SPOT = int('0x4610',16); # The light is a spotloght. +OBJECT_LAMP_OFF = int('0x4620',16); # The light off. +OBJECT_LAMP_ATTENUATE = int('0x4625',16); +OBJECT_LAMP_RAYSHADE = int('0x4627',16); +OBJECT_LAMP_SHADOWED = int('0x4630',16); +OBJECT_LAMP_LOCAL_SHADOW = int('0x4640',16); +OBJECT_LAMP_LOCAL_SHADOW2 = int('0x4641',16); +OBJECT_LAMP_SEE_CONE = int('0x4650',16); +OBJECT_LAMP_SPOT_RECTANGULAR= int('0x4651',16); +OBJECT_LAMP_SPOT_OVERSHOOT= int('0x4652',16); +OBJECT_LAMP_SPOT_PROJECTOR= int('0x4653',16); +OBJECT_LAMP_EXCLUDE= int('0x4654',16); +OBJECT_LAMP_RANGE= int('0x4655',16); +OBJECT_LAMP_ROLL= int('0x4656',16); +OBJECT_LAMP_SPOT_ASPECT= int('0x4657',16); +OBJECT_LAMP_RAY_BIAS= int('0x4658',16); +OBJECT_LAMP_INNER_RANGE= int('0x4659',16); +OBJECT_LAMP_OUTER_RANGE= int('0x465A',16); +OBJECT_LAMP_MULTIPLIER = int('0x465B',16); +OBJECT_LAMP_AMBIENT_LIGHT = int('0x4680',16); -OBJECT_CAMERA= long('0x4700',16); # This lets un know we are reading a camera object +OBJECT_CAMERA= int('0x4700',16); # This lets un know we are reading a camera object #>------ sub defines of CAMERA -OBJECT_CAM_RANGES= long('0x4720',16); # The camera range values +OBJECT_CAM_RANGES= int('0x4720',16); # The camera range values #>------ sub defines of OBJECT_MESH -OBJECT_VERTICES = long('0x4110',16); # The objects vertices -OBJECT_FACES = long('0x4120',16); # The objects faces -OBJECT_MATERIAL = long('0x4130',16); # This is found if the object has a material, either texture map or color -OBJECT_UV = long('0x4140',16); # The UV texture coordinates -OBJECT_TRANS_MATRIX = long('0x4160',16); # The Object Matrix +OBJECT_VERTICES = int('0x4110',16); # The objects vertices +OBJECT_FACES = int('0x4120',16); # The objects faces +OBJECT_MATERIAL = int('0x4130',16); # This is found if the object has a material, either texture map or color +OBJECT_UV = int('0x4140',16); # The UV texture coordinates +OBJECT_TRANS_MATRIX = int('0x4160',16); # The Object Matrix global scn scn= None @@ -255,10 +258,10 @@ class chunk: self.bytes_read=0 def dump(self): - print 'ID: ', self.ID - print 'ID in hex: ', hex(self.ID) - print 'length: ', self.length - print 'bytes_read: ', self.bytes_read + print('ID: ', self.ID) + print('ID in hex: ', hex(self.ID)) + print('length: ', self.length) + print('bytes_read: ', self.bytes_read) def read_chunk(file, chunk): temp_data=file.read(calcsize(chunk.binary_format)) @@ -309,13 +312,13 @@ def add_texture_to_material(image, texture, material, mapto): elif mapto=='BUMP': map=Texture.MapTo.NOR else: - print '/tError: Cannot map to "%s"\n\tassuming diffuse color. modify material "%s" later.' % (mapto, material.name) + print('/tError: Cannot map to "%s"\n\tassuming diffuse color. modify material "%s" later.' % (mapto, material.name)) map=Texture.MapTo.COL if image: texture.setImage(image) # double check its an image. free_tex_slots= [i for i, tex in enumerate( material.getTextures() ) if tex==None] if not free_tex_slots: - print '/tError: Cannot add "%s" map. 10 Texture slots alredy used.' % mapto + print('/tError: Cannot add "%s" map. 10 Texture slots alredy used.' % mapto) else: material.setTexture(free_tex_slots[0],texture,Texture.TexCo.UV,map) @@ -374,7 +377,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): myVertMapping = {} vertMappingIndex = 0 - vertsToUse = [i for i in xrange(len(myContextMesh_vertls)) if faceVertUsers[i]] + vertsToUse = [i for i in range(len(myContextMesh_vertls)) if faceVertUsers[i]] myVertMapping = dict( [ (ii, i) for i, ii in enumerate(vertsToUse) ] ) tempName= '%s_%s' % (contextObName, matName) # matName may be None. @@ -423,7 +426,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): importedObjects.append(ob) bmesh.calcNormals() - for matName, faces in myContextMeshMaterials.iteritems(): + for matName, faces in myContextMeshMaterials.items(): makeMeshMaterialCopy(matName, faces) if len(materialFaces)!=len(myContextMesh_facels): @@ -455,7 +458,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): new_chunk.bytes_read+= 4 #read the 4 bytes for the version number #this loader works with version 3 and below, but may not with 4 and above if (version>3): - print '\tNon-Fatal Error: Version greater than 3, may not load correctly: ', version + print('\tNon-Fatal Error: Version greater than 3, may not load correctly: ', version) #is it an object info chunk? elif (new_chunk.ID==OBJECTINFO): @@ -684,7 +687,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): return temp_data #contextMesh.verts.extend( [Vector(),] ) # DUMMYVERT! - remove when blenders internals are fixed. - contextMesh_vertls= [getvert() for i in xrange(num_verts)] + contextMesh_vertls= [getvert() for i in range(num_verts)] #print 'object verts: bytes read: ', new_chunk.bytes_read @@ -702,7 +705,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): v1,v2,v3,dummy= unpack('<4H', temp_data) return v1, v2, v3 - contextMesh_facels= [ getface() for i in xrange(num_faces) ] + contextMesh_facels= [ getface() for i in range(num_faces) ] elif (new_chunk.ID==OBJECT_MATERIAL): @@ -719,7 +722,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): new_chunk.bytes_read+= STRUCT_SIZE_UNSIGNED_SHORT return unpack(' Date: Wed, 12 Aug 2009 16:37:05 +0000 Subject: [PATCH 64/68] - converted 3ds exporter to 2.5 API - added Object.is_visible() RNA function to determine if an object is on the visible layer of active scene --- release/io/export_3ds.py | 151 ++++++++++++------ .../blender/makesrna/intern/rna_object_api.c | 12 ++ 2 files changed, 117 insertions(+), 46 deletions(-) diff --git a/release/io/export_3ds.py b/release/io/export_3ds.py index c4a73a44b0c..56268b2925a 100644 --- a/release/io/export_3ds.py +++ b/release/io/export_3ds.py @@ -47,6 +47,8 @@ from the lib3ds project (http://lib3ds.sourceforge.net/) sourcecode. ###################################################### import struct +import os +import time import bpy @@ -142,7 +144,8 @@ ROT_TRACK_TAG = int("0xB021",16); SCL_TRACK_TAG = int("0xB022",16); def uv_key(uv): - return round(uv.x, 6), round(uv.y, 6) + return round(uv[0], 6), round(uv[1], 6) +# return round(uv.x, 6), round(uv.y, 6) # size defines: SZ_SHORT = 2 @@ -275,7 +278,8 @@ class _3ds_rgb_color(object): return 3 def write(self,file): - file.write( struct.pack('<3c', chr(int(255*self.r)), chr(int(255*self.g)), chr(int(255*self.b)) ) ) + file.write( struct.pack('<3B', int(255*self.r), int(255*self.g), int(255*self.b) ) ) +# file.write( struct.pack('<3c', chr(int(255*self.r)), chr(int(255*self.g)), chr(int(255*self.b)) ) ) def __str__(self): return '{%f, %f, %f}' % (self.r, self.g, self.b) @@ -427,14 +431,19 @@ class _3ds_chunk(object): def get_material_images(material): # blender utility func. - images = [] if material: - for mtex in material.getTextures(): - if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: - image = mtex.tex.image - if image: - images.append(image) # maye want to include info like diffuse, spec here. - return images + return [s.texture.image for s in material.textures if s and s.texture.type == 'IMAGE' and s.texture.image] + + return [] +# images = [] +# if material: +# for mtex in material.getTextures(): +# if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: +# image = mtex.tex.image +# if image: +# images.append(image) # maye want to include info like diffuse, spec here. +# return images + def make_material_subchunk(id, color): '''Make a material subchunk. @@ -457,7 +466,8 @@ def make_material_texture_chunk(id, images): mat_sub = _3ds_chunk(id) def add_image(img): - filename = image.filename.split('\\')[-1].split('/')[-1] + filename = os.path.basename(image.filename) +# filename = image.filename.split('\\')[-1].split('/')[-1] mat_sub_file = _3ds_chunk(MATMAPFILE) mat_sub_file.add_variable("mapfile", _3ds_string(sane_name(filename))) mat_sub.add_subchunk(mat_sub_file) @@ -485,9 +495,12 @@ def make_material_chunk(material, image): material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, (1,1,1) )) else: - material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, [a*material.amb for a in material.rgbCol] )) - material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, material.rgbCol)) - material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specCol)) + material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, [a*material.ambient for a in material.diffuse_color] )) +# material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, [a*material.amb for a in material.rgbCol] )) + material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, material.diffuse_color)) +# material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, material.rgbCol)) + material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specular_color)) +# material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specCol)) images = get_material_images(material) # can be None if image: images.append(image) @@ -516,28 +529,38 @@ def extract_triangles(mesh): If the mesh contains quads, they will be split into triangles.''' tri_list = [] - do_uv = mesh.faceUV + do_uv = len(mesh.uv_textures) +# do_uv = mesh.faceUV - if not do_uv: - face_uv = None +# if not do_uv: +# face_uv = None img = None - for face in mesh.faces: - f_v = face.v + for i, face in enumerate(mesh.faces): + f_v = face.verts +# f_v = face.v + + uf = mesh.active_uv_texture.data[i] if do_uv else None if do_uv: - f_uv = face.uv - img = face.image + f_uv = (uf.uv1, uf.uv2, uf.uv3, uf.uv4) if face.verts[3] else (uf.uv1, uf.uv2, uf.uv3) +# f_uv = face.uv + img = uf.image if uf else None +# img = face.image if img: img = img.name - - if len(f_v)==3: - new_tri = tri_wrapper((f_v[0].index, f_v[1].index, f_v[2].index), face.mat, img) + + if f_v[3] == 0: + # if len(f_v)==3: + new_tri = tri_wrapper((f_v[0], f_v[1], f_v[2]), face.material_index, img) +# new_tri = tri_wrapper((f_v[0].index, f_v[1].index, f_v[2].index), face.mat, img) if (do_uv): new_tri.faceuvs= uv_key(f_uv[0]), uv_key(f_uv[1]), uv_key(f_uv[2]) tri_list.append(new_tri) else: #it's a quad - new_tri = tri_wrapper((f_v[0].index, f_v[1].index, f_v[2].index), face.mat, img) - new_tri_2 = tri_wrapper((f_v[0].index, f_v[2].index, f_v[3].index), face.mat, img) + new_tri = tri_wrapper((f_v[0], f_v[1], f_v[2]), face.material_index, img) +# new_tri = tri_wrapper((f_v[0].index, f_v[1].index, f_v[2].index), face.mat, img) + new_tri_2 = tri_wrapper((f_v[0], f_v[2], f_v[3]), face.material_index, img) +# new_tri_2 = tri_wrapper((f_v[0].index, f_v[2].index, f_v[3].index), face.mat, img) if (do_uv): new_tri.faceuvs= uv_key(f_uv[0]), uv_key(f_uv[1]), uv_key(f_uv[2]) @@ -629,7 +652,8 @@ def make_faces_chunk(tri_list, mesh, materialDict): face_list = _3ds_array() - if mesh.faceUV: + if len(mesh.uv_textures): +# if mesh.faceUV: # Gather materials used in this mesh - mat/image pairs unique_mats = {} for i,tri in enumerate(tri_list): @@ -706,7 +730,8 @@ def make_mesh_chunk(mesh, materialDict): # Extract the triangles from the mesh: tri_list = extract_triangles(mesh) - if mesh.faceUV: + if len(mesh.uv_textures): +# if mesh.faceUV: # Remove the face UVs and convert it to vertex UV: vert_array, uv_array, tri_list = remove_face_uv(mesh.verts, tri_list) else: @@ -715,10 +740,13 @@ def make_mesh_chunk(mesh, materialDict): for vert in mesh.verts: vert_array.add(_3ds_point_3d(vert.co)) # If the mesh has vertex UVs, create an array of UVs: - if mesh.vertexUV: + if len(mesh.sticky): +# if mesh.vertexUV: uv_array = _3ds_array() - for vert in mesh.verts: - uv_array.add(_3ds_point_uv(vert.uvco)) + for uv in mesh.sticky: +# for vert in mesh.verts: + uv_array.add(_3ds_point_uv(uv.co)) +# uv_array.add(_3ds_point_uv(vert.uvco)) else: # no UV at all: uv_array = None @@ -878,7 +906,7 @@ def save_3ds(filename, context): # return # XXX - time1 = bpy.sys.time() + time1 = time.clock() # time1= Blender.sys.time() # Blender.Window.WaitCursor(1) @@ -909,31 +937,56 @@ def save_3ds(filename, context): # each material is added once): materialDict = {} mesh_objects = [] - for ob in context.selected_objects: + for ob in [ob for ob in context.scene.objects if ob.is_visible()]: # for ob in sce.objects.context: - for ob_derived, mat in getDerivedObjects(ob, False): - data = getMeshFromObject(ob_derived, None, True, False, sce) + + # get derived objects + derived = [] + + # ignore dupli children + if ob.parent and ob.parent.dupli_type != 'NONE': + continue + + if ob.dupli_type != 'NONE': + ob.create_dupli_list() + derived = [(dob.object, dob.matrix) for dob in ob.dupli_list] + else: + derived = [(ob, ob.matrix)] + + for ob_derived, mat in derived: +# for ob_derived, mat in getDerivedObjects(ob, False): + + if ob.type not in ('MESH', 'CURVE', 'SURFACE', 'TEXT', 'META'): + continue + + data = ob_derived.create_mesh(True, 'PREVIEW') +# data = getMeshFromObject(ob_derived, None, True, False, sce) if data: - data.transform(mat, recalc_normals=False) + data.transform(mat) +# data.transform(mat, recalc_normals=False) mesh_objects.append((ob_derived, data)) mat_ls = data.materials mat_ls_len = len(mat_ls) + # get material/image tuples. - if data.faceUV: + if len(data.uv_textures): +# if data.faceUV: if not mat_ls: mat = mat_name = None - for f in data.faces: + for f, uf in zip(data.faces, data.active_uv_texture.data): if mat_ls: - mat_index = f.mat + mat_index = f.material_index +# mat_index = f.mat if mat_index >= mat_ls_len: mat_index = f.mat = 0 mat = mat_ls[mat_index] if mat: mat_name = mat.name else: mat_name = None # else there alredy set to none - - img = f.image + + img = uf.image +# img = f.image if img: img_name = img.name else: img_name = None @@ -947,8 +1000,10 @@ def save_3ds(filename, context): # Why 0 Why! for f in data.faces: - if f.mat >= mat_ls_len: - f.mat = 0 + if f.material_index >= mat_ls_len: +# if f.mat >= mat_ls_len: + f.material_index = 0 +# f.mat = 0 # Make material chunks for all materials used in the meshes: for mat_and_image in materialDict.values(): @@ -980,7 +1035,10 @@ def save_3ds(filename, context): # make a kf object node for the object: kfdata.add_subchunk(make_kf_obj_node(ob, name_to_id)) ''' - blender_mesh.verts = None +# if not blender_mesh.users: + bpy.data.remove_mesh(blender_mesh) +# blender_mesh.verts = None + i+=i # Create chunks for all empties: @@ -1013,8 +1071,9 @@ def save_3ds(filename, context): file.close() # Debugging only: report the exporting time: - Blender.Window.WaitCursor(0) - print("3ds export time: %.2f" % (Blender.sys.time() - time1)) +# Blender.Window.WaitCursor(0) + print("3ds export time: %.2f" % (time.clock() - time1)) +# print("3ds export time: %.2f" % (Blender.sys.time() - time1)) # Debugging only: dump the chunk hierarchy: #primary.dump() @@ -1042,7 +1101,7 @@ class EXPORT_OT_3ds(bpy.types.Operator): ] def execute(self, context): - raise Exception("Not doing anything yet.") + save_3ds(self.filename, context) return ('FINISHED',) def invoke(self, context, event): diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 8192801d9fb..f0a42987848 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -333,6 +333,11 @@ static Object *rna_Object_find_armature(Object *ob) return ob_arm; } +int rna_Object_is_visible(Object *ob, bContext *C) +{ + return ob->lay & CTX_data_scene(C)->lay; +} + /* static void rna_Mesh_assign_verts_to_group(Object *ob, bDeformGroup *group, int *indices, int totindex, float weight, int assignmode) { @@ -443,6 +448,13 @@ void RNA_api_object(StructRNA *srna) func= RNA_def_function(srna, "make_display_list", "rna_Object_make_display_list"); RNA_def_function_ui_description(func, "Update object's display data."); /* XXX describe better */ RNA_def_function_flag(func, FUNC_USE_CONTEXT); + + /* View */ + func= RNA_def_function(srna, "is_visible", "rna_Object_is_visible"); + RNA_def_function_ui_description(func, "Determine if object is visible in active scene."); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + parm= RNA_def_boolean(func, "is_visible", 0, "", "Object visibility."); + RNA_def_function_return(func, parm); } #endif From 44ddff5c516058a73f0297a94845b2e835307d2f Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Thu, 13 Aug 2009 11:14:06 +0000 Subject: [PATCH 65/68] - 3ds importer working (without the "scale to size" option) - changing object.matrix now updates object.loc/rot/scale - added bpy.data.add_lamp() --- release/io/import_3ds.py | 884 ++++++++++-------- release/io/import_obj.py | 15 +- source/blender/makesrna/intern/rna_main_api.c | 27 + .../makesrna/intern/rna_material_api.c | 2 +- source/blender/makesrna/intern/rna_mesh_api.c | 6 +- source/blender/makesrna/intern/rna_object.c | 7 + 6 files changed, 552 insertions(+), 389 deletions(-) diff --git a/release/io/import_3ds.py b/release/io/import_3ds.py index a08fdb8fc85..0269219b63d 100644 --- a/release/io/import_3ds.py +++ b/release/io/import_3ds.py @@ -125,9 +125,14 @@ Loader is based on 3ds loader from www.gametutorials.com (Thanks DigiBen). # Importing modules -from struct import calcsize, unpack +import os +import time +import struct + +from import_obj import unpack_face_list, load_image import bpy +import Mathutils # import Blender # from Blender import Mesh, Object, Material, Image, Texture, Lamp, Mathutils @@ -149,7 +154,7 @@ import bpy # except: # from sets import Set as set -BOUNDS_3DS= [] +BOUNDS_3DS = [] #this script imports uvcoords as sticky vertex coords @@ -157,9 +162,9 @@ BOUNDS_3DS= [] #which shold be more useful. def createBlenderTexture(material, name, image): - texture= bpy.data.textures.new(name) + texture = bpy.data.textures.new(name) texture.setType('Image') - texture.image= image + texture.image = image material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL) @@ -170,7 +175,7 @@ def createBlenderTexture(material, name, image): #Some of the chunks that we will see #----- Primary Chunk, at the beginning of each file -PRIMARY= int('0x4D4D',16) +PRIMARY = int('0x4D4D',16) #------ Main Chunks OBJECTINFO = int('0x3D3D',16); #This gives the version of the mesh and is found right before the material and object information @@ -178,8 +183,8 @@ VERSION = int('0x0002',16); #This gives the version of the .3ds f EDITKEYFRAME= int('0xB000',16); #This is the header for all of the key frame info #------ sub defines of OBJECTINFO -MATERIAL=45055 #0xAFFF // This stored the texture info -OBJECT=16384 #0x4000 // This stores the faces, vertices, etc... +MATERIAL = 45055 #0xAFFF // This stored the texture info +OBJECT = 16384 #0x4000 // This stores the faces, vertices, etc... #>------ sub defines of MATERIAL #------ sub defines of MATERIAL_BLOCK @@ -213,16 +218,16 @@ OBJECT_LAMP_SHADOWED = int('0x4630',16); OBJECT_LAMP_LOCAL_SHADOW = int('0x4640',16); OBJECT_LAMP_LOCAL_SHADOW2 = int('0x4641',16); OBJECT_LAMP_SEE_CONE = int('0x4650',16); -OBJECT_LAMP_SPOT_RECTANGULAR= int('0x4651',16); -OBJECT_LAMP_SPOT_OVERSHOOT= int('0x4652',16); -OBJECT_LAMP_SPOT_PROJECTOR= int('0x4653',16); -OBJECT_LAMP_EXCLUDE= int('0x4654',16); -OBJECT_LAMP_RANGE= int('0x4655',16); -OBJECT_LAMP_ROLL= int('0x4656',16); -OBJECT_LAMP_SPOT_ASPECT= int('0x4657',16); -OBJECT_LAMP_RAY_BIAS= int('0x4658',16); -OBJECT_LAMP_INNER_RANGE= int('0x4659',16); -OBJECT_LAMP_OUTER_RANGE= int('0x465A',16); +OBJECT_LAMP_SPOT_RECTANGULAR = int('0x4651',16); +OBJECT_LAMP_SPOT_OVERSHOOT = int('0x4652',16); +OBJECT_LAMP_SPOT_PROJECTOR = int('0x4653',16); +OBJECT_LAMP_EXCLUDE = int('0x4654',16); +OBJECT_LAMP_RANGE = int('0x4655',16); +OBJECT_LAMP_ROLL = int('0x4656',16); +OBJECT_LAMP_SPOT_ASPECT = int('0x4657',16); +OBJECT_LAMP_RAY_BIAS = int('0x4658',16); +OBJECT_LAMP_INNER_RANGE = int('0x4659',16); +OBJECT_LAMP_OUTER_RANGE = int('0x465A',16); OBJECT_LAMP_MULTIPLIER = int('0x465B',16); OBJECT_LAMP_AMBIENT_LIGHT = int('0x4680',16); @@ -241,21 +246,21 @@ OBJECT_UV = int('0x4140',16); # The UV texture coordinates OBJECT_TRANS_MATRIX = int('0x4160',16); # The Object Matrix global scn -scn= None +scn = None #the chunk class class chunk: - ID=0 - length=0 - bytes_read=0 + ID = 0 + length = 0 + bytes_read = 0 #we don't read in the bytes_read, we compute that binary_format='3): + if (version > 3): print('\tNon-Fatal Error: Version greater than 3, may not load correctly: ', version) #is it an object info chunk? - elif (new_chunk.ID==OBJECTINFO): - #print 'elif (new_chunk.ID==OBJECTINFO):' + elif (new_chunk.ID == OBJECTINFO): + #print 'elif (new_chunk.ID == OBJECTINFO):' # print 'found an OBJECTINFO chunk' process_next_chunk(file, new_chunk, importedObjects, IMAGE_SEARCH) #keep track of how much we read in the main chunk - new_chunk.bytes_read+=temp_chunk.bytes_read + new_chunk.bytes_read += temp_chunk.bytes_read #is it an object chunk? - elif (new_chunk.ID==OBJECT): + elif (new_chunk.ID == OBJECT): if CreateBlenderObject: putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials) - contextMesh_vertls= []; contextMesh_facels= [] + contextMesh_vertls = []; contextMesh_facels = [] ## preparando para receber o proximo objeto - contextMeshMaterials= {} # matname:[face_idxs] - contextMeshUV= None - #contextMesh.vertexUV= 1 # Make sticky coords. + contextMeshMaterials = {} # matname:[face_idxs] + contextMeshUV = None + #contextMesh.vertexUV = 1 # Make sticky coords. # Reset matrix - contextMatrix_rot= None - #contextMatrix_tx= None + contextMatrix_rot = None + #contextMatrix_tx = None - CreateBlenderObject= True - tempName= read_string(file) - contextObName= tempName + CreateBlenderObject = True + tempName = read_string(file) + contextObName = tempName new_chunk.bytes_read += len(tempName)+1 #is it a material chunk? - elif (new_chunk.ID==MATERIAL): - #print 'elif (new_chunk.ID==MATERIAL):' - contextMaterial= bpy.data.materials.new('Material') + elif (new_chunk.ID == MATERIAL): + +# print("read material") + + #print 'elif (new_chunk.ID == MATERIAL):' + contextMaterial = bpy.data.add_material('Material') +# contextMaterial = bpy.data.materials.new('Material') - elif (new_chunk.ID==MAT_NAME): - #print 'elif (new_chunk.ID==MAT_NAME):' - material_name= read_string(file) + elif (new_chunk.ID == MAT_NAME): + #print 'elif (new_chunk.ID == MAT_NAME):' + material_name = read_string(file) + +# print("material name", material_name) #plus one for the null character that ended the string - new_chunk.bytes_read+= len(material_name)+1 + new_chunk.bytes_read += len(material_name)+1 - contextMaterial.name= material_name.rstrip() # remove trailing whitespace + contextMaterial.name = material_name.rstrip() # remove trailing whitespace MATDICT[material_name]= (contextMaterial.name, contextMaterial) - elif (new_chunk.ID==MAT_AMBIENT): - #print 'elif (new_chunk.ID==MAT_AMBIENT):' + elif (new_chunk.ID == MAT_AMBIENT): + #print 'elif (new_chunk.ID == MAT_AMBIENT):' read_chunk(file, temp_chunk) - if (temp_chunk.ID==MAT_FLOAT_COLOR): - temp_data=file.read(calcsize('3f')) - temp_chunk.bytes_read+=12 - contextMaterial.mirCol=[float(col) for col in unpack('<3f', temp_data)] - elif (temp_chunk.ID==MAT_24BIT_COLOR): - temp_data=file.read(calcsize('3B')) - temp_chunk.bytes_read+= 3 - contextMaterial.mirCol= [float(col)/255 for col in unpack('<3B', temp_data)] # data [0,1,2] == rgb + if (temp_chunk.ID == MAT_FLOAT_COLOR): + contextMaterial.mirror_color = read_float_color(temp_chunk) +# temp_data = file.read(struct.calcsize('3f')) +# temp_chunk.bytes_read += 12 +# contextMaterial.mirCol = [float(col) for col in struct.unpack('<3f', temp_data)] + elif (temp_chunk.ID == MAT_24BIT_COLOR): + contextMaterial.mirror_color = read_byte_color(temp_chunk) +# temp_data = file.read(struct.calcsize('3B')) +# temp_chunk.bytes_read += 3 +# contextMaterial.mirCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb else: skip_to_end(file, temp_chunk) - new_chunk.bytes_read+= temp_chunk.bytes_read + new_chunk.bytes_read += temp_chunk.bytes_read - elif (new_chunk.ID==MAT_DIFFUSE): - #print 'elif (new_chunk.ID==MAT_DIFFUSE):' + elif (new_chunk.ID == MAT_DIFFUSE): + #print 'elif (new_chunk.ID == MAT_DIFFUSE):' read_chunk(file, temp_chunk) - if (temp_chunk.ID==MAT_FLOAT_COLOR): - temp_data=file.read(calcsize('3f')) - temp_chunk.bytes_read+=12 - contextMaterial.rgbCol=[float(col) for col in unpack('<3f', temp_data)] - elif (temp_chunk.ID==MAT_24BIT_COLOR): - temp_data=file.read(calcsize('3B')) - temp_chunk.bytes_read+= 3 - contextMaterial.rgbCol= [float(col)/255 for col in unpack('<3B', temp_data)] # data [0,1,2] == rgb + if (temp_chunk.ID == MAT_FLOAT_COLOR): + contextMaterial.diffuse_color = read_float_color(temp_chunk) +# temp_data = file.read(struct.calcsize('3f')) +# temp_chunk.bytes_read += 12 +# contextMaterial.rgbCol = [float(col) for col in struct.unpack('<3f', temp_data)] + elif (temp_chunk.ID == MAT_24BIT_COLOR): + contextMaterial.diffuse_color = read_byte_color(temp_chunk) +# temp_data = file.read(struct.calcsize('3B')) +# temp_chunk.bytes_read += 3 +# contextMaterial.rgbCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb else: skip_to_end(file, temp_chunk) - new_chunk.bytes_read+= temp_chunk.bytes_read - elif (new_chunk.ID==MAT_SPECULAR): - #print 'elif (new_chunk.ID==MAT_SPECULAR):' +# print("read material diffuse color", contextMaterial.diffuse_color) + + new_chunk.bytes_read += temp_chunk.bytes_read + + elif (new_chunk.ID == MAT_SPECULAR): + #print 'elif (new_chunk.ID == MAT_SPECULAR):' read_chunk(file, temp_chunk) - if (temp_chunk.ID==MAT_FLOAT_COLOR): - temp_data=file.read(calcsize('3f')) - temp_chunk.bytes_read+=12 - contextMaterial.mirCol=[float(col) for col in unpack('<3f', temp_data)] - elif (temp_chunk.ID==MAT_24BIT_COLOR): - temp_data=file.read(calcsize('3B')) - temp_chunk.bytes_read+= 3 - contextMaterial.mirCol= [float(col)/255 for col in unpack('<3B', temp_data)] # data [0,1,2] == rgb + if (temp_chunk.ID == MAT_FLOAT_COLOR): + contextMaterial.specular_color = read_float_color(temp_chunk) +# temp_data = file.read(struct.calcsize('3f')) +# temp_chunk.bytes_read += 12 +# contextMaterial.mirCol = [float(col) for col in struct.unpack('<3f', temp_data)] + elif (temp_chunk.ID == MAT_24BIT_COLOR): + contextMaterial.specular_color = read_byte_color(temp_chunk) +# temp_data = file.read(struct.calcsize('3B')) +# temp_chunk.bytes_read += 3 +# contextMaterial.mirCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb else: skip_to_end(file, temp_chunk) - new_chunk.bytes_read+= temp_chunk.bytes_read + new_chunk.bytes_read += temp_chunk.bytes_read - elif (new_chunk.ID==MAT_TEXTURE_MAP): - #print 'elif (new_chunk.ID==MAT_TEXTURE_MAP):' - new_texture= bpy.data.textures.new('Diffuse') - new_texture.setType('Image') - img = None - while (new_chunk.bytes_read BOUNDS_3DS[i+3]: - BOUNDS_3DS[i+3]= v[i] # min + if v[i] > BOUNDS_3DS[i + 3]: + BOUNDS_3DS[i + 3]= v[i] # min # Get the max axis x/y/z - max_axis= max(BOUNDS_3DS[3]-BOUNDS_3DS[0], BOUNDS_3DS[4]-BOUNDS_3DS[1], BOUNDS_3DS[5]-BOUNDS_3DS[2]) + max_axis = max(BOUNDS_3DS[3]-BOUNDS_3DS[0], BOUNDS_3DS[4]-BOUNDS_3DS[1], BOUNDS_3DS[5]-BOUNDS_3DS[2]) # print max_axis - if max_axis < 1<<30: # Should never be false but just make sure. + if max_axis < 1 << 30: # Should never be false but just make sure. # Get a new scale factor if set as an option - SCALE=1.0 - while (max_axis*SCALE) > IMPORT_CONSTRAIN_BOUNDS: + SCALE = 1.0 + while (max_axis * SCALE) > IMPORT_CONSTRAIN_BOUNDS: SCALE/=10 # SCALE Matrix - SCALE_MAT= Blender.Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1]) + SCALE_MAT = Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1]) +# SCALE_MAT = Blender.Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1]) for ob in importedObjects: - ob.setMatrix(ob.matrixWorld*SCALE_MAT) + ob.setMatrix(ob.matrixWorld * SCALE_MAT) # Done constraining to bounds. # Select all new objects. - print('finished importing: "%s" in %.4f sec.' % (filename, (Blender.sys.time()-time1))) + print('finished importing: "%s" in %.4f sec.' % (filename, (time.clock()-time1))) +# print('finished importing: "%s" in %.4f sec.' % (filename, (Blender.sys.time()-time1))) file.close() - Blender.Window.WaitCursor(0) +# Blender.Window.WaitCursor(0) -DEBUG= False +DEBUG = False # if __name__=='__main__' and not DEBUG: -# if calcsize==None: +# if calcsize == None: # Blender.Draw.PupMenu('Error%t|a full python installation not found') # else: # Blender.Window.FileSelector(load_3ds, 'Import 3DS', '*.3ds') @@ -976,14 +1095,14 @@ DEBUG= False else: import os # DEBUG ONLY - TIME= Blender.sys.time() + TIME = Blender.sys.time() import os print 'Searching for files' os.system('find /metavr/ -iname "*.3ds" > /tmp/temp3ds_list') # os.system('find /storage/ -iname "*.3ds" > /tmp/temp3ds_list') print '...Done' - file= open('/tmp/temp3ds_list', 'r') - lines= file.readlines() + file = open('/tmp/temp3ds_list', 'r') + lines = file.readlines() file.close() # sort by filesize for faster testing lines_size = [(os.path.getsize(f[:-1]), f[:-1]) for f in lines] @@ -1001,7 +1120,7 @@ else: #_3ds= _3ds[:-1] print 'Importing', _3ds, '\nNUMBER', i, 'of', len(lines) _3ds_file= _3ds.split('/')[-1].split('\\')[-1] - newScn= Blender.Scene.New(_3ds_file) + newScn = Blender.Scene.New(_3ds_file) newScn.makeCurrent() load_3ds(_3ds, False) @@ -1020,11 +1139,14 @@ class IMPORT_OT_3ds(bpy.types.Operator): # to the class instance from the operator settings before calling. __props__ = [ - bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for importing the 3DS file", maxlen= 1024, default= ""), + bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for importing the 3DS file", maxlen=1024, default= ""), +# bpy.props.FloatProperty(attr="size_constraint", name="Size Constraint", description="Scale the model by 10 until it reacehs the size constraint. Zero Disables.", min=0.0, max=1000.0, soft_min=0.0, soft_max=1000.0, default=10.0), +# bpy.props.BoolProperty(attr="search_images", name="Image Search", description="Search subdirectories for any assosiated images (Warning, may be slow)", default=True), +# bpy.props.BoolProperty(attr="apply_matrix", name="Transform Fix", description="Workaround for object transformations importing incorrectly", default=False), ] def execute(self, context): - raise Exception("Not doing anything yet.") + load_3ds(self.filename, context, 0.0, False, False) return ('FINISHED',) def invoke(self, context, event): @@ -1037,3 +1159,7 @@ class IMPORT_OT_3ds(bpy.types.Operator): return context.active_object != None bpy.ops.add(IMPORT_OT_3ds) + +# NOTES: +# why add 1 extra vertex? and remove it when done? +# disabled scaling to size, this requires exposing bb (easy) and understanding how it works (needs some time) diff --git a/release/io/import_obj.py b/release/io/import_obj.py index f064561a512..484be6d54b4 100644 --- a/release/io/import_obj.py +++ b/release/io/import_obj.py @@ -294,18 +294,21 @@ def line_value(line_split): return ' '.join( line_split[1:] ) # limited replacement for BPyImage.comprehensiveImageLoad -def load_image(imagepath, direc): +def load_image(imagepath, dirname): if os.path.exists(imagepath): return bpy.data.add_image(imagepath) - im_base = os.path.basename(imagepath) + variants = [os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))] - tmp = os.path.join(direc, im_base) - if os.path.exists(tmp): - return bpy.data.add_image(tmp) + for path in variants: + if os.path.exists(path): + return bpy.data.add_image(path) + else: + print(path, "doesn't exist") # TODO comprehensiveImageLoad also searched in bpy.config.textureDir + return None def obj_image_load(imagepath, DIR, IMAGE_SEARCH): @@ -387,7 +390,7 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ # blender_material.setTexture(1, texture, Texture.TexCo.UV, Texture.MapTo.CMIR) # TODO- Add AMB to BPY API elif type == 'Ks': - blender_material.add_texture(texture, "UV", "SPECULAR") + blender_material.add_texture(texture, "UV", "SPECULARITY") # blender_material.setTexture(2, texture, Texture.TexCo.UV, Texture.MapTo.SPEC) elif type == 'Bump': diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 17f3800e10e..9d42c473f50 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -44,6 +44,9 @@ #include "BKE_object.h" #include "BKE_material.h" #include "BKE_image.h" +#include "BKE_texture.h" + +#include "DNA_lamp_types.h" static Mesh *rna_Main_add_mesh(Main *main, char *name) { @@ -62,6 +65,23 @@ static void rna_Main_remove_mesh(Main *main, ReportList *reports, Mesh *me) /* XXX python now has invalid pointer? */ } +static Lamp *rna_Main_add_lamp(Main *main, char *name) +{ + Lamp *la= add_lamp(name); + la->id.us--; + return la; +} + +/* +static void rna_Main_remove_lamp(Main *main, ReportList *reports, Lamp *la) +{ + if(la->id.us == 0) + free_libblock(&main->lamp, la); + else + BKE_report(reports, RPT_ERROR, "Lamp must have zero users to be removed."); +} +*/ + static Object* rna_Main_add_object(Main *main, int type, char *name) { Object *ob= add_only_object(type, name); @@ -158,6 +178,13 @@ void RNA_api_main(StructRNA *srna) parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh to remove."); RNA_def_property_flag(parm, PROP_REQUIRED); + func= RNA_def_function(srna, "add_lamp", "rna_Main_add_lamp"); + RNA_def_function_ui_description(func, "Add a new lamp."); + parm= RNA_def_string(func, "name", "Lamp", 0, "", "New name for the datablock."); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_pointer(func, "mesh", "Lamp", "", "New lamp."); + RNA_def_function_return(func, parm); + func= RNA_def_function(srna, "add_material", "rna_Main_add_material"); RNA_def_function_ui_description(func, "Add a new material."); parm= RNA_def_string(func, "name", "Material", 0, "", "New name for the datablock."); /* optional */ diff --git a/source/blender/makesrna/intern/rna_material_api.c b/source/blender/makesrna/intern/rna_material_api.c index e2b47460fdb..aa28b6b923c 100644 --- a/source/blender/makesrna/intern/rna_material_api.c +++ b/source/blender/makesrna/intern/rna_material_api.c @@ -101,7 +101,7 @@ void RNA_api_material(StructRNA *srna) static EnumPropertyItem prop_texture_mapto_items[] = { {MAP_COL, "COLOR", 0, "Color", "Causes the texture to affect basic color of the material"}, {MAP_NORM, "NORMAL", 0, "Normal", "Causes the texture to affect the rendered normal"}, - {MAP_COLSPEC, "SPEC_COLOR", 0, "Specularity Color", "Causes the texture to affect the specularity color"}, + {MAP_COLSPEC, "SPECULAR_COLOR", 0, "Specularity Color", "Causes the texture to affect the specularity color"}, {MAP_COLMIR, "MIRROR", 0, "Mirror", "Causes the texture to affect the mirror color"}, {MAP_REF, "REFLECTION", 0, "Reflection", "Causes the texture to affect the value of the materials reflectivity"}, {MAP_SPEC, "SPECULARITY", 0, "Specularity", "Causes the texture to affect the value of specularity"}, diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index b50cc678f4f..9d4dad1fb5b 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -248,7 +248,7 @@ static void rna_Mesh_add_geometry(Mesh *mesh, int verts, int edges, int faces) rna_Mesh_add_faces(mesh, faces); } -static void rna_Mesh_add_uv_layer(Mesh *me) +static void rna_Mesh_add_uv_texture(Mesh *me) { me->mtface= CustomData_add_layer(&me->fdata, CD_MTFACE, CD_DEFAULT, NULL, me->totface); } @@ -306,8 +306,8 @@ void RNA_api_mesh(StructRNA *srna) parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh, remove it if it is only used for export."); RNA_def_function_return(func, parm); - func= RNA_def_function(srna, "add_uv_layer", "rna_Mesh_add_uv_layer"); - RNA_def_function_ui_description(func, "Add new UV layer to Mesh."); + func= RNA_def_function(srna, "add_uv_texture", "rna_Mesh_add_uv_texture"); + RNA_def_function_ui_description(func, "Add a UV texture layer to Mesh."); func= RNA_def_function(srna, "calc_normals", "rna_Mesh_calc_normals"); RNA_def_function_ui_description(func, "Calculate vertex normals."); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 6fe254dcb68..32307709387 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -74,6 +74,12 @@ void rna_Object_update(bContext *C, PointerRNA *ptr) DAG_object_flush_update(CTX_data_scene(C), ptr->id.data, OB_RECALC_OB); } +void rna_Object_matrix_update(bContext *C, PointerRNA *ptr) +{ + ED_object_apply_obmat(ptr->id.data); + rna_Object_update(C, ptr); +} + void rna_Object_update_data(bContext *C, PointerRNA *ptr) { DAG_object_flush_update(CTX_data_scene(C), ptr->id.data, OB_RECALC_DATA); @@ -1101,6 +1107,7 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "obmat"); RNA_def_property_array(prop, 16); RNA_def_property_ui_text(prop, "Matrix", "Transformation matrix."); + RNA_def_property_update(prop, NC_OBJECT|ND_TRANSFORM, "rna_Object_matrix_update"); /* collections */ prop= RNA_def_property(srna, "constraints", PROP_COLLECTION, PROP_NONE); From 09042ce9a507b7f9ea36a63e60bbda6266541579 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Mon, 17 Aug 2009 14:29:29 +0000 Subject: [PATCH 66/68] Ported X3D exporter. --- release/io/export_3ds.py | 36 ++-- release/io/export_x3d.py | 448 ++++++++++++++++++++++++++------------- 2 files changed, 326 insertions(+), 158 deletions(-) diff --git a/release/io/export_3ds.py b/release/io/export_3ds.py index 56268b2925a..19c12146769 100644 --- a/release/io/export_3ds.py +++ b/release/io/export_3ds.py @@ -60,6 +60,22 @@ import bpy # except: # struct = None +# also used by X3D exporter +# return a tuple (free, object list), free is True if memory should be freed later with free_derived_objects() +def create_derived_objects(ob): + if ob.parent and ob.parent.dupli_type != 'NONE': + return False, None + + if ob.dupli_type != 'NONE': + ob.create_dupli_list() + return True, [(dob.object, dob.matrix) for dob in ob.dupli_list] + else: + return False, [(ob, ob.matrix)] + +# also used by X3D exporter +def free_derived_objects(ob): + ob.free_dupli_list() + # So 3ds max can open files, limit names to 12 in length # this is verry annoying for filenames! name_unique = [] @@ -941,17 +957,9 @@ def save_3ds(filename, context): # for ob in sce.objects.context: # get derived objects - derived = [] + free, derived = create_derived_objects(ob) - # ignore dupli children - if ob.parent and ob.parent.dupli_type != 'NONE': - continue - - if ob.dupli_type != 'NONE': - ob.create_dupli_list() - derived = [(dob.object, dob.matrix) for dob in ob.dupli_list] - else: - derived = [(ob, ob.matrix)] + if derived == None: continue for ob_derived, mat in derived: # for ob_derived, mat in getDerivedObjects(ob, False): @@ -1003,8 +1011,12 @@ def save_3ds(filename, context): if f.material_index >= mat_ls_len: # if f.mat >= mat_ls_len: f.material_index = 0 -# f.mat = 0 - + # f.mat = 0 + + if free: + free_derived_objects(ob) + + # Make material chunks for all materials used in the meshes: for mat_and_image in materialDict.values(): object_info.add_subchunk(make_material_chunk(mat_and_image[0], mat_and_image[1])) diff --git a/release/io/export_x3d.py b/release/io/export_x3d.py index b57be2286e9..30a4b1483b0 100644 --- a/release/io/export_x3d.py +++ b/release/io/export_x3d.py @@ -54,10 +54,13 @@ Known issues:
#################################### import math +import os import bpy import Mathutils +from export_3ds import create_derived_objects, free_derived_objects + # import Blender # from Blender import Object, Lamp, Draw, Image, Text, sys, Mesh # from Blender.Scene import Render @@ -166,8 +169,10 @@ class x3d_class: self.file.write("\n") self.file.write("\n") self.file.write("\n") - self.file.write("\t\n" % sys.basename(bfile)) - self.file.write("\t\n" % Blender.Get('version')) + self.file.write("\t\n" % os.path.basename(bfile)) + # self.file.write("\t\n" % sys.basename(bfile)) + self.file.write("\t\n" % '2.5') + # self.file.write("\t\n" % Blender.Get('version')) self.file.write("\t\n") self.file.write("\n") self.file.write("\n") @@ -211,9 +216,12 @@ class x3d_class: ''' def writeViewpoint(self, ob, mat, scene): - context = scene.render - ratio = float(context.imageSizeY())/float(context.imageSizeX()) - lens = (360* (math.atan(ratio *16 / ob.data.getLens()) / math.pi))*(math.pi/180) + context = scene.render_data + # context = scene.render + ratio = float(context.resolution_x)/float(context.resolution_y) + # ratio = float(context.imageSizeY())/float(context.imageSizeX()) + lens = (360* (math.atan(ratio *16 / ob.data.lens) / math.pi))*(math.pi/180) + # lens = (360* (math.atan(ratio *16 / ob.data.getLens()) / math.pi))*(math.pi/180) lens = min(lens, math.pi) # get the camera location, subtract 90 degress from X to orient like X3D does @@ -221,7 +229,8 @@ class x3d_class: loc = self.rotatePointForVRML(mat.translationPart()) rot = mat.toEuler() - rot = (((rot[0]-90)*DEG2RAD), rot[1]*DEG2RAD, rot[2]*DEG2RAD) + rot = (((rot[0]-90)), rot[1], rot[2]) + # rot = (((rot[0]-90)*DEG2RAD), rot[1]*DEG2RAD, rot[2]*DEG2RAD) nRot = self.rotatePointForVRML( rot ) # convert to Quaternion and to Angle Axis Q = self.eulerToQuaternions(nRot[0], nRot[1], nRot[2]) @@ -237,13 +246,18 @@ class x3d_class: def writeFog(self, world): if world: - mtype = world.getMistype() - mparam = world.getMist() - grd = world.getHor() + mtype = world.mist.falloff + # mtype = world.getMistype() + mparam = world.mist + # mparam = world.getMist() + grd = world.horizon_color + # grd = world.getHor() grd0, grd1, grd2 = grd[0], grd[1], grd[2] else: return - if (mtype == 1 or mtype == 2): + if (mtype == 'LINEAR' or mtype == 'INVERSE_QUADRATIC'): + mtype = 1 if mtype == 'LINEAR' else 2 + # if (mtype == 1 or mtype == 2): self.file.write("\n\n" % round(mparam[2],self.cp)) @@ -256,7 +270,8 @@ class x3d_class: def writeSpotLight(self, ob, mtx, lamp, world): safeName = self.cleanStr(ob.name) if world: - ambi = world.amb + ambi = world.ambient_color + # ambi = world.amb ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5 else: ambi = 0 @@ -264,7 +279,8 @@ class x3d_class: # compute cutoff and beamwidth intensity=min(lamp.energy/1.75,1.0) - beamWidth=((lamp.spotSize*math.pi)/180.0)*.37; + beamWidth=((lamp.spot_size*math.pi)/180.0)*.37; + # beamWidth=((lamp.spotSize*math.pi)/180.0)*.37; cutOffAngle=beamWidth*1.3 dx,dy,dz=self.computeDirection(mtx) @@ -275,12 +291,14 @@ class x3d_class: #location=(ob.matrixWorld*MATWORLD).translationPart() # now passed location=(mtx*MATWORLD).translationPart() - radius = lamp.dist*math.cos(beamWidth) + radius = lamp.distance*math.cos(beamWidth) + # radius = lamp.dist*math.cos(beamWidth) self.file.write("\n\n" % (round(dx,4),round(dy,4),round(dz,4))) def writePointLight(self, ob, mtx, lamp, world): safeName = self.cleanStr(ob.name) if world: - ambi = world.amb + ambi = world.ambient_color + # ambi = world.amb ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5 else: ambi = 0 @@ -318,9 +339,11 @@ class x3d_class: self.file.write("\n\n" % (round(location[0],3), round(location[1],3), round(location[2],3))) ''' def writeNode(self, ob, mtx): @@ -362,24 +385,41 @@ class x3d_class: vColors={} # 'multi':1 meshName = self.cleanStr(ob.name) - meshME = self.cleanStr(ob.getData(mesh=1).name) # We dont care if its the mesh name or not + meshME = self.cleanStr(ob.data.name) # We dont care if its the mesh name or not + # meshME = self.cleanStr(ob.getData(mesh=1).name) # We dont care if its the mesh name or not if len(mesh.faces) == 0: return - mode = 0 - if mesh.faceUV: - for face in mesh.faces: - mode |= face.mode + mode = [] + # mode = 0 + if mesh.active_uv_texture: + # if mesh.faceUV: + for face in mesh.active_uv_texture.data: + # for face in mesh.faces: + if face.halo and 'HALO' not in mode: + mode += ['HALO'] + if face.billboard and 'BILLBOARD' not in mode: + mode += ['BILLBOARD'] + if face.object_color and 'OBJECT_COLOR' not in mode: + mode += ['OBJECT_COLOR'] + if face.collision and 'COLLISION' not in mode: + mode += ['COLLISION'] + # mode |= face.mode - if mode & Mesh.FaceModes.HALO and self.halonode == 0: + if 'HALO' in mode and self.halonode == 0: + # if mode & Mesh.FaceModes.HALO and self.halonode == 0: self.writeIndented("\n",1) self.halonode = 1 - elif mode & Mesh.FaceModes.BILLBOARD and self.billnode == 0: + elif 'BILLBOARD' in mode and self.billnode == 0: + # elif mode & Mesh.FaceModes.BILLBOARD and self.billnode == 0: self.writeIndented("\n",1) self.billnode = 1 - elif mode & Mesh.FaceModes.OBCOL and self.matonly == 0: + elif 'OBJECT_COLOR' in mode and self.matonly == 0: + # elif mode & Mesh.FaceModes.OBCOL and self.matonly == 0: self.matonly = 1 - elif mode & Mesh.FaceModes.TILES and self.tilenode == 0: - self.tilenode = 1 - elif not mode & Mesh.FaceModes.DYNAMIC and self.collnode == 0: + # TF_TILES is marked as deprecated in DNA_meshdata_types.h + # elif mode & Mesh.FaceModes.TILES and self.tilenode == 0: + # self.tilenode = 1 + elif 'COLLISION' not in mode and self.collnode == 0: + # elif not mode & Mesh.FaceModes.DYNAMIC and self.collnode == 0: self.writeIndented("\n",1) self.collnode = 1 @@ -401,34 +441,44 @@ class x3d_class: quat = mtx.toQuat() rot= quat.axis - # self.writeIndented('\n' % (rot[0], rot[1], rot[2], rot[3])) self.writeIndented('\n' % \ - (meshName, loc[0], loc[1], loc[2], sca[0], sca[1], sca[2], rot[0], rot[1], rot[2], quat.angle*DEG2RAD) ) + (meshName, loc[0], loc[1], loc[2], sca[0], sca[1], sca[2], rot[0], rot[1], rot[2], quat.angle) ) + # self.writeIndented('\n' % \ + # (meshName, loc[0], loc[1], loc[2], sca[0], sca[1], sca[2], rot[0], rot[1], rot[2], quat.angle*DEG2RAD) ) self.writeIndented("\n",1) maters=mesh.materials hasImageTexture=0 issmooth=0 - if len(maters) > 0 or mesh.faceUV: + if len(maters) > 0 or mesh.active_uv_texture: + # if len(maters) > 0 or mesh.faceUV: self.writeIndented("\n", 1) # right now this script can only handle a single material per mesh. if len(maters) >= 1: mat=maters[0] - matFlags = mat.getMode() - if not matFlags & Blender.Material.Modes['TEXFACE']: - self.writeMaterial(mat, self.cleanStr(maters[0].name,''), world) + # matFlags = mat.getMode() + if not mat.face_texture: + # if not matFlags & Blender.Material.Modes['TEXFACE']: + self.writeMaterial(mat, self.cleanStr(mat.name,''), world) + # self.writeMaterial(mat, self.cleanStr(maters[0].name,''), world) if len(maters) > 1: print("Warning: mesh named %s has multiple materials" % meshName) print("Warning: only one material per object handled") #-- textures - if mesh.faceUV: - for face in mesh.faces: - if (hasImageTexture == 0) and (face.image): + face = None + if mesh.active_uv_texture: + # if mesh.faceUV: + for face in mesh.active_uv_texture.data: + # for face in mesh.faces: + if face.image: + # if (hasImageTexture == 0) and (face.image): self.writeImageTexture(face.image) - hasImageTexture=1 # keep track of face texture - if self.tilenode == 1: + # hasImageTexture=1 # keep track of face texture + break + if self.tilenode == 1 and face and face.image: + # if self.tilenode == 1: self.writeIndented("\n" % (face.image.xrep, face.image.yrep)) self.tilenode = 0 self.writeIndented("\n", -1) @@ -458,11 +508,13 @@ class x3d_class: issmooth=1 break if issmooth==1: - creaseAngle=(mesh.degr)*(math.pi/180.0) + creaseAngle=(mesh.autosmooth_angle)*(math.pi/180.0) + # creaseAngle=(mesh.degr)*(math.pi/180.0) self.file.write("creaseAngle=\"%s\" " % (round(creaseAngle,self.cp))) #--- output textureCoordinates if UV texture used - if mesh.faceUV: + if mesh.active_uv_texture: + # if mesh.faceUV: if self.matonly == 1 and self.share == 1: self.writeFaceColors(mesh) elif hasImageTexture == 1: @@ -476,7 +528,8 @@ class x3d_class: self.writeCoordinates(ob, mesh, meshName, EXPORT_TRI) #--- output textureCoordinates if UV texture used - if mesh.faceUV: + if mesh.active_uv_texture: + # if mesh.faceUV: if hasImageTexture == 1: self.writeTextureCoordinates(mesh) elif self.matonly == 1 and self.share == 1: @@ -516,16 +569,22 @@ class x3d_class: if self.writingcoords == 0: self.file.write('coordIndex="') for face in mesh.faces: - fv = face.v + fv = face.verts + # fv = face.v - if len(face)==3: - self.file.write("%i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index)) + if len(fv)==3: + # if len(face)==3: + self.file.write("%i %i %i -1, " % (fv[0], fv[1], fv[2])) + # self.file.write("%i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index)) else: if EXPORT_TRI: - self.file.write("%i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index)) - self.file.write("%i %i %i -1, " % (fv[0].index, fv[2].index, fv[3].index)) + self.file.write("%i %i %i -1, " % (fv[0], fv[1], fv[2])) + # self.file.write("%i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index)) + self.file.write("%i %i %i -1, " % (fv[0], fv[2], fv[3])) + # self.file.write("%i %i %i -1, " % (fv[0].index, fv[2].index, fv[3].index)) else: - self.file.write("%i %i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index, fv[3].index)) + self.file.write("%i %i %i %i -1, " % (fv[0], fv[1], fv[2], fv[3])) + # self.file.write("%i %i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index, fv[3].index)) self.file.write("\">\n") else: @@ -543,8 +602,12 @@ class x3d_class: texIndexList=[] j=0 - for face in mesh.faces: - for uv in face.uv: + for face in mesh.active_uv_texture.data: + # for face in mesh.faces: + uvs = [face.uv1, face.uv2, face.uv3, face.uv4] if face.verts[3] else [face.uv1, face.uv2, face.uv3] + + for uv in uvs: + # for uv in face.uv: texIndexList.append(j) texCoordList.append(uv) j=j+1 @@ -568,15 +631,24 @@ class x3d_class: def writeFaceColors(self, mesh): if self.writingcolor == 0: self.file.write("colorPerVertex=\"false\" ") - else: + elif mesh.active_vertex_color: + # else: self.writeIndented(" 2: - print("Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b)) - aColor = self.rgbToFS(c) - self.file.write("%s, " % aColor) + for face in mesh.active_vertex_color.data: + c = face.color1 + if self.verbose > 2: + print("Debug: face.col r=%d g=%d b=%d" % (c[0], c[1], c[2])) + # print("Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b)) + aColor = self.rgbToFS(c) + self.file.write("%s, " % aColor) + + # for face in mesh.faces: + # if face.col: + # c=face.col[0] + # if self.verbose > 2: + # print("Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b)) + # aColor = self.rgbToFS(c) + # self.file.write("%s, " % aColor) self.file.write("\" />") self.writeIndented("\n",-1) @@ -589,22 +661,31 @@ class x3d_class: self.matNames[matName]=1 - ambient = mat.amb/3 - diffuseR, diffuseG, diffuseB = mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2] + ambient = mat.ambient/3 + # ambient = mat.amb/3 + diffuseR, diffuseG, diffuseB = tuple(mat.diffuse_color) + # diffuseR, diffuseG, diffuseB = mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2] if world: - ambi = world.getAmb() - ambi0, ambi1, ambi2 = (ambi[0]*mat.amb)*2, (ambi[1]*mat.amb)*2, (ambi[2]*mat.amb)*2 + ambi = world.ambient_color + # ambi = world.getAmb() + ambi0, ambi1, ambi2 = (ambi[0]*mat.ambient)*2, (ambi[1]*mat.ambient)*2, (ambi[2]*mat.ambient)*2 + # ambi0, ambi1, ambi2 = (ambi[0]*mat.amb)*2, (ambi[1]*mat.amb)*2, (ambi[2]*mat.amb)*2 else: ambi0, ambi1, ambi2 = 0, 0, 0 emisR, emisG, emisB = (diffuseR*mat.emit+ambi0)/2, (diffuseG*mat.emit+ambi1)/2, (diffuseB*mat.emit+ambi2)/2 - shininess = mat.hard/512.0 - specR = (mat.specCol[0]+0.001)/(1.25/(mat.spec+0.001)) - specG = (mat.specCol[1]+0.001)/(1.25/(mat.spec+0.001)) - specB = (mat.specCol[2]+0.001)/(1.25/(mat.spec+0.001)) + shininess = mat.specular_hardness/512.0 + # shininess = mat.hard/512.0 + specR = (mat.specular_color[0]+0.001)/(1.25/(mat.specular_reflection+0.001)) + # specR = (mat.specCol[0]+0.001)/(1.25/(mat.spec+0.001)) + specG = (mat.specular_color[1]+0.001)/(1.25/(mat.specular_reflection+0.001)) + # specG = (mat.specCol[1]+0.001)/(1.25/(mat.spec+0.001)) + specB = (mat.specular_color[2]+0.001)/(1.25/(mat.specular_reflection+0.001)) + # specB = (mat.specCol[2]+0.001)/(1.25/(mat.spec+0.001)) transp = 1-mat.alpha - matFlags = mat.getMode() - if matFlags & Blender.Material.Modes['SHADELESS']: + # matFlags = mat.getMode() + if mat.shadeless: + # if matFlags & Blender.Material.Modes['SHADELESS']: ambient = 1 shine = 1 specR = emitR = diffuseR @@ -635,10 +716,13 @@ class x3d_class: def writeBackground(self, world, alltextures): if world: worldname = world.name else: return - blending = world.getSkytype() - grd = world.getHor() + blending = (world.blend_sky, world.paper_sky, world.real_sky) + # blending = world.getSkytype() + grd = world.horizon_color + # grd = world.getHor() grd0, grd1, grd2 = grd[0], grd[1], grd[2] - sky = world.getZen() + sky = world.zenith_color + # sky = world.getZen() sky0, sky1, sky2 = sky[0], sky[1], sky[2] mix0, mix1, mix2 = grd[0]+sky[0], grd[1]+sky[1], grd[2]+sky[2] mix0, mix1, mix2 = mix0/2, mix1/2, mix2/2 @@ -646,27 +730,32 @@ class x3d_class: if worldname not in self.namesStandard: self.file.write("DEF=\"%s\" " % self.secureName(worldname)) # No Skytype - just Hor color - if blending == 0: + if blending == (0, 0, 0): + # if blending == 0: self.file.write("groundColor=\"%s %s %s\" " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) self.file.write("skyColor=\"%s %s %s\" " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) # Blend Gradient - elif blending == 1: + elif blending == (1, 0, 0): + # elif blending == 1: self.file.write("groundColor=\"%s %s %s, " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) self.file.write("%s %s %s\" groundAngle=\"1.57, 1.57\" " %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp))) self.file.write("skyColor=\"%s %s %s, " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) self.file.write("%s %s %s\" skyAngle=\"1.57, 1.57\" " %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp))) # Blend+Real Gradient Inverse - elif blending == 3: + elif blending == (1, 0, 1): + # elif blending == 3: self.file.write("groundColor=\"%s %s %s, " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) self.file.write("%s %s %s\" groundAngle=\"1.57, 1.57\" " %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp))) self.file.write("skyColor=\"%s %s %s, " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) self.file.write("%s %s %s\" skyAngle=\"1.57, 1.57\" " %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp))) # Paper - just Zen Color - elif blending == 4: + elif blending == (0, 0, 1): + # elif blending == 4: self.file.write("groundColor=\"%s %s %s\" " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) self.file.write("skyColor=\"%s %s %s\" " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) # Blend+Real+Paper - komplex gradient - elif blending == 7: + elif blending == (1, 1, 1): + # elif blending == 7: self.writeIndented("groundColor=\"%s %s %s, " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) self.writeIndented("%s %s %s\" groundAngle=\"1.57, 1.57\" " %(round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) self.writeIndented("skyColor=\"%s %s %s, " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) @@ -675,22 +764,43 @@ class x3d_class: else: self.file.write("groundColor=\"%s %s %s\" " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) self.file.write("skyColor=\"%s %s %s\" " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) + alltexture = len(alltextures) + for i in range(alltexture): - namemat = alltextures[i].name - pic = alltextures[i].getImage() + tex = alltextures[i] + + if tex.type != 'IMAGE': + continue + + namemat = tex.name + # namemat = alltextures[i].name + + pic = tex.image + + # using .expandpath just in case, os.path may not expect // + basename = os.path.basename(bpy.sys.expandpath(pic.filename)) + + pic = alltextures[i].image + # pic = alltextures[i].getImage() if (namemat == "back") and (pic != None): - self.file.write("\n\tbackUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + self.file.write("\n\tbackUrl=\"%s\" " % basename) + # self.file.write("\n\tbackUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) elif (namemat == "bottom") and (pic != None): - self.writeIndented("bottomUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + self.writeIndented("bottomUrl=\"%s\" " % basename) + # self.writeIndented("bottomUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) elif (namemat == "front") and (pic != None): - self.writeIndented("frontUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + self.writeIndented("frontUrl=\"%s\" " % basename) + # self.writeIndented("frontUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) elif (namemat == "left") and (pic != None): - self.writeIndented("leftUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + self.writeIndented("leftUrl=\"%s\" " % basename) + # self.writeIndented("leftUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) elif (namemat == "right") and (pic != None): - self.writeIndented("rightUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + self.writeIndented("rightUrl=\"%s\" " % basename) + # self.writeIndented("rightUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) elif (namemat == "top") and (pic != None): - self.writeIndented("topUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + self.writeIndented("topUrl=\"%s\" " % basename) + # self.writeIndented("topUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) self.writeIndented("/>\n\n") ########################################################## @@ -711,44 +821,65 @@ class x3d_class: self.proto = 0 - # COPIED FROM OBJ EXPORTER - if EXPORT_APPLY_MODIFIERS: - temp_mesh_name = '~tmp-mesh' + # # COPIED FROM OBJ EXPORTER + # if EXPORT_APPLY_MODIFIERS: + # temp_mesh_name = '~tmp-mesh' - # Get the container mesh. - used for applying modifiers and non mesh objects. - containerMesh = meshName = tempMesh = None - for meshName in Blender.NMesh.GetNames(): - if meshName.startswith(temp_mesh_name): - tempMesh = Mesh.Get(meshName) - if not tempMesh.users: - containerMesh = tempMesh - if not containerMesh: - containerMesh = Mesh.New(temp_mesh_name) + # # Get the container mesh. - used for applying modifiers and non mesh objects. + # containerMesh = meshName = tempMesh = None + # for meshName in Blender.NMesh.GetNames(): + # if meshName.startswith(temp_mesh_name): + # tempMesh = Mesh.Get(meshName) + # if not tempMesh.users: + # containerMesh = tempMesh + # if not containerMesh: + # containerMesh = Mesh.New(temp_mesh_name) # -------------------------- - for ob_main in scene.objects.context: - for ob, ob_mat in BPyObject.getDerivedObjects(ob_main): + for ob_main in [o for o in scene.objects if o.is_visible()]: + # for ob_main in scene.objects.context: + + free, derived = create_derived_objects(ob_main) + + if derived == None: continue + + for ob, ob_mat in derived: + # for ob, ob_mat in BPyObject.getDerivedObjects(ob_main): objType=ob.type objName=ob.name self.matonly = 0 - if objType == "Camera": + if objType == "CAMERA": + # if objType == "Camera": self.writeViewpoint(ob, ob_mat, scene) - elif objType in ("Mesh", "Curve", "Surf", "Text") : - if EXPORT_APPLY_MODIFIERS or objType != 'Mesh': - me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, False, scene) + elif objType in ("MESH", "CURVE", "SURF", "TEXT") : + # elif objType in ("Mesh", "Curve", "Surf", "Text") : + if EXPORT_APPLY_MODIFIERS or objType != 'MESH': + # if EXPORT_APPLY_MODIFIERS or objType != 'Mesh': + me = ob.create_mesh(EXPORT_APPLY_MODIFIERS, 'PREVIEW') + # me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, False, scene) else: - me = ob.getData(mesh=1) + me = ob.data + # me = ob.getData(mesh=1) self.writeIndexedFaceSet(ob, me, ob_mat, world, EXPORT_TRI = EXPORT_TRI) - elif objType == "Lamp": + + # free mesh created with create_mesh() + if me != ob.data: + bpy.data.remove_mesh(me) + + elif objType == "LAMP": + # elif objType == "Lamp": data= ob.data datatype=data.type - if datatype == Lamp.Types.Lamp: + if datatype == 'POINT': + # if datatype == Lamp.Types.Lamp: self.writePointLight(ob, ob_mat, data, world) - elif datatype == Lamp.Types.Spot: + elif datatype == 'SPOT': + # elif datatype == Lamp.Types.Spot: self.writeSpotLight(ob, ob_mat, data, world) - elif datatype == Lamp.Types.Sun: + elif datatype == 'SUN': + # elif datatype == Lamp.Types.Sun: self.writeDirectionalLight(ob, ob_mat, data, world) else: self.writeDirectionalLight(ob, ob_mat, data, world) @@ -758,12 +889,15 @@ class x3d_class: else: #print "Info: Ignoring [%s], object type [%s] not handle yet" % (object.name,object.getType) pass - + + if free: + free_derived_objects(ob_main) + self.file.write("\n\n") - if EXPORT_APPLY_MODIFIERS: - if containerMesh: - containerMesh.verts = None + # if EXPORT_APPLY_MODIFIERS: + # if containerMesh: + # containerMesh.verts = None self.cleanup() @@ -812,10 +946,13 @@ class x3d_class: faceMap={} nFaceIndx=0 - if mesh.faceUV: - for face in mesh.faces: + if mesh.active_uv_texture: + # if mesh.faceUV: + for face in mesh.active_uv_texture.data: + # for face in mesh.faces: sidename=''; - if face.mode & Mesh.FaceModes.TWOSIDE: + if face.twoside: + # if face.mode & Mesh.FaceModes.TWOSIDE: sidename='two' else: sidename='one' @@ -859,31 +996,38 @@ class x3d_class: print("Debug: face.image=%s" % face.image.name) print("Debug: face.materialIndex=%d" % face.materialIndex) - def getVertexColorByIndx(self, mesh, indx): - c = None - for face in mesh.faces: - j=0 - for vertex in face.v: - if vertex.index == indx: - c=face.col[j] - break - j=j+1 - if c: break - return c + # XXX not used + # def getVertexColorByIndx(self, mesh, indx): + # c = None + # for face in mesh.faces: + # j=0 + # for vertex in face.v: + # if vertex.index == indx: + # c=face.col[j] + # break + # j=j+1 + # if c: break + # return c def meshToString(self,mesh): - print("Debug: mesh.hasVertexUV=%d" % mesh.vertexColors) - print("Debug: mesh.faceUV=%d" % mesh.faceUV) - print("Debug: mesh.hasVertexColours=%d" % mesh.hasVertexColours()) + # print("Debug: mesh.hasVertexUV=%d" % mesh.vertexColors) + print("Debug: mesh.faceUV=%d" % (len(mesh.uv_textures) > 0)) + # print("Debug: mesh.faceUV=%d" % mesh.faceUV) + print("Debug: mesh.hasVertexColours=%d" % (len(mesh.vertex_colors) > 0)) + # print("Debug: mesh.hasVertexColours=%d" % mesh.hasVertexColours()) print("Debug: mesh.verts=%d" % len(mesh.verts)) print("Debug: mesh.faces=%d" % len(mesh.faces)) print("Debug: mesh.materials=%d" % len(mesh.materials)) def rgbToFS(self, c): - s="%s %s %s" % ( - round(c.r/255.0,self.cp), - round(c.g/255.0,self.cp), - round(c.b/255.0,self.cp)) + s="%s %s %s" % (round(c[0]/255.0,self.cp), + round(c[1]/255.0,self.cp), + round(c[2]/255.0,self.cp)) + + # s="%s %s %s" % ( + # round(c.r/255.0,self.cp), + # round(c.g/255.0,self.cp), + # round(c.b/255.0,self.cp)) return s def computeDirection(self, mtx): @@ -891,9 +1035,10 @@ class x3d_class: ax,ay,az = (mtx*MATWORLD).toEuler() - ax *= DEG2RAD - ay *= DEG2RAD - az *= DEG2RAD + # ax *= DEG2RAD + # ay *= DEG2RAD + # az *= DEG2RAD + # rot X x1=x y1=y*math.cos(ax)-z*math.sin(ax) @@ -980,11 +1125,11 @@ class x3d_class: # Callbacks, needed before Main ########################################################## -def x3d_export(filename, \ - EXPORT_APPLY_MODIFIERS= False,\ - EXPORT_TRI= False,\ - EXPORT_GZIP= False,\ - ): +def x3d_export(filename, + context, + EXPORT_APPLY_MODIFIERS=False, + EXPORT_TRI=False, + EXPORT_GZIP=False): if EXPORT_GZIP: if not filename.lower().endswith('.x3dz'): @@ -994,9 +1139,13 @@ def x3d_export(filename, \ filename = '.'.join(filename.split('.')[:-1]) + '.x3d' - scene = Blender.Scene.GetCurrent() + scene = context.scene + # scene = Blender.Scene.GetCurrent() world = scene.world - alltextures = Blender.Texture.Get() + + # XXX these are global textures while .Get() returned only scene's? + alltextures = bpy.data.textures + # alltextures = Blender.Texture.Get() wrlexport=x3d_class(filename) wrlexport.export(\ @@ -1064,11 +1213,15 @@ class EXPORT_OT_x3d(bpy.types.Operator): # to the class instance from the operator settings before calling. __props__ = [ - bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the X3D file", maxlen= 1024, default= ""), + bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the X3D file", maxlen=1024, default=""), + + bpy.props.BoolProperty(attr="apply_modifiers", name="Apply Modifiers", description="Use transformed mesh data from each object.", default=True), + bpy.props.BoolProperty(attr="triangulate", name="Triangulate", description="Triangulate quads.", default=False), + bpy.props.BoolProperty(attr="compress", name="Compress", description="GZip the resulting file, requires a full python install.", default=False), ] def execute(self, context): - raise Exception("Not doing anything yet.") + x3d_export(self.filename, context, self.apply_modifiers, self.triangulate, self.compress) return ('FINISHED',) def invoke(self, context, event): @@ -1081,3 +1234,6 @@ class EXPORT_OT_x3d(bpy.types.Operator): return context.active_object != None bpy.ops.add(EXPORT_OT_x3d) + +# NOTES +# - blender version is hardcoded From 7220792f187f91a2ef3eb0c930a13674b2a8a80a Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Wed, 19 Aug 2009 09:52:13 +0000 Subject: [PATCH 67/68] Various fixes in rna_*_api.c files to remove compiler warnings. --- source/blender/editors/include/ED_mesh.h | 1 + source/blender/editors/mesh/editmesh_mods.c | 5 +++++ source/blender/makesrna/intern/rna_action_api.c | 2 ++ source/blender/makesrna/intern/rna_image_api.c | 2 ++ source/blender/makesrna/intern/rna_internal.h | 6 ++++-- source/blender/makesrna/intern/rna_mesh_api.c | 3 +++ source/blender/makesrna/intern/rna_object_api.c | 2 +- source/blender/makesrna/intern/rna_pose_api.c | 4 ++-- source/blender/makesrna/intern/rna_scene_api.c | 4 ++++ source/blender/python/SConscript | 2 +- source/creator/creator.c | 4 ++++ source/creator/tests/{alltest.c => test.c} | 0 source/creator/tests/test.h | 6 ++++++ 13 files changed, 35 insertions(+), 6 deletions(-) rename source/creator/tests/{alltest.c => test.c} (100%) create mode 100644 source/creator/tests/test.h diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index cceebeadc96..696e8f823eb 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -118,6 +118,7 @@ void EM_select_face(struct EditFace *efa, int sel); void EM_select_face_fgon(struct EditMesh *em, struct EditFace *efa, int val); void EM_select_swap(struct EditMesh *em); void EM_toggle_select_all(struct EditMesh *em); +void EM_select_all(struct EditMesh *em); void EM_selectmode_flush(struct EditMesh *em); void EM_deselect_flush(struct EditMesh *em); void EM_selectmode_set(struct EditMesh *em); diff --git a/source/blender/editors/mesh/editmesh_mods.c b/source/blender/editors/mesh/editmesh_mods.c index d27aa3f7e3a..363b6ecc4e4 100644 --- a/source/blender/editors/mesh/editmesh_mods.c +++ b/source/blender/editors/mesh/editmesh_mods.c @@ -3288,6 +3288,11 @@ void EM_toggle_select_all(EditMesh *em) /* exported for UV */ EM_set_flag_all(em, SELECT); } +void EM_select_all(EditMesh *em) +{ + EM_set_flag_all(em, SELECT); +} + static int toggle_select_all_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); diff --git a/source/blender/makesrna/intern/rna_action_api.c b/source/blender/makesrna/intern/rna_action_api.c index 2b51a424fcf..11efa6d5f8a 100644 --- a/source/blender/makesrna/intern/rna_action_api.c +++ b/source/blender/makesrna/intern/rna_action_api.c @@ -36,6 +36,8 @@ #ifdef RNA_RUNTIME +#include "BKE_action.h" + #include "DNA_anim_types.h" #include "DNA_curve_types.h" diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index ccc9846600d..fee379cd285 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -41,6 +41,8 @@ #include "BKE_utildefines.h" #include "BKE_image.h" +#include "MEM_guardedalloc.h" + /* User should check if returned path exists before copying a file there. diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index cff77fbb34b..af94f0ccc6f 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -197,13 +197,15 @@ void rna_Object_update_data(struct bContext *C, struct PointerRNA *ptr); /* API functions */ +void RNA_api_action(StructRNA *srna); +void RNA_api_image(struct StructRNA *srna); void RNA_api_main(struct StructRNA *srna); +void RNA_api_material(StructRNA *srna); void RNA_api_mesh(struct StructRNA *srna); void RNA_api_object(struct StructRNA *srna); +void RNA_api_scene(struct StructRNA *srna); void RNA_api_ui_layout(struct StructRNA *srna); void RNA_api_wm(struct StructRNA *srna); -void RNA_api_scene(struct StructRNA *srna); -void RNA_api_material(StructRNA *srna); /* ID Properties */ diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index 9d4dad1fb5b..1db2f155d14 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -42,6 +42,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_material.h" #include "DNA_mesh_types.h" #include "DNA_scene_types.h" @@ -52,6 +53,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "MEM_guardedalloc.h" + static void rna_Mesh_calc_edges(Mesh *mesh) { CustomData edata; diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index f0a42987848..3c79c1cbc21 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -273,7 +273,7 @@ static void rna_Object_convert_to_triface(Object *ob, bContext *C, ReportList *r make_editMesh(sce, ob); /* select all */ - EM_set_flag_all(me->edit_mesh, SELECT); + EM_select_all(me->edit_mesh); convert_to_triface(me->edit_mesh, 0); diff --git a/source/blender/makesrna/intern/rna_pose_api.c b/source/blender/makesrna/intern/rna_pose_api.c index 9ac7713b2e9..42bb52d8544 100644 --- a/source/blender/makesrna/intern/rna_pose_api.c +++ b/source/blender/makesrna/intern/rna_pose_api.c @@ -45,8 +45,8 @@ void RNA_api_pose(StructRNA *srna) { - FunctionRNA *func; - PropertyRNA *parm; + /* FunctionRNA *func; */ + /* PropertyRNA *parm; */ } diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index f3cbb630df5..076fe38ed2f 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -38,8 +38,12 @@ #ifdef RNA_RUNTIME #include "BKE_scene.h" +#include "BKE_depsgraph.h" + #include "ED_object.h" +#include "WM_api.h" + static void rna_Scene_add_object(Scene *sce, ReportList *reports, Object *ob) { Base *base= object_in_scene(ob, sce); diff --git a/source/blender/python/SConscript b/source/blender/python/SConscript index 357d2c99d3b..d44cf762a0f 100644 --- a/source/blender/python/SConscript +++ b/source/blender/python/SConscript @@ -3,7 +3,7 @@ Import ('env') sources = env.Glob('intern/*.c') -incs = '. ../editors/include ../makesdna ../makesrna ../makesrna/intern ../blenlib ../blenkernel ../nodes' +incs = '. ../editors/include ../makesdna ../makesrna ../blenlib ../blenkernel ../nodes' incs += ' ../imbuf ../blenloader ../render/extern/include ../windowmanager' incs += ' #intern/guardedalloc #intern/memutil #extern/glew/include' incs += ' ' + env['BF_PYTHON_INC'] diff --git a/source/creator/creator.c b/source/creator/creator.c index e196213c945..6ecde9e5546 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -97,6 +97,10 @@ #include "binreloc.h" #endif +#ifdef WITH_UNIT_TEST +#include "tests/test.h" +#endif + // from buildinfo.c #ifdef BUILD_DATE extern char * build_date; diff --git a/source/creator/tests/alltest.c b/source/creator/tests/test.c similarity index 100% rename from source/creator/tests/alltest.c rename to source/creator/tests/test.c diff --git a/source/creator/tests/test.h b/source/creator/tests/test.h new file mode 100644 index 00000000000..37bdab301fc --- /dev/null +++ b/source/creator/tests/test.h @@ -0,0 +1,6 @@ +#ifndef _BF_UNIT_TEST +#define _BF_UNIT_TEST + +int run_tests(); + +#endif /* _BF_UNIT_TEST */ From c8af263e5d8d9d41a757e8438cdcf3b64d57e0c0 Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Mon, 14 Sep 2009 14:55:49 +0000 Subject: [PATCH 68/68] Reverted Mesh.verts from dynamic array since it breaks foreach_set used by import scripts. Did a few fixes in scripts to reflect recent RNA changes. --- release/io/export_obj.py | 6 +++--- release/io/export_x3d.py | 2 +- release/io/import_3ds.py | 3 ++- release/io/import_obj.py | 2 +- source/blender/makesrna/intern/rna_mesh.c | 3 +++ source/blender/python/intern/bpy_array.c | 2 +- source/blender/python/intern/bpy_rna.c | 8 +------- 7 files changed, 12 insertions(+), 14 deletions(-) diff --git a/release/io/export_obj.py b/release/io/export_obj.py index b2d4af7c517..d52ee8ec158 100644 --- a/release/io/export_obj.py +++ b/release/io/export_obj.py @@ -118,8 +118,8 @@ def write_mtl(scene, filename, copy_images): if mat: file.write('Ns %.6f\n' % ((mat.specular_hardness-1) * 1.9607843137254901) ) # Hardness, convert blenders 1-511 to MTL's file.write('Ka %.6f %.6f %.6f\n' % tuple([c*mat.ambient for c in worldAmb]) ) # Ambient, uses mirror colour, - file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.diffuse_reflection for c in mat.diffuse_color]) ) # Diffuse - file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.specular_reflection for c in mat.specular_color]) ) # Specular + file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.diffuse_intensity for c in mat.diffuse_color]) ) # Diffuse + file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.specular_intensity for c in mat.specular_color]) ) # Specular if hasattr(mat, "ior"): file.write('Ni %.6f\n' % mat.ior) # Refraction index else: @@ -129,7 +129,7 @@ def write_mtl(scene, filename, copy_images): # 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting. if mat.shadeless: file.write('illum 0\n') # ignore lighting - elif mat.specular_reflection == 0: + elif mat.specular_intensity == 0: file.write('illum 1\n') # no specular. else: file.write('illum 2\n') # light normaly diff --git a/release/io/export_x3d.py b/release/io/export_x3d.py index 30a4b1483b0..3661d78a343 100644 --- a/release/io/export_x3d.py +++ b/release/io/export_x3d.py @@ -770,7 +770,7 @@ class x3d_class: for i in range(alltexture): tex = alltextures[i] - if tex.type != 'IMAGE': + if tex.type != 'IMAGE' or tex.image == None: continue namemat = tex.name diff --git a/release/io/import_3ds.py b/release/io/import_3ds.py index 0269219b63d..f2dc99d5b7e 100644 --- a/release/io/import_3ds.py +++ b/release/io/import_3ds.py @@ -463,7 +463,8 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): ''' if contextMatrix_rot: - ob.matrix = [x for row in contextMatrix_rot for x in row] + # ob.matrix = [x for row in contextMatrix_rot for x in row] + ob.matrix = contextMatrix_rot # ob.setMatrix(contextMatrix_rot) importedObjects.append(ob) diff --git a/release/io/import_obj.py b/release/io/import_obj.py index 484be6d54b4..34f6575dba2 100644 --- a/release/io/import_obj.py +++ b/release/io/import_obj.py @@ -715,7 +715,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l # face_mapping= me.faces.extend([f[0] for f in faces], indexList=True) if verts_tex and me.faces: - me.add_uv_layer() + me.add_uv_texture() # me.faceUV= 1 # TEXMODE= Mesh.FaceModes['TEX'] diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index e1744bb3d23..f1a46b199b4 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -981,10 +981,13 @@ static void rna_def_mface(BlenderRNA *brna) // XXX allows creating invalid meshes prop= RNA_def_property(srna, "verts", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "v1"); RNA_def_property_array(prop, 4); + /* RNA_def_property_flag(prop, PROP_DYNAMIC); RNA_def_property_dynamic_array_funcs(prop, "rna_MeshFace_verts_get_length"); RNA_def_property_int_funcs(prop, "rna_MeshFace_verts_get", "rna_MeshFace_verts_set", NULL); + */ RNA_def_property_ui_text(prop, "Vertices", "Vertex indices"); prop= RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); diff --git a/source/blender/python/intern/bpy_array.c b/source/blender/python/intern/bpy_array.c index f11c95e7ed5..4459c7c313f 100644 --- a/source/blender/python/intern/bpy_array.c +++ b/source/blender/python/intern/bpy_array.c @@ -1,5 +1,5 @@ /** - * + * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 8395e5c82e4..49105422325 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -545,9 +545,6 @@ int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, void *data, PyObject *v int len = RNA_property_array_length(ptr, prop); if (len > 0) { - /* char error_str[512]; */ - int ok= 1; - #ifdef USE_MATHUTILS if(MatrixObject_Check(value)) { MatrixObject *mat = (MatrixObject*)value; @@ -559,10 +556,7 @@ int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, void *data, PyObject *v PyErr_Format(PyExc_TypeError, "%.200s RNA array assignment expected a sequence instead of %.200s instance.", error_prefix, Py_TYPE(value)->tp_name); return -1; } - /* done getting the length */ - ok= pyrna_py_to_array(ptr, prop, data, value, error_prefix); - - if (!ok) { + else if (!pyrna_py_to_array(ptr, prop, data, value, error_prefix)) { /* PyErr_Format(PyExc_AttributeError, "%.200s %s", error_prefix, error_str); */ return -1; }