glTF exporter: Options tweaks, image merging fixes
This commit is contained in:
parent
0c494907cf
commit
3087c4e458
|
@ -74,11 +74,18 @@ class ExportGLTF2_Base:
|
|||
|
||||
export_format: EnumProperty(
|
||||
name='Format',
|
||||
items=(('GLB', 'glb', 'Exports a single file, with all data packed in binary form. Most efficient and portable, but more difficult to edit later'),
|
||||
('GLTF_EMBEDDED', 'glTF embedded)', 'Exports a single file, with all data packed in JSON. Less efficient than binary, but easier to edit later'),
|
||||
('GLTF', 'glTF', 'Exports multiple files, with separate JSON (.gltf), binary (.bin), and texture data. Easiest to edit later')),
|
||||
description='Output format and embedding options. Binary is most efficient, but JSON (embedded or separate) may be easier to edit later',
|
||||
default='GLTF'
|
||||
items=(('GLB', 'glTF Binary (.glb)',
|
||||
'Exports a single file, with all data packed in binary form. '
|
||||
'Most efficient and portable, but more difficult to edit later'),
|
||||
('GLTF_EMBEDDED', 'glTF Embedded (.gltf)',
|
||||
'Exports a single file, with all data packed in JSON. '
|
||||
'Less efficient than binary, but easier to edit later'),
|
||||
('GLTF_SEPARATE', 'glTF Separate (.gltf + .bin + textures)',
|
||||
'Exports multiple files, with separate JSON, binary and texture data. '
|
||||
'Easiest to edit later')),
|
||||
description='Output format and embedding options. Binary is most efficient, '
|
||||
'but JSON (embedded or separate) may be easier to edit later.',
|
||||
default='GLB'
|
||||
)
|
||||
|
||||
export_copyright: StringProperty(
|
||||
|
|
|
@ -18,6 +18,8 @@ from io_scene_gltf2.io.com import gltf2_io
|
|||
from io_scene_gltf2.blender.exp import gltf2_blender_gather_nodes
|
||||
from io_scene_gltf2.blender.exp import gltf2_blender_gather_animations
|
||||
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
|
||||
from io_scene_gltf2.blender.exp import gltf2_blender_generate_extras
|
||||
from io_scene_gltf2.blender.exp import gltf2_blender_export_keys
|
||||
|
||||
|
||||
def gather_gltf2(export_settings):
|
||||
|
@ -39,7 +41,7 @@ def gather_gltf2(export_settings):
|
|||
def __gather_scene(blender_scene, export_settings):
|
||||
scene = gltf2_io.Scene(
|
||||
extensions=None,
|
||||
extras=None,
|
||||
extras=__gather_extras(blender_scene, export_settings),
|
||||
name=blender_scene.name,
|
||||
nodes=[]
|
||||
)
|
||||
|
@ -59,3 +61,9 @@ def __gather_animations(blender_scene, export_settings):
|
|||
animations += gltf2_blender_gather_animations.gather_animations(blender_object, export_settings)
|
||||
return animations
|
||||
|
||||
|
||||
def __gather_extras(blender_object, export_settings):
|
||||
if export_settings[gltf2_blender_export_keys.EXTRAS]:
|
||||
return gltf2_blender_generate_extras.generate_extras(blender_object)
|
||||
return None
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ def __filter_image(sockets_or_slots, export_settings):
|
|||
|
||||
|
||||
def __gather_buffer_view(sockets_or_slots, export_settings):
|
||||
if export_settings[gltf2_blender_export_keys.FORMAT] != 'GLTF':
|
||||
if export_settings[gltf2_blender_export_keys.FORMAT] != 'GLTF_SEPARATE':
|
||||
image = __get_image_data(sockets_or_slots)
|
||||
return gltf2_io_binary_data.BinaryData(
|
||||
data=image.to_image_data(__gather_mime_type(sockets_or_slots, export_settings)))
|
||||
|
@ -79,7 +79,7 @@ def __gather_name(sockets_or_slots, export_settings):
|
|||
|
||||
|
||||
def __gather_uri(sockets_or_slots, export_settings):
|
||||
if export_settings[gltf2_blender_export_keys.FORMAT] == 'GLTF':
|
||||
if export_settings[gltf2_blender_export_keys.FORMAT] == 'GLTF_SEPARATE':
|
||||
# as usual we just store the data in place instead of already resolving the references
|
||||
return __get_image_data(sockets_or_slots)
|
||||
return None
|
||||
|
@ -121,6 +121,7 @@ def __get_image_data(sockets_or_slots):
|
|||
pixels = [split_pixels_by_channels(result.shader_node.image)[channel]]
|
||||
else:
|
||||
pixels = split_pixels_by_channels(result.shader_node.image)
|
||||
channel = 0
|
||||
|
||||
file_name = os.path.splitext(result.shader_node.image.name)[0]
|
||||
|
||||
|
@ -128,12 +129,13 @@ def __get_image_data(sockets_or_slots):
|
|||
file_name,
|
||||
result.shader_node.image.size[0],
|
||||
result.shader_node.image.size[1],
|
||||
channel,
|
||||
pixels)
|
||||
|
||||
if image is None:
|
||||
image = image_data
|
||||
else:
|
||||
image.add_to_image(image_data)
|
||||
image.add_to_image(channel, image_data)
|
||||
|
||||
return image
|
||||
elif __is_slot(sockets_or_slots):
|
||||
|
@ -144,6 +146,7 @@ def __get_image_data(sockets_or_slots):
|
|||
texture.name,
|
||||
texture.image.size[0],
|
||||
texture.image.size[1],
|
||||
0,
|
||||
pixels)
|
||||
return image_data
|
||||
else:
|
||||
|
|
|
@ -96,7 +96,7 @@ def __gather_double_sided(blender_material, export_settings):
|
|||
|
||||
def __gather_emmissive_factor(blender_material, export_settings):
|
||||
emissive_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Emissive")
|
||||
if isinstance(emissive_socket, bpy.types.NodeSocket):
|
||||
if isinstance(emissive_socket, bpy.types.NodeSocket) and not emissive_socket.is_linked:
|
||||
return list(emissive_socket.default_value)[0:3]
|
||||
return None
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ def __gather_base_color_factor(blender_material, export_settings):
|
|||
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Base Color")
|
||||
if base_color_socket is None:
|
||||
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "BaseColor")
|
||||
if isinstance(base_color_socket, bpy.types.NodeSocket):
|
||||
if isinstance(base_color_socket, bpy.types.NodeSocket) and not base_color_socket.is_linked:
|
||||
return list(base_color_socket.default_value)
|
||||
return None
|
||||
|
||||
|
@ -67,7 +67,7 @@ def __gather_extras(blender_material, export_settings):
|
|||
|
||||
def __gather_metallic_factor(blender_material, export_settings):
|
||||
metallic_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Metallic")
|
||||
if isinstance(metallic_socket, bpy.types.NodeSocket):
|
||||
if isinstance(metallic_socket, bpy.types.NodeSocket) and not metallic_socket.is_linked:
|
||||
return metallic_socket.default_value
|
||||
return None
|
||||
|
||||
|
@ -87,7 +87,7 @@ def __gather_metallic_roughness_texture(blender_material, export_settings):
|
|||
|
||||
def __gather_roughness_factor(blender_material, export_settings):
|
||||
roughness_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Roughness")
|
||||
if isinstance(roughness_socket, bpy.types.NodeSocket):
|
||||
if isinstance(roughness_socket, bpy.types.NodeSocket) and not roughness_socket.is_linked:
|
||||
return roughness_socket.default_value
|
||||
return None
|
||||
|
||||
|
|
|
@ -23,21 +23,38 @@ class ImageData:
|
|||
# FUTURE_WORK: as a method to allow the node graph to be better supported, we could model some of
|
||||
# the node graph elements with numpy functions
|
||||
|
||||
def __init__(self, name: str, width: int, height: int, channels: typing.Optional[typing.List[np.ndarray]] = []):
|
||||
def __init__(self, name: str, width: int, height: int, offset: int, channels: typing.Optional[typing.List[np.ndarray]] = []):
|
||||
if width <= 0 or height <= 0:
|
||||
raise ValueError("Image data can not have zero width or height")
|
||||
if offset + len(channels) > 4:
|
||||
raise ValueError("Image data can not have more than 4 channels")
|
||||
|
||||
self.channels = []
|
||||
for fill in range(offset):
|
||||
# Fill before.
|
||||
self.channels.append(np.ones_like(channels[0]))
|
||||
self.channels += channels
|
||||
total_channels = len(self.channels)
|
||||
for fill in range(total_channels, 4):
|
||||
# Fill after.
|
||||
self.channels.append(np.ones_like(channels[0]))
|
||||
|
||||
self.name = name
|
||||
self.channels = channels
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
def add_to_image(self, image_data):
|
||||
def add_to_image(self, channel, image_data):
|
||||
if self.width != image_data.width or self.height != image_data.height:
|
||||
raise ValueError("Image dimensions do not match")
|
||||
if len(self.channels) + len(image_data.channels) > 4:
|
||||
raise ValueError("Can't append image: channels full")
|
||||
if channel < 0 or channel > 3:
|
||||
raise ValueError("Can't append image: channels out of bounds")
|
||||
if len(image_data.channels) != 4:
|
||||
raise ValueError("Can't append image: incomplete image")
|
||||
|
||||
self.name += image_data.name
|
||||
self.channels += image_data.channels
|
||||
|
||||
# Replace channel.
|
||||
self.channels[channel] = image_data.channels[channel]
|
||||
|
||||
@property
|
||||
def r(self):
|
||||
|
@ -73,13 +90,13 @@ class ImageData:
|
|||
|
||||
# if there is no data, create a single pixel image
|
||||
if not channels:
|
||||
channels = np.zeros((1, 1))
|
||||
channels = np.ones((1, 1))
|
||||
|
||||
# fill all channels of the png
|
||||
for _ in range(4 - len(channels)):
|
||||
channels.append(np.ones_like(channels[0]))
|
||||
# fill all channels of the png
|
||||
for _ in range(4 - len(channels)):
|
||||
channels.append(np.ones_like(channels[0]))
|
||||
|
||||
image = np.concatenate(self.channels, axis=1)
|
||||
image = np.concatenate(channels, axis=1)
|
||||
image = image.flatten()
|
||||
image = (image * 255.0).astype(np.uint8)
|
||||
buf = image.tobytes()
|
||||
|
|
Loading…
Reference in New Issue