soc-2008-mxcurioni: merged changes to revision 14798, compilation works for rendering...
[blender-staging.git] / release / scripts / xsi_export.py
1 #!BPY
2
3
4 """
5 Name: 'SoftImage XSI (.xsi)...'
6 Blender: 236
7 Group: 'Export'
8 Tooltip: 'Export to a SoftImage XSI file'
9 """
10
11 __author__ = ("Elira")
12 __url__ = ["Author's site, http://www.creative-realms.net/~elira/blender.html",
13 "SoftImage's site, www.softimage.com", "elysiun"]
14 __email__ = ["scripts"]
15 __version__ = "2005/11/01"
16
17
18 __bpydoc__ = """\
19 This script exports to the XSI format.
20
21 Usage:
22
23 Run this script from "File->Export" menu.
24
25 Note:<br>
26 - Updates by Mal Duffin, to assist with XSI to Shockwave 3D conversion.
27 """
28
29 # $Id: xsi_export.py,v 1.4.6 2005/11/01 
30 #
31 #------------------------------------------------------------------------
32 # XSI exporter for blender 2.36 or above
33 #
34 # ***** BEGIN GPL LICENSE BLOCK *****
35 #
36 # This program is free software; you can redistribute it and/or
37 # modify it under the terms of the GNU General Public License
38 # as published by the Free Software Foundation; either version 2
39 # of the License, or (at your option) any later version.
40 #
41 # This program is distributed in the hope that it will be useful,
42 # but WITHOUT ANY WARRANTY; without even the implied warranty of
43 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
44 # GNU General Public License for more details.
45 #
46 # You should have received a copy of the GNU General Public License
47 # along with this program; if not, write to the Free Software Foundation,
48 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
49 #
50 # ***** END GPL LICENCE BLOCK *****
51 #
52
53
54 #
55 # ---------------------------------------------------------------------------
56 # XSI Export V 1.4.1 by Elira (at) creative-realms (dot) net
57
58 # Updates by Mal Duffin, to assist with XSI to Shockwave 3D conversion
59 # ---------------------------------------------------------------------------
60 # 0.0.0 - This header and having blender ID the file.
61 # 0.1.0 - Output the statis xsi header elements
62 # 0.2.0 - create a full shell output (no content just structure)
63 # 0.3.0 - output used materials from the full materials list
64 # 0.4.0 - output the object model minor data
65 # 0.5.0 - output the object shape data, storing a uv table
66 # 0.6.0 - output the triangle lists (uv references stored uv table)
67 # 0.7.0 - convert output to genuine file writes.
68 # 1.0.0 - Admit this script exists and wait for flames
69 # 1.1.0 - Correctly export mesh shapes
70 # 1.2.0 - Mesh positioning corrected, added back normals
71 # 1.3.0 - conditionally output uv co-ordinates
72 # 1.4.0 - export vertex paint colours.
73 # ---------------------------------------------------------------------------
74 # 1.4.1 - added basic normal export code, 
75 #         to get XSI to Shockwave 3D converter working ( Mal Duffin )
76 # 1.4.2 - invalid mesh checking
77 #         better normal exporting
78 #         general code clean up
79 # 1.4.3 - basic light exporting
80 #         fix for ambient light being ignored by Shockwave 3D converter
81 # 1.4.4 - basic camera exporting
82 # 1.4.5 - exports normals correctly
83 # 1.4.6 - exports multiple materials per object
84 # ---------------------------------------------------------------------------
85 # TO DO
86 # - Support texturing
87 #  - for both methods of texturing ( render method, and Game Engine method )
88 # ---------------------------------------------------------------------------
89 # add required modules
90
91 import Blender
92 from Blender import sys as bsys
93 from Blender import Mathutils
94 from Blender import Lamp
95 from Blender import Camera
96 import math
97
98
99
100 # ---------------------------------------------------------------------------
101 # globals to make things a lot lot easier
102 OBJ = []                # the object list
103 MAT = []                # the materials list 
104 UVC = []                # uv vert co-ords
105 UVI = []                # uv vert index
106 VCC = []                # vert colour co-ords
107 VCI = []                # vert colour index
108 FD  = []                # file handle
109 NORMALS = []                    # normal list
110 mats = []
111 EXPORT_DIR = ''
112 WORLD = Blender.World.GetCurrent() 
113
114 # ---------------------------------------------------------------------------
115 # get_path returns the path portion o/wf the supplied filename.
116 # ---------------------------------------------------------------------------
117 def get_path(file):
118   l=len(file)
119   r=0
120   for i in range(l, 0, -1):
121     if r == 0:
122       if file[i-1] == "/" or file[i-1] == "\\":
123         r = i
124   return file[:r]
125
126
127
128 # ---------------------------------------------------------------------------
129 # r2d - radians to degrees
130 # ---------------------------------------------------------------------------
131 def r2d(r):
132   return round(r*180.0/math.pi,4)
133
134
135
136 # ---------------------------------------------------------------------------
137 # d2r - degrees to radians
138 # ---------------------------------------------------------------------------
139 def d2r(d):
140   return (d*math.pi)/180.0
141
142
143
144 # ---------------------------------------------------------------------------
145 # get_filename returns the filename 
146 # ---------------------------------------------------------------------------
147 def get_filename(file):
148   l=len(file)
149   r=0
150   for i in range(l, 0, -1):
151     if r == 0:
152       if file[i-1] == "/" or file[i-1] == "\\":
153         r = i
154   return file[r:]
155
156
157 # ---------------------------------------------------------------------------
158 # find materials returns all materials on an object.
159 # ---------------------------------------------------------------------------
160 def get_materials(obj):
161
162   # any materials attached to the object itself
163   mats = obj.getMaterials(0)
164
165   if 'Mesh' != obj.type:
166     return mats
167
168   # now drop down to the mesh level
169   #mesh = Blender.NMesh.GetRaw(obj.data.name)
170
171   mats.extend(obj.getData(mesh=1).materials)
172
173   # return the materials list
174   
175   # Is this correct!!?? - None materials raise an error otherwise
176   # but it might screw up the indicies.. TODO... check the exported files.
177   return [m for m in mats if m] 
178
179 # ---------------------------------------------------------------------------
180 # do_header writes out the header data
181 # ---------------------------------------------------------------------------
182 def do_header():
183
184   global FD
185
186   # this says which xsi version
187   FD.write("xsi 0300txt 0032\n\n")
188
189   # static fileinfo block
190   FD.write("SI_FileInfo  {\n")
191   FD.write("    \"Blender Scene\",\n")
192   FD.write("    \"Blender User\",\n")
193   FD.write("    \"Now\",\n")
194   FD.write("    \"xsi_export Blender Scene Exporter\",\n")
195   FD.write("}\n\n")
196
197   # static scene block
198   FD.write("SI_Scene no_name {\n")
199   FD.write("    \"FRAMES\",\n")
200   FD.write("    0.000000,\n")
201   FD.write("    100.000000,\n")
202   FD.write("    30.000000,\n")
203   FD.write("}\n\n")
204
205   # static co-ordinate system block
206   FD.write("SI_CoordinateSystem coord {\n")
207   FD.write("    1,\n")
208   FD.write("    0,\n")
209   FD.write("    1,\n")
210   FD.write("    0,\n")
211   FD.write("    5,\n")
212   FD.write("    2,\n")
213   FD.write("}\n\n")
214
215   # static angle block 
216   FD.write("SI_Angle  {\n")
217   FD.write("    0,\n")
218   FD.write("}\n\n")
219
220   # static ambience block 
221   if WORLD:     ambient = WORLD.getAmb()
222   else:         ambient = 0,0,0
223
224   FD.write("SI_Ambience  {\n")
225   FD.write("    %f,\n" % ambient[0])
226   FD.write("    %f,\n" % ambient[1])
227   FD.write("    %f,\n" % ambient[2])
228   FD.write("}\n\n")
229
230
231
232 # ---------------------------------------------------------------------------
233 # do_materiallibrary writes out the materials subsection.
234 # ---------------------------------------------------------------------------
235 def do_materiallibrary():
236
237   global OBJ, MAT, FD
238
239   # set some flags first
240   mnum = 0
241
242   # run through every material, how many used?
243   for mat in MAT:
244     nmat = mat.name
245
246     # first, is this material on any of the objects.
247     f = 0
248     for obj in OBJ:
249       ml = get_materials(obj)
250       for mli in ml:
251         nmli = mli.name
252         if nmli == nmat:
253           f = 1
254           mnum += 1
255           break
256       if f == 1:
257         break
258
259   bCreateDefault = 0
260   # if none then exit
261   if not mnum:
262     bCreateDefault = 1
263 #    return
264     
265   # get to work create the materiallibrary wrapper and fill.
266   FD.write("SI_MaterialLibrary  {\n")
267   FD.write("    " + str(mnum) + ",\n")
268
269   # run through every material, write the used ones
270   for mat in MAT:
271     nmat = mat.name
272
273     # find out if on any object, if so we write.
274     f = 0
275     for obj in OBJ:
276       ml = get_materials(obj)
277       for mli in ml:
278         nmli = mli.name
279         if nmli == nmat:
280           do_material(mat)
281           f = 1
282           break
283       if f == 1:
284         break
285
286   if bCreateDefault == 1:
287     do_material ( 0 )
288
289   # clean up
290   FD.write("}\n\n")
291
292
293 def removeSpacesFromName(name):
294   name = name.replace ( " ", "_" )
295   return name
296
297
298 # ---------------------------------------------------------------------------
299 # do_material writes out this material.
300 # ---------------------------------------------------------------------------
301 def do_material(mat):
302
303   global FD
304
305   if mat == 0:
306     name = "__default"
307     cr = 1.0
308     cg = 1.0
309     cb = 1.0    
310     ca = 1.0
311     sp = 0.0
312     sr = 0.0
313     sg = 0.0
314     sb = 0.0
315     em = 0.0
316     am = 1.0
317     sm = 0
318   else:
319                 
320
321     # get the name first
322     name = mat.name
323
324    # face colour                r, g, b, a
325   # power (spec decay)  fl
326   # spec colour         r, g, b
327   # emmisive colourm    r, g, b
328   # shading model       int   constant, lambert, phong, blinn, shadow, vertex
329   # ambient colour      r, g, b
330
331   # get and print the base material block
332     cr, cg, cb = mat.getRGBCol()
333     ca = mat.getAlpha()
334
335     sp = 0.0
336     sr, sg, sb = mat.getSpecCol()
337     em = mat.getEmit()
338     am = mat.getAmb()
339  
340   # how do we render this material? start with constant (0)
341     sm = 0
342     fl = mat.getMode()
343     if fl & Blender.Material.Modes['VCOL_PAINT']:
344       sm = 5
345   
346
347   FD.write("    SI_Material " + removeSpacesFromName(name) + " {\n")
348   FD.write("            %f,\n" % cr)
349   FD.write("            %f,\n" % cg)
350   FD.write("            %f,\n" % cb)
351   FD.write("            %f,\n" % ca)
352   FD.write("            %f,\n" % sp)
353   FD.write("            %f,\n" % sr)
354   FD.write("            %f,\n" % sg)
355   FD.write("            %f,\n" % sb)
356   FD.write("            %f,\n" % em)
357   FD.write("            %f,\n" % em)
358   FD.write("            %f,\n" % em)
359   FD.write("            %d,\n" % sm)
360   #FD.write("           %f,\n" % am)
361   #FD.write("           %f,\n" % am)
362   #FD.write("           %f,\n" % am)
363   FD.write("            %f,\n" % cr)
364   FD.write("            %f,\n" % cg)
365   FD.write("            %f,\n" % cb)
366
367   if mat != 0:
368   # if this material has a texture, then add here
369     mtex = mat.getTextures()
370     for mt in mtex:
371       if mt:
372         do_texture(mt)
373
374   FD.write("    }\n")
375
376
377
378 # ---------------------------------------------------------------------------
379 # do_texture writes out this texture if usable.
380 # ---------------------------------------------------------------------------
381 def do_texture(mtex):
382   global FD
383
384
385   # get our texture
386   tex = mtex.tex
387   tn = tex.name
388
389
390   # what type of texture, we are limitd
391   if tex.type != Blender.Texture.Types.IMAGE:
392     return
393
394
395   FD.write("            SI_Texture2D " + tn + " {\n")
396
397   img = tex.getImage()
398   iname = get_filename(img.getFilename())
399
400   FD.write("                    \"" + iname + "\",\n")
401
402   # mapping type  ? uv  map wrapped is 4, how to detect?
403   # start with a simple xy mapping ie 0
404   FD.write("                    4,\n")
405   
406   if img.has_data:      ix, iy = img.getSize()
407   else:                         ix, iy = 512,512
408
409   # image width, and height
410   FD.write("                    %d,\n" % ix)
411   FD.write("                    %d,\n" % iy)
412   # u crop min/max, v crop min/max
413   mincu, mincv, maxcu, maxcv = tex.crop
414   FD.write("                    %d,\n" % ( mincu * ix ) )
415   FD.write("                    %d,\n" % ( maxcu * ix - 1 ) )
416   FD.write("                    %d,\n" % ( mincv * iy ) )
417   FD.write("                    %d,\n" % ( maxcv * iy - 1) )
418   # uv swap
419   uvs =0
420   if (tex.flags & Blender.Texture.Flags.FLIPBLEND):
421     uvs = 1
422   FD.write("                    %d,\n" % uvs )
423   # u/v repeat
424   if img.has_data:      iru = img.getXRep()
425   else:                 iru = 1
426   FD.write("                    %d,\n" % iru )
427   if img.has_data:      irv = img.getYRep()
428   else:                 irv = 1
429
430   FD.write("                    %d,\n" % irv )
431   # u/v alt - 0, 0
432   FD.write("                    0,\n" ) 
433   FD.write("                    0,\n" )
434   # u/v scale - 1,1
435   FD.write("                    1.000000,\n" )
436   FD.write("                    1.000000,\n" )
437   # u/v offset - 0,0
438   FD.write("                    0.000000,\n" )
439   FD.write("                    0.000000,\n" )
440   # proj mat 4x4 1 0 0 0, 0 1 0 0, 0 0 1 0, 0 0 0 1  is default
441   FD.write("                    1.000000,\n" )
442   FD.write("                    0.000000,\n" )
443   FD.write("                    0.000000,\n" )
444   FD.write("                    0.000000,\n" )
445
446   FD.write("                    0.000000,\n" )
447   FD.write("                    1.000000,\n" )
448   FD.write("                    0.000000,\n" )
449   FD.write("                    0.000000,\n" )
450
451   FD.write("                    0.000000,\n" )
452   FD.write("                    0.000000,\n" )
453   FD.write("                    1.000000,\n" )
454   FD.write("                    0.000000,\n" )
455
456   FD.write("                    0.000000,\n" )
457   FD.write("                    0.000000,\n" )
458   FD.write("                    0.000000,\n" )
459   FD.write("                    1.000000,\n" )
460
461   # blending type - 3
462   FD.write("                    3,\n" )
463   # blending - 1
464   FD.write("                    1.000000,\n" )
465   # ambient - 0
466   FD.write("                    0.000000,\n" )
467   # diffuse - 1
468   FD.write("                    1.000000,\n" )
469   # speculara - 0
470   FD.write("                    0.000000,\n" )
471   # transparent - 0
472   FD.write("                    0.000000,\n" )
473   # reflective - 0
474   FD.write("                    0.000000,\n" )
475   # roughness - 0
476   FD.write("                    0.000000,\n" )
477   
478   # close off this texture
479   FD.write("            }\n")
480
481
482
483 # ---------------------------------------------------------------------------
484 # do_model_transform dumps out the transform data
485 # ---------------------------------------------------------------------------
486 def do_model_transform(obj):
487
488   global FD
489
490   # now output
491   FD.write("            SI_Transform SRT-" + removeSpacesFromName( obj.name ) + " {\n" )
492
493   
494
495   # write out the object size? (scaling)
496   FD.write("                    %f,\n" % obj.SizeX )
497   FD.write("                    %f,\n" % obj.SizeY )
498   FD.write("                    %f,\n" % obj.SizeZ )
499   
500   # write out the object rotation
501   FD.write("                    %f,\n" % r2d(obj.RotX) )
502   FD.write("                    %f,\n" % r2d(obj.RotY) )
503   FD.write("                    %f,\n" % r2d(obj.RotZ) )
504
505   # this is the position of the object's axis
506   FD.write("                    %f,\n" % obj.LocX )
507   FD.write("                    %f,\n" % obj.LocY )
508   FD.write("                    %f,\n" % obj.LocZ )
509   FD.write("            }\n\n")
510
511
512
513 # ---------------------------------------------------------------------------
514 # do_model_visibility marks if the model is visible or not???
515 # ---------------------------------------------------------------------------
516 def do_model_visibility(obj):
517
518   global FD
519
520   # for now this is a static block
521   FD.write("            SI_Visibility  {\n" )
522   FD.write("                    1,\n" )
523   FD.write("            }\n\n" )
524
525
526
527 # ---------------------------------------------------------------------------
528 # do_model_material sets the global material for the model
529 # ---------------------------------------------------------------------------
530 def do_model_material(obj):
531
532   global FD
533
534   # do we have one?
535   ml = get_materials(obj)
536
537
538   n = 0
539   for mli in ml:
540     if mli:
541       n+=1
542       if n == 1:
543         mat=mli
544
545
546   # if no materials just go back
547   if n == 0:
548     return
549
550   # for now we grab the first material on the list.
551
552   for mat in ml:
553     FD.write("          SI_GlobalMaterial  {\n" )
554     FD.write("                  \"" + removeSpacesFromName(mat.name) + "\",\n" )
555     FD.write("                  \"NODE\",\n" )
556     FD.write("          }\n\n" )
557
558
559 # ---------------------------------------------------------------------------
560 # do_collect_uv, makes an easy to use list out of the uv data
561 # todo, remove duplicates and compress the list size, xsi supports this.
562 # ---------------------------------------------------------------------------
563 def do_collect_uv(mesh):
564
565   global UVC, UVI
566
567   # reset the uv details first.
568   UVI = []
569   UVC = []
570
571   #print "Textures..."
572   #mtex = mat.getTextures()
573   #for mt in mtex:
574   #  print mt
575
576
577   # if no uv data then return
578   if not mesh.hasFaceUV():
579     return
580
581   # run through all the faces
582   j = 0
583   for f in mesh.faces:
584     for uv in f.uv:
585       UVI.append(j)
586       UVC.append(uv)
587       j+=1
588     UVI.append(-1)
589
590
591
592 # ---------------------------------------------------------------------------
593 # do_collect_colour, makes an easy to use list out of the colour data
594 # todo, remove duplicates and compress the list size, xsi supports this.
595 # ---------------------------------------------------------------------------
596 def do_collect_colour(mesh):
597
598   global VCC, VCI
599
600   # reset the uv details first.
601   VCC = []
602   VCI = []
603
604   # if no uv data then return
605   if not mesh.hasVertexColours():
606     return
607
608   # run through all the faces
609   j = 0
610   for f in mesh.faces:
611     for c in f.col:
612       VCI.append(j)
613       VCC.append(c)
614       j+=1
615     VCI.append(-1)
616
617
618
619 # ---------------------------------------------------------------------------
620 # do_mesh_shape outputs the shape data
621 # ---------------------------------------------------------------------------
622 def do_mesh_shape(obj):
623
624   global UVC, UVI, VCC, VCI, FD, NORMALS
625
626   # Grab the mesh itself
627   mesh = obj.data
628
629   # get the world matrix
630   matrix = obj.getMatrix('worldspace')
631
632   # we need to decide about vertex and uv details first.
633   do_collect_uv(mesh)
634   do_collect_colour(mesh)
635
636   # output the shell
637   elements=2
638   if len(UVC):
639     elements+=1
640   if len(VCC):
641     elements+=1
642   FD.write("                    SI_Shape SHP-" + removeSpacesFromName ( obj.name ) + "-ORG {\n" )
643   FD.write("                            %d,\n" % elements )
644   FD.write("                            \"ORDERED\",\n\n" )
645
646   # vertices first
647   FD.write("                            %d,\n" % len(mesh.verts) )
648   FD.write("                            \"POSITION\",\n" )
649   for v in mesh.verts:
650     FD.write("                          %f,%f,%f,\n" % (v.co[0], v.co[1], v.co[2]) )
651   FD.write("\n")
652
653
654   print "  MESH NAME = " + mesh.name
655
656   NORMALS = []
657   for f in mesh.faces:
658     NORMALS.append ( f.no )
659   for v in mesh.verts:
660     aTemp = [v.no[0], v.no[1], v.no[2]]
661     NORMALS.append ( aTemp )
662
663
664   FD.write("                            %d,\n" % len(NORMALS) )
665   FD.write("                            \"NORMAL\",\n" )                                                        
666                                 
667   for n in NORMALS:
668         FD.write("                              %f,%f,%f,\n" % ( n[0], n[1], n[2] ) )
669
670   # if vertex colour data then process
671   if mesh.hasVertexColours():
672
673     # put out the co-ord header
674     FD.write("                          %d,\n" % len(VCC) )
675     FD.write("                          \"COLOR\",\n" )
676
677     # now output them
678     for vc in VCC:
679       FD.write("                                %f,%f,%f,%f,\n" % (vc.r/255.0, vc.g/255.0, vc.b/255.0, vc.a/255.0) )
680
681
682
683   # if uv data then process
684   if mesh.hasFaceUV():
685     # put out the co-ord header
686     FD.write("                          %d,\n" % len(UVC) )
687     FD.write("                          \"TEX_COORD_UV\",\n" )
688
689     # now output them
690     for uv in UVC:
691       FD.write("                                %f,%f\n" % (uv[0], uv[1]) )
692
693   # close off
694   FD.write("                    }\n" )
695  
696
697
698 # ---------------------------------------------------------------------------
699 # do_mesh_faces outputs the faces data
700 # ---------------------------------------------------------------------------
701 def do_mesh_faces(obj):
702
703   global FD, UVI, VCI, mats
704
705   # do we have a texture?
706   ml = get_materials(obj)
707   n = 0
708   for mli in ml:
709     if mli:
710       n+=1
711       if n == 1:
712         mat=mli
713
714   # Grab the mesh itself
715   # mesh = Blender.NMesh.GetRaw(obj.data.name)
716   
717   # mesh = Blender.NMesh.GetRawFromObject(obj.name)
718
719   mesh = obj.data
720
721
722
723   tris = []
724   normalX = []
725   mats = []
726   for f in mesh.faces:
727     tris.extend ( triangulate_face(f) )
728     aVal = triangulate_normals(mesh,f)
729
730     for v in aVal:
731       normalX.append ( v )
732
733
734   triangles = len(tris)
735
736   if n == 0:
737     FD.write("                  SI_TriangleList " + removeSpacesFromName(obj.name) + " {\n")
738     FD.write("                          %d,\n" % triangles)
739
740     ostring="                           \"NORMAL"
741     if len(VCI):
742       ostring += "|COLOR"
743     if len(UVC):
744       ostring += "|TEX_COORD_UV"
745     ostring += "\",\n"
746     FD.write(ostring)
747
748     FD.write("                          \"\",\n\n")
749
750     for t in tris:
751       FD.write("                                %d,%d,%d,\n" % (t[0], t[2], t[1]))
752
753     FD.write("\n")
754
755     for n in normalX:
756       FD.write("                                %d,%d,%d,\n" % ( n[0], n[1], n[2] ) )
757
758   # finally close this triangle list off
759     FD.write("                  }\n\n")
760
761
762
763   print "total materials"
764   print ml
765
766   for mIndex in range (0,len(ml)):
767     mat = ml[mIndex]
768     print "checking materials"
769     print mat
770
771     aTriCount = 0
772     for tIndex in range ( 0, len ( tris ) ):
773       aMat = mats[tIndex]
774       if aMat == mIndex:
775         aTriCount = aTriCount + 1       
776
777   #
778   # output the shell
779     FD.write("                  SI_TriangleList " + removeSpacesFromName(obj.name) + " {\n")
780   #  FD.write("                         %d,\n" % triangles)
781     FD.write("                          %d,\n" % aTriCount)
782
783     ostring="                           \"NORMAL"
784     if len(VCI):
785       ostring += "|COLOR"
786     if len(UVC):
787       ostring += "|TEX_COORD_UV"
788     ostring += "\",\n"
789     FD.write(ostring)
790
791
792     FD.write("                          \"" + removeSpacesFromName ( mat.name ) + "\",\n\n")
793
794 #    FD.write("                         \"\",\n\n")
795
796
797     for tIndex in range ( 0, len ( tris ) ):
798       aMat = mats[tIndex]
799       if mIndex == aMat:
800         t = tris[tIndex]
801         FD.write("                              %d,%d,%d,\n" % (t[0], t[2], t[1]))
802
803     FD.write("\n")
804
805
806
807 #    for n in normalX:
808     for tIndex in range ( 0, len ( tris ) ):
809       aMat = mats[tIndex]
810       if mIndex == aMat:
811         n = normalX[tIndex]
812         FD.write("                              %d,%d,%d,\n" % ( n[0], n[1], n[2] ) )
813
814
815
816   # if we have it, put out the colour vertex list
817   #  ostring = "                                "
818   #  for i in range(len(VCI)):
819     # if a -1 its end of line, write.
820   #    if VCI[i] == -1:
821   #      ostring = ostring + "\n"
822   #      FD.write(ostring)
823   #      ostring="                              "
824   #    else:
825   #      ostring = ostring + "%d," % VCI[i]
826
827   # The final set is to work out the uv list, its one set per face
828   #  ostring = "                                "
829   #  for i in range(len(UVI)):
830   #    # if a -1 its end of line, write.
831   #    if UVI[i] == -1:
832   #      ostring = ostring + "\n"
833   #      FD.write(ostring)
834   #      ostring="                              "
835   #    else:
836   #      ostring = ostring + "%d," % UVI[i]
837
838   # finally close this triangle list off
839     FD.write("                  }\n\n")
840
841
842 def getNormalInfo(mesh, faceInfo):
843   global NORMALS
844   aNL = []
845   for fi in faceInfo:
846     aN = []
847
848     aFace = mesh.faces[fi[0]]
849
850     print aFace
851
852     if (aFace.smooth):
853       aN.append ( NORMALS.index ( aFace.v.no[0] ) )
854       aN.append ( NORMALS.index ( aFace.v.no[1] ) )
855       aN.append ( NORMALS.index ( aFace.v.no[2] ) )
856     else:
857       aN.append ( NORMALS.index ( aFace.no ) )
858       aN.append ( NORMALS.index ( aFace.no ) )
859       aN.append ( NORMALS.index ( aFace.no ) )
860
861 #      aN.append ( NORMALS.index ( mesh.faces[fi[0]].no ) )
862 #      aN.append ( NORMALS.index ( mesh.faces[fi[0]].no ) )
863 #      aN.append ( NORMALS.index ( mesh.faces[fi[0]].no ) )
864     
865     aNL.append ( aN )
866   return aNL
867  
868
869
870 # copy of code to triangulate mesh
871 ##################################
872 def triangulate_face(f):    
873   if len(f.v) <= 3:
874     #newFaces = [ [f.v[0].index, f.v[1].index, f.v[2].index] ]
875     newFaces = [ [f.v[0].index, f.v[2].index, f.v[1].index] ]
876     mats.append ( f.materialIndex )
877   else:
878     #newFaces = [ [f.v[0].index, f.v[1].index, f.v[2].index] ]
879     #newFaces.append ( [f.v[3].index, f.v[0].index, f.v[2].index] )
880     newFaces = [ [f.v[0].index, f.v[2].index, f.v[1].index] ]
881     newFaces.append ( [f.v[3].index, f.v[2].index, f.v[0].index] )
882     mats.append ( f.materialIndex )
883     mats.append ( f.materialIndex )
884
885   return newFaces
886
887 # copy of code to triangulate mesh
888 ##################################
889 def triangulate_normals(mesh, f): 
890  
891   if len(f.v) <= 3:
892     if f.smooth:        
893       n1 = get_normal_index ( mesh, [f.v[0].no[0], f.v[0].no[1], f.v[0].no[2]] )
894       n2 = get_normal_index ( mesh, [f.v[1].no[0], f.v[1].no[1], f.v[1].no[2]] )
895       n3 = get_normal_index ( mesh, [f.v[2].no[0], f.v[2].no[1], f.v[2].no[2]] )
896       newNormals = [[ n1, n2, n3 ]]
897     else:
898       n1 = get_normal_index ( mesh, [f.no[0], f.no[1], f.no[2]] )
899       newNormals = [[ n1, n1, n1 ]]       
900   else:
901     if f.smooth:
902       n1 = get_normal_index ( mesh, [f.v[0].no[0], f.v[0].no[1], f.v[0].no[2]] )
903       n2 = get_normal_index ( mesh, [f.v[1].no[0], f.v[1].no[1], f.v[1].no[2]] )
904       n3 = get_normal_index ( mesh, [f.v[2].no[0], f.v[2].no[1], f.v[2].no[2]] )
905       n4 = get_normal_index ( mesh, [f.v[3].no[0], f.v[3].no[1], f.v[3].no[2]] )
906       newNormals = [ [ n1, n2, n3 ] ]
907       newNormals.append ( [ n4, n1, n3 ] )
908
909 #      newNormals = [[ n1, n3, n2 ]]
910 #      newNormals.append ( [ n4, n3, n1 ] )
911     else:
912       n1 = get_normal_index ( mesh, [f.no[0], f.no[1], f.no[2]] )
913       newNormals = [[ n1, n1, n1 ]]
914       newNormals.append ( [ n1, n1, n1 ] )
915
916   return newNormals
917
918
919
920 ##################################
921 def get_normal_index(mesh,normal):
922   global NORMALS
923
924   indx=NORMALS.index(normal)
925   return indx
926
927
928 # ---------------------------------------------------------------------------
929 # do_model_mesh outputs the shape/triangelist wrapper block
930 # ---------------------------------------------------------------------------
931 def do_model_mesh(obj):
932
933   global FD
934
935   # output the shell
936   FD.write("            SI_Mesh MSH-" + removeSpacesFromName(obj.name) + " {\n")
937
938   # todo, add calc normals and calc uv here
939   # these can be used in both the following sections.
940
941   # next the shape
942   do_mesh_shape(obj)
943
944   # finally the trangle list
945   do_mesh_faces(obj)
946
947   # finally close this mesh off
948   FD.write("            }\n\n")
949
950
951
952 # ---------------------------------------------------------------------------
953 # do_model actually outputs a mesh model
954 # ---------------------------------------------------------------------------
955 def do_model(obj):
956
957   global FD
958
959   # we only want meshes for now.
960   if 'Mesh' != obj.type:
961     return
962
963   # check if the mesh is valid
964   if validMesh(obj) <> 0:
965           print "INVALID MESH " + obj.name
966           return
967
968
969   print "Exporting model " + obj.name
970
971   # start model
972   FD.write("    SI_Model MDL-" + removeSpacesFromName(obj.name) + " {\n")
973
974   # do transform
975   do_model_transform(obj)
976
977   # do visibility
978   do_model_visibility(obj)
979
980   # do global material
981   do_model_material(obj)
982
983   # do the mesh
984   do_model_mesh(obj)
985
986   # close this model
987   FD.write("    }\n")
988
989 #
990 # check for invalid mesh ( faces that have < 3 vertices )
991 #
992
993 def validMesh (obj):
994   mesh = obj.data
995   for f in mesh.faces:
996     if len(f.v) < 3:
997       print "MESH HAS FACES WITH < 3 VERTICES"
998       return 1
999   if len (mesh.faces) == 0:
1000     print "MESH HAS NO FACES"
1001     return 1
1002         
1003   return 0
1004
1005 # ---------------------------------------------------------------------------
1006 # do_models is the process which allows us to write out a bunch of models
1007 # ---------------------------------------------------------------------------
1008 def do_models():
1009
1010   global OBJ, MAT, FD
1011
1012   #create the full scene wrapper object
1013   FD.write("SI_Model MDL-SceneRoot {\n")
1014   FD.write("    SI_Transform SRT-SceneRoot {\n" )
1015   FD.write("            1.000000,\n")
1016   FD.write("            1.000000,\n")
1017   FD.write("            1.000000,\n")
1018   FD.write("            -90.000000,\n")
1019   FD.write("            0.000000,\n")
1020   FD.write("            0.000000,\n")
1021   FD.write("            0.000000,\n")
1022   FD.write("            0.000000,\n")
1023   FD.write("            0.000000,\n")
1024   FD.write("    }\n\n")
1025
1026   # now process the actual selected meshes themselves
1027   for obj in OBJ:
1028     do_model(obj)
1029  
1030   for obj in OBJ:
1031     do_light(obj)
1032
1033   for obj in OBJ:
1034     do_camera(obj)
1035
1036   do_light_ambient ()
1037
1038   # finally close off the model list
1039   FD.write("}\n")
1040
1041
1042 # ---------------------------------------------------------------------------
1043 # do_light actually outputs a light model
1044 # ---------------------------------------------------------------------------
1045 def do_light(obj):
1046
1047   global FD
1048
1049   # we only want lights for now.
1050   if 'Lamp' != obj.type:
1051     return
1052
1053   print "Exporting light " + obj.name
1054
1055   aLampType = 1
1056
1057   lmpName=Lamp.Get(obj.getData(name_only=1))
1058   lmpType=lmpName.getType()
1059
1060   if lmpType == Lamp.Types.Lamp:
1061     aLampType = 0
1062   elif lmpType == Lamp.Types.Spot:
1063     aLampType = 0
1064   elif lmpType == Lamp.Types.Sun:
1065     aLampType = 1
1066   else:
1067     aLampType = 0
1068
1069   # start model
1070   FD.write("    SI_Light " + removeSpacesFromName(obj.name) + " {\n")
1071
1072   # do type
1073   FD.write("            %d,\n" % aLampType)
1074
1075   lampName= obj.data
1076   colour = lampName.col
1077
1078   # do color
1079   FD.write("            %f,\n" % colour[0] )
1080   FD.write("            %f,\n" % colour[1] )
1081   FD.write("            %f,\n" % colour[2] )
1082
1083   # do position
1084
1085   FD.write("            %f,\n" % obj.LocX )
1086   FD.write("            %f,\n" % obj.LocY )
1087   FD.write("            %f,\n" % obj.LocZ )
1088
1089
1090   # close this model
1091   FD.write("    }\n")
1092
1093
1094 # ---------------------------------------------------------------------------
1095 # do_light actually outputs a light model
1096 # ---------------------------------------------------------------------------
1097 def do_camera(obj):
1098
1099   global FD
1100
1101   # we only want cameras for now.
1102   if 'Camera' != obj.type:
1103     return
1104
1105   print "Exporting camera " + obj.name
1106
1107
1108
1109   # start model
1110   FD.write("    SI_Camera " + removeSpacesFromName(obj.name) + " {\n")
1111
1112
1113   cameraName=obj.data
1114   
1115   # colour = cameraName.col
1116
1117   # do position
1118
1119   FD.write("            %f,\n" % obj.LocX )
1120   FD.write("            %f,\n" % obj.LocY )
1121   FD.write("            %f,\n" % obj.LocZ )
1122
1123   # looking at
1124
1125   FD.write("            %f,\n" % 0.0 )
1126   FD.write("            %f,\n" % 0.0 )
1127   FD.write("            %f,\n" % 0.0 )
1128
1129   # roll
1130   FD.write("            %f,\n" % 0.0 )
1131
1132   aLens = cameraName.getLens()
1133
1134   # field of view
1135   FD.write("            %f,\n" % aLens )
1136
1137   # near plane
1138   FD.write("            %f,\n" % 1.0 )
1139
1140   # far plane
1141   FD.write("            %f,\n" % 10000000.0 )
1142
1143
1144   # close this model
1145   FD.write("    }\n")
1146
1147
1148
1149 # ---------------------------------------------------------------------------
1150 # write out the ambient light ( for Shockwave 3D converter )
1151 # ---------------------------------------------------------------------------
1152
1153 def do_light_ambient():
1154   if WORLD:     ambient = WORLD.getAmb()
1155   else:         ambient = 0,0,0
1156
1157   FD.write("    SI_Light ambient_sw3d {\n")
1158
1159   FD.write("            9,\n")
1160   FD.write("            %f,\n" % ambient[0])
1161   FD.write("            %f,\n" % ambient[1])
1162   FD.write("            %f,\n" % ambient[2])
1163   FD.write("            0.00000000,\n")
1164   FD.write("            0.00000000,\n")
1165   FD.write("            0.00000000,\n")
1166
1167   FD.write("    }\n")
1168
1169
1170
1171 # ---------------------------------------------------------------------------
1172 # export_xsi is the wrapper function to process the loading of an xsi model.
1173 # ---------------------------------------------------------------------------
1174 def export_xsi(filename):
1175
1176   global OBJ, MAT, FD, EXPORT_DIR
1177
1178   # safety check
1179   if filename.find('.xsi', -4) <= 0:
1180     print "XSI not found"
1181     filename += '.xsi'
1182
1183
1184   export_dir = bsys.dirname(filename)
1185   if export_dir != EXPORT_DIR:
1186     EXPORT_DIR = export_dir   
1187
1188   # open our output  
1189   FD = open(filename, 'w')
1190
1191   # get the selected objects, otherwise get them all
1192   #OBJ = Blender.Object.GetSelected()
1193   #if not OBJ:
1194   
1195   OBJ = list(Blender.Scene.GetCurrent().objects) #Blender.Object.Get()
1196
1197   # we need some objects, if none specified stop
1198   if not OBJ:
1199     return
1200
1201   # if any exist, grab the materials
1202   MAT = Blender.Material.Get()
1203
1204   # output the header data
1205   do_header()
1206
1207   # output the materials used by the selected objects.
1208   do_materiallibrary()
1209
1210   # we punch out the models, that is, the meshes themselves
1211   do_models()
1212
1213
1214   # finally close our file
1215   FD.close()
1216
1217
1218
1219 # ---------------------------------------------------------------------------
1220 # Lets trigger it off now
1221 # Blender.Window.FileSelector(export_xsi, 'Export SoftImage XSI')
1222
1223 fname = bsys.makename(ext=".xsi")
1224 if EXPORT_DIR <> '':
1225   fname = bsys.join(EXPORT_DIR, bsys.basename(fname))
1226
1227 Blender.Window.FileSelector(export_xsi, "Export SoftImage XSI", fname)