Home | Trees | Indices | Help |
|
---|
|
1 """ 2 :mod:`pyffi.formats.tri` --- TRI (.tri) 3 ======================================= 4 5 A .tri file contains facial expression data, that is, morphs for dynamic 6 expressions such as smile, frown, and so on. 7 8 Implementation 9 -------------- 10 11 .. autoclass:: TriFormat 12 :show-inheritance: 13 :members: 14 15 Regression tests 16 ---------------- 17 18 Read a TRI file 19 ^^^^^^^^^^^^^^^ 20 21 >>> # check and read tri file 22 >>> stream = open('tests/tri/mmouthxivilai.tri', 'rb') 23 >>> data = TriFormat.Data() 24 >>> data.inspect(stream) 25 >>> # do some stuff with header? 26 >>> data.num_vertices 27 89 28 >>> data.num_tri_faces 29 215 30 >>> data.num_quad_faces 31 0 32 >>> data.num_uvs 33 89 34 >>> data.num_morphs 35 18 36 >>> data.read(stream) # doctest: +ELLIPSIS 37 >>> print([str(morph.name.decode("ascii")) for morph in data.morphs]) 38 ['Fear', 'Surprise', 'Aah', 'BigAah', 'BMP', 'ChJSh', 'DST', 'Eee', 'Eh', \ 39 'FV', 'I', 'K', 'N', 'Oh', 'OohQ', 'R', 'Th', 'W'] 40 41 Parse all TRI files in a directory tree 42 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 43 44 >>> for stream, data in TriFormat.walkData('tests/tri'): 45 ... print(stream.name) 46 tests/tri/mmouthxivilai.tri 47 48 Create an TRI file from scratch and write to file 49 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 50 51 >>> data = TriFormat.Data() 52 >>> from tempfile import TemporaryFile 53 >>> stream = TemporaryFile() 54 >>> data.write(stream) 55 """ 56 57 # ***** BEGIN LICENSE BLOCK ***** 58 # 59 # Copyright (c) 2007-2011, Python File Format Interface 60 # All rights reserved. 61 # 62 # Redistribution and use in source and binary forms, with or without 63 # modification, are permitted provided that the following conditions 64 # are met: 65 # 66 # * Redistributions of source code must retain the above copyright 67 # notice, this list of conditions and the following disclaimer. 68 # 69 # * Redistributions in binary form must reproduce the above 70 # copyright notice, this list of conditions and the following 71 # disclaimer in the documentation and/or other materials provided 72 # with the distribution. 73 # 74 # * Neither the name of the Python File Format Interface 75 # project nor the names of its contributors may be used to endorse 76 # or promote products derived from this software without specific 77 # prior written permission. 78 # 79 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 80 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 81 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 82 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 83 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 84 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 85 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 86 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 87 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 88 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 89 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 90 # POSSIBILITY OF SUCH DAMAGE. 91 # 92 # ***** END LICENSE BLOCK ***** 93 94 from itertools import chain, izip 95 import struct 96 import os 97 import re 98 99 import pyffi.object_models.xml 100 import pyffi.object_models.common 101 from pyffi.object_models.xml.basic import BasicBase 102 import pyffi.object_models 103 from pyffi.utils.graph import EdgeFilter106 """This class implements the TRI format.""" 107 xml_file_name = 'tri.xml' 108 # where to look for tri.xml and in what order: 109 # TRIXMLPATH env var, or TriFormat module directory 110 xml_file_path = [os.getenv('TRIXMLPATH'), os.path.dirname(__file__)] 111 # file name regular expression match 112 RE_FILENAME = re.compile(r'^.*\.tri$', re.IGNORECASE) 113 114 # basic types 115 int = pyffi.object_models.common.Int 116 uint = pyffi.object_models.common.UInt 117 byte = pyffi.object_models.common.Byte 118 ubyte = pyffi.object_models.common.UByte 119 char = pyffi.object_models.common.Char 120 short = pyffi.object_models.common.Short 121 ushort = pyffi.object_models.common.UShort 122 float = pyffi.object_models.common.Float 123 124 # implementation of tri-specific basic types 125425 426 if __name__=='__main__': 427 import doctest 428 doctest.testmod() 429127156129 """Return number of bytes this type occupies in a file. 130 131 :return: Number of bytes. 132 """ 133 return ( 134 1 + 135 pyffi.object_models.common.SizedString.get_size(self, data) 136 )137139 """Read string from stream. 140 141 :param stream: The stream to read from. 142 :type stream: file 143 """ 144 pyffi.object_models.common.SizedString.read(self, stream, data) 145 self._value = self._value.rstrip(pyffi.object_models.common._b00)146148 """Write string to stream. 149 150 :param stream: The stream to write to. 151 :type stream: file 152 """ 153 self._value += pyffi.object_models.common._b00 154 pyffi.object_models.common.SizedString.write(self, stream, data) 155 self._value = self._value.rstrip(pyffi.object_models.common._b00)158 """Basic type which implements the header of a TRI file.""" 161 164202166 return self.__str__()167169 """Return a hash value for this value. 170 171 :return: An immutable object that can be used as a hash. 172 """ 173 return None174176 """Read header string from stream and check it. 177 178 :param stream: The stream to read from. 179 :type stream: file 180 """ 181 hdrstr = stream.read(5) 182 # check if the string is correct 183 if hdrstr != "FRTRI".encode("ascii"): 184 raise ValueError( 185 "invalid TRI header: expected 'FRTRI' but got '%s'" 186 % hdrstr)187189 """Write the header string to stream. 190 191 :param stream: The stream to write to. 192 :type stream: file 193 """ 194 stream.write("FRTRI".encode("ascii"))195204 _value = 3 205230 231 @staticmethod207 return self._value208 211213 return '%03i' % self._value214 217219 return self._value220 224 227229 return self.__str__()233 """Converts version string into an integer. 234 235 :param version_str: The version string. 236 :type version_str: str 237 :return: A version integer. 238 239 >>> TriFormat.version_number('003') 240 3 241 >>> TriFormat.version_number('XXX') 242 -1 243 """ 244 try: 245 # note: always '003' in all files seen so far 246 return int(version_str) 247 except ValueError: 248 # not supported 249 return -1250252 """A class to contain the actual tri data.""" 253368 369 # XXX copied from pyffi.formats.egm.EgmFormat.MorphRecord255 """Quickly checks if stream contains TRI data, by looking at 256 the first 8 bytes. Reads the signature and the version. 257 258 :param stream: The stream to inspect. 259 :type stream: file 260 """ 261 pos = stream.tell() 262 try: 263 self._signature_value_.read(stream, self) 264 self._version_value_.read(stream, self) 265 finally: 266 stream.seek(pos)267 268 # overriding pyffi.object_models.FileFormat.Data methods 269271 """Quickly checks if stream contains TRI data, and reads 272 everything up to the arrays. 273 274 :param stream: The stream to inspect. 275 :type stream: file 276 """ 277 pos = stream.tell() 278 try: 279 self.inspect_quick(stream) 280 self._signature_value_.read(stream, self) 281 self._version_value_.read(stream, self) 282 self._num_vertices_value_.read(stream, self) 283 self._num_tri_faces_value_.read(stream, self) 284 self._num_quad_faces_value_.read(stream, self) 285 self._unknown_1_value_.read(stream, self) 286 self._unknown_2_value_.read(stream, self) 287 self._num_uvs_value_.read(stream, self) 288 self._has_uv_value_.read(stream, self) 289 self._num_morphs_value_.read(stream, self) 290 self._num_modifiers_value_.read(stream, self) 291 self._num_modifier_vertices_value_.read(stream, self) 292 finally: 293 stream.seek(pos)294 295297 """Read a tri file. 298 299 :param stream: The stream from which to read. 300 :type stream: ``file`` 301 """ 302 self.inspect_quick(stream) 303 pyffi.object_models.xml.struct_.StructBase.read( 304 self, stream, self) 305 306 # check if we are at the end of the file 307 if stream.read(1): 308 raise ValueError( 309 'end of file not reached: corrupt tri file?') 310 311 # copy modifier vertices into modifier records 312 start_index = 0 313 for modifier in self.modifiers: 314 modifier.modifier_vertices.update_size() 315 for src_vert, dst_vert in izip( 316 self.modifier_vertices[ 317 start_index:start_index 318 + modifier.num_vertices_to_modify], 319 modifier.modifier_vertices): 320 dst_vert.x = src_vert.x 321 dst_vert.y = src_vert.y 322 dst_vert.z = src_vert.z 323 start_index += modifier.num_vertices_to_modify324326 """Write a tri file. 327 328 :param stream: The stream to which to write. 329 :type stream: ``file`` 330 """ 331 # copy modifier vertices from modifier records to header 332 if self.modifiers: 333 self.num_modifier_vertices = sum( 334 modifier.num_vertices_to_modify 335 for modifier in self.modifiers) 336 self.modifier_vertices.update_size() 337 for self_vert, vert in izip( 338 self.modifier_vertices, 339 chain(*(modifier.modifier_vertices 340 for modifier in self.modifiers))): 341 self_vert.x = vert.x 342 self_vert.y = vert.y 343 self_vert.z = vert.z 344 else: 345 self.num_modifier_vertices = 0 346 self.modifier_vertices.update_size() 347 # write the data 348 pyffi.object_models.xml.struct_.StructBase.write( 349 self, stream, self)350352 """Add a morph.""" 353 self.num_morphs += 1 354 self.morphs.update_size() 355 return self.morphs[-1]356358 """Add a modifier.""" 359 self.num_modifiers += 1 360 self.modifiers.update_size() 361 return self.modifiers[-1]362 363 # GlobalNode 364371 """ 372 >>> # create morph with 3 vertices. 373 >>> morph = TriFormat.MorphRecord(argument=3) 374 >>> morph.set_relative_vertices( 375 ... [(3, 5, 2), (1, 3, 2), (-9, 3, -1)]) 376 >>> # scale should be 9/32768.0 = 0.0002746... 377 >>> morph.scale # doctest: +ELLIPSIS 378 0.0002746... 379 >>> for vert in morph.get_relative_vertices(): 380 ... print([int(1000 * x + 0.5) for x in vert]) 381 [3000, 5000, 2000] 382 [1000, 3000, 2000] 383 [-8999, 3000, -999] 384 """386 for vert in self.vertices: 387 yield (vert.x * self.scale, 388 vert.y * self.scale, 389 vert.z * self.scale)390392 # copy to list 393 vertices = list(vertices) 394 # check length 395 if len(vertices) != self.arg: 396 raise ValueError("expected %i vertices, but got %i" 397 % (self.arg, len(vertices))) 398 # get extreme values of morph 399 max_value = max(max(abs(value) for value in vert) 400 for vert in vertices) 401 # calculate scale 402 self.scale = max_value / 32767.0 403 inv_scale = 1 / self.scale 404 # set vertices 405 for vert, self_vert in izip(vertices, self.vertices): 406 self_vert.x = int(vert[0] * inv_scale) 407 self_vert.y = int(vert[1] * inv_scale) 408 self_vert.z = int(vert[2] * inv_scale)409411 """Apply scale factor to data. 412 413 >>> # create morph with 3 vertices. 414 >>> morph = TriFormat.MorphRecord(argument=3) 415 >>> morph.set_relative_vertices( 416 ... [(3, 5, 2), (1, 3, 2), (-9, 3, -1)]) 417 >>> morph.apply_scale(2) 418 >>> for vert in morph.get_relative_vertices(): 419 ... print([int(1000 * x + 0.5) for x in vert]) 420 [6000, 10000, 4000] 421 [2000, 6000, 4000] 422 [-17999, 6000, -1999] 423 """ 424 self.scale *= scale
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Mon Oct 10 19:04:13 2011 | http://epydoc.sourceforge.net |