Merge pull request #2114 from wmatyjewicz/pyassimp-metadata-access

Native Python interface for accessing metadata in PyAssimp
pull/2117/head
Kim Kulling 2018-09-02 23:15:57 +02:00 committed by GitHub
commit c5eb39cb7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 20 deletions

View File

@ -66,6 +66,13 @@ def make_tuple(ai_obj, type = None):
return res return res
# Returns unicode object for Python 2, and str object for Python 3.
def _convert_assimp_string(assimp_string):
try:
return unicode(assimp_string.data, errors='ignore')
except:
return str(assimp_string.data, errors='ignore')
# It is faster and more correct to have an init function for each assimp class # It is faster and more correct to have an init function for each assimp class
def _init_face(aiFace): def _init_face(aiFace):
aiFace.indices = [aiFace.mIndices[i] for i in range(aiFace.mNumIndices)] aiFace.indices = [aiFace.mIndices[i] for i in range(aiFace.mNumIndices)]
@ -118,14 +125,9 @@ def _init(self, target = None, parent = None):
continue continue
if m == 'mName': if m == 'mName':
obj = self.mName target.name = str(_convert_assimp_string(self.mName))
try: target.__class__.__repr__ = lambda x: str(x.__class__) + "(" + getattr(x, 'name','') + ")"
uni = unicode(obj.data, errors='ignore') target.__class__.__str__ = lambda x: getattr(x, 'name', '')
except:
uni = str(obj.data, errors='ignore')
target.name = str( uni )
target.__class__.__repr__ = lambda x: str(x.__class__) + "(" + getattr(x, 'name','') + ")"
target.__class__.__str__ = lambda x: getattr(x, 'name', '')
continue continue
name = m[1:].lower() name = m[1:].lower()
@ -220,6 +222,9 @@ def _init(self, target = None, parent = None):
if isinstance(self, structs.Texture): if isinstance(self, structs.Texture):
_finalize_texture(self, target) _finalize_texture(self, target)
if isinstance(self, structs.Metadata):
_finalize_metadata(self, target)
return self return self
@ -412,6 +417,43 @@ def _finalize_mesh(mesh, target):
faces = [f.indices for f in target.faces] faces = [f.indices for f in target.faces]
setattr(target, 'faces', faces) setattr(target, 'faces', faces)
def _init_metadata_entry(entry):
from ctypes import POINTER, c_bool, c_int32, c_uint64, c_float, c_double, cast
entry.type = entry.mType
if entry.type == structs.MetadataEntry.AI_BOOL:
entry.data = cast(entry.mData, POINTER(c_bool)).contents.value
elif entry.type == structs.MetadataEntry.AI_INT32:
entry.data = cast(entry.mData, POINTER(c_int32)).contents.value
elif entry.type == structs.MetadataEntry.AI_UINT64:
entry.data = cast(entry.mData, POINTER(c_uint64)).contents.value
elif entry.type == structs.MetadataEntry.AI_FLOAT:
entry.data = cast(entry.mData, POINTER(c_float)).contents.value
elif entry.type == structs.MetadataEntry.AI_DOUBLE:
entry.data = cast(entry.mData, POINTER(c_double)).contents.value
elif entry.type == structs.MetadataEntry.AI_AISTRING:
assimp_string = cast(entry.mData, POINTER(structs.String)).contents
entry.data = _convert_assimp_string(assimp_string)
elif entry.type == structs.MetadataEntry.AI_AIVECTOR3D:
assimp_vector = cast(entry.mData, POINTER(structs.Vector3D)).contents
entry.data = make_tuple(assimp_vector)
return entry
def _finalize_metadata(metadata, target):
""" Building the metadata object is a bit specific.
Firstly, there are two separate arrays: one with metadata keys and one
with metadata values, and there are no corresponding mNum* attributes,
so the C arrays are not converted to Python arrays using the generic
code in the _init function.
Secondly, a metadata entry value has to be cast according to declared
metadata entry type.
"""
length = metadata.mNumProperties
setattr(target, 'keys', [str(_convert_assimp_string(metadata.mKeys[i])) for i in range(length)])
setattr(target, 'values', [_init_metadata_entry(metadata.mValues[i]) for i in range(length)])
class PropertyGetter(dict): class PropertyGetter(dict):
def __getitem__(self, key): def __getitem__(self, key):
@ -443,11 +485,8 @@ def _get_properties(properties, length):
for p in [properties[i] for i in range(length)]: for p in [properties[i] for i in range(length)]:
#the name #the name
p = p.contents p = p.contents
try: key = str(_convert_assimp_string(p.mKey))
uni = unicode(p.mKey.data, errors='ignore') key = (key.split('.')[1], p.mSemantic)
except:
uni = str(p.mKey.data, errors='ignore')
key = (str(uni).split('.')[1], p.mSemantic)
#the data #the data
from ctypes import POINTER, cast, c_int, c_float, sizeof from ctypes import POINTER, cast, c_int, c_float, sizeof
@ -455,11 +494,7 @@ def _get_properties(properties, length):
arr = cast(p.mData, POINTER(c_float * int(p.mDataLength/sizeof(c_float)) )).contents arr = cast(p.mData, POINTER(c_float * int(p.mDataLength/sizeof(c_float)) )).contents
value = [x for x in arr] value = [x for x in arr]
elif p.mType == 3: #string can't be an array elif p.mType == 3: #string can't be an array
try: value = _convert_assimp_string(cast(p.mData, POINTER(structs.MaterialPropertyString)).contents)
uni = unicode(cast(p.mData, POINTER(structs.MaterialPropertyString)).contents.data, errors='ignore')
except:
uni = str(cast(p.mData, POINTER(structs.MaterialPropertyString)).contents.data, errors='ignore')
value = uni
elif p.mType == 4: elif p.mType == 4:
arr = cast(p.mData, POINTER(c_int * int(p.mDataLength/sizeof(c_int)) )).contents arr = cast(p.mData, POINTER(c_int * int(p.mDataLength/sizeof(c_int)) )).contents

View File

@ -291,7 +291,7 @@ Node._fields_ = [
# Metadata associated with this node or NULL if there is no metadata. # Metadata associated with this node or NULL if there is no metadata.
# Whether any metadata is generated depends on the source file format. # Whether any metadata is generated depends on the source file format.
("mMetadata", POINTER(POINTER(Metadata))), ("mMetadata", POINTER(Metadata)),
] ]
class Light(Structure): class Light(Structure):
@ -939,7 +939,7 @@ class Scene(Structure):
# This data contains global metadata which belongs to the scene like # This data contains global metadata which belongs to the scene like
# unit-conversions, versions, vendors or other model-specific data. This # unit-conversions, versions, vendors or other model-specific data. This
# can be used to store format-specific metadata as well. # can be used to store format-specific metadata as well.
("mMetadata", POINTER(POINTER(Metadata))), ("mMetadata", POINTER(Metadata)),
] ]
assimp_structs_as_tuple = (Matrix4x4, assimp_structs_as_tuple = (Matrix4x4,