[pyassimp] Large rewrite: more generic, easier to use
Main changes: - dynamic creation of idiomatic python fields corresponding to ASSIMP ones, - hidding of pointers, - use of numpy for transformation and mesh data storage For instance, to access the list of meshes of a children of the root node, previously we did: scene.mRootNode.contents.mChildren[1].contents.mMeshes Now, it is: scene.rootnode.children[1].meshes Arrays are now regular Python list. Also added a 'post-processing' to access directly to certain objects, and not through their index. For instance: Before: mymesh_id = scene.mRootNode.contents.mChildren[1].contents.mMeshes[2] mymesh = scene.mMeshes[mymesh_id] Now: scene.rootnode.children[1].meshes[2] Initialization of the Python wrappers is not delayed anymore: everything is done during the loading (which leads to long start time, but prevent unexpected slowing at runtime) This commit also remove several 'ad-hoc' manipulation that should not be needed anymore. While here, use Python logging when necessary.pull/8/merge^2
parent
bed0e4e83f
commit
7000ea05c5
|
@ -1 +1,369 @@
|
||||||
#-*- coding: UTF-8 -*-
|
#-*- coding: UTF-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
PyAssimp
|
||||||
|
|
||||||
|
This is the main-module of PyAssimp.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
if sys.version_info < (2,5):
|
||||||
|
raise 'pyassimp: need python 2.5 or newer'
|
||||||
|
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
import os
|
||||||
|
import helper
|
||||||
|
from errors import AssimpError
|
||||||
|
|
||||||
|
import structs
|
||||||
|
|
||||||
|
import logging; logger = logging.getLogger("pyassimp")
|
||||||
|
|
||||||
|
#logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logging.basicConfig()
|
||||||
|
|
||||||
|
assimp_structs_as_tuple = (
|
||||||
|
structs.Matrix4x4,
|
||||||
|
structs.Matrix3x3,
|
||||||
|
structs.Vector2D,
|
||||||
|
structs.Vector3D,
|
||||||
|
structs.Color3D,
|
||||||
|
structs.Color4D,
|
||||||
|
structs.Quaternion,
|
||||||
|
structs.Plane,
|
||||||
|
structs.Texel)
|
||||||
|
|
||||||
|
def make_tuple(ai_obj):
|
||||||
|
return tuple([getattr(ai_obj, e[0]) for e in ai_obj._fields_])
|
||||||
|
|
||||||
|
def call_init(obj, caller = None):
|
||||||
|
# init children
|
||||||
|
if hasattr(obj, '_init'):
|
||||||
|
logger.debug("Init of children: ")
|
||||||
|
obj._init()
|
||||||
|
logger.debug("Back to " + str(caller))
|
||||||
|
|
||||||
|
# pointers
|
||||||
|
elif hasattr(obj, 'contents'):
|
||||||
|
if hasattr(obj.contents, '_init'):
|
||||||
|
logger.debug("Init of children (pointer): ")
|
||||||
|
obj.contents._init(target = obj)
|
||||||
|
logger.debug("Back to " + str(caller))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _init(self, target = None):
|
||||||
|
"""
|
||||||
|
Custom initialize() for C structs, adds safely accessable member functionality.
|
||||||
|
|
||||||
|
:param target: set the object which receive the added methods. Useful when manipulating
|
||||||
|
pointers, to skip the intermediate 'contents' deferencing.
|
||||||
|
"""
|
||||||
|
if hasattr(self, '_is_init'):
|
||||||
|
logger.debug("" + str(self.__class__) + " already initialized.")
|
||||||
|
return self
|
||||||
|
self._is_init = True
|
||||||
|
|
||||||
|
if not target:
|
||||||
|
target = self
|
||||||
|
|
||||||
|
#for m in self.__class__.__dict__.keys():
|
||||||
|
|
||||||
|
|
||||||
|
for m in dir(self):
|
||||||
|
|
||||||
|
name = m[1:].lower()
|
||||||
|
|
||||||
|
#if 'normals' in name : import pdb;pdb.set_trace()
|
||||||
|
|
||||||
|
if m.startswith("_"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
obj = getattr(self, m)
|
||||||
|
|
||||||
|
if m.startswith('mNum'):
|
||||||
|
if 'm' + m[4:] in dir(self):
|
||||||
|
continue # will be processed later on
|
||||||
|
else:
|
||||||
|
setattr(target, name, obj)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Create tuples
|
||||||
|
if isinstance(obj, assimp_structs_as_tuple):
|
||||||
|
setattr(target, name, make_tuple(obj))
|
||||||
|
logger.debug(str(self) + ": Added tuple " + str(getattr(target, name)) + " as self." + name.lower())
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
if isinstance(obj, structs.String):
|
||||||
|
setattr(target, 'name', str(obj.data))
|
||||||
|
setattr(target.__class__, '__repr__', lambda x: str(x.__class__) + "(" + x.name + ")")
|
||||||
|
setattr(target.__class__, '__str__', lambda x: x.name)
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
if m.startswith('m'):
|
||||||
|
|
||||||
|
|
||||||
|
if hasattr(self, 'mNum' + m[1:]):
|
||||||
|
|
||||||
|
length = getattr(self, 'mNum' + m[1:])
|
||||||
|
|
||||||
|
# -> special case: properties are
|
||||||
|
# stored as a dict.
|
||||||
|
if m == 'mProperties':
|
||||||
|
setattr(target, name, _get_properties(obj, length))
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
#import pdb;pdb.set_trace()
|
||||||
|
if not obj: # empty!
|
||||||
|
setattr(target, name, [])
|
||||||
|
logger.debug(str(self) + ": " + name + " is an empty list.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
if obj._type_ in assimp_structs_as_tuple:
|
||||||
|
setattr(target, name, [make_tuple(obj[i]) for i in xrange(length)])
|
||||||
|
|
||||||
|
logger.debug(str(self) + ": Added a list data (type "+ str(type(obj)) + ") as self." + name)
|
||||||
|
|
||||||
|
else:
|
||||||
|
setattr(target, name, [obj[i] for i in xrange(length)]) #TODO: maybe not necessary to recreate an array?
|
||||||
|
|
||||||
|
logger.debug(str(self) + ": Added list of " + str(obj) + " " + name + " as self." + name + " (type: " + str(type(obj)) + ")")
|
||||||
|
|
||||||
|
# initialize array elements
|
||||||
|
for e in getattr(target, name):
|
||||||
|
call_init(e, caller = self)
|
||||||
|
|
||||||
|
|
||||||
|
except IndexError:
|
||||||
|
logger.warning("in " + str(self) +" : mismatch between mNum" + name + " and the actual amount of data in m" + name + ". This may be due to version mismatch between libassimp and pyassimp. Skipping this field.")
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(str(e))
|
||||||
|
logger.warning("in " + str(self) +" : table of " + name + " not initialized (NULL pointer). Skipping this field.")
|
||||||
|
|
||||||
|
|
||||||
|
else: # starts with 'm' but not iterable
|
||||||
|
|
||||||
|
setattr(target, name, obj)
|
||||||
|
logger.debug("Added " + name + " as self." + name + " (type: " + str(type(obj)) + ")")
|
||||||
|
|
||||||
|
if name not in ["parent"]: # do not re-initialize my parent
|
||||||
|
call_init(obj, caller = self)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if isinstance(self, structs.Mesh):
|
||||||
|
_finalize_mesh(self, target)
|
||||||
|
|
||||||
|
if isinstance(self, structs.Texture):
|
||||||
|
_finalize_texture(self, target)
|
||||||
|
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Python magic to add the _init() function to all C struct classes.
|
||||||
|
"""
|
||||||
|
for struct in dir(structs):
|
||||||
|
if not (struct.startswith('_') or struct.startswith('c_') or struct == "Structure" or struct == "POINTER") and not isinstance(getattr(structs, struct),int):
|
||||||
|
|
||||||
|
logger.debug("Adding _init to class " + struct)
|
||||||
|
setattr(getattr(structs, struct), '_init', _init)
|
||||||
|
|
||||||
|
|
||||||
|
class AssimpLib(object):
|
||||||
|
"""
|
||||||
|
Assimp-Singleton
|
||||||
|
"""
|
||||||
|
load, release, dll = helper.search_library()
|
||||||
|
|
||||||
|
#the loader as singleton
|
||||||
|
_assimp_lib = AssimpLib()
|
||||||
|
|
||||||
|
def pythonize_assimp(type, obj, scene):
|
||||||
|
""" This method modify the Assimp data structures
|
||||||
|
to make them easier to work with in Python.
|
||||||
|
|
||||||
|
Supported operations:
|
||||||
|
- MESH: replace a list of mesh IDs by reference to these meshes
|
||||||
|
|
||||||
|
:param type: the type of modification to operate (cf above)
|
||||||
|
:param obj: the input object to modify
|
||||||
|
:param scene: a reference to the whole scene
|
||||||
|
"""
|
||||||
|
|
||||||
|
if type == "MESH":
|
||||||
|
meshes = []
|
||||||
|
for i in obj:
|
||||||
|
meshes.append(scene.meshes[i])
|
||||||
|
return meshes
|
||||||
|
|
||||||
|
|
||||||
|
def recur_pythonize(node, scene):
|
||||||
|
""" 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 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
|
||||||
|
model = _assimp_lib.load(filename, processing)
|
||||||
|
if not model:
|
||||||
|
#Uhhh, something went wrong!
|
||||||
|
raise AssimpError, ("could not import file: %s" % filename)
|
||||||
|
|
||||||
|
#logger.debug("Initializing recursively tuples")
|
||||||
|
#_init_tuples(model.contents)
|
||||||
|
|
||||||
|
logger.debug("Initializing recursively objects")
|
||||||
|
scene = model.contents._init()
|
||||||
|
|
||||||
|
recur_pythonize(scene.rootnode, scene)
|
||||||
|
|
||||||
|
return scene
|
||||||
|
|
||||||
|
def release(scene):
|
||||||
|
from ctypes import pointer
|
||||||
|
_assimp_lib.release(pointer(scene))
|
||||||
|
|
||||||
|
def _finalize_texture(tex, target):
|
||||||
|
setattr(target, "achformathint", tex.achFormatHint)
|
||||||
|
data = [make_tuple(getattr(tex, pcData)[i]) for i in xrange(tex.mWidth * tex.mHeight)]
|
||||||
|
setattr(target, "data", data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _finalize_mesh(mesh, target):
|
||||||
|
""" Building of meshes is a bit specific.
|
||||||
|
|
||||||
|
We override here the various datasets that can
|
||||||
|
not be process as regular fields.
|
||||||
|
|
||||||
|
For instance, the length of the normals array is
|
||||||
|
mNumVertices (no mNumNormals is available)
|
||||||
|
"""
|
||||||
|
nb_vertices = getattr(mesh, "mNumVertices")
|
||||||
|
|
||||||
|
def fill(name):
|
||||||
|
mAttr = getattr(mesh, name)
|
||||||
|
if mAttr:
|
||||||
|
data = [make_tuple(getattr(mesh, name)[i]) for i in xrange(nb_vertices)]
|
||||||
|
setattr(target, name[1:].lower(), data)
|
||||||
|
else:
|
||||||
|
setattr(target, name[1:].lower(), [])
|
||||||
|
|
||||||
|
def fillarray(name):
|
||||||
|
mAttr = getattr(mesh, name)
|
||||||
|
|
||||||
|
data = []
|
||||||
|
for index, mSubAttr in enumerate(mAttr):
|
||||||
|
if mSubAttr:
|
||||||
|
data.append([make_tuple(getattr(mesh, name)[index][i]) for i in xrange(nb_vertices)])
|
||||||
|
|
||||||
|
setattr(target, name[1:].lower(), data)
|
||||||
|
|
||||||
|
fill("mNormals")
|
||||||
|
fill("mTangents")
|
||||||
|
fill("mBitangents")
|
||||||
|
|
||||||
|
fillarray("mColors")
|
||||||
|
fillarray("mTextureCoords")
|
||||||
|
|
||||||
|
def _get_properties(properties, length):
|
||||||
|
"""
|
||||||
|
Convenience Function to get the material properties as a dict
|
||||||
|
and values in a python format.
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
#read all properties
|
||||||
|
for p in [properties[i] for i in xrange(length)]:
|
||||||
|
#the name
|
||||||
|
p = p.contents
|
||||||
|
key = str(p.mKey.data)
|
||||||
|
|
||||||
|
#the data
|
||||||
|
from ctypes import POINTER, cast, c_int, c_float, sizeof
|
||||||
|
if p.mType == 1:
|
||||||
|
arr = cast(p.mData, POINTER(c_float*(p.mDataLength/sizeof(c_float)) )).contents
|
||||||
|
value = [x for x in arr]
|
||||||
|
elif p.mType == 3: #string can't be an array
|
||||||
|
value = cast(p.mData, POINTER(structs.String)).contents.data
|
||||||
|
elif p.mType == 4:
|
||||||
|
arr = cast(p.mData, POINTER(c_int*(p.mDataLength/sizeof(c_int)) )).contents
|
||||||
|
value = [x for x in arr]
|
||||||
|
else:
|
||||||
|
value = p.mData[:p.mDataLength]
|
||||||
|
|
||||||
|
result[key] = value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def aiGetMaterialFloatArray(material, key):
|
||||||
|
AI_SUCCESS = 0
|
||||||
|
from ctypes import byref, pointer, cast, c_float, POINTER, sizeof, c_uint
|
||||||
|
out = structs.Color4D()
|
||||||
|
max = c_uint(sizeof(structs.Color4D))
|
||||||
|
r=_assimp_lib.dll.aiGetMaterialFloatArray(pointer(material),
|
||||||
|
key[0],
|
||||||
|
key[1],
|
||||||
|
key[2],
|
||||||
|
byref(out),
|
||||||
|
byref(max))
|
||||||
|
|
||||||
|
if (r != AI_SUCCESS):
|
||||||
|
raise AssimpError("aiGetMaterialFloatArray failed!")
|
||||||
|
|
||||||
|
out._init()
|
||||||
|
return [out[i] for i in xrange(max.value)]
|
||||||
|
|
||||||
|
def aiGetMaterialString(material, key):
|
||||||
|
AI_SUCCESS = 0
|
||||||
|
from ctypes import byref, pointer, cast, c_float, POINTER, sizeof, c_uint
|
||||||
|
out = structs.String()
|
||||||
|
r=_assimp_lib.dll.aiGetMaterialString(pointer(material),
|
||||||
|
key[0],
|
||||||
|
key[1],
|
||||||
|
key[2],
|
||||||
|
byref(out))
|
||||||
|
|
||||||
|
if (r != AI_SUCCESS):
|
||||||
|
raise AssimpError("aiGetMaterialString failed!")
|
||||||
|
|
||||||
|
return str(out.data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def decompose_matrix(matrix):
|
||||||
|
if not isinstance(matrix, structs.Matrix4x4):
|
||||||
|
raise AssimpError("pyassimp.decompose_matrix failed: Not a Matrix4x4!")
|
||||||
|
|
||||||
|
scaling = structs.Vector3D()
|
||||||
|
rotation = structs.Quaternion()
|
||||||
|
position = structs.Vector3D()
|
||||||
|
|
||||||
|
from ctypes import byref, pointer
|
||||||
|
_assimp_lib.dll.aiDecomposeMatrix(pointer(matrix), byref(scaling), byref(rotation), byref(position))
|
||||||
|
return scaling._init(), rotation._init(), position._init()
|
||||||
|
|
|
@ -1,276 +0,0 @@
|
||||||
#-*- coding: UTF-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
PyAssimp
|
|
||||||
|
|
||||||
This is the main-module of PyAssimp.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
if sys.version_info < (2,5):
|
|
||||||
raise 'pyassimp: need python 2.5 or newer'
|
|
||||||
|
|
||||||
import structs
|
|
||||||
import ctypes
|
|
||||||
import os
|
|
||||||
import helper
|
|
||||||
from errors import AssimpError
|
|
||||||
|
|
||||||
class aiArray:
|
|
||||||
"""
|
|
||||||
A python class to 'safely' access C arrays.
|
|
||||||
For m<Name> and mNum<Name> assimp class members.
|
|
||||||
"""
|
|
||||||
def __init__(self, instance, dataName, sizeName, i=None):
|
|
||||||
self.instance = instance
|
|
||||||
self.dataName = dataName
|
|
||||||
self.sizeName = sizeName
|
|
||||||
self.i = i
|
|
||||||
self.count = 0
|
|
||||||
|
|
||||||
def _GetSize(self):
|
|
||||||
return getattr(self.instance, self.sizeName)
|
|
||||||
|
|
||||||
def _GetData(self, index):
|
|
||||||
if self.i != None:
|
|
||||||
if not bool(getattr(self.instance, self.dataName)[self.i]):
|
|
||||||
return None
|
|
||||||
item = getattr(self.instance, self.dataName)[self.i][index]
|
|
||||||
else:
|
|
||||||
item = getattr(self.instance, self.dataName)[index]
|
|
||||||
if hasattr(item, 'contents'):
|
|
||||||
return item.contents._init()
|
|
||||||
elif hasattr(item, '_init'):
|
|
||||||
return item._init()
|
|
||||||
else:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def next(self):
|
|
||||||
if self.count >= self._GetSize():
|
|
||||||
self.count = 0
|
|
||||||
raise StopIteration
|
|
||||||
else:
|
|
||||||
c = self.count
|
|
||||||
self.count += 1
|
|
||||||
return self._GetData(c)
|
|
||||||
|
|
||||||
def __getitem__(self, index):
|
|
||||||
if isinstance(index, slice):
|
|
||||||
indices = index.indices(len(self))
|
|
||||||
return [self.__getitem__(i) for i in range(*indices)]
|
|
||||||
|
|
||||||
if index < 0 or index >= self._GetSize():
|
|
||||||
raise IndexError("aiArray index out of range")
|
|
||||||
return self._GetData(index)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return int(self._GetSize())
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return str([x for x in self])
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return str([x for x in self])
|
|
||||||
|
|
||||||
class aiTuple:
|
|
||||||
"""
|
|
||||||
A python class to 'safely' access C structs in a python tuple fashion.
|
|
||||||
For C structs like vectors, matrices, colors, ...
|
|
||||||
"""
|
|
||||||
def __init__(self, instance):
|
|
||||||
self.instance = instance
|
|
||||||
self.count = 0
|
|
||||||
|
|
||||||
def _GetSize(self):
|
|
||||||
return len(self.instance._fields_)
|
|
||||||
|
|
||||||
def _GetData(self, index):
|
|
||||||
return getattr(self.instance, self.instance._fields_[index][0])
|
|
||||||
|
|
||||||
def next(self):
|
|
||||||
if self.count >= self._GetSize():
|
|
||||||
self.count = 0
|
|
||||||
raise StopIteration
|
|
||||||
else:
|
|
||||||
c = self.count
|
|
||||||
self.count += 1
|
|
||||||
return self._GetData(c)
|
|
||||||
|
|
||||||
def __getitem__(self, index):
|
|
||||||
if isinstance(index, slice):
|
|
||||||
indices = index.indices(len(self))
|
|
||||||
return [self.__getitem__(i) for i in range(*indices)]
|
|
||||||
|
|
||||||
if index < 0 or index >= self._GetSize():
|
|
||||||
raise IndexError("aiTuple index out of range")
|
|
||||||
return self._GetData(index)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return int(self._GetSize())
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return str([x for x in self])
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return str([x for x in self])
|
|
||||||
|
|
||||||
def _init(self):
|
|
||||||
"""
|
|
||||||
Custom initialize() for C structs, adds safely accessable member functionality.
|
|
||||||
"""
|
|
||||||
if hasattr(self, '_is_init'):
|
|
||||||
return self
|
|
||||||
self._is_init = True
|
|
||||||
|
|
||||||
if str(self.__class__.__name__) == "MaterialProperty":
|
|
||||||
self.mKey._init()
|
|
||||||
|
|
||||||
for m in self.__class__.__dict__.keys():
|
|
||||||
if m.startswith('mNum'):
|
|
||||||
name = m.split('mNum')[1]
|
|
||||||
if 'm'+name in self.__class__.__dict__.keys():
|
|
||||||
setattr(self.__class__, name.lower(), aiArray(self, 'm'+name , m))
|
|
||||||
if name.lower() == "vertices":
|
|
||||||
setattr(self.__class__, "normals", aiArray(self, 'mNormals' , m))
|
|
||||||
setattr(self.__class__, "tangents", aiArray(self, 'mTangents' , m))
|
|
||||||
setattr(self.__class__, "bitangets", aiArray(self, 'mBitangents' , m))
|
|
||||||
setattr(self.__class__, "colors", [aiArray(self, 'mColors' , m, o) for o in xrange(len(self.mColors))])
|
|
||||||
setattr(self.__class__, "texcoords", [aiArray(self, 'mTextureCoords' , m, o) for o in xrange(len(self.mColors))])
|
|
||||||
|
|
||||||
elif m == "x" or m == "a1" or m == "b": # Vector, matrix, quat, color
|
|
||||||
self._tuple = aiTuple(self)
|
|
||||||
setattr(self.__class__, '__getitem__', lambda x, y: x._tuple.__getitem__(y))
|
|
||||||
setattr(self.__class__, '__iter__', lambda x: x._tuple)
|
|
||||||
setattr(self.__class__, 'next', lambda x: x._tuple.next)
|
|
||||||
setattr(self.__class__, '__repr__', lambda x: str([c for c in x]))
|
|
||||||
break
|
|
||||||
elif m == "data": #String
|
|
||||||
setattr(self.__class__, '__repr__', lambda x: str(x.data))
|
|
||||||
setattr(self.__class__, '__str__', lambda x: str(x.data))
|
|
||||||
break
|
|
||||||
|
|
||||||
if hasattr(getattr(self, m), '_init'):
|
|
||||||
getattr(self, m)._init()
|
|
||||||
|
|
||||||
return self
|
|
||||||
|
|
||||||
"""
|
|
||||||
Python magic to add the _init() function to all C struct classes.
|
|
||||||
"""
|
|
||||||
for struct in dir(structs):
|
|
||||||
if not (struct.startswith('_') or struct.startswith('c_') or struct == "Structure" or struct == "POINTER") and not isinstance(getattr(structs, struct),int):
|
|
||||||
setattr(getattr(structs, struct), '_init', _init)
|
|
||||||
|
|
||||||
|
|
||||||
class AssimpLib(object):
|
|
||||||
"""
|
|
||||||
Assimp-Singleton
|
|
||||||
"""
|
|
||||||
load, release, dll = helper.search_library()
|
|
||||||
|
|
||||||
#the loader as singleton
|
|
||||||
_assimp_lib = AssimpLib()
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
model = _assimp_lib.load(filename, processing)
|
|
||||||
if not model:
|
|
||||||
#Uhhh, something went wrong!
|
|
||||||
raise AssimpError, ("could not import file: %s" % filename)
|
|
||||||
|
|
||||||
return model.contents._init()
|
|
||||||
|
|
||||||
def release(scene):
|
|
||||||
from ctypes import pointer
|
|
||||||
_assimp_lib.release(pointer(scene))
|
|
||||||
|
|
||||||
def aiGetMaterialFloatArray(material, key):
|
|
||||||
AI_SUCCESS = 0
|
|
||||||
from ctypes import byref, pointer, cast, c_float, POINTER, sizeof, c_uint
|
|
||||||
out = structs.Color4D()
|
|
||||||
max = c_uint(sizeof(structs.Color4D))
|
|
||||||
r=_assimp_lib.dll.aiGetMaterialFloatArray(pointer(material),
|
|
||||||
key[0],
|
|
||||||
key[1],
|
|
||||||
key[2],
|
|
||||||
byref(out),
|
|
||||||
byref(max))
|
|
||||||
|
|
||||||
if (r != AI_SUCCESS):
|
|
||||||
raise AssimpError("aiGetMaterialFloatArray failed!")
|
|
||||||
|
|
||||||
out._init()
|
|
||||||
return [out[i] for i in xrange(max.value)]
|
|
||||||
|
|
||||||
def aiGetMaterialString(material, key):
|
|
||||||
AI_SUCCESS = 0
|
|
||||||
from ctypes import byref, pointer, cast, c_float, POINTER, sizeof, c_uint
|
|
||||||
out = structs.String()
|
|
||||||
r=_assimp_lib.dll.aiGetMaterialString(pointer(material),
|
|
||||||
key[0],
|
|
||||||
key[1],
|
|
||||||
key[2],
|
|
||||||
byref(out))
|
|
||||||
|
|
||||||
if (r != AI_SUCCESS):
|
|
||||||
raise AssimpError("aiGetMaterialString failed!")
|
|
||||||
|
|
||||||
return str(out.data)
|
|
||||||
|
|
||||||
def GetMaterialProperties(material):
|
|
||||||
"""
|
|
||||||
Convenience Function to get the material properties as a dict
|
|
||||||
and values in a python format.
|
|
||||||
"""
|
|
||||||
result = {}
|
|
||||||
#read all properties
|
|
||||||
for p in material.properties:
|
|
||||||
#the name
|
|
||||||
key = p.mKey.data
|
|
||||||
|
|
||||||
#the data
|
|
||||||
from ctypes import POINTER, cast, c_int, c_float, sizeof
|
|
||||||
if p.mType == 1:
|
|
||||||
arr = cast(p.mData, POINTER(c_float*(p.mDataLength/sizeof(c_float)) )).contents
|
|
||||||
value = [x for x in arr]
|
|
||||||
elif p.mType == 3: #string can't be an array
|
|
||||||
value = cast(p.mData, POINTER(structs.String)).contents.data
|
|
||||||
elif p.mType == 4:
|
|
||||||
arr = cast(p.mData, POINTER(c_int*(p.mDataLength/sizeof(c_int)) )).contents
|
|
||||||
value = [x for x in arr]
|
|
||||||
else:
|
|
||||||
value = p.mData[:p.mDataLength]
|
|
||||||
|
|
||||||
result[key] = value
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def aiDecomposeMatrix(matrix):
|
|
||||||
if not isinstance(matrix, structs.Matrix4x4):
|
|
||||||
raise AssimpError("aiDecomposeMatrix failed: Not a aiMatrix4x4!")
|
|
||||||
|
|
||||||
scaling = structs.Vector3D()
|
|
||||||
rotation = structs.Quaternion()
|
|
||||||
position = structs.Vector3D()
|
|
||||||
|
|
||||||
from ctypes import byref, pointer
|
|
||||||
_assimp_lib.dll.aiDecomposeMatrix(pointer(matrix), byref(scaling), byref(rotation), byref(position))
|
|
||||||
return scaling._init(), rotation._init(), position._init()
|
|
Loading…
Reference in New Issue