Форум сайта python.su
Приветствую форумчан, и прошу помощи с использованием скрипта экспорта файлов из Blender в War Thunder CDK.
Конечно логичнее спрашивать у разработчиков скрипта. Спрашивал, второй месяц нет ответа, а моя тема потихоньку принимает вид блога и собирает просмотры.
Имею очень поверхностное понимание в программировании.
Блендеровские скрипты пишутся на питоне, по этому здесь создаю эту тему.
Суть вороса…
Проект War Thunder CDK заточен под 3DsMax.
В комплекте идет куча плагинов для Максов, разных версий и разрядности.
Как я смог понять. Плагин для Макса невозможно самостоятельно переделать для блендер.
Для этого нужно знать как работает Максов плагин.
Но в War Thunder CDK рядом с папкой набитой Максовыми плагинами для разных вверсий, есть папка blender_plugins.
В папке редмишка
Copy whole this folder to blender (“…\Blender Foundation\Blender\2.71\scripts\addons\io_export_dag” )
1. В параметрах текстуры (custom properties) должен быть указан ее тип (параметр - type, значение из dagorShaders.cfg - “diffuse”, “CUBE environment”, “normalmap” и т.п.
2. В параметрах материала (custom properties) казываются необходимые параметры для его настройки, согласно dagorShaders.cfg
Сам скрипт
# http://live.warthunder.com/assistance_agreement/ bl_info = {"name": "Export to Dagor Engine", "description": "Script exports mesh to Dagor Engine", "author": "http://live.warthunder.com/assistance_agreement/", "version": (1, 0), "blender": (2, 68, 0), "location": "File > Export", "wiki_url": "", "tracker_url": "http://live.warthunder.com/assistance_agreement/", "category": "Import-Export"} # Standard library imports import sys import os # Blender imports import bpy from bpy_extras.io_utils import ImportHelper from bpy.props import IntProperty, StringProperty from bpy.types import AddonPreferences, Operator, Panel import mathutils, math, struct from mathutils import * import subprocess SUPPORTED_TYPES = ('MESH')#,'CURVE','EMPTY','TEXT','CAMERA','LAMP') class BLKWriter: file = None ident = 0 def getIdent(self): s = "" for x in range(0, self.ident): s += " " return s def writeHeader(self): self.file.write('HEAD:t="BLK(float)"\n') return def open( self, filename ): self.file = open(filename, "wt") self.writeHeader() return def close( self ): self.file.flush() self.file.close() def beginBlock(self, name): self.file.write('\n%s%s{\n' % ( self.getIdent(), name ) ) self.ident += 1 return def endBlock(self ): self.ident -= 1 if self.ident < 0 : #assert self.ident = 0 self.file.write('%s}\n' % self.getIdent() ) return def writeStr(self, name, str): self.file.write('%s%s:t="%s"\n' % ( self.getIdent(), name, str ) ) return def writeInt(self, name, val): self.file.write('%s%s:i=%d\n' % ( self.getIdent(), name, val ) ) return def writeFloat(self, name, val): self.file.write('%s%s:r=%g\n' % ( self.getIdent(), name, val ) ) return def writeBool(self, name, val): self.file.write('%s%s:b=%s\n' % ( self.getIdent(), name, "yes" if val else "no" ) ) return def writeTM(self, name, m): self.file.write('%s%s:m=[[%f, %f, %f] [%f, %f, %f] [%f, %f, %f] [%f, %f, %f]]\n' % ( self.getIdent(), name, m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2] ) ) #self.file.write('%s%s:m=[[%f, %f, %f] [%f, %f, %f] [%f, %f, %f] [%f, %f, %f]]\n' % ( self.getIdent(), name, m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[0][3], m[1][3], m[2][3] ) ) #self.file.write('%s%s:m=[[%f, %f, %f] [%f, %f, %f] [%f, %f, %f] [%f, %f, %f]]\n' % ( self.getIdent(), name, m[0][0], m[2][0], m[1][0], m[0][1], m[2][1], m[1][1], m[0][2], m[2][2], m[1][2], m[0][3], m[2][3], m[1][3] ) ) #self.file.write('%s\n' % m ) return class MaterialExp: def __init__(self): self.index = -1 self.mat = None class MeshExp: def __init__(self): self.vertexes = list() self.faces = list() self.uv = list() self.normals = list() self.normals_per_face_ver = list() self.normals_ver = list() self.normals_faces = list() def addNormal( self, normal ): for i,n in enumerate(self.normals_ver): if n == normal: return i index = len( self.normals_ver ) self.normals_ver.append( normal ) return index class NodeExp: def __init__(self): self.name = "" self.id = -1 self.parent_id = -1 #self.parent = None self.child = list() self.mesh = None self.tm = None self.wtm = None self.renderable = True class DagExporter(Operator, ImportHelper): bl_idname = "dag.exporter" bl_label = "Dagor Engine (.dag)" bl_options = {'REGISTER', 'UNDO'} file = None nodes = None last_index = 0 writer = None materials = {} def __init__(self): self.writer = BLKWriter() self.last_index = 0 self.nodes = list() self.materials = {} # ImportHelper mixin class uses this filename_ext = ".dag" filter_glob = StringProperty(default="*.dag", options={'HIDDEN'}) def createRootNode( self ): node = NodeExp() node.id = 65535; node.parent_id = -1; node.tm = Matrix() node.tm.identity() node.wtm = Matrix() node.wtm.identity() node.renderable=False self.nodes.append( node ) return node def writeNode(self, node ): self.writer.beginBlock("node") self.writer.writeStr( "name", node.name ) self.writer.writeInt( "id", node.id ) self.writer.writeInt( "parent_id", node.parent_id ) self.writer.writeBool( "renderable", node.renderable ) self.writer.writeBool( "castshadow", False ) self.writer.writeBool( "rcvshadow", False ) self.writer.writeTM( "tm", node.tm ) if node.mesh == None: self.writer.endBlock() return self.writer.beginBlock("vert_data") for i,v in enumerate( node.mesh.vertexes): self.writer.writeStr( "vert", str( '[%d] x:%f y:%f z:%f' % (i, v.x, v.y, v.z ) ) ); self.writer.endBlock() self.writer.beginBlock("face_data") for i,f in enumerate(node.mesh.faces): self.writer.writeStr( "face", str( '[%d] i1:%d i2:%d i3:%d smgrp:%d mat: %d' % (i, f[ 0 ], f[ 1 ], f[ 2 ], 0, f[ 3 ] ) ) ); self.writer.endBlock() self.writer.beginBlock("normal_vert") for i,n in enumerate(node.mesh.normals_ver): self.writer.writeStr( "nvert", str( '[%d] x:%f y:%f z:%f' % (i, n[ 0 ], n[ 1 ], n[ 2 ] ) ) ); self.writer.endBlock() self.writer.beginBlock("normal_faces") for i,f in enumerate(node.mesh.normals_faces): self.writer.writeStr( "nface", str( '[%d] i1:%d i2:%d i3:%d' % (i, f[ 0 ], f[ 1 ], f[ 2 ] ) ) ); self.writer.endBlock() if len(node.mesh.uv) > 0: self.writer.beginBlock("uv_data") for channel in node.mesh.uv: #self.writer.writeStr( str('%s\n' % channel ) ); self.writer.beginBlock("channel") self.writer.beginBlock("uv_vert") for i,f in enumerate(channel): self.writer.writeStr( "uv", str( '[%d] x:%f y:%f' % (i, f[0],1.0-f[1] ) ) ); #self.writer.writeStr( str('%s\n' % ch ) ); self.writer.endBlock() self.writer.endBlock() self.writer.endBlock() self.writer.endBlock() return def dumpObjectData( self, obj, node, scene ): #mesh_calc_normals() if obj.type != 'MESH': try: me = obj.to_mesh(scene, True, "PREVIEW") except: me = None is_tmp_mesh = True else: me = obj.data if not me.tessfaces and me.polygons: me.calc_tessface() is_tmp_mesh = False has_uv = bool(me.tessface_uv_textures) has_vcol = bool(me.tessface_vertex_colors) if has_uv == False : self.file.write("Object hasn't UV mapping\n" ) # export data if me is not None: if len( obj.material_slots ) == 0: print( "Need assign material to object \"%s\"\n" % obj.name ) return me.calc_normals_split() materials_indexes = [ list() for p in range( len( obj.material_slots ) )] for i,slot in enumerate( obj.material_slots ): self.file.write(" Material %s\n" % slot ) #self.file.write(" Mat %s\n" % slot.material.name ) if slot.material.name not in self.materials: mat_exp = MaterialExp() mat_exp.index = len(self.materials) mat_exp.mat = slot.material self.materials[ slot.material.name ] = mat_exp val = self.materials.get( slot.material.name ) if val != None: materials_indexes[i] = val.index else: materials_indexes[i] = -1 #print("Mesh object:", me.name) self.file.write(str('mesh %s\n' % me) ) meshExp = MeshExp() for v in me.vertices: #self.file.write(str('vert %s %s %s\n' % (v.co.x, v.co.y, v.co.z) ) ) meshExp.vertexes.append( v.co ) meshExp.normals.append( -v.normal ); meshExp.uv = [ list() for p in range( len( me.tessface_uv_textures ) )] for i, f in enumerate( me.tessfaces ): #self.file.write(str('vf %s %s %s\n' % (f.vertices[0], f.vertices[1], f.vertices[2] ) ) ) useSmooth = f.use_smooth self.file.write(str('mat %d\n' % f.material_index ) ) fexp = [] fexp.append( f.vertices[0] ) fexp.append( f.vertices[2] ) fexp.append( f.vertices[1] ) fexp.append( materials_indexes[ f.material_index ] ); meshExp.faces.append( fexp ) norm = [] if useSmooth: norm.append( -meshExp.normals[ f.vertices[0] ] ) norm.append( -meshExp.normals[ f.vertices[2] ] ) norm.append( -meshExp.normals[ f.vertices[1] ] ) else: norm.append( f.normal ) norm.append( f.normal ) norm.append( f.normal ) meshExp.normals_per_face_ver.append( norm ) norm_face_ind = [] for nv in norm: norm_face_ind.append( meshExp.addNormal( nv ) ) meshExp.normals_faces.append( norm_face_ind ) for n, layer in enumerate( me.tessface_uv_textures ): uv = layer.data[i].uv #self.file.write(str('uv_face[%d] (%f,%f), (%f,%f), (%f,%f)\n' % (n, uv[0][0], uv[0][1],uv[1][0], uv[1][0],uv[2][0],uv[2][1]) ) ) meshExp.uv[ n ].append( uv[0] ) meshExp.uv[ n ].append( uv[2] ) meshExp.uv[ n ].append( uv[1] ) if len(f.vertices) == 4: # if quad meshExp.uv[ n ].append( uv[2] ) meshExp.uv[ n ].append( uv[0] ) meshExp.uv[ n ].append( uv[3] ) self.file.write(str('smth %s\n' % f.use_smooth ) ) if len(f.vertices) == 4: # if quad self.file.write(str('vf %s %s %s\n' % (f.vertices[2], f.vertices[3], f.vertices[0] ) ) ) fexp = [] fexp.append( f.vertices[2] ) fexp.append( f.vertices[0] ) fexp.append( f.vertices[3] ) fexp.append( materials_indexes[f.material_index] ); meshExp.faces.append( fexp ) #self.file.write(str('uv_face (%f,%f), (%f,%f), (%f,%f)\n' % (uv[2][0], uv[2][1],uv[3][0], uv[3][0],uv[0][0],uv[0][1]) ) ) norm = [] if useSmooth: norm.append( -meshExp.normals[ f.vertices[2] ] ) norm.append( -meshExp.normals[ f.vertices[0] ] ) norm.append( -meshExp.normals[ f.vertices[3] ] ) else: norm.append( f.normal ) norm.append( f.normal ) norm.append( f.normal ) meshExp.normals_per_face_ver.append( norm ) norm_face_ind = [] for nv in norm: norm_face_ind.append( meshExp.addNormal( nv ) ) meshExp.normals_faces.append( norm_face_ind ) node.mesh = meshExp if is_tmp_mesh: bpy.data.meshes.remove(me) return def recursObjects( self, objs, parent, scene): objects = (ob for ob in objs if ob.is_visible(scene) and ob.type in SUPPORTED_TYPES) for o in objects: self.last_index += 1; node = NodeExp() node.name = o.name node.id = self.last_index; node.parent_id = parent.id mParentInv = parent.wtm.copy() mParentInv.invert() node.wtm = o.matrix_world; node.tm = mParentInv * node.wtm; #-------------------------------------- if parent.parent_id == -1 : tmp_r = node.tm[1].copy() node.tm[1] = node.tm[2].copy() node.tm[2] = tmp_r.copy() node.tm.transpose() #self.file.write( str( '(%s) mat: %s\n' % ( node.name, node.tm ) ) ) #-------------------------------------- #if o.name == 'gear_c' : # self.file.write( str( 'mat: %s\n' % o.matrix_world ) ) parent.child.append( node ) print('Exporting %s, parent%s\n' % ( node.name, parent.name) ) self.nodes.append( node ) self.dumpObjectData( o, node, scene ) self.recursObjects( o.children, node, scene ) return def writeParam(self,params): for key in params: if key[0] == "_RNA_UI": continue self.writer.beginBlock( "param") self.writer.writeStr( "name", key[0] ) self.writer.writeStr( "value", key[1] ) self.writer.endBlock() def writeMaterail(self, mat, id): self.writer.beginBlock( "material") self.writer.writeInt( "id", id ) self.writer.writeStr( "name", mat.name ) self.writer.writeStr( "diffuse", str( 'r:%f g:%f b:%f' % (mat.diffuse_color.r, mat.diffuse_color.g, mat.diffuse_color.b)) ) self.writer.writeStr( "specular", str( 'r:%f g:%f b:%f' % (mat.specular_color.r, mat.specular_color.g, mat.specular_color.b)) ) for mtex_slot in mat.texture_slots: if mtex_slot: #self.file.write( "test: %s" % str( mtex_slot.uv_layer ) ) if hasattr(mtex_slot.texture , 'image'): self.writer.beginBlock( "texture" ) self.writer.writeStr( "filepath", mtex_slot.texture.image.filepath ) self.writeParam( mtex_slot.texture.items() ) self.writer.endBlock() self.writeParam( mat.items() ) self.writer.endBlock() def writeBlk(self, filename): self.writer.open( filename ) for n in self.nodes: self.writeNode( n ) for key, value in self.materials.items(): self.writeMaterail( value.mat, value.index ) print(key, value) self.writer.close() return def execute(self, context): filepath = self.filepath filepath = bpy.path.ensure_ext(filepath, self.filename_ext) print("Blender Version {}.{}.{}".format(bpy.app.version[0], bpy.app.version[1], bpy.app.version[2])) print("Filepath: {}".format(filepath)) scene = context.scene self.file = open(filepath+".log", "wt") objs = (ob for ob in scene.objects if ob.is_visible(scene) and ob.type in SUPPORTED_TYPES and ob.parent == None) par_node = self.createRootNode() self.recursObjects( objs, par_node, scene ) path_blk = filepath + ".blk" print( 'path_blk=%s' % path_blk ); self.writeBlk( path_blk ) prev_path = os.getcwd() new_path = os.path.dirname( os.path.realpath(__file__) ) os.chdir( new_path ) print(new_path); try: return_code = subprocess.call(["blk2dag.exe", path_blk, filepath ]) print( 'ret=%d' % return_code ); except IOError: print( 'convert failed' ); os.chdir( prev_path ) self.file.flush() self.file.close() return {'FINISHED'} def menu_func_import(self, context): self.layout.operator(DagExporter.bl_idname,text=DagExporter.bl_label) def register(): bpy.utils.register_module(__name__) bpy.types.INFO_MT_file_export.append(menu_func_import) def unregister(): bpy.utils.unregister_module(__name__) bpy.types.INFO_MT_file_export.remove(menu_func_import) if __name__ == "__main__": register()
Офлайн
PovertТут питон ни при чём. В этом скрипте используется API блендера, которое сложное само по себе. Даже если питон выучишь, знание апи блендера от этого не появится.
Посоветуйте хоть какой нибудь учебник по питону “внятный” для моего уровня.
Офлайн
Питон учится за неделю, по любому учебнику. Вы потратили 2 месяца. Скорее всего программирование это не ваше. Проще заказать работу специалисту.
Офлайн
Блендер хобби. Захотелось смоделить чтото по масштабнее чем картинка или моделька.
Моделю в Блендер. Модель с UV, экспортирую в fbx (не портится масштаб). Потом в Максе навешиваю материалы и сглаживание и перегоняю в *.dag.
Интерфейс Макса для меня “не логичный”. Трудные моменты уже “прояндексил”, ритуал становится привычным.
Но нудное тыкание мышкой по туче кнопочек в Максе… По этому и заморочился с экспортом напрямую с Блендера.
Спасибо за подсказку про Application Programming Interface, попробую почитать блендерскую вики на эту тему, может что и станет понятнее.
Офлайн