是时候宣布我的另一个项目了…… - 哆啦比猫的技术瞎扯 - Arch Linux · ドラえもん · 实时绘制
是时候宣布我的另一个项目了……
……那就是:FrinX 游戏引擎,开源的 GTA(侠盗车手)式游戏引擎
一直都想写,现在终于有能力写了;不过目前还在早期的 alpha 阶段,没有任何可玩性。
由于今天终于把困扰我已久动画功能搞定了,所以来此宣布一下;顺便做一下动画导出技术总结(这个是最让我纠结的)
我也算是 Blender 的老用户了,但对其 python API 一直很不爽,因为我总是找不到我想要的东西(e.g. 动画数据,action 数据,etc.),而且那 document 真的是 sucks。
今天是难得的双休中的一天,所以静下心来把 BLender 2.6 内置的 exporter 的源代码通读了一番,终于知道要怎样导出动画了。
废话不多说,上源代码:
################################################ # # 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.
本作品采用知识共享署名·非商业性使用·相同方式共享 3.0 中国大陆许可协议进行许可。
文中凡未特殊声明且未声明为引用的代码均以 MIT 协议授权。
blog comments powered by Disqus