1 ############################################################################## 2 # 3 # Extract geometry object from Poserfile. 4 # 5 # Should run with Python 2 and 3 (Poser 10/11/12) 6 # inside and outside of Poser. 7 ############################################################################## 8 9 10 # Python 2/3 compatibility -------------------- 11 from __future__ import print_function 12 13 import sys, os, re 14 import wx, gzip 15 from collections import deque 16 17 if sys.version_info.major > 2: 18 basestring = str 19 map = lambda a, b: [a(_b) for _b in b] 20 21 try: 22 INSIDEPOSER = True 23 import poser 24 25 except ImportError: 26 INSIDEPOSER = False 27 app = wx.App() 28 # import POSER_FAKE as poser 29 30 ############################################################################## 31 32 globals().setdefault("DEFAULT_DIR", os.path.dirname(__file__)) 33 34 35 class Filereader(object): 36 """ 37 Base class to deal with Poser files. 38 """ 39 40 readlinesearch = re.compile("[{}]").search # Quick search for curly braces 41 42 def __init__(self, fname): 43 self.fh = None 44 if not isinstance(fname, basestring): 45 raise ValueError("given parameter must be <str> (filename)") 46 47 self.filesize = os.path.getsize(fname) 48 49 if self.fh is None: 50 self.fh = gzip.open(fname) if self.is_zip(fname) else open(fname) 51 52 self._linebuffer = deque() 53 self.indent = 0 54 self.filepos = 0 55 self.EOF = False 56 self.bufferSize = 64 * 1024 57 58 assert hasattr(self.fh, "read") and \ 59 hasattr(self.fh, "seek"), \ 60 "Something is wrong with this file: '%s'" % fname 61
62 @staticmethod 63 def is_zip(fname): 64 """ 65 Returns True if <fname> is a zip-file, else False. 66 """ 67 with open(fname, "rb") as fh: 68 return fh.read(3) == chr(0x1f) + chr(0x8b) + chr(0x08) 69
70 def is_closed(self): 71 if callable(self.fh.closed): return self.fh.closed() 72 return self.fh.closed 73
74 def seek(self, pos): 75 assert pos <= self.filesize 76 self._linebuffer.clear() 77 self.fh.seek(pos) 78 self.EOF = False 79 self.filepos = pos 80
81 def readline(self): 82 """:rtype: str""" 83 if len(self._linebuffer) == 0: 84 newline = self.fh.readlines(self.bufferSize) 85 if len(newline) == 0: 86 self.EOF = True 87 return "" 88 self._linebuffer.extend(newline) 89 90 line = str(self._linebuffer.popleft()) 91 length = len(line) 92 self.filepos += length 93 94 s = self.readlinesearch(line) 95 if s: 96 idx = s.start() 97 c = line[idx] 98 if idx == 0: 99 s = line[idx + 1:] 100 self.filepos -= length - 1 101 self._linebuffer.appendleft(s) 102 self.indent += 1 if c == "{" else -1 103 return c 104 else: 105 l = line[idx:] 106 self.filepos -= length - idx 107 self._linebuffer.appendleft(l) 108 return line[:idx] 109 110 return line 111
112 def insertLine(self, line): 113 """:type line: str""" 114 assert isinstance(line, str) 115 self.indent -= line.count("{") 116 self.indent += line.count("}") 117 self._linebuffer.appendleft(line) 118 self.filepos -= len(line) 119 120
121 class Reader(Filereader): 122 def __init__(self, in_name, out_name): 123 super(self.__class__, self).__init__(in_name) 124 self.current_actortype = self.current_actorname = None 125 self.out_fh = open(out_name, "w") 126 print("Original file: ", in_name) 127 print("Modified file:", out_name) 128
129 def current_objname(self): 130 posername = "" 131 fname = self.current_actortype + "_" + self.current_actorname + ".obj" 132 if INSIDEPOSER: 133 user = os.environ.get("USERNAME") 134 path = os.path.join(poser.ContentRootLocation(), "Runtime", "Geometries", user) 135 posername = ":".join(["Runtime", "Geometries", user, fname]) 136 if not os.path.exists(path): 137 os.makedirs(path) 138 else: 139 path = os.path.dirname(self.fh.name) 140 141 return posername, os.path.join(path, fname) 142
143 def run(self): 144 while not self.EOF: 145 line = self.readline() 146 l = re.split(r"\s+", line.strip(), 2) # split line into max 2 parts if possible. 147 148 if self.indent == 1: 149 # All props/actors/figures are at indentation level one. 150 if l[0] in ("actor", "prop"): 151 # Save the last actor/prop name 152 self.current_actortype, self.current_actorname = l 153 154 elif self.indent == 2: 155 # Parameters of above props etc are on indent level 2. 156 if len(l) == 1 and l[0] == "geomCustom": 157 while self.indent == 2: # find start 158 self.readline() 159 160 poser_name, obj_name = self.current_objname() # get a name for this object 161 print("Exported obj:", obj_name) 162 163 with open(obj_name, "w") as obj: 164 t = "\t" * (self.indent - 2) 165 self.out_fh.write(t + "storageOffset 0 0.34870 0\n" + 166 t + "objFileGeom 0 0 " + poser_name or obj_name) 167 168 obj.write("# %s: %s\n#" % (self.current_actortype, self.current_actorname)) 169 while True: 170 # read the objects content 171 line = self.readline().strip() 172 l = re.split(r"\s+", line, 2) 173 if l[0] == "}": 174 break 175 elif l[0].startswith("numb"): 176 obj.write("# %s\n" % line) 177 else: 178 obj.write(line + "\n") 179 obj.write("\n") # make sure file ends with a linefeed 180 181 continue # don't write the last read line 182 183 self.out_fh.write(line) 184 185 self.fh.close() 186 187 188 if __name__ == "__main__": 189 with wx.FileDialog(None, "Select Poser File", 190 DEFAULT_DIR, style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as dialog: 191 if dialog.ShowModal() == wx.ID_OK: 192 fname = dialog.GetPath() 193 DEFAULT_DIR = os.path.dirname(fname) 194 f = fname.rsplit(".", 1) 195 newname = f[0] + "_NEW" + "." + f[-1] 196 reader = Reader(fname, newname) 197 reader.run() 198