Advertisement
Guest User

Untitled

a guest
Nov 26th, 2016
27
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.44 KB | None | 0 0
  1. #!/usr/bin/python
  2. """
  3. This Version: $Id: obj2egg.py,v 1.7 2008/05/26 17:42:53 andyp Exp $
  4. Info: info >at< pfastergames.com
  5.  
  6. Extended from: http://panda3d.org/phpbb2/viewtopic.php?t=3378
  7. .___..__ .___.___.___.__..__ . .
  8. | [__)[__ [__ [__ | |[__)|\/|
  9. | | \[___[___| |__|| \| |
  10. obj2egg.py [n##][b][t][s] filename1.obj ...
  11. -n regenerate normals with # degree smoothing
  12. exaple -n30 (normals at less 30 degrees will be smoothed)
  13. -b make binarmals
  14. -t make tangents
  15. -s show in pview
  16.  
  17. licensed under WTFPL (http://sam.zoy.org/wtfpl/)
  18. """
  19.  
  20. from pandac.PandaModules import *
  21. import math
  22. import string
  23. import getopt
  24. import sys, os
  25.  
  26.  
  27. def floats(float_list):
  28. """coerce a list of strings that represent floats into a list of floats"""
  29. return [ float(number) for number in float_list ]
  30.  
  31. def ints(int_list):
  32. """coerce a list of strings that represent integers into a list of integers"""
  33. return [ int(number) for number in int_list ]
  34.  
  35.  
  36. class ObjMaterial:
  37. """a wavefront material"""
  38. def __init__(self):
  39. self.filename = None
  40. self.name = "default"
  41. self.eggdiffusetexture = None
  42. self.eggmaterial = None
  43. self.attrib = {}
  44. self.attrib["Ns"] = 100.0
  45. self.attrib["d"] = 1.0
  46. self.attrib["illum"] = 2
  47. # "magenta"
  48. self.attrib["Kd"] = [1.0, 0.0, 1.0]
  49. self.attrib["Ka"] = [0.0, 0.0, 0.0]
  50. self.attrib["Ks"] = [0.0, 0.0, 0.0]
  51. self.attrib["Ke"] = [0.0, 0.0, 0.0]
  52.  
  53. def put(self, key, value):
  54. self.attrib[key] = value
  55. return self
  56.  
  57. def get(self, key):
  58. if self.attrib.has_key(key):
  59. return self.attrib[key]
  60. return None
  61.  
  62. def has_key(self, key):
  63. return self.attrib.has_key(key)
  64.  
  65. def isTextured(self):
  66. # for k in ("map_Kd", "map_Bump", "map_Ks"): <-- NOT YET
  67. if self.attrib.has_key("map_Kd"):
  68. return True;
  69. return False;
  70.  
  71. def getEggTexture(self):
  72. if self.eggdiffusetexture:
  73. return self.eggdiffusetexture
  74. if not self.isTextured():
  75. return none
  76. m = EggTexture(self.name + "_diffuse", self.get("map_Kd"))
  77. m.setFormat(EggTexture.FRgb)
  78. m.setMagfilter(EggTexture.FTLinearMipmapLinear)
  79. m.setMinfilter(EggTexture.FTLinearMipmapLinear)
  80. m.setWrapU(EggTexture.WMRepeat)
  81. m.setWrapV(EggTexture.WMRepeat)
  82. self.eggdiffusetexture = m
  83. return self.eggdiffusetexture
  84.  
  85. def getEggMaterial(self):
  86. if self.eggmaterial:
  87. return self.eggmaterial
  88. m = EggMaterial(self.name + "_mat")
  89. # XXX TODO: add support for specular, and obey illum setting
  90. # XXX as best as we can
  91. rgb = self.get("Kd")
  92. if rgb is not None:
  93. m.setDiff(Vec4(rgb[0], rgb[1], rgb[2], 1.0))
  94. rgb = self.get("Ka")
  95. if rgb is not None:
  96. m.setAmb(Vec4(rgb[0], rgb[1], rgb[2], 1.0))
  97. rgb = self.get("Ks")
  98. if rgb is not None:
  99. m.setSpec(Vec4(rgb[0], rgb[1], rgb[2], 1.0))
  100. ns = self.get("Ns")
  101. if ns is not None:
  102. m.setShininess(ns)
  103. self.eggmaterial = m
  104. return self.eggmaterial
  105.  
  106. class MtlFile:
  107. """an object representing all Wavefront materials in a .mtl file"""
  108. def __init__(self, filename=None):
  109. self.filename = None
  110. self.materials = {}
  111. self.comments = {}
  112. if filename is not None:
  113. self.read(filename)
  114.  
  115. def read(self, filename, verbose=False):
  116. self.filename = filename
  117. self.materials = {}
  118. self.comments = {}
  119. try:
  120. file = open(filename)
  121. except:
  122. return self
  123. linenumber = 0
  124. mat = None
  125. for line in file.readlines():
  126. line = line.strip()
  127. linenumber = linenumber + 1
  128. if not line:
  129. continue
  130. if line[0] == '#':
  131. self.comments[linenumber] = line
  132. print line
  133. continue
  134. tokens = line.split()
  135. if not tokens:
  136. continue
  137. if verbose: print "tokens[0]:", tokens
  138. if tokens[0] == "newmtl":
  139. mat = ObjMaterial()
  140. mat.filename = filename
  141. mat.name = tokens[1]
  142. self.materials[mat.name] = mat
  143. if verbose: print "newmtl:", mat.name
  144. continue
  145. if tokens[0] in ("Ns", "d", "Tr"):
  146. # "d factor" - specifies the dissovle for the current material,
  147. # 1.0 is full opaque
  148. # "Ns exponent" - specifies the specular exponent. A high exponent
  149. # results in a tight, concentrated highlight.
  150. mat.put(tokens[0], float(tokens[1]))
  151. continue
  152. if tokens[0] in ("illum"):
  153. # according to http://www.fileformat.info/format/material/
  154. # 0 = Color on and Ambient off
  155. # 1 = Color on and Ambient on
  156. # 2 = Highlight on
  157. # 3 = Reflection on and Ray trace on
  158. # 4 = Transparency: Glass on, Reflection: Ray trace on
  159. # 5 = Reflection: Fesnel on and Ray trace on
  160. # 6 = Transparency: Refraction on, Reflection: Fresnel off and Ray trace on
  161. # 7 = Transparency: Refraction on, Refelction: Fresnel on and Ray Trace on
  162. # 8 = Reflection on and Ray trace off
  163. # 9 = Transparency: Glass on, Reflection: Ray trace off
  164. # 10 = Casts shadows onto invisible surfaces
  165. mat.put(tokens[0], int(tokens[1]))
  166. continue
  167. if tokens[0] in ("Kd", "Ka", "Ks", "Ke"):
  168. mat.put(tokens[0], floats(tokens[1:]))
  169. continue
  170. if tokens[0] in ("map_Kd", "map_Bump", "map_Ks", "map_bump", "bump"):
  171. # Ultimate Unwrap 3D Pro emits these:
  172. # map_Kd == diffuse
  173. # map_Bump == bump
  174. # map_Ks == specular
  175. mat.put(tokens[0], pathify(tokens[1]))
  176. if verbose: print "map:", mat.name, tokens[0], mat.get(tokens[0])
  177. continue
  178. if tokens[0] in ("Ni"):
  179. # blender's .obj exporter can emit this "Ni 1.000000"
  180. mat.put(tokens[0], float(tokens[1]))
  181. continue
  182. print "file \"%s\": line %d: unrecognized:" % (filename, linenumber), tokens
  183. file.close()
  184. if verbose: print "%d materials" % len(self.materials), "loaded from", filename
  185. return self
  186.  
  187. class ObjFile:
  188. """a representation of a wavefront .obj file"""
  189. def __init__(self, filename=None):
  190. self.filename = None
  191. self.objects = ["defaultobject"]
  192. self.groups = ["defaultgroup"]
  193. self.points = []
  194. self.uvs = []
  195. self.normals = []
  196. self.faces = []
  197. self.polylines = []
  198. self.matlibs = []
  199. self.materialsbyname = {}
  200. self.comments = {}
  201. self.currentobject = self.objects[0]
  202. self.currentgroup = self.groups[0]
  203. self.currentmaterial = None
  204. if filename is not None:
  205. self.read(filename)
  206.  
  207. def read(self, filename, verbose=False):
  208. if verbose: print "ObjFile.read:", "filename:", filename
  209. self.filename = filename
  210. self.objects = ["defaultobject"]
  211. self.groups = ["defaultgroup"]
  212. self.points = []
  213. self.uvs = []
  214. self.normals = []
  215. self.faces = []
  216. self.polylines = []
  217. self.matlibs = []
  218. self.materialsbyname = {}
  219. self.comments = {}
  220. self.currentobject = self.objects[0]
  221. self.currentgroup = self.groups[0]
  222. self.currentmaterial = None
  223. try:
  224. file = open(filename)
  225. except:
  226. return self
  227. linenumber = 0
  228. for line in file.readlines():
  229. line = line.strip()
  230. linenumber = linenumber + 1
  231. if not line:
  232. continue
  233. if line[0] == '#':
  234. self.comments[linenumber] = line
  235. print line
  236. continue
  237. tokens = line.split()
  238. if not tokens:
  239. continue
  240. if tokens[0] == "mtllib":
  241. if verbose: print "mtllib:", tokens[1:]
  242. mtllib = MtlFile(tokens[1])
  243. # if verbose: print mtllib
  244. self.matlibs.append(mtllib)
  245. self.indexmaterials(mtllib)
  246. continue
  247. if tokens[0] == "g":
  248. if verbose: print "g:", tokens[1:]
  249. self.__newgroup("".join(tokens[1:]))
  250. continue
  251. if tokens[0] == "o":
  252. if verbose: print "o:", tokens[1:]
  253. self.__newobject("".join(tokens[1:]))
  254. continue
  255. if tokens[0] == "usemtl":
  256. if verbose: print "usemtl:", tokens[1:]
  257. self.__usematerial(tokens[1])
  258. continue
  259. if tokens[0] == "v":
  260. if verbose: print "v:", tokens[1:]
  261. self.__newv(tokens[1:])
  262. continue
  263. if tokens[0] == "vn":
  264. if verbose: print "vn:", tokens[1:]
  265. self.__newnormal(tokens[1:])
  266. continue
  267. if tokens[0] == "vt":
  268. if verbose: print "vt:", tokens[1:]
  269. self.__newuv(tokens[1:])
  270. continue
  271. if tokens[0] == "f":
  272. if verbose: print "f:", tokens[1:]
  273. self.__newface(tokens[1:])
  274. continue
  275. if tokens[0] == "s":
  276. # apparently, this enables/disables smoothing
  277. print "%s:%d:" % (filename, linenumber), "ignoring:", tokens
  278. continue
  279. if tokens[0] == "l":
  280. if verbose: print "l:", tokens[1:]
  281. self.__newpolyline(tokens[1:])
  282. continue
  283. print "%s:%d:" % (filename, linenumber), "unknown:", tokens
  284. file.close()
  285. return self
  286.  
  287. def __vertlist(self, lst):
  288. res = []
  289. for vert in lst:
  290. vinfo = vert.split("/")
  291. vlen = len(vinfo)
  292. vertex = {'v':None, 'vt':None, 'vn':None}
  293. if vlen == 1:
  294. vertex['v'] = int(vinfo[0])
  295. elif vlen == 2:
  296. if vinfo[0] != '':
  297. vertex['v'] = int(vinfo[0])
  298. if vinfo[1] != '':
  299. vertex['vt'] = int(vinfo[1])
  300. elif vlen == 3:
  301. if vinfo[0] != '':
  302. vertex['v'] = int(vinfo[0])
  303. if vinfo[1] != '':
  304. vertex['vt'] = int(vinfo[1])
  305. if vinfo[2] != '':
  306. vertex['vn'] = int(vinfo[2])
  307. else:
  308. print "aborting..."
  309. raise UNKNOWN, res
  310. res.append(vertex)
  311. if False: print res
  312. return res
  313.  
  314. def __enclose(self, lst):
  315. mdata = (self.currentobject, self.currentgroup, self.currentmaterial)
  316. return (lst, mdata)
  317.  
  318. def __newpolyline(self, l):
  319. polyline = self.__vertlist(l)
  320. if False: print "__newline:", polyline
  321. self.polylines.append(self.__enclose(polyline))
  322. return self
  323.  
  324. def __newface(self, f):
  325. face = self.__vertlist(f)
  326. if False: print face
  327. self.faces.append(self.__enclose(face))
  328. return self
  329.  
  330. def __newuv(self, uv):
  331. self.uvs.append(floats(uv))
  332. return self
  333.  
  334. def __newnormal(self, normal):
  335. self.normals.append(floats(normal))
  336. return self
  337.  
  338. def __newv(self, v):
  339. # capture the current metadata with vertices
  340. vdata = floats(v)
  341. mdata = (self.currentobject, self.currentgroup, self.currentmaterial)
  342. vinfo = (vdata, mdata)
  343. self.points.append(vinfo)
  344. return self
  345.  
  346. def indexmaterials(self, mtllib, verbose=False):
  347. # traverse the materials defined in mtllib, indexing
  348. # them by name.
  349. for mname in mtllib.materials:
  350. mobj = mtllib.materials[mname]
  351. self.materialsbyname[mobj.name] = mobj
  352. if verbose:
  353. print "indexmaterials:", mtllib.filename, "materials:", self.materialsbyname.keys()
  354. return self
  355.  
  356. def __closeobject(self):
  357. self.currentobject = "defaultobject"
  358. return self
  359.  
  360. def __newobject(self, object):
  361. self.__closeobject()
  362. if False: print "__newobject:", "object:", object
  363. self.currentobject = object
  364. self.objects.append(object)
  365. return self
  366.  
  367. def __closegroup(self):
  368. self.currentgroup = "defaultgroup"
  369. return self
  370.  
  371. def __newgroup(self, group):
  372. self.__closegroup()
  373. if False: print "__newgroup:", "group:", group
  374. self.currentgroup = group
  375. self.groups.append(group)
  376. return self
  377.  
  378. def __usematerial(self, material):
  379. if False: print "__usematerial:", "material:", material
  380. if self.materialsbyname.has_key(material):
  381. self.currentmaterial = material
  382. else:
  383. print "warning:", "__usematerial:", "unknown material:", material
  384. return self
  385.  
  386. def __itemsby(self, itemlist, objname, groupname):
  387. res = []
  388. for item in itemlist:
  389. vlist, mdata = item
  390. wobj, wgrp, wmat = mdata
  391. if (wobj == objname) and (wgrp == groupname):
  392. res.append(item)
  393. return res
  394.  
  395. def __facesby(self, objname, groupname):
  396. return self.__itemsby(self.faces, objname, groupname)
  397.  
  398. def __linesby(self, objname, groupname):
  399. return self.__itemsby(self.polylines, objname, groupname)
  400.  
  401. def __eggifyverts(self, eprim, evpool, vlist):
  402. for vertex in vlist:
  403. ixyz = vertex['v']
  404. vinfo = self.points[ixyz-1]
  405. vxyz, vmeta = vinfo
  406. ev = EggVertex()
  407. ev.setPos(Point3D(vxyz[0], vxyz[1], vxyz[2]))
  408. iuv = vertex['vt']
  409. if iuv is not None:
  410. vuv = self.uvs[iuv-1]
  411. ev.setUv(Point2D(vuv[0], vuv[1]))
  412. inormal = vertex['vn']
  413. if inormal is not None:
  414. vn = self.normals[inormal-1]
  415. ev.setNormal(Vec3D(vn[0], vn[1], vn[2]))
  416. evpool.addVertex(ev)
  417. eprim.addVertex(ev)
  418. return self
  419.  
  420. def __eggifymats(self, eprim, wmat):
  421. if self.materialsbyname.has_key(wmat):
  422. mtl = self.materialsbyname[wmat]
  423. if mtl.isTextured():
  424. eprim.setTexture(mtl.getEggTexture())
  425. # NOTE: it looks like you almost always want to setMaterial()
  426. # for textured polys.... [continued below...]
  427. eprim.setMaterial(mtl.getEggMaterial())
  428. rgb = mtl.get("Kd")
  429. if rgb is not None:
  430. # ... and some untextured .obj's store the color of the
  431. # material # in the Kd settings...
  432. eprim.setColor(Vec4(rgb[0], rgb[1], rgb[2], 1.0))
  433. # [continued...] but you may *not* always want to assign
  434. # materials to untextured polys... hmmmm.
  435. if False:
  436. eprim.setMaterial(mtl.getEggMaterial())
  437. return self
  438.  
  439. def __facestoegg(self, egg, objname, groupname):
  440. selectedfaces = self.__facesby(objname, groupname)
  441. if len(selectedfaces) == 0:
  442. return self
  443. eobj = EggGroup(objname)
  444. egg.addChild(eobj)
  445. egrp = EggGroup(groupname)
  446. eobj.addChild(egrp)
  447. evpool = EggVertexPool(groupname)
  448. egrp.addChild(evpool)
  449. for face in selectedfaces:
  450. vlist, mdata = face
  451. wobj, wgrp, wmat = mdata
  452. epoly = EggPolygon()
  453. egrp.addChild(epoly)
  454. self.__eggifymats(epoly, wmat)
  455. self.__eggifyverts(epoly, evpool, vlist)
  456. #; each matching face
  457. return self
  458.  
  459. def __polylinestoegg(self, egg, objname, groupname):
  460. selectedlines = self.__linesby(objname, groupname)
  461. if len(selectedlines) == 0:
  462. return self
  463. eobj = EggGroup(objname)
  464. egg.addChild(eobj)
  465. egrp = EggGroup(groupname)
  466. eobj.addChild(egrp)
  467. evpool = EggVertexPool(groupname)
  468. egrp.addChild(evpool)
  469. for line in selectedlines:
  470. vlist, mdata = line
  471. wobj, wgrp, wmat = mdata
  472. eline = EggLine()
  473. egrp.addChild(eline)
  474. self.__eggifymats(eline, wmat)
  475. self.__eggifyverts(eline, evpool, vlist)
  476. #; each matching line
  477. return self
  478.  
  479. def toEgg(self, verbose=True):
  480. if verbose: print "converting..."
  481. # make a new egg
  482. egg = EggData()
  483. # convert polygon faces
  484. if len(self.faces) > 0:
  485. for objname in self.objects:
  486. for groupname in self.groups:
  487. self.__facestoegg(egg, objname, groupname)
  488. # convert polylines
  489. if len(self.polylines) > 0:
  490. for objname in self.objects:
  491. for groupname in self.groups:
  492. self.__polylinestoegg(egg, objname, groupname)
  493. return egg
  494.  
  495. def pathify(path):
  496. if os.path.isfile(path):
  497. return path
  498. # if it was written on win32, it may have \'s in it, and
  499. # also a full rather than relative pathname (Hexagon does this... ick)
  500. orig = path
  501. path = path.lower()
  502. path = path.replace("\\", "/")
  503. h, t = os.path.split(path)
  504. if os.path.isfile(t):
  505. return t
  506. print "warning: can't make sense of this map file name:", orig
  507. return t
  508.  
  509. def main(argv=None):
  510. if argv is None:
  511. argv = sys.argv
  512. try:
  513. opts, args = getopt.getopt(argv[1:], "hn:bs", ["help", "normals", "binormals", "show"])
  514. except getopt.error, msg:
  515. print msg
  516. print __doc__
  517. return 2
  518. show = False
  519. for o, a in opts:
  520. if o in ("-h", "--help"):
  521. print __doc__
  522. return 0
  523. elif o in ("-s", "--show"):
  524. show = True
  525. for infile in args:
  526. try:
  527. if ".obj" not in infile:
  528. print "WARNING", finfile, "does not look like a valid obj file"
  529. continue
  530. obj = ObjFile(infile)
  531. egg = obj.toEgg()
  532. f, e = os.path.splitext(infile)
  533. outfile = f + ".egg"
  534. for o, a in opts:
  535. if o in ("-n", "--normals"):
  536. egg.recomputeVertexNormals(float(a))
  537. elif o in ("-b", "--binormals"):
  538. egg.recomputeTangentBinormal(GlobPattern(""))
  539. egg.removeUnusedVertices(GlobPattern(""))
  540. if True:
  541. egg.triangulatePolygons(EggData.TConvex & EggData.TPolygon)
  542. if True:
  543. egg.recomputePolygonNormals()
  544. egg.writeEgg(Filename(outfile))
  545. if show:
  546. os.system("pview " + outfile)
  547. except Exception,e:
  548. print e
  549. return 0
  550.  
  551. if __name__ == "__main__":
  552. sys.exit(main())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement