From 4f5a880306cf97e3675ecaa1a884f8f8ce2bf5cf Mon Sep 17 00:00:00 2001 From: sebastianhempel Date: Wed, 17 Sep 2008 15:25:20 +0000 Subject: [PATCH] PyAssimp initial import. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@147 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- port/PyAssimp/README | 4 + port/PyAssimp/pyassimp/__init__.py | 0 port/PyAssimp/pyassimp/pyassimp.py | 223 +++++++++++++ port/PyAssimp/pyassimp/structs.py | 520 +++++++++++++++++++++++++++++ port/PyAssimp/sample.py | 24 ++ 5 files changed, 771 insertions(+) create mode 100644 port/PyAssimp/README create mode 100644 port/PyAssimp/pyassimp/__init__.py create mode 100644 port/PyAssimp/pyassimp/pyassimp.py create mode 100644 port/PyAssimp/pyassimp/structs.py create mode 100644 port/PyAssimp/sample.py diff --git a/port/PyAssimp/README b/port/PyAssimp/README new file mode 100644 index 000000000..dde25c50e --- /dev/null +++ b/port/PyAssimp/README @@ -0,0 +1,4 @@ + PyAssimp Readme + --------------- + +To be filled. \ No newline at end of file diff --git a/port/PyAssimp/pyassimp/__init__.py b/port/PyAssimp/pyassimp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/port/PyAssimp/pyassimp/pyassimp.py b/port/PyAssimp/pyassimp/pyassimp.py new file mode 100644 index 000000000..49b2a7004 --- /dev/null +++ b/port/PyAssimp/pyassimp/pyassimp.py @@ -0,0 +1,223 @@ +import structs +import ctypes +import os +from ctypes import POINTER, c_int, c_uint, c_double, c_char, c_float + + +#get the assimp path +LIBRARY = os.path.join(os.path.dirname(__file__), "assimp.so") + + +class AssimpError(BaseException): + """ + If ann internal error occures. + """ + pass + + + +class AssimpLib(object): + #open library + _dll = ctypes.cdll.LoadLibrary(LIBRARY) + + #get functions + load = _dll.aiImportFile + load.restype = POINTER(structs.SCENE) + + release = _dll.aiReleaseImport + + + +class AssimpBase(object): + """ + Base class for all Assimp-classes. + """ + + def _load_array(self, data, count, cons): + """ + Loads a whole array out of data, and constructs a new object. If data + is NULL, an empty list will be returned. + + data - pointer to array + count - size of the array + cons - constructor + + result array data + """ + if data: + return [cons(data[i]) for i in range(count)] + else: + return [] + + +class Scene(AssimpBase): + """ + The root structure of the imported data. + Everything that was imported from the given file can be accessed from here. + """ + + #possible flags + FLAGS = {1 : "AI_SCENE_FLAGS_ANIM_SKELETON_ONLY"} + + + def __init__(self, model): + """ + Converts the model-data to a real scene + + model - the raw model-data + """ + #process data + self._load(model) + + + def _load(self, model): + """ + Converts model from raw-data to fancy data! + + model - pointer to data + """ + #store scene flags + self.flags = model.flags + + #load mesh-data + self.meshes = self._load_array(model.mMeshes, + model.mNumMeshes, + lambda x: Mesh(x.contents)) + + + def list_flags(self): + """ + Returns a list of all used flags. + + result list of flags + """ + return [name for (key, value) in Scene.FLAGS.iteritems() + if (key & self.flags)>0] + + +class Mesh(AssimpBase): + """ + A mesh represents a geometry or model with a single material. + It usually consists of a number of vertices and a series of primitives/faces + referencing the vertices. In addition there might be a series of bones, each + of them addressing a number of vertices with a certain weight. Vertex data + is presented in channels with each channel containing a single per-vertex + information such as a set of texture coords or a normal vector. + If a data pointer is non-null, the corresponding data stream is present. + + A Mesh uses only a single material which is referenced by a material ID. + """ + + def __init__(self, mesh): + """ + Loads mesh from raw-data. + """ + #process data + self._load(mesh) + + + def _load(self, mesh): + """ + Loads mesh-data from raw data + + mesh - raw mesh-data + """ + #converts a VECTOR3D-struct to a tuple + vec2tuple = lambda x: (x.x, x.y, x.z) + + #load vertices + self.vertices = self._load_array(mesh.mVertices, + mesh.mNumVertices, + vec2tuple) + + #load normals + self.normals = self._load_array(mesh.mNormals, + mesh.mNumVertices, + vec2tuple) + + #load tangents + self.tangents = self._load_array(mesh.mTangents, + mesh.mNumVertices, + vec2tuple) + + #load bitangents + self.bitangents = self._load_array(mesh.mBitangents, + mesh.mNumVertices, + vec2tuple) + + #vertex color sets + self.colors = self._load_colors(mesh) + + #number of texture coordinates + self.numuv = self._load_uv_component_count(mesh) #FIXME + + + def _load_uv_component_count(self, mesh): + """ + Loads the number of components for a given UV channel. + + mesh - mesh-data + + result (count channel 1, count channel 2, ...) + """ + return tuple(mesh.mNumUVComponents[i] + for i in range(structs.MESH.AI_MAX_NUMBER_OF_TEXTURECOORDS)) + + + def _load_colors(self, mesh): + """ + Loads color sets. + + mesh - mesh with color sets + + result all color sets + """ + result = [] + + #for all possible sets + for i in range(structs.MESH.AI_MAX_NUMBER_OF_COLOR_SETS): + #try this set + x = mesh.mColors[i] + + if x: + channel = [] + + #read data for al vertices! + for j in range(mesh.mNumVertices): + c = x[j] + channel.append((c.r, c.g, c.b, c.a)) + + result.append(channel) + + + return result + + + +#the loader as singleton +_assimp_lib = AssimpLib() + + +def load(filename, processing=0): + """ + Loads the model with some specific processing parameters. + + filename - file to load model from + processing - processing parameters + + result Scene-object with model-data + + throws AssimpError - could not open file + """ + #read pure data + model = _assimp_lib.load(filename, processing) + if not model: + #Uhhh, something went wrong! + raise AssimpError, ("could not import file: %s" % filename) + + try: + #create scene + return Scene(model.contents) + finally: + #forget raw data + _assimp_lib.release(model) \ No newline at end of file diff --git a/port/PyAssimp/pyassimp/structs.py b/port/PyAssimp/pyassimp/structs.py new file mode 100644 index 000000000..d509e9108 --- /dev/null +++ b/port/PyAssimp/pyassimp/structs.py @@ -0,0 +1,520 @@ +""" +All ASSIMP C-structures. +""" + +from ctypes import POINTER, c_int, c_uint, c_char, c_float, Structure, c_char_p, c_double, c_ubyte + + +class STRING(Structure): + """ + Represents a String in ASSIMP. + """ + + #Maximum length of a string. See "MAXLEN" in "aiTypes.h" + MAXLEN = 1024 + + _fields_ = [ + #length of the string + ("length", c_uint), + + #string data + ("data", c_char*MAXLEN) + ] + + + +class MATRIX4X4(Structure): + """ + ASSIMP's 4x4-matrix + """ + + _fields_ = [ + #all the 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): + """ + A node in the imported hierarchy. + + Each node has name, a parent node (except for the root node), + a transformation relative to its parent and possibly several child nodes. + Simple file formats don't support hierarchical structures, for these formats + the imported scene does consist of only a single root node with no childs. + """ + pass + +NODE._fields_ = [ + #The name of the node + ("mName", STRING), + + #The transformation relative to the node's parent. + ("aiMatrix4x4", 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_int)) + ] + + +class VECTOR3D(Structure): + """ + Represents a three-dimensional vector. + """ + + _fields_ = [ + ("x", c_float), + ("y", c_float), + ("z", c_float) + ] + + +class COLOR4D(Structure): + """ + Represents a color in Red-Green-Blue space including an alpha component. + """ + + _fields_ = [ + ("r", c_float), + ("g", c_float), + ("b", c_float), + ("a", c_float) + ] + + +class FACE(Structure): + """ + A single face in a mesh, referring to multiple vertices. + If mNumIndices is 3, the face is a triangle, + for mNumIndices > 3 it's a polygon. + Point and line primitives are rarely used and are NOT supported. However, + a load could pass them as degenerated triangles. + """ + + _fields_ = [ + #Number of indices defining this face. 3 for a triangle, >3 for polygon + ("mNumIndices", c_uint), + + #Pointer to the indices array. Size of the array is given in numIndices. + ("mIndices", POINTER(c_uint)) + ] + + +class VERTEXWEIGHT(Structure): + """ + A single influence of a bone on a vertex. + """ + + _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 BONE(Structure): + """ + A single bone of a mesh. A bone has a name by which it can be found + in the frame hierarchy and by which it can be addressed by animations. + In addition it has a number of influences on vertices. + """ + + _fields_ = [ + #The name of the bone. + ("mName", STRING), + + #The number of vertices affected by this bone + ("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): + """ + A mesh represents a geometry or model with a single material. + It usually consists of a number of vertices and a series of primitives/faces + referencing the vertices. In addition there might be a series of bones, each + of them addressing a number of vertices with a certain weight. Vertex data + is presented in channels with each channel containing a single per-vertex + information such as a set of texture coords or a normal vector. + If a data pointer is non-null, the corresponding data stream is present. + + A Mesh uses only a single material which is referenced by a material ID. + """ + + #Maximum number of texture coord sets (UV(W) channels) per mesh. + #See "AI_MAX_NUMBER_OF_TEXTURECOORDS" in "aiMesh.h" for further + #information. + AI_MAX_NUMBER_OF_TEXTURECOORDS = 4 + + + #Maximum number of vertex color sets per mesh. + #Normally: Diffuse, specular, ambient and emissive + #However one could use the vertex color sets for any other purpose, too. + AI_MAX_NUMBER_OF_COLOR_SETS = 4 + + + _fields_ = [ + #The number of vertices in this mesh. + #This is also the size of all of the per-vertex data arrays + ("mNumVertices", c_uint), #OK + + #The number of primitives (triangles, polygones, lines) in this mesh. + #This is also the size of the mFaces array + ("mNumFaces", c_uint), + + #Vertex positions. + #This array is always present in a mesh. The array is + #mNumVertices in size. + ("mVertices", POINTER(VECTOR3D)), #OK + + #Vertex normals. + #The array contains normalized vectors, NULL if not present. + #The array is mNumVertices in size. + ("mNormals", POINTER(VECTOR3D)), #OK + + #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. + #@note If the mesh contains tangents, it automatically also + #contains bitangents. + ("mTangents", POINTER(VECTOR3D)), #OK + + #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)), #OK + + #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), #OK + + #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 contstructed from. + #Each face referres to a number of vertices by their indices. + #This array is always present in a mesh, its size is given + #in mNumFaces. + ("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) + ] + + +class MATERIALPROPERTY(Structure): + """ + Data structure for a single property inside a material. + """ + + _fields_ = [ + #Specifies the name of the property (key) + #Keys are case insensitive. + ("mKey", STRING), + + #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. + #THIS IS AN ENUM! + ("mType", c_int), + + #Binary buffer to hold the property's value + #The buffer has no terminal character. However, + #if a string is stored inside it may use 0 as terminal, + #but it would be contained in mDataLength. This member + #is never 0 + ("mData", c_char_p) + ] + + +class MATERIAL(Structure): + """ + Data structure for a material. + + Material data is stored using a key-value structure, called property + (to guarant that the system is maximally flexible). + The library defines a set of standard keys (AI_MATKEY) which should be + enough for nearly all purposes. + """ + + _fields_ = [ + #List of all material properties loaded. + ("mProperties", POINTER(POINTER(MATERIALPROPERTY))), + + #Number of properties loaded + ("mNumProperties", c_uint), + ("mNumAllocated", c_uint) + ] + + +class VECTORKEY(Structure): + """ + A time-value pair specifying a certain 3D vector for the given time. + """ + + _fields_ = [ + #The time of this key + ("mTime", c_double), + + #The value of this key + ("mValue", VECTOR3D) + ] + + +class QUATERNION(Structure): + """ + Represents a quaternion in a 4D vector. + """ + + _fields = [ + #the components + ("w", c_float), + ("x", c_float), + ("y", c_float), + ("z", c_float) + ] + + +class QUATKEY(Structure): + """ + A time-value pair specifying a rotation for the given time. For joint + animations the rotation is usually expressed using a quaternion. + """ + + _fields_ = [ + #The time of this key + ("mTime", c_double), + + #The value of this key + ("mValue", QUATERNION) + ] + + +class BONEANIM(Structure): + """ + Describes the animation of a single node. The name specifies the bone/node + which is affected by this animation channel. The keyframes are given in + three separate series of values, one each for position, rotation and + scaling. + + NOTE: The name "BoneAnim" is misleading. This structure is also used to + describe the animation of regular nodes on the node graph. They needn't be + nodes. + """ + + _fields_ = [ + #The name of the bone affected by this animation. + ("mBoneName", 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. + ("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. + ("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. + ("mScalingKeys", POINTER(VECTORKEY)) + ] + + +class ANIMATION(Structure): + """ + An animation consists of keyframe data for a number of bones. + For each bone affected by the animation a separate series of data is given. + """ + + _fields_ = [ + #The name of the animation. If the modelling 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 bone. + ("mNumBones", c_uint), + + #The bone animation channels. Each channel affects a single bone. + #The array is mNumBones in size. + ("mBones", POINTER(POINTER(BONEANIM))) + ] + + +class TEXEL(Structure): + """ + Helper structure to represent a texel in ARGB8888 format + """ + + _fields_ = [ + ("b", c_ubyte), + ("g", c_ubyte), + ("r", c_ubyte), + ("a", c_ubyte) + ] + + +class TEXTURE(Structure): + """ + Helper structure to describe an embedded texture + + Normally textures are contained in external files but some file formats + do embedd them. + """ + + _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 an unknown 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 + #will be 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 leading dot. + #E.g. 'dds\0', 'pcx\0'. All characters are lower-case. + ("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 SCENE(Structure): + """ + The root structure of the imported data. + Everything that was imported from the given file can be accessed from here. + """ + + _fields_ = [ + #Any combination of the AI_SCENE_FLAGS_XXX flags + ("flags", c_uint), #OK + + #The root node of the hierarchy. + #There will always be at least the root node if the import + #was successful. 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), #OK + + #The array of meshes. + #Use the indices given in the aiNode structure to access + #this array. The array is mNumMeshes in size. + ("mMeshes", POINTER(POINTER(MESH))), #OK + + #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. + ("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 embedd their textures into the file. + #An example is Quake's MDL format (which is also used by + #some GameStudio(TM) versions) + ("mTextures", POINTER(POINTER(TEXTURE))) + ] \ No newline at end of file diff --git a/port/PyAssimp/sample.py b/port/PyAssimp/sample.py new file mode 100644 index 000000000..b3fe8866c --- /dev/null +++ b/port/PyAssimp/sample.py @@ -0,0 +1,24 @@ +from pyassimp import pyassimp + +MODEL = r"../test.ase" + +def main(): + scene = pyassimp.load(MODEL) + + #write some statistics + print "SCENE:" + print " flags:", ", ".join(scene.list_flags()) + print " meshes:", len(scene.meshes) + print "" + print "MESHES:" + for index, mesh in enumerate(scene.meshes): + print " MESH", index+1 + print " vertices:", len(mesh.vertices) + print " first:", mesh.vertices[:3] + print " colors:", len(mesh.colors) + print " uv-counts:", mesh.numuv + print "" + + +if __name__ == "__main__": + main() \ No newline at end of file