是时候宣布我的另一个项目了…… - 哆啦比猫's Blog - I'm an ArchLinuxer

是时候宣布我的另一个项目了……

哆啦比猫 posted @ 2012年3月17日 23:19 in 项目 with tags c game linux frinx gta , 1913 阅读

……那就是: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, 2011, 2012, 2013, 2014, 2015-2016 and 2017.
知识共享许可协议本作品采用知识共享署名·非商业性使用·相同方式共享 3.0 中国大陆许可协议进行许可。
文中凡未特殊声明且未声明为引用的代码均以 MIT 协议授权。

blog comments powered by Disqus
© 2010, 2011, 2012, 2013, 2014, 2015-2016 and 2017 Giumo Xavier Clanjor (哆啦比猫/兰威举).
© 2013, 2014, 2015-2016 and 2017 The Dark Colorscheme Designed by Giumo Xavier Clanjor (哆啦比猫/兰威举).
知识共享署名·非商业性使用·相同方式共享 3.0 中国大陆许可协议
| © 2007 LinuxGem | Design by Matthew "Agent Spork" McGee