mirror of https://github.com/wanadev/yoga.git
274 lines
7.3 KiB
Python
274 lines
7.3 KiB
Python
"""
|
|
This module allows to convert and optimize 3D models.
|
|
|
|
|
|
Usage
|
|
-----
|
|
|
|
converting and optimizing a model::
|
|
|
|
import yoga.model
|
|
yoga.model.optimize("./input.fbx", "./output.glb")
|
|
|
|
You can also tune the output by passing options::
|
|
|
|
yoga.model.optimize("./input.fbx", "./output.glb", options={
|
|
# Model options
|
|
"output_format": "glb", # "glb"|"gltf"
|
|
"fallback_texture": None, # None|<FileLike>|"./image.png"
|
|
"no_graph_optimization": False, # True|False
|
|
"no_meshes_optimization": False, # True|False
|
|
"no_textures_optimization": False, # True|False
|
|
"no_fix_infacing_normals": False, # True|False
|
|
|
|
# Images (textures) options
|
|
"image_output_format": "orig", # "orig"|"auto"|"jpeg"|"png"|"webp"|"webpl"
|
|
"image_resize": "orig", # "orig"|[width,height]
|
|
"image_jpeg_quality": 0.84, # 0.00-1.0
|
|
"image_webp_quality": 0.90, # 0.00-1.0
|
|
"image_opacity_threshold": 254, # 0-255
|
|
"image_png_slow_optimization": False, # True|False
|
|
})
|
|
|
|
|
|
Available Model Options
|
|
-----------------------
|
|
|
|
output_format
|
|
~~~~~~~~~~~~~
|
|
|
|
The format of the output model.
|
|
|
|
::
|
|
|
|
yoga.model.optimize("./input.fbx", "./output.glb", options={
|
|
"output_format": "glb",
|
|
})
|
|
|
|
The following formats are supported:
|
|
|
|
* ``glb`` (default)
|
|
* ``gltf``
|
|
|
|
|
|
fallback_texture
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
This option allows you to provide a fallback texture that will be used when
|
|
YOGA is unable to find one of the model textures.
|
|
|
|
The following values are allowed:
|
|
|
|
* ``None``: no fallback texture,
|
|
* a Python ``str`` (and ``unicode`` for Python 2): path of a fallback image
|
|
file (e.g. ``"./fallback.png"``),
|
|
* a "File-like" object (file, ``BytesIO``, etc.).
|
|
|
|
::
|
|
|
|
yoga.model.optimize("./input.fbx", "./output.glb", options={
|
|
"fallback_texture": None,
|
|
})
|
|
|
|
::
|
|
|
|
yoga.model.optimize("./input.fbx", "./output.glb", options={
|
|
"fallback_texture": "./fallback.png",
|
|
})
|
|
|
|
::
|
|
|
|
image_file = open("./fallback.png", "rb")
|
|
yoga.model.optimize("./input.fbx", "./output.glb", options={
|
|
"fallback_texture": image_file,
|
|
})
|
|
|
|
|
|
no_graph_optimization
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Disables empty graph nodes merging.
|
|
|
|
::
|
|
|
|
yoga.model.optimize("./input.fbx", "./output.glb", options={
|
|
"no_graph_optimization": False,
|
|
})
|
|
|
|
|
|
no_meshes_optimization
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Disable mesh optimization.
|
|
|
|
::
|
|
|
|
yoga.model.optimize("./input.fbx", "./output.glb", options={
|
|
"no_meshes_optimization": False,
|
|
})
|
|
|
|
|
|
no_textures_optimization
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Disable texture optimizations (textures will not be optimized using YOGA
|
|
Image).
|
|
|
|
::
|
|
|
|
yoga.model.optimize("./input.fbx", "./output.glb", options={
|
|
"no_textures_optimization": False,
|
|
})
|
|
|
|
|
|
no_fix_infacing_normals
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Disables the assimp's infacing normals fix. This postprocess tries to determine
|
|
which meshes have normal vectors that are facing inwards and inverts them. See
|
|
the `assimp documentation
|
|
<http://assimp.sourceforge.net/lib_html/postprocess_8h.html>`_ for more
|
|
details.
|
|
|
|
::
|
|
|
|
yoga.model.optimize("./input.fbx", "./output.glb", options={
|
|
"no_fix_infacing_normals": False,
|
|
})
|
|
|
|
|
|
Available Image Options
|
|
-----------------------
|
|
|
|
YOGA Model optimizes textures using YOGA Images, so there are options
|
|
equivalent to the YOGA Image ones available:
|
|
|
|
* The YOGA Model ``image_output_format`` option is equivalent to the
|
|
``output_format`` of YOGA Image,
|
|
|
|
* The YOGA Model ``image_resize`` option is equivalent to the ``resize`` of
|
|
YOGA Image,
|
|
|
|
* The YOGA Model ``image_jpeg_quality`` option is equivalent to the
|
|
``jpeg_quality`` of YOGA Image,
|
|
|
|
* The YOGA Model ``image_webp_quality`` option is equivalent to the
|
|
``webp_quality`` of YOGA Image,
|
|
|
|
* The YOGA Model ``image_opacity_threshold`` option is equivalent to the
|
|
``opacity_threshold`` of YOGA Image,
|
|
|
|
* The YOGA Model ``image_png_slow_optimization`` option is equivalent to the
|
|
``png_slow_optimization`` of YOGA Image,
|
|
|
|
* The YOGA Model ``image_enable_quantization`` option is equivalent to the
|
|
``enable_quantization`` of YOGA Image,
|
|
|
|
* The YOGA Model ``image_quantization_dithering_level`` option is equivalent to the
|
|
``quantization_dithering_level`` of YOGA Image,
|
|
|
|
* The YOGA Model ``image_quantization_max_colors`` option is equivalent to the
|
|
``quantization_max_colors`` of YOGA Image,
|
|
|
|
See :ref:`YOGA Image options <yoga_image_api_options>` for more information.
|
|
|
|
|
|
API
|
|
---
|
|
"""
|
|
|
|
import sys
|
|
import os.path
|
|
|
|
from .assimp import assimp_import_from_bytes, assimp_export_to_bytes
|
|
from .options import normalize_options, extract_image_options
|
|
from .helpers import model_embed_images
|
|
|
|
|
|
def optimize(
|
|
input_file,
|
|
output_file,
|
|
options={},
|
|
textures=None,
|
|
verbose=False,
|
|
quiet=False,
|
|
):
|
|
"""Optimize given model.
|
|
|
|
:param str,file-like input_file: The input model file.
|
|
:param str,file-like output_file: The output model file.
|
|
:param dict options: Optimization options (see above).
|
|
:param dict textures: A dictionary that maps textures path to bytes. When
|
|
not ``None``, there will be no file system reads in
|
|
order to find referenced textures. YOGA will look
|
|
into that dictionary instead.
|
|
:param bool verbose: If ``True``, Assmimp debug message will be print to
|
|
stdout.
|
|
:param bool quiet: If ``True``, YOGA will not write any warning on stdout
|
|
or stderr.
|
|
|
|
Example ``textures`` dictionary::
|
|
|
|
textures = {
|
|
"images/texture1.png": open("./images/texture1.png", "rb").read(),
|
|
"texture2.png": open("./texture1.png", "rb").read(),
|
|
}
|
|
"""
|
|
|
|
model_options = normalize_options(options)
|
|
image_options = extract_image_options(options)
|
|
|
|
if quiet:
|
|
verbose = False
|
|
|
|
# Open file if possible
|
|
if not hasattr(input_file, "read"):
|
|
input_file = open(input_file, "rb")
|
|
|
|
root_path = None
|
|
if hasattr(input_file, "name"):
|
|
root_path = os.path.dirname(os.path.abspath(input_file.name))
|
|
|
|
if sys.version_info.major == 2 and type(root_path) is str:
|
|
root_path = root_path.decode("utf-8")
|
|
|
|
# input_file -> string (path), bytes, file-like
|
|
if hasattr(input_file, "read"):
|
|
input_file = input_file.read()
|
|
|
|
# Import the scene
|
|
scene = assimp_import_from_bytes(
|
|
input_file,
|
|
not model_options["no_graph_optimization"],
|
|
not model_options["no_meshes_optimization"],
|
|
not model_options["no_fix_infacing_normals"],
|
|
verbose,
|
|
)
|
|
|
|
# Embed images
|
|
# @note We save the bytes to a dictionary so that the garbage collector
|
|
# does not occur before exporting the scene a bit later
|
|
images_bytes = {}
|
|
model_embed_images(
|
|
scene["cffi_pointer"].images,
|
|
images_bytes,
|
|
not model_options["no_textures_optimization"],
|
|
model_options["fallback_texture"],
|
|
root_path,
|
|
image_options,
|
|
textures,
|
|
quiet,
|
|
)
|
|
|
|
# Export the scene
|
|
bytes_out = assimp_export_to_bytes(
|
|
scene["cffi_pointer"],
|
|
model_options["output_format"],
|
|
)
|
|
|
|
# Write to output
|
|
if not hasattr(output_file, "write"):
|
|
output_file = open(output_file, "wb")
|
|
|
|
output_file.write(bytes_out)
|