是时候宣布我的另一个项目了…… - 哆啦比猫的技术瞎扯 - 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