assimp/port/PyAssimp/pyassimp/helper.py

184 lines
5.8 KiB
Python

#-*- coding: UTF-8 -*-
"""
Some fancy helper functions.
"""
import os
import ctypes
from ctypes import POINTER
import operator
import numpy
from numpy import linalg
import logging;logger = logging.getLogger("pyassimp")
from .errors import AssimpError
additional_dirs, ext_whitelist = [],[]
# populate search directories and lists of allowed file extensions
# depending on the platform we're running on.
if os.name=='posix':
additional_dirs.append('/usr/lib/')
additional_dirs.append('/usr/local/lib/')
# note - this won't catch libassimp.so.N.n, but
# currently there's always a symlink called
# libassimp.so in /usr/local/lib.
ext_whitelist.append('.so')
# libassimp.dylib in /usr/local/lib
ext_whitelist.append('.dylib')
elif os.name=='nt':
ext_whitelist.append('.dll')
path_dirs = os.environ['PATH'].split(';')
for dir_candidate in path_dirs:
if 'assimp' in dir_candidate.lower():
additional_dirs.append(dir_candidate)
#print(additional_dirs)
def vec2tuple(x):
""" Converts a VECTOR3D to a Tuple """
return (x.x, x.y, x.z)
def transform(vector3, matrix4x4):
""" Apply a transformation matrix on a 3D vector.
:param vector3: a numpy array with 3 elements
:param matrix4x4: a numpy 4x4 matrix
"""
return numpy.dot(matrix4x4, numpy.append(vector3, 1.))
def get_bounding_box(scene):
bb_min = [1e10, 1e10, 1e10] # x,y,z
bb_max = [-1e10, -1e10, -1e10] # x,y,z
return get_bounding_box_for_node(scene.rootnode, bb_min, bb_max, linalg.inv(scene.rootnode.transformation))
def get_bounding_box_for_node(node, bb_min, bb_max, transformation):
transformation = numpy.dot(transformation, node.transformation)
for mesh in node.meshes:
for v in mesh.vertices:
v = transform(v, transformation)
bb_min[0] = min(bb_min[0], v[0])
bb_min[1] = min(bb_min[1], v[1])
bb_min[2] = min(bb_min[2], v[2])
bb_max[0] = max(bb_max[0], v[0])
bb_max[1] = max(bb_max[1], v[1])
bb_max[2] = max(bb_max[2], v[2])
for child in node.children:
bb_min, bb_max = get_bounding_box_for_node(child, bb_min, bb_max, transformation)
return bb_min, bb_max
def try_load_functions(library_path, dll):
'''
Try to bind to aiImportFile and aiReleaseImport
Arguments
---------
library_path: path to current lib
dll: ctypes handle to library
Returns
---------
If unsuccessful:
None
If successful:
Tuple containing (library_path,
load from filename function,
load from memory function
release function,
ctypes handle to assimp library)
'''
try:
load = dll.aiImportFile
release = dll.aiReleaseImport
load_mem = dll.aiImportFileFromMemory
except AttributeError:
#OK, this is a library, but it doesn't have the functions we need
return None
# library found!
from .structs import Scene
load.restype = POINTER(Scene)
load_mem.restype = POINTER(Scene)
return (library_path, load, load_mem, release, dll)
def search_library():
'''
Loads the assimp library.
Throws exception AssimpError if no library_path is found
Returns: tuple, (load from filename function,
load from memory function,
release function,
dll)
'''
#this path
folder = os.path.dirname(__file__)
# silence 'DLL not found' message boxes on win
try:
ctypes.windll.kernel32.SetErrorMode(0x8007)
except AttributeError:
pass
candidates = []
# test every file
for curfolder in [folder]+additional_dirs:
for filename in os.listdir(curfolder):
# our minimum requirement for candidates is that
# they should contain 'assimp' somewhere in
# their name
if filename.lower().find('assimp')==-1 or\
os.path.splitext(filename)[-1].lower() not in ext_whitelist:
continue
library_path = os.path.join(curfolder, filename)
logger.debug('Try ' + library_path)
try:
dll = ctypes.cdll.LoadLibrary(library_path)
except Exception as e:
logger.warning(str(e))
# OK, this except is evil. But different OSs will throw different
# errors. So just ignore any errors.
continue
# see if the functions we need are in the dll
loaded = try_load_functions(library_path, dll)
if loaded: candidates.append(loaded)
if not candidates:
# no library found
raise AssimpError("assimp library not found")
else:
# get the newest library_path
candidates = map(lambda x: (os.lstat(x[0])[-2], x), candidates)
res = max(candidates, key=operator.itemgetter(0))[1]
logger.debug('Using assimp library located at ' + res[0])
# XXX: if there are 1000 dll/so files containing 'assimp'
# in their name, do we have all of them in our address
# space now until gc kicks in?
# XXX: take version postfix of the .so on linux?
return res[1:]
def hasattr_silent(object, name):
"""
Calls hasttr() with the given parameters and preserves the legacy (pre-Python 3.2)
functionality of silently catching exceptions.
Returns the result of hasatter() or False if an exception was raised.
"""
try:
return hasattr(object, name)
except:
return False