diff --git a/blender/obj_inport_export.py b/blender/obj_inport_export.py new file mode 100644 index 00000000..13a0e342 --- /dev/null +++ b/blender/obj_inport_export.py @@ -0,0 +1,1269 @@ +#!BPY +"""Registration info for Blender menus: +Name: 'OBJ...' +Blender: 232 +Group: 'Import' +Tip: 'Import Wavefront File Format (*.obj)' +""" + +#========================================================================= +# Wavefront OBJ Importer/Exporter v1.2 +# This is a complete OBJ importer and exporter script +# All Rights Reserved +# chris@artdreamdesigns.com +#========================================================================= + +#========================================================================= +# mise a jour pour Blender 228 et suivant jm soler +# mise a jour pour importer zbrush obj avec uvmapping jm soler +# mise a jour pour utiliser le selecteur de fichier jm soler +# mise a jour pour importer les fichiers obj de forester/arboretum jm soler +#========================================================================= + +#========================================================================= +# DESCRIPTION: This script allows for full importing and exporting of +# .obj files. uv texture coordinates and normals can be exported and +# imported. .obj groups and materials can also be converted to and +# from material indexes in Blender. +# +# INSTALLATION: +# You need the FULL python installation to run this script. You can +# down load the latest version of PYTHON from http://www.python.org. +# +# INSTRUCTIONS (You definitely want to read this!): +# Once the script is loaded in Blender, hit alt-p. This will bring up +# the main interface panel. You will have a choise of exporting or +# importing an .obj module. If you are exporting you must have at least +# one mesh selected in Blender, or you will get an error. +# You can change the export filename by entering the path and filename +# in the dialog. If you do not enter a path, the path will default to +# your blender directory. You can change the default path in the script <==== NOTE +# itself by modifying the variable 'Filename' at the top of the script. +# +# EXPORTING: +# There are 4 different export options: Default, Groups, Material Layers, +# and Standard. "Default" will export your mesh using Material Layers if +# the mesh has material indexes. "Groups" and "Material Layers" are +# logically equivalent, but are a different .obj format. If you are +# exporting a Poser morph target, you must select "Material Layers". <===== NOTE +# "Standard" will ignore grouping information, even if your mesh has +# material indexes. +# +# There is also a choice between export using "mesh coordinates" or +# "object coordinates". "Object coordinates" are any location, rotation, +# or scaling values created outside of mesh edit. They belong to the object +# rather than the mesh. If you export using mesh coordinates (the default) +# the center of the object will always be at 0, 0, 0. Export using "mesh +# coordinates is definintely what you want to use if you are working with +# a Poser morph target. If you are exporting a group of objects, you will +# automatically be placed in "object coordinate" mode. +# +# IMPORTING: +# If your OBJ model has uv mapping coordinates, and you want to use them <===== NOTE +# in Blender, you can access them in two ways. The best way is through Blender's +# realtime UV coordinates which you enable simply by selecting the UV option in +# the material edit window. This gives you an exact version of the uv coordinates. +# An older method is to select the "stick" option in the material edit window. I +# really don't know why anyone would want to use this option since it cannot handle +# seams and texture overlap, but I left it in just in case someone could use it for +# something. +# +# If your OBJ contains groups, once it has been imported, it may still appear +# to lack any material indexes. If this happens, it can be remedied <=== NOTE +# by going to the mesh editor window, clicking on the mesh selection button, and +# reselecting the mesh you have just imported. You will now have as many +# materials attached to your object as there are groups. You can then select +# different groups by doing a material select when you are in edit mode. +# +# Finally, you might have problems with certain parts of the object not displaying +# after you go in and out of edit mode the first time. To fix this, simply go into +# edit mode again, and select the "remove doubles" option. +# +# +# HISTORY: +# Nov 13, 2001: Initial Release +# Nov 16, 2001: Version 1.1 - no longer need to pre-define dummy materials +# Dec 13, 2001: Version 1.2 - now imports into realtime UV (the UV button in the material edit window), and +# exports realtime UV. This format is more compatible with the native .OBJ uv format. Should eliminate +# texture misalignments and seams. Simply press the UV button in the material edit window after importing. +# +# GetRaw +#================================ + + +# =============================== +# Setup our runtime constants +# =============================== + +DEBUG=1 #Set this to "1" to see extra messages +MESHVERSION=3 # If the export file doesn't work, +FILEVERSION=3 # try changing these to "2" + +EVENT_PATHCHANGE= 1 +EVENT_IMPORT= 2 +EVENT_IMPORT_CONT= 3 +EVENT_OPTIONS= 4 +EVENT_EXPORT= 7 +EVENT_EXPORT_CHK= 5 +EVENT_EXPORT_CANCEL= 6 +EVENT_QUIT= 8 +EVENT_EXPORT_ERR= 9 +EVENT_TYPE= 10 +EVENT_DONE= 11 +EVENT_IMPORT_SELECT= 12 + +# =============================== +# Import our libraries +# =============================== + +#import string +#import os +#import struct + +try: + import nt + os=nt + os.sep='\\' +except: + import posix + os=posix + os.sep='/' + +def isdir(path): + try: + st = os.stat(path) + return 1 + except: + return 0 + +def split(pathname): + PATHNAME=pathname + PATHNAME=PATHNAME.replace('\\','/') + k0=PATHNAME.split('/') + directory=pathname.replace(k0[len(k0)-1],'') + Name=k0[len(k0)-1] + return directory, Name + +def join(l0,l1): + return l0+os.sep+l1 + +os.isdir=isdir +os.split=split +os.join=join + +import math +import Blender +#import Blender210 +from Blender import * +from Blender import NMesh +from Blender.Draw import * +from Blender.BGL import * +from Blender import Material +from Blender import Window + + + +# =============================== +# Input Variables +# =============================== + +Filename = "G:\\tmp\\test.obj" + +gFilename=Create(Filename) +gAlert = 0 +type = 1 +exporttype = 1 +returncode = 0 +operation = "Export" +center = [0,0,0] +rotation = [0,0,0] +Transform = [] +multiflag = 0 + +#================================ +# def Fileselect function: +#================================ +def ImportFunctionselet(filename): + global gFilename + global ExportOptions + global ExportType + global type + global exporttype + global operation + global gAlert + gFilename.val=filename + ImportFunction(filename, type) + operation = "Import" + +#================================ +def ExitGUI (): +#================================ + Exit() + +#================================ +def EventGUI (event): +#================================ + global gFilename + global ExportOptions + global ExportType + global type + global exporttype + global operation + global gAlert + + if (event==EVENT_IMPORT): + ImportFunction(gFilename.val, type) + operation = "Import" + + if (event==EVENT_IMPORT_SELECT): + Window.FileSelector (ImportFunctionselet, 'IMPORT FILE') + + + if (event==EVENT_IMPORT_CONT): + gAlert = 0 + operation = "Import" + Draw () + + if (event==EVENT_EXPORT): + ExportFunction(gFilename.val, type) + operation = "Export" + + if (event==EVENT_EXPORT_CHK): + ExportFunctionOK(gFilename.val, type) + Draw () + if (event==EVENT_EXPORT_CANCEL): + gAlert = 0 + Draw () + if (event==EVENT_OPTIONS): + type = ExportOptions.val + Draw () + if (event==EVENT_TYPE): + exporttype = ExportType.val + Draw () + if (event==EVENT_EXPORT_ERR): + gAlert = 0 + Draw () + if (event==EVENT_DONE): + gAlert = 0 + Draw () + if (event==EVENT_QUIT): + ExitGUI() + +#================================ +def DrawGUI(): +#================================ + global type + global exporttype + global operation + + glClearColor (0.6,0.6,0.6,0) + glClear (GL_COLOR_BUFFER_BIT) + + global gFilename + global gAlert + global ExportOptions + global ExportType + + if (gAlert==0): + # Add in the copyright notice and title + glRasterPos2d(32, 380) + Text("Wavefront OBJ Importer/Exporter") + glRasterPos2d(32, 350) + Text("Copyright (C) Chris Lynch 2001") + + gFilename=String ("Filename: ",EVENT_PATHCHANGE,32,250,320,32,gFilename.val,255,"Full pathname and filename") + Button ("Export",EVENT_EXPORT,32,200,100,32) + Button ("Import",EVENT_IMPORT,252,200,100,32) + Button ("Select Import",EVENT_IMPORT_SELECT,355,200,100,32) + glRasterPos2d(32, 165) + Text("Select Export Options:") + options = "Export Options %t| Default %x1| Material Layers %x2| Obj Groups %x3| Standard %x4" + ExportOptions = Menu (options,EVENT_OPTIONS,200,150,150,32, type) + Button ("Done",EVENT_QUIT,142,50,100,32) + glRasterPos2d(32, 115) + Text("Export using ") + options = "Export Type %t| Mesh Coordinates %x1| Object Coordinates %x2" + ExportType = Menu (options,EVENT_TYPE,170,100,180,32, exporttype) + Button ("Done",EVENT_QUIT,142,50,100,32) + + elif (gAlert==1): + glRasterPos2i (32,250) + Text (gFilename.val+ " already exists. Save anyway?") + Button ("Save",EVENT_EXPORT_CHK,150,200,50,32) + Button ("Cancel",EVENT_EXPORT_CANCEL,250,200,50,32) + gAlert = 0 + elif (gAlert==2): + glRasterPos2i (32,250) + Text (gFilename.val+ " cannot be found. Check directory and filename.") + Button ("Continue",EVENT_IMPORT_CONT,32,190,70,32) + gAlert = 0 + elif gAlert == 3: + glRasterPos2i (32,250) + Text ("No objects selected to export. You must select one or more objects.") + Button ("Continue",EVENT_EXPORT_ERR,192,200,70,32) + gAlert = 0 + elif gAlert == 5: + glRasterPos2i (32,250) + Text ("Invalid directory path.") + Button ("Continue",EVENT_EXPORT_ERR,192,200,70,32) + gAlert = 0 + else: + glRasterPos2i (32,250) + Text (str(operation)+ " of " +str(gFilename.val)+ " done.") + Button ("Continue",EVENT_DONE,192,200,70,32) + +#================================ +def RegisterGUI (): +#================================ + Register (DrawGUI,None,EventGUI) + +#================================ +# MAIN SCRIPT +#================================ +# Opens a file, writes data in it +# and closes it up. +#================================ +RegisterGUI() + +#================================ +def ImportFunction (importName, type): +#================================ + global gFilename + global gAlert + + try: + FILE=open (importName,"r") + directory, Name = os.split(gFilename.val) + print directory, Name + words = Name.split(".") + Name = words[0] + ObjImport(FILE, Name, gFilename.val) + FILE.close() + gAlert = 4 + Draw () + except IOError: + gAlert=2 + Draw () + +#================================ +def ExportFunction (exportName, type): +#================================ + global gFilename + global gAlert + + try: + FILE=open (exportName,"r") + FILE.close() + gAlert = 1 + Draw () + except IOError: + + directory, Name = os.split(gFilename.val) + + + if os.isdir(directory): + ExportFunctionOK(exportName, type) + Draw () + else: + gAlert = 5 + Draw () + +#================================ +def ExportFunctionOK (exportName, type): +#================================ + global gFilename + global gAlert + global returncode + + FILE=open (exportName,"w") + + directory, Name = os.split(gFilename.val) + + words = Name.split(".") + Name = words[0] + ObjExport(FILE, Name, type) + if returncode > 0: + gAlert = 3 + else: + gAlert = 4 + FILE.flush() + FILE.close() + +#========================= +def ObjImport(file, Name, filename): +#========================= + vcount = 0 + vncount = 0 + vtcount = 0 + fcount = 0 + gcount = 0 + setcount = 0 + groupflag = 0 + objectflag = 0 + mtlflag = 0 + baseindex = 0 + basevtcount = 0 + basevncount = 0 + matindex = 0 + + pointList = [] + uvList = [] + normalList = [] + faceList = [] + materialList = [] + imagelist = [] + + uv = [] + lines = file.readlines() + linenumber = 1 + + for line in lines: + words = line.split() + if words and words[0] == "#": + pass # ignore comments + elif words and words[0] == "v": + vcount = vcount + 1 + + for n_ in [1,2,3]: + if words[n_].find(',')!=-1: + words[n_]=words[n_].replace(',','.') + + x = float(words[1]) + y = float(words[2]) + z = float(words[3]) + + pointList.append([x, y, z]) + + elif words and words[0] == "vt": + vtcount = vtcount + 1 + for n_ in [1,2]: + if words[n_].find(',')!=-1: + words[n_]=words[n_].replace(',','.') + + u = float(words[1]) + v = float(words[2]) + uvList.append([u, v]) + + elif words and words[0] == "vn": + vncount = vncount + 1 + + for n_ in [1,2,3]: + if words[n_].find(',')!=-1: + words[n_]=words[n_].replace(',','.') + + i = float(words[1]) + j = float(words[2]) + k = float(words[3]) + normalList.append([i, j, k]) + + elif words and words[0] == "f": + fcount = fcount + 1 + vi = [] # vertex indices + ti = [] # texture indices + ni = [] # normal indices + words = words[1:] + lcount = len(words) + for index in (xrange(lcount)): + if words[index].find( "/") == -1: + vindex = int(words[index]) + if vindex < 0: vindex = baseindex + vindex + 1 + vi.append(vindex) + else: + vtn = words[index].split( "/") + vindex = int(vtn[0]) + if vindex < 0: vindex = baseindex + vindex + 1 + vi.append(vindex) + + if len(vtn) > 1 and vtn[1]: + tindex = int(vtn[1]) + if tindex < 0: tindex = basevtcount +tindex + 1 + ti.append(tindex) + + if len(vtn) > 2 and vtn[2]: + nindex = int(vtn[2]) + if nindex < 0: nindex = basevncount +nindex + 1 + ni.append(nindex) + faceList.append([vi, ti, ni, matindex]) + + elif words and words[0] == "o": + ObjectName = words[1] + objectflag = 1 + #print "Name is %s" % ObjectName + + elif words and words[0] == "g": + groupflag = 1 + index = len(words) + if objectflag == 0: + objectflag = 1 + if index > 1: + ObjectName = words[1].join("_") + GroupName = words[1].join("_") + else: + ObjectName = "Default" + GroupName = "Default" + #print "Object name is %s" % ObjectName + #print "Group name is %s" % GroupName + else: + if index > 1: + GroupName = join(words[1],"_") + else: + GroupName = "Default" + #print "Group name is %s" % GroupName + + if mtlflag == 0: + matindex = AddMeshMaterial(GroupName,materialList, matindex) + gcount = gcount + 1 + + if fcount > 0: + baseindex = vcount + basevncount = vncount + basevtcount = vtcount + + elif words and words[0] == "mtllib": + # try to export materials + directory, dummy = os.split(filename) + filename = os.join(directory, words[1]) + print "try to import : ",filename + + try: + file = open(filename, "r") + except: + print "no material file %s" % filename + else: + mtlflag = 0 + #file = open(filename, "r") + line = file.readline() + mtlflag = 1 + while line: + words = line.split() + if words and words[0] == "newmtl": + name = words[1] + line = file.readline() # Ns ? + words = line.split() + while words[0] not in ["Ka","Kd","Ks","map_Kd"]: + line = file.readline() + words = line.split() + + if words[0] == "Ka": + Ka = [float(words[1]), + float(words[2]), + float(words[3])] + line = file.readline() # Kd + words = line.split() + + if words[0] == "Kd": + Kd = [float(words[1]), + float(words[2]), + float(words[3])] + line = file.readline() # Ks + words = line.split() + + if words[0] == "Ks": + Ks = [float(words[1]), + float(words[2]), + float(words[3])] + line = file.readline() # Ks + words = line.split() + + if words[0] == "map_Kd": + Kmap= words[1] + img=os.join(directory, Kmap) + im=Blender.Image.Load(img) + line = file.readline() # Ks + words = line.split() + + matindex = AddGlobalMaterial(name, matindex) + matlist = Material.Get() + + if len(matlist) > 0: + if name!='defaultMat': + material = matlist[matindex] + material.R = Kd[0] + material.G = Kd[1] + material.B = Kd[2] + try: + material.specCol[0] = Ks[0] + material.specCol[1] = Ks[1] + material.specCol[2] = Ks[2] + except: + pass + try: + alpha = 1 - ((Ka[0]+Ka[1]+Ka[2])/3) + except: + pass + try: + material.alpha = alpha + except: + pass + + try: + + img=os.join(directory, Kmap) + im=Blender.Image.Load(img) + imagelist.append(im) + + t=Blender.Texture.New(Kmap) + t.setType('Image') + t.setImage(im) + + material.setTexture(0,t) + material.getTextures()[0].texco=16 + except: + pass + + else: + material = matlist[matindex] + + material.R = 0.8 + material.G = 0.8 + material.B = 0.8 + material.specCol[0] = 0.5 + material.specCol[1] = 0.5 + material.specCol[2] = 0.5 + + img=os.join(directory, Kmap) + im=Blender.Image.Load(img) + imagelist.append(im) + + t=Blender.Texture.New(Kmap) + t.setType('Image') + t.setImage(im) + + material.setTexture(0,t) + material.getTextures()[0].texco=16 + + else: + mtlflag = 0 + + line = file.readline() + + + file.close() + + elif words and words[0] == "usemtl": + if mtlflag == 1: + name = words[1] + matindex = AddMeshMaterial(name, materialList, matindex) + elif words: + print "%s: %s" % (linenumber, words) + linenumber = linenumber + 1 + file.close() + + # import in Blender + + print "import into Blender ..." + mesh = NMesh.GetRaw () + + i = 0 + while i < vcount: + x, y, z = pointList[i] + vert=NMesh.Vert(x, y, z) + mesh.verts.append(vert) + i=i+1 + + if vtcount > 0: + #mesh.hasFaceUV() = 1 + print ("Object has uv coordinates") + + if len(materialList) > 0: + for m in materialList: + try: + M=Material.Get(m) + mesh.materials.append(M) + except: + pass + + total = len(faceList) + i = 0 + + for f in faceList: + if i%1000 == 0: + print ("Progress = "+ str(i)+"/"+ str(total)) + + i = i + 1 + vi, ti, ni, matindex = f + face=NMesh.Face() + if len(materialList) > 0: + face.mat = matindex + + limit = len(vi) + setcount = setcount + len(vi) + c = 0 + + while c < limit: + m = vi[c]-1 + if vtcount > 0 and len(ti) > c: + n = ti[c]-1 + if vncount > 0 and len(ni) > c: + p = ni[c]-1 + + if vtcount > 0: + try: + u, v = uvList[n] + except: + pass + + """ + # multiply uv coordinates by 2 and add 1. Apparently blender uses uv range of 1 to 3 (not 0 to 1). + mesh.verts[m].uvco[0] = (u*2)+1 + mesh.verts[m].uvco[1] = (v*2)+1 + """ + + if vncount > 0: + if p > len(normalList): + print("normal len = " +str(len(normalList))+ " vector len = " +str(len(pointList))) + print("p = " +str(p)) + x, y, z = normalList[p] + mesh.verts[m].no[0] = x + mesh.verts[m].no[1] = y + mesh.verts[m].no[2] = z + c = c+1 + + if len(vi) < 5: + for index in vi: + face.v.append (mesh.verts[index-1]) + + if vtcount > 0: + for index in ti: + u, v = uvList[index-1] + face.uv.append((u,v)) + + if len(imagelist)>0: + face.image=imagelist[0] + #print + + if vcount>0: + face.smooth=1 + + mesh.faces.append(face) + + print "all other (general) polygons ..." + for f in faceList: + vi, ti, ni, matindex = f + if len(vi) > 4: + # export the polygon as edges + print ("Odd face, vertices = "+ str(len(vi))) + for i in range(len(vi)-2): + face = NMesh.Face() + if len(materialList) > 0: + face.mat = matindex + face.v.append(mesh.verts[vi[0]-1]) + face.v.append(mesh.verts[vi[i+1]-1]) + face.v.append(mesh.verts[vi[i+2]-1]) + + if vtcount > 0: + if len(ti) > i+2: + u, v = uvList[ti[0]-1] + face.uv.append((u,v)) + u, v = uvList[ti[i+1]-1] + face.uv.append((u,v)) + u, v = uvList[ti[i+2]-1] + face.uv.append((u,v)) + + mesh.faces.append(face) + + NMesh.PutRaw(mesh, Name,1) + + print ("Total number of vertices is "+ str(vcount)) + print ("Total number of faces is "+ str(len(faceList))) + print ("Total number of sets is "+ str(setcount)) + + + print("Finished importing " +str(Name)+ ".obj") + +#========================================= +def AddMeshMaterial(name, materialList, matindex): +#========================================= + + index = 0 + found = 0 + limit = len(materialList) + + while index < limit: + if materialList[index] == name: + matindex = index + found = 1 + index = limit + index = index + 1 + + if found == 0: + materialList.append(name) + matindex = len(materialList)-1 + + return matindex + +#========================================= +def AddGlobalMaterial (name, matindex): +#========================================= + + index = 0 + found = 0 + matindex = 0 + MatList = Material.Get() + limit = len(MatList) + + while index < limit: + if MatList[index].name == name: + matindex = index + found = 1 + index = limit + index = index + 1 + + if found == 0: + material = Material.New(name) + matindex = index + + return matindex + +#================================ +def ObjExport(FILE, Name, type): +#================================ + global returncode + global vertexcount + global uvcount + global Transform + global multiflag + global exporttype + + vertexcount = 0 + uvcount = 0 + returncode = 0 + print("Writing %s..." % Name) + FILE.write("# Wavefront OBJ (1.0) exported by lynx's OBJ import/export script\n\n") + + Objects = Object.GetSelected() + if Objects == []: + print("You have not selected an object!") + returncode = 4 + else: + for object in Objects: + MtlList = [] + if len(Objects) > 1 or exporttype > 1: + Transform = CreateMatrix(object, Transform) + multiflag = 1 + + mesh = NMesh.GetRawFromObject(object.name) + ObjName = mesh.name + has_uvco = mesh.hasVertexUV() + + FILE.write("# Meshname:\t%s\n" % ObjName) + + faces = mesh.faces + materials = mesh.materials + Vertices = mesh.verts + GlobalMaterials = Material.Get() + + if len(materials) >= 1 and len(GlobalMaterials) > 0 and type < 4: + CreateMtlFile(Name, materials, MtlList) + + # Total Vertices and faces; comment if not useful + FILE.write("# Total number of Faces:\t%s\n" % len(faces)) + FILE.write("# Total number of Vertices:\t%s\n" % len(Vertices)) + + FILE.write("\n") + + # print first image map for uvcoords to use + # to be updated when we get access to other textures + if mesh.hasFaceUV(): FILE.write("# UV Texture:\t%s\n\n" % mesh.hasFaceUV()) + + if len(materials) >= 1 and len(GlobalMaterials) > 0 and type < 3: + UseLayers(faces, Vertices, MtlList, has_uvco, FILE, ObjName, Name) + elif len(materials) >= 1 and len(GlobalMaterials) > 0 and type == 3: + UseMtl(faces, Vertices, MtlList, has_uvco, FILE, ObjName, Name) + else: + Standard(faces, Vertices, has_uvco, FILE, ObjName) + +#================================================ +def CreateMtlFile (name, MeshMaterials, MtlList): +#================================================ + global gFilename + + # try to export materials + directory, mtlname = os.split(gFilename.val) + mtlname = name + ".mtl" + filename = os.join(directory, mtlname) + file = open(filename, "w") + + file.write("# Materials for %s.\n" % (name + ".obj")) + file.write("# Created by Blender.\n") + file.write("# These files must be in the same directory for the materials to be read correctly.\n\n") + + MatList = Material.Get() + print str(MeshMaterials) + + MtlNList=[] + for m in MatList: + MtlNList.append(m.name) + + counter = 1 + found = 0 + + for material in MeshMaterials: + for mtl in MtlList: + if material == mtl: + found = 1 + + MtlList.append(material) + + if found == 0: + file.write("newmtl %s \n" % material.name) + index = 0 + print material, MatList + while index < len(MatList): + if material.name == MatList[index].name: + mtl = MatList[index] + index = len(MatList) + found = 1 + index = index + 1 + + if found == 1: + alpha = mtl.getAlpha() + file.write(" Ka %s %s %s \n" % (round(1-alpha,5), round(1-alpha,5), round(1-alpha,5))) + file.write(" Kd %s %s %s \n" % (round(mtl.R,5), round(mtl.G,5), round(mtl.B,5))) + file.write(" Ks %s %s %s \n" % (round(mtl.specCol[0],5), round(mtl.specCol[1],5), round(mtl.specCol[2],5))) + mtextures = mtl.getTextures() # get a list of the MTex objects + try: + for mtex in mtextures: + if mtex.tex.type == Texture.Types.IMAGE and (mtex.texco & Texture.TexCo.UV): + file.write(" map_Kd %s \n" % Blender.sys.basename(mtex[0].tex.image.filename)) + break + except: + if mtextures[0].tex.type == Texture.Types.IMAGE and (mtextures[0].texco & Texture.TexCo.UV): + file.write(" map_Kd %s \n" % Blender.sys.basename(mtextures[0].tex.image.filename)) + + + file.write(" illum 1\n") + + else: + file.write(" Ka %s %s %s \n" % (0, 0, 0)) + file.write(" Kd %s %s %s \n" % (1, 1, 1)) + file.write(" Ks %s %s %s \n" % (1, 1, 1)) + file.write(" illum 1\n") + + found = 0 + + file.flush() + file.close() + +#=========================================================== +def Standard(faces, Vertices, has_uvco, FILE, ObjName): +#=========================================================== + global vertexcount + global uvcount + global multiflag + + uvPtrs = [] + uvList = [] + + FILE.write("o %s\n\n" % (ObjName)) + FILE.write("g %s\n\n" % (ObjName)) + + for v in Vertices: + vert = v.co + if multiflag == 1: + vert = Alter(vert, Transform) + x, y, z = vert + + FILE.write("v %s %s %s\n" % (x, y, z)) + + uv_flag = 0 + for face in faces: + for uv in face.uv: + found = 0 + index = len(uvList) + limit = 0 + if len(uvList)-200 > 0: + limit = len(uvList)-200 + while index > limit and found == 0: + uv_value = uvList[index-1] + if uv[0] == uv_value[0] and uv[1] == uv_value[1]: + uvPtrs.append(index+uvcount) + found = 1 + index = index - 1 + if found == 0: + uvList.append(uv) + index = len(uvList) + uvPtrs.append(index+uvcount) + u, v = uv + FILE.write("vt %s %s\n" % (u, v)) + uv_flag = 1 + + if has_uvco and uv_flag == 0: + for v in Vertices: + u, v, z = v.uvco + u = (u-1)/2 + v = (v-1)/2 + FILE.write("vt %s %s\n" % (u, v)) + + for v in Vertices: + x, y, z = v.no + FILE.write("vn %s %s %s\n" % (x, y, z)) + + p = 0 + uvindex = 0 + total = len(faces) + + for face in faces: + p = p+1 + if (p%1000) == 0: + print ("Progress = "+ str(p)+ " of "+ str(total) +" faces") + + FILE.write("f ") + for index in range(len(face.v)): + v = face.v[index].index + vertexcount + if len(face.uv) > 0: + FILE.write("%s/%s/%s " % (v+1, uvPtrs[uvindex], v+1)) + uvindex = uvindex+1 + elif has_uvco: + FILE.write("%s/%s/%s " % (v+1, v+1, v+1)) + else: + FILE.write("%s//%s " % (v+1, v+1)) + FILE.write("\n") + + vertexcount = vertexcount + len(Vertices) + uvcount = uvcount + len(uvList) + + print("Export of " +str(ObjName)+ ".obj finished.\n") + +#===================================================================== +def UseLayers(faces, Vertices, MtlList, has_uvco, FILE, ObjName, Name): +#===================================================================== + global vertexcount + global uvcount + global multiflag + + uvPtrs = [] + uvList = [] + + FILE.write("mtllib %s\n\n" % (Name + ".mtl")) + FILE.write("g %s\n\n" % (ObjName)) + + for v in Vertices: + vert = v.co + if multiflag == 1: + vert = Alter(vert, Transform) + x, y, z = vert + FILE.write("v %s %s %s\n" % (x, y, z)) + + uv_flag = 0 + for m in range(len(MtlList)): + for face in faces: + if face.mat == m: + for uv in face.uv: + found = 0 + index = len(uvList) + limit = 0 + if len(uvList)-200 > 0: + limit = len(uvList)-200 + while index > limit and found == 0: + uv_value = uvList[index-1] + if uv[0] == uv_value[0] and uv[1] == uv_value[1]: + uvPtrs.append(index+uvcount) + found = 1 + index = index - 1 + if found == 0: + uvList.append(uv) + index = len(uvList) + uvPtrs.append(index+uvcount) + u, v = uv + FILE.write("vt %s %s\n" % (u, v)) + uv_flag = 1 + + if has_uvco and uv_flag == 0: + for v in Vertices: + u, v, z = v.uvco + u = (u-1)/2 + v = (v-1)/2 + FILE.write("vt %s %s\n" % (u, v)) + + for v in Vertices: + x, y, z = v.no + FILE.write("vn %s %s %s\n" % (x, y, z)) + + total = len(faces) + p = 0 + uvindex = 0 + for m in range(len(MtlList)): + FILE.write("usemtl %s\n" % (MtlList[m].name)) + for face in faces: + if face.mat == m: + p = p+1 + if (p%1000) == 0: + print ("Progress = "+ str(p)+ " of "+ str(total) +" faces") + + FILE.write("f ") + for index in range(len(face.v)): + v = face.v[index].index + vertexcount + if len(face.uv) > 0: + FILE.write("%s/%s/%s " % (v+1, uvPtrs[uvindex], v+1)) + uvindex = uvindex+1 + elif has_uvco: + FILE.write("%s/%s/%s " % (v+1, v+1, v+1)) + else: + FILE.write("%s//%s " % (v+1, v+1)) + FILE.write("\n") + + vertexcount = vertexcount + len(Vertices) + print("Export of " +str(ObjName)+ ".obj using material layers finished.\n") + +#================================================================== +def UseMtl(faces, Vertices, MtlList, has_uvco, FILE, ObjName, Name): +#================================================================== + global vertexcount + global multiflag + + FILE.write("mtllib %s\n\n" % (Name + ".mtl")) + FILE.write("o %s\n\n" % (ObjName)) + + index = 0 + VertexList = [] + for vertex in Vertices: + VertexList.append(-1) + index = index + 1 + print("number of vertices is " +str(len(VertexList))) + + Totalindex = 0 + ix = 0 + NewVertexList = [] + NewVertexCo = [] + for m in range(len(MtlList)): + # Group name is the name of the mesh + if MtlList[m]: + FILE.write("g %s\n" % (MtlList[m].name+str(m+1))) + else: + FILE.write("g %s\n" % ("Null"+str(m+1))) + FILE.write("s off\n\n") + + FILE.write("usemtl %s\n\n" % (MtlList[m].name)) + + for face in faces: + if face.mat == m: + for vertex in face.v: + v = vertex.index + if VertexList[v] < 0: + VertexList[v] = Totalindex + NewVertexList.append(v) + Totalindex = Totalindex + 1 + + for v_old in NewVertexList: + vert = Vertices[v_old].co + if multiflag == 1: + vert = Alter(vert, Transform) + x, y, z = vert + FILE.write("v %s %s %s\n" % (x, y, z)) + NewVertexCo.append([x,y,z]) + + if has_uvco: + for v_old in NewVertexList: + u, v, z = Vertices[v_old].uvco + u = (u-1)/2 + v = (v-1)/2 + FILE.write("vt %s %s\n" % (u, v)) + + for v_old in NewVertexList: + x, y, z = Vertices[v_old].no + FILE.write("vn %s %s %s\n" % (x, y, z)) + + for face in faces: + if face.mat == m: + FILE.write("f ") + for index in range(len(face.v)): + v = face.v[index].index + v_new = VertexList[v] + if has_uvco: + FILE.write("%s/%s/%s " % (v_new+1, v_new+1, v_new+1)) + else: + FILE.write("%s//%s " % (v_new+1, v_new+1)) + FILE.write("\n") + + FILE.write("\n") + + NewVertexList = [] + print("Group " +str(m+1)+ " of " +str(len(MtlList))+ " finished.") + + print("Export of " +str(ObjName)+ ".obj using groups finished.\n") + +#======================================== +def CreateMatrix(object, Transform): +#======================================== + Mx = [] + My = [] + Mz = [] + T1 = [] + Transform = [] + + angle = object.RotX + Mx.append([1, 0, 0]) + y = math.cos(angle) + z = -math.sin(angle) + Mx.append([0, y, z]) + y = math.sin(angle) + z = math.cos(angle) + Mx.append([0, y, z]) + + angle = object.RotY + x = math.cos(angle) + z = math.sin(angle) + My.append([x, 0, z]) + My.append([0, 1, 0]) + x = -math.sin(angle) + z = math.cos(angle) + My.append([x, 0, z]) + + angle = object.RotZ + x = math.cos(angle) + y = -math.sin(angle) + Mz.append([x, y, 0]) + x = math.sin(angle) + y = math.cos(angle) + Mz.append([x, y, 0]) + Mz.append([0, 0, 1]) + + m0 = Mx[0] + m1 = Mx[1] + m2 = Mx[2] + for row in My: + x, y, z = row + nx = x*m0[0] + y*m1[0] + z*m2[0] + ny = x*m0[1] + y*m1[1] + z*m2[1] + nz = x*m0[2] + y*m1[2] + z*m2[2] + T1.append([nx, ny, nz]) + + m0 = T1[0] + m1 = T1[1] + m2 = T1[2] + for row in Mz: + x, y, z = row + nx = x*m0[0] + y*m1[0] + z*m2[0] + ny = x*m0[1] + y*m1[1] + z*m2[1] + nz = x*m0[2] + y*m1[2] + z*m2[2] + Transform.append([nx, ny, nz]) + + Transform.append([object.SizeX, object.SizeY, object.SizeZ]) + Transform.append([object.LocX, object.LocY, object.LocZ]) + + return Transform + +#====================================== +def Alter(vect, Transform): +#====================================== + v2 = [] + nv = [] + + x, y, z = vect + sx, sy, sz = Transform[3] + lx, ly, lz = Transform[4] + + v2.append(x*sx) + v2.append(y*sy) + v2.append(z*sz) + + for index in range(len(vect)): + t = Transform[index] + nv.append(v2[0]*t[0] + v2[1]*t[1] +v2[2]*t[2]) + + nv[0] = nv[0]+lx + nv[1] = nv[1]+ly + nv[2] = nv[2]+lz + + return nv diff --git a/blender/wrl2export.py b/blender/wrl2export.py new file mode 100644 index 00000000..3fedfb9f --- /dev/null +++ b/blender/wrl2export.py @@ -0,0 +1,1080 @@ +#!BPY +""" Registration info for Blender menus: +Name: 'VRML 2.0 (.wrl)...' +Blender: 242 +Group: 'Export' +Submenu: 'All Objects...' all +Submenu: 'All Objects compressed...' comp +Submenu: 'Selected Objects...' selected +Submenu: 'Selected Objects compressed...' compselected +Tooltip: 'Export to VRML2 (.wrl) file.' +""" + +#------------------------------------------------------------------------ +# VRML2 exporter for blender 2.42 or above +# +# Source: http://blender.kimballsoftware.com/ +# +# $Id: wrl2export.py,v 1.32 2006/09/29 10:19 kimballr Exp kimballr $ +# +# Authors: Rick Kimball with much inspiration +# from the forum at www.elysiun.com +# and irc://irc.freenode.net/blenderchat +# Ken Miller and Steve Matthews (Added Camera Support) +# +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Copyright (C) 2003,2004: Rick Kimball rick@vrmlworld.net +# +# 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 ***** +# +# To use script: +# 1.) copy to C:\program files\blender foundation\blender\.blender\scripts +# 2.) Select the File/Export/VRML2 menu item in blender. +# +# Notes: +# This exporter only deals with Meshes, Camera, Lights and Viewpoints +# The output filename is same as current blender file with .wrl extension +# Error messages go to the Blender DOS console window +# +# The latest version of this python export script: +# http://blender.kimballsoftware.com/ +# +# If you like this script, try using http://vrmlworld.net/ +# to show off your VRML world. +# +# 2006-09-29 by Rick Kimball +# o Fixed for Blender 2.42 +# o uses new file select dialog. +# o prompts before overwriting output +# o Added support for gzip compressed output +# +# 2004-01-19 by Rick Kimball +# o added sub menus and file selector dialog +# +# 2004-01-17 by Rick Kimball +# o add meta comments so script will appear in export menu list +# +# 2003-11-01 by Rick Kimball +# o fixed issues related to Lamp object and 2.28a API. +# +# 2003-07-19 by Rick Kimball +# o made compatible with new Python API in 2.28 +# +# 2003-01-16 by Ken Miller - with math help from Steve Matthews :) +# o Added support for exporting cameras out of Blender +# o Sets the name of the camera as the object name +# o sets the description of the camera as the object name, +# which should be modified to something meaningful +# o sets the position and orientation +# +# 2003-01-19 Rick Kimball +# o Added Support For PointLight, SpotLight and DirectionalLight using Lamps +# o Creates multi singlesided or doublesided IFS +# o Creates IndexedLineSets if DrawTypes is WIRE instead of Shaded +# +# 2003-02-03 Rick Kimball +# o attempts to catch exceptions for empty objects +# +# 2003-02-04 Rick Kimball +# o fixed file overwrite problem when blender filename is all uppercase +# +# 2003-02-08 Rick Kimball +# o cleanStr() creates valid VRML DEF names even if object.name +# is zero length or uses VRML reserved names or characters +# +#------------------------------------------------------------------------ +# Known Issue: +# o doesn't handle multiple materials (don't use material indices) +# o doesn't handle multiple UV textures on a single mesh. (create a mesh for each texture) +# o material colors need work +# o spotlight softness needs work +# o can't get the texture array associated with material * not the UV ones +# o can't set smoothing, crease angle and mesh smoothing * setting not accesible +# +# Still Todo: +# +# - Support for material indexes +# - Automatically Split IFS when multiple UV textures found * warning only now +# - Automatically Split IFS when combination of single vs double sided +# - Automatically Split IFS when face with only 2 vertices is found should be an ILS +# - Export common coordinate map for split IFS +# - Intelligent color array vs color index +# - Support more blender objects: World +# - Figure out how to output Animation +# - Add GUI to control the following: +# o All/Layer/Object output radio button +# o Color per vertex toggle yes/no +# o Complex/Simple VRML output radio button +# o Compressed/Uncompressed output radio button +# o Decimal precision dropdown 1,2,3,4,5,6 +# o IFS/Elevation Grid output radio button +# o Normals output toggle yes/no +# o Proto output toggle yes/no +# o Verbose console progress + +import Blender +from Blender import Object, Mesh, NMesh, Lamp, Draw, BGL, Image, Text, sys, Mathutils +import math + +#-- module constants +radD=math.pi/180.0 +rad90=90.0*radD # for rotation +rad30=30.0*radD # default crease angle +extension = '' +ARG='' + +# Blender is Z up, VRML is Y up, both are right hand coordinate +# systems, so to go from Blender coords to VRML coords we rotate +# by 90 degrees around the X axis. In matrix notation, we have a +# matrix, and it's inverse, as: +M_blen2vrml = Mathutils.Matrix([1,0,0,0], \ + [0,0,1,0], \ + [0,-1,0,0], \ + [0,0,0,1]) + +M_vrml2blen = Mathutils.Matrix([1,0,0,0], \ + [0,0,-1,0], \ + [0,1,0,0], \ + [0,0,0,1]) + +M_rotX = Mathutils.Matrix([1.0, 0.0, 0.0], \ + [0.0, 0.0, -1.0], \ + [0.0, 1.0, 0.0]) + +#------------------------------------------------------------------------ +#-- utility functions and classes -- +#------------------------------------------------------------------------ +def rad2deg(v): + return round(v*180.0/math.pi,4) + +def deg2rad(v): + return (v*math.pi)/180.0; + +def eulToVecRot(self, RotX, RotY, RotZ): + ti = RotX*0.5 + tj = RotY*0.5 + th = RotZ*0.5 + + ci = math.cos(ti) + cj = math.cos(tj) + ch = math.cos(th) + si = math.sin(ti) + sj = math.sin(tj) + sh = math.sin(th) + cc = ci*ch + cs = ci*sh + sc = si*ch + ss = si*sh + + q0 = cj*cc + sj*ss + q1 = cj*sc - sj*cs + q2 = cj*ss + sj*cc + q3 = cj*cs - sj*sc + + angle = 2 * math.acos(q0) + if (math.fabs(angle) < 0.000001): + axis = [1.0, 0.0, 0.0] + else: + sphi = 1.0/math.sqrt(1.0 - (q0*q0)) + axis = [q1 * sphi, q2 * sphi, q3 * sphi] + + a = Mathutils.Vector(axis) + a.normalize() + return ([a.x, a.y, a.z], angle) + +class DrawTypes: + """Object DrawTypes enum values + BOUNDS - draw only the bounding box of the object + WIRE - draw object as a wire frame + SOLID - draw object with flat shading + SHADED - draw object with OpenGL shading +""" + BOUNDBOX = 1 + WIRE = 2 + SOLID = 3 + SHADED = 4 + TEXTURE = 5 + +#------------------------------------------------------ +# the Blender.Object class seems to be missing this... +#------------------------------------------------------ +if not hasattr(Blender.Object,'DrawTypes'): + Blender.Object.DrawTypes = DrawTypes() + +#------------------------------------------------------------------------ +#-- VRML2Export -- +#------------------------------------------------------------------------ +class VRML2Export: + #------------------------------------------------------------------------ + def __init__(self, filename): + #--- public you can change these --- + self.verbose=1 # level of verbosity in console 0-none, 1-some, 2-most + self.cp=3 # decimals for material color values 0.000 - 1.000 + self.vp=3 # decimals for vertex coordinate values 0.000 - n.000 + self.tp=3 # decimals for texture coordinate values 0.000 - 1.000 + self.ambientIntensity=.2 + self.defCreaseAngle=rad30 + self.smooth=0 + + #--- class private don't touch --- + self.texNames={} # dictionary of textureNames + self.matNames={} # dictionary of materiaNames + self.indentLevel=0 # keeps track of current indenting + self.filename=filename + self.file = open(filename, "w") + self.bNav=0 + self.nNodeID=0 + self.VRMLReserved=[ "Anchor","Appearance","Anchor","AudioClip","Background", + "Billboard", "Box", "Collision", "Color", "ColorInterpolator", + "Cone", "Coordinate", "CoordinateInterpolator", "Cylinder", + "CylinderSensor", "DirectionalLight", "ElevationGrid", + "Extrustion", "Fog", "FontStyle", "Group", "ImageTexture", + "IndexedFaceSet", "IndexedLineSet", "Inline", "LOD", + "Material", "MovieTexture", "NavigationInfo", "Normal", + "NormalInterpolator","OrientationInterpolator", "PixelTexture", + "PlaneSensor", "PointLight", "PointSet", "PositionInterpolator", + "ProxmimitySensor", "ScalarInterpolator", "Script", "Shape", + "Sound", "Sphere", "SphereSensor", "SpotLight", "Switch", + "Text", "TextureCoordinate", "TextureTransform", "TimeSensor", + "TouchSensor", "Transform", "Viewpoint", "VisibilitySensor", + "WorldInfo" + ] + + #------------------------------------------------------------------------ + # writeHeader, export file, cleanup + #------------------------------------------------------------------------ + def writeHeader(self): + self.file.write("#VRML V2.0 utf8\n") + self.file.write("# modeled using blender3d http://blender.org/$\n") + self.file.write("# exported using wrl2export.py version $Revision: 1.32 $\n") + self.file.write("# get latest exporter at http://kimballsoftware.com/blender/\n\n") + + def export(self, scene): + self.scene=scene + print "Info: starting VRML2 export to " + self.filename + "..." + + self.writeHeader() + theObjects = [] + if ARG == 'selected' or ARG == 'compselected': + theObjects = Blender.Object.GetSelected() + else: + theObjects = scene.getChildren() + + for object in theObjects: + try: + objType=object.getType() + + if objType == "Mesh": + self.writeIndexedFaceSet(object, normals = 0) + elif objType == "Camera": + self.writeCameraInfo(object) + elif objType == "Lamp": + # if there is a lamp then we probably want to turn off the headlight + if self.bNav == 0: + self.writeNavigationInfo() + self.bNav=1 + #endif + + lamp=Lamp.Get(object.data.getName()) + try: + lampType=lamp.getType() + + if lampType == Lamp.Types.Lamp: + self.writePointLight(object, lamp) + elif lampType == Lamp.Types.Spot: + self.writeSpotLight(object, lamp) + elif lampType == Lamp.Types.Sun: + self.writeDirectionalLight(object, lamp) + else: + self.writeDirectionalLight(object, lamp) + #endif + except AttributeError: + print "Error: Unable to get type info for %s" % object.name + else: + print "Info: Ignoring [%s], object type [%s] not handle yet" % \ + (object.name,object.getType()) + #endif + except ValueError: + print "Error: object named %s has problem with accessing an attribute" % object.name + #end try + #endfor + self.cleanup() + + def cleanup(self): + self.file.close() + self.texNames={} + self.matNames={} + self.indentLevel=0 + print "Info: finished VRML2 export to %s\n" % self.filename + + #------------------------------------------------------------------------ + # Writes out camera info as a viewpoint + # Handles orientation, position + # Use camera object name to set description + #------------------------------------------------------------------------ + def writeCameraInfo(self, object): + if self.verbose > 0: + print "Info: exporting camera named="+object.name + #endif + + self.writeIndented("DEF %s Viewpoint {\n" % (self.cleanStr(object.name)), 1) + + self.writeIndented("description \"%s\" \n" % (object.name)) + + # get the camera location, subtract 90 degress from X to orient like VRML does + loc = self.rotatePointForVRML(object.loc) + rot = [object.RotX - 1.57, object.RotY, object.RotZ] + nRot = self.rotatePointForVRML(rot) + + # convert to Quaternion and to Angle Axis + Q = self.eulerToQuaternions(nRot[0], nRot[1], nRot[2]) + Q1 = self.multiplyQuaternions(Q[0], Q[1]) + Qf = self.multiplyQuaternions(Q1, Q[2]) + angleAxis = self.quaternionToAngleAxis(Qf) + + # write orientation statement + self.writeIndented("orientation %3.2f %3.2f %3.2f %3.2f\n" % + (angleAxis[0], angleAxis[1], -angleAxis[2], angleAxis[3])) + + # write position statement + self.writeIndented("position %3.2f %3.2f %3.2f\n" % + (loc[0], loc[1], loc[2])) + + self.writeIndented("} # Viewpoint\n", -1) + + self.writeIndented("\n") + #enddef + + #------------------------------------------------------------------------ + def writeIndexedFaceSet(self, object, normals = 0): + if self.verbose > 0: + print "Info: exporting mesh named=["+object.name+"]" + #endif + + imageMap={} # set of used images + sided={} # 'one':cnt , 'two':cnt + vColors={} # 'multi':1 + + mesh=object.getData() + + nIFSCnt=self.countIFSSetsNeeded(mesh, imageMap, sided, vColors) + + objName = self.cleanStr(object.name) + + if nIFSCnt > 1: + self.writeIndented("DEF %s%s Group {\n" % ("G_", objName),1) + self.writeIndented("children [\n",1) + #endif + + if self.verbose > 0: + print "Debug: [%s] has %d UV Textures" % (object.name, nIFSCnt) + #endif + + if sided.has_key('two') and sided['two'] > 0: + bTwoSided=1 + else: + bTwoSided=0 + + self.writeIndented("DEF %s Shape {\n" % objName,1) + + # show script debugging info + if self.verbose > 1: + self.meshToString(mesh) + print "Debug: mesh.faces[" + for face in mesh.faces: + self.faceToString(face) + #endfor + print "Debug: ]" + #endif + + maters=mesh.materials + hasImageTexture=0 + + if len(maters) > 0 or mesh.hasFaceUV(): + self.writeIndented("appearance Appearance {\n", 1) + + # right now this script can only handle a single material per mesh. + if len(maters) >= 1: + mat=Blender.Material.Get(maters[0].name) + self.writeMaterial(mat, self.cleanStr(maters[0].name,'mat_')) + if len(maters) > 1: + print "Warning: mesh named %s has multiple materials" % objName + print "Warning: only one material per object handled" + #endif + else: + self.writeIndented("material NULL\n") + #endif + + #-- textures + if mesh.hasFaceUV(): + for face in mesh.faces: + if (hasImageTexture == 0) and (face.image): + self.writeImageTexture(face.image.name) + hasImageTexture=1 # keep track of face texture + #endif + #endfor + #endif + + self.writeIndented("} # Appearance\n", -1) + #endif + + #------------------------------------------------------------------- + #-- + #-- IndexedFaceSet or IndexedLineSet + # + + # check if object is wireframe only + if object.drawType == Blender.Object.DrawTypes.WIRE: + # user selected WIRE=2 on the Drawtype=Wire on (F9) Edit page + ifStyle="IndexedLineSet" + else: + # user selected BOUNDS=1, SOLID=3, SHARED=4, or TEXTURE=5 + ifStyle="IndexedFaceSet" + #endif + + meshName=self.cleanStr(mesh.name) + + self.writeIndented("geometry DEF %s %s {\n" % (meshName,ifStyle), 1) + if object.drawType != Blender.Object.DrawTypes.WIRE: + if bTwoSided == 1: + self.writeIndented("solid FALSE # two sided\n") + else: + self.writeIndented("solid TRUE # one sided\n") + #endif + #endif + + #--- + #--- output coordinates + self.writeCoordinates(object, mesh, objName) + + if object.drawType != Blender.Object.DrawTypes.WIRE: + #--- + #--- output textureCoordinates if UV texture used + if mesh.hasFaceUV(): + self.writeTextureCoordinates(mesh) + self.writeVertexColors(mesh) # experiment + #if hasImageTexture == 1: + # self.writeTextureCoordinates(mesh) + # if vColors.has_key('multi'): + # self.writeVertexColors(mesh) # experiment + # #endif + #else: + # self.writeFaceColors(mesh) + #endif hasImageTexture + #endif hasFaceUV + + # TBD: figure out how to get this properly + if self.smooth: + creaseAngle=self.defCreaseAngle; + self.writeIndented("creaseAngle %s\n" % creaseAngle) + else: + self.writeIndented("creaseAngle 0.0 # in radians\n") + #endif mesh.smooth + #endif WIRE + + #--- output vertexColors + if mesh.hasVertexColours() and vColors.has_key('multi'): + self.writeVertexColors(mesh) + #endif + + #--- output closing braces + self.writeIndented("} # %s\n" % ifStyle, -1) + self.writeIndented("} # Shape\n", -1) + + if nIFSCnt > 1: + self.writeIndented("] # children\n", -1) + self.writeIndented("} # Group\n", -1) + #endif + + self.writeIndented("\n") + #enddef + + #------------------------------------------------------------------------ + def writeCoordinates(self, object, mesh, objName): + #-- vertices + self.writeIndented("coord Coordinate {\n", 1) + self.writeIndented("point [\n", 1) + meshVertexList = mesh.verts + + # create vertex list and pre rotate -90 degrees X for VRML + mm=object.getMatrix() + for vertex in meshVertexList: + ob_matrix = Mathutils.Matrix(object.getMatrix('worldspace')) + mm = M_blen2vrml * ob_matrix * M_vrml2blen + blenvert = Mathutils.Vector(vertex.co) + v=M_blen2vrml * blenvert * mm + self.writeIndented("%s %s %s,\n" % + (round(v[0],self.vp), + round(v[1],self.vp), + round(v[2],self.vp) )) + #endfor + self.writeIndented("] # point\n", -1) + self.writeIndented("} # Coordinate\n", -1) + + self.writeIndented("coordIndex [\n", 1) + coordIndexList=[] + for face in mesh.faces: + cordStr="" + for i in range(len(face)): + indx=meshVertexList.index(face[i]) + cordStr = cordStr + "%s, " % indx + #endfor + self.writeIndented(cordStr + "-1,\n") + #endfor + self.writeIndented("] # coordIndex\n", -1) + #enddef + + #------------------------------------------------------------------------ + def writeTextureCoordinates(self, mesh): + texCoordList=[] + texIndexList=[] + j=0 + + for face in mesh.faces: + for i in range(len(face)): + texIndexList.append(j) + texCoordList.append(face.uv[i]) + j=j+1 + #endfor + texIndexList.append(-1) + #endfor + + self.writeIndented("texCoord TextureCoordinate {\n", 1) + self.writeIndented("point [\n", 1) + for i in range(len(texCoordList)): + self.writeIndented("%s %s," % + (round(texCoordList[i][0],self.tp), + round(texCoordList[i][1],self.tp))+"\n") + #endfor + self.writeIndented("] # point\n", -1) + self.writeIndented("} # texCoord\n", -1) + + self.writeIndented("texCoordIndex [\n", 1) + texIndxStr="" + for i in range(len(texIndexList)): + texIndxStr = texIndxStr + "%d, " % texIndexList[i] + if texIndexList[i]==-1: + self.writeIndented(texIndxStr + "\n") + texIndxStr="" + #endif + #endfor + self.writeIndented("] # texCoordIndex\n", -1) + #enddef + + #------------------------------------------------------------------------ + def writeFaceColors(self, mesh): + self.writeIndented("colorPerVertex FALSE\n") + self.writeIndented("color Color {\n",1) + self.writeIndented("color [\n",1) + + for face in mesh.faces: + if face.col: + c=face.col[0] + if self.verbose > 1: + print "Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b) + #endif + + aColor = self.rgbToFS(c) + self.writeIndented("%s,\n" % aColor) + #endfor + + self.writeIndented("] # color\n",-1) + self.writeIndented("} # Color\n",-1) + #enddef + + #------------------------------------------------------------------------ + def writeVertexColors(self, mesh): + self.writeIndented("colorPerVertex TRUE\n") + self.writeIndented("color Color {\n",1) + self.writeIndented("color [\n",1) + + for i in range(len(mesh.verts)): + c=self.getVertexColorByIndx(mesh,i) + if self.verbose > 1: + print "Debug: vertex[%d].col r=%d g=%d b=%d" % (i, c.r, c.g, c.b) + #endif + + aColor = self.rgbToFS(c) + self.writeIndented("%s,\n" % aColor) + #endfor + + self.writeIndented("] # color\n",-1) + self.writeIndented("} # Color\n",-1) + #enddef + + #------------------------------------------------------------------------ + def writeMaterial(self, mat, matName): + # look up material name, use it if available + if self.matNames.has_key(matName): + self.writeIndented("material USE %s\n" % matName) + self.matNames[matName]+=1 + return; + #endif + + self.matNames[matName]=1 + + ambient = mat.amb + diffuseR, diffuseG, diffuseB = mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2] + emisR, emisG, emisB = diffuseR*mat.emit, diffuseG*mat.emit, diffuseB*mat.emit + shininess = mat.hard/255.0 + specR = mat.specCol[0] + specG = mat.specCol[1] + specB = mat.specCol[2] + transp = 1-mat.alpha + + self.writeIndented("material DEF %s Material {\n" % matName, 1) + self.writeIndented("diffuseColor %s %s %s" % + (round(diffuseR,self.cp), round(diffuseG,self.cp), round(diffuseB,self.cp)) + + "\n") + self.writeIndented("ambientIntensity %s" % + (round(ambient,self.cp))+ + "\n") + self.writeIndented("specularColor %s %s %s" % + (round(specR,self.cp), round(specG,self.cp), round(specB,self.cp)) + + "\n" ) + self.writeIndented("emissiveColor %s %s %s" % + (round(emisR,self.cp), round(emisG,self.cp), round(emisB,self.cp)) + + "\n" ) + self.writeIndented("shininess %s" % + (round(shininess,self.cp)) + + "\n" ) + self.writeIndented("transparency %s" % + (round(transp,self.cp)) + + "\n") + self.writeIndented("} # Material\n",-1) + #enddef + + #------------------------------------------------------------------------ + def writeImageTexture(self, name): + if self.texNames.has_key(name): + self.writeIndented("texture USE %s\n" % self.cleanStr(name)) + self.texNames[name] += 1 + return + else: + self.writeIndented("texture DEF %s ImageTexture {\n" % self.cleanStr(name), 1) + self.writeIndented("url \"%s\"\n" % name) + self.writeIndented("} # ImageTexture \n",-1) + self.texNames[name] = 1 + #endif + #enddef + + #------------------------------------------------------------------------ + def writeSpotLight(self, object, lamp): + safeName = self.cleanStr(object.name) + + # compute cutoff and beamwidth + intensity=min(lamp.energy/1.5,1.0) # TBD: figure out the right value + + beamWidth=deg2rad(lamp.spotSize)*.5; + cutOffAngle=beamWidth*.99 + + (dx,dy,dz)=self.computeDirection(object) + # note -dx seems to equal om[3][0] + # note -dz seems to equal om[3][1] + # note dy seems to equal om[3][2] + om = object.getMatrix() + + location=self.rotVertex(om, (0,0,0)); + radius = lamp.dist*math.cos(beamWidth) + self.writeIndented("DEF %s SpotLight {\n" % safeName,1) + self.writeIndented("radius %s\n" % radius ) + self.writeIndented("intensity %s\n" % intensity ) + self.writeIndented("beamWidth %s # lamp.spotSize %s\n" % (beamWidth, lamp.spotSize) ) + self.writeIndented("cutOffAngle %s # lamp.spotBlend %s\n" % (cutOffAngle, lamp.spotBlend)) + self.writeIndented("direction %s %s %s # lamp.RotX=%s RotY=%s RotZ=%s\n" % \ + (round(dx,3),round(dy,3),round(dz,3), + round(rad2deg(object.RotX),3), + round(rad2deg(object.RotY),3), + round(rad2deg(object.RotZ),3))) + self.writeIndented("location %s %s %s\n" % (round(location[0],3), + round(location[1],3), + round(location[2],3))) + self.writeIndented("} # SpotLight\n",-1) + + # export a cone that matches the spotlight in verbose mode + if self.verbose > 1: + self.writeIndented("#generated visible spotlight cone\n") + self.writeIndented("Transform { # Spotlight Cone\n",1) + self.writeIndented("translation %s %s %s\n" % (round(location[0],3), + round(location[1],3), + round(location[2],3))) + rot = [object.RotX, object.RotY, object.RotZ] + nRot = self.rotatePointForVRML(rot) + + # convert to Quaternion and to Angle Axis + Q = self.eulerToQuaternions(nRot[0], nRot[1], nRot[2]) + Q1 = self.multiplyQuaternions(Q[0], Q[1]) + Qf = self.multiplyQuaternions(Q1, Q[2]) + angleAxis = self.quaternionToAngleAxis(Qf) + + # write orientation statement + self.writeIndented("rotation %3.2f %3.2f %3.2f %3.2f\n" % + (angleAxis[0], angleAxis[1], -angleAxis[2], angleAxis[3])) + + self.writeIndented("children [\n",1) + + ch=radius + br=ch*math.sin(beamWidth) + self.writeIndented("Transform {\n",1) + self.writeIndented("translation 0 -%s 0\n" % (ch/2)) + self.writeIndented("children ") + self.writeIndented("Collision {\n",1) + self.writeIndented("collide FALSE children Shape {\n",1) + self.writeIndented("geometry Cone { height %s bottomRadius %s }\n" % (ch, br)) + self.writeIndented("appearance Appearance{\n",1) + self.writeIndented("material Material { diffuseColor 1 1 1 transparency .8 }\n") + self.writeIndented("} # Appearance\n",-1) + self.writeIndented("} # Shape\n",-1) + self.writeIndented("} # Collision\n",-1) + self.writeIndented("} # Transform visible cone \n",-1) + self.writeIndented("] # Spot children\n",-1) + self.writeIndented("} # SpotLight Cone Transform\n",-1) + #endif debug cone + self.writeIndented("\n") + #enddef + + #------------------------------------------------------------------------ + def writeDirectionalLight(self, object, lamp): + safeName = self.cleanStr(object.name) + + intensity=min(lamp.energy/1.5, 1.0) # TBD: figure out the right value + (dx,dy,dz)=self.computeDirection(object) + + self.writeIndented("DEF %s DirectionalLight {\n" % safeName,1) + self.writeIndented("ambientIntensity %s\n" % self.ambientIntensity ) + self.writeIndented("intensity %s\n" % intensity ) + self.writeIndented("direction %s %s %s\n" % (round(dx,4),round(dy,4),round(dz,4))) + self.writeIndented("} # DirectionalLight\n",-1) + self.writeIndented("\n") + #enddef + + #------------------------------------------------------------------------ + def writePointLight(self, object, lamp): + safeName = self.cleanStr(object.name) + + om = object.getMatrix() + location=self.rotVertex(om, (0,0,0)); + intensity=min(lamp.energy/1.5,1.0) # TBD: figure out the right value + + radius = lamp.dist + self.writeIndented("DEF %s PointLight {\n" % safeName,1) + self.writeIndented("ambientIntensity %s\n" % self.ambientIntensity ) + self.writeIndented("intensity %s\n" % intensity ) + self.writeIndented("location %s %s %s\n" % (round(location[0],3), + round(location[1],3), + round(location[2],3))) + self.writeIndented("radius %s\n" % radius ) + self.writeIndented("} # PointLight\n",-1) + self.writeIndented("\n") + #enddef + + #------------------------------------------------------------------------ + def writeNavigationInfo(self): + self.writeIndented("NavigationInfo {\n",1) + self.writeIndented("headlight FALSE\n") + self.writeIndented("avatarSize [0.25, 1.75, 0.75]\n") + self.writeIndented("} # NavigationInfo\n",-1) + self.writeIndented("\n") + #enddef + + #------------------------------------------------------------------------ + #--- Utility methods + #------------------------------------------------------------------------ + + def cleanStr(self, name, prefix='rsvd_'): + """cleanStr(name,prefix) - try to create a valid VRML DEF name from object name""" + + newName=name[:] + if len(newName) == 0: + self.nNodeID+=1 + return "%s%d" % (prefix, self.nNodeID) + + if newName in self.VRMLReserved: + newName='%s%s' % (prefix,newName) + #endif + + if newName[0] in ['0','1','2','3','4','5','6','7','8','9']: + newName='%s%s' % ('_',newName) + #endif + + for bad in [' ','"','#',"'",',','.','[','\\',']','{','}']: + newName=newName.replace(bad,'_') + return newName + #enddef + + def countIFSSetsNeeded(self, mesh, imageMap, sided, vColors): + """ + countIFFSetsNeeded() - should look at a blender mesh to determine + how many VRML IndexFaceSets or IndexLineSets are needed. A + new mesh created under the following conditions: + + o - split by UV Textures / one per mesh + o - split by face, one sided and two sided + o - split by smooth and flat faces + o - split when faces only have 2 vertices * needs to be an IndexLineSet + """ + + imageNameMap={} + faceMap={} + nFaceIndx=0 + + for face in mesh.faces: + sidename=''; + if (face.mode & NMesh.FaceModes.TWOSIDE) == NMesh.FaceModes.TWOSIDE: + sidename='two' + else: + sidename='one' + #endif + + if not vColors.has_key('multi'): + for face in mesh.faces: + if face.col: + c=face.col[0] + if c.r != 255 and c.g != 255 and c.b !=255: + vColors['multi']=1 + #endif + #endif + #endfor + #endif + + if sided.has_key(sidename): + sided[sidename]+=1 + else: + sided[sidename]=1 + #endif + + if face.image: + faceName="%s_%s" % (face.image.name, sidename); + + if imageMap.has_key(faceName): + imageMap[faceName].append(face) + else: + imageMap[faceName]=[face.image.name,sidename,face] + #endif + #endif + #endfor + + if self.verbose > 0: + for faceName in imageMap.keys(): + ifs=imageMap[faceName] + print "Debug: faceName=%s image=%s, solid=%s facecnt=%d" % \ + (faceName, ifs[0], ifs[1], len(ifs)-2) + #endif + #endif + + return len(imageMap.keys()) + #enddef + + def faceToString(self,face): + print "Debug: face.flag=0x%x (bitflags)" % face.flag + if face.flag & NMesh.FaceFlags.SELECT == NMesh.FaceFlags.SELECT: + print "Debug: face.flag.SELECT=true" + #endif + + print "Debug: face.mode=0x%x (bitflags)" % face.mode + if (face.mode & NMesh.FaceModes.TWOSIDE) == NMesh.FaceModes.TWOSIDE: + print "Debug: face.mode twosided" + #endif + + print "Debug: face.transp=0x%x (enum)" % face.transp + if face.transp == NMesh.FaceTranspModes.SOLID: + print "Debug: face.transp.SOLID" + # + + if face.image: + print "Debug: face.image=%s" % face.image.name + #endif + print "Debug: face.materialIndex=%d" % face.materialIndex + #enddef + + def getVertexColorByIndx(self, mesh, indx): + for face in mesh.faces: + j=0 + for vertex in face.v: + if vertex.index == indx: + c=face.col[j] + #endif + j=j+1 + #endfor + #endfor + return c + #enddef + + def meshToString(self,mesh): + print "Debug: mesh.hasVertexUV=%d" % mesh.hasVertexUV() + print "Debug: mesh.hasFaceUV=%d" % mesh.hasFaceUV() + print "Debug: mesh.hasVertexColours=%d" % mesh.hasVertexColours() + print "Debug: mesh.verts=%d" % len(mesh.verts) + print "Debug: mesh.faces=%d" % len(mesh.faces) + print "Debug: mesh.materials=%d" % len(mesh.materials) + #enddef + + def rgbToFS(self, c): + s="%s %s %s" % ( + round(c.r/255.0,self.cp), + round(c.g/255.0,self.cp), + round(c.b/255.0,self.cp)) + return s + #enddef + + def computeDirection(self, object): + x,y,z=(0,-1.0,0) # point down + ax,ay,az = (object.RotX,object.RotZ,object.RotY) + + # rot X + x1=x + y1=y*math.cos(ax)-z*math.sin(ax) + z1=y*math.sin(ax)+z*math.cos(ax) + + # rot Y + x2=x1*math.cos(ay)+z1*math.sin(ay) + y2=y1 + z2=z1*math.cos(ay)-x1*math.sin(ay) + + # rot Z + x3=x2*math.cos(az)-y2*math.sin(az) + y3=x2*math.sin(az)+y2*math.cos(az) + z3=z2 + + return [x3,y3,z3] + #enddef + + + # swap Y and Z to handle axis difference between Blender and VRML + #------------------------------------------------------------------------ + def rotatePointForVRML(self, v): + x = v[0] + y = v[2] + z = -v[1] + + vrmlPoint=[x, y, z] + return vrmlPoint + #enddef + + def rotVertex(self, mm, v): + lx,ly,lz=v[0],v[1],v[2] + gx=(mm[0][0]*lx + mm[1][0]*ly + mm[2][0]*lz) + mm[3][0] + gy=((mm[0][2]*lx + mm[1][2]*ly+ mm[2][2]*lz) + mm[3][2]) + gz=-((mm[0][1]*lx + mm[1][1]*ly + mm[2][1]*lz) + mm[3][1]) + rotatedv=[gx,gy,gz] + return rotatedv + #enddef + + def writeIndented(self, s, inc=0): + if inc < 1: + self.indentLevel = self.indentLevel + inc + #endif + + spaces="" + for x in xrange(self.indentLevel): + spaces = spaces + " " + #endfor + self.file.write(spaces + s) + + if inc > 0: + self.indentLevel = self.indentLevel + inc + #endif + #enddef + + # Converts a Euler to three new Quaternions + # Angles of Euler are passed in as radians + #------------------------------------------------------------------------ + def eulerToQuaternions(self, x, y, z): + Qx = [math.cos(x/2), math.sin(x/2), 0, 0] + Qy = [math.cos(y/2), 0, math.sin(y/2), 0] + Qz = [math.cos(z/2), 0, 0, math.sin(z/2)] + + quaternionVec=[Qx,Qy,Qz] + return quaternionVec + #enddef + + # Multiply two Quaternions together to get a new Quaternion + #------------------------------------------------------------------------ + def multiplyQuaternions(self, Q1, Q2): + result = [((Q1[0] * Q2[0]) - (Q1[1] * Q2[1]) - (Q1[2] * Q2[2]) - (Q1[3] * Q2[3])), + ((Q1[0] * Q2[1]) + (Q1[1] * Q2[0]) + (Q1[2] * Q2[3]) - (Q1[3] * Q2[2])), + ((Q1[0] * Q2[2]) + (Q1[2] * Q2[0]) + (Q1[3] * Q2[1]) - (Q1[1] * Q2[3])), + ((Q1[0] * Q2[3]) + (Q1[3] * Q2[0]) + (Q1[1] * Q2[2]) - (Q1[2] * Q2[1]))] + + return result + #enddef + + # Convert a Quaternion to an Angle Axis (ax, ay, az, angle) + # angle is in radians + #------------------------------------------------------------------------ + def quaternionToAngleAxis(self, Qf): + scale = math.pow(Qf[1],2) + math.pow(Qf[2],2) + math.pow(Qf[3],2) + ax = Qf[1] + ay = Qf[2] + az = Qf[3] + + if scale > .0001: + ax/=scale + ay/=scale + az/=scale + #endif + + angle = 2 * math.acos(Qf[0]) + + result = [ax, ay, az, angle] + return result + #endef + +def file_callback(filename): + if sys.exists(filename): + result = Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0") + if result != 1: + return + + if not filename.endswith(extension): + filename += extension + + wrlexport=VRML2Export(filename) + scene = Blender.Scene.getCurrent() + wrlexport.export(scene) +#enddef + +#------------------------------------------------------------------------ +# main routine +#------------------------------------------------------------------------ +try: + ARG = __script__['arg'] # user selected argument +except: + print "older version" + +if Blender.Get('version') < 242: + print "Warning: VRML2 export failed, wrong blender version!" + print " You aren't running blender version 2.42a or greater" + print " download a newer version from http://blender3d.com/" +else: + if ARG == 'comp' or ARG == 'compselected': + extension=".wrz" + from gzip import * + else: + extension=".wrl" + #endif + Blender.Window.FileSelector(file_callback,"Save VRML 2.0", \ + sys.makename(ext=extension)) + m = Mathutils.Matrix() + print m + e = m.toEuler() + e.x = e.x-90.0 + m = e.toMatrix() + print e + print m +#endif diff --git a/external/ejson b/external/ejson index 217c1947..2627da57 160000 --- a/external/ejson +++ b/external/ejson @@ -1 +1 @@ -Subproject commit 217c1947651b35724df50c2319e1f16296649169 +Subproject commit 2627da57d8555bd69facc1cff923ab0b8d498893 diff --git a/sources/lutin_ewol.py b/sources/lutin_ewol.py index 3c2c2326..ea1bab04 100755 --- a/sources/lutin_ewol.py +++ b/sources/lutin_ewol.py @@ -138,7 +138,7 @@ def Create(target): #myModule.SetConfig(['Config.in','ConfigLinux.in']) # name of the dependency - myModule.AddModuleDepend(['etk', 'freetype', 'exml', 'egami', 'date']) + myModule.AddModuleDepend(['etk', 'freetype', 'exml', 'ejson', 'egami', 'date']) #ifeq ("$(CONFIG_BUILD_BULLET)","y") #myModule.AddModuleDepend('bullet')