是时候宣布我的另一个项目了…… - 哆啦比猫的技术瞎扯 - Arch Linux · ドラえもん · 实时绘制
是时候宣布我的另一个项目了……
……那就是:FrinX 游戏引擎,开源的 GTA(侠盗车手)式游戏引擎
一直都想写,现在终于有能力写了;不过目前还在早期的 alpha 阶段,没有任何可玩性。
由于今天终于把困扰我已久动画功能搞定了,所以来此宣布一下;顺便做一下动画导出技术总结(这个是最让我纠结的)
我也算是 Blender 的老用户了,但对其 python API 一直很不爽,因为我总是找不到我想要的东西(e.g. 动画数据,action 数据,etc.),而且那 document 真的是 sucks。
今天是难得的双休中的一天,所以静下心来把 BLender 2.6 内置的 exporter 的源代码通读了一番,终于知道要怎样导出动画了。
废话不多说,上源代码:
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 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 | ################################################ # # FrinX Object Exporter # For FrinX 0.17 or later version. # # Copyright (C) eXerigumo Clanjor, 2010-2012. # Licensed under GPLv2. ABSOLUTELY NO WARRANTY! # ######################################## # # User Configuration # OUTPUT_FILE = "/tmp/object.fo" FRAME_STEP = 10 ############################################# # DON'T MODIFY CODES BELOW UNLESS # # YOU KNOW WHAT YOU'RE DOING! # ############################################# import bpy; from struct import * ######################################## # # Error Prompt # class MenuShowError(bpy.types.Menu): bl_label = "Error" bl_idname = "OBJECT_MT_menu_show_error" def draw( self , context): layout = self .layout lines = self .msg.split( "\n" ); showIcon = True for line in lines: if showIcon: layout.label(text = line, icon = 'ERROR' ) showIcon = False else : layout.label(text = line) def showError(msg): MenuShowError.msg = msg; bpy.utils.register_class(MenuShowError) bpy.ops.wm.call_menu(name = MenuShowError.bl_idname) bpy.utils.unregister_class(MenuShowError) ######################################## # # Exporter # def fx_export( file ): meshes = bpy.data.meshes arms = bpy.data.armatures objects = bpy.data.objects actions = bpy.data.actions scene = bpy.context.scene def reallen(d): cnt = 0 for i in d: if i.users > 0 : # exclude deleted datas cnt + = 1 return cnt def export_face(m): file .write(pack( "H" , len (m.faces))) for f in m.faces: if len (f.vertices) ! = 3 : showError( "Must only contain triangular faces!\n" \ "It is: " + str ( list (f.vertices))) return # face triangle vertices id file .write(pack( "HHH" , f.vertices[ 0 ], f.vertices[ 1 ], f.vertices[ 2 ])) # uv coordinates if len (m.uv_textures) = = 0 : showError( "Must have a UV texture!" ) return uv = m.uv_textures[ 0 ].data[f.index].uv_raw for i in range ( 6 ): file .write(pack( "f" , uv[i])) def export_action(ob, arm, act, frame_time): set_to_static_action(arm) act_start, act_end = act.frame_range act_start = int (act_start) act_end = int (act_end) frame_cnt = int ((act_end - act_start) / FRAME_STEP) + 1 # write action name file .write(convert_string(act.name)) # write frame count file .write(pack( "B" , frame_cnt)) # the real staff... arm.animation_data.action = act for i in range (act_start, act_end, FRAME_STEP): scene.frame_set(i) m = ob.to_mesh(scene, True , 'PREVIEW' ) for v in m.vertices: # note that i have switched the y- and z-axis. file .write(pack( "fff" , v.co[ 0 ], - v.co[ 2 ], v.co[ 1 ])) meshes.remove(m) def set_to_static_action(arm): arm.animation_data.action = actions[ 'static' ] scene.frame_set(actions[ 'static' ].frame_range[ 0 ]) def convert_string(s): rv = pack( 'B' , len (s)) for ch in s: rv + = bytes(ch, encoding = "utf8" ) return rv def get_ob_by_type(t): for ob in bpy.data.objects: if ob. type = = t \ and ob.users > 0 : # exclude deleted objects return ob # only support one mesh # XXX, exclude collision boundary mesh if reallen(meshes) ! = 1 : showError( "There must be one (and only one) mesh!" ); return # only support one armature if reallen(arms) ! = 1 : showError( "There must be one (and only one) armature!" ); return # there must be at least a "static" action, # or no vertices data will be exported, which # is sure to cause error. if actions.find( "static" ) = = - 1 \ or actions[ "static" ].users = = 0 : showError( "There must be a \"static\" action!" ); return # pre-exporting-checking done, open file for exporting file = open ( file , "wb" ) ob = get_ob_by_type( 'MESH' ) arm = get_ob_by_type( 'ARMATURE' ) # XXX, exclude collision boundary mesh export_face(ob.data) # write vertices count file .write(pack( "H" , len (ob.data.vertices))) # write frame time frame_time = int ( 1000 / scene.render.fps / scene.render.fps_base) file .write(pack( "H" , FRAME_STEP * frame_time)) # write action count file .write(pack( "B" , reallen(actions))) for act in actions: if act.users > 0 : export_action(ob, arm, act, frame_time) #set_to_static_action(arm) file .close() fx_export(OUTPUT_FILE); |
对于无论如何都找不到的 common dialogs(e.g. 消息框),我表示很蛋疼。好吧,既然它不提供,我就自己写了一个,亦即代码开头的“Error Prompt”。
对于导出代码,我觉得还是蛮清晰的,不想多说什么。我只想说一句:Blender Python API Sucks!!!!!!!!!!!!!!
For FrinX,更多信息请参见:frinxge.tk FrinX Wiki
凡未特殊声明(转载/翻译),所有文章均为原创。
by Giumo Xavier Clanjor (哆啦比猫/兰威举), 2010-2019.

文中凡未特殊声明且未声明为引用的代码均以 MIT 协议授权。