[pyassimp] Support Python3
This include the renaming of __init__.py in core.py to solve tricky imports issues sample.py runs correctly, except that the ctype structure returned is not complete. The issue is very similar to the one we have with 32bits/64bits issues Removed the PyAssimp3 subproject project. To maintain source compatibility between py2 and py3, python >= 2.6 is now requiredpull/8/merge^2
parent
a6a30233e1
commit
18825179d0
|
@ -2,9 +2,9 @@ PyAssimp Readme
|
||||||
===============
|
===============
|
||||||
|
|
||||||
-- a simple Python wrapper for Assimp using ctypes to access
|
-- a simple Python wrapper for Assimp using ctypes to access
|
||||||
the library. Tested for Python 2.6. Known not to work with
|
the library. Requires Python >= 2.6.
|
||||||
Python 2.4.
|
|
||||||
|
|
||||||
|
Python 3 support is mostly here, but not well tested.
|
||||||
|
|
||||||
Note that pyassimp is not complete. Many ASSIMP features are missing. In
|
Note that pyassimp is not complete. Many ASSIMP features are missing. In
|
||||||
particular, only loading of models is currently supported (no export).
|
particular, only loading of models is currently supported (no export).
|
||||||
|
@ -25,8 +25,8 @@ substituted by assertions ...):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
||||||
import pyassimp
|
from pyassimp.core import *
|
||||||
scene = pyassimp.load('hello.3ds')
|
scene = load('hello.3ds')
|
||||||
|
|
||||||
assert len(scene.meshes)
|
assert len(scene.meshes)
|
||||||
mesh = scene.meshes[0]
|
mesh = scene.meshes[0]
|
||||||
|
@ -35,7 +35,7 @@ assert len(mesh.vertices)
|
||||||
print(mesh.vertices[0])
|
print(mesh.vertices[0])
|
||||||
|
|
||||||
# don't forget this one, or you will leak!
|
# don't forget this one, or you will leak!
|
||||||
pyassimp.release(scene)
|
release(scene)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -44,13 +44,13 @@ scene:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
||||||
import pyassimp
|
from pyassimp.core import *
|
||||||
scene = pyassimp.load('hello.3ds')
|
scene = load('hello.3ds')
|
||||||
|
|
||||||
for c in scene.rootnode.children:
|
for c in scene.rootnode.children:
|
||||||
print(str(c))
|
print(str(c))
|
||||||
|
|
||||||
pyassimp.release(scene)
|
release(scene)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,408 +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 ctypes
|
|
||||||
import os
|
|
||||||
import helper
|
|
||||||
from errors import AssimpError
|
|
||||||
|
|
||||||
import structs
|
|
||||||
|
|
||||||
import logging; logger = logging.getLogger("pyassimp")
|
|
||||||
|
|
||||||
import numpy
|
|
||||||
|
|
||||||
#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, type = None):
|
|
||||||
res = None
|
|
||||||
|
|
||||||
if isinstance(ai_obj, structs.Matrix4x4):
|
|
||||||
res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_]).reshape((4,4))
|
|
||||||
#import pdb;pdb.set_trace()
|
|
||||||
elif isinstance(ai_obj, structs.Matrix3x3):
|
|
||||||
res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_]).reshape((3,3))
|
|
||||||
else:
|
|
||||||
res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_])
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
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, numpy.array([make_tuple(obj[i]) for i in xrange(length)], dtype=numpy.float32))
|
|
||||||
|
|
||||||
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
|
|
||||||
- ADDTRANSFORMATION: add a reference to an object's transformation taken from their associated node.
|
|
||||||
|
|
||||||
: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
|
|
||||||
|
|
||||||
if type == "ADDTRANSFORMATION":
|
|
||||||
|
|
||||||
def getnode(node, name):
|
|
||||||
if node.name == name: return node
|
|
||||||
for child in node.children:
|
|
||||||
n = getnode(child, name)
|
|
||||||
if n: return n
|
|
||||||
|
|
||||||
node = getnode(scene.rootnode, obj.name)
|
|
||||||
if not node:
|
|
||||||
raise AssimpError("Object " + str(obj) + " has no associated node!")
|
|
||||||
setattr(obj, "transformation", node.transformation)
|
|
||||||
|
|
||||||
|
|
||||||
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 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
|
|
||||||
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 = numpy.array([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 = numpy.array([make_tuple(getattr(mesh, name)[i]) for i in xrange(nb_vertices)], dtype=numpy.float32)
|
|
||||||
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(), numpy.array(data, dtype=numpy.float32))
|
|
||||||
|
|
||||||
fill("mNormals")
|
|
||||||
fill("mTangents")
|
|
||||||
fill("mBitangents")
|
|
||||||
|
|
||||||
fillarray("mColors")
|
|
||||||
fillarray("mTextureCoords")
|
|
||||||
|
|
||||||
# prepare faces
|
|
||||||
faces = numpy.array([f.indices for f in target.faces], dtype=numpy.int32)
|
|
||||||
setattr(target, 'faces', faces)
|
|
||||||
|
|
||||||
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 = numpy.array([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 = numpy.array([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()
|
|
|
@ -0,0 +1,409 @@
|
||||||
|
#-*- coding: UTF-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
PyAssimp
|
||||||
|
|
||||||
|
This is the main-module of PyAssimp.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
if sys.version_info < (2,6):
|
||||||
|
raise 'pyassimp: need python 2.6 or newer'
|
||||||
|
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
import logging; logger = logging.getLogger("pyassimp")
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
|
||||||
|
from . import structs
|
||||||
|
from .errors import AssimpError
|
||||||
|
from . import helper
|
||||||
|
|
||||||
|
|
||||||
|
#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, type = None):
|
||||||
|
res = None
|
||||||
|
|
||||||
|
if isinstance(ai_obj, structs.Matrix4x4):
|
||||||
|
res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_]).reshape((4,4))
|
||||||
|
#import pdb;pdb.set_trace()
|
||||||
|
elif isinstance(ai_obj, structs.Matrix3x3):
|
||||||
|
res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_]).reshape((3,3))
|
||||||
|
else:
|
||||||
|
res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_])
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
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, numpy.array([make_tuple(obj[i]) for i in range(length)], dtype=numpy.float32))
|
||||||
|
|
||||||
|
logger.debug(str(self) + ": Added a list data (type "+ str(type(obj)) + ") as self." + name)
|
||||||
|
|
||||||
|
else:
|
||||||
|
setattr(target, name, [obj[i] for i in range(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
|
||||||
|
- ADDTRANSFORMATION: add a reference to an object's transformation taken from their associated node.
|
||||||
|
|
||||||
|
: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
|
||||||
|
|
||||||
|
if type == "ADDTRANSFORMATION":
|
||||||
|
|
||||||
|
def getnode(node, name):
|
||||||
|
if node.name == name: return node
|
||||||
|
for child in node.children:
|
||||||
|
n = getnode(child, name)
|
||||||
|
if n: return n
|
||||||
|
|
||||||
|
node = getnode(scene.rootnode, obj.name)
|
||||||
|
if not node:
|
||||||
|
raise AssimpError("Object " + str(obj) + " has no associated node!")
|
||||||
|
setattr(obj, "transformation", node.transformation)
|
||||||
|
|
||||||
|
|
||||||
|
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 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)
|
||||||
|
if not model:
|
||||||
|
#Uhhh, something went wrong!
|
||||||
|
raise AssimpError("could not import file: %s" % filename)
|
||||||
|
|
||||||
|
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 = numpy.array([make_tuple(getattr(tex, pcData)[i]) for i in range(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 = numpy.array([make_tuple(getattr(mesh, name)[i]) for i in range(nb_vertices)], dtype=numpy.float32)
|
||||||
|
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 range(nb_vertices)])
|
||||||
|
|
||||||
|
setattr(target, name[1:].lower(), numpy.array(data, dtype=numpy.float32))
|
||||||
|
|
||||||
|
fill("mNormals")
|
||||||
|
fill("mTangents")
|
||||||
|
fill("mBitangents")
|
||||||
|
|
||||||
|
fillarray("mColors")
|
||||||
|
fillarray("mTextureCoords")
|
||||||
|
|
||||||
|
# prepare faces
|
||||||
|
faces = numpy.array([f.indices for f in target.faces], dtype=numpy.int32)
|
||||||
|
setattr(target, 'faces', faces)
|
||||||
|
|
||||||
|
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 range(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 * int(p.mDataLength/sizeof(c_float)) )).contents
|
||||||
|
value = numpy.array([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 * int(p.mDataLength/sizeof(c_int)) )).contents
|
||||||
|
value = numpy.array([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 range(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()
|
|
@ -6,19 +6,20 @@ Some fancy helper functions.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import ctypes
|
import ctypes
|
||||||
import structs
|
|
||||||
import operator
|
|
||||||
|
|
||||||
from errors import AssimpError
|
|
||||||
from ctypes import POINTER
|
from ctypes import POINTER
|
||||||
|
import operator
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
|
import logging;logger = logging.getLogger("pyassimp")
|
||||||
|
|
||||||
|
from .errors import AssimpError
|
||||||
|
|
||||||
additional_dirs, ext_whitelist = [],[]
|
additional_dirs, ext_whitelist = [],[]
|
||||||
|
|
||||||
# populate search directories and lists of allowed file extensions
|
# populate search directories and lists of allowed file extensions
|
||||||
# depending on the platform we're running on.
|
# depending on the platform we're running on.
|
||||||
if os.name=='posix':
|
if os.name=='posix':
|
||||||
|
additional_dirs.append('/home/skadge/openrobots/lib/')
|
||||||
additional_dirs.append('/usr/lib/')
|
additional_dirs.append('/usr/lib/')
|
||||||
additional_dirs.append('/usr/local/lib/')
|
additional_dirs.append('/usr/local/lib/')
|
||||||
|
|
||||||
|
@ -89,7 +90,8 @@ def try_load_functions(library,dll,candidates):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
#Library found!
|
#Library found!
|
||||||
load.restype = POINTER(structs.Scene)
|
from .structs import Scene
|
||||||
|
load.restype = POINTER(Scene)
|
||||||
|
|
||||||
candidates.append((library, load, release, dll))
|
candidates.append((library, load, release, dll))
|
||||||
|
|
||||||
|
@ -123,10 +125,11 @@ def search_library():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
library = os.path.join(curfolder, filename)
|
library = os.path.join(curfolder, filename)
|
||||||
#print 'Try ',library
|
logger.debug('Try ' + library)
|
||||||
try:
|
try:
|
||||||
dll = ctypes.cdll.LoadLibrary(library)
|
dll = ctypes.cdll.LoadLibrary(library)
|
||||||
except:
|
except Exception as e:
|
||||||
|
logger.warning(str(e))
|
||||||
# OK, this except is evil. But different OSs will throw different
|
# OK, this except is evil. But different OSs will throw different
|
||||||
# errors. So just ignore any errors.
|
# errors. So just ignore any errors.
|
||||||
continue
|
continue
|
||||||
|
@ -135,12 +138,12 @@ def search_library():
|
||||||
|
|
||||||
if not candidates:
|
if not candidates:
|
||||||
# no library found
|
# no library found
|
||||||
raise AssimpError, "assimp library not found"
|
raise AssimpError("assimp library not found")
|
||||||
else:
|
else:
|
||||||
# get the newest library
|
# get the newest library
|
||||||
candidates = map(lambda x: (os.lstat(x[0])[-2], x), candidates)
|
candidates = map(lambda x: (os.lstat(x[0])[-2], x), candidates)
|
||||||
res = max(candidates, key=operator.itemgetter(0))[1]
|
res = max(candidates, key=operator.itemgetter(0))[1]
|
||||||
#print 'Taking ',res[0]
|
logger.debug('Using assimp library located at ' + res[0])
|
||||||
|
|
||||||
# XXX: if there are 1000 dll/so files containing 'assimp'
|
# XXX: if there are 1000 dll/so files containing 'assimp'
|
||||||
# in their name, do we have all of them in our address
|
# in their name, do we have all of them in our address
|
||||||
|
|
|
@ -25,7 +25,7 @@ logging.basicConfig(level=logging.INFO)
|
||||||
import math
|
import math
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
import pyassimp
|
from pyassimp import core as pyassimp
|
||||||
from pyassimp.postprocess import *
|
from pyassimp.postprocess import *
|
||||||
from pyassimp.helper import *
|
from pyassimp.helper import *
|
||||||
|
|
||||||
|
@ -35,28 +35,28 @@ height = 400
|
||||||
width = 708
|
width = 708
|
||||||
|
|
||||||
zup = numpy.matrix([[1, 0, 0, 0],
|
zup = numpy.matrix([[1, 0, 0, 0],
|
||||||
[0, 1, 0, 0],
|
[0, 1, 0, 0],
|
||||||
[0, 0, 1, 0],
|
[0, 0, 1, 0],
|
||||||
[0, 0, 0, 1]])
|
[0, 0, 0, 1]])
|
||||||
|
|
||||||
class GLRenderer():
|
class GLRenderer():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.scene = None
|
self.scene = None
|
||||||
|
|
||||||
self.auto_rotate = False
|
self.auto_rotate = False
|
||||||
self.viewangle_h = self.viewangle_v = 0.0
|
self.viewangle_h = self.viewangle_v = 0.0
|
||||||
self.current_cam_index = 0
|
self.current_cam_index = 0
|
||||||
|
|
||||||
# for FPS calculation
|
# for FPS calculation
|
||||||
self.prev_time = 0
|
self.prev_time = 0
|
||||||
self.prev_fps_time = 0
|
self.prev_fps_time = 0
|
||||||
self.frames = 0
|
self.frames = 0
|
||||||
|
|
||||||
def onkeypress(self, key, x, y):
|
def onkeypress(self, key, x, y):
|
||||||
if key == 'a':
|
if key == 'a':
|
||||||
self.auto_rotate = not self.auto_rotate
|
self.auto_rotate = not self.auto_rotate
|
||||||
self.viewangle_h = 0.
|
self.viewangle_h = 0.
|
||||||
self.viewangle_v = 0.
|
self.viewangle_v = 0.
|
||||||
if key == 'z':
|
if key == 'z':
|
||||||
self.viewangle_v = -0.04
|
self.viewangle_v = -0.04
|
||||||
if key == 's':
|
if key == 's':
|
||||||
|
|
|
@ -6,14 +6,9 @@ This module demonstrates the functionality of PyAssimp.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
import pyassimp
|
from pyassimp import core as pyassimp
|
||||||
import os, sys
|
import os, sys
|
||||||
|
|
||||||
#get a model out of assimp's test-data if none is provided on the command line
|
|
||||||
DEFAULT_MODEL = os.path.join(os.path.dirname(__file__),
|
|
||||||
"..", "..",
|
|
||||||
"test", "models", "MDL", "MDL3 (3DGS A4)", "minigun.MDL")
|
|
||||||
|
|
||||||
def recur_node(node,level = 0):
|
def recur_node(node,level = 0):
|
||||||
print(" " + "\t" * level + "- " + str(node))
|
print(" " + "\t" * level + "- " + str(node))
|
||||||
for child in node.children:
|
for child in node.children:
|
||||||
|
@ -21,61 +16,61 @@ def recur_node(node,level = 0):
|
||||||
|
|
||||||
|
|
||||||
def main(filename=None):
|
def main(filename=None):
|
||||||
filename = filename or DEFAULT_MODEL
|
|
||||||
scene = pyassimp.load(filename)
|
scene = pyassimp.load(filename)
|
||||||
|
|
||||||
#the model we load
|
#the model we load
|
||||||
print "MODEL:", filename
|
print("MODEL:" + filename)
|
||||||
print
|
print
|
||||||
|
|
||||||
#write some statistics
|
#write some statistics
|
||||||
print "SCENE:"
|
print("SCENE:")
|
||||||
print " meshes:", len(scene.meshes)
|
print(" meshes:" + str(len(scene.meshes)))
|
||||||
print " materials:", len(scene.materials)
|
print(" materials:" + str(len(scene.materials)))
|
||||||
print " textures:", len(scene.textures)
|
print(" textures:" + str(len(scene.textures)))
|
||||||
print
|
print
|
||||||
|
|
||||||
print "NODES:"
|
print("NODES:")
|
||||||
recur_node(scene.rootnode)
|
recur_node(scene.rootnode)
|
||||||
|
|
||||||
print
|
print
|
||||||
print "MESHES:"
|
print("MESHES:")
|
||||||
for index, mesh in enumerate(scene.meshes):
|
for index, mesh in enumerate(scene.meshes):
|
||||||
print " MESH", index+1
|
print(" MESH" + str(index+1))
|
||||||
print " material id:", mesh.materialindex+1
|
print(" material id:" + str(mesh.materialindex+1))
|
||||||
print " vertices:", len(mesh.vertices)
|
print(" vertices:" + str(len(mesh.vertices)))
|
||||||
print " first 3 verts:\n", mesh.vertices[:3]
|
print(" first 3 verts:\n" + str(mesh.vertices[:3]))
|
||||||
if mesh.normals.any():
|
if mesh.normals.any():
|
||||||
print " first 3 normals:\n", mesh.normals[:3]
|
print(" first 3 normals:\n" + str(mesh.normals[:3]))
|
||||||
else:
|
else:
|
||||||
print " no normals"
|
print(" no normals")
|
||||||
print " colors:", len(mesh.colors)
|
print(" colors:" + str(len(mesh.colors)))
|
||||||
tcs = mesh.texturecoords
|
tcs = mesh.texturecoords
|
||||||
if tcs:
|
if tcs:
|
||||||
for index, tc in enumerate(tcs):
|
for index, tc in enumerate(tcs):
|
||||||
print " texture-coords "+ str(index) + ":", len(tcs[index]), "first3:", tcs[index][:3]
|
print(" texture-coords "+ str(index) + ":" + str(len(tcs[index])) + "first3:" + str(tcs[index][:3]))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print " no texture coordinates"
|
print(" no texture coordinates")
|
||||||
print " uv-component-count:", len(mesh.numuvcomponents)
|
print(" uv-component-count:" + str(len(mesh.numuvcomponents)))
|
||||||
print " faces:", len(mesh.faces), "first:\n", mesh.faces[:3]
|
print(" faces:" + str(len(mesh.faces)) + " -> first:\n" + str(mesh.faces[:3]))
|
||||||
print " bones:", len(mesh.bones), "first:", [str(b) for b in mesh.bones[:3]]
|
print(" bones:" + str(len(mesh.bones)) + " -> first:" + str([str(b) for b in mesh.bones[:3]]))
|
||||||
print
|
print
|
||||||
|
|
||||||
print "MATERIALS:"
|
print("MATERIALS:")
|
||||||
for index, material in enumerate(scene.materials):
|
for index, material in enumerate(scene.materials):
|
||||||
print(" MATERIAL (id:" + str(index+1) + ")")
|
print(" MATERIAL (id:" + str(index+1) + ")")
|
||||||
for key, value in material.properties.items():
|
for key, value in material.properties.items():
|
||||||
print " %s: %s" % (key, value)
|
print(" %s: %s" % (key, value))
|
||||||
print
|
print
|
||||||
|
|
||||||
print "TEXTURES:"
|
print("TEXTURES:")
|
||||||
for index, texture in enumerate(scene.textures):
|
for index, texture in enumerate(scene.textures):
|
||||||
print " TEXTURE", index+1
|
print(" TEXTURE" + str(index+1))
|
||||||
print " width:", texture.width
|
print(" width:" + str(texture.width))
|
||||||
print " height:", texture.height
|
print(" height:" + str(texture.height))
|
||||||
print " hint:", texture.achformathint
|
print(" hint:" + str(texture.achformathint))
|
||||||
print " data (size):", len(texture.data)
|
print(" data (size):" + str(len(texture.data)))
|
||||||
|
|
||||||
# Finally release the model
|
# Finally release the model
|
||||||
pyassimp.release(scene)
|
pyassimp.release(scene)
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
PyAssimp3 Readme
|
|
||||||
---------------
|
|
||||||
|
|
||||||
|
|
||||||
-- a simple Python3 wrapper for Assimp using ctypes to access
|
|
||||||
the library. Works with Python 3 upwards.
|
|
||||||
|
|
||||||
|
|
||||||
Note that pyassimp is by no means considered mature. It works,
|
|
||||||
but it is far away from wrapping Assimp perfectly.
|
|
||||||
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
=====
|
|
||||||
|
|
||||||
To get started with pyAssimp, examine the sample.py script, which
|
|
||||||
illustrates the basic usage. All Assimp data structures are
|
|
||||||
wrapped using ctypes. All the data+length fields in Assimp's
|
|
||||||
data structures (such as 'aiMesh::mNumVertices','aiMesh::mVertices')
|
|
||||||
are replaced by simple python lists, so you can call len() on
|
|
||||||
them to get their respective size and access members using
|
|
||||||
[].
|
|
||||||
|
|
||||||
For example, to load a file named 'hello.3ds' and print the first
|
|
||||||
vertex of the first mesh, you would do (proper error handling
|
|
||||||
substituted by assertions ...):
|
|
||||||
|
|
||||||
> from pyassimp import pyassimp, errors
|
|
||||||
>
|
|
||||||
> try:
|
|
||||||
> scene = pyassimp.load('hello.3ds')
|
|
||||||
> except AssimpError, msg:
|
|
||||||
> print(msg)
|
|
||||||
> return
|
|
||||||
|
|
||||||
> assert len(scene.meshes)
|
|
||||||
> mesh = scene.meshes[0]
|
|
||||||
|
|
||||||
> assert len(mesh.vertices)
|
|
||||||
> print(mesh.vertices[0])
|
|
||||||
|
|
||||||
> # don't forget this one, or you will leak!
|
|
||||||
> pyassimp.release(scene)
|
|
||||||
|
|
||||||
|
|
||||||
INSTALL
|
|
||||||
=======
|
|
||||||
|
|
||||||
PyAssimp requires a assimp dynamic library (DLL on windows,
|
|
||||||
so on linux :-) in order to work. The default search directories
|
|
||||||
are:
|
|
||||||
|
|
||||||
- the current directory
|
|
||||||
- on linux additionally: /usr/local/lib
|
|
||||||
|
|
||||||
To build that library, refer to the Assimp master INSTALL
|
|
||||||
instructions. To look in more places, edit ./pyassimp/helper.py.
|
|
||||||
There's an 'additional_dirs' list waiting for your entries.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
#-*- coding: UTF-8 -*-
|
|
|
@ -1,11 +0,0 @@
|
||||||
#-*- coding: UTF-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
All possible errors.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class AssimpError(BaseException):
|
|
||||||
"""
|
|
||||||
If an internal error occures.
|
|
||||||
"""
|
|
||||||
pass
|
|
|
@ -1,119 +0,0 @@
|
||||||
#-*- coding: UTF-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
Some fancy helper functions.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os, sys
|
|
||||||
import ctypes
|
|
||||||
from . import structs
|
|
||||||
import operator
|
|
||||||
|
|
||||||
from .errors import AssimpError
|
|
||||||
from ctypes import POINTER
|
|
||||||
|
|
||||||
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/local/lib/')
|
|
||||||
|
|
||||||
if sys.platform.lower() == 'darwin':
|
|
||||||
ext_whitelist.append('.dylib')
|
|
||||||
|
|
||||||
# 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')
|
|
||||||
|
|
||||||
elif os.name=='nt':
|
|
||||||
ext_whitelist.append('.dll')
|
|
||||||
|
|
||||||
def vec2tuple(x):
|
|
||||||
""" Converts a VECTOR3D to a Tuple """
|
|
||||||
return (x.x, x.y, x.z)
|
|
||||||
|
|
||||||
|
|
||||||
def try_load_functions(library,dll,candidates):
|
|
||||||
"""try to functbind to aiImportFile and aiReleaseImport
|
|
||||||
|
|
||||||
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
|
|
||||||
except AttributeError:
|
|
||||||
#OK, this is a library, but it has not the functions we need
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
#Library found!
|
|
||||||
load.restype = POINTER(structs.Scene)
|
|
||||||
candidates.append((library, load, release, dll))
|
|
||||||
|
|
||||||
|
|
||||||
def search_library():
|
|
||||||
"""Loads the assimp-Library.
|
|
||||||
|
|
||||||
result (load-function, release-function)
|
|
||||||
exception AssimpError if no library is found
|
|
||||||
|
|
||||||
"""
|
|
||||||
#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 = os.path.join(curfolder, filename)
|
|
||||||
print('PyAssimp3: trying ',library)
|
|
||||||
try:
|
|
||||||
dll = ctypes.cdll.LoadLibrary(library)
|
|
||||||
except:
|
|
||||||
# OK, this except is evil. But different OSs will throw different
|
|
||||||
# errors. So just ignore any errors.
|
|
||||||
continue
|
|
||||||
|
|
||||||
try_load_functions(library,dll,candidates)
|
|
||||||
|
|
||||||
if not candidates:
|
|
||||||
# no library found
|
|
||||||
raise AssimpError("assimp library not found")
|
|
||||||
else:
|
|
||||||
# get the newest library
|
|
||||||
candidates = [(os.lstat(x[0])[-2], x) for x in candidates]
|
|
||||||
res = max(candidates, key=operator.itemgetter(0))[1]
|
|
||||||
print('PyAssimp3: taking ',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:]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,314 +0,0 @@
|
||||||
#-*- coding: UTF-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
PyAssimp
|
|
||||||
|
|
||||||
This is the main-module of PyAssimp.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
if sys.version_info < (3,0):
|
|
||||||
raise Exception('pyassimp: need python 3.0 or newer')
|
|
||||||
|
|
||||||
from . import structs
|
|
||||||
import ctypes
|
|
||||||
import os
|
|
||||||
from . 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])
|
|
||||||
|
|
||||||
class StringUInt32(ctypes.Structure):
|
|
||||||
"""
|
|
||||||
A ctypes structure used for material strings.
|
|
||||||
This is a workaround for a type mismatch for the length field between strings used for materials and elsewhere.
|
|
||||||
"""
|
|
||||||
|
|
||||||
MAXLEN = 1024
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
("length", ctypes.c_uint32),
|
|
||||||
("data", ctypes.c_char*MAXLEN),
|
|
||||||
]
|
|
||||||
|
|
||||||
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 list(self.__class__.__dict__.keys()):
|
|
||||||
if m.startswith('mNum'):
|
|
||||||
name = m.split('mNum')[1]
|
|
||||||
if 'm'+name in list(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 range(len(self.mColors))])
|
|
||||||
setattr(self.__class__, "texcoords", [aiArray(self, 'mTextureCoords' , m, o) for o in range(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
|
|
||||||
from ctypes import c_char_p, c_uint
|
|
||||||
model = _assimp_lib.load(c_char_p(filename), c_uint(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 range(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.
|
|
||||||
This function returns the following tuple: (name, clr, mat, tex) where:
|
|
||||||
name: is the name of the material
|
|
||||||
clr: is a dictionary of color parameters
|
|
||||||
mat: is a dictionary of material parameters
|
|
||||||
tex: is a triply nested dictionary than can be indexed by index, semantic, and then key:
|
|
||||||
tex[index][semantic][key]
|
|
||||||
"""
|
|
||||||
name = ""
|
|
||||||
clr = {}
|
|
||||||
mat = {}
|
|
||||||
tex = {}
|
|
||||||
#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
|
|
||||||
try:
|
|
||||||
value = cast(p.mData, POINTER(StringUInt32)).contents.data
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
print('UnicodeDecodeError reading material property, ignoring.')
|
|
||||||
continue
|
|
||||||
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]
|
|
||||||
|
|
||||||
#store in the appropriate dict
|
|
||||||
if key == b'?mat.name':
|
|
||||||
name = value
|
|
||||||
elif key[:5] == b'$mat.':
|
|
||||||
mat[key[5:]] = value
|
|
||||||
elif key[:5] == b'$clr.':
|
|
||||||
clr[key[5:]] = value
|
|
||||||
elif key[:5] == b'$tex.':
|
|
||||||
if p.mIndex not in tex:
|
|
||||||
tex[p.mIndex] = {}
|
|
||||||
if p.mSemantic not in tex[p.mIndex]:
|
|
||||||
tex[p.mIndex][p.mSemantic] = {}
|
|
||||||
tex[p.mIndex][p.mSemantic][key[5:]] = value
|
|
||||||
|
|
||||||
return (name, clr, mat, tex)
|
|
||||||
|
|
||||||
|
|
||||||
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()
|
|
|
@ -1,424 +0,0 @@
|
||||||
#-*- coding: UTF-8 -*-
|
|
||||||
|
|
||||||
from ctypes import POINTER, c_int, c_uint, c_char, c_float, Structure, c_char_p, c_double, c_ubyte, c_size_t
|
|
||||||
|
|
||||||
|
|
||||||
class Vector2D(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiVector2D.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
("x", c_float),("y", c_float),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Texel(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiTexture.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
("b", c_ubyte),("g", c_ubyte),("r", c_ubyte),("a", c_ubyte),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Plane(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiTypes.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
# Plane equation
|
|
||||||
("a", c_float),("b", c_float),("c", c_float),("d", c_float),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Color3D(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiTypes.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
# Red, green and blue color values
|
|
||||||
("r", c_float),("g", c_float),("b", c_float),
|
|
||||||
]
|
|
||||||
|
|
||||||
class String(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiTypes.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
MAXLEN = 1024
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#Binary length of the string excluding the terminal 0. This is NOT the# logical length of strings containing UTF-8 multibyte sequences! It's# the number of bytes from the beginning of the string to its end.#
|
|
||||||
("length", c_size_t),#String buffer. Size limit is MAXLEN#
|
|
||||||
("data", c_char*MAXLEN),
|
|
||||||
]
|
|
||||||
|
|
||||||
class MemoryInfo(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiTypes.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#Storage allocated for texture data#
|
|
||||||
("textures", c_uint),#Storage allocated for material data#
|
|
||||||
("materials", c_uint),#Storage allocated for mesh data#
|
|
||||||
("meshes", c_uint),#Storage allocated for node data#
|
|
||||||
("nodes", c_uint),#Storage allocated for animation data#
|
|
||||||
("animations", c_uint),#Storage allocated for camera data#
|
|
||||||
("cameras", c_uint),#Storage allocated for light data#
|
|
||||||
("lights", c_uint),#Total storage allocated for the full import.#
|
|
||||||
("total", c_uint),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Matrix3x3(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiMatrix3x3.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
("a1", c_float),("a2", c_float),("a3", c_float),
|
|
||||||
("b1", c_float),("b2", c_float),("b3", c_float),
|
|
||||||
("c1", c_float),("c2", c_float),("c3", c_float),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Color4D(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiColor4D.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
# Red, green, blue and alpha color values
|
|
||||||
("r", c_float),("g", c_float),("b", c_float),("a", c_float),
|
|
||||||
]
|
|
||||||
|
|
||||||
class ExportFormatDesc(Structure):
|
|
||||||
"""
|
|
||||||
See 'export.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
# a short string ID to uniquely identify the export format. Use this ID string to# specify which file format you want to export to when calling aiExportScene().# Example: "dae" or "obj"
|
|
||||||
("id", POINTER(c_char)),# A short description of the file format to present to users. Useful if you want# to allow the user to select an export format.
|
|
||||||
("description", POINTER(c_char)),# Recommended file extension for the exported file in lower case.
|
|
||||||
("fileExtension", POINTER(c_char)),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Quaternion(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiQuaternion.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
# w,x,y,z components of the quaternion
|
|
||||||
("w", c_float),("x", c_float),("y", c_float),("z", c_float),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Vector3D(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiVector3D.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
("x", c_float),("y", c_float),("z", c_float),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Face(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiMesh.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
# Number of indices defining this face.# The maximum value for this member is #AI_MAX_FACE_INDICES.
|
|
||||||
("mNumIndices", c_uint),# Pointer to the indices array. Size of the array is given in numIndices.
|
|
||||||
("mIndices", POINTER(c_uint)),
|
|
||||||
]
|
|
||||||
|
|
||||||
class VertexWeight(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiMesh.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
# Index of the vertex which is influenced by the bone.
|
|
||||||
("mVertexId", c_uint),# The strength of the influence in the range (0...1).# The influence from all bones at one vertex amounts to 1.
|
|
||||||
("mWeight", c_float),
|
|
||||||
]
|
|
||||||
|
|
||||||
class MeshKey(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiAnim.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#The time of this key#
|
|
||||||
("mTime", c_double),#Index into the aiMesh::mAnimMeshes array of the# mesh coresponding to the #aiMeshAnim hosting this# key frame. The referenced anim mesh is evaluated# according to the rules defined in the docs for #aiAnimMesh.#
|
|
||||||
("mValue", c_uint),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Matrix4x4(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiMatrix4x4.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
("a1", c_float),("a2", c_float),("a3", c_float),("a4", c_float),
|
|
||||||
("b1", c_float),("b2", c_float),("b3", c_float),("b4", c_float),
|
|
||||||
("c1", c_float),("c2", c_float),("c3", c_float),("c4", c_float),
|
|
||||||
("d1", c_float),("d2", c_float),("d3", c_float),("d4", c_float),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Node(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiScene.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
Node._fields_ = [
|
|
||||||
#The name of the node.##The name might be empty (length of zero) but all nodes which#need to be accessed afterwards by bones or anims are usually named.#Multiple nodes may have the same name, but nodes which are accessed#by bones (see #aiBone and #aiMesh::mBones) *must* be unique.##Cameras and lights are assigned to a specific node name - if there#are multiple nodes with this name, they're assigned to each of them.#<br>#There are no limitations regarding the characters contained in#this text. You should be able to handle stuff like whitespace, tabs,#linefeeds, quotation marks, ampersands, ... .#
|
|
||||||
("mName", String),#The transformation relative to the node's parent.#
|
|
||||||
("mTransformation", Matrix4x4),#Parent node. NULL if this node is the root node.#
|
|
||||||
("mParent", POINTER(Node)),#The number of child nodes of this node.#
|
|
||||||
("mNumChildren", c_uint),#The child nodes of this node. NULL if mNumChildren is 0.#
|
|
||||||
("mChildren", POINTER(POINTER(Node))),#The number of meshes of this node.#
|
|
||||||
("mNumMeshes", c_uint),#The meshes of this node. Each entry is an index into the mesh#
|
|
||||||
("mMeshes", POINTER(c_uint)),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Camera(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiCamera.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#The name of the camera.## There must be a node in the scenegraph with the same name.# This node specifies the position of the camera in the scene# hierarchy and can be animated.#
|
|
||||||
("mName", String),#Position of the camera relative to the coordinate space# defined by the corresponding node.## The default value is 0|0|0.#
|
|
||||||
("mPosition", Vector3D),#'Up' - vector of the camera coordinate system relative to# the coordinate space defined by the corresponding node.## The 'right' vector of the camera coordinate system is# the cross product of the up and lookAt vectors.# The default value is 0|1|0. The vector# may be normalized, but it needn't.#
|
|
||||||
("mUp", Vector3D),#'LookAt' - vector of the camera coordinate system relative to# the coordinate space defined by the corresponding node.## This is the viewing direction of the user.# The default value is 0|0|1. The vector# may be normalized, but it needn't.#
|
|
||||||
("mLookAt", Vector3D),#Half horizontal field of view angle, in radians.## The field of view angle is the angle between the center# line of the screen and the left or right border.# The default value is 1/4PI.#
|
|
||||||
("mHorizontalFOV", c_float),#Distance of the near clipping plane from the camera.##The value may not be 0.f (for arithmetic reasons to prevent#a division through zero). The default value is 0.1f.#
|
|
||||||
("mClipPlaneNear", c_float),#Distance of the far clipping plane from the camera.##The far clipping plane must, of course, be further away than the#near clipping plane. The default value is 1000.f. The ratio#between the near and the far plane should not be too#large (between 1000-10000 should be ok) to avoid floating-point#inaccuracies which could lead to z-fighting.#
|
|
||||||
("mClipPlaneFar", c_float),#Screen aspect ratio.##This is the ration between the width and the height of the#screen. Typical values are 4/3, 1/2 or 1/1. This value is#0 if the aspect ratio is not defined in the source file.#0 is also the default value.#
|
|
||||||
("mAspect", c_float),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Texture(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiTexture.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#Width of the texture, in pixels##If mHeight is zero the texture is compressed in a format#like JPEG. In this case mWidth specifies the size of the#memory area pcData is pointing to, in bytes.#
|
|
||||||
("mWidth", c_uint),#Height of the texture, in pixels##If this value is zero, pcData points to an compressed texture#in any format (e.g. JPEG).#
|
|
||||||
("mHeight", c_uint),#A hint from the loader to make it easier for applications# to determine the type of embedded compressed textures.##If mHeight != 0 this member is undefined. Otherwise it#is set set to '\\0\\0\\0\\0' if the loader has no additional#information about the texture file format used OR the#file extension of the format without a trailing dot. If there#are multiple file extensions for a format, the shortest#extension is chosen (JPEG maps to 'jpg', not to 'jpeg').#E.g. 'dds\\0', 'pcx\\0', 'jpg\\0'. All characters are lower-case.#The fourth character will always be '\\0'.#
|
|
||||||
("achFormatHint", c_char*4),#Data of the texture.##Points to an array of mWidth#mHeight aiTexel's.#The format of the texture data is always ARGB8888 to#make the implementation for user of the library as easy#as possible. If mHeight = 0 this is a pointer to a memory#buffer of size mWidth containing the compressed texture#data. Good luck, have fun!#
|
|
||||||
("pcData", POINTER(Texel)),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Ray(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiTypes.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
# Position and direction of the ray
|
|
||||||
("pos", Vector3D),("dir", Vector3D),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Light(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiLight.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#The name of the light source.## There must be a node in the scenegraph with the same name.# This node specifies the position of the light in the scene# hierarchy and can be animated.#
|
|
||||||
("mName", String),#The type of the light source.##aiLightSource_UNDEFINED is not a valid value for this member.#
|
|
||||||
("mType", c_uint),#Position of the light source in space. Relative to the# transformation of the node corresponding to the light.## The position is undefined for directional lights.#
|
|
||||||
("mPosition", Vector3D),#Direction of the light source in space. Relative to the# transformation of the node corresponding to the light.## The direction is undefined for point lights. The vector# may be normalized, but it needn't.#
|
|
||||||
("mDirection", Vector3D),#Constant light attenuation factor.## The intensity of the light source at a given distance 'd' from# the light's position is# @code# Atten = 1/( att0 + att1#d + att2#d*d)# @endcode# This member corresponds to the att0 variable in the equation.# Naturally undefined for directional lights.#
|
|
||||||
("mAttenuationConstant", c_float),#Linear light attenuation factor.## The intensity of the light source at a given distance 'd' from# the light's position is# @code# Atten = 1/( att0 + att1#d + att2#d*d)# @endcode# This member corresponds to the att1 variable in the equation.# Naturally undefined for directional lights.#
|
|
||||||
("mAttenuationLinear", c_float),#Quadratic light attenuation factor.## The intensity of the light source at a given distance 'd' from# the light's position is# @code# Atten = 1/( att0 + att1#d + att2#d*d)# @endcode# This member corresponds to the att2 variable in the equation.# Naturally undefined for directional lights.#
|
|
||||||
("mAttenuationQuadratic", c_float),#Diffuse color of the light source## The diffuse light color is multiplied with the diffuse# material color to obtain the final color that contributes# to the diffuse shading term.#
|
|
||||||
("mColorDiffuse", Color3D),#Specular color of the light source## The specular light color is multiplied with the specular# material color to obtain the final color that contributes# to the specular shading term.#
|
|
||||||
("mColorSpecular", Color3D),#Ambient color of the light source## The ambient light color is multiplied with the ambient# material color to obtain the final color that contributes# to the ambient shading term. Most renderers will ignore# this value it, is just a remaining of the fixed-function pipeline# that is still supported by quite many file formats.#
|
|
||||||
("mColorAmbient", Color3D),#Inner angle of a spot light's light cone.## The spot light has maximum influence on objects inside this# angle. The angle is given in radians. It is 2PI for point# lights and undefined for directional lights.#
|
|
||||||
("mAngleInnerCone", c_float),#Outer angle of a spot light's light cone.## The spot light does not affect objects outside this angle.# The angle is given in radians. It is 2PI for point lights and# undefined for directional lights. The outer angle must be# greater than or equal to the inner angle.# It is assumed that the application uses a smooth# interpolation between the inner and the outer cone of the# spot light.#
|
|
||||||
("mAngleOuterCone", c_float),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Bone(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiMesh.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
# The name of the bone.
|
|
||||||
("mName", String),# The number of vertices affected by this bone# The maximum value for this member is #AI_MAX_BONE_WEIGHTS.
|
|
||||||
("mNumWeights", c_uint),# The vertices affected by this bone
|
|
||||||
("mWeights", POINTER(VertexWeight)),# Matrix that transforms from mesh space to bone space in bind pose
|
|
||||||
("mOffsetMatrix", Matrix4x4),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Mesh(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiMesh.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
AI_MAX_FACE_INDICES = 0x7fff
|
|
||||||
AI_MAX_BONE_WEIGHTS = 0x7fffffff
|
|
||||||
AI_MAX_VERTICES = 0x7fffffff
|
|
||||||
AI_MAX_FACES = 0x7fffffff
|
|
||||||
AI_MAX_NUMBER_OF_COLOR_SETS = 0x8
|
|
||||||
AI_MAX_NUMBER_OF_TEXTURECOORDS = 0x8
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#Bitwise combination of the members of the #aiPrimitiveType enum.#This specifies which types of primitives are present in the mesh.#The "SortByPrimitiveType"-Step can be used to make sure the#output meshes consist of one primitive type each.#
|
|
||||||
("mPrimitiveTypes", c_uint),#The number of vertices in this mesh.#This is also the size of all of the per-vertex data arrays.#The maximum value for this member is #AI_MAX_VERTICES.#
|
|
||||||
("mNumVertices", c_uint),#The number of primitives (triangles, polygons, lines) in this mesh.#This is also the size of the mFaces array.#The maximum value for this member is #AI_MAX_FACES.#
|
|
||||||
("mNumFaces", c_uint),#Vertex positions.#This array is always present in a mesh. The array is#mNumVertices in size.#
|
|
||||||
("mVertices", POINTER(Vector3D)),#Vertex normals.#The array contains normalized vectors, NULL if not present.#The array is mNumVertices in size. Normals are undefined for#point and line primitives. A mesh consisting of points and#lines only may not have normal vectors. Meshes with mixed#primitive types (i.e. lines and triangles) may have normals,#but the normals for vertices that are only referenced by#point or line primitives are undefined and set to QNaN (WARN:#qNaN compares to inequal to *everything*, even to qNaN itself.#Using code like this to check whether a field is qnan is:#@code##define IS_QNAN(f) (f != f)#@endcode#still dangerous because even 1.f == 1.f could evaluate to false! (#remember the subtleties of IEEE754 artithmetics). Use stuff like#@c fpclassify instead.#@note Normal vectors computed by Assimp are always unit-length.#However, this needn't apply for normals that have been taken# directly from the model file.#
|
|
||||||
("mNormals", POINTER(Vector3D)),#Vertex tangents.#The tangent of a vertex points in the direction of the positive#X texture axis. The array contains normalized vectors, NULL if#not present. The array is mNumVertices in size. A mesh consisting#of points and lines only may not have normal vectors. Meshes with#mixed primitive types (i.e. lines and triangles) may have#normals, but the normals for vertices that are only referenced by#point or line primitives are undefined and set to qNaN. See#the #mNormals member for a detailled discussion of qNaNs.#@note If the mesh contains tangents, it automatically also#contains bitangents.#
|
|
||||||
("mTangents", POINTER(Vector3D)),#Vertex bitangents.#The bitangent of a vertex points in the direction of the positive#Y texture axis. The array contains normalized vectors, NULL if not#present. The array is mNumVertices in size.#@note If the mesh contains tangents, it automatically also contains#bitangents.#
|
|
||||||
("mBitangents", POINTER(Vector3D)),#Vertex color sets.#A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex#colors per vertex. NULL if not present. Each array is#mNumVertices in size if present.#
|
|
||||||
("mColors", POINTER(Color4D)*AI_MAX_NUMBER_OF_COLOR_SETS),#Vertex texture coords, also known as UV channels.#A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per#vertex. NULL if not present. The array is mNumVertices in size.#
|
|
||||||
("mTextureCoords", POINTER(Vector3D)*AI_MAX_NUMBER_OF_TEXTURECOORDS),#Specifies the number of components for a given UV channel.#Up to three channels are supported (UVW, for accessing volume#or cube maps). If the value is 2 for a given channel n, the#component p.z of mTextureCoords[n][p] is set to 0.0f.#If the value is 1 for a given channel, p.y is set to 0.0f, too.#@note 4D coords are not supported#
|
|
||||||
("mNumUVComponents", c_uint*AI_MAX_NUMBER_OF_TEXTURECOORDS),#The faces the mesh is constructed from.#Each face refers to a number of vertices by their indices.#This array is always present in a mesh, its size is given#in mNumFaces. If the #AI_SCENE_FLAGS_NON_VERBOSE_FORMAT#is NOT set each face references an unique set of vertices.#
|
|
||||||
("mFaces", POINTER(Face)),#The number of bones this mesh contains.#Can be 0, in which case the mBones array is NULL.#
|
|
||||||
("mNumBones", c_uint),#The bones of this mesh.#A bone consists of a name by which it can be found in the#frame hierarchy and a set of vertex weights.#
|
|
||||||
("mBones", POINTER(POINTER(Bone))),#The material used by this mesh.#A mesh does use only a single material. If an imported model uses#multiple materials, the import splits up the mesh. Use this value#as index into the scene's material list.#
|
|
||||||
("mMaterialIndex", c_uint),#Name of the mesh. Meshes can be named, but this is not a# requirement and leaving this field empty is totally fine.# There are mainly three uses for mesh names:# - some formats name nodes and meshes independently.# - importers tend to split meshes up to meet the# one-material-per-mesh requirement. Assigning# the same (dummy) name to each of the result meshes# aids the caller at recovering the original mesh# partitioning.# - Vertex animations refer to meshes by their names.#
|
|
||||||
("mName", String),#NOT CURRENTLY IN USE. The number of attachment meshes#
|
|
||||||
("mNumAnimMeshes", c_uint),#NOT CURRENTLY IN USE. Attachment meshes for this mesh, for vertex-based animation.# Attachment meshes carry replacement data for some of the# mesh'es vertex components (usually positions, normals).#
|
|
||||||
]
|
|
||||||
|
|
||||||
class VectorKey(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiAnim.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#The time of this key#
|
|
||||||
("mTime", c_double),#The value of this key#
|
|
||||||
("mValue", Vector3D),
|
|
||||||
]
|
|
||||||
|
|
||||||
class QuatKey(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiAnim.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#The time of this key#
|
|
||||||
("mTime", c_double),#The value of this key#
|
|
||||||
("mValue", Quaternion),
|
|
||||||
]
|
|
||||||
|
|
||||||
class NodeAnim(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiAnim.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#The name of the node affected by this animation. The node# must exist and it must be unique.#
|
|
||||||
("mNodeName", String),#The number of position keys#
|
|
||||||
("mNumPositionKeys", c_uint),#The position keys of this animation channel. Positions are#specified as 3D vector. The array is mNumPositionKeys in size.##If there are position keys, there will also be at least one#scaling and one rotation key.#
|
|
||||||
("mPositionKeys", POINTER(VectorKey)),#The number of rotation keys#
|
|
||||||
("mNumRotationKeys", c_uint),#The rotation keys of this animation channel. Rotations are# given as quaternions, which are 4D vectors. The array is# mNumRotationKeys in size.##If there are rotation keys, there will also be at least one#scaling and one position key.#
|
|
||||||
("mRotationKeys", POINTER(QuatKey)),#The number of scaling keys#
|
|
||||||
("mNumScalingKeys", c_uint),#The scaling keys of this animation channel. Scalings are# specified as 3D vector. The array is mNumScalingKeys in size.##If there are scaling keys, there will also be at least one#position and one rotation key.#
|
|
||||||
("mScalingKeys", POINTER(VectorKey)),#Defines how the animation behaves before the first# key is encountered.## The default value is aiAnimBehaviour_DEFAULT (the original# transformation matrix of the affected node is used).#
|
|
||||||
("mPreState", c_uint),#Defines how the animation behaves after the last# key was processed.## The default value is aiAnimBehaviour_DEFAULT (the original# transformation matrix of the affected node is taken).#
|
|
||||||
("mPostState", c_uint),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Animation(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiAnim.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#The name of the animation. If the modeling package this data was# exported from does support only a single animation channel, this# name is usually empty (length is zero).#
|
|
||||||
("mName", String),#Duration of the animation in ticks.#
|
|
||||||
("mDuration", c_double),#Ticks per second. 0 if not specified in the imported file#
|
|
||||||
("mTicksPerSecond", c_double),#The number of bone animation channels. Each channel affects# a single node.#
|
|
||||||
("mNumChannels", c_uint),#The node animation channels. Each channel affects a single node.# The array is mNumChannels in size.#
|
|
||||||
("mChannels", POINTER(POINTER(NodeAnim))),#The number of mesh animation channels. Each channel affects# a single mesh and defines vertex-based animation.#
|
|
||||||
("mNumMeshChannels", c_uint),#The mesh animation channels. Each channel affects a single mesh.# The array is mNumMeshChannels in size.#
|
|
||||||
]
|
|
||||||
|
|
||||||
class UVTransform(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiMaterial.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#Translation on the u and v axes.## The default value is (0|0).#
|
|
||||||
("mTranslation", Vector2D),#Scaling on the u and v axes.## The default value is (1|1).#
|
|
||||||
("mScaling", Vector2D),#Rotation - in counter-clockwise direction.## The rotation angle is specified in radians. The# rotation center is 0.5f|0.5f. The default value# 0.f.#
|
|
||||||
("mRotation", c_float),
|
|
||||||
]
|
|
||||||
|
|
||||||
class MaterialProperty(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiMaterial.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#Specifies the name of the property (key)# Keys are generally case insensitive.#
|
|
||||||
("mKey", String),#Textures: Specifies their exact usage semantic.#For non-texture properties, this member is always 0#(or, better-said, #aiTextureType_NONE).#
|
|
||||||
("mSemantic", c_uint),#Textures: Specifies the index of the texture.# For non-texture properties, this member is always 0.#
|
|
||||||
("mIndex", c_uint),#Size of the buffer mData is pointing to, in bytes.# This value may not be 0.#
|
|
||||||
("mDataLength", c_uint),#Type information for the property.##Defines the data layout inside the data buffer. This is used#by the library internally to perform debug checks and to#utilize proper type conversions.#(It's probably a hacky solution, but it works.)#
|
|
||||||
("mType", c_uint),#Binary buffer to hold the property's value.#The size of the buffer is always mDataLength.#
|
|
||||||
("mData", POINTER(c_char)),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Material(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiMaterial.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#List of all material properties loaded.#
|
|
||||||
("mProperties", POINTER(POINTER(MaterialProperty))),#Number of properties in the data base#
|
|
||||||
("mNumProperties", c_uint),#Storage allocated#
|
|
||||||
("mNumAllocated", c_uint),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Scene(Structure):
|
|
||||||
"""
|
|
||||||
See 'aiScene.h' for details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
AI_SCENE_FLAGS_INCOMPLETE = 0x1
|
|
||||||
AI_SCENE_FLAGS_VALIDATED = 0x2
|
|
||||||
AI_SCENE_FLAGS_VALIDATION_WARNING = 0x4
|
|
||||||
AI_SCENE_FLAGS_NON_VERBOSE_FORMAT = 0x8
|
|
||||||
AI_SCENE_FLAGS_TERRAIN = 0x10
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
#Any combination of the AI_SCENE_FLAGS_XXX flags. By default#this value is 0, no flags are set. Most applications will#want to reject all scenes with the AI_SCENE_FLAGS_INCOMPLETE#bit set.#
|
|
||||||
("mFlags", c_uint),#The root node of the hierarchy.##There will always be at least the root node if the import#was successful (and no special flags have been set).#Presence of further nodes depends on the format and content#of the imported file.#
|
|
||||||
("mRootNode", POINTER(Node)),#The number of meshes in the scene.#
|
|
||||||
("mNumMeshes", c_uint),#The array of meshes.##Use the indices given in the aiNode structure to access#this array. The array is mNumMeshes in size. If the#AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always#be at least ONE material.#
|
|
||||||
("mMeshes", POINTER(POINTER(Mesh))),#The number of materials in the scene.#
|
|
||||||
("mNumMaterials", c_uint),#The array of materials.##Use the index given in each aiMesh structure to access this#array. The array is mNumMaterials in size. If the#AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always#be at least ONE material.#
|
|
||||||
("mMaterials", POINTER(POINTER(Material))),#The number of animations in the scene.#
|
|
||||||
("mNumAnimations", c_uint),#The array of animations.##All animations imported from the given file are listed here.#The array is mNumAnimations in size.#
|
|
||||||
("mAnimations", POINTER(POINTER(Animation))),#The number of textures embedded into the file#
|
|
||||||
("mNumTextures", c_uint),#The array of embedded textures.##Not many file formats embed their textures into the file.#An example is Quake's MDL format (which is also used by#some GameStudio versions)#
|
|
||||||
("mTextures", POINTER(POINTER(Texture))),#The number of light sources in the scene. Light sources#are fully optional, in most cases this attribute will be 0#
|
|
||||||
("mNumLights", c_uint),#The array of light sources.##All light sources imported from the given file are#listed here. The array is mNumLights in size.#
|
|
||||||
("mLights", POINTER(POINTER(Light))),#The number of cameras in the scene. Cameras#are fully optional, in most cases this attribute will be 0#
|
|
||||||
("mNumCameras", c_uint),#The array of cameras.##All cameras imported from the given file are listed here.#The array is mNumCameras in size. The first camera in the#array (if existing) is the default camera view into#the scene.#
|
|
||||||
("mCameras", POINTER(POINTER(Camera))),
|
|
||||||
]
|
|
|
@ -1,45 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
#-*- coding: UTF-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
This module uses the sample.py script to load all test models it finds.
|
|
||||||
|
|
||||||
Note: this is not an exhaustive test suite, it does not check the
|
|
||||||
data structures in detail. It just verifies whether basic
|
|
||||||
loading and querying of 3d models using pyassimp works.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
import sys,os
|
|
||||||
import sample
|
|
||||||
from pyassimp import pyassimp,errors
|
|
||||||
|
|
||||||
# paths to be walkd recursively
|
|
||||||
basepaths = [os.path.join('..','..','test','models'), os.path.join('..','..','test','models-nonbsd')]
|
|
||||||
|
|
||||||
# file extensions to be considered
|
|
||||||
extensions = ['.3ds','.x','.lwo','.obj','.md5mesh','.dxf','.ply','.stl','.dae','.md5anim','.lws','.irrmesh','.nff','.off','.blend']
|
|
||||||
|
|
||||||
def run_tests():
|
|
||||||
ok,err = 0,0
|
|
||||||
for path in basepaths:
|
|
||||||
for root, dirs, files in os.walk(path):
|
|
||||||
for afile in files:
|
|
||||||
base,ext = os.path.splitext(afile)
|
|
||||||
if ext in extensions:
|
|
||||||
try:
|
|
||||||
sample.main(os.path.join(root,afile))
|
|
||||||
ok += 1
|
|
||||||
except errors.AssimpError as error:
|
|
||||||
# assimp error is fine, this is a controlled case
|
|
||||||
print(error)
|
|
||||||
err += 1
|
|
||||||
print(('** Loaded %s models, got controlled errors for %s files' % (ok,err)))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_tests()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
#-*- coding: UTF-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
This module demonstrates the functionality of PyAssimp.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
from pyassimp import pyassimp
|
|
||||||
import os, sys
|
|
||||||
|
|
||||||
#get a model out of assimp's test-data if none is provided on the command line
|
|
||||||
DEFAULT_MODEL = os.path.join(os.path.dirname(__file__),
|
|
||||||
"..", "..",
|
|
||||||
"test", "models", "Collada", "duck.dae")
|
|
||||||
|
|
||||||
def main(filename=None):
|
|
||||||
filename = filename or DEFAULT_MODEL
|
|
||||||
scene = pyassimp.load(filename)
|
|
||||||
|
|
||||||
#the model we load
|
|
||||||
print("MODEL:", filename)
|
|
||||||
print()
|
|
||||||
|
|
||||||
#write some statistics
|
|
||||||
print("SCENE:")
|
|
||||||
print(" meshes:", len(scene.meshes))
|
|
||||||
print(" materials:", len(scene.materials))
|
|
||||||
print(" textures:", len(scene.textures))
|
|
||||||
print()
|
|
||||||
|
|
||||||
print("MESHES:")
|
|
||||||
for index, mesh in enumerate(scene.meshes):
|
|
||||||
print(" MESH", index+1)
|
|
||||||
print(" material:", mesh.mMaterialIndex+1)
|
|
||||||
print(" vertices:", len(mesh.vertices))
|
|
||||||
print(" first 3 verts:", mesh.vertices[:3])
|
|
||||||
#if len(mesh.normals):
|
|
||||||
# print " first 3 normals:", mesh.normals[:3]
|
|
||||||
print(" colors:", len(mesh.colors))
|
|
||||||
tc = mesh.texcoords
|
|
||||||
print(" texture-coords 1:", len(tc[0]), "first3:", tc[0][:3])
|
|
||||||
print(" texture-coords 2:", len(tc[1]), "first3:", tc[1][:3])
|
|
||||||
print(" texture-coords 3:", len(tc[2]), "first3:", tc[2][:3])
|
|
||||||
print(" texture-coords 4:", len(tc[3]), "first3:", tc[3][:3])
|
|
||||||
print(" uv-component-count:", len(mesh.mNumUVComponents))
|
|
||||||
print(" faces:", len(mesh.faces), "first:", [f.indices for f in mesh.faces[:3]])
|
|
||||||
print(" bones:", len(mesh.bones), "first:", [b.mName for b in mesh.bones[:3]])
|
|
||||||
print()
|
|
||||||
|
|
||||||
print("MATERIALS:")
|
|
||||||
for index, material in enumerate(scene.materials):
|
|
||||||
print(" MATERIAL", index+1)
|
|
||||||
properties = pyassimp.GetMaterialProperties(material)
|
|
||||||
for key in properties:
|
|
||||||
print(" %s: %s" % (key, properties[key]))
|
|
||||||
print()
|
|
||||||
|
|
||||||
print("TEXTURES:")
|
|
||||||
for index, texture in enumerate(scene.textures):
|
|
||||||
print(" TEXTURE", index+1)
|
|
||||||
print(" width:", texture.mWidth)
|
|
||||||
print(" height:", texture.mHeight)
|
|
||||||
print(" hint:", texture.achFormatHint)
|
|
||||||
print(" data (size):", texture.mWidth*texture.mHeight)
|
|
||||||
|
|
||||||
# Finally release the model
|
|
||||||
pyassimp.release(scene)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main(sys.argv[1] if len(sys.argv)>1 else None)
|
|
Loading…
Reference in New Issue