OSDN Git Service

add README
[meshio/pymeshio.git] / 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 0.1 20080128:
21 0.2 20100518: refactoring.
22 0.3 20100606: integrate 2.4 and 2.5.
23 0.4 20100626: refactoring.
24 0.5 20100710: add [apply_modifier] option(2.5 only).
25 0.6 20100714: remove shape_key when apply_modifier. fix material.
26 2.0 20100724: update for Blender2.53.
27 2.1 20101005: update for Blender2.54.
28 2.2 20101228: update for Blender2.55.
29 2.4 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 def isBlender24():
49     return sys.version_info[0]<3
50
51
52 class MQOMaterial(object):
53     __slots__=[
54             'name', 'shader', 'r', 'g', 'b', 'a',
55             'dif', 'amb', 'emi',
56             ]
57     def __init__(self, name, shader=3):
58         self.name=name
59         self.shader=shader
60         self.r=0.5
61         self.g=0.5
62         self.b=0.5
63         self.a=1
64         self.dif=1
65         self.amb=0
66         self.emi=0
67
68     def __str__(self):
69         return "\"%s\" shader(%d) col(%f %f %f %f) dif(%f) amb(%f) emi(%f)" % (
70                 self.name, self.shader, self.r, self.g, self.b, self.a,
71                 self.dif, self.amb, self.emi
72                 )
73
74
75 if isBlender24():
76     # for 2.4
77     import Blender
78     from Blender import Mathutils
79     import bpy
80
81     # wrapper
82     import bl24 as bl
83
84     def materialToMqo(m):
85         material=MQOMaterial(m.name, 3)
86         material.r=m.rgbCol[0]
87         material.g=m.rgbCol[1]
88         material.b=m.rgbCol[2]
89         material.a=m.alpha
90         return material
91
92 else:
93     # for 2.5
94     import bpy
95
96     # wrapper
97     import bl25 as bl
98
99     def materialToMqo(m):
100         material=MQOMaterial(m.name, 3)
101         material.r=m.diffuse_color[0]
102         material.g=m.diffuse_color[1]
103         material.b=m.diffuse_color[2]
104         material.a=m.alpha
105         material.amb=m.ambient
106         material.emi=m.emit
107         return material
108
109 def apply_transform(vec, matrix):
110     x, y, z = vec
111     xloc, yloc, zloc = matrix[3][0], matrix[3][1], matrix[3][2]
112     return    x*matrix[0][0] + y*matrix[1][0] + z*matrix[2][0] + xloc,\
113             x*matrix[0][1] + y*matrix[1][1] + z*matrix[2][1] + yloc,\
114             x*matrix[0][2] + y*matrix[1][2] + z*matrix[2][2] + zloc
115
116 def convert_to_mqo(vec):
117     return vec.x, vec.z, -vec.y
118
119
120 class OutlineNode(object):
121     __slots__=['o', 'children']
122     def __init__(self, o):
123         self.o=o
124         self.children=[]
125
126     def __str__(self):
127         return "<Node %s>" % self.o
128
129
130 class ObjectInfo(object):
131     __slots__=['object', 'depth', 'material_map']
132     def __init__(self, o, depth):
133         self.object=o
134         self.depth=depth
135         self.material_map={}
136
137     def __str__(self):
138         return "<ObjectInfo %d %s>" % (self.depth, self.object)
139
140
141 class MqoExporter(object):
142     __slots__=["materials", "objects", 'scale', 'apply_modifier',]
143     def __init__(self, scale, apply_modifier):
144         self.objects=[]
145         self.materials=[]
146         self.scale=scale
147         self.apply_modifier=apply_modifier
148
149     def setup(self, scene):
150         # 木構造を構築する
151         object_node_map={}
152         for o in scene.objects:
153             object_node_map[o]=OutlineNode(o)
154         for node in object_node_map.values():
155             if node.o.parent:
156                 object_node_map[node.o.parent].children.append(node)
157
158         # ルートを得る
159         root=object_node_map[scene.objects.active]
160
161         # 情報を集める
162         if root.o.type.upper()=='EMPTY':
163             # depth調整 
164             for node in root.children:
165                 self.__setup(node)
166         else:
167             self.__setup(root)
168
169     def __setup(self, node, depth=0):
170         info=ObjectInfo(node.o, depth)
171         self.objects.append(info)
172         if node.o.type.upper()=='MESH':
173             # set material index
174             for i, m in enumerate(node.o.data.materials):
175                 info.material_map[i]=self.__getOrAddMaterial(m)
176         # recursive
177         for child in node.children:
178             self.__setup(child, depth+1)
179             
180     def __getOrAddMaterial(self, material):
181         for i, m in enumerate(self.materials):
182             if m==material:
183                 return i
184         index=len(self.materials)
185         self.materials.append(material)
186         return index
187
188     def write(self, path):
189         bl.message("open: "+path)
190         io=bl.Writer(path, 'cp932')
191         self.__write_header(io)
192         self.__write_scene(io)
193         print("Writing MaterialChunk")
194         self.__write_materials(io, os.path.dirname(path))
195         print("Writing ObjectChunk")
196         for info in self.objects:
197             self.__write_object(io, info)
198         io.write("Eof\r\n")
199         io.flush()
200         io.close()
201
202     def __write_header(self, io):
203         io.write("Metasequoia Document\r\n")
204         io.write("Format Text Ver 1.0\r\n")
205         io.write("\r\n")
206
207     def __write_scene(self, io):
208         print("Writing SceneChunk")
209         io.write("Scene {\r\n")
210         io.write("}\r\n")
211
212     def __write_materials(self, io, dirname):
213         # each material    
214         io.write("Material %d {\r\n" % (len(self.materials)))
215         for m in self.materials:
216             io.write(str(materialToMqo(m)))
217             # ToDo separated alpha texture
218             for filename in bl.material.eachTexturePath(m):
219                 if len(dirname)>0 and filename.startswith(dirname):
220                     # 相対パスに変換する
221                     filename=filename[len(dirname)+1:]
222                 io.write(" tex(\"%s\")" % filename)
223                 break
224             io.write("\r\n") 
225         # end of chunk
226         io.write("}\r\n") 
227
228     def __write_object(self, io, info):
229         print(info)
230
231         obj=info.object
232         if obj.type.upper()=='MESH' or obj.type.upper()=='EMPTY':
233             pass
234         else:
235             print(obj.type)
236             return
237
238         io.write("Object \""+obj.name+"\" {\r\n")
239
240         # depth
241         io.write("\tdepth %d\r\n" % info.depth)
242
243         # mirror
244         if not self.apply_modifier:
245             if bl.modifier.hasType(obj, 'MIRROR'):
246                     io.write("\tmirror 1\r\n")
247                     io.write("\tmirror_axis 1\r\n")
248
249         if obj.type.upper()=='MESH':
250             # duplicate and applyMatrix
251             copyMesh, copyObj=bl.object.duplicate(obj)
252             # apply transform
253             copyObj.scale=obj.scale
254             bpy.ops.object.scale_apply()
255             copyObj.rotation_euler=obj.rotation_euler
256             bpy.ops.object.rotation_apply()
257             copyObj.location=obj.location
258             bpy.ops.object.location_apply()
259             # apply modifier
260             if self.apply_modifier:
261                 # remove shape key
262                 while bl.object.hasShapeKey(copyObj):
263                     bpy.ops.object.shape_key_remove()
264                 for m in [m for m in copyObj.modifiers]:
265                     if m.type=='SOLIDFY':
266                         continue
267                     elif m.type=='ARMATURE':
268                         bpy.ops.object.modifier_apply(modifier=m.name)
269                     elif m.type=='MIRROR':
270                         bpy.ops.object.modifier_apply(modifier=m.name)
271                     else:
272                         print(m.type)
273             # write mesh
274             self.__write_mesh(io, copyMesh, info.material_map)
275             bl.object.delete(copyObj)
276
277         io.write("}\r\n") # end of object
278
279     def __write_mesh(self, io, mesh, material_map):
280         # vertices
281         io.write("\tvertex %d {\r\n" % len(mesh.verts))
282         for vert in mesh.verts:
283             x, y, z = convert_to_mqo(vert.co)
284             io.write("\t\t%f %f %f\r\n" % 
285                     (x*self.scale, y*self.scale, z*self.scale)) # rotate to y-up
286         io.write("\t}\r\n")
287
288         # faces
289         io.write("\tface %d {\r\n" % len(mesh.faces))
290         for i, face in enumerate(mesh.faces):
291             count=bl.face.getVertexCount(face)
292             # V
293             io.write("\t\t%d V(" % count)
294             for j in reversed(bl.face.getVertices(face)):
295                 io.write("%d " % j)
296             io.write(")")
297             # mat
298             if len(mesh.materials):
299                 io.write(" M(%d)" % 
300                         material_map[bl.face.getMaterialIndex(face)])
301             # UV
302             if bl.mesh.hasUV(mesh) and bl.mesh.hasFaceUV(mesh, i, face):
303                 io.write(" UV(")
304                 for uv in reversed(bl.mesh.getFaceUV(mesh, i, face, count)):
305                     # reverse vertical value
306                     io.write("%f %f " % (uv[0], 1.0-uv[1])) 
307                 io.write(")")
308             io.write("\r\n")
309         io.write("\t}\r\n") # end of faces
310
311
312 def __execute(filename, scene, scale=10, apply_modifier=False):
313     if scene.objects.active:
314         exporter=MqoExporter(scale, apply_modifier)
315         exporter.setup(scene)
316         exporter.write(filename)
317     else:
318         bl.message('no active object !')
319
320