Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # -*- coding: utf-8 -*-
- #------------------------------------------------------------------------------
- # panda3d-egg exporter for blender 2.5
- # written by Reto Spoerri (Dez.2010) (rspoerri at nouser dot org)
- #------------------------------------------------------------------------------
- # whats done:
- # - basic material settings (diffuse color and specular and shininess)
- # - smooth and flat shaded faces
- # - multi-uv maps
- # - basic texturing stuff
- #------------------------------------------------------------------------------
- # missing functionality:
- # - face vertex colors
- # - bone animations (any animations)
- # - uv-wrapping-modes
- # - testing
- # - double sided vertices
- # - interface, currently the output path needs to be defined manually
- #------------------------------------------------------------------------------
- # known or possible bugs:
- # - lots of
- #------------------------------------------------------------------------------
- # functionality:
- # exports blender 2.5 data as egg file
- # how it works:
- # (1) the blender node-tree is walked trough (class __init__ functions)
- # - Root
- # - World (not used)
- # - Scene
- # - Object
- # - Object (recursive)
- # - MeshData
- # - Face
- # - Vertex
- # - UvTexture
- # - UvTextureData
- # - Curve (not implemented)
- # - Material
- # - TextureSlot
- # - Texture
- # (2) the write funtion of each class goes trough
- # - Materials
- # - Textures
- # - Scene
- # - Object
- # - Object (recursive)
- # - MeshData
- # -> the MeshData.write function converts blender faces and vertices
- # into egg face and vertice data which is then written
- #------------------------------------------------------------------------------
- bl_addon_info = {
- "name": "Export Panda3d Egg Format (.egg)",
- "author": "Reto Spoerri",
- "version": (0, 1),
- "blender": (2, 5, 4),
- "api": 31847,
- "location": "File > Export",
- "description": "Export to the Panda3d Model Format (.x)",
- "warning": "",
- "category": "Import/Export"}
- pviewBinary = '/Developer/Tools/Panda3D/pview'
- #------------------------------------------------------------------------------
- # dont need to change
- #------------------------------------------------------------------------------
- import bpy
- import subprocess
- import os
- import time
- #from bpy.props import *
- from io_utils import ExportHelper
- eggFilename = 'out-004.egg'
- rootFolder = '/Users/rspoerri/Desktop/eggExporter25/'
- logFilename = 'eggExporter.log'
- # number of spaces to indent on each level
- indentAdd = 2
- DEBUG = 2 # 0 disabled, 1 file, 2 print
- #------------------------------------------------------------------------------
- # DEBUG WRITER
- #------------------------------------------------------------------------------
- class Debug(object):
- def __init__(self):
- if DEBUG == 0:
- pass
- if DEBUG == 1:
- self.debugfile = open(os.path.join(rootFolder, logFilename), 'wt')
- self.write(time.asctime()+":\n")
- if DEBUG == 2:
- pass
- def write(self, *text):
- if DEBUG == 1:
- if len(text) == 1 and isinstance(text, str):
- self.debugfile.write(text[0].rstrip()+"\n")
- else:
- self.debugfile.write(str(text)+"\n")
- self.debugfile.flush()
- if DEBUG == 2:
- if len(text) == 1 and isinstance(text, str):
- print(text[0].rstrip()+"\n")
- else:
- print(str(text)+"\n")
- debug = Debug()
- #------------------------------------------------------------------------------
- # SOME FUNCTIONS, taken from the 2.49 exporter
- #------------------------------------------------------------------------------
- def eggSafeName(s):
- """Function that converts names into something suitable for the egg file format - simply puts " around names that contain spaces and prunes bad characters, replacing them with an underscore."""
- s = s.replace('"','_') # Sure there are more bad characters, but this will do for now.
- if ' ' in s:
- return '"' + s + '"'
- else:
- return s
- def convertFileNameToPanda(filename):
- """Converts Blender filenames to Panda 3D filenames."""
- path = filename.replace('//', './').replace('\\', '/')
- if os.name == 'nt' and path.find(':') != -1:
- path = '/'+ path[0].lower() + path[2:]
- return path
- #------------------------------------------------------------------------------
- # A SIMPLE RECURSIVE HIERARCHY
- #------------------------------------------------------------------------------
- class Node(object):
- def __init__(self, parent):
- self.parent = parent
- self.children = list()
- def write(self, recursion):
- eggContent = ''
- for child in self.children:
- eggContent += child.write(recursion+1)
- return eggContent
- #------------------------------------------------------------------------------
- # indent function, prints (argument) number of spaces
- #------------------------------------------------------------------------------
- def indent(level):
- return level*indentAdd*" "
- #------------------------------------------------------------------------------
- # A BLENDER CURVE
- #------------------------------------------------------------------------------
- class CurveData(Node):
- def __init__(self, parent, node):
- super(Curve, self).__init__(parent)
- #------------------------------------------------------------------------------
- # A BLENDER VERTEX
- #------------------------------------------------------------------------------
- class Vertex(Node):
- def __init__(self, parent, vertexNode):
- super(Vertex, self).__init__(parent)
- self.index = vertexNode.index
- # the coordinate system must be in world coordinates
- self.coordinate = (self.parent.parent.worldTransform * vertexNode.co)
- self.normal = vertexNode.normal[:]
- if vertexNode.normal.length != 0:
- # convert into world coordinates
- self.normal = (self.parent.parent.worldTransform.rotation_part() * vertexNode.normal).normalize()[:]
- def __repr__(self):
- out = 'vertex: %i : %s (%s)\n' % (self.index, " ".join(map(str, self.coordinate)), " ".join(map(str, self.normal)))
- for [uvName, uvData] in self.uvTextures:
- out += " - uv: %s : %s\n" % (uvName, " ".join(map(str, uvData)))
- return out
- #------------------------------------------------------------------------------
- # A BLENDER FACE
- #------------------------------------------------------------------------------
- class Face(Node):
- def __init__(self, parent, faceNode):
- super(Face, self).__init__(parent)
- self.index = faceNode.index
- self.vertices = faceNode.vertices[:]
- self.normal = faceNode.normal[:]
- if faceNode.normal.length != 0:
- # convert into world coordinates
- self.normal = (self.parent.parent.worldTransform.rotation_part() * faceNode.normal).normalize()[:]
- self.flat_faces = not faceNode.use_smooth
- self.material_index = faceNode.material_index
- def __repr__(self):
- out = 'face: %i : %s (%s)\n' % (self.index, " ".join(map(str, self.vertices)), " ".join(map(str, self.normal)))
- return out
- #------------------------------------------------------------------------------
- # A BLENDER MESH
- #------------------------------------------------------------------------------
- class MeshData(Node):
- def __init__(self, parent, meshDataNode):
- super(MeshData, self).__init__(parent)
- self.name = eggSafeName(meshDataNode.name)
- self.materials = list()
- self.textures = list()
- for material in meshDataNode.materials:
- if material:
- self.materials.append(eggSafeName(material.name))
- for texture_slots in material.texture_slots:
- if texture_slots:
- self.textures.append(eggSafeName(texture_slots.name))
- # add all faces and remember smooth ones, by default it's set to hard
- self.faces = list()
- for faceNode in meshDataNode.faces:
- self.faces.append(Face(self, faceNode))
- # add all vertices
- self.vertices = list()
- for vertexNode in meshDataNode.vertices:
- self.vertices.append(Vertex(self, vertexNode))
- self.uvTextures = list()
- for uvTexturesNode in meshDataNode.uv_textures:
- self.uvTextures.append(UvTextures(self, uvTexturesNode))
- def write(self, recursion):
- eggVertexList = list()
- class EggVertex():
- def __init__(self, index, coordinate, smooth, normal, uvData):
- eggVertexList.append(self)
- self.index = index
- self.coordinate = coordinate
- self.smooth = smooth
- self.normal = normal
- self.uvData = uvData
- def write(self, recursion):
- # hard version
- eggContent = ""
- eggContent += indent(recursion+1)+"<Vertex> %i {\n" % (self.index)
- eggContent += indent(recursion+2)+"%s\n" % " ".join(map(str, self.coordinate))
- # uv & vertex_color & normal untested
- for uvName, uvCoord in self.uvData:
- eggContent += indent(recursion+2)+"<UV> %s { %s }\n" % (uvName, " ".join(map(str, uvCoord)))
- # without normals these are smooth edges
- if self.smooth:
- eggContent += indent(recursion+2)+"<Normal> { %s }" % " ".join(map(str, self.normal))+"\n"
- # this is not implemented yet
- #if self.vertex_color:
- # eggContent += indent(recursion+2)+"<RGBA> { %s }" % " ".join(map(str, self.vertex_color))+"\n"
- eggContent += indent(recursion+1)+"}\n"
- return eggContent
- eggFaceList = list()
- class EggFace():
- def __init__(self, index, name, vertices, normal, flat, material, textures):
- eggFaceList.append(self)
- self.index = index
- self.name = name
- self.vertices = vertices
- self.normal = normal
- self.flat = flat
- self.material = material
- self.textures = textures
- def write(self, recursion):
- eggContent = ""
- eggContent += indent(recursion )+"<Polygon> {\n"
- eggContent += indent(recursion+1)+"<VertexRef> {\n"
- eggContent += indent(recursion+2)+" ".join(map(str, self.vertices))+"\n"
- eggContent += indent(recursion+2)+"<Ref> { %s }\n" % self.name
- eggContent += indent(recursion+1)+"}\n"
- # this makes hard edges, but is overridden by smooth ones (which dont know if it shall be smooth or hard)
- if self.flat:
- eggContent += indent(recursion+1)+"<Normal> { %s }\n" % " ".join(map(str, self.normal))
- if self.material:
- eggContent += indent(recursion+1)+"<MRef> { %s }\n" % self.material
- for texture in self.textures:
- if texture:
- eggContent += indent(recursion+1)+"<TRef> { %s }\n" % texture
- eggContent += indent(recursion)+"}\n"
- return eggContent
- eggVertexIndex = 0
- for faceId, face in enumerate(self.faces):
- faceIndex = face.index
- faceVertices = face.vertices
- faceNormal = face.normal
- faceFlat = face.flat_faces
- faceMaterial = None
- if face.material_index < len(self.materials):
- faceMaterial = self.materials[face.material_index]
- faceTextures = self.textures
- # list of the vertex id's for the egg file
- eggFaceVertexList = list()
- for faceVerticesId, vertexIndex in enumerate(faceVertices):
- vertex = self.vertices[vertexIndex]
- vertexCoordinate = vertex.coordinate
- vertexNormal = vertex.normal
- vertexSmooth = not faceFlat
- #vertexUv = faceUvs[faceVerticesId]
- uvData = []
- for uvTexture in self.uvTextures:
- uvData.append([uvTexture.name, uvTexture.uvTextureData[faceIndex].uv[faceVerticesId]])
- if len(uvData) > 0:
- uvData[0][0] = ""
- # create the vertices for the egg file
- EggVertex(eggVertexIndex, vertexCoordinate, vertexSmooth, vertexNormal, uvData)
- eggFaceVertexList.append(eggVertexIndex)
- eggVertexIndex += 1
- # create the faces for the egg file
- EggFace(faceIndex, self.name, eggFaceVertexList, faceNormal, faceFlat, faceMaterial, faceTextures)
- # handle vertices first
- eggContent = ""
- eggContent += indent(recursion)+"<VertexPool> %s {\n" % self.name
- for vertex in eggVertexList:
- eggContent += vertex.write(recursion)
- eggContent += indent(recursion)+"}\n"
- # handle faces afterwards
- for face in eggFaceList:
- #if isinstance(childNode, Face):
- eggContent += face.write(recursion)
- return eggContent
- def __repr__(self):
- ''' for debugging purposes '''
- out = ''
- for c in self.materials:
- out += c.__repr__()+"\n"
- for c in self.faces:
- out += c.__repr__()+"\n"
- for c in self.vertices:
- out += c.__repr__()+"\n"
- for c in self.uvTextures:
- out += c.__repr__()+"\n"
- return out
- #------------------------------------------------------------------------------
- # BLENDER OBJECT NODE
- #------------------------------------------------------------------------------
- class Object(Node):
- def __init__(self, parent, objectNode):
- super(Object, self).__init__(parent)
- self.name = eggSafeName(objectNode.name)
- self.worldTransform = objectNode.matrix_world
- self.localTransform = objectNode.matrix_local
- # different type of objects
- if objectNode.type == 'MESH':
- self.children.append(MeshData(self, objectNode.data))
- if objectNode.type == 'CURVE':
- self.children.append(CurveData(self, objectNode.data))
- # childrens
- for childNode in objectNode.children:
- self.children.append(Object(self, childNode))
- def write(self, recursion):
- eggContent = ""
- eggContent += indent(recursion)+"<Group> %s {\n" % (self.name)
- if self.worldTransform:
- eggContent += indent(recursion+1)+"<Transform> {\n"
- eggContent += indent(recursion+2)+"<Matrix4> {\n"
- eggContent += indent(recursion+3)+" ".join(map(str, self.worldTransform[0]))+"\n"
- eggContent += indent(recursion+3)+" ".join(map(str, self.worldTransform[1]))+"\n"
- eggContent += indent(recursion+3)+" ".join(map(str, self.worldTransform[2]))+"\n"
- eggContent += indent(recursion+3)+" ".join(map(str, self.worldTransform[3]))+"\n"
- eggContent += indent(recursion+2)+"}\n"
- eggContent += indent(recursion+1)+"}\n"
- eggContent += super(Object, self).write(recursion)
- eggContent += indent(recursion)+"}\n"
- return eggContent
- #------------------------------------------------------------------------------
- # BLENDER SCENE NODE
- #------------------------------------------------------------------------------
- class Scene(Node):
- def __init__(self, parent, sceneNode):
- super(Scene, self).__init__(parent)
- self.name = eggSafeName(sceneNode.name)
- for object in sceneNode.objects:
- if not object.parent: # only handle objects without parents
- self.children.append(Object(self, object))
- def write(self, recursion):
- eggContent = ""
- for childNode in self.children:
- eggContent += childNode.write(recursion)
- return eggContent
- #------------------------------------------------------------------------------
- # BLENDER WORLD NODE
- #------------------------------------------------------------------------------
- class World(Node):
- # dont know what worlds are used for...
- def __init__(self, parent, worldNode):
- super(World, self).__init__(parent)
- def write(self, recursion):
- eggContent = ""
- for childNode in self.children:
- eggContent += childNode.write(recursion)
- return eggContent
- #------------------------------------------------------------------------------
- # THE BLENDER UVTEXTURE (a single entry of a uv)
- #------------------------------------------------------------------------------
- class UvTextureData(Node):
- def __init__(self, parent, uvTextureDataNode):
- super(UvTextureData, self).__init__(parent)
- self.uv = list()
- for uvCoordinate in uvTextureDataNode.uv:
- self.uv.append(uvCoordinate[:])
- def __repr__(self):
- out = ''
- for c in self.uv:
- out += str(c)
- return out
- #------------------------------------------------------------------------------
- # THE BLENDER UVTEXTURES
- #------------------------------------------------------------------------------
- class UvTextures(Node):
- def __init__(self, parent, uvTexturesNode):
- super(UvTextures, self).__init__(parent)
- self.name = uvTexturesNode.name
- self.uvTextureData = list()
- for uvTextureDataNode in uvTexturesNode.data:
- self.uvTextureData.append(UvTextureData(self, uvTextureDataNode))
- def __repr__(self):
- out = self.name+"\n"
- for c in self.uvTextureData:
- out += c.__repr__()+"\n"
- return out
- #------------------------------------------------------------------------------
- # MATERIALS
- #------------------------------------------------------------------------------
- all_materials = dict()
- class Material(Node):
- def __init__(self, parent, materialNode):
- super(Material, self).__init__(parent)
- debug.write("MATERIAL")
- self.name = eggSafeName(materialNode.name)
- self.diffuse_color = materialNode.diffuse_color[:]
- specular_intensity = materialNode.specular_intensity/2.0
- self.specular_color = [
- materialNode.specular_color[0]*specular_intensity,
- materialNode.specular_color[1]*specular_intensity,
- materialNode.specular_color[2]*specular_intensity,
- ]
- self.shininess = materialNode.specular_hardness/4.0
- self.texture_slot_names = list()
- self.texture_slots = list()
- for texture_slot in materialNode.texture_slots:
- if texture_slot:
- self.texture_slot_names.append(texture_slot.name)
- self.texture_slots.append(TextureSlot(self, texture_slot))
- # store in global all_materials
- global all_materials
- all_materials[self.name] = self
- def write(self, recursion):
- eggContent = ""
- if self.name:
- eggContent += indent(recursion)+"<Material> %s {\n" % self.name
- if self.diffuse_color:
- eggContent += indent(recursion+1)+"<Scalar> diffr { %f }\n" % self.diffuse_color[0]
- eggContent += indent(recursion+1)+"<Scalar> diffg { %f }\n" % self.diffuse_color[1]
- eggContent += indent(recursion+1)+"<Scalar> diffb { %f }\n" % self.diffuse_color[2]
- if self.specular_color:
- eggContent += indent(recursion+1)+"<Scalar> specr { %f }\n" % self.specular_color[0]
- eggContent += indent(recursion+1)+"<Scalar> specg { %f }\n" % self.specular_color[1]
- eggContent += indent(recursion+1)+"<Scalar> specb { %f }\n" % self.specular_color[2]
- if self.shininess:
- eggContent += indent(recursion+1)+"<Scalar> shininess { %f }\n" % self.shininess
- eggContent += indent(recursion)+"}\n"
- eggContent += super(Material, self).write(recursion+1)
- return eggContent
- #------------------------------------------------------------------------------
- # TEXTURE
- # a texture is a subpart of a material[x]->texture_slot[x]->texture
- # or directly accessable on the root
- #------------------------------------------------------------------------------
- all_textures = dict()
- class Texture(Node):
- def __init__(self, parent, textureNode):
- super(Texture, self).__init__(parent)
- if textureNode.type != 'IMAGE':
- debug.write('texture with invalid type')
- all_textures[self.name] = self
- def write(self, recursion):
- eggContent = ""
- return eggContent
- #------------------------------------------------------------------------------
- # TEXTURE_SLOT
- # it has access to more informations then the texture itself
- # it knows about the modes, that the texture is defined to
- #------------------------------------------------------------------------------
- all_texture_slots = dict()
- class TextureSlot(Node):
- def __init__(self, parent, textureSlotNode):
- super(TextureSlot, self).__init__(parent)
- self.name = eggSafeName(textureSlotNode.name)
- #self.texture = Texture(self, textureSlotNode.texture)
- self.envType = 'MODULATE' # default
- # "normal" coloring modes
- if textureSlotNode.blend_type == 'MIX':
- self.envType = 'MIX'
- # or when there is a texture on the object already
- #self.envType = 'MODULATE'
- if textureSlotNode.blend_type == 'MULTIPLY':
- self.envType = 'MODULATE'
- if textureSlotNode.blend_type == 'ADD':
- self.envType = 'ADD'
- if textureSlotNode.blend_type == 'SCREEN':
- self.envType = 'DECAL'
- # if it's a normal map
- debug.write("textureSlotNode.use_map_normal", textureSlotNode.use_map_normal)
- if textureSlotNode.use_map_normal:
- debug.write("textureSlotNode.texture.use_normal_map", textureSlotNode.texture.use_normal_map)
- if textureSlotNode.texture.use_normal_map:
- self.envType = 'NORMAL'
- else:
- self.envType = 'HEIGHT'
- # if it's a glossmap
- if textureSlotNode.use_map_specular:
- self.envType = 'GLOSS'
- # if it's a glowmap
- if textureSlotNode.use_map_emit:
- self.envType = 'GLOW'
- debug.write("envtype", self.envType)
- self.textureName = eggSafeName(textureSlotNode.texture.name)
- self.textureImageName = None
- self.textureImageFilepath = "INVALID"
- self.wrapMode = 'REPEAT'
- self.filter = 'LINEAR_MIPMAP_LINEAR'
- if textureSlotNode.texture.use_mipmap:
- if not textureSlotNode.texture.use_interpolation:
- self.filter = 'NEAREST'
- if textureSlotNode.texture.image:
- if textureSlotNode.texture.image.source != 'FILE':
- debug.write('texture.image with invalid source')
- if textureSlotNode.texture.image.mapping != 'UV':
- debug.write('texture.image with invalid mapping')
- if textureSlotNode.texture.image.type != 'IMAGE':
- debug.write('texture.image with invalid type')
- self.textureImageName = eggSafeName(textureSlotNode.texture.image.name)
- self.textureImageFilepath = textureSlotNode.texture.image.filepath
- global all_texture_slots
- debug.write("found texture slot", self.name, self)
- all_texture_slots[self.name] = self
- debug.write("all_texture_slots", all_texture_slots.keys())
- def write(self, recursion):
- eggContent = ""
- eggContent += indent(recursion)+"<Texture> %s {\n" % self.textureName
- if self.textureImageFilepath:
- eggContent += indent(recursion+1)+'"%s"\n' % self.textureImageFilepath
- eggContent += indent(recursion+1)+"<Scalar> saved-result { 1 }\n"
- eggContent += indent(recursion+1)+"<Scalar> envtype { %s }\n" % self.envType
- eggContent += indent(recursion+1)+"<Scalar> minfilter { %s }\n" % self.filter
- eggContent += indent(recursion+1)+"<Scalar> magfilter { %s }\n" % self.filter
- eggContent += indent(recursion+1)+"<Scalar> wrap { %s }\n" % self.wrapMode
- eggContent += indent(recursion)+"}\n"
- return eggContent
- #------------------------------------------------------------------------------
- # BLENDER SCENE ROOT
- #------------------------------------------------------------------------------
- class Root(Node):
- def __init__(self, rootNode):
- super(Root, self).__init__(None)
- for worldNode in rootNode.worlds:
- self.children.append(World(self, worldNode))
- for sceneNode in rootNode.scenes:
- self.children.append(Scene(self, sceneNode))
- for materialNode in rootNode.materials:
- self.children.append(Material(self, materialNode))
- #for textureNode in rootNode.textures:
- # self.children.append(Texture(self, textureNode))
- def write(self, filename):
- outfile = open(filename, 'wt')
- eggContent = ""
- # handle materials first
- for child in self.children:
- if isinstance(child, Material):
- eggContent += child.write(0)
- # handle textures
- global all_texture_slots
- debug.write("final all_texture_slots", all_texture_slots.keys())
- for childName, child in all_texture_slots.items():
- if isinstance(child, TextureSlot):
- debug.write(childName, child)
- eggContent += child.write(0)
- # handle scenes second
- for child in self.children:
- if isinstance(child, Scene):
- eggContent += child.write(0)
- # handle worlds last
- for child in self.children:
- if isinstance(child, World):
- eggContent += child.write(0)
- outfile.write(eggContent)
- outfile.close()
- #------------------------------------------------------------------------------
- # BLENDER GUI STUFF
- #------------------------------------------------------------------------------
- class ExportEgg(bpy.types.Operator):
- '''Export a Panda3d Egg file (.egg)'''
- bl_idname = "export.panda3d_egg"
- bl_label = "Export Panda3d EGG"
- filename_ext = ".egg"
- filepath = bpy.props.StringProperty()
- filename = bpy.props.StringProperty()
- directory = bpy.props.StringProperty()
- verbose = bpy.props.BoolProperty(
- name="Verbose",
- description="Run the exporter in debug mode. Check the console for output.",
- default=False
- )
- preview = bpy.props.BoolProperty(
- name="Preview",
- description="View the exported Egg in PView",
- default=True,
- )
- def execute(self, context):
- #Append .x if needed
- filepath = self.filepath
- if not filepath.lower().endswith(".egg"):
- filepath += ".egg"
- Root(bpy.data).write(filepath)
- if self.preview:
- pid = subprocess.Popen([pviewBinary, eggFilename]).pid
- return {"FINISHED"}
- def invoke(self, context, event):
- if not self.filepath:
- self.filepath = os.path.splitext(context.blend_data.filepath)[0] + self.filename_ext
- context.window_manager.add_fileselect(self)
- return {"RUNNING_MODAL"}
- def menu_func(self, context):
- default_path = os.path.splitext(bpy.data.filepath)[0] + ".egg"
- self.layout.operator(ExportEgg.bl_idname, text="Panda3d Egg (.egg)").filepath = default_path
- def register():
- bpy.types.INFO_MT_file_export.append(menu_func)
- def unregister():
- bpy.types.INFO_MT_file_export.remove(menu_func)
- if __name__ == "__main__":
- register()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement