Usage ===== Model import ^^^^^^^^^^^^ The whole work to import a model with :mod:`assimpcy` is done by :func:`aiImportFile` The package uses the same functions and parameter names of the original library, so examples from the official Assimp docs and other tutorials can be used with minor changes. .. code:: python from assimpcy import aiImportFile, aiPostProcessSteps as pp flags = pp.aiProcess_JoinIdenticalVertices | pp.aiProcess_Triangulate scene = aiImportFile('some_model.3ds', flags) print('Vertex 0 = {}'.format(scene.mMeshes[0].mVertices[0])) Matrices, quaternions and vectors are returned as Numpy arrays. .. note:: There is no need to release the scene. This job is performed by :py:func:`aiImportFile` Textures ^^^^^^^^ To locate the model's textures, first you need to retrieve the materials, let's say *material 0*: .. code:: python material0 = scene.mMaterials[0] Depending on the file format, each material may contain one or more properties called `TEXTURE_usage_IDX`, where: - **usage** corresponds to one of the texture types found on :py:enum:`aiTextureType` enumeration. The actual string used in the property can be queried by passing a type to :py:func:`TextureTypeToString`. - **IDX** is the channel the texture belongs to. For instance, a property called `TEXTURE_DIFFUSE_0` found in material *0* would correspond to the texture file to be used in the slot **0** of the **diffuse** channel of the first material. This property will either be a string pointing to: 1. A file in the model's folder (or a subfolder), 2. A file in an accompanying archive (e.g., "textures.zip"), or 3. An *embedded texture* included in the scene structure, under :py:class:`scene.mTextures`. If it's the latter, the string will start with a "*", followed by the texture's index in `mTextures`:: "*0" corresponds to `scene.mTextures[0]` "*1" is `scene.mTextures[1]` And so on. Some file formats, instead of or in addition to this, include a list of :py:class:`FileInfo` objects in the `textures` dictionary of each material: .. code:: python keys, values = zip(*material0.textures.items()) Each value will be an instance of :py:class:`FileInfo` using a "texture ID" as key (e.g., TEXTURE_DIFFUSE_0). If present, `FileInfo` objects will containing useful information to render every texture of the model. Specifically, three members are required to obtain a texture and assign it: - **type**: One of :py:enum:`aiTextureType`. - **index**: The channel the texture belongs to - **path**: The path where this texture can be located. Has the same value as the material's property with the same key:: `material0.textures['TEXTURE_DIFFUSE_0'].path` == `material0.properties['TEXTURE_DIFFUSE_0']` In any case, to query the presence of a given texture type and retrieve the file path: .. code:: python from assimpcy import getTextureID mat0_textures = {} wanted_textures = ["Diffuse", "Normals", "Base Color"] # values from 'assimpcy.TEXTURE_TYPE_DICT' index = 0 # for channel 0. Query others if needed for wt in wanted_textures: tid = getTextureID(wt, index) if tid in material0.properties: mat0_textures[wt] = material0.properties[tid] elif tid in material0.textures: mat0_textures[wt] = material0.textures[tid].path Embedded textures ^^^^^^^^^^^^^^^^^ When a model includes the textures within the same file, they are stored in the :py:class:`scene.mTextures` list. To extract them: .. code:: python from assimpcy import aiImportFile, aiPostProcessSteps as pp flags = pp.aiProcess_JoinIdenticalVertices | pp.aiProcess_Triangulate scene = aiImportFile('my_model.3ds', flags) if scene.HasTextures: from io import BytesIO from PIL import Image import numpy as np for t in scene.mTextures: data = t.pcData hint = t.achFormatHint.decode() if len(hint) == 3: # the hint indicates the texture format as an extension (e.g., png) img_file = BytesIO(data) img = Image.open(img_file) # let Pillow figure out the image format w, h = img.size data = np.asarray(img) # flatten the data array # to send it to a graphics library else: # no hint or raw data description (e.g., argb8888) w, h = t.mWidth, t.mHeight data = np.reshape(data, (h, w, 4)) # << skip this to keep the array # flat for use in a graphics library # store the 'data' variable for later use or save it as a file # using the extension from the hint, if present, or as a format compatible with the texture components Conversion ^^^^^^^^^^^^^^^^^ You can convert a model directly from one format to another using the :py:func:`convertFile` function of :py:class:`Exporter`. .. code:: python from assimpcy import Exporter exporter = Exporter() model_path = 'my_model.3ds' destination = 'my_exported_model.dae' formatID = 'collada' ret = exporter.convertFile(model_path, destination, formatID) Instead of passing a format name as 'formatID', you can pass an extension supported by your assimp build, e.g., '.dae' .. code:: python ret = exporter.convertFile(model_path, destination, '.dae') This function will convert all the data found in the original file into whatever is supported by the target format. Check a more complete example in the `Github repo `_. .. note:: While :py:func:`convertFile` supports passing flags to modify the scene before export, this might be tricky. We suggest to experiment first with the usual flags and then, if the result is not the expected, remove all the flags and try again. From the Assimp docs: Specifying 'preprocessing' flags is useful if the input scene does not conform to Assimp's default conventions as specified in the Data Structures Page. In short, this means the geometry data should use a right-handed coordinate systems, face winding should be counter-clockwise and the UV coordinate origin is assumed to be in the upper left. The #aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and #aiProcess_FlipWindingOrder flags are used in the import side to allow users to have those defaults automatically adapted to their conventions. Specifying those flags for exporting has the opposite effect, respectively. .. note:: Work is being done to implement arbitrary scene export into any supported format in a future release. Cilly ^^^^^ `Cilly `_ is a silly cylinder that dances and dances. .. image:: https://raw.githubusercontent.com/jr-garcia/AssimpCy/master/examples/models/cilly/cilly.png :alt: Cilly - 3D rigged and textured cylinder