~siretart/ubuntu/utopic/blender/libav10

« back to all changes in this revision

Viewing changes to release/scripts/addons/io_scene_map/export_map.py

  • Committer: Bazaar Package Importer
  • Author(s): Kevin Roy
  • Date: 2011-06-24 11:13:28 UTC
  • mto: (14.1.6 experimental) (1.5.1)
  • mto: This revision was merged to the branch mainline in revision 28.
  • Revision ID: james.westby@ubuntu.com-20110624111328-27ribg6l36edf2ay
Tags: upstream-2.58-svn37702
ImportĀ upstreamĀ versionĀ 2.58-svn37702

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# ##### BEGIN GPL LICENSE BLOCK #####
 
2
#
 
3
#  This program is free software; you can redistribute it and/or
 
4
#  modify it under the terms of the GNU General Public License
 
5
#  as published by the Free Software Foundation; either version 2
 
6
#  of the License, or (at your option) any later version.
 
7
#
 
8
#  This program is distributed in the hope that it will be useful,
 
9
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
#  GNU General Public License for more details.
 
12
#
 
13
#  You should have received a copy of the GNU General Public License
 
14
#  along with this program; if not, write to the Free Software Foundation,
 
15
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
16
#
 
17
# ##### END GPL LICENSE BLOCK #####
 
18
 
 
19
# <pep8 compliant>
 
20
 
 
21
import bpy
 
22
 
 
23
# TODO, make options
 
24
PREF_SCALE = 100
 
25
PREF_FACE_THICK = 0.1
 
26
PREF_GRID_SNAP = False
 
27
# Quake 1/2?
 
28
# PREF_DEF_TEX_OPTS = Draw.Create(' 0 0 0 1 1\n') # not user settable yet
 
29
# Quake 3+?
 
30
PREF_DEF_TEX_OPTS = ' 0 0 0 1 1 0 0 0\n'  # not user settable yet
 
31
 
 
32
PREF_NULL_TEX = 'NULL'  # not user settable yet
 
33
PREF_INVIS_TEX = 'common/caulk'
 
34
 
 
35
 
 
36
def face_uv_get(face):
 
37
    """ Workaround 2.5x change.
 
38
    """
 
39
    me = face.id_data
 
40
    uv_faces = me.uv_textures.active
 
41
    if uv_faces:
 
42
        return uv_faces.data[face.index]
 
43
    else:
 
44
        return None
 
45
 
 
46
 
 
47
def write_cube2brush(file, faces):
 
48
    '''
 
49
    Takes 6 faces and writes a brush,
 
50
    these faces can be from 1 mesh, 1 cube within a mesh of larger cubes
 
51
    Faces could even come from different meshes or be contrived.
 
52
    '''
 
53
    import os
 
54
    # comment only
 
55
    # file.write('// brush "%s", "%s"\n' % (ob.name, ob.data.name))
 
56
    file.write('// brush from cube\n{\n')
 
57
 
 
58
    if PREF_GRID_SNAP:
 
59
        format_vec = '( %d %d %d ) '
 
60
    else:
 
61
        format_vec = '( %.8f %.8f %.8f ) '
 
62
 
 
63
    for f in faces:
 
64
        # from 4 verts this gets them in reversed order and only 3 of them
 
65
        # 0,1,2,3 -> 2,1,0
 
66
        me = f.id_data  #XXX25
 
67
        for v in f.vertices[:][2::-1]:
 
68
            file.write(format_vec % me.vertices[v].co[:])
 
69
 
 
70
        uf = face_uv_get(f)
 
71
 
 
72
        if uf and uf.hide:
 
73
            file.write(PREF_INVIS_TEX)
 
74
        else:
 
75
            image = uf.image if uf else None
 
76
 
 
77
            if image:
 
78
                file.write(os.path.splitext(os.path.basename(image.filename))[0])
 
79
            else:
 
80
                file.write(PREF_NULL_TEX)
 
81
 
 
82
        # Texture stuff ignored for now
 
83
        file.write(PREF_DEF_TEX_OPTS)
 
84
    file.write('}\n')
 
85
 
 
86
 
 
87
def round_vec(v):
 
88
    if PREF_GRID_SNAP:
 
89
        return v.to_tuple(0)
 
90
    else:
 
91
        return v[:]
 
92
 
 
93
 
 
94
def write_face2brush(file, face):
 
95
    '''
 
96
    takes a face and writes it as a brush
 
97
    each face is a cube/brush
 
98
    '''
 
99
 
 
100
    if PREF_GRID_SNAP:
 
101
        format_vec = '( %d %d %d ) '
 
102
    else:
 
103
        format_vec = '( %.8f %.8f %.8f ) '
 
104
 
 
105
    image_text = PREF_NULL_TEX
 
106
 
 
107
    uf = face_uv_get(face)
 
108
 
 
109
    if uf and uf.hide:
 
110
        image_text = PREF_INVIS_TEX
 
111
    else:
 
112
        image = uf.image if uf else None
 
113
 
 
114
        if image:
 
115
            image_text = os.path.splitext(os.path.basename(image.filename))[0]
 
116
 
 
117
    # reuse face vertices
 
118
    _v = face.id_data.vertices  #XXX25
 
119
    f_vertices = [_v[vi] for vi in face.vertices]
 
120
    del _v  #XXX25
 
121
 
 
122
    # original verts as tuples for writing
 
123
    orig_vco = [v.co[:] for v in f_vertices]
 
124
 
 
125
    # new verts that give the face a thickness
 
126
    dist = PREF_SCALE * PREF_FACE_THICK
 
127
    new_vco = [round_vec(v.co - (v.normal * dist)) for v in f_vertices]
 
128
    #new_vco = [round_vec(v.co - (face.no * dist)) for v in face]
 
129
 
 
130
    file.write('// brush from face\n{\n')
 
131
    # front
 
132
    for co in orig_vco[2::-1]:
 
133
        file.write(format_vec % co)
 
134
    file.write(image_text)
 
135
    # Texture stuff ignored for now
 
136
    file.write(PREF_DEF_TEX_OPTS)
 
137
 
 
138
    for co in new_vco[:3]:
 
139
        file.write(format_vec % co)
 
140
    if uf and uf.use_twoside:
 
141
        file.write(image_text)
 
142
    else:
 
143
        file.write(PREF_INVIS_TEX)
 
144
 
 
145
    # Texture stuff ignored for now
 
146
    file.write(PREF_DEF_TEX_OPTS)
 
147
 
 
148
    # sides.
 
149
    if len(orig_vco) == 3:  # Tri, it seemms tri brushes are supported.
 
150
        index_pairs = ((0, 1), (1, 2), (2, 0))
 
151
    else:
 
152
        index_pairs = ((0, 1), (1, 2), (2, 3), (3, 0))
 
153
 
 
154
    for i1, i2 in index_pairs:
 
155
        for co in orig_vco[i1], orig_vco[i2], new_vco[i2]:
 
156
            file.write(format_vec % co)
 
157
        file.write(PREF_INVIS_TEX)
 
158
        file.write(PREF_DEF_TEX_OPTS)
 
159
 
 
160
    file.write('}\n')
 
161
 
 
162
 
 
163
def is_cube_facegroup(faces):
 
164
    '''
 
165
    Returens a bool, true if the faces make up a cube
 
166
    '''
 
167
    # cube must have 6 faces
 
168
    if len(faces) != 6:
 
169
        # print('1')
 
170
        return False
 
171
 
 
172
    # Check for quads and that there are 6 unique verts
 
173
    verts = {}
 
174
    for f in faces:
 
175
        f_v = f.vertices[:]
 
176
        if len(f_v) != 4:
 
177
            return False
 
178
 
 
179
        for v in f_v:
 
180
            verts[v] = 0
 
181
 
 
182
    if len(verts) != 8:
 
183
        return False
 
184
 
 
185
    # Now check that each vert has 3 face users
 
186
    for f in faces:
 
187
        f_v = f.vertices[:]
 
188
        for v in f_v:
 
189
            verts[v] += 1
 
190
 
 
191
    for v in verts.values():
 
192
        if v != 3:  # vert has 3 users?
 
193
            return False
 
194
 
 
195
    # Could we check for 12 unique edges??, probably not needed.
 
196
    return True
 
197
 
 
198
 
 
199
def is_tricyl_facegroup(faces):
 
200
    '''
 
201
    is the face group a tri cylinder
 
202
    Returens a bool, true if the faces make an extruded tri solid
 
203
    '''
 
204
 
 
205
    # cube must have 5 faces
 
206
    if len(faces) != 5:
 
207
        #  print('1')
 
208
        return False
 
209
 
 
210
    # Check for quads and that there are 6 unique verts
 
211
    verts = {}
 
212
    tottri = 0
 
213
    for f in faces:
 
214
        if len(f) == 3:
 
215
            tottri += 1
 
216
 
 
217
        for v in f:
 
218
            verts[v.index] = 0
 
219
 
 
220
    if len(verts) != 6 or tottri != 2:
 
221
        return False
 
222
 
 
223
    # Now check that each vert has 3 face users
 
224
    for f in faces:
 
225
        for v in f:
 
226
            verts[v.index] += 1
 
227
 
 
228
    for v in verts.values():
 
229
        if v != 3:  # vert has 3 users?
 
230
            return False
 
231
 
 
232
    # Could we check for 12 unique edges??, probably not needed.
 
233
    return True
 
234
 
 
235
 
 
236
def write_node_map(file, ob):
 
237
    '''
 
238
    Writes the properties of an object (empty in this case)
 
239
    as a MAP node as long as it has the property name - classname
 
240
    returns True/False based on weather a node was written
 
241
    '''
 
242
    props = [(p.name, p.value) for p in ob.game.properties]
 
243
 
 
244
    IS_MAP_NODE = False
 
245
    for name, value in props:
 
246
        if name == "classname":
 
247
            IS_MAP_NODE = True
 
248
            break
 
249
 
 
250
    if not IS_MAP_NODE:
 
251
        return False
 
252
 
 
253
    # Write a node
 
254
    file.write('{\n')
 
255
    for name_value in props:
 
256
        file.write('"%s" "%s"\n' % name_value)
 
257
    if PREF_GRID_SNAP:
 
258
        file.write('"origin" "%d %d %d"\n' %
 
259
                   tuple([round(axis * PREF_SCALE)
 
260
                          for axis in ob.matrix_world.to_translation()]))
 
261
    else:
 
262
        file.write('"origin" "%.6f %.6f %.6f"\n' %
 
263
                   tuple([axis * PREF_SCALE
 
264
                          for axis in ob.matrix_world.to_translation()]))
 
265
 
 
266
    file.write('}\n')
 
267
    return True
 
268
 
 
269
 
 
270
def export_map(context, filepath):
 
271
    """
 
272
    pup_block = [\
 
273
    ('Scale:', PREF_SCALE, 1, 1000, 'Scale the blender scene by this value.'),\
 
274
    ('Face Width:', PREF_FACE_THICK, 0.01, 10, 'Thickness of faces exported as brushes.'),\
 
275
    ('Grid Snap', PREF_GRID_SNAP, 'snaps floating point values to whole numbers.'),\
 
276
    'Null Texture',\
 
277
    ('', PREF_NULL_TEX, 1, 128, 'Export textureless faces with this texture'),\
 
278
    'Unseen Texture',\
 
279
    ('', PREF_INVIS_TEX, 1, 128, 'Export invisible faces with this texture'),\
 
280
    ]
 
281
 
 
282
    if not Draw.PupBlock('map export', pup_block):
 
283
        return
 
284
    """
 
285
    import time
 
286
    from mathutils import Vector, Matrix
 
287
    from bpy_extras import mesh_utils
 
288
 
 
289
    t = time.time()
 
290
    print("Map Exporter 0.0")
 
291
    file = open(filepath, 'w')
 
292
 
 
293
    scene = context.scene
 
294
    objects = context.selected_objects
 
295
 
 
296
    obs_mesh = []
 
297
    obs_lamp = []
 
298
    obs_surf = []
 
299
    obs_empty = []
 
300
 
 
301
    SCALE_MAT = Matrix()
 
302
    SCALE_MAT[0][0] = SCALE_MAT[1][1] = SCALE_MAT[2][2] = PREF_SCALE
 
303
 
 
304
    TOTBRUSH = TOTLAMP = TOTNODE = 0
 
305
 
 
306
    for ob in objects:
 
307
        type = ob.type
 
308
        if type == 'MESH':
 
309
            obs_mesh.append(ob)
 
310
        elif type == 'SURFACE':
 
311
            obs_surf.append(ob)
 
312
        elif type == 'LAMP':
 
313
            obs_lamp.append(ob)
 
314
        elif type == 'EMPTY':
 
315
            obs_empty.append(ob)
 
316
 
 
317
    if obs_mesh or obs_surf:
 
318
        # brushes and surf's must be under worldspan
 
319
        file.write('\n// entity 0\n')
 
320
        file.write('{\n')
 
321
        file.write('"classname" "worldspawn"\n')
 
322
 
 
323
    print("\twriting cubes from meshes")
 
324
    for ob in obs_mesh:
 
325
        dummy_mesh = ob.to_mesh(scene, True, 'PREVIEW')
 
326
 
 
327
        #print len(mesh_split2connected(dummy_mesh))
 
328
 
 
329
        # Is the object 1 cube? - object-is-a-brush
 
330
        # 1 to tx the normals also
 
331
        dummy_mesh.transform(ob.matrix_world * SCALE_MAT)
 
332
 
 
333
        if PREF_GRID_SNAP:
 
334
            for v in dummy_mesh.verts:
 
335
                v.co[:] = v.co.to_tuple(0)
 
336
 
 
337
        # High quality normals
 
338
        #XXX25: BPyMesh.meshCalcNormals(dummy_mesh)
 
339
 
 
340
        # Split mesh into connected regions
 
341
        for face_group in mesh_utils.mesh_linked_faces(dummy_mesh):
 
342
            if is_cube_facegroup(face_group):
 
343
                write_cube2brush(file, face_group)
 
344
                TOTBRUSH += 1
 
345
            elif is_tricyl_facegroup(face_group):
 
346
                write_cube2brush(file, face_group)
 
347
                TOTBRUSH += 1
 
348
            else:
 
349
                for f in face_group:
 
350
                    write_face2brush(file, f)
 
351
                    TOTBRUSH += 1
 
352
 
 
353
            #print 'warning, not exporting "%s" it is not a cube' % ob.name
 
354
        bpy.data.meshes.remove(dummy_mesh)
 
355
 
 
356
    valid_dims = 3, 5, 7, 9, 11, 13, 15
 
357
    for ob in obs_surf:
 
358
        '''
 
359
        Surf, patches
 
360
        '''
 
361
        data = ob.data
 
362
        surf_name = data.name
 
363
        mat = ob.matrix_world * SCALE_MAT
 
364
 
 
365
        # This is what a valid patch looks like
 
366
 
 
367
        """
 
368
// brush 0
 
369
{
 
370
patchDef2
 
371
{
 
372
NULL
 
373
( 3 3 0 0 0 )
 
374
(
 
375
( ( -64 -64 0 0 0 ) ( -64 0 0 0 -2 ) ( -64 64 0 0 -4 ) )
 
376
( ( 0 -64 0 2 0 ) ( 0 0 0 2 -2 ) ( 0 64 0 2 -4 ) )
 
377
( ( 64 -64 0 4 0 ) ( 64 0 0 4 -2 ) ( 80 88 0 4 -4 ) )
 
378
)
 
379
}
 
380
}
 
381
        """
 
382
        for i, nurb in enumerate(data.splines):
 
383
            u = nurb.point_count_u
 
384
            v = nurb.point_count_v
 
385
            if u in valid_dims and v in valid_dims:
 
386
 
 
387
                file.write('// brush %d surf_name\n' % i)
 
388
                file.write('{\n')
 
389
                file.write('patchDef2\n')
 
390
                file.write('{\n')
 
391
                file.write('NULL\n')
 
392
                file.write('( %d %d 0 0 0 )\n' % (u, v))
 
393
                file.write('(\n')
 
394
 
 
395
                u_iter = 0
 
396
                for p in nurb.points:
 
397
 
 
398
                    if u_iter == 0:
 
399
                        file.write('(')
 
400
 
 
401
                    u_iter += 1
 
402
 
 
403
                    # add nmapping 0 0 ?
 
404
                    if PREF_GRID_SNAP:
 
405
                        file.write(" ( %d %d %d 0 0 )" %
 
406
                                   round_vec(p.co.xyz * mat))
 
407
                    else:
 
408
                        file.write(' ( %.6f %.6f %.6f 0 0 )' %
 
409
                                   (p.co.xyz * mat)[:])
 
410
 
 
411
                    # Move to next line
 
412
                    if u_iter == u:
 
413
                        file.write(' )\n')
 
414
                        u_iter = 0
 
415
 
 
416
                file.write(')\n')
 
417
                file.write('}\n')
 
418
                file.write('}\n')
 
419
                # Debugging
 
420
                # for p in nurb: print 'patch', p
 
421
 
 
422
            else:
 
423
                print("Warning: not exporting patch",
 
424
                      surf_name, u, v, 'Unsupported')
 
425
 
 
426
    if obs_mesh or obs_surf:
 
427
        file.write('}\n')  # end worldspan
 
428
 
 
429
    print("\twriting lamps")
 
430
    for ob in obs_lamp:
 
431
        print("\t\t%s" % ob.name)
 
432
        lamp = ob.data
 
433
        file.write('{\n')
 
434
        file.write('"classname" "light"\n')
 
435
        file.write('"light" "%.6f"\n' % (lamp.distance * PREF_SCALE))
 
436
        if PREF_GRID_SNAP:
 
437
            file.write('"origin" "%d %d %d"\n' %
 
438
                       tuple([round(axis * PREF_SCALE)
 
439
                              for axis in ob.matrix_world.to_translation()]))
 
440
        else:
 
441
            file.write('"origin" "%.6f %.6f %.6f"\n' %
 
442
                       tuple([axis * PREF_SCALE
 
443
                              for axis in ob.matrix_world.to_translation()]))
 
444
 
 
445
        file.write('"_color" "%.6f %.6f %.6f"\n' % tuple(lamp.color))
 
446
        file.write('"style" "0"\n')
 
447
        file.write('}\n')
 
448
        TOTLAMP += 1
 
449
 
 
450
    print("\twriting empty objects as nodes")
 
451
    for ob in obs_empty:
 
452
        if write_node_map(file, ob):
 
453
            print("\t\t%s" % ob.name)
 
454
            TOTNODE += 1
 
455
        else:
 
456
            print("\t\tignoring %s" % ob.name)
 
457
 
 
458
    file.close()
 
459
 
 
460
    print("Exported Map in %.4fsec" % (time.time() - t))
 
461
    print("Brushes: %d  Nodes: %d  Lamps %d\n" % (TOTBRUSH, TOTNODE, TOTLAMP))
 
462
 
 
463
 
 
464
def save(operator,
 
465
         context,
 
466
         filepath=None,
 
467
         ):
 
468
 
 
469
    export_map(context, filepath)
 
470
    
 
471
    return {'FINISHED'}