4
Name: 'TrueSpace (.cob)...'
7
Tooltip: 'Export selected meshes to TrueSpace File Format (.cob)'
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"
16
This script exports meshes to TrueSpace file format.
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.
23
Select meshes to be exported and run this script from "File->Export" menu.
26
Vertex colors will be exported, if they are present.
29
Before exporting to .cob format, the mesh must have real-time UV
30
coordinates. Press the FKEY to assign them.
33
There are a few differences between how Blender & TrueSpace represent
34
their objects' transformation matrices. Blender simply uses a 4x4 matrix,
35
and trueSpace splits it into the following two fields.
37
For the 'Local Axes' values: The x, y, and z-axis represent a simple
38
rotation matrix. This is equivalent to Blender's object matrix before
39
it was combined with the object's scaling matrix. Dividing each value by
40
the appropriate scaling factor (and transposing at the same time)
41
produces the original rotation matrix.
43
For the 'Current Position' values: This is equivalent to Blender's
44
object matrix except that the last row is omitted and the xyz location
45
is used in the last column. Binary format uses a 4x3 matrix, ascii
46
format uses a 4x4 matrix.
48
For Cameras: The matrix here gets a little confusing, and I'm not sure of
52
# $Id: truespace_export.py,v 1.11 2007/01/27 04:58:09 campbellbarton Exp $
54
# +---------------------------------------------------------+
55
# | Copyright (c) 2001 Anthony D'Agostino |
56
# | http://www.redrival.com/scorpius |
57
# | scorpius@netzero.com |
59
# | Read and write Caligari trueSpace File Format (*.cob) |
60
# +---------------------------------------------------------+
62
# ***** BEGIN GPL LICENSE BLOCK *****
64
# This program is free software; you can redistribute it and/or
65
# modify it under the terms of the GNU General Public License
66
# as published by the Free Software Foundation; either version 2
67
# of the License, or (at your option) any later version.
69
# This program is distributed in the hope that it will be useful,
70
# but WITHOUT ANY WARRANTY; without even the implied warranty of
71
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
72
# GNU General Public License for more details.
74
# You should have received a copy of the GNU General Public License
75
# along with this program; if not, write to the Free Software Foundation,
76
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
78
# ***** END GPL LICENCE BLOCK *****
80
import Blender, meshtools
81
import struct, cStringIO, time
83
# ==============================
84
# === Write trueSpace Format ===
85
# ==============================
88
file = open(filename, "wb")
89
objects = Blender.Object.GetSelected()
93
G,P,V,U,M = 1000,2000,3000,4000,5000
94
for obj_index, obj in enumerate(objects):
96
meshname = obj.getData(name_only=1)
97
mesh = Blender.NMesh.GetRaw(meshname)
101
grou = generate_grou('Group ' + `obj_index+1`)
102
polh = generate_polh(objname, obj, mesh)
103
if meshtools.has_vertex_colors(mesh): vcol = generate_vcol(mesh)
104
unit = generate_unit()
105
mat1 = generate_mat1(mesh)
107
if obj_index == 0: X = 0
109
write_chunk(file, "Grou", 0, 1, G, X, grou)
110
write_chunk(file, "PolH", 0, 4, P, G, polh)
111
if meshtools.has_vertex_colors(mesh) and vcol:
112
write_chunk(file, "VCol", 1, 0, V, P, vcol)
113
write_chunk(file, "Unit", 0, 1, U, P, unit)
114
write_chunk(file, "Mat1", 0, 5, M, P, mat1)
117
G,P,V,U,M = map(lambda x: x+1, [G,P,V,U,M])
119
write_chunk(file, "END ", 1, 0, 0, 0, '') # End Of File Chunk
121
Blender.Window.DrawProgressBar(1.0, '') # clear progressbar
124
seconds = " in %.2f %s" % (end-start, "seconds")
125
message = "Successfully exported " + filename.split('\\')[-1].split('/')[-1] + seconds
126
meshtools.print_boxed(message)
128
# =============================
129
# === Write COB File Header ===
130
# =============================
131
def write_header(file):
132
file.write("Caligari V00.01BLH"+" "*13+"\n")
134
# ===================
135
# === Write Chunk ===
136
# ===================
137
def write_chunk(file, name, major, minor, chunk_id, parent_id, data):
139
file.write(struct.pack("<2h", major, minor))
140
file.write(struct.pack("<2l", chunk_id, parent_id))
141
file.write(struct.pack("<1l", len(data)))
144
# ============================================
145
# === Generate PolH (Polygonal Data) Chunk ===
146
# ============================================
147
def generate_polh(objname, obj, mesh):
148
data = cStringIO.StringIO()
149
write_ObjectName(data, objname)
150
write_LocalAxes(data, obj)
151
write_CurrentPosition(data, obj)
152
write_VertexList(data, mesh)
153
uvcoords = write_UVCoordsList(data, mesh)
154
write_FaceList(data, mesh, uvcoords)
155
return data.getvalue()
157
# === Write Object Name ===
158
def write_ObjectName(data, objname):
159
data.write(struct.pack("<h", 0)) # dupecount
160
data.write(struct.pack("<h", len(objname)))
163
# === Write Local Axes ===
164
def write_LocalAxes(data, obj):
166
data.write(struct.pack("<fff", mat[3][0], mat[3][1], mat[3][2]))
167
data.write(struct.pack("<fff", mat[0][0]/obj.SizeX, mat[1][0]/obj.SizeX, mat[2][0]/obj.SizeX))
168
data.write(struct.pack("<fff", mat[0][1]/obj.SizeY, mat[1][1]/obj.SizeY, mat[2][1]/obj.SizeY))
169
data.write(struct.pack("<fff", mat[0][2]/obj.SizeZ, mat[1][2]/obj.SizeZ, mat[2][2]/obj.SizeZ))
171
# === Write Current Position ===
172
def write_CurrentPosition(data, obj):
174
data.write(struct.pack("<ffff", mat[0][0], mat[0][1], mat[0][2], mat[3][0]))
175
data.write(struct.pack("<ffff", mat[1][0], mat[1][1], mat[1][2], mat[3][1]))
176
data.write(struct.pack("<ffff", mat[2][0], mat[2][1], mat[2][2], mat[3][2]))
178
# === Write Vertex List ===
179
def write_VertexList(data, mesh):
180
data.write(struct.pack("<l", len(mesh.verts)))
181
for i, v in enumerate(mesh.verts):
182
if not i%100 and meshtools.show_progress:
183
Blender.Window.DrawProgressBar(float(i)/len(mesh.verts), "Writing Verts")
185
data.write(struct.pack("<fff", -y, x, z))
187
# === Write UV Vertex List ===
188
def write_UVCoordsList(data, mesh):
189
if not mesh.hasFaceUV():
190
data.write(struct.pack("<l", 1))
191
data.write(struct.pack("<2f", 0,0))
193
# === Default UV Coords (one image per face) ===
194
# data.write(struct.pack("<l", 4))
195
# data.write(struct.pack("<8f", 0,0, 0,1, 1,1, 1,0))
196
# return {(0,0): 0, (0,1): 1, (1,1): 2, (1,0): 3}
197
# === Default UV Coords (one image per face) ===
199
# === collect, remove duplicates, add indices, and write the uv list ===
200
uvdata = cStringIO.StringIO()
203
for i, f in enumerate(mesh.faces):
204
if not i%100 and meshtools.show_progress:
205
Blender.Window.DrawProgressBar(float(i)/len(mesh.faces), "Writing UV Coords")
206
numfaceverts = len(f)
207
for j in xrange(numfaceverts-1, -1, -1): # Reverse order
209
if not uvcoords.has_key((u,v)):
210
uvcoords[(u,v)] = uvidx
212
uvdata.write(struct.pack("<ff", u,v))
213
uvdata = uvdata.getvalue()
215
numuvcoords = len(uvdata)/8
216
data.write(struct.pack("<l", numuvcoords))
218
#print "Number of uvcoords:", numuvcoords, '=', len(uvcoords)
221
# === Write Face List ===
222
def write_FaceList(data, mesh, uvcoords):
223
data.write(struct.pack("<l", len(mesh.faces)))
224
for i in xrange(len(mesh.faces)):
225
if not i%100 and meshtools.show_progress:
226
Blender.Window.DrawProgressBar(float(i)/len(mesh.faces), "Writing Faces")
227
numfaceverts = len(mesh.faces[i].v)
228
data.write(struct.pack("<B", 0x10)) # Cull Back Faces Flag
229
data.write(struct.pack("<h", numfaceverts))
230
data.write(struct.pack("<h", 0)) # Material Index
231
for j in xrange(numfaceverts-1, -1, -1): # Reverse order
232
index = mesh.faces[i].v[j].index
234
uv = mesh.faces[i].uv[j]
238
data.write(struct.pack("<ll", index, uvidx))
240
# ===========================================
241
# === Generate VCol (Vertex Colors) Chunk ===
242
# ===========================================
243
def generate_vcol(mesh):
244
data = cStringIO.StringIO()
245
data.write(struct.pack("<l", len(mesh.faces)))
248
for i in xrange(len(mesh.faces)):
249
if not i%100 and meshtools.show_progress:
250
Blender.Window.DrawProgressBar(float(i)/len(mesh.faces), "Writing Vertex Colors")
251
numfaceverts = len(mesh.faces[i].v)
252
data.write(struct.pack("<ll", i, numfaceverts))
253
for j in xrange(numfaceverts-1, -1, -1): # Reverse order
254
r = mesh.faces[i].col[j].r
255
g = mesh.faces[i].col[j].g
256
b = mesh.faces[i].col[j].b
257
a = 100 # 100 is opaque in ts
258
uniquecolors[(r,g,b)] = None
259
unique_alpha[mesh.faces[i].col[j].a] = None
260
data.write(struct.pack("<BBBB", r,g,b,a))
262
#print "uniquecolors:", uniquecolors.keys()
263
#print "unique_alpha:", unique_alpha.keys()
264
if len(uniquecolors) == 1:
267
return data.getvalue()
269
# ==================================
270
# === Generate Unit (Size) Chunk ===
271
# ==================================
273
data = cStringIO.StringIO()
274
data.write(struct.pack("<h", 2))
275
return data.getvalue()
277
# ======================================
278
# === Generate Mat1 (Material) Chunk ===
279
# ======================================
280
def generate_mat1(mesh):
282
def get_crufty_mesh_image():
283
'''Crufty because it only uses 1 image
291
data = cStringIO.StringIO()
292
data.write(struct.pack("<h", 0))
293
data.write(struct.pack("<ccB", "p", "a", 0))
294
data.write(struct.pack("<fff", 1.0, 1.0, 1.0)) # rgb (0.0 - 1.0)
295
data.write(struct.pack("<fffff", 1, 1, 0, 0, 1))
297
tex_mapname = get_crufty_mesh_image()
301
data.write(struct.pack("<B", 0x00))
302
data.write(struct.pack("<h", len(tex_mapname)))
303
data.write(tex_mapname)
304
data.write(struct.pack("<4f", 0,0, 1,1))
305
return data.getvalue()
307
# ============================
308
# === Generate Group Chunk ===
309
# ============================
310
def generate_grou(name):
311
data = cStringIO.StringIO()
312
write_ObjectName(data, name)
313
data.write(struct.pack("<12f", 0,0,0, 1,0,0, 0,1,0, 0,0,1))
314
data.write(struct.pack("<12f", 1,0,0,0, 0,1,0,0, 0,0,1,0))
315
return data.getvalue()
317
def fs_callback(filename):
318
if not filename.lower().endswith('.cob'): filename += '.cob'
321
if __name__ == '__main__':
322
Blender.Window.FileSelector(fs_callback, "Export COB", Blender.sys.makename(ext='.cob'))
324
# === Matrix Differences between Blender & trueSpace ===
326
# For the 'Local Axes' values:
327
# The x, y, and z-axis represent a simple rotation matrix.
328
# This is equivalent to Blender's object matrix before it was
329
# combined with the object's scaling matrix. Dividing each value
330
# by the appropriate scaling factor (and transposing at the same
331
# time) produces the original rotation matrix.
333
# For the 'Current Position' values:
334
# This is equivalent to Blender's object matrix except that the
335
# last row is omitted and the xyz location is used in the last
336
# column. Binary format uses a 4x3 matrix, ascii format uses a 4x4
339
# For Cameras: The matrix is a little confusing.