From a77bee47665d33ec64d6225f6fdce47572e9cb5f Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Mon, 3 Dec 2018 23:33:02 +0100 Subject: glTF: Various export fixs/enhancements * Extras * AlphaMode, material names * fix camera export * refactoring export option * apply modifier option --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_export.py | 117 ++++++++++++--------- io_scene_gltf2/blender/exp/gltf2_blender_gather.py | 2 - .../blender/exp/gltf2_blender_gather_materials.py | 16 ++- .../blender/exp/gltf2_blender_gather_mesh.py | 6 +- .../blender/exp/gltf2_blender_gather_nodes.py | 39 ++++--- .../blender/exp/gltf2_blender_gltf2_exporter.py | 7 +- 7 files changed, 116 insertions(+), 73 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index e2877db9..10577813 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -348,7 +348,7 @@ class ExportGLTF2_Base: export_settings['gltf_binary'] = bytearray() export_settings['gltf_binaryfilename'] = os.path.splitext(os.path.basename(self.filepath))[0] + '.bin' - return gltf2_blender_export.save(self, context, export_settings) + return gltf2_blender_export.save(context, export_settings) def draw(self, context): layout = self.layout diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_export.py b/io_scene_gltf2/blender/exp/gltf2_blender_export.py index 1adbe473..7ef3941d 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_export.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_export.py @@ -12,76 +12,93 @@ # See the License for the specific language governing permissions and # limitations under the License. -import bpy import sys import traceback -from . import gltf2_blender_export_keys -from . import gltf2_blender_gather -from .gltf2_blender_gltf2_exporter import GlTF2Exporter -from ..com import gltf2_blender_json -from ...io.exp import gltf2_io_export -from ...io.com.gltf2_io_debug import print_console, print_newline +from io_scene_gltf2.blender.com import gltf2_blender_json +from io_scene_gltf2.blender.exp import gltf2_blender_export_keys +from io_scene_gltf2.blender.exp import gltf2_blender_gather +from io_scene_gltf2.blender.exp.gltf2_blender_gltf2_exporter import GlTF2Exporter +from io_scene_gltf2.io.com.gltf2_io_debug import print_console, print_newline +from io_scene_gltf2.io.exp import gltf2_io_export -def save(operator, - context, - export_settings): +def save(context, export_settings): """Start the glTF 2.0 export and saves to content either to a .gltf or .glb file.""" - print_console('INFO', 'Starting glTF 2.0 export') - context.window_manager.progress_begin(0, 100) - context.window_manager.progress_update(0) + __notify_start(context) + json, buffer = __export(export_settings) + __write_file(json, buffer, export_settings) + __notify_end(context) + return {'FINISHED'} + + +def __export(export_settings): + exporter = GlTF2Exporter(__get_copyright(export_settings)) + __add_root_objects(exporter, export_settings) + buffer = __create_buffer(exporter, export_settings) + exporter.finalize_images(export_settings[gltf2_blender_export_keys.FILE_DIRECTORY]) + json = __fix_json(exporter.glTF.to_dict()) - if not export_settings[gltf2_blender_export_keys.COPYRIGHT]: - export_settings[gltf2_blender_export_keys.COPYRIGHT] = None + return json, buffer + +def __get_copyright(export_settings): + if export_settings[gltf2_blender_export_keys.COPYRIGHT]: + return export_settings[gltf2_blender_export_keys.COPYRIGHT] + return None + + +def __add_root_objects(exporter, export_settings): scenes, animations = gltf2_blender_gather.gather_gltf2(export_settings) - exporter = GlTF2Exporter(copyright=export_settings[gltf2_blender_export_keys.COPYRIGHT]) for scene in scenes: exporter.add_scene(scene) for animation in animations: exporter.add_animation(animation) + +def __create_buffer(exporter, export_settings): buffer = bytes() - if export_settings[gltf2_blender_export_keys.FORMAT] != 'GLB': - # .gltf + if export_settings[gltf2_blender_export_keys.FORMAT] == 'GLB': + buffer = exporter.finalize_buffer(export_settings[gltf2_blender_export_keys.FILE_DIRECTORY], is_glb=True) + else: if export_settings[gltf2_blender_export_keys.FORMAT] == 'GLTF': exporter.finalize_buffer(export_settings[gltf2_blender_export_keys.FILE_DIRECTORY]) else: exporter.finalize_buffer(export_settings[gltf2_blender_export_keys.FILE_DIRECTORY], export_settings[gltf2_blender_export_keys.BINARY_FILENAME]) - else: - # .glb - buffer = exporter.finalize_buffer(export_settings[gltf2_blender_export_keys.FILE_DIRECTORY], is_glb=True) - exporter.finalize_images(export_settings[gltf2_blender_export_keys.FILE_DIRECTORY]) - glTF = exporter.glTF - # + return buffer + +def __fix_json(obj): # TODO: move to custom JSON encoder - def dict_strip(obj): - o = obj - if isinstance(obj, dict): - o = {} - for k, v in obj.items(): - if v is None: - continue - elif isinstance(v, list) and len(v) == 0: - continue - o[k] = dict_strip(v) - elif isinstance(obj, list): - o = [] - for v in obj: - o.append(dict_strip(v)) - elif isinstance(obj, float): - # force floats to int, if they are integers (prevent INTEGER_WRITTEN_AS_FLOAT validator warnings) - if int(obj) == obj: - return int(obj) - return o + fixed = obj + if isinstance(obj, dict): + fixed = {} + for key, value in obj.items(): + if value is None: + continue + elif isinstance(value, list) and len(value) == 0: + continue + fixed[key] = __fix_json(value) + elif isinstance(obj, list): + fixed = [] + for value in obj: + fixed.append(__fix_json(value)) + elif isinstance(obj, float): + # force floats to int, if they are integers (prevent INTEGER_WRITTEN_AS_FLOAT validator warnings) + if int(obj) == obj: + return int(obj) + return fixed + +def __write_file(json, buffer, export_settings): try: - gltf2_io_export.save_gltf(dict_strip(glTF.to_dict()), export_settings, gltf2_blender_json.BlenderJSONEncoder, - buffer) + gltf2_io_export.save_gltf( + json, + export_settings, + gltf2_blender_json.BlenderJSONEncoder, + buffer) except AssertionError as e: _, _, tb = sys.exc_info() traceback.print_tb(tb) # Fixed format @@ -92,9 +109,15 @@ def save(operator, print_console('ERROR', str(e)) raise e + +def __notify_start(context): + print_console('INFO', 'Starting glTF 2.0 export') + context.window_manager.progress_begin(0, 100) + context.window_manager.progress_update(0) + + +def __notify_end(context): print_console('INFO', 'Finished glTF 2.0 export') context.window_manager.progress_end() print_newline() - return {'FINISHED'} - diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather.py index 6f4f3b1e..ad9e7619 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather.py @@ -50,8 +50,6 @@ def __gather_scene(blender_scene, export_settings): if node is not None: scene.nodes.append(node) - # TODO: lights - return scene diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py index 357b9061..4e97f3a9 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py @@ -21,6 +21,7 @@ from io_scene_gltf2.blender.exp import gltf2_blender_gather_material_normal_text from io_scene_gltf2.blender.exp import gltf2_blender_gather_material_occlusion_texture_info_class from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials_pbr_metallic_roughness +from io_scene_gltf2.blender.exp import gltf2_blender_generate_extras from io_scene_gltf2.blender.exp import gltf2_blender_get @@ -76,10 +77,16 @@ def __filter_material(blender_material, export_settings): def __gather_alpha_cutoff(blender_material, export_settings): + if blender_material.blend_method == 'CLIP': + return blender_material.alpha_threshold return None def __gather_alpha_mode(blender_material, export_settings): + if blender_material.blend_method == 'CLIP': + return 'MASK' + elif blender_material.blend_method == 'BLEND': + return 'BLEND' return None @@ -90,7 +97,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): - return list(emissive_socket.default_value) + return list(emissive_socket.default_value)[0:3] return None @@ -108,13 +115,14 @@ def __gather_extensions(blender_material, export_settings): return extensions if extensions else None -def __gather_extras(blender_material, export_setttings): +def __gather_extras(blender_material, export_settings): + if export_settings['gltf_extras']: + return gltf2_blender_generate_extras.generate_extras(blender_material) return None def __gather_name(blender_material, export_settings): - - return None + return blender_material.name def __gather_normal_texture(blender_material, export_settings): diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py index 57903287..f32eb733 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py @@ -17,15 +17,17 @@ from typing import Optional, Dict, List, Any from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached from io_scene_gltf2.io.com import gltf2_io from io_scene_gltf2.blender.exp import gltf2_blender_gather_primitives +from io_scene_gltf2.blender.exp import gltf2_blender_generate_extras @cached def gather_mesh(blender_mesh: bpy.types.Mesh, vertex_groups: Optional[bpy.types.VertexGroups], modifiers: Optional[bpy.types.ObjectModifiers], + skip_filter: bool, export_settings ) -> Optional[gltf2_io.Mesh]: - if not __filter_mesh(blender_mesh, vertex_groups, modifiers, export_settings): + if not skip_filter and not __filter_mesh(blender_mesh, vertex_groups, modifiers, export_settings): return None mesh = gltf2_io.Mesh( @@ -62,6 +64,8 @@ def __gather_extras(blender_mesh: bpy.types.Mesh, modifiers: Optional[bpy.types.ObjectModifiers], export_settings ) -> Optional[Dict[Any, Any]]: + if export_settings['gltf_extras']: + return gltf2_blender_generate_extras.generate_extras(blender_mesh) return None diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py index dbfafbde..0913d464 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py @@ -24,6 +24,7 @@ from io_scene_gltf2.blender.exp import gltf2_blender_gather_mesh from io_scene_gltf2.blender.exp import gltf2_blender_gather_joints from io_scene_gltf2.blender.exp import gltf2_blender_extract from io_scene_gltf2.blender.exp import gltf2_blender_gather_lights +from io_scene_gltf2.blender.exp import gltf2_blender_generate_extras from io_scene_gltf2.io.com import gltf2_io from io_scene_gltf2.io.com import gltf2_io_extensions @@ -57,8 +58,8 @@ def gather_node(blender_object, export_settings): if blender_object.type == 'CAMERA' and export_settings[gltf2_blender_export_keys.CAMERAS]: correction_node = __get_correction_node(blender_object, export_settings) correction_node.camera = node.camera - node.camera = None node.children.append(correction_node) + node.camera = None return node @@ -129,6 +130,8 @@ def __gather_extensions(blender_object, export_settings): def __gather_extras(blender_object, export_settings): + if export_settings['gltf_extras']: + return gltf2_blender_generate_extras.generate_extras(blender_object) return None @@ -138,19 +141,31 @@ def __gather_matrix(blender_object, export_settings): def __gather_mesh(blender_object, export_settings): - if blender_object.type == "MESH": - # If not using vertex group, they are irrelevant for caching --> ensure that they do not trigger a cache miss - vertex_groups = blender_object.vertex_groups - modifiers = blender_object.modifiers - if len(vertex_groups) == 0: - vertex_groups = None - if len(modifiers) == 0: - modifiers = None - - return gltf2_blender_gather_mesh.gather_mesh(blender_object.data, vertex_groups, modifiers, export_settings) - else: + if blender_object.type != "MESH": return None + # If not using vertex group, they are irrelevant for caching --> ensure that they do not trigger a cache miss + vertex_groups = blender_object.vertex_groups + modifiers = blender_object.modifiers + if len(vertex_groups) == 0: + vertex_groups = None + if len(modifiers) == 0: + modifiers = None + + if export_settings[gltf2_blender_export_keys.APPLY]: + blender_mesh = blender_object.to_mesh(bpy.context.depsgraph, True) + skip_filter = True + else: + blender_mesh = blender_object.data + skip_filter = False + + result = gltf2_blender_gather_mesh.gather_mesh(blender_mesh, vertex_groups, modifiers, skip_filter, export_settings) + + if export_settings[gltf2_blender_export_keys.APPLY]: + bpy.data.meshes.remove(blender_mesh) + + return result + def __gather_name(blender_object, export_settings): if blender_object.instance_type == 'COLLECTION' and blender_object.instance_collection: diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py b/io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py index 919e3d3a..b1408b62 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py @@ -105,12 +105,7 @@ class GlTF2Exporter: return self.__gltf def finalize_buffer(self, output_path=None, buffer_name=None, is_glb=False): - """ - Finalize the glTF and write buffers. - - :param buffer_path: - :return: - """ + """Finalize the glTF and write buffers.""" if self.__finalized: raise RuntimeError("Tried to finalize buffers for finalized glTF file") -- cgit v1.2.3