Browse Source

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

Native Python interface for accessing metadata in PyAssimp
Kim Kulling 7 năm trước cách đây
mục cha
commit
c5eb39cb7b

+ 53 - 18
port/PyAssimp/pyassimp/core.py

@@ -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
-            try:
-                uni = unicode(obj.data, errors='ignore')
-            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', '')
+            target.name = str(_convert_assimp_string(self.mName))
+            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:
-            uni = unicode(p.mKey.data, errors='ignore')
-        except:
-            uni = str(p.mKey.data, errors='ignore')
-        key = (str(uni).split('.')[1], p.mSemantic)
+        key = str(_convert_assimp_string(p.mKey))
+        key = (key.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:
-                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
+            value = _convert_assimp_string(cast(p.mData, POINTER(structs.MaterialPropertyString)).contents)
 
 
         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

+ 2 - 2
port/PyAssimp/pyassimp/structs.py

@@ -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,