OSDN Git Service

implement shape key
[meshio/pymeshio.git] / blender26-meshio / export_mqo.py
1 #!BPY
2 # coding: utf-8
3
4 """
5 Name: 'Metasequoia (.mqo)...'
6 Blender: 245
7 Group: 'Export'
8 Tooltip: 'Save as Metasequoia MQO File'
9 """
10 __author__= 'ousttrue'
11 __url__ = ["http://gunload.web.fc2.com/blender/"]
12 __version__= '2.4'
13 __bpydoc__ = """\
14 This script is an exporter to MQO file format.
15
16 Usage:
17
18 Run this script from "File->Export" menu.
19
20 20080128:
21 20100518: refactoring.
22 20100606: integrate 2.4 and 2.5.
23 20100626: refactoring.
24 20100710: add [apply_modifier] option(2.5 only).
25 20100714: remove shape_key when apply_modifier. fix material.
26 20100724: update for Blender2.53.
27 20101005: update for Blender2.54.
28 20101228: update for Blender2.55.
29 20110429: update for Blender2.57b.
30 """
31
32 bl_addon_info = {
33         'category': 'Import/Export',
34         'name': 'Export: Metasequioa Model Format (.mqo)',
35         'author': 'ousttrue',
36         'version': (2, 1),
37         'blender': (2, 5, 3),
38         'location': 'File > Export',
39         'description': 'Export to the Metasequioa Model Format (.mqo)',
40         'warning': '', # used for warning icon and text in addons panel
41         'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',
42         'tracker_url': 'http://sourceforge.jp/ticket/newticket.php?group_id=5081',
43         }
44
45 import os
46 import sys
47
48 class MQOMaterial(object):
49     __slots__=[
50             'name', 'shader', 'r', 'g', 'b', 'a',
51             'dif', 'amb', 'emi',
52             ]
53     def __init__(self, name, shader=3):
54         self.name=name
55         self.shader=shader
56         self.r=0.5
57         self.g=0.5
58         self.b=0.5
59         self.a=1
60         self.dif=1
61         self.amb=0
62         self.emi=0
63
64     def __str__(self):
65         return "\"%s\" shader(%d) col(%f %f %f %f) dif(%f) amb(%f) emi(%f)" % (
66                 self.name, self.shader, self.r, self.g, self.b, self.a,
67                 self.dif, self.amb, self.emi
68                 )
69
70
71 # for 2.5
72 import bpy
73
74 # wrapper
75 from . import bl
76
77 def materialToMqo(m):
78     material=MQOMaterial(m.name, 3)
79     material.r=m.diffuse_color[0]
80     material.g=m.diffuse_color[1]
81     material.b=m.diffuse_color[2]
82     material.a=m.alpha
83     material.amb=m.ambient
84     material.emi=m.emit
85     return material
86
87 def apply_transform(vec, matrix):
88     x, y, z = vec
89     xloc, yloc, zloc = matrix[3][0], matrix[3][1], matrix[3][2]
90     return    x*matrix[0][0] + y*matrix[1][0] + z*matrix[2][0] + xloc,\
91             x*matrix[0][1] + y*matrix[1][1] + z*matrix[2][1] + yloc,\
92             x*matrix[0][2] + y*matrix[1][2] + z*matrix[2][2] + zloc
93
94 def convert_to_mqo(vec):
95     return vec.x, vec.z, -vec.y
96
97
98 class OutlineNode(object):
99     __slots__=['o', 'children']
100     def __init__(self, o):
101         self.o=o
102         self.children=[]
103
104     def __str__(self):
105         return "<Node %s>" % self.o
106
107
108 class ObjectInfo(object):
109     __slots__=['object', 'depth', 'material_map']
110     def __init__(self, o, depth):
111         self.object=o
112         self.depth=depth
113         self.material_map={}
114
115     def __str__(self):
116         return "<ObjectInfo %d %s>" % (self.depth, self.object)
117
118
119 class MqoExporter(object):
120     __slots__=["materials", "objects", 'scale', 'apply_modifier',]
121     def __init__(self, scale, apply_modifier):
122         self.objects=[]
123         self.materials=[]
124         self.scale=scale
125         self.apply_modifier=apply_modifier
126
127     def setup(self, scene):
128         # 木構造を構築する
129         object_node_map={}
130         for o in scene.objects:
131             object_node_map[o]=OutlineNode(o)
132         for node in object_node_map.values():
133             if node.o.parent:
134                 object_node_map[node.o.parent].children.append(node)
135
136         # ルートを得る
137         root=object_node_map[scene.objects.active]
138
139         # 情報を集める
140         if root.o.type.upper()=='EMPTY':
141             # depth調整 
142             for node in root.children:
143                 self.__setup(node)
144         else:
145             self.__setup(root)
146
147     def __setup(self, node, depth=0):
148         info=ObjectInfo(node.o, depth)
149         self.objects.append(info)
150         if node.o.type.upper()=='MESH':
151             # set material index
152             for i, m in enumerate(node.o.data.materials):
153                 info.material_map[i]=self.__getOrAddMaterial(m)
154         # recursive
155         for child in node.children:
156             self.__setup(child, depth+1)
157             
158     def __getOrAddMaterial(self, material):
159         for i, m in enumerate(self.materials):
160             if m==material:
161                 return i
162         index=len(self.materials)
163         self.materials.append(material)
164         return index
165
166     def write(self, path):
167         bl.message("open: "+path)
168         io=bl.Writer(path, 'cp932')
169         self.__write_header(io)
170         self.__write_scene(io)
171         print("Writing MaterialChunk")
172         self.__write_materials(io, os.path.dirname(path))
173         print("Writing ObjectChunk")
174         for info in self.objects:
175             self.__write_object(io, info)
176         io.write("Eof\r\n")
177         io.flush()
178         io.close()
179
180     def __write_header(self, io):
181         io.write("Metasequoia Document\r\n")
182         io.write("Format Text Ver 1.0\r\n")
183         io.write("\r\n")
184
185     def __write_scene(self, io):
186         print("Writing SceneChunk")
187         io.write("Scene {\r\n")
188         io.write("}\r\n")
189
190     def __write_materials(self, io, dirname):
191         # each material    
192         io.write("Material %d {\r\n" % (len(self.materials)))
193         for m in self.materials:
194             io.write(str(materialToMqo(m)))
195             # ToDo separated alpha texture
196             for filename in bl.material.eachTexturePath(m):
197                 if len(dirname)>0 and filename.startswith(dirname):
198                     # 相対パスに変換する
199                     filename=filename[len(dirname)+1:]
200                 io.write(" tex(\"%s\")" % filename)
201                 break
202             io.write("\r\n") 
203         # end of chunk
204         io.write("}\r\n") 
205
206     def __write_object(self, io, info):
207         print(info)
208
209         obj=info.object
210         if obj.type.upper()=='MESH' or obj.type.upper()=='EMPTY':
211             pass
212         else:
213             print(obj.type)
214             return
215
216         io.write("Object \""+obj.name+"\" {\r\n")
217
218         # depth
219         io.write("\tdepth %d\r\n" % info.depth)
220
221         # mirror
222         if not self.apply_modifier:
223             if bl.modifier.hasType(obj, 'MIRROR'):
224                     io.write("\tmirror 1\r\n")
225                     io.write("\tmirror_axis 1\r\n")
226
227         if obj.type.upper()=='MESH':
228             # duplicate and applyMatrix
229             copyMesh, copyObj=bl.object.duplicate(obj)
230             # apply transform
231             """
232             copyObj.scale=obj.scale
233             bpy.ops.object.scale_apply()
234             copyObj.rotation_euler=obj.rotation_euler
235             bpy.ops.object.rotation_apply()
236             copyObj.location=obj.location
237             bpy.ops.object.location_apply()
238             """
239             copyMesh.transform(obj.matrix_world)
240             # apply modifier
241             if self.apply_modifier:
242                 # remove shape key
243                 while bl.object.hasShapeKey(copyObj):
244                     bpy.ops.object.shape_key_remove()
245                 for m in [m for m in copyObj.modifiers]:
246                     if m.type=='SOLIDFY':
247                         continue
248                     elif m.type=='ARMATURE':
249                         bpy.ops.object.modifier_apply(modifier=m.name)
250                     elif m.type=='MIRROR':
251                         bpy.ops.object.modifier_apply(modifier=m.name)
252                     else:
253                         print(m.type)
254             # write mesh
255             self.__write_mesh(io, copyMesh, info.material_map)
256             bl.object.delete(copyObj)
257
258         io.write("}\r\n") # end of object
259
260     def __write_mesh(self, io, mesh, material_map):
261         # vertices
262         io.write("\tvertex %d {\r\n" % len(mesh.vertices))
263         for vert in mesh.vertices:
264             x, y, z = convert_to_mqo(vert.co)
265             io.write("\t\t%f %f %f\r\n" % 
266                     (x*self.scale, y*self.scale, z*self.scale)) # rotate to y-up
267         io.write("\t}\r\n")
268
269         # faces
270         io.write("\tface %d {\r\n" % len(mesh.faces))
271         for i, face in enumerate(mesh.faces):
272             count=bl.face.getVertexCount(face)
273             # V
274             io.write("\t\t%d V(" % count)
275             for j in reversed(bl.face.getVertices(face)):
276                 io.write("%d " % j)
277             io.write(")")
278             # mat
279             if len(mesh.materials):
280                 io.write(" M(%d)" % 
281                         material_map[bl.face.getMaterialIndex(face)])
282             # UV
283             if bl.mesh.hasUV(mesh) and bl.mesh.hasFaceUV(mesh, i, face):
284                 io.write(" UV(")
285                 for uv in reversed(bl.mesh.getFaceUV(mesh, i, face, count)):
286                     # reverse vertical value
287                     io.write("%f %f " % (uv[0], 1.0-uv[1])) 
288                 io.write(")")
289             io.write("\r\n")
290         io.write("\t}\r\n") # end of faces
291
292
293 def _execute(filepath='', scale=10, apply_modifier=False):
294     if bl.object.getActive():
295         exporter=MqoExporter(scale, apply_modifier)
296         exporter.setup(bl.scene.get())
297         exporter.write(filepath)
298     else:
299         bl.message('no active object !')
300