helper.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #-*- coding: UTF-8 -*-
  2. """
  3. Some fancy helper functions.
  4. """
  5. import os
  6. import ctypes
  7. from ctypes import POINTER
  8. import operator
  9. import numpy
  10. from numpy import linalg
  11. import logging;logger = logging.getLogger("pyassimp")
  12. from .errors import AssimpError
  13. additional_dirs, ext_whitelist = [],[]
  14. # populate search directories and lists of allowed file extensions
  15. # depending on the platform we're running on.
  16. if os.name=='posix':
  17. additional_dirs.append('/usr/lib/')
  18. additional_dirs.append('/usr/local/lib/')
  19. # note - this won't catch libassimp.so.N.n, but
  20. # currently there's always a symlink called
  21. # libassimp.so in /usr/local/lib.
  22. ext_whitelist.append('.so')
  23. # libassimp.dylib in /usr/local/lib
  24. ext_whitelist.append('.dylib')
  25. elif os.name=='nt':
  26. ext_whitelist.append('.dll')
  27. path_dirs = os.environ['PATH'].split(';')
  28. for dir_candidate in path_dirs:
  29. if 'assimp' in dir_candidate.lower():
  30. additional_dirs.append(dir_candidate)
  31. #print(additional_dirs)
  32. def vec2tuple(x):
  33. """ Converts a VECTOR3D to a Tuple """
  34. return (x.x, x.y, x.z)
  35. def transform(vector3, matrix4x4):
  36. """ Apply a transformation matrix on a 3D vector.
  37. :param vector3: a numpy array with 3 elements
  38. :param matrix4x4: a numpy 4x4 matrix
  39. """
  40. return numpy.dot(matrix4x4, numpy.append(vector3, 1.))
  41. def get_bounding_box(scene):
  42. bb_min = [1e10, 1e10, 1e10] # x,y,z
  43. bb_max = [-1e10, -1e10, -1e10] # x,y,z
  44. return get_bounding_box_for_node(scene.rootnode, bb_min, bb_max, linalg.inv(scene.rootnode.transformation))
  45. def get_bounding_box_for_node(node, bb_min, bb_max, transformation):
  46. transformation = numpy.dot(transformation, node.transformation)
  47. for mesh in node.meshes:
  48. for v in mesh.vertices:
  49. v = transform(v, transformation)
  50. bb_min[0] = min(bb_min[0], v[0])
  51. bb_min[1] = min(bb_min[1], v[1])
  52. bb_min[2] = min(bb_min[2], v[2])
  53. bb_max[0] = max(bb_max[0], v[0])
  54. bb_max[1] = max(bb_max[1], v[1])
  55. bb_max[2] = max(bb_max[2], v[2])
  56. for child in node.children:
  57. bb_min, bb_max = get_bounding_box_for_node(child, bb_min, bb_max, transformation)
  58. return bb_min, bb_max
  59. def try_load_functions(library_path, dll):
  60. '''
  61. Try to bind to aiImportFile and aiReleaseImport
  62. Arguments
  63. ---------
  64. library_path: path to current lib
  65. dll: ctypes handle to library
  66. Returns
  67. ---------
  68. If unsuccessful:
  69. None
  70. If successful:
  71. Tuple containing (library_path,
  72. load from filename function,
  73. load from memory function
  74. release function,
  75. ctypes handle to assimp library)
  76. '''
  77. try:
  78. load = dll.aiImportFile
  79. release = dll.aiReleaseImport
  80. load_mem = dll.aiImportFileFromMemory
  81. except AttributeError:
  82. #OK, this is a library, but it doesn't have the functions we need
  83. return None
  84. # library found!
  85. from .structs import Scene
  86. load.restype = POINTER(Scene)
  87. load_mem.restype = POINTER(Scene)
  88. return (library_path, load, load_mem, release, dll)
  89. def search_library():
  90. '''
  91. Loads the assimp library.
  92. Throws exception AssimpError if no library_path is found
  93. Returns: tuple, (load from filename function,
  94. load from memory function,
  95. release function,
  96. dll)
  97. '''
  98. #this path
  99. folder = os.path.dirname(__file__)
  100. # silence 'DLL not found' message boxes on win
  101. try:
  102. ctypes.windll.kernel32.SetErrorMode(0x8007)
  103. except AttributeError:
  104. pass
  105. candidates = []
  106. # test every file
  107. for curfolder in [folder]+additional_dirs:
  108. for filename in os.listdir(curfolder):
  109. # our minimum requirement for candidates is that
  110. # they should contain 'assimp' somewhere in
  111. # their name
  112. if filename.lower().find('assimp')==-1 or\
  113. os.path.splitext(filename)[-1].lower() not in ext_whitelist:
  114. continue
  115. library_path = os.path.join(curfolder, filename)
  116. logger.debug('Try ' + library_path)
  117. try:
  118. dll = ctypes.cdll.LoadLibrary(library_path)
  119. except Exception as e:
  120. logger.warning(str(e))
  121. # OK, this except is evil. But different OSs will throw different
  122. # errors. So just ignore any errors.
  123. continue
  124. # see if the functions we need are in the dll
  125. loaded = try_load_functions(library_path, dll)
  126. if loaded: candidates.append(loaded)
  127. if not candidates:
  128. # no library found
  129. raise AssimpError("assimp library not found")
  130. else:
  131. # get the newest library_path
  132. candidates = map(lambda x: (os.lstat(x[0])[-2], x), candidates)
  133. res = max(candidates, key=operator.itemgetter(0))[1]
  134. logger.debug('Using assimp library located at ' + res[0])
  135. # XXX: if there are 1000 dll/so files containing 'assimp'
  136. # in their name, do we have all of them in our address
  137. # space now until gc kicks in?
  138. # XXX: take version postfix of the .so on linux?
  139. return res[1:]
  140. def hasattr_silent(object, name):
  141. """
  142. Calls hasttr() with the given parameters and preserves the legacy (pre-Python 3.2)
  143. functionality of silently catching exceptions.
  144. Returns the result of hasatter() or False if an exception was raised.
  145. """
  146. try:
  147. return hasattr(object, name)
  148. except:
  149. return False