added the ability to load from file objects with pyassimp.load, as opposed to only being able to load from paths
parent
7fdcb25516
commit
5ae65987c0
|
@ -10,29 +10,20 @@ import sys
|
|||
if sys.version_info < (2,6):
|
||||
raise 'pyassimp: need python 2.6 or newer'
|
||||
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
import numpy
|
||||
|
||||
import logging; logger = logging.getLogger("pyassimp")
|
||||
|
||||
# Attach a default, null handler, to the logger.
|
||||
# applications can easily get log messages from pyassimp
|
||||
# by calling for instance
|
||||
# >>> logging.basicConfig(level=logging.DEBUG)
|
||||
# before importing pyassimp
|
||||
class NullHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
pass
|
||||
h = NullHandler()
|
||||
logger.addHandler(h)
|
||||
import logging
|
||||
logger = logging.getLogger("pyassimp")
|
||||
# attach default null handler to logger so it doesn't complain
|
||||
# even if you don't attach another handler to logger
|
||||
logger.addHandler(logging.NullHandler())
|
||||
|
||||
from . import structs
|
||||
from .errors import AssimpError
|
||||
from . import helper
|
||||
|
||||
|
||||
assimp_structs_as_tuple = (
|
||||
structs.Matrix4x4,
|
||||
structs.Matrix3x3,
|
||||
|
@ -59,10 +50,9 @@ def make_tuple(ai_obj, type = None):
|
|||
|
||||
# It is faster and more correct to have an init function for each assimp class
|
||||
def _init_face(aiFace):
|
||||
aiFace.indices = [aiFace.mIndices[i] for i in range(aiFace.mNumIndices)]
|
||||
aiFace.indices = [aiFace.mIndices[i] for i in xrange(aiFace.mNumIndices)]
|
||||
|
||||
assimp_struct_inits = \
|
||||
{ structs.Face : _init_face }
|
||||
assimp_struct_inits = { structs.Face : _init_face }
|
||||
|
||||
def call_init(obj, caller = None):
|
||||
if helper.hasattr_silent(obj,'contents'): #pointer
|
||||
|
@ -86,7 +76,7 @@ def _is_init_type(obj):
|
|||
|
||||
def _init(self, target = None, parent = None):
|
||||
"""
|
||||
Custom initialize() for C structs, adds safely accessable member functionality.
|
||||
Custom initialize() for C structs, adds safely accessible member functionality.
|
||||
|
||||
:param target: set the object which receive the added methods. Useful when manipulating
|
||||
pointers, to skip the intermediate 'contents' deferencing.
|
||||
|
@ -214,7 +204,7 @@ class AssimpLib(object):
|
|||
"""
|
||||
Assimp-Singleton
|
||||
"""
|
||||
load, release, dll = helper.search_library()
|
||||
load, load_mem, release, dll = helper.search_library()
|
||||
|
||||
#the loader as singleton
|
||||
_assimp_lib = AssimpLib()
|
||||
|
@ -239,7 +229,6 @@ def pythonize_assimp(type, obj, scene):
|
|||
return meshes
|
||||
|
||||
if type == "ADDTRANSFORMATION":
|
||||
|
||||
def getnode(node, name):
|
||||
if node.name == name: return node
|
||||
for child in node.children:
|
||||
|
@ -251,51 +240,65 @@ def pythonize_assimp(type, obj, scene):
|
|||
raise AssimpError("Object " + str(obj) + " has no associated node!")
|
||||
setattr(obj, "transformation", node.transformation)
|
||||
|
||||
|
||||
def recur_pythonize(node, scene):
|
||||
""" Recursively call pythonize_assimp on
|
||||
'''
|
||||
Recursively call pythonize_assimp on
|
||||
nodes tree to apply several post-processing to
|
||||
pythonize the assimp datastructures.
|
||||
"""
|
||||
|
||||
'''
|
||||
node.meshes = pythonize_assimp("MESH", node.meshes, scene)
|
||||
|
||||
|
||||
for mesh in node.meshes:
|
||||
mesh.material = scene.materials[mesh.materialindex]
|
||||
|
||||
for cam in scene.cameras:
|
||||
pythonize_assimp("ADDTRANSFORMATION", cam, scene)
|
||||
|
||||
#for light in scene.lights:
|
||||
# pythonize_assimp("ADDTRANSFORMATION", light, scene)
|
||||
|
||||
for c in node.children:
|
||||
recur_pythonize(c, scene)
|
||||
|
||||
|
||||
def load(filename, processing=0):
|
||||
"""
|
||||
Loads the model with some specific processing parameters.
|
||||
|
||||
filename - file to load model from
|
||||
processing - processing parameters
|
||||
|
||||
result Scene-object with model-data
|
||||
|
||||
throws AssimpError - could not open file
|
||||
"""
|
||||
#read pure data
|
||||
#from ctypes import c_char_p, c_uint
|
||||
#model = _assimp_lib.load(c_char_p(filename), c_uint(processing))
|
||||
model = _assimp_lib.load(filename.encode("ascii"), processing)
|
||||
def load(filename, processing=0, file_type=None):
|
||||
'''
|
||||
Load a model into a scene. On failure throws AssimpError.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
filename: Either a filename or a file object to load model from.
|
||||
If a file object is passed, file_type MUST be specified
|
||||
Otherwise Assimp has no idea which importer to use.
|
||||
This is named 'filename' so as to not break legacy code.
|
||||
processing: assimp processing parameters
|
||||
file_type: string, such as 'stl'
|
||||
|
||||
Returns
|
||||
---------
|
||||
Scene object with model-data
|
||||
'''
|
||||
|
||||
if hasattr(filename, 'read'):
|
||||
'''
|
||||
This is the case where a file object has been passed to load.
|
||||
It is calling the following function:
|
||||
const aiScene* aiImportFileFromMemory(const char* pBuffer,
|
||||
unsigned int pLength,
|
||||
unsigned int pFlags,
|
||||
const char* pHint)
|
||||
'''
|
||||
if file_type == None:
|
||||
raise AssimpError('File type must be specified when passing file objects!')
|
||||
data = filename.read()
|
||||
model = _assimp_lib.load_mem(data,
|
||||
len(data),
|
||||
processing,
|
||||
file_type)
|
||||
else:
|
||||
# a filename string has been passed
|
||||
model = _assimp_lib.load(filename.encode("ascii"), processing)
|
||||
|
||||
if not model:
|
||||
#Uhhh, something went wrong!
|
||||
raise AssimpError("could not import file: %s" % filename)
|
||||
|
||||
raise AssimpError('Could not import file!')
|
||||
scene = _init(model.contents)
|
||||
|
||||
recur_pythonize(scene.rootnode, scene)
|
||||
|
||||
return scene
|
||||
|
||||
def release(scene):
|
||||
|
|
|
@ -75,42 +75,50 @@ def get_bounding_box_for_node(node, bb_min, bb_max, transformation):
|
|||
|
||||
return bb_min, bb_max
|
||||
|
||||
|
||||
|
||||
def try_load_functions(library,dll,candidates):
|
||||
"""try to functbind to aiImportFile and aiReleaseImport
|
||||
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 function,
|
||||
release function,
|
||||
ctypes handle to assimp library)
|
||||
'''
|
||||
|
||||
library - path to current lib
|
||||
dll - ctypes handle to it
|
||||
candidates - receives matching candidates
|
||||
|
||||
They serve as signal functions to detect assimp,
|
||||
also they're currently the only functions we need.
|
||||
insert (library,aiImportFile,aiReleaseImport,dll)
|
||||
into 'candidates' if successful.
|
||||
|
||||
"""
|
||||
try:
|
||||
load = dll.aiImportFile
|
||||
release = dll.aiReleaseImport
|
||||
load = dll.aiImportFile
|
||||
release = dll.aiReleaseImport
|
||||
load_mem = dll.aiImportFileFromMemory
|
||||
except AttributeError:
|
||||
#OK, this is a library, but it has not the functions we need
|
||||
pass
|
||||
else:
|
||||
#Library found!
|
||||
from .structs import Scene
|
||||
load.restype = POINTER(Scene)
|
||||
|
||||
candidates.append((library, load, release, dll))
|
||||
|
||||
#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.
|
||||
'''
|
||||
Loads the assimp library.
|
||||
Throws exception AssimpError if no library_path is found
|
||||
|
||||
result (load-function, release-function)
|
||||
exception AssimpError if no library is found
|
||||
|
||||
"""
|
||||
Returns: tuple, (load filename function,
|
||||
release function,
|
||||
dll,
|
||||
load from memory function)
|
||||
'''
|
||||
#this path
|
||||
folder = os.path.dirname(__file__)
|
||||
|
||||
|
@ -121,7 +129,6 @@ def search_library():
|
|||
pass
|
||||
|
||||
candidates = []
|
||||
|
||||
# test every file
|
||||
for curfolder in [folder]+additional_dirs:
|
||||
for filename in os.listdir(curfolder):
|
||||
|
@ -132,26 +139,27 @@ def search_library():
|
|||
os.path.splitext(filename)[-1].lower() not in ext_whitelist:
|
||||
continue
|
||||
|
||||
library = os.path.join(curfolder, filename)
|
||||
logger.debug('Try ' + library)
|
||||
library_path = os.path.join(curfolder, filename)
|
||||
logger.debug('Try ' + library_path)
|
||||
try:
|
||||
dll = ctypes.cdll.LoadLibrary(library)
|
||||
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
|
||||
|
||||
loaded = try_load_functions(library_path, dll)
|
||||
if loaded: candidates.append(loaded)
|
||||
|
||||
try_load_functions(library,dll,candidates)
|
||||
|
||||
if not candidates:
|
||||
# no library found
|
||||
raise AssimpError("assimp library not found")
|
||||
# no library_path found
|
||||
raise AssimpError("assimp library_path not found")
|
||||
else:
|
||||
# get the newest library
|
||||
# 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])
|
||||
logger.debug('Using assimp library_path 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
|
||||
|
|
Loading…
Reference in New Issue