Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions addons/io_scene_gltf2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,8 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
'Be aware of a possible loss in quality'),
('WEBP', 'WebP Format',
'Save images as WebPs as main image (no fallback)'),
('KTX2', 'KTX2 Format',
'Save images as KTX2 with BasisU supercompression'),
('NONE', 'None',
'Don\'t export images'),
),
Expand All @@ -370,23 +372,33 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
default='AUTO'
)

export_image_add_webp: BoolProperty(
name='Create WebP',
export_image_add_compressed_images: BoolProperty(
name='Create Compressed Images',
description=(
"Creates WebP textures for every texture. "
"For already WebP textures, nothing happens"
"Creates compressed textures (WebP or KTX2)for every texture. "
"For already compressed textures, nothing happens"
),
default=False
)

export_image_webp_fallback: BoolProperty(
name='WebP Fallback',
export_compressed_images_fallback: BoolProperty(
name='Compressed Images Fallback',
description=(
"For all WebP textures, create a PNG fallback texture"
"For all compressed textures (WebP or KTX2), create a PNG fallback texture"
),
default=False
)

export_compressed_images_type: EnumProperty(
name='Compressed Images Type',
items=(('WEBP', 'WebP', 'Use WebP format for compressed textures'),
('KTX2', 'KTX2', 'Use KTX2 format with BasisU supercompression for compressed textures')),
description=(
"Choose the format for compressed textures fallback (WebP or KTX2 with BasisU supercompression)"
),
default='WEBP'
)

export_texture_dir: StringProperty(
name='Textures',
description='Folder to place texture files in. Relative to the .gltf file',
Expand Down Expand Up @@ -1139,8 +1151,9 @@ def execute(self, context):

export_settings['gltf_format'] = self.export_format
export_settings['gltf_image_format'] = self.export_image_format
export_settings['gltf_add_webp'] = self.export_image_add_webp
export_settings['gltf_webp_fallback'] = self.export_image_webp_fallback
export_settings['gltf_add_compressed_images'] = self.export_image_add_compressed_images
export_settings['gltf_compressed_images_fallback'] = self.export_compressed_images_fallback
export_settings['gltf_compressed_images_type'] = self.export_compressed_images_type
export_settings['gltf_image_quality'] = self.export_image_quality
export_settings['gltf_copyright'] = self.export_copyright
export_settings['gltf_texcoords'] = self.export_texcoords
Expand Down Expand Up @@ -1548,13 +1561,16 @@ def export_panel_data_material(layout, operator):
if operator.export_image_format in ["AUTO", "JPEG", "WEBP"]:
col.prop(operator, 'export_image_quality')
col = body.column()
col.active = operator.export_image_format != "WEBP" and operator.export_materials not in [
col.active = operator.export_image_format not in ["WEBP", "KTX2"] and operator.export_materials not in [
'PLACEHOLDER', 'NONE', 'VIEWPORT']
col.prop(operator, "export_image_add_webp")
col.prop(operator, "export_image_add_compressed_images")
col = body.column()
col.active = operator.export_image_add_compressed_images
col.prop(operator, "export_compressed_images_type")
col = body.column()
col.active = operator.export_image_format != "WEBP" and operator.export_materials not in [
col.active = operator.export_image_format not in ["WEBP", "KTX2"] and operator.export_materials not in [
'PLACEHOLDER', 'NONE', 'VIEWPORT']
col.prop(operator, "export_image_webp_fallback")
col.prop(operator, "export_compressed_images_fallback")

header, sub_body = body.panel("GLTF_export_data_material_unused", default_closed=True)
header.label(text="Unused Textures & Images")
Expand Down
12 changes: 10 additions & 2 deletions addons/io_scene_gltf2/blender/exp/material/encode_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ def encode(self, mime_type: Optional[str], export_settings) -> Tuple[bytes, bool
self.file_format = {
"image/jpeg": "JPEG",
"image/png": "PNG",
"image/webp": "WEBP"
"image/webp": "WEBP",
"image/ktx2": "KTX2",
}.get(mime_type, "PNG")

# Happy path = we can just use an existing Blender image
Expand Down Expand Up @@ -415,6 +416,9 @@ def __encode_from_image(self, image: bpy.types.Image, export_settings) -> bytes:
elif self.file_format == 'WEBP':
if data[8:12] == b'WEBP':
return data
elif self.file_format == 'KTX2':
if data[0:12] == b'\xABKTX 20\xBB\r\n\x1A\n':
return data

# Copy to a temp image and save.
with TmpImageGuard() as guard:
Expand All @@ -440,7 +444,9 @@ def __encode_from_image_tile(self, udim_image, tile, export_settings):
elif self.file_format == 'WEBP':
if data[8:12] == b'WEBP':
return data

elif self.file_format == 'KTX2':
if data[0:12] == b'\xABKTX 20\xBB\r\n\x1A\n':
return data
# We don't manage UDIM packed image, so this could not happen to be here
# Lets display an error
export_settings['log'].error(
Expand All @@ -459,6 +465,8 @@ def _encode_temp_image(tmp_image: bpy.types.Image, file_format: str, export_sett
# if image is jpeg, use quality export settings
if file_format in ["JPEG", "WEBP"]:
tmp_image.save(quality=export_settings['gltf_image_quality'])
elif file_format == "KTX2":
tmp_image.save() # TODO add KTX2 options
else:
tmp_image.save()

Expand Down
27 changes: 27 additions & 0 deletions addons/io_scene_gltf2/blender/exp/material/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,15 @@ def __gather_mime_type(sockets, export_image, export_settings):
if socket.socket.identifier == "Alpha":
if export_settings["gltf_image_format"] == "WEBP":
return "image/webp"
elif export_settings["gltf_image_format"] == "KTX2":
return "image/ktx2"
else:
# If we keep image as is (no channel composition), we need to keep original format (for WebP)
image = export_image.blender_image(export_settings)
if image is not None and __is_blender_image_a_webp(image):
return "image/webp"
elif image is not None and __is_blender_image_a_ktx2(image):
return "image/ktx2"
return "image/png"

if export_settings["gltf_image_format"] == "AUTO":
Expand All @@ -168,12 +172,16 @@ def __gather_mime_type(sockets, export_image, export_settings):
return "image/jpeg"
elif image is not None and __is_blender_image_a_webp(image):
return "image/webp"
elif image is not None and __is_blender_image_a_ktx2(image):
return "image/ktx2"
return "image/png"

elif export_settings["gltf_image_format"] == "WEBP":
return "image/webp"
elif export_settings["gltf_image_format"] == "JPEG":
return "image/jpeg"
elif export_settings["gltf_image_format"] == "KTX2":
return "image/ktx2"


def __gather_name(export_image, use_tile, export_settings):
Expand Down Expand Up @@ -486,6 +494,17 @@ def __is_blender_image_a_webp(image: bpy.types.Image) -> bool:
return path.endswith('.webp')


def __is_blender_image_a_ktx2(image: bpy.types.Image) -> bool:
if image.source not in ['FILE', 'TILED']:
return False
if image.filepath_raw == '' and image.packed_file:
# Magic of ktx2
return image.packed_file.data[:12] == b'\xABKTX 22\xBB\r\n\x1A\n'
else:
path = image.filepath_raw.lower()
return path.endswith('.ktx2')


def get_gltf_image_from_blender_image(blender_image_name, export_settings):
export_image = ExportImage.from_blender_image(bpy.data.images[blender_image_name])

Expand Down Expand Up @@ -518,7 +537,15 @@ def __get_mime_type_of_image(blender_image_name, export_settings):
return "image/jpeg"
elif __is_blender_image_a_webp(image):
return "image/webp"
elif __is_blender_image_a_ktx2(image):
return "image/ktx2"
return "image/png"

elif export_settings["gltf_image_format"] == "JPEG":
return "image/jpeg"
elif export_settings["gltf_image_format"] == "WEBP":
return "image/webp"
elif export_settings["gltf_image_format"] == "KTX2":
return "image/ktx2"
else:
return "image/png"
Loading
Loading