f50e00f6d8a67583409cf8085a1538d065e32a7f
[blender.git] / release / scripts / truespace_export.py
1 #!BPY
2
3 """
4 Name: 'TrueSpace...'
5 Blender: 232
6 Group: 'Export'
7 Tooltip: 'Export selected meshes to trueSpace File Format (*.cob)'
8 """
9
10 # $Id$
11 #
12 # +---------------------------------------------------------+
13 # | Copyright (c) 2001 Anthony D'Agostino                   |
14 # | http://www.redrival.com/scorpius                        |
15 # | scorpius@netzero.com                                    |
16 # | June 12, 2001                                           |
17 # | Released under the Blender Artistic Licence (BAL)       |
18 # | Import Export Suite v0.5                                |
19 # +---------------------------------------------------------+
20 # | Read and write Caligari trueSpace File Format (*.cob)   |
21 # +---------------------------------------------------------+
22
23 import Blender, mod_meshtools
24 import struct, os, cStringIO, time
25
26 # ==============================
27 # === Write trueSpace Format ===
28 # ==============================
29 def write(filename):
30         start = time.clock()
31         file = open(filename, "wb")
32         objects = Blender.Object.GetSelected()
33
34         write_header(file)
35
36         G,P,V,U,M = 1000,2000,3000,4000,5000
37         for object in objects:
38                 objname = object.name
39                 meshname = object.data.name
40                 mesh = Blender.NMesh.GetRaw(meshname)
41                 obj = Blender.Object.Get(objname)
42                 if not mesh: continue
43
44                 grou = generate_grou('Group ' + `objects.index(object)+1`)
45                 polh = generate_polh(objname, obj, mesh)
46                 if mod_meshtools.has_vertex_colors(mesh): vcol = generate_vcol(mesh)
47                 unit = generate_unit()
48                 mat1 = generate_mat1(mesh)
49
50                 if objects.index(object) == 0: X = 0
51
52                 write_chunk(file, "Grou", 0, 1, G, X, grou)
53                 write_chunk(file, "PolH", 0, 4, P, G, polh)
54                 if mod_meshtools.has_vertex_colors(mesh) and vcol:
55                         write_chunk(file, "VCol", 1, 0, V, P, vcol)
56                 write_chunk(file, "Unit", 0, 1, U, P, unit)
57                 write_chunk(file, "Mat1", 0, 5, M, P, mat1)
58
59                 X = G
60                 G,P,V,U,M = map(lambda x: x+1, [G,P,V,U,M])
61
62         write_chunk(file, "END ", 1, 0, 0, 0, '') # End Of File Chunk
63
64         Blender.Window.DrawProgressBar(1.0, '')  # clear progressbar
65         file.close()
66         end = time.clock()
67         seconds = " in %.2f %s" % (end-start, "seconds")
68         message = "Successfully exported " + os.path.basename(filename) + seconds
69         mod_meshtools.print_boxed(message)
70
71 # =============================
72 # === Write COB File Header ===
73 # =============================
74 def write_header(file):
75         file.write("Caligari V00.01BLH"+" "*13+"\n")
76
77 # ===================
78 # === Write Chunk ===
79 # ===================
80 def write_chunk(file, name, major, minor, chunk_id, parent_id, data):
81         file.write(name)
82         file.write(struct.pack("<2h", major, minor))
83         file.write(struct.pack("<2l", chunk_id, parent_id))
84         file.write(struct.pack("<1l", len(data)))
85         file.write(data)
86
87 # ============================================
88 # === Generate PolH (Polygonal Data) Chunk ===
89 # ============================================
90 def generate_polh(objname, obj, mesh):
91         data = cStringIO.StringIO()
92         write_ObjectName(data, objname)
93         write_LocalAxes(data, obj)
94         write_CurrentPosition(data, obj)
95         write_VertexList(data, mesh)
96         uvcoords = write_UVCoordsList(data, mesh)
97         write_FaceList(data, mesh, uvcoords)
98         return data.getvalue()
99
100 # === Write Object Name ===
101 def write_ObjectName(data, objname):
102         data.write(struct.pack("<h", 0))  # dupecount
103         data.write(struct.pack("<h", len(objname)))
104         data.write(objname)
105
106 # === Write Local Axes ===
107 def write_LocalAxes(data, obj):
108         data.write(struct.pack("<fff", obj.mat[3][0], obj.mat[3][1], obj.mat[3][2]))
109         data.write(struct.pack("<fff", obj.mat[0][0]/obj.SizeX, obj.mat[1][0]/obj.SizeX, obj.mat[2][0]/obj.SizeX))
110         data.write(struct.pack("<fff", obj.mat[0][1]/obj.SizeY, obj.mat[1][1]/obj.SizeY, obj.mat[2][1]/obj.SizeY))
111         data.write(struct.pack("<fff", obj.mat[0][2]/obj.SizeZ, obj.mat[1][2]/obj.SizeZ, obj.mat[2][2]/obj.SizeZ))
112
113 # === Write Current Position ===
114 def write_CurrentPosition(data, obj):
115         data.write(struct.pack("<ffff", obj.mat[0][0], obj.mat[0][1], obj.mat[0][2], obj.mat[3][0]))
116         data.write(struct.pack("<ffff", obj.mat[1][0], obj.mat[1][1], obj.mat[1][2], obj.mat[3][1]))
117         data.write(struct.pack("<ffff", obj.mat[2][0], obj.mat[2][1], obj.mat[2][2], obj.mat[3][2]))
118
119 # === Write Vertex List ===
120 def write_VertexList(data, mesh):
121         data.write(struct.pack("<l", len(mesh.verts)))
122         for i in range(len(mesh.verts)):
123                 if not i%100 and mod_meshtools.show_progress:
124                         Blender.Window.DrawProgressBar(float(i)/len(mesh.verts), "Writing Verts")
125                 x, y, z = mesh.verts[i].co
126                 data.write(struct.pack("<fff", -y, x, z))
127
128 # === Write UV Vertex List ===
129 def write_UVCoordsList(data, mesh):
130         if not mesh.hasFaceUV():
131                 data.write(struct.pack("<l", 1))
132                 data.write(struct.pack("<2f", 0,0))
133                 return {(0,0): 0}
134                 # === Default UV Coords (one image per face) ===
135                 # data.write(struct.pack("<l", 4))
136                 # data.write(struct.pack("<8f", 0,0, 0,1, 1,1, 1,0))
137                 # return {(0,0): 0, (0,1): 1, (1,1): 2, (1,0): 3}
138                 # === Default UV Coords (one image per face) ===
139
140         # === collect, remove duplicates, add indices, and write the uv list ===
141         uvdata = cStringIO.StringIO()
142         uvcoords = {}
143         uvidx = 0
144         for i in range(len(mesh.faces)):
145                 if not i%100 and mod_meshtools.show_progress:
146                         Blender.Window.DrawProgressBar(float(i)/len(mesh.faces), "Writing UV Coords")
147                 numfaceverts = len(mesh.faces[i].v)
148                 for j in range(numfaceverts-1, -1, -1):         # Reverse order
149                         u,v = mesh.faces[i].uv[j]
150                         if not uvcoords.has_key((u,v)):
151                                 uvcoords[(u,v)] = uvidx
152                                 uvidx += 1
153                                 uvdata.write(struct.pack("<ff", u,v))
154         uvdata = uvdata.getvalue()
155
156         numuvcoords = len(uvdata)/8
157         data.write(struct.pack("<l", numuvcoords))
158         data.write(uvdata)
159         #print "Number of uvcoords:", numuvcoords, '=', len(uvcoords)
160         return uvcoords
161
162 # === Write Face List ===
163 def write_FaceList(data, mesh, uvcoords):
164         data.write(struct.pack("<l", len(mesh.faces)))
165         for i in range(len(mesh.faces)):
166                 if not i%100 and mod_meshtools.show_progress:
167                         Blender.Window.DrawProgressBar(float(i)/len(mesh.faces), "Writing Faces")
168                 numfaceverts = len(mesh.faces[i].v)
169                 data.write(struct.pack("<B", 0x10))         # Cull Back Faces Flag
170                 data.write(struct.pack("<h", numfaceverts))
171                 data.write(struct.pack("<h", 0))            # Material Index
172                 for j in range(numfaceverts-1, -1, -1):         # Reverse order
173                         index = mesh.faces[i].v[j].index
174                         if mesh.hasFaceUV():
175                                 uv = mesh.faces[i].uv[j]
176                                 uvidx = uvcoords[uv]
177                         else:
178                                 uvidx = 0
179                         data.write(struct.pack("<ll", index, uvidx))
180
181 # ===========================================
182 # === Generate VCol (Vertex Colors) Chunk ===
183 # ===========================================
184 def generate_vcol(mesh):
185         data = cStringIO.StringIO()
186         data.write(struct.pack("<l", len(mesh.faces)))
187         uniquecolors = {}
188         unique_alpha = {}
189         for i in range(len(mesh.faces)):
190                 if not i%100 and mod_meshtools.show_progress:
191                         Blender.Window.DrawProgressBar(float(i)/len(mesh.faces), "Writing Vertex Colors")
192                 numfaceverts = len(mesh.faces[i].v)
193                 data.write(struct.pack("<ll", i, numfaceverts))
194                 for j in range(numfaceverts-1, -1, -1):         # Reverse order
195                         r = mesh.faces[i].col[j].r
196                         g = mesh.faces[i].col[j].g
197                         b = mesh.faces[i].col[j].b
198                         a = 100  # 100 is opaque in ts
199                         uniquecolors[(r,g,b)] = None
200                         unique_alpha[mesh.faces[i].col[j].a] = None
201                         data.write(struct.pack("<BBBB", r,g,b,a))
202
203         #print "uniquecolors:", uniquecolors.keys()
204         #print "unique_alpha:", unique_alpha.keys()
205         if len(uniquecolors) == 1:
206                 return None
207         else:
208                 return data.getvalue()
209
210 # ==================================
211 # === Generate Unit (Size) Chunk ===
212 # ==================================
213 def generate_unit():
214         data = cStringIO.StringIO()
215         data.write(struct.pack("<h", 2))
216         return data.getvalue()
217
218 # ======================================
219 # === Generate Mat1 (Material) Chunk ===
220 # ======================================
221 def generate_mat1(mesh):
222         data = cStringIO.StringIO()
223         data.write(struct.pack("<h", 0))
224         data.write(struct.pack("<ccB", "p", "a", 0))
225         data.write(struct.pack("<fff", 1.0, 1.0, 1.0))  # rgb (0.0 - 1.0)
226         data.write(struct.pack("<fffff", 1, 1, 0, 0, 1))
227         if mesh.hasFaceUV():
228                 tex_mapname = r"c:\image\maps\one-dot.tga"
229                 data.write("t:")
230                 data.write(struct.pack("<B", 0x00))
231                 data.write(struct.pack("<h", len(tex_mapname)))
232                 data.write(tex_mapname)
233                 data.write(struct.pack("<4f", 0,0, 1,1))
234         return data.getvalue()
235
236 # ============================
237 # === Generate Group Chunk ===
238 # ============================
239 def generate_grou(name):
240         data = cStringIO.StringIO()
241         write_ObjectName(data, name)
242         data.write(struct.pack("<12f", 0,0,0, 1,0,0, 0,1,0, 0,0,1))
243         data.write(struct.pack("<12f", 1,0,0,0, 0,1,0,0, 0,0,1,0))
244         return data.getvalue()
245
246 def fs_callback(filename):
247         if filename.find('.cob', -4) <= 0: filename += '.cob'
248         write(filename)
249
250 Blender.Window.FileSelector(fs_callback, "COB Export")
251
252 # === Matrix Differences between Blender & trueSpace ===
253 #
254 # For the 'Local Axes' values:
255 # The x, y, and z-axis represent a simple rotation matrix.
256 # This is equivalent to Blender's object matrix before it was
257 # combined with the object's scaling matrix.  Dividing each value
258 # by the appropriate scaling factor (and transposing at the same
259 # time) produces the original rotation matrix.
260 #
261 # For the 'Current Position' values:
262 # This is equivalent to Blender's object matrix except that the
263 # last row is omitted and the xyz location is used in the last
264 # column.  Binary format uses a 4x3 matrix, ascii format uses a 4x4
265 # matrix.
266 #
267 # For Cameras: The matrix is a little confusing.