home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Multimedia / k3d-setup-0.7.11.0.exe / lib / site-packages / cgkit / objimport.py < prev    next >
Encoding:
Python Source  |  2007-01-11  |  17.6 KB  |  562 lines

  1. # ***** BEGIN LICENSE BLOCK *****
  2. # Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. #
  4. # The contents of this file are subject to the Mozilla Public License Version
  5. # 1.1 (the "License"); you may not use this file except in compliance with
  6. # the License. You may obtain a copy of the License at
  7. # http://www.mozilla.org/MPL/
  8. #
  9. # Software distributed under the License is distributed on an "AS IS" basis,
  10. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. # for the specific language governing rights and limitations under the
  12. # License.
  13. #
  14. # The Original Code is the Python Computer Graphics Kit.
  15. #
  16. # The Initial Developer of the Original Code is Matthias Baas.
  17. # Portions created by the Initial Developer are Copyright (C) 2004
  18. # the Initial Developer. All Rights Reserved.
  19. #
  20. # Contributor(s):
  21. #
  22. # Alternatively, the contents of this file may be used under the terms of
  23. # either the GNU General Public License Version 2 or later (the "GPL"), or
  24. # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  25. # in which case the provisions of the GPL or the LGPL are applicable instead
  26. # of those above. If you wish to allow use of your version of this file only
  27. # under the terms of either the GPL or the LGPL, and not to allow others to
  28. # use your version of this file under the terms of the MPL, indicate your
  29. # decision by deleting the provisions above and replace them with the notice
  30. # and other provisions required by the GPL or the LGPL. If you do not delete
  31. # the provisions above, a recipient may use your version of this file under
  32. # the terms of any one of the MPL, the GPL or the LGPL.
  33. #
  34. # ***** END LICENSE BLOCK *****
  35. # $Id: objimport.py,v 1.11 2006/03/20 19:33:24 mbaas Exp $
  36.  
  37. import os.path, sys
  38. from cgtypes import *
  39. from worldobject import WorldObject
  40. from trimesh import TriMesh
  41. from trimeshgeom import TriMeshGeom
  42. from polyhedron import Polyhedron
  43. from polyhedrongeom import PolyhedronGeom
  44. import pluginmanager
  45. from cgkit.all import UNIFORM, VARYING, FACEVARYING, NORMAL, FLOAT, INT, OBJMaterial, OBJTextureMap
  46. import cmds
  47. import objmtl
  48.  
  49.  
  50. class _MTLReader(objmtl.MTLReader):
  51.     """Read MTL files and create a list of materials.
  52.  
  53.     Reads an MTL file and stores the materials as OBJMaterial objects
  54.     in the list self.material.
  55.     """
  56.     
  57.     def __init__(self):
  58.         objmtl.MTLReader.__init__(self)
  59.         self.materials = []
  60.         self.currentmat = None
  61.  
  62.     def begin(self):
  63.         self.materials = []
  64.         self.currentmat = None
  65.  
  66.     def end(self):
  67.         pass
  68.  
  69.     def newmtl(self, name):
  70.         mat = OBJMaterial(name=name)
  71.         self.materials.append(mat)
  72.         self.currentmat = mat
  73.  
  74.     def illum(self, model):
  75.         if self.currentmat!=None:
  76.             self.currentmat.illum = model
  77.  
  78.     def Ka(self, c):
  79.         if self.currentmat!=None:
  80.             self.currentmat.Ka = c
  81.  
  82.     def Kd(self, c):
  83.         if self.currentmat!=None:
  84.             self.currentmat.Kd = c
  85.  
  86.     def Ks(self, c):
  87.         if self.currentmat!=None:
  88.             self.currentmat.Ks = c
  89.  
  90.     def Ks(self, c):
  91.         if self.currentmat!=None:
  92.             self.currentmat.Ks = c
  93.  
  94.     def Ke(self, c):
  95.         if self.currentmat!=None:
  96.             self.currentmat.Ke = c
  97.  
  98.     def Ns(self, shininess):
  99.         if self.currentmat!=None:
  100.             self.currentmat.Ns = shininess
  101.  
  102.     def Ni(self, refl):
  103.         if self.currentmat!=None:
  104.             self.currentmat.Ni = refl
  105.  
  106.     def d(self, alpha):
  107.         if self.currentmat!=None:
  108.             self.currentmat.d = alpha
  109.  
  110.     def Tr(self, alpha):
  111.         if self.currentmat!=None:
  112.             self.currentmat.Tr = alpha
  113.  
  114.     def Tf(self, c):
  115.         if self.currentmat!=None:
  116.             self.currentmat.Tf = c
  117.  
  118.     def sharpness(self, v):
  119.         if self.currentmat!=None:
  120.             self.currentmat.sharpness = v
  121.  
  122.     def map_Ka(self, name, options):
  123.         if self.currentmat!=None:
  124.             self.currentmat.map_Ka = self.createMap(name, options)
  125.  
  126.     def map_Kd(self, name, options):
  127.         if self.currentmat!=None:
  128.             self.currentmat.map_Kd = self.createMap(name, options)
  129.  
  130.     def map_Ks(self, name, options):
  131.         if self.currentmat!=None:
  132.             self.currentmat.map_Ks = self.createMap(name, options)
  133.  
  134.     def map_Ke(self, name, options):
  135.         if self.currentmat!=None:
  136.             self.currentmat.map_Ke = self.createMap(name, options)
  137.  
  138.     def map_Ns(self, name, options):
  139.         if self.currentmat!=None:
  140.             self.currentmat.map_Ns = self.createMap(name, options)
  141.  
  142.     def map_d(self, name, options):
  143.         if self.currentmat!=None:
  144.             self.currentmat.map_d = self.createMap(name, options)
  145.  
  146.     def map_Bump(self, name, options):
  147.         if self.currentmat!=None:
  148.             map = self.createMap(name, options)
  149.             map.bumpsize = options.get("-bm", 1.0)            
  150.             self.currentmat.map_Bump = map
  151.  
  152.     def refl(self, name, options):
  153.         if self.currentmat!=None:
  154.             map = self.createMap(name, options)
  155.             map.refltype = options.get("-type", "sphere")
  156.             self.currentmat.refl.append(map)
  157.  
  158.  
  159.     def createMap(self, name, options):
  160.         map = OBJTextureMap(filename = name,
  161.                             offset = options.get("-o", (0,0,0)),
  162.                             scale = options.get("-s", (1,1,1)),
  163.                             turb = options.get("-t", (0,0,0)),
  164.                             mm = options.get("-mm", (0,1.0)),
  165.                             clamp = options.get("-clamp", False),
  166.                             blendu = options.get("-blendu", True),
  167.                             blendv = options.get("-blendv", True))
  168.         return map
  169.         
  170.  
  171. class _OBJReader(objmtl.OBJReader):
  172.     """Read an OBJ file.
  173.     """
  174.     
  175.     def __init__(self, root=None):
  176.         objmtl.OBJReader.__init__(self)
  177.  
  178.         # Dummy vertex at position 0 because the indices in an OBJ file
  179.         # start with 1
  180.         self.verts = [vec3(0)]
  181.         self.normals = [vec3(0)]
  182.         self.tverts = [vec3(0)]
  183.  
  184.         # Local object hierarchy. Each "node" is a 2-tuple (WorldObject,
  185.         # Childs) where Childs is a dictionary that contains the children
  186.         # "nodes". The value is the object name.
  187.         self.hierarchy = (root, {})
  188.  
  189.         # Store the group names ('g')
  190.         self.groupnames = []
  191.  
  192.         # A dictionary with OBJMaterial objects read from the MTL files
  193.         # Key is the material name
  194.         self.materials = {}
  195.         # Material stack. Contains 2-tuples (offset, OBJMaterial) where
  196.         # offset is the face index of the first face (since the last 'g'
  197.         # command that uses this material)
  198.         # The stack is cleared (the last element remains) whenever the 'g'
  199.         # command occurs.
  200.         self.materialstack = [(0, OBJMaterial())]
  201.  
  202.         # The collected faces
  203.         self.faces = []
  204.         # Flag that indicates if self.faces only contains triangles
  205.         self.trimesh_flag = True
  206.  
  207.         # Was it already reported that points aren't supported?
  208.         self.point_msg_flag = False
  209.         # Was it already reported that lines aren't supported?
  210.         self.line_msg_flag = False
  211.  
  212.  
  213.     def end(self):
  214.         # Trigger the creation of the last object
  215.         self.g("default")
  216.  
  217.     # mtllib
  218.     def mtllib(self, *files):
  219.         mtlreader = _MTLReader()
  220.         # Iterate over all MTL file names
  221.         for fname in files:
  222.             # Read the MTL file...
  223.             try:
  224.                 f = file(fname)
  225.             except IOError, e:
  226.                 print e
  227.                 continue
  228.             mtlreader.read(f)
  229.             f.close()
  230.             # ...and keep the materials that don't exist yet
  231.             for mat in mtlreader.materials:
  232.                 if mat.name not in self.materials:
  233.                     self.materials[mat.name] = mat
  234.  
  235.     # usemtl
  236.     def usemtl(self, name):
  237.         if name not in self.materials:
  238.             print >>sys.stderr, 'WARNING: No definition found for material "%s"'%name
  239.             # Store None so that the warning message will only get printed
  240.             # once for each missing material name
  241.             self.materials[name] = None
  242.  
  243.         # Put the material on the stack (replace the top material if it has
  244.         # the same offset (i.e. it is unused))
  245.         offset = len(self.faces)
  246.         mat = self.materials[name]
  247.         if self.materialstack[-1][0]==offset:
  248.             self.materialstack.pop()
  249.         self.materialstack.append((len(self.faces), self.materials[name]))
  250.  
  251.     # v
  252.     def v(self, vert):
  253.         """Vertex."""
  254.         v = vec3(vert.x, vert.y, vert.z)
  255.         try:
  256.             v /= vert.w
  257.         except:
  258.             pass
  259.         self.verts.append(v)
  260.  
  261.     # vn
  262.     def vn(self, n):
  263.         """Normal."""
  264.         self.normals.append(n)
  265.  
  266.     # vt
  267.     def vt(self, tv):
  268.         """Texture vertex."""
  269.         self.tverts.append(tv)
  270.  
  271.     # p
  272.     def p(self, *verts):
  273.         """Points."""
  274.         if self.point_msg_flag:
  275.             return
  276.         print "OBJ import: Points are not supported"
  277.         self.point_msg_flag = True
  278.  
  279.     # l
  280.     def l(self, *verts):
  281.         """Line."""
  282.         if self.line_msg_flag:
  283.             return
  284.         print "OBJ import: Lines are not supported"
  285.         self.line_msg_flag = True
  286.  
  287.     # f
  288.     def f(self, *verts):
  289.         """Face."""
  290. #        ve = []
  291. #        for v,tv,n in verts:
  292. #            ve.append((v,None,None))
  293.  
  294.         self.faces.append(verts)
  295. #        self.faces.append(ve)
  296.         if len(verts)!=3:
  297.             self.trimesh_flag = False
  298.  
  299.     # g
  300.     def g(self, *groups):
  301.         """Grouping info.
  302.         """
  303.         if len(self.faces)!=0:
  304.             parent, names, node = self.findParent(self.groupnames)
  305.             name = "_".join(names)
  306.             if name=="":
  307.                 name = "Mesh"
  308.             if self.trimesh_flag:
  309.                 obj = self.createTriMesh(parent=parent, name=name)
  310.             else:
  311.                 obj = self.createPolyhedron(parent=parent, name=name)
  312.             self.updateHierarchy(node, obj)
  313.         self.faces = []
  314.         self.trimesh_flag = True
  315.         self.groupnames = groups
  316.         # Clear the stack (the last material remains)
  317.         self.materialstack = [(0, self.materialstack[-1][1])]
  318.  
  319.     # createTriMesh
  320.     def createTriMesh(self, parent=None, name=None):
  321.         """Create a triangle mesh from the current set of faces.
  322.  
  323.         This method may only be called if the faces really have only
  324.         3 vertices.
  325.  
  326.         Returns the TriMesh object.
  327.         """
  328.  
  329.         # Build lookup tables (so that only the verts that are really
  330.         # required are stored in the TriMesh)
  331.         #
  332.         # Key: Original vertex index - Value: New vertex index
  333.         vert_lut = {}
  334.         has_normals = True
  335.         has_tverts = True
  336.         iv = 0
  337.         for f in self.faces:
  338.             for v,tv,n in f:
  339.                 if v not in vert_lut:
  340.                     vert_lut[v] = iv
  341.                     iv+=1
  342.                 if tv==None:
  343.                     has_tverts = False
  344.                 if n==None:
  345.                     has_normals = False
  346.  
  347.         numfaces = len(self.faces)
  348.         numverts = len(vert_lut)
  349.  
  350.         tm = TriMeshGeom()
  351.         tm.verts.resize(numverts)
  352.         tm.faces.resize(numfaces)
  353.  
  354.         # Set vertices
  355.         for v in vert_lut:
  356.             newidx = vert_lut[v]
  357.             tm.verts[newidx] = self.verts[v]
  358.  
  359.         # Set faces
  360.         idx = 0
  361.         for i,f in enumerate(self.faces):
  362.             fi = []
  363.             for v,tv,n in f:
  364.                 fi.append(vert_lut[v])
  365.             tm.faces[i] = fi
  366.  
  367.         # Create variable N for storing the normals
  368.         if has_normals:
  369.             tm.newVariable("N", FACEVARYING, NORMAL)
  370.             N = tm.slot("N")
  371.             idx = 0
  372.             for f in self.faces:
  373.                 for v,tv,n in f:
  374.                     N[idx] = self.normals[n]
  375.                     idx += 1
  376.  
  377.         # Set texture vertices
  378.         if has_tverts:
  379.             tm.newVariable("st", FACEVARYING, FLOAT, 2)
  380.             st = tm.slot("st")
  381.             idx = 0
  382.             for f in self.faces:
  383.                 for v,tv,n in f:
  384.                     st[idx] = self.tverts[tv]
  385.                     idx += 1
  386.  
  387.         obj = TriMesh(name=name, parent=parent)
  388.         obj.geom = tm
  389.         # Set the materials
  390.         self.initMaterial(obj)
  391.         return obj
  392.  
  393.     # createPolyhedron
  394.     def createPolyhedron(self, parent=None, name=None):
  395.         """Create a polyhedron from the current set of faces.
  396.  
  397.         Returns the Polyhedron object.
  398.         """
  399.  
  400.         # Build lookup tables (so that only the verts that are really
  401.         # required are stored in the Polyhedron)
  402.         #
  403.         # Key: Original vertex index - Value: New vertex index
  404.         vert_lut = {}
  405.         has_normals = True
  406.         has_tverts = True
  407.         iv = 0
  408.         for f in self.faces:
  409.             for v,tv,n in f:
  410.                 if v not in vert_lut:
  411.                     vert_lut[v] = iv
  412.                     iv+=1
  413.                 if tv==None:
  414.                     has_tverts = False
  415.                 if n==None:
  416.                     has_normals = False
  417.  
  418.         numpolys = len(self.faces)
  419.         numverts = len(vert_lut)
  420.  
  421.         pg = PolyhedronGeom()
  422.         pg.verts.resize(numverts)
  423.         pg.setNumPolys(numpolys)
  424.  
  425.         # Set vertices
  426.         for v in vert_lut:
  427.             newidx = vert_lut[v]
  428.             pg.verts[newidx] = self.verts[v]
  429.  
  430.         # Set polys (this has to be done *before* any FACEVARYING variable
  431.         # is created, otherwise the size of the variable wouldn't be known)
  432.         idx = 0
  433.         for i,f in enumerate(self.faces):
  434.             loop = []
  435.             for v,tv,n in f:
  436.                 loop.append(vert_lut[v])
  437.             pg.setPoly(i,[loop])
  438.  
  439.         # Create variable N for storing the normals
  440.         if has_normals:
  441.             pg.newVariable("N", FACEVARYING, NORMAL)
  442.             N = pg.slot("N")
  443.             idx = 0
  444.             for f in self.faces:
  445.                 for v,tv,n in f:
  446.                     N[idx] = self.normals[n]
  447.                     idx += 1
  448.  
  449.         # Set texture vertices
  450.         if has_tverts:
  451.             pg.newVariable("st", FACEVARYING, FLOAT, 2)
  452.             st = pg.slot("st")
  453.             idx = 0
  454.             for f in self.faces:
  455.                 for v,tv,n in f:
  456.                     st[idx] = self.tverts[tv]
  457.                     idx += 1
  458.  
  459.         obj = Polyhedron(name=name, parent=parent)
  460.         obj.geom = pg
  461.         # Set the materials
  462.         self.initMaterial(obj)
  463.         return obj
  464.  
  465.  
  466.     # initMaterial
  467.     def initMaterial(self, obj):
  468.         """Set the material(s) and the matid primitive variable.
  469.         """
  470.         # Set the materials
  471.         nummats = len(self.materialstack)
  472.         obj.setNumMaterials(nummats)
  473.         for i, (offset, mat) in enumerate(self.materialstack):
  474.             obj.setMaterial(mat, i)
  475.  
  476.         # Create a variable "matid" if there's more than 1 material...
  477.         if nummats>1:
  478.             obj.geom.newVariable("matid", UNIFORM, INT)
  479.             matid = obj.geom.slot("matid")
  480.             for id in range(len(self.materialstack)):
  481.                 begin = self.materialstack[id][0]
  482.                 if id+1<len(self.materialstack):
  483.                     end = self.materialstack[id+1][0]
  484.                 else:
  485.                     end = matid.size()
  486.                 for j in range(begin, end):
  487.                     matid[j] = id
  488.  
  489.  
  490.     # findParent
  491.     def findParent(self, groupnames):
  492.         """Find out who is the parent and what is the name of an object.
  493.  
  494.         groupnames is the value of the 'g' tag. The method assumes that
  495.         one of the elements is the name of the object and the other are
  496.         parents. It's just that it is not known what is what.
  497.         The method tests each name if it matches a previously created
  498.         object at the current level and tries to traverse to the point
  499.         where the object has to be inserted. If everything went fine,
  500.         there should be one name left in the end and that's the name
  501.         of the object.
  502.         The methos returns a 3-tuple (parent, names, Hierarchy_node)
  503.         where parent is the parent WorldObject (or None), names is a
  504.         list of unprocessed names (ideally this should contain 1 element
  505.         which is the object's name) and Hierarchy_node is an internal
  506.         data item that is later used in the updateHierarchy() method).
  507.         """
  508.         names = list(groupnames)
  509.         node = self.hierarchy
  510.  
  511.         while 1:
  512.             for name in names:
  513.                 # Is 'name' the name of a previously created object at the
  514.                 # current hierarchy level?
  515.                 if name in node[1]:
  516.                     node = node[1][name]
  517.                     names.remove(name)
  518.                     # Restart search at the next hierarchy level
  519.                     break
  520.             else:
  521.                 # None of the names was found at the current hierarchy level
  522.                 return node[0], names, node
  523.  
  524.     def updateHierarchy(self, node, obj):
  525.         """Update the local hierarchy after a WorldObject was created.
  526.  
  527.         node is the 3rd element from the tuple returned by findParent().
  528.         obj is the created WorldObject.
  529.         """
  530.         node[1][obj.name] = (obj, {})
  531.  
  532. # OBJImporter
  533. class OBJImporter:
  534.  
  535.     _protocols = ["Import"]
  536.  
  537.     # extension
  538.     def extension():
  539.         """Return the file extensions for this format."""
  540.         return ["obj"]
  541.     extension = staticmethod(extension)
  542.  
  543.     # description
  544.     def description(self):
  545.         """Return a short description for the file dialog."""
  546.         return "Wavefront object file"
  547.     description = staticmethod(description)
  548.  
  549.     # importFile
  550.     def importFile(self, filename, parent=None):
  551.         """Import an OBJ file."""
  552.  
  553.         f = file(filename)
  554.  
  555.         reader = _OBJReader(root=parent)
  556.         reader.read(f)
  557.  
  558. ######################################################################
  559.  
  560. # Register the ObjImporter class as a plugin class
  561. pluginmanager.register(OBJImporter)
  562.