Scripts:
[blender.git] / release / scripts / truespace_import.py
1 #!BPY
2
3 """
4 Name: 'TrueSpace (.cob)...'
5 Blender: 232
6 Group: 'Import'
7 Tooltip: 'Import TrueSpace Object File Format (.cob)'
8 """
9
10 __author__ = "Anthony D'Agostino (Scorpius)"
11 __url__ = ("blender", "elysiun",
12 "Author's homepage, http://www.redrival.com/scorpius")
13 __version__ = "Part of IOSuite 0.5"
14
15 __bpydoc__ = """\
16 This script imports TrueSpace files to Blender
17
18 TrueSpace is a commercial modeling and rendering application. The .cob
19 file format is composed of 'chunks,' is well defined, and easy to read and
20 write. It's very similar to LightWave's lwo format.
21
22 Usage:<br>
23         Execute this script from the "File->Import" menu and choose a TrueSpace
24 file to open.
25
26 Supported:<br>
27         Meshes only. Supports UV Coordinates. COB files in ascii format can't be
28 read.
29
30 Missing:<br>
31         Materials, and Vertex Color info will be ignored.
32
33 Known issues:<br>
34         Triangulation of convex polygons works fine, and uses a very simple
35 fanning algorithm. Convex polygons (i.e., shaped like the letter "U")
36 require a different algorithm, and will be triagulated incorrectly.
37
38 Notes:<br>
39         There are a few differences between how Blender & TrueSpace represent
40 their objects' transformation matrices. Blender simply uses a 4x4 matrix,
41 and trueSpace splits it into the following two fields.
42
43         For the 'Local Axes' values: The x, y, and z-axis represent a simple
44 rotation matrix.  This is equivalent to Blender's object matrix before
45 it was combined with the object's scaling matrix. Dividing each value by
46 the appropriate scaling factor (and transposing at the same time)
47 produces the original rotation matrix.
48
49         For the 'Current Position' values:  This is equivalent to Blender's
50 object matrix except that the last row is omitted and the xyz location
51 is used in the last column. Binary format uses a 4x3 matrix, ascii
52 format uses a 4x4 matrix.
53
54 For Cameras: The matrix here gets a little confusing, and I'm not sure of
55 how to handle it.
56 """
57
58 # $Id$
59 #
60 # +---------------------------------------------------------+
61 # | Copyright (c) 2001 Anthony D'Agostino                   |
62 # | http://www.redrival.com/scorpius                        |
63 # | scorpius@netzero.com                                    |
64 # | June 12, 2001                                           |
65 # | Released under the Blender Artistic Licence (BAL)       |
66 # | Import Export Suite v0.5                                |
67 # +---------------------------------------------------------+
68 # | Read and write Caligari trueSpace File Format (*.cob)   |
69 # +---------------------------------------------------------+
70
71 import Blender, mod_meshtools
72 import struct, chunk, os, cStringIO, time
73
74 # =======================
75 # === COB Chunk Class ===
76 # =======================
77 class CobChunk(chunk.Chunk):
78         def __init__(self, file, align = 0, bigendian = 0, inclheader = 0): #$ COB
79                 self.closed = 0
80                 self.align = align      # whether to align to word (2-byte) boundaries
81                 if bigendian:
82                         strflag = '>'
83                 else:
84                         strflag = '<'
85                 self.file = file
86                 self.chunkname = file.read(4)
87                 if len(self.chunkname) < 4:
88                         raise EOFError
89                 self.major_ver, = struct.unpack(strflag+'h', file.read(2))      #$ COB
90                 self.minor_ver, = struct.unpack(strflag+'h', file.read(2))      #$ COB
91                 self.chunk_id,  = struct.unpack(strflag+'l', file.read(4))      #$ COB
92                 self.parent_id, = struct.unpack(strflag+'l', file.read(4))      #$ COB
93                 try:
94                         self.chunksize = struct.unpack(strflag+'l', file.read(4))[0]
95                 except struct.error:
96                         raise EOFError
97                 if inclheader:
98                         self.chunksize = self.chunksize - 20                                            #$ COB
99                 self.size_read = 0
100                 try:
101                         self.offset = self.file.tell()
102                 except:
103                         self.seekable = 0
104                 else:
105                         self.seekable = 1
106
107 # ============================
108 # === Read COB File Header ===
109 # ============================
110 def read_header(file):
111         magic,   = struct.unpack("<9s", file.read(9))
112         version, = struct.unpack("<6s", file.read(6))
113         format,  = struct.unpack("<1c", file.read(1))
114         endian,  = struct.unpack("<2s", file.read(2))
115         misc,    = struct.unpack("13s", file.read(13))
116         newline, = struct.unpack("<1B", file.read(1))
117         return format
118
119 # ========================================
120 # === Read PolH (Polygonal Data) Chunk ===
121 # ========================================
122 def read_polh(chunk):
123         data = cStringIO.StringIO(chunk.read())
124         oname = read_ObjectName(data)
125         local = read_LocalAxes(data)
126         crpos = read_CurrentPosition(data)
127         verts = read_VertexList(data)
128         uvcoords = read_UVCoords(data)
129         faces, facesuv = read_FaceList(data, chunk)
130         return verts, faces, oname, facesuv, uvcoords
131
132 # === Read Object Name ===
133 def read_ObjectName(data):
134         dupecount, namelen = struct.unpack("<hh", data.read(4))
135         objname = data.read(namelen)
136         if objname == '': objname = 'NoName'
137         if dupecount > 0: objname = objname + ', ' + `dupecount`
138         return objname
139
140 # === Read Local Axes ===
141 def read_LocalAxes(data):
142         location = struct.unpack("<fff", data.read(12))
143         rotation_matrix=[]
144         for i in range(3):
145                 row = struct.unpack("<fff", data.read(12))
146                 #print "% f % f % f" % row
147                 rotation_matrix.append(list(row))
148         #print
149         rotation_matrix = mod_meshtools.transpose(rotation_matrix)
150
151 # === Read Current Position ===
152 def read_CurrentPosition(data):
153         transformation_matrix=[]
154         for i in range(3):
155                 row = struct.unpack("<ffff", data.read(16))
156                 #print "% f % f % f % f" % row
157                 transformation_matrix.append(list(row))
158         #print
159
160 # === Read Vertex List ===
161 def read_VertexList(data):
162         verts = []
163         numverts, = struct.unpack("<l", data.read(4))
164         for i in range(numverts):
165                 if not i%100 and mod_meshtools.show_progress:
166                         Blender.Window.DrawProgressBar(float(i)/numverts, "Reading Verts")
167                 x, y, z = struct.unpack("<fff", data.read(12))
168                 verts.append((y, -x, z))
169         return verts
170
171 # === Read UV Vertex List ===
172 def read_UVCoords(data):
173         uvcoords = []
174         numuvcoords, = struct.unpack("<l", data.read(4))
175         for i in range(numuvcoords):
176                 if not i%100 and mod_meshtools.show_progress:
177                         Blender.Window.DrawProgressBar(float(i)/numuvcoords, "Reading UV Coords")
178                 uv = struct.unpack("<ff", data.read(8))
179                 uvcoords.append(uv)
180
181         #print "num uvcoords:", len(uvcoords)
182         #for i in range(len(uvcoords)): print "%.4f, %.4f" % uvcoords[i]
183         return uvcoords
184
185 # === Read Face List ===
186 def read_FaceList(data, chunk):
187         faces = []                                 ; facesuv = []
188         numfaces, = struct.unpack("<l", data.read(4))
189         for i in range(numfaces):
190                 if not i%100 and mod_meshtools.show_progress:
191                         Blender.Window.DrawProgressBar(float(i)/numfaces, "Reading Faces")
192
193                 face_flags, numfaceverts = struct.unpack("<Bh", data.read(3))
194
195                 if (face_flags & 0x08) == 0x08:
196                         print "face #" + `i-1` + " contains a hole."
197                         pass
198                 else:
199                         data.read(2)  # Material Index
200
201                 facev = []                         ; faceuv = []
202                 for j in range(numfaceverts):
203                         index, uvidx = struct.unpack("<ll", data.read(8))
204                         facev.append(index); faceuv.append(uvidx)
205                 facev.reverse()            ; faceuv.reverse()
206                 faces.append(facev)    ; facesuv.append(faceuv)
207
208         if chunk.minor_ver == 6:
209                 DrawFlags, RadiosityQuality = struct.unpack("<lh", data.read(6))
210         if chunk.minor_ver == 8:
211                 DrawFlags, = struct.unpack("<l", data.read(4))
212
213         return faces                       , facesuv
214
215 # =============================
216 # === Read trueSpace Format ===
217 # =============================
218 def read(filename):
219         start = time.clock()
220         file = open(filename, "rb")
221
222         # === COB header ===
223         if read_header(file) == 'A':
224                 print "Can't read ASCII format"
225                 return
226
227         while 1:
228                 try:
229                         cobchunk = CobChunk(file)
230                 except EOFError:
231                         break
232                 if cobchunk.chunkname == "PolH":
233                         verts, faces, objname, facesuv, uvcoords = read_polh(cobchunk)
234                         mod_meshtools.create_mesh(verts, faces, objname, facesuv, uvcoords)
235
236                         '''
237                         object = Blender.Object.GetSelected()
238                         obj = Blender.Object.Get(objname)
239                         obj.loc = location
240                         obj.rot = mod_meshtools.mat2euler(rotation_matrix)
241                         obj.size = (transformation_matrix[0][0]/rotation_matrix[0][0],
242                                                 transformation_matrix[1][1]/rotation_matrix[1][1],
243                                                 transformation_matrix[2][2]/rotation_matrix[2][2])
244
245                         '''
246                 else:
247                         cobchunk.skip()
248
249         Blender.Window.DrawProgressBar(1.0, '')  # clear progressbar
250         file.close()
251         end = time.clock()
252         seconds = " in %.2f %s" % (end-start, "seconds")
253         message = "Successfully imported " + os.path.basename(filename) + seconds
254         mod_meshtools.print_boxed(message)
255         #print "objname :", objname
256         #print "numverts:", len(verts)
257         #print "numfaces:", len(faces)
258
259 def fs_callback(filename):
260         read(filename)
261
262 Blender.Window.FileSelector(fs_callback, "Import COB")
263