OSDN Git Service

implement toon textures.
[meshio/meshio.git] / swig / blender / mqo_export.py
1 #!BPY\r
2 # coding: utf-8\r
3 \r
4 """\r
5 Name: 'Metasequoia (.mqo)...'\r
6 Blender: 245\r
7 Group: 'Export'\r
8 Tooltip: 'Save as Metasequoia MQO File'\r
9 """\r
10 __author__= 'ousttrue'\r
11 __url__ = ["http://gunload.web.fc2.com/blender/"]\r
12 __version__= '0.3 2010/06/06'\r
13 __bpydoc__ = """\\r
14 This script is an exporter to MQO file format.\r
15 \r
16 Usage:\r
17 \r
18 Run this script from "File->Export" menu.\r
19 \r
20 0.1 20080128:\r
21 0.2 20100518: refactoring.\r
22 0.3 20100606: integrate 2.4 and 2.5.\r
23 0.4 20100626: refactoring.\r
24 """\r
25 \r
26 import os\r
27 import sys\r
28 \r
29 def isBlender24():\r
30     return sys.version_info[0]<3\r
31 \r
32 if isBlender24():\r
33     # for 2.4\r
34     import Blender\r
35     from Blender import Mathutils\r
36     import bpy\r
37 \r
38     # wrapper\r
39     import bl24 as bl\r
40 \r
41     def materialToMqo(m):\r
42         return "\"%s\" shader(3) col(%f %f %f %f)" % (\r
43                 m.name, m.rgbCol[0], m.rgbCol[1], m.rgbCol[2], m.alpha)\r
44 \r
45 else:\r
46     # for 2.5\r
47     import bpy\r
48     from bpy.props import *\r
49 \r
50     # wrapper\r
51     import bl25 as bl\r
52 \r
53     def materialToMqo(m):\r
54         return "\"%s\" shader(3) col(%f %f %f %f)" % (\r
55                 m.name, \r
56                 m.diffuse_color[0], m.diffuse_color[1], m.diffuse_color[2], \r
57                 m.alpha)\r
58 \r
59 def apply_transform(vec, matrix):\r
60     x, y, z = vec\r
61     xloc, yloc, zloc = matrix[3][0], matrix[3][1], matrix[3][2]\r
62     return    x*matrix[0][0] + y*matrix[1][0] + z*matrix[2][0] + xloc,\\r
63             x*matrix[0][1] + y*matrix[1][1] + z*matrix[2][1] + yloc,\\r
64             x*matrix[0][2] + y*matrix[1][2] + z*matrix[2][2] + zloc\r
65 \r
66 def convert_to_mqo(vec):\r
67     return vec.x, vec.z, -vec.y\r
68 \r
69 \r
70 class OutlineNode(object):\r
71     __slots__=['o', 'children']\r
72     def __init__(self, o):\r
73         self.o=o\r
74         self.children=[]\r
75 \r
76     def __str__(self):\r
77         return "<Node %s>" % self.o\r
78 \r
79 \r
80 class ObjectInfo(object):\r
81     __slots__=['object', 'depth', 'material_map']\r
82     def __init__(self, o, depth):\r
83         self.object=o\r
84         self.depth=depth\r
85         self.material_map={}\r
86 \r
87     def __str__(self):\r
88         return "<ObjectInfo %d %s>" % (self.depth, self.object)\r
89 \r
90 \r
91 class MqoExporter(object):\r
92     __slots__=["materials", "objects", 'scale',]\r
93     def __init__(self, scale):\r
94         self.objects=[]\r
95         self.materials=[]\r
96         self.scale=scale\r
97 \r
98     def setup(self, scene):\r
99         # 木構造を構築する\r
100         object_node_map={}\r
101         for o in scene.objects:\r
102             object_node_map[o]=OutlineNode(o)\r
103         for node in object_node_map.values():\r
104             if node.o.parent:\r
105                 object_node_map[node.o.parent].children.append(node)\r
106 \r
107         # ルートを得る\r
108         root=object_node_map[scene.objects.active]\r
109 \r
110         # 情報を集める\r
111         if root.o.type.upper()=='EMPTY':\r
112             # depth調整 \r
113             for node in root.children:\r
114                 self.__setup(node)\r
115         else:\r
116             self.__setup(root)\r
117 \r
118     def __setup(self, node, depth=0):\r
119         info=ObjectInfo(node.o, depth)\r
120         self.objects.append(info)\r
121         if node.o.type.upper()=='MESH':\r
122             # set material index\r
123             for i, m in enumerate(node.o.data.materials):\r
124                 info.material_map[i]=self.__getOrAddMaterial(m)\r
125         # recursive\r
126         for child in node.children:\r
127             self.__setup(child, depth+1)\r
128             \r
129     def __getOrAddMaterial(self, material):\r
130         for i, m in enumerate(self.materials):\r
131             if m==material:\r
132                 return i\r
133         index=len(self.materials)\r
134         self.materials.append(material)\r
135         return index\r
136 \r
137     def write(self, path):\r
138         io=bl.Writer(path, 'cp932')\r
139         self.__write_header(io)\r
140         self.__write_scene(io)\r
141         print("Writing MaterialChunk")\r
142         self.__write_materials(io, os.path.dirname(path))\r
143         print("Writing ObjectChunk")\r
144         for info in self.objects:\r
145             self.__write_object(io, info)\r
146         io.write("Eof\r\n")\r
147         io.flush()\r
148         io.close()\r
149 \r
150     def __write_header(self, io):\r
151         io.write("Metasequoia Document\r\n")\r
152         io.write("Format Text Ver 1.0\r\n")\r
153         io.write("\r\n")\r
154 \r
155     def __write_scene(self, io):\r
156         print("Writing SceneChunk")\r
157         io.write("Scene {\r\n")\r
158         io.write("}\r\n")\r
159 \r
160     def __write_materials(self, io, dirname):\r
161         # each material    \r
162         io.write("Material %d {\r\n" % (len(self.materials)))\r
163         for m in self.materials:\r
164             io.write(materialToMqo(m))\r
165             # ToDo separated alpha texture\r
166             for filename in bl.material.eachTexturePath(m):\r
167                 if len(dirname)>0 and filename.startswith(dirname):\r
168                     # 相対パスに変換する\r
169                     filename=filename[len(dirname)+1:]\r
170                 io.write(" tex(\"%s\")" % filename)\r
171                 break\r
172             io.write("\r\n") \r
173         # end of chunk\r
174         io.write("}\r\n") \r
175 \r
176     def __write_object(self, io, info):\r
177         print(info)\r
178 \r
179         obj=info.object\r
180         if obj.type.upper()=='MESH' or obj.type.upper()=='EMPTY':\r
181             pass\r
182         else:\r
183             print(obj.type)\r
184             return\r
185 \r
186         io.write("Object \""+obj.name+"\" {\r\n")\r
187 \r
188         # depth\r
189         io.write("\tdepth %d\r\n" % info.depth)\r
190 \r
191         # mirror\r
192         if bl.modifier.hasType(obj, 'MIRROR'):\r
193                 io.write("\tmirror 1\r\n")\r
194                 io.write("\tmirror_axis 1\r\n")\r
195 \r
196         if obj.type.upper()=='MESH':\r
197             # duplicate and applyMatrix\r
198             copyMesh, copyObj=bl.object.duplicate(obj)\r
199             # apply transform\r
200             copyObj.scale=obj.scale\r
201             bpy.ops.object.scale_apply()\r
202             copyObj.rotation_euler=obj.rotation_euler\r
203             bpy.ops.object.rotation_apply()\r
204             copyObj.location=obj.location\r
205             bpy.ops.object.location_apply()\r
206             # write mesh\r
207             self.__write_mesh(io, copyMesh, info.material_map)\r
208             bl.object.delete(copyObj)\r
209 \r
210         io.write("}\r\n") # end of object\r
211 \r
212     def __write_mesh(self, io, mesh, material_map):\r
213         # vertices\r
214         io.write("\tvertex %d {\r\n" % len(mesh.verts))\r
215         for vert in mesh.verts:\r
216             x, y, z = convert_to_mqo(vert.co)\r
217             io.write("\t\t%f %f %f\r\n" % \r
218                     (x*self.scale, y*self.scale, z*self.scale)) # rotate to y-up\r
219         io.write("\t}\r\n")\r
220 \r
221         # faces\r
222         io.write("\tface %d {\r\n" % len(mesh.faces))\r
223         for i, face in enumerate(mesh.faces):\r
224             count=bl.face.getVertexCount(face)\r
225             # V\r
226             io.write("\t\t%d V(" % count)\r
227             for j in reversed(bl.face.getVertices(face)):\r
228                 io.write("%d " % j)\r
229             io.write(")")\r
230             # mat\r
231             if len(mesh.materials):\r
232                 io.write(" M(%d)" % \r
233                         material_map[bl.face.getMaterialIndex(face)])\r
234             # UV\r
235             if bl.mesh.hasUV(mesh) and bl.mesh.hasFaceUV(mesh, i, face):\r
236                 io.write(" UV(")\r
237                 for uv in reversed(bl.mesh.getFaceUV(mesh, i, face, count)):\r
238                     # reverse vertical value\r
239                     io.write("%f %f " % (uv[0], 1.0-uv[1])) \r
240                 io.write(")")\r
241             io.write("\r\n")\r
242         io.write("\t}\r\n") # end of faces\r
243 \r
244 \r
245 def __execute(filename, scene, scale=10):\r
246     if not scene.objects.active:\r
247         bl.message('no active object !')\r
248         return\r
249 \r
250     exporter=MqoExporter(scale)\r
251     exporter.setup(scene)\r
252     exporter.write(filename)\r
253 \r
254 \r
255 if isBlender24():\r
256     # for 2.4\r
257     def execute_24(filename):\r
258         scene=Blender.Scene.GetCurrent()\r
259         bl.initialize('mqo_export', scene)\r
260         __execute(\r
261                 filename.decode(bl.INTERNAL_ENCODING), \r
262                 scene)\r
263         bl.finalize()\r
264 \r
265     # execute\r
266     Blender.Window.FileSelector(\r
267             execute_24, \r
268             'Export Metasequoia MQO', \r
269             Blender.sys.makename(ext='.mqo'))\r
270 \r
271 else:\r
272     # for 2.5\r
273     def execute_25(path, scene, scale):\r
274         bl.initialize('mqo_export', scene)\r
275         __execute(path, scene, scale)\r
276         bl.finalize()\r
277 \r
278     # operator\r
279     class EXPORT_OT_mqo(bpy.types.Operator):\r
280         '''Save a Metasequoia MQO file.'''\r
281         bl_idname = "export_scene.mqo"\r
282         bl_label = 'Export MQO'\r
283 \r
284         # List of operator properties, the attributes will be assigned\r
285         # to the class instance from the operator settings before calling.\r
286 \r
287         path = StringProperty(\r
288                 name="File Path",\r
289                 description="File path used for exporting the MQO file",\r
290                 maxlen= 1024,\r
291                 default= ""\r
292                 )\r
293         filename = StringProperty(\r
294                 name="File Name", \r
295                 description="Name of the file.")\r
296         directory = StringProperty(\r
297                 name="Directory", \r
298                 description="Directory of the file.")\r
299 \r
300         scale = FloatProperty(\r
301                 name="Scale", \r
302                 description="Scale the MQO by this value", \r
303                 min=0.0001, max=1000000.0, \r
304                 soft_min=0.001, soft_max=100.0, default=10.0)\r
305 \r
306         check_existing = BoolProperty(\r
307                 name="Check Existing",\r
308                 description="Check and warn on overwriting existing files",\r
309                 default=True,\r
310                 options=set('HIDDEN'))\r
311 \r
312         def execute(self, context):\r
313             execute_25(\r
314                     self.properties.path, \r
315                     context.scene, \r
316                     self.properties.scale)\r
317             return 'FINISHED'\r
318 \r
319         def invoke(self, context, event):\r
320             wm=context.manager\r
321             wm.add_fileselect(self)\r
322             return 'RUNNING_MODAL'\r
323 \r
324     # register menu\r
325     def menu_func(self, context): \r
326         #default_path=bpy.data.filename.replace(".blend", ".mqo")\r
327         self.layout.operator(\r
328                 EXPORT_OT_mqo.bl_idname, \r
329                 text="Metasequoia (.mqo)")#.path=default_path\r
330 \r
331     def register():\r
332         bpy.types.register(EXPORT_OT_mqo)\r
333         bpy.types.INFO_MT_file_export.append(menu_func)\r
334 \r
335     def unregister():\r
336         bpy.types.unregister(EXPORT_OT_mqo)\r
337         bpy.types.INFO_MT_file_export.remove(menu_func)\r
338 \r
339     if __name__ == "__main__":\r
340         register()\r
341 \r