1228 lines
30 KiB
Python
1228 lines
30 KiB
Python
#!BPY
|
|
|
|
|
|
"""
|
|
Name: 'SoftImage XSI (.xsi)...'
|
|
Blender: 236
|
|
Group: 'Export'
|
|
Tooltip: 'Export to a SoftImage XSI file'
|
|
"""
|
|
|
|
__author__ = ("Elira")
|
|
__url__ = ["Author's site, http://www.creative-realms.net/~elira/blender.html",
|
|
"SoftImage's site, www.softimage.com", "blenderartists.org"]
|
|
__email__ = ["scripts"]
|
|
__version__ = "2005/11/01"
|
|
|
|
|
|
__bpydoc__ = """\
|
|
This script exports to the XSI format.
|
|
|
|
Usage:
|
|
|
|
Run this script from "File->Export" menu.
|
|
|
|
Note:<br>
|
|
- Updates by Mal Duffin, to assist with XSI to Shockwave 3D conversion.
|
|
"""
|
|
|
|
# $Id: xsi_export.py,v 1.4.6 2005/11/01
|
|
#
|
|
#------------------------------------------------------------------------
|
|
# XSI exporter for blender 2.36 or above
|
|
#
|
|
# ***** BEGIN GPL LICENSE BLOCK *****
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
#
|
|
# ***** END GPL LICENCE BLOCK *****
|
|
#
|
|
|
|
|
|
#
|
|
# ---------------------------------------------------------------------------
|
|
# XSI Export V 1.4.1 by Elira (at) creative-realms (dot) net
|
|
#
|
|
# Updates by Mal Duffin, to assist with XSI to Shockwave 3D conversion
|
|
# ---------------------------------------------------------------------------
|
|
# 0.0.0 - This header and having blender ID the file.
|
|
# 0.1.0 - Output the statis xsi header elements
|
|
# 0.2.0 - create a full shell output (no content just structure)
|
|
# 0.3.0 - output used materials from the full materials list
|
|
# 0.4.0 - output the object model minor data
|
|
# 0.5.0 - output the object shape data, storing a uv table
|
|
# 0.6.0 - output the triangle lists (uv references stored uv table)
|
|
# 0.7.0 - convert output to genuine file writes.
|
|
# 1.0.0 - Admit this script exists and wait for flames
|
|
# 1.1.0 - Correctly export mesh shapes
|
|
# 1.2.0 - Mesh positioning corrected, added back normals
|
|
# 1.3.0 - conditionally output uv co-ordinates
|
|
# 1.4.0 - export vertex paint colours.
|
|
# ---------------------------------------------------------------------------
|
|
# 1.4.1 - added basic normal export code,
|
|
# to get XSI to Shockwave 3D converter working ( Mal Duffin )
|
|
# 1.4.2 - invalid mesh checking
|
|
# better normal exporting
|
|
# general code clean up
|
|
# 1.4.3 - basic light exporting
|
|
# fix for ambient light being ignored by Shockwave 3D converter
|
|
# 1.4.4 - basic camera exporting
|
|
# 1.4.5 - exports normals correctly
|
|
# 1.4.6 - exports multiple materials per object
|
|
# ---------------------------------------------------------------------------
|
|
# TO DO
|
|
# - Support texturing
|
|
# - for both methods of texturing ( render method, and Game Engine method )
|
|
# ---------------------------------------------------------------------------
|
|
# add required modules
|
|
|
|
import Blender
|
|
from Blender import sys as bsys
|
|
from Blender import Mathutils
|
|
from Blender import Lamp
|
|
from Blender import Camera
|
|
import math
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# globals to make things a lot lot easier
|
|
OBJ = [] # the object list
|
|
MAT = [] # the materials list
|
|
UVC = [] # uv vert co-ords
|
|
UVI = [] # uv vert index
|
|
VCC = [] # vert colour co-ords
|
|
VCI = [] # vert colour index
|
|
FD = [] # file handle
|
|
NORMALS = [] # normal list
|
|
mats = []
|
|
EXPORT_DIR = ''
|
|
WORLD = Blender.World.GetCurrent()
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# get_path returns the path portion o/wf the supplied filename.
|
|
# ---------------------------------------------------------------------------
|
|
def get_path(file):
|
|
l=len(file)
|
|
r=0
|
|
for i in range(l, 0, -1):
|
|
if r == 0:
|
|
if file[i-1] == "/" or file[i-1] == "\\":
|
|
r = i
|
|
return file[:r]
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# r2d - radians to degrees
|
|
# ---------------------------------------------------------------------------
|
|
def r2d(r):
|
|
return round(r*180.0/math.pi,4)
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# d2r - degrees to radians
|
|
# ---------------------------------------------------------------------------
|
|
def d2r(d):
|
|
return (d*math.pi)/180.0
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# get_filename returns the filename
|
|
# ---------------------------------------------------------------------------
|
|
def get_filename(file):
|
|
l=len(file)
|
|
r=0
|
|
for i in range(l, 0, -1):
|
|
if r == 0:
|
|
if file[i-1] == "/" or file[i-1] == "\\":
|
|
r = i
|
|
return file[r:]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# find materials returns all materials on an object.
|
|
# ---------------------------------------------------------------------------
|
|
def get_materials(obj):
|
|
|
|
# any materials attached to the object itself
|
|
mats = obj.getMaterials(0)
|
|
|
|
if 'Mesh' != obj.type:
|
|
return mats
|
|
|
|
# now drop down to the mesh level
|
|
#mesh = Blender.NMesh.GetRaw(obj.data.name)
|
|
|
|
mats.extend(obj.getData(mesh=1).materials)
|
|
|
|
# return the materials list
|
|
|
|
# Is this correct!!?? - None materials raise an error otherwise
|
|
# but it might screw up the indicies.. TODO... check the exported files.
|
|
return [m for m in mats if m]
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_header writes out the header data
|
|
# ---------------------------------------------------------------------------
|
|
def do_header():
|
|
|
|
global FD
|
|
|
|
# this says which xsi version
|
|
FD.write("xsi 0300txt 0032\n\n")
|
|
|
|
# static fileinfo block
|
|
FD.write("SI_FileInfo {\n")
|
|
FD.write(" \"Blender Scene\",\n")
|
|
FD.write(" \"Blender User\",\n")
|
|
FD.write(" \"Now\",\n")
|
|
FD.write(" \"xsi_export Blender Scene Exporter\",\n")
|
|
FD.write("}\n\n")
|
|
|
|
# static scene block
|
|
FD.write("SI_Scene no_name {\n")
|
|
FD.write(" \"FRAMES\",\n")
|
|
FD.write(" 0.000000,\n")
|
|
FD.write(" 100.000000,\n")
|
|
FD.write(" 30.000000,\n")
|
|
FD.write("}\n\n")
|
|
|
|
# static co-ordinate system block
|
|
FD.write("SI_CoordinateSystem coord {\n")
|
|
FD.write(" 1,\n")
|
|
FD.write(" 0,\n")
|
|
FD.write(" 1,\n")
|
|
FD.write(" 0,\n")
|
|
FD.write(" 5,\n")
|
|
FD.write(" 2,\n")
|
|
FD.write("}\n\n")
|
|
|
|
# static angle block
|
|
FD.write("SI_Angle {\n")
|
|
FD.write(" 0,\n")
|
|
FD.write("}\n\n")
|
|
|
|
# static ambience block
|
|
if WORLD: ambient = WORLD.getAmb()
|
|
else: ambient = 0,0,0
|
|
|
|
FD.write("SI_Ambience {\n")
|
|
FD.write(" %f,\n" % ambient[0])
|
|
FD.write(" %f,\n" % ambient[1])
|
|
FD.write(" %f,\n" % ambient[2])
|
|
FD.write("}\n\n")
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_materiallibrary writes out the materials subsection.
|
|
# ---------------------------------------------------------------------------
|
|
def do_materiallibrary():
|
|
|
|
global OBJ, MAT, FD
|
|
|
|
# set some flags first
|
|
mnum = 0
|
|
|
|
# run through every material, how many used?
|
|
for mat in MAT:
|
|
nmat = mat.name
|
|
|
|
# first, is this material on any of the objects.
|
|
f = 0
|
|
for obj in OBJ:
|
|
ml = get_materials(obj)
|
|
for mli in ml:
|
|
nmli = mli.name
|
|
if nmli == nmat:
|
|
f = 1
|
|
mnum += 1
|
|
break
|
|
if f == 1:
|
|
break
|
|
|
|
bCreateDefault = 0
|
|
# if none then exit
|
|
if not mnum:
|
|
bCreateDefault = 1
|
|
# return
|
|
|
|
# get to work create the materiallibrary wrapper and fill.
|
|
FD.write("SI_MaterialLibrary {\n")
|
|
FD.write(" " + str(mnum) + ",\n")
|
|
|
|
# run through every material, write the used ones
|
|
for mat in MAT:
|
|
nmat = mat.name
|
|
|
|
# find out if on any object, if so we write.
|
|
f = 0
|
|
for obj in OBJ:
|
|
ml = get_materials(obj)
|
|
for mli in ml:
|
|
nmli = mli.name
|
|
if nmli == nmat:
|
|
do_material(mat)
|
|
f = 1
|
|
break
|
|
if f == 1:
|
|
break
|
|
|
|
if bCreateDefault == 1:
|
|
do_material ( 0 )
|
|
|
|
# clean up
|
|
FD.write("}\n\n")
|
|
|
|
|
|
def removeSpacesFromName(name):
|
|
name = name.replace ( " ", "_" )
|
|
return name
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_material writes out this material.
|
|
# ---------------------------------------------------------------------------
|
|
def do_material(mat):
|
|
|
|
global FD
|
|
|
|
if mat == 0:
|
|
name = "__default"
|
|
cr = 1.0
|
|
cg = 1.0
|
|
cb = 1.0
|
|
ca = 1.0
|
|
sp = 0.0
|
|
sr = 0.0
|
|
sg = 0.0
|
|
sb = 0.0
|
|
em = 0.0
|
|
am = 1.0
|
|
sm = 0
|
|
else:
|
|
|
|
|
|
# get the name first
|
|
name = mat.name
|
|
|
|
# face colour r, g, b, a
|
|
# power (spec decay) fl
|
|
# spec colour r, g, b
|
|
# emmisive colourm r, g, b
|
|
# shading model int constant, lambert, phong, blinn, shadow, vertex
|
|
# ambient colour r, g, b
|
|
|
|
# get and print the base material block
|
|
cr, cg, cb = mat.getRGBCol()
|
|
ca = mat.getAlpha()
|
|
|
|
sp = 0.0
|
|
sr, sg, sb = mat.getSpecCol()
|
|
em = mat.getEmit()
|
|
am = mat.getAmb()
|
|
|
|
# how do we render this material? start with constant (0)
|
|
sm = 0
|
|
fl = mat.getMode()
|
|
if fl & Blender.Material.Modes['VCOL_PAINT']:
|
|
sm = 5
|
|
|
|
|
|
FD.write(" SI_Material " + removeSpacesFromName(name) + " {\n")
|
|
FD.write(" %f,\n" % cr)
|
|
FD.write(" %f,\n" % cg)
|
|
FD.write(" %f,\n" % cb)
|
|
FD.write(" %f,\n" % ca)
|
|
FD.write(" %f,\n" % sp)
|
|
FD.write(" %f,\n" % sr)
|
|
FD.write(" %f,\n" % sg)
|
|
FD.write(" %f,\n" % sb)
|
|
FD.write(" %f,\n" % em)
|
|
FD.write(" %f,\n" % em)
|
|
FD.write(" %f,\n" % em)
|
|
FD.write(" %d,\n" % sm)
|
|
#FD.write(" %f,\n" % am)
|
|
#FD.write(" %f,\n" % am)
|
|
#FD.write(" %f,\n" % am)
|
|
FD.write(" %f,\n" % cr)
|
|
FD.write(" %f,\n" % cg)
|
|
FD.write(" %f,\n" % cb)
|
|
|
|
if mat != 0:
|
|
# if this material has a texture, then add here
|
|
mtex = mat.getTextures()
|
|
for mt in mtex:
|
|
if mt:
|
|
do_texture(mt)
|
|
|
|
FD.write(" }\n")
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_texture writes out this texture if usable.
|
|
# ---------------------------------------------------------------------------
|
|
def do_texture(mtex):
|
|
global FD
|
|
|
|
|
|
# get our texture
|
|
tex = mtex.tex
|
|
tn = tex.name
|
|
|
|
|
|
# what type of texture, we are limitd
|
|
if tex.type != Blender.Texture.Types.IMAGE:
|
|
return
|
|
|
|
|
|
FD.write(" SI_Texture2D " + tn + " {\n")
|
|
|
|
img = tex.getImage()
|
|
iname = get_filename(img.getFilename())
|
|
|
|
FD.write(" \"" + iname + "\",\n")
|
|
|
|
# mapping type ? uv map wrapped is 4, how to detect?
|
|
# start with a simple xy mapping ie 0
|
|
FD.write(" 4,\n")
|
|
|
|
if img.has_data: ix, iy = img.getSize()
|
|
else: ix, iy = 512,512
|
|
|
|
# image width, and height
|
|
FD.write(" %d,\n" % ix)
|
|
FD.write(" %d,\n" % iy)
|
|
# u crop min/max, v crop min/max
|
|
mincu, mincv, maxcu, maxcv = tex.crop
|
|
FD.write(" %d,\n" % ( mincu * ix ) )
|
|
FD.write(" %d,\n" % ( maxcu * ix - 1 ) )
|
|
FD.write(" %d,\n" % ( mincv * iy ) )
|
|
FD.write(" %d,\n" % ( maxcv * iy - 1) )
|
|
# uv swap
|
|
uvs =0
|
|
if (tex.flags & Blender.Texture.Flags.FLIPBLEND):
|
|
uvs = 1
|
|
FD.write(" %d,\n" % uvs )
|
|
# u/v repeat
|
|
if img.has_data: iru = img.getXRep()
|
|
else: iru = 1
|
|
FD.write(" %d,\n" % iru )
|
|
if img.has_data: irv = img.getYRep()
|
|
else: irv = 1
|
|
|
|
FD.write(" %d,\n" % irv )
|
|
# u/v alt - 0, 0
|
|
FD.write(" 0,\n" )
|
|
FD.write(" 0,\n" )
|
|
# u/v scale - 1,1
|
|
FD.write(" 1.000000,\n" )
|
|
FD.write(" 1.000000,\n" )
|
|
# u/v offset - 0,0
|
|
FD.write(" 0.000000,\n" )
|
|
FD.write(" 0.000000,\n" )
|
|
# proj mat 4x4 1 0 0 0, 0 1 0 0, 0 0 1 0, 0 0 0 1 is default
|
|
FD.write(" 1.000000,\n" )
|
|
FD.write(" 0.000000,\n" )
|
|
FD.write(" 0.000000,\n" )
|
|
FD.write(" 0.000000,\n" )
|
|
|
|
FD.write(" 0.000000,\n" )
|
|
FD.write(" 1.000000,\n" )
|
|
FD.write(" 0.000000,\n" )
|
|
FD.write(" 0.000000,\n" )
|
|
|
|
FD.write(" 0.000000,\n" )
|
|
FD.write(" 0.000000,\n" )
|
|
FD.write(" 1.000000,\n" )
|
|
FD.write(" 0.000000,\n" )
|
|
|
|
FD.write(" 0.000000,\n" )
|
|
FD.write(" 0.000000,\n" )
|
|
FD.write(" 0.000000,\n" )
|
|
FD.write(" 1.000000,\n" )
|
|
|
|
# blending type - 3
|
|
FD.write(" 3,\n" )
|
|
# blending - 1
|
|
FD.write(" 1.000000,\n" )
|
|
# ambient - 0
|
|
FD.write(" 0.000000,\n" )
|
|
# diffuse - 1
|
|
FD.write(" 1.000000,\n" )
|
|
# speculara - 0
|
|
FD.write(" 0.000000,\n" )
|
|
# transparent - 0
|
|
FD.write(" 0.000000,\n" )
|
|
# reflective - 0
|
|
FD.write(" 0.000000,\n" )
|
|
# roughness - 0
|
|
FD.write(" 0.000000,\n" )
|
|
|
|
# close off this texture
|
|
FD.write(" }\n")
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_model_transform dumps out the transform data
|
|
# ---------------------------------------------------------------------------
|
|
def do_model_transform(obj):
|
|
|
|
global FD
|
|
|
|
# now output
|
|
FD.write(" SI_Transform SRT-" + removeSpacesFromName( obj.name ) + " {\n" )
|
|
|
|
|
|
|
|
# write out the object size? (scaling)
|
|
FD.write(" %f,\n" % obj.SizeX )
|
|
FD.write(" %f,\n" % obj.SizeY )
|
|
FD.write(" %f,\n" % obj.SizeZ )
|
|
|
|
# write out the object rotation
|
|
FD.write(" %f,\n" % r2d(obj.RotX) )
|
|
FD.write(" %f,\n" % r2d(obj.RotY) )
|
|
FD.write(" %f,\n" % r2d(obj.RotZ) )
|
|
|
|
# this is the position of the object's axis
|
|
FD.write(" %f,\n" % obj.LocX )
|
|
FD.write(" %f,\n" % obj.LocY )
|
|
FD.write(" %f,\n" % obj.LocZ )
|
|
FD.write(" }\n\n")
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_model_visibility marks if the model is visible or not???
|
|
# ---------------------------------------------------------------------------
|
|
def do_model_visibility(obj):
|
|
|
|
global FD
|
|
|
|
# for now this is a static block
|
|
FD.write(" SI_Visibility {\n" )
|
|
FD.write(" 1,\n" )
|
|
FD.write(" }\n\n" )
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_model_material sets the global material for the model
|
|
# ---------------------------------------------------------------------------
|
|
def do_model_material(obj):
|
|
|
|
global FD
|
|
|
|
# do we have one?
|
|
ml = get_materials(obj)
|
|
|
|
|
|
n = 0
|
|
for mli in ml:
|
|
if mli:
|
|
n+=1
|
|
if n == 1:
|
|
mat=mli
|
|
|
|
|
|
# if no materials just go back
|
|
if n == 0:
|
|
return
|
|
|
|
# for now we grab the first material on the list.
|
|
|
|
for mat in ml:
|
|
FD.write(" SI_GlobalMaterial {\n" )
|
|
FD.write(" \"" + removeSpacesFromName(mat.name) + "\",\n" )
|
|
FD.write(" \"NODE\",\n" )
|
|
FD.write(" }\n\n" )
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_collect_uv, makes an easy to use list out of the uv data
|
|
# todo, remove duplicates and compress the list size, xsi supports this.
|
|
# ---------------------------------------------------------------------------
|
|
def do_collect_uv(mesh):
|
|
|
|
global UVC, UVI
|
|
|
|
# reset the uv details first.
|
|
UVI = []
|
|
UVC = []
|
|
|
|
#print "Textures..."
|
|
#mtex = mat.getTextures()
|
|
#for mt in mtex:
|
|
# print mt
|
|
|
|
|
|
# if no uv data then return
|
|
if not mesh.hasFaceUV():
|
|
return
|
|
|
|
# run through all the faces
|
|
j = 0
|
|
for f in mesh.faces:
|
|
for uv in f.uv:
|
|
UVI.append(j)
|
|
UVC.append(uv)
|
|
j+=1
|
|
UVI.append(-1)
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_collect_colour, makes an easy to use list out of the colour data
|
|
# todo, remove duplicates and compress the list size, xsi supports this.
|
|
# ---------------------------------------------------------------------------
|
|
def do_collect_colour(mesh):
|
|
|
|
global VCC, VCI
|
|
|
|
# reset the uv details first.
|
|
VCC = []
|
|
VCI = []
|
|
|
|
# if no uv data then return
|
|
if not mesh.hasVertexColours():
|
|
return
|
|
|
|
# run through all the faces
|
|
j = 0
|
|
for f in mesh.faces:
|
|
for c in f.col:
|
|
VCI.append(j)
|
|
VCC.append(c)
|
|
j+=1
|
|
VCI.append(-1)
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_mesh_shape outputs the shape data
|
|
# ---------------------------------------------------------------------------
|
|
def do_mesh_shape(obj):
|
|
|
|
global UVC, UVI, VCC, VCI, FD, NORMALS
|
|
|
|
# Grab the mesh itself
|
|
mesh = obj.data
|
|
|
|
# get the world matrix
|
|
matrix = obj.getMatrix('worldspace')
|
|
|
|
# we need to decide about vertex and uv details first.
|
|
do_collect_uv(mesh)
|
|
do_collect_colour(mesh)
|
|
|
|
# output the shell
|
|
elements=2
|
|
if len(UVC):
|
|
elements+=1
|
|
if len(VCC):
|
|
elements+=1
|
|
FD.write(" SI_Shape SHP-" + removeSpacesFromName ( obj.name ) + "-ORG {\n" )
|
|
FD.write(" %d,\n" % elements )
|
|
FD.write(" \"ORDERED\",\n\n" )
|
|
|
|
# vertices first
|
|
FD.write(" %d,\n" % len(mesh.verts) )
|
|
FD.write(" \"POSITION\",\n" )
|
|
for v in mesh.verts:
|
|
FD.write(" %f,%f,%f,\n" % (v.co[0], v.co[1], v.co[2]) )
|
|
FD.write("\n")
|
|
|
|
|
|
print " MESH NAME = " + mesh.name
|
|
|
|
NORMALS = []
|
|
for f in mesh.faces:
|
|
NORMALS.append ( f.no )
|
|
for v in mesh.verts:
|
|
aTemp = [v.no[0], v.no[1], v.no[2]]
|
|
NORMALS.append ( aTemp )
|
|
|
|
|
|
FD.write(" %d,\n" % len(NORMALS) )
|
|
FD.write(" \"NORMAL\",\n" )
|
|
|
|
for n in NORMALS:
|
|
FD.write(" %f,%f,%f,\n" % ( n[0], n[1], n[2] ) )
|
|
|
|
# if vertex colour data then process
|
|
if mesh.hasVertexColours():
|
|
|
|
# put out the co-ord header
|
|
FD.write(" %d,\n" % len(VCC) )
|
|
FD.write(" \"COLOR\",\n" )
|
|
|
|
# now output them
|
|
for vc in VCC:
|
|
FD.write(" %f,%f,%f,%f,\n" % (vc.r/255.0, vc.g/255.0, vc.b/255.0, vc.a/255.0) )
|
|
|
|
|
|
|
|
# if uv data then process
|
|
if mesh.hasFaceUV():
|
|
# put out the co-ord header
|
|
FD.write(" %d,\n" % len(UVC) )
|
|
FD.write(" \"TEX_COORD_UV\",\n" )
|
|
|
|
# now output them
|
|
for uv in UVC:
|
|
FD.write(" %f,%f\n" % (uv[0], uv[1]) )
|
|
|
|
# close off
|
|
FD.write(" }\n" )
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_mesh_faces outputs the faces data
|
|
# ---------------------------------------------------------------------------
|
|
def do_mesh_faces(obj):
|
|
|
|
global FD, UVI, VCI, mats
|
|
|
|
# do we have a texture?
|
|
ml = get_materials(obj)
|
|
n = 0
|
|
for mli in ml:
|
|
if mli:
|
|
n+=1
|
|
if n == 1:
|
|
mat=mli
|
|
|
|
# Grab the mesh itself
|
|
# mesh = Blender.NMesh.GetRaw(obj.data.name)
|
|
|
|
# mesh = Blender.NMesh.GetRawFromObject(obj.name)
|
|
|
|
mesh = obj.data
|
|
|
|
|
|
|
|
tris = []
|
|
normalX = []
|
|
mats = []
|
|
for f in mesh.faces:
|
|
tris.extend ( triangulate_face(f) )
|
|
aVal = triangulate_normals(mesh,f)
|
|
|
|
for v in aVal:
|
|
normalX.append ( v )
|
|
|
|
|
|
triangles = len(tris)
|
|
|
|
if n == 0:
|
|
FD.write(" SI_TriangleList " + removeSpacesFromName(obj.name) + " {\n")
|
|
FD.write(" %d,\n" % triangles)
|
|
|
|
ostring=" \"NORMAL"
|
|
if len(VCI):
|
|
ostring += "|COLOR"
|
|
if len(UVC):
|
|
ostring += "|TEX_COORD_UV"
|
|
ostring += "\",\n"
|
|
FD.write(ostring)
|
|
|
|
FD.write(" \"\",\n\n")
|
|
|
|
for t in tris:
|
|
FD.write(" %d,%d,%d,\n" % (t[0], t[2], t[1]))
|
|
|
|
FD.write("\n")
|
|
|
|
for n in normalX:
|
|
FD.write(" %d,%d,%d,\n" % ( n[0], n[1], n[2] ) )
|
|
|
|
# finally close this triangle list off
|
|
FD.write(" }\n\n")
|
|
|
|
|
|
|
|
print "total materials"
|
|
print ml
|
|
|
|
for mIndex in range (0,len(ml)):
|
|
mat = ml[mIndex]
|
|
print "checking materials"
|
|
print mat
|
|
|
|
aTriCount = 0
|
|
for tIndex in range ( 0, len ( tris ) ):
|
|
aMat = mats[tIndex]
|
|
if aMat == mIndex:
|
|
aTriCount = aTriCount + 1
|
|
|
|
#
|
|
# output the shell
|
|
FD.write(" SI_TriangleList " + removeSpacesFromName(obj.name) + " {\n")
|
|
# FD.write(" %d,\n" % triangles)
|
|
FD.write(" %d,\n" % aTriCount)
|
|
|
|
ostring=" \"NORMAL"
|
|
if len(VCI):
|
|
ostring += "|COLOR"
|
|
if len(UVC):
|
|
ostring += "|TEX_COORD_UV"
|
|
ostring += "\",\n"
|
|
FD.write(ostring)
|
|
|
|
|
|
FD.write(" \"" + removeSpacesFromName ( mat.name ) + "\",\n\n")
|
|
|
|
# FD.write(" \"\",\n\n")
|
|
|
|
|
|
for tIndex in range ( 0, len ( tris ) ):
|
|
aMat = mats[tIndex]
|
|
if mIndex == aMat:
|
|
t = tris[tIndex]
|
|
FD.write(" %d,%d,%d,\n" % (t[0], t[2], t[1]))
|
|
|
|
FD.write("\n")
|
|
|
|
|
|
|
|
# for n in normalX:
|
|
for tIndex in range ( 0, len ( tris ) ):
|
|
aMat = mats[tIndex]
|
|
if mIndex == aMat:
|
|
n = normalX[tIndex]
|
|
FD.write(" %d,%d,%d,\n" % ( n[0], n[1], n[2] ) )
|
|
|
|
|
|
|
|
# if we have it, put out the colour vertex list
|
|
# ostring = " "
|
|
# for i in range(len(VCI)):
|
|
# if a -1 its end of line, write.
|
|
# if VCI[i] == -1:
|
|
# ostring = ostring + "\n"
|
|
# FD.write(ostring)
|
|
# ostring=" "
|
|
# else:
|
|
# ostring = ostring + "%d," % VCI[i]
|
|
|
|
# The final set is to work out the uv list, its one set per face
|
|
# ostring = " "
|
|
# for i in range(len(UVI)):
|
|
# # if a -1 its end of line, write.
|
|
# if UVI[i] == -1:
|
|
# ostring = ostring + "\n"
|
|
# FD.write(ostring)
|
|
# ostring=" "
|
|
# else:
|
|
# ostring = ostring + "%d," % UVI[i]
|
|
|
|
# finally close this triangle list off
|
|
FD.write(" }\n\n")
|
|
|
|
|
|
def getNormalInfo(mesh, faceInfo):
|
|
global NORMALS
|
|
aNL = []
|
|
for fi in faceInfo:
|
|
aN = []
|
|
|
|
aFace = mesh.faces[fi[0]]
|
|
|
|
print aFace
|
|
|
|
if (aFace.smooth):
|
|
aN.append ( NORMALS.index ( aFace.v.no[0] ) )
|
|
aN.append ( NORMALS.index ( aFace.v.no[1] ) )
|
|
aN.append ( NORMALS.index ( aFace.v.no[2] ) )
|
|
else:
|
|
aN.append ( NORMALS.index ( aFace.no ) )
|
|
aN.append ( NORMALS.index ( aFace.no ) )
|
|
aN.append ( NORMALS.index ( aFace.no ) )
|
|
|
|
# aN.append ( NORMALS.index ( mesh.faces[fi[0]].no ) )
|
|
# aN.append ( NORMALS.index ( mesh.faces[fi[0]].no ) )
|
|
# aN.append ( NORMALS.index ( mesh.faces[fi[0]].no ) )
|
|
|
|
aNL.append ( aN )
|
|
return aNL
|
|
|
|
|
|
|
|
# copy of code to triangulate mesh
|
|
##################################
|
|
def triangulate_face(f):
|
|
if len(f.v) <= 3:
|
|
#newFaces = [ [f.v[0].index, f.v[1].index, f.v[2].index] ]
|
|
newFaces = [ [f.v[0].index, f.v[2].index, f.v[1].index] ]
|
|
mats.append ( f.materialIndex )
|
|
else:
|
|
#newFaces = [ [f.v[0].index, f.v[1].index, f.v[2].index] ]
|
|
#newFaces.append ( [f.v[3].index, f.v[0].index, f.v[2].index] )
|
|
newFaces = [ [f.v[0].index, f.v[2].index, f.v[1].index] ]
|
|
newFaces.append ( [f.v[3].index, f.v[2].index, f.v[0].index] )
|
|
mats.append ( f.materialIndex )
|
|
mats.append ( f.materialIndex )
|
|
|
|
return newFaces
|
|
|
|
# copy of code to triangulate mesh
|
|
##################################
|
|
def triangulate_normals(mesh, f):
|
|
|
|
if len(f.v) <= 3:
|
|
if f.smooth:
|
|
n1 = get_normal_index ( mesh, [f.v[0].no[0], f.v[0].no[1], f.v[0].no[2]] )
|
|
n2 = get_normal_index ( mesh, [f.v[1].no[0], f.v[1].no[1], f.v[1].no[2]] )
|
|
n3 = get_normal_index ( mesh, [f.v[2].no[0], f.v[2].no[1], f.v[2].no[2]] )
|
|
newNormals = [[ n1, n2, n3 ]]
|
|
else:
|
|
n1 = get_normal_index ( mesh, [f.no[0], f.no[1], f.no[2]] )
|
|
newNormals = [[ n1, n1, n1 ]]
|
|
else:
|
|
if f.smooth:
|
|
n1 = get_normal_index ( mesh, [f.v[0].no[0], f.v[0].no[1], f.v[0].no[2]] )
|
|
n2 = get_normal_index ( mesh, [f.v[1].no[0], f.v[1].no[1], f.v[1].no[2]] )
|
|
n3 = get_normal_index ( mesh, [f.v[2].no[0], f.v[2].no[1], f.v[2].no[2]] )
|
|
n4 = get_normal_index ( mesh, [f.v[3].no[0], f.v[3].no[1], f.v[3].no[2]] )
|
|
newNormals = [ [ n1, n2, n3 ] ]
|
|
newNormals.append ( [ n4, n1, n3 ] )
|
|
|
|
# newNormals = [[ n1, n3, n2 ]]
|
|
# newNormals.append ( [ n4, n3, n1 ] )
|
|
else:
|
|
n1 = get_normal_index ( mesh, [f.no[0], f.no[1], f.no[2]] )
|
|
newNormals = [[ n1, n1, n1 ]]
|
|
newNormals.append ( [ n1, n1, n1 ] )
|
|
|
|
return newNormals
|
|
|
|
|
|
|
|
##################################
|
|
def get_normal_index(mesh,normal):
|
|
global NORMALS
|
|
|
|
indx=NORMALS.index(normal)
|
|
return indx
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_model_mesh outputs the shape/triangelist wrapper block
|
|
# ---------------------------------------------------------------------------
|
|
def do_model_mesh(obj):
|
|
|
|
global FD
|
|
|
|
# output the shell
|
|
FD.write(" SI_Mesh MSH-" + removeSpacesFromName(obj.name) + " {\n")
|
|
|
|
# todo, add calc normals and calc uv here
|
|
# these can be used in both the following sections.
|
|
|
|
# next the shape
|
|
do_mesh_shape(obj)
|
|
|
|
# finally the trangle list
|
|
do_mesh_faces(obj)
|
|
|
|
# finally close this mesh off
|
|
FD.write(" }\n\n")
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_model actually outputs a mesh model
|
|
# ---------------------------------------------------------------------------
|
|
def do_model(obj):
|
|
|
|
global FD
|
|
|
|
# we only want meshes for now.
|
|
if 'Mesh' != obj.type:
|
|
return
|
|
|
|
# check if the mesh is valid
|
|
if validMesh(obj) <> 0:
|
|
print "INVALID MESH " + obj.name
|
|
return
|
|
|
|
|
|
print "Exporting model " + obj.name
|
|
|
|
# start model
|
|
FD.write(" SI_Model MDL-" + removeSpacesFromName(obj.name) + " {\n")
|
|
|
|
# do transform
|
|
do_model_transform(obj)
|
|
|
|
# do visibility
|
|
do_model_visibility(obj)
|
|
|
|
# do global material
|
|
do_model_material(obj)
|
|
|
|
# do the mesh
|
|
do_model_mesh(obj)
|
|
|
|
# close this model
|
|
FD.write(" }\n")
|
|
|
|
#
|
|
# check for invalid mesh ( faces that have < 3 vertices )
|
|
#
|
|
|
|
def validMesh (obj):
|
|
mesh = obj.data
|
|
for f in mesh.faces:
|
|
if len(f.v) < 3:
|
|
print "MESH HAS FACES WITH < 3 VERTICES"
|
|
return 1
|
|
if len (mesh.faces) == 0:
|
|
print "MESH HAS NO FACES"
|
|
return 1
|
|
|
|
return 0
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_models is the process which allows us to write out a bunch of models
|
|
# ---------------------------------------------------------------------------
|
|
def do_models():
|
|
|
|
global OBJ, MAT, FD
|
|
|
|
#create the full scene wrapper object
|
|
FD.write("SI_Model MDL-SceneRoot {\n")
|
|
FD.write(" SI_Transform SRT-SceneRoot {\n" )
|
|
FD.write(" 1.000000,\n")
|
|
FD.write(" 1.000000,\n")
|
|
FD.write(" 1.000000,\n")
|
|
FD.write(" -90.000000,\n")
|
|
FD.write(" 0.000000,\n")
|
|
FD.write(" 0.000000,\n")
|
|
FD.write(" 0.000000,\n")
|
|
FD.write(" 0.000000,\n")
|
|
FD.write(" 0.000000,\n")
|
|
FD.write(" }\n\n")
|
|
|
|
# now process the actual selected meshes themselves
|
|
for obj in OBJ:
|
|
do_model(obj)
|
|
|
|
for obj in OBJ:
|
|
do_light(obj)
|
|
|
|
for obj in OBJ:
|
|
do_camera(obj)
|
|
|
|
do_light_ambient ()
|
|
|
|
# finally close off the model list
|
|
FD.write("}\n")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_light actually outputs a light model
|
|
# ---------------------------------------------------------------------------
|
|
def do_light(obj):
|
|
|
|
global FD
|
|
|
|
# we only want lights for now.
|
|
if 'Lamp' != obj.type:
|
|
return
|
|
|
|
print "Exporting light " + obj.name
|
|
|
|
aLampType = 1
|
|
|
|
lmpName=Lamp.Get(obj.getData(name_only=1))
|
|
lmpType=lmpName.getType()
|
|
|
|
if lmpType == Lamp.Types.Lamp:
|
|
aLampType = 0
|
|
elif lmpType == Lamp.Types.Spot:
|
|
aLampType = 0
|
|
elif lmpType == Lamp.Types.Sun:
|
|
aLampType = 1
|
|
else:
|
|
aLampType = 0
|
|
|
|
# start model
|
|
FD.write(" SI_Light " + removeSpacesFromName(obj.name) + " {\n")
|
|
|
|
# do type
|
|
FD.write(" %d,\n" % aLampType)
|
|
|
|
lampName= obj.data
|
|
colour = lampName.col
|
|
|
|
# do color
|
|
FD.write(" %f,\n" % colour[0] )
|
|
FD.write(" %f,\n" % colour[1] )
|
|
FD.write(" %f,\n" % colour[2] )
|
|
|
|
# do position
|
|
|
|
FD.write(" %f,\n" % obj.LocX )
|
|
FD.write(" %f,\n" % obj.LocY )
|
|
FD.write(" %f,\n" % obj.LocZ )
|
|
|
|
|
|
# close this model
|
|
FD.write(" }\n")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# do_light actually outputs a light model
|
|
# ---------------------------------------------------------------------------
|
|
def do_camera(obj):
|
|
|
|
global FD
|
|
|
|
# we only want cameras for now.
|
|
if 'Camera' != obj.type:
|
|
return
|
|
|
|
print "Exporting camera " + obj.name
|
|
|
|
|
|
|
|
# start model
|
|
FD.write(" SI_Camera " + removeSpacesFromName(obj.name) + " {\n")
|
|
|
|
|
|
cameraName=obj.data
|
|
|
|
# colour = cameraName.col
|
|
|
|
# do position
|
|
|
|
FD.write(" %f,\n" % obj.LocX )
|
|
FD.write(" %f,\n" % obj.LocY )
|
|
FD.write(" %f,\n" % obj.LocZ )
|
|
|
|
# looking at
|
|
|
|
FD.write(" %f,\n" % 0.0 )
|
|
FD.write(" %f,\n" % 0.0 )
|
|
FD.write(" %f,\n" % 0.0 )
|
|
|
|
# roll
|
|
FD.write(" %f,\n" % 0.0 )
|
|
|
|
aLens = cameraName.getLens()
|
|
|
|
# field of view
|
|
FD.write(" %f,\n" % aLens )
|
|
|
|
# near plane
|
|
FD.write(" %f,\n" % 1.0 )
|
|
|
|
# far plane
|
|
FD.write(" %f,\n" % 10000000.0 )
|
|
|
|
|
|
# close this model
|
|
FD.write(" }\n")
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# write out the ambient light ( for Shockwave 3D converter )
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def do_light_ambient():
|
|
if WORLD: ambient = WORLD.getAmb()
|
|
else: ambient = 0,0,0
|
|
|
|
FD.write(" SI_Light ambient_sw3d {\n")
|
|
|
|
FD.write(" 9,\n")
|
|
FD.write(" %f,\n" % ambient[0])
|
|
FD.write(" %f,\n" % ambient[1])
|
|
FD.write(" %f,\n" % ambient[2])
|
|
FD.write(" 0.00000000,\n")
|
|
FD.write(" 0.00000000,\n")
|
|
FD.write(" 0.00000000,\n")
|
|
|
|
FD.write(" }\n")
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# export_xsi is the wrapper function to process the loading of an xsi model.
|
|
# ---------------------------------------------------------------------------
|
|
def export_xsi(filename):
|
|
|
|
global OBJ, MAT, FD, EXPORT_DIR
|
|
|
|
# safety check
|
|
if filename.find('.xsi', -4) <= 0:
|
|
print "XSI not found"
|
|
filename += '.xsi'
|
|
|
|
|
|
export_dir = bsys.dirname(filename)
|
|
if export_dir != EXPORT_DIR:
|
|
EXPORT_DIR = export_dir
|
|
|
|
# open our output
|
|
FD = open(filename, 'w')
|
|
|
|
# get the selected objects, otherwise get them all
|
|
#OBJ = Blender.Object.GetSelected()
|
|
#if not OBJ:
|
|
|
|
OBJ = list(Blender.Scene.GetCurrent().objects) #Blender.Object.Get()
|
|
|
|
# we need some objects, if none specified stop
|
|
if not OBJ:
|
|
return
|
|
|
|
# if any exist, grab the materials
|
|
MAT = Blender.Material.Get()
|
|
|
|
# output the header data
|
|
do_header()
|
|
|
|
# output the materials used by the selected objects.
|
|
do_materiallibrary()
|
|
|
|
# we punch out the models, that is, the meshes themselves
|
|
do_models()
|
|
|
|
|
|
# finally close our file
|
|
FD.close()
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Lets trigger it off now
|
|
# Blender.Window.FileSelector(export_xsi, 'Export SoftImage XSI')
|
|
|
|
fname = bsys.makename(ext=".xsi")
|
|
if EXPORT_DIR <> '':
|
|
fname = bsys.join(EXPORT_DIR, bsys.basename(fname))
|
|
|
|
Blender.Window.FileSelector(export_xsi, "Export SoftImage XSI", fname)
|