Usage
Model import
The whole work to import a model with assimpcy is done by 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.
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 aiImportFile()
Textures
To locate the model’s textures, first you need to retrieve the materials, let’s say material 0:
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
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:
A file in the model’s folder (or a subfolder),
A file in an accompanying archive (e.g., “textures.zip”), or
An embedded texture included in the scene structure, under
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 FileInfo objects in the
textures dictionary of each material:
keys, values = zip(*material0.textures.items())
Each value will be an instance of 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:
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 scene.mTextures list.
To extract them:
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 convertFile() function of
Exporter.
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’
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 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.