Scripts:
[blender.git] / release / scripts / wings_export.py
1 #!BPY
2
3 """
4 Name: 'Wings3D (.wings)...'
5 Blender: 232
6 Group: 'Export'
7 Tooltip: 'Export selected mesh to Wings3D File Format (.wings)'
8 """
9
10 __author__ = "Anthony D'Agostino (Scorpius)"
11 __url__ = ("blender", "elysiun",
12 "Author's homepage, http://www.redrival.com/scorpius",
13 "Wings 3D, http://www.wings3d.com")
14 __version__ = "Part of IOSuite 0.5"
15
16 __bpydoc__ = """\
17 This script exports meshes to Wings3D file format.
18
19 Wings3D is an open source polygon modeler written in Erlang, a
20 language similar to Lisp. The .wings file format is a binary
21 representation of erlang terms (lists, tuples, atoms, etc.) and is
22 compressed with zlib.
23
24 Usage:<br>
25         Select meshes to be exported and run this script from "File->Export" menu.
26
27 Supported:<br>
28         1. Exports meshes only. Hint: use ALT-C to convert non-mesh objects,
29 and CTRL-ALT-A if you have "dupliverts" objects.<br>
30         2. Exports Vertex Colors & Radiosity Solutions.
31
32 Missing:<br>
33         Materials and UV Coordinates info will be ignored.
34
35 Known issues:<br>
36         Exports only well-behaved and topologically correct meshes (i.e,
37 closed meshes, manifolds, meshes imported from wings, etc). The mesh
38 cannot have duplicate vertices, missing faces (holes), open edges, etc.<br>
39         PowerUser Hint: In editmode, if CTRL-ALT-SHIFT-M results in a selection,
40 then your mesh is not a manifold and most likely will not export.
41
42 Notes:<br>
43         Last tested with Wings 3D 0.98.25 & Blender 2.35a.
44 """
45
46 # $Id$
47 #
48 # +---------------------------------------------------------+
49 # | Copyright (c) 2002 Anthony D'Agostino                   |
50 # | http://www.redrival.com/scorpius                        |
51 # | scorpius@netzero.com                                    |
52 # | Feb 19, 2002                                            |
53 # | Released under the Blender Artistic Licence (BAL)       |
54 # | Import Export Suite v0.5                                |
55 # +---------------------------------------------------------+
56 # | Read and write Wings3D File Format (*.wings)            |
57 # +---------------------------------------------------------+
58
59 import Blender, mod_meshtools
60 import struct, time, sys, os, zlib, cStringIO
61
62 # ===============================================
63 # === Write The 'Header' Common To All Chunks ===
64 # ===============================================
65 def write_chunkheader(data, version, tag, name):
66         data.write(struct.pack(">BB", version, tag))
67         data.write(struct.pack(">BH", 0x64, len(name)))
68         data.write(name)
69
70 # ===================
71 # === Write Faces ===
72 # ===================
73 def write_faces(data, mesh):
74         numfaces = len(mesh.faces)
75         data.write(struct.pack(">BL", 0x6C, numfaces))
76         #for i in range(numfaces):
77         #        if not i%100 and mod_meshtools.show_progress: Blender.Window.DrawProgressBar(float(i)/numfaces, "Writing Faces")
78         #        data.write("\x6A")
79         data.write("\x6A" * numfaces) # same, but faster than the above loop
80         data.write("\x6A")
81
82 # ===================
83 # === Write Verts ===
84 # ===================
85 def write_verts(data, mesh):
86         numverts = len(mesh.verts)
87         data.write(struct.pack(">BL", 0x6C, numverts))
88         for i in range(numverts):
89                 if not i%100 and mod_meshtools.show_progress: Blender.Window.DrawProgressBar(float(i)/numverts, "Writing Verts")
90                 data.write(struct.pack(">BLBL", 0x6C, 1, 0x6D, 24))
91                 #data.write("\x6c\x00\x00\x00\x01\x6D\x00\x00\x00\x30")
92                 x, y, z = mesh.verts[i].co
93                 data.write(struct.pack(">ddd", x, z, -y))
94                 data.write("\x6A")
95         data.write("\x6A")
96
97 # ===================
98 # === Write Edges ===
99 # ===================
100 def write_edges(data, mesh, edge_table):
101         numedges = len(edge_table)
102         data.write(struct.pack(">BL", 0x6C, numedges))
103         keys = edge_table.keys()
104         keys.sort()
105         for key in keys:
106                 i = edge_table[key][6]
107                 if not i%100 and mod_meshtools.show_progress: Blender.Window.DrawProgressBar(float(i)/numedges, "Writing Edges")
108
109                 if mod_meshtools.has_vertex_colors(mesh):
110                         r1, g1, b1 = edge_table[key][7]
111                         r2, g2, b2 = edge_table[key][8]
112                         data.write("\x6C\x00\x00\x00\x02")
113                         data.write("\x68\x02\x64\x00\x05color")
114                         data.write("\x6D\x00\x00\x00\x30")
115                         data.write(struct.pack(">dddddd", r1, g1, b1, r2, g2, b2))
116                         #print "%f %f %f - %f %f %f" % (r1, g1, b1, r2, g2, b2)
117                 else:
118                         data.write("\x6C\x00\x00\x00\x01")  # BL
119
120                 #$write_chunkheader(data, 0x68, 0x09, "edge")
121                 data.write("\x68\x09\x64\x00\x04edge") # faster
122
123                 # Sv Ev (Reversed)
124                 data.write(struct.pack(">BLBL", 0x62, key[1], 0x62, key[0]))
125
126                 # Lf Rf LP LS RP RS
127                 for i in range(6):
128                         if edge_table[key][i] < 256:
129                                 data.write(struct.pack(">BB", 0x61, edge_table[key][i]))
130                         else:
131                                 data.write(struct.pack(">BL", 0x62, edge_table[key][i]))
132
133                 data.write("\x6A")
134
135         data.write("\x6A")
136
137 # ===============================
138 # === Write The Material Mode ===
139 # ===============================
140 def write_mode(data, mesh):
141         data.write("\x6A")
142         data.write(struct.pack(">BL", 0x6C, 1))
143         write_chunkheader(data, 0x68, 0x02, "mode")
144         if mod_meshtools.has_vertex_colors(mesh):
145                 data.write(struct.pack(">BH6s", 0x64, 6, "vertex"))
146         else:
147                 data.write(struct.pack(">BH8s", 0x64, 8, "material"))
148         data.write("\x6A")
149
150 # ======================
151 # === Write Material ===
152 # ======================
153 def write_material(data, mesh):
154         data.write("\x6A")
155         data.write(struct.pack(">BL", 0x6C, 1))
156         write_chunkheader(data, 0x68, 0x02, "my default")
157
158         data.write(struct.pack(">BL", 0x6C, 2))
159         write_chunkheader(data, 0x68, 0x02, "maps")
160         data.write("\x6A")
161         write_chunkheader(data, 0x68, 0x02, "opengl")
162
163         # === The Material Components ===
164         data.write(struct.pack(">BL", 0x6C, 5))
165
166         write_chunkheader(data, 0x68, 0x02, "diffuse")
167         data.write("\x68\x04")
168         data.write("\x63"+"1.00000000000000000000"+"e+000"+"\x00"*4)
169         data.write("\x63"+"1.00000000000000000000"+"e+000"+"\x00"*4)
170         data.write("\x63"+"1.00000000000000000000"+"e+000"+"\x00"*4)
171         data.write("\x63"+"1.00000000000000000000"+"e+000"+"\x00"*4)
172
173         write_chunkheader(data, 0x68, 0x02, "ambient")
174         data.write("\x68\x04")
175         data.write("\x63"+"1.00000000000000000000"+"e+000"+"\x00"*4)
176         data.write("\x63"+"1.00000000000000000000"+"e+000"+"\x00"*4)
177         data.write("\x63"+"1.00000000000000000000"+"e+000"+"\x00"*4)
178         data.write("\x63"+"1.00000000000000000000"+"e+000"+"\x00"*4)
179
180         write_chunkheader(data, 0x68, 0x02, "specular")
181         data.write("\x68\x04")
182         data.write("\x63"+"1.00000000000000000000"+"e+000"+"\x00"*4)
183         data.write("\x63"+"1.00000000000000000000"+"e+000"+"\x00"*4)
184         data.write("\x63"+"1.00000000000000000000"+"e+000"+"\x00"*4)
185         data.write("\x63"+"1.00000000000000000000"+"e+000"+"\x00"*4)
186
187         write_chunkheader(data, 0x68, 0x02, "emission")
188         data.write("\x68\x04")
189         data.write("\x63"+"0.00000000000000000000"+"e+000"+"\x00"*4)
190         data.write("\x63"+"0.00000000000000000000"+"e+000"+"\x00"*4)
191         data.write("\x63"+"0.00000000000000000000"+"e+000"+"\x00"*4)
192         data.write("\x63"+"0.00000000000000000000"+"e+000"+"\x00"*4)
193
194         write_chunkheader(data, 0x68, 0x02, "shininess")
195         data.write("\x63"+"0.00000000000000000000"+"e+000"+"\x00"*4)
196
197         #write_chunkheader(data, 0x68, 0x02, "twosided")
198         #data.write(struct.pack(">BH4s", 0x64, 4, "true"))
199
200         data.write("\x6A"*3)    # use *4 if no ambient light
201
202 # =====================
203 # === Generate Data ===
204 # =====================
205 def generate_data(objname, edge_table, mesh):
206         data = cStringIO.StringIO()
207
208         # === wings chunk ===
209         write_chunkheader(data, 0x68, 0x03, "wings")
210
211         numobjs = 1 # len(Blender.Object.GetSelected())
212         data.write("\x61\x02\x68\x03") # misc bytes
213         data.write(struct.pack(">BL", 0x6C, numobjs))
214
215         # === object chunk ===
216         write_chunkheader(data, 0x68, 0x04, "object")
217         data.write(struct.pack(">BH", 0x6B, len(objname)))
218         data.write(objname)
219
220         # === winged chunk ===
221         write_chunkheader(data, 0x68, 0x05, "winged")
222         write_edges(data, mesh, edge_table)
223         write_faces(data, mesh)
224         write_verts(data, mesh)
225         write_mode(data, mesh)
226         write_material(data, mesh)
227         write_ambient_light(data)
228         return data.getvalue()
229
230 # ===========================
231 # === Write Ambient Light ===
232 # ===========================
233 def write_ambient_light(data):
234         light = [       # A quick cheat ;)
235         0x6C, 0x00, 0x00, 0x00, 0x01, 0x68, 0x02, 0x64, 0x00, 0x06, 0x6C, 0x69,
236         0x67, 0x68, 0x74, 0x73, 0x6C, 0x00, 0x00, 0x00, 0x01, 0x68, 0x02, 0x6B,
237         0x00, 0x07, 0x41, 0x6D, 0x62, 0x69, 0x65, 0x6E, 0x74, 0x6C, 0x00, 0x00,
238         0x00, 0x08, 0x68, 0x02, 0x64, 0x00, 0x07, 0x76, 0x69, 0x73, 0x69, 0x62,
239         0x6C, 0x65, 0x64, 0x00, 0x04, 0x74, 0x72, 0x75, 0x65, 0x68, 0x02, 0x64,
240         0x00, 0x06, 0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64, 0x64, 0x00, 0x05, 0x66,
241         0x61, 0x6C, 0x73, 0x65, 0x68, 0x02, 0x64, 0x00, 0x06, 0x6F, 0x70, 0x65,
242         0x6E, 0x67, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x03, 0x68, 0x02, 0x64, 0x00,
243         0x04, 0x74, 0x79, 0x70, 0x65, 0x64, 0x00, 0x07, 0x61, 0x6D, 0x62, 0x69,
244         0x65, 0x6E, 0x74, 0x68, 0x02, 0x64, 0x00, 0x07, 0x61, 0x6D, 0x62, 0x69,
245         0x65, 0x6E, 0x74, 0x68, 0x04, 0x63, 0x31, 0x2E, 0x30, 0x30, 0x30, 0x30,
246         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
247         0x30, 0x30, 0x30, 0x30, 0x65, 0x2B, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
248         0x00, 0x63, 0x31, 0x2E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
249         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
250         0x65, 0x2B, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x63, 0x31, 0x2E,
251         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
252         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x65, 0x2B, 0x30, 0x30,
253         0x30, 0x00, 0x00, 0x00, 0x00, 0x63, 0x31, 0x2E, 0x30, 0x30, 0x30, 0x30,
254         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
255         0x30, 0x30, 0x30, 0x30, 0x65, 0x2B, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
256         0x00, 0x68, 0x02, 0x64, 0x00, 0x08, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x69,
257         0x6F, 0x6E, 0x68, 0x03, 0x63, 0x30, 0x2E, 0x30, 0x30, 0x30, 0x30, 0x30,
258         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
259         0x30, 0x30, 0x30, 0x65, 0x2B, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
260         0x63, 0x33, 0x2E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
261         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x65,
262         0x2B, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x63, 0x30, 0x2E, 0x30,
263         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
264         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x65, 0x2B, 0x30, 0x30, 0x30,
265         0x00, 0x00, 0x00, 0x00, 0x6A, 0x68, 0x02, 0x64, 0x00, 0x07, 0x76, 0x69,
266         0x73, 0x69, 0x62, 0x6C, 0x65, 0x64, 0x00, 0x04, 0x74, 0x72, 0x75, 0x65,
267         0x68, 0x02, 0x64, 0x00, 0x06, 0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64, 0x64,
268         0x00, 0x05, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x68, 0x02, 0x64, 0x00, 0x06,
269         0x79, 0x61, 0x66, 0x72, 0x61, 0x79, 0x6C, 0x00, 0x00, 0x00, 0x0B, 0x68,
270         0x02, 0x64, 0x00, 0x09, 0x6D, 0x69, 0x6E, 0x69, 0x6D, 0x69, 0x7A, 0x65,
271         0x64, 0x64, 0x00, 0x04, 0x74, 0x72, 0x75, 0x65, 0x68, 0x02, 0x64, 0x00,
272         0x05, 0x70, 0x6F, 0x77, 0x65, 0x72, 0x63, 0x31, 0x2E, 0x30, 0x30, 0x30,
273         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
274         0x30, 0x30, 0x30, 0x30, 0x30, 0x65, 0x2B, 0x30, 0x30, 0x30, 0x00, 0x00,
275         0x00, 0x00, 0x68, 0x02, 0x64, 0x00, 0x04, 0x74, 0x79, 0x70, 0x65, 0x64,
276         0x00, 0x09, 0x68, 0x65, 0x6D, 0x69, 0x6C, 0x69, 0x67, 0x68, 0x74, 0x68,
277         0x02, 0x64, 0x00, 0x07, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x73, 0x62,
278         0x00, 0x00, 0x01, 0x00, 0x68, 0x02, 0x64, 0x00, 0x05, 0x64, 0x65, 0x70,
279         0x74, 0x68, 0x61, 0x03, 0x68, 0x02, 0x64, 0x00, 0x0A, 0x62, 0x61, 0x63,
280         0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x64, 0x00, 0x09, 0x75, 0x6E,
281         0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x64, 0x68, 0x02, 0x64, 0x00, 0x18,
282         0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x5F, 0x66,
283         0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x5F, 0x48, 0x44, 0x52, 0x49,
284         0x6A, 0x68, 0x02, 0x64, 0x00, 0x19, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72,
285         0x6F, 0x75, 0x6E, 0x64, 0x5F, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D,
286         0x65, 0x5F, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x6A, 0x68, 0x02, 0x64, 0x00,
287         0x1A, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x5F,
288         0x65, 0x78, 0x70, 0x6F, 0x73, 0x75, 0x72, 0x65, 0x5F, 0x61, 0x64, 0x6A,
289         0x75, 0x73, 0x74, 0x61, 0x00, 0x68, 0x02, 0x64, 0x00, 0x12, 0x62, 0x61,
290         0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x5F, 0x6D, 0x61, 0x70,
291         0x70, 0x69, 0x6E, 0x67, 0x64, 0x00, 0x05, 0x70, 0x72, 0x6F, 0x62, 0x65,
292         0x68, 0x02, 0x64, 0x00, 0x10, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F,
293         0x75, 0x6E, 0x64, 0x5F, 0x70, 0x6F, 0x77, 0x65, 0x72, 0x63, 0x31, 0x2E,
294         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
295         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x65, 0x2B, 0x30, 0x30,
296         0x30, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x68, 0x02, 0x64, 0x00, 0x07, 0x76,
297         0x69, 0x73, 0x69, 0x62, 0x6C, 0x65, 0x64, 0x00, 0x04, 0x74, 0x72, 0x75,
298         0x65, 0x68, 0x02, 0x64, 0x00, 0x06, 0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64,
299         0x64, 0x00, 0x05, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x6A, 0x6A, 0x6A]
300         data.write("".join(map(chr, light)))
301
302 # ==========================
303 # === Write Wings Format ===
304 # ==========================
305 def write(filename):
306         start = time.clock()
307
308         objects = Blender.Object.GetSelected()
309
310         objname = objects[0].name
311         meshname = objects[0].data.name
312         mesh = Blender.NMesh.GetRaw(meshname)
313         obj = Blender.Object.Get(objname)
314
315         try:
316                 edge_table = mod_meshtools.generate_edgetable(mesh)
317         except:
318                 edge_table = {}
319                 message = "Unable to generate\nEdge Table for mesh.\n"
320                 message += "Object name is: " + meshname
321                 mod_meshtools.print_boxed(message)
322                 Blender.Draw.PupMenu("Wings Export error|Unable to generate Edge Table for mesh")
323                 return 
324
325
326 #               if 0:
327 #                       import Tkinter, tkMessageBox
328 #                       sys.argv=['wings.pyo','wings.pyc'] # ?
329 #
330 #                       #Tkinter.NoDefaultRoot()
331 #                       win1 = Tkinter.Tk()
332 #                       ans = tkMessageBox.showerror("Error", message)
333 #                       win1.pack()
334 #                       print ans
335 #                       if ans:
336 #                               win1.quit()
337 #                       win1.mainloop()
338 #
339 #               else:
340 #                       from Tkinter import Label
341 #                       sys.argv = 'wings.py'
342 #                       widget = Label(None, text=message)
343 #                       #widget.title("Error")
344 #                       widget.pack()
345 #                       widget.mainloop()
346
347         data = generate_data(objname, edge_table, mesh)
348         dsize = len(data)
349         Blender.Window.DrawProgressBar(0.98, "Compressing Data")
350         data = zlib.compress(data, 6)
351         fsize = len(data)+6
352         header = "#!WINGS-1.0\r\n\032\04"
353         misc = 0x8350
354
355         file = open(filename, "wb")
356         file.write(header)
357         file.write(struct.pack(">L", fsize))
358         file.write(struct.pack(">H", misc))
359         file.write(struct.pack(">L", dsize))
360         file.write(data)
361
362         # Blender.Window.RedrawAll()
363         Blender.Window.DrawProgressBar(1.0, '')  # clear progressbar
364         file.close()
365         end = time.clock()
366         seconds = " in %.2f %s" % (end-start, "seconds")
367         message = "Successfully exported " + os.path.basename(filename) + seconds + '\n\n'
368         message += "objname : " + objname + '\n'
369         message += "faces   : " + `len(mesh.faces)` + '\n'
370         message += "edges   : " + `len(edge_table)` + '\n'
371         message += "verts   : " + `len(mesh.verts)` + '\n'
372         mod_meshtools.print_boxed(message)
373
374 def fs_callback(filename):
375         if filename.find('.wings', -6) <= 0: filename += '.wings'
376         write(filename)
377
378
379 if Blender.Object.GetSelected()[0].getType() != "Mesh":
380         Blender.Draw.PupMenu("Wings Export error|Selected object is not a mesh!")
381 else:
382         Blender.Window.FileSelector(fs_callback, "Export Wings3D")