#!BPY
"""
Name: 'Kerkythea (.xml)...'
Blender: 241
Group: 'Export'
Tooltip: 'Save a Kerkythea scene file'
"""
__author__ = "Ioannis Pantazopoulos, Campbell Barton, Federico 'Lox' Lucignano, Jm Soler"
__url__ = ["blender", "elysiun", "http://users.ntua.gr/~jpanta/Graphics/Kerkythea", "http://members.iinet.net.au/~cpbarton/ideasman/", "http://www.infosquid.net/en"]
__version__ = "1.02.03"
# +---------------------------------------------------------+
# | Slight modification by Jean-michel Soler
# |--- Jul 29 2006 - version 1.02.03
# | little modificatio of the data export to get correct
# | uvmapping coords .
# +-----
# +---------------------------------------------------------+
# | Slight modification by Federico "Lox" Lucignano
# |--- Feb 25 2006 - version 1.02.02
# | Fixed exported camera focal length to match exactly the Blender renderer result (for using kerkythea's renders in Blender's noodles composit editor)
# | Added output Image height and width in tha exported camera node
# +-----
# +---------------------------------------------------------+
# | Slight modifications by Ioannis Pantazopoulos
# |--- Nov 8 - version 1.2.01
# | Fixed self-luminance of models (even zero-luminance objects were assigned luminance before leading to increased render time)
# | Fixed scene name when path included '/' character (which is reserved by K parser)
# +---------------------------------------------------------+
#
# +---------------------------------------------------------+
# | Heavily Modified by ideasman (Campbell Barton)
# |--- Aug 01 - version 1.01.005
# | Fixed light falloff/hotspot range problems, still touchy
# | Added ray mirror. Uses Blender RayMirr value * the blenders MirrColour
# | Merged Material settings, so images and raytracing can be mixed works well.
# | Added material cast shadows/ignore shadows opts from blender.
# | Added material emit, so can do nice radiosity styly objects emmiting light to eachother.
# | Added simple support for invisible materials, will be invisible if materials alpha is 0.0, any other value will by fully visible.
# |---
# | Optimized 4x Speedup, remove doubles while exporting with a dict hash lookup. Test showed 22sec, 27meg --> 4.5sec 7meg improvement.
# | Added support for multiple materials per mesh, this includes splitting materials by Texface images. (if Texface not used then MTex is used)
# | Improved material compatibility and added (colour, specular, ior...) also weighted ambient colour to the worls AMB (if existing) with teh materials AMB slider.
# | Added many setting for lights- inverse square, no shadows, falloff etc.
# | Improved data gathering methods
# | Removed dependancies on external modules
# | Now uses current camera for export as default.
# | Mesh export uses the deformed mesh so subserf works.
# | Improved string formatting - still more todo.
# | Fixed UV mapped faces, was not working, exporting incorrect Syntax
# | Added basic support for Texface if no texture image exists.
# | Improved mesh export, chunky 3 verts per face, but It seems that to keep normals and UVs in sync you need to do that.
# | ...however checking and removing doubles would be possible, yet slow.
# | Added to Blenders export menu. No file Fileselector, File will always export with the Blend name replaced with XML.
# | General Cleanum.
# +---------------------------------------------------------+
# ***** 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 *****
# --------------------------------------------------------------------------
import Blender
from Blender import Material, Scene
from Blender.Scene import Render
# ===================================
# === Write Kerkythea Mesh Format ===
# ===================================
# ====================
# === Write Meshes ===
# ====================
def write_meshes(file, mesh_objects):
world = Blender.World.GetCurrent()
mesh_objects.sort(lambda a,b: cmp(a.name, b.name))
for object in mesh_objects:
mesh = Blender.NMesh.GetRawFromObject(object.name)
'''
We are going to split the faces by material.
Each material can be identified by its dictionary key
(MatName, texFaceImageName) - texFaceImageName can be None
Each item will hold a face list
'''
# Store all faces under there mat/image tuple permutations of
MATERIAL_DICT = {}
meshMaterials = mesh.materials
meshMatListLen = len(meshMaterials)
# Use the faces to build a
for f in mesh.faces:
if len(f) > 2:
# Get the Mat and material names.
if meshMatListLen:
if f.mat >= len(meshMaterials):
matName =None
else:
# Face has a valid index.
matName = meshMaterials[f.mat].name
else:
matName = None
if f.image:
imageName = f.image.name
else:
imageName = None
try:
MATERIAL_DICT[ (matName, imageName) ].append(f)
except KeyError:
MATERIAL_DICT[ (matName, imageName) ] = [f]
# Loop through faces, subdivided by materials.
for matKey, matFaces in MATERIAL_DICT.items():
triFaces = [f for f in matFaces if len(f) == 3]
quadFaces = [f for f in matFaces if len(f) == 4]
count = len(triFaces) + (len(quadFaces)*2)
# MAKE A LIST OF ALL VERT PROPERTIES SO WE CAN WRITE IN SYNC!
allVertProps = [] # Realy a list, just using a dict for removing doubles
if mesh.hasFaceUV(): # With UVs
for f in matFaces:
if f.smooth: # Use vertex normal
for i in range(len(f)):
allVertProps.append(( tuple(f[i].co), tuple(f[i].no), (f.uv[i][0],1.0-f.uv[i][1]) ) )
else: # use face normal
no = tuple(f.no)
for i in range(len(f)):
allVertProps.append(( (tuple(f[i].co), no, (f.uv[i][0],1.0-f.uv[i][1]) )))
else: # Without UV's
for f in matFaces:
if f.smooth: # Use vertex normal
for i in range(len(f)):
allVertProps.append(( (tuple(f[i].co), tuple(f[i].no) ) ))
else: # use face normal
no = tuple(f.no)
for i in range(len(f)):
allVertProps.append(( (tuple(f[i].co), no) ))
removeDoubleFaceIndicies = [] # list of face indicies for latere writing.
# Make a dict from the vertex properties to remove doubles.
allVertPropsDict = {}
# This block of code basicly assign an index to each vert, using an old index
# if all the verts data (normal, uv co) batch up, so we dont waist data.
# Alling the way it builds a list of face indicies that we use later to write the faces.
vertIndex = 0
ioffset = 0
for f in matFaces:
newFace = []
for faceOffsetIndex in range(len(f)):
try:
index = allVertPropsDict[allVertProps[vertIndex+faceOffsetIndex]]
ioffset += 1
except KeyError:
index = allVertPropsDict[allVertProps[vertIndex+faceOffsetIndex]] = vertIndex + faceOffsetIndex - ioffset
newFace.append(index)
removeDoubleFaceIndicies.append(newFace)
vertIndex+=len(f)
# isnt a dict but may as well reuse the same var,
# by the way, its a sorted list.
allVertProps = allVertPropsDict.items()
allVertProps.sort(lambda a,b: cmp(a[1], b[1]))
i = len(allVertProps)
while i:
i-=1
allVertProps[i] = allVertProps[i][0]
#allVertProps = quadFaceVertProps + triFaceVertProps
# For readability
PROP_COORD = 0
PROP_NORMAL = 1
PROP_FUV = 2
# Begin Model
file.write('\n")
# =====================
# === Write Camera ====
# =====================
def write_cameras(file, camera_objects, renderingContext):
for camera_object in camera_objects:
camera = camera_object.getData()
# fovh = 2 * math.atan(16/camera.lens) #in radians KerkytheaFocalLength=(1/2)/atan(FOV/2)
file.write('\n' % (camera_object.name, camera_object.name) )
file.write('\n' % (camera.lens/24))
file.write('\n' % (renderingContext.imageSizeX(), renderingContext.imageSizeY()))
m = camera_object.matrixWorld
file.write('\n' %\
(m[0][0],-m[1][0],-m[2][0],m[3][0], m[0][1],-m[1][1],-m[2][1],m[3][1], m[0][2],-m[1][2],-m[2][2],m[3][2]))
file.write('\n')
# =====================
# === Write Lights ===
# =====================
def write_lights(file, light_objects):
for object in light_objects:
lamp = object.data
flag = lamp.mode
file.write('\n' % (object.name, object.getData(1)) )
if lamp.type == 2: # spot light
file.write('\n')
# Hot spot must be smaller then falloff, 1.001 works, but 1.0 wont render a lamp with full blender-lamp falloff.
file.write( '\n' % (lamp.getSpotSize() * (1.001-lamp.getSpotBlend() ) ) )
file.write( '\n' % lamp.getSpotSize() ) # Blenders lamp softness is a value 0/1, 1 being most soft.
else:
file.write('\n')
file.write('\n')
file.write('\n' % tuple(lamp.col) )
file.write('\n')
if flag & lamp.Modes['Quad']:
file.write('\n')
else:
file.write('\n')
file.write('\n')
file.write('\n')
file.write('\n' % (flag & lamp.Modes['Shadows']) )
file.write('\n' % (lamp.getSoftness() > 1.0) )
file.write('\n' % (flag & lamp.Modes['Negative']))
file.write('\n')
file.write('\n')
file.write('\n' % (lamp.energy*2)) # Blenders lamp energy doubled seems roughly correct.
m=object.matrix
if lamp.type == 3: # directional light - put it far away
m[3][0]=m[3][0]+(m[3][0]-0)*10000
m[3][1]=m[3][1]+(m[3][1]-0)*10000
m[3][2]=m[3][2]+(m[3][2]-0)*10000
file.write('\n' %\
(m[0][0],-m[1][0],-m[2][0],m[3][0], m[0][1],-m[1][1],-m[2][1],m[3][1], m[0][2],-m[1][2],-m[2][2],m[3][2]))
# Focus dist, use clip end
file.write('\n' % lamp.getClipEnd())
# Softness radius, use spotlamps softness, not quite exuivilent since blenders softness isnt a radius rather a value for bluring a shadowe map.
file.write('\n' % ((lamp.getSoftness()-1) * 0.1) ) # devide by 10 since blenders is less powfurl
file.write("\n")
# =====================
# === Write Globals ===
# =====================
def write_global_settings(file):
world = Blender.World.GetCurrent()
file.write('\n')
if world: # world == None if there is no world.
file.write('\n' % tuple(world.getAmb()) )
file.write('\n' % tuple(world.getZen()) )
else:
file.write('\n')
file.write('\n')
file.write('\n')
file.write('\n')
file.write('\n')
# ========================
# === Write "K" Header ===
# ========================
def write_header(file):
name = Blender.Get('filename')
file.write('\n' % name.replace('/','\\'))
# ========================
# === Write "K" Footer ===
# ========================
def write_footer(file):
file.write('\n')
# ============
# === main ===
# ============
def main():
currentBlendDir = Blender.sys.expandpath('//')
currentBlendFile = Blender.Get('filename')
k_filename = currentBlendFile.replace('.blend', '.xml')
#k_filename = '/test.xml' # For testing
# Ask the user if they want to write to the current dir.
choice = Blender.Draw.PupMenu('Write Kerkythea to: ' + k_filename + ' ?%t|OK!')
if choice == -1:
return
time1 = Blender.sys.time()
print 'Starting to export:"%s"' % k_filename
# Build a list of objects.
scn = Blender.Scene.GetCurrent()
mesh_objects = []
light_objects = []
camera_objects = []
context = scn.getRenderingContext()
# Camera list, current camera first.
camera_main = scn.getCurrentCamera()
if camera_main:
camera_objects.append(camera_main)
for ob in scn.getChildren():
obType = ob.getType()
if obType == 'Mesh':
mesh_objects.append(ob)
elif obType == 'Lamp':
light_objects.append(ob)
elif obType == 'Camera' and ob != camera_main:
camera_objects.append(ob)
file = open(k_filename, "wb")
write_header(file)
write_global_settings(file)
write_meshes(file, mesh_objects)
write_cameras(file, camera_objects, context)
write_lights(file, light_objects)
write_footer(file)
file.close()
print 'Export Time: %.4f'% (Blender.sys.time() - time1)
print "Kerkythea xml Exported to:", k_filename
main()