Blender_bevy_components_wor.../tools/gltf_auto_export/helpers_export.py

197 lines
9.2 KiB
Python
Raw Normal View History

import os
import bpy
feat(Save & load): new crate bevy_gltf_save_load + lots of upgrades & improvements (#95) * feat(bevy_gltf_save_load): saving & loading implemented * created new crate for save & load features, uses & filters out blueprints for efficient loading * saving & loading, even with dynamically spawned nested hierarchies works * component filter , resource filter & save path root are configurable * for saving: added removal & cleanup logic for children component with children that have been filtered out: ie no more invalid children getting stored in the save files ! * added sending of event once saving is done * feat(examples/save-load): example for the new crate * loading level static & dynamic data as blueprints * added a bit of ui when entering saving & loading states & cleanup when exiting * feat(bevy_gltf_blueprints): significant rewrite of how the crate works * simplified spawning process, no more spawning children containing blueprints etc * simplified post process : instead of copying original entity into blueprint root we now copy blueprint root data (components & children) into the original entity ! fixes #96 * much simpler code wise * solves issue with needing to register components that we only use on the bevy side (not gltf) since we are not copying the bevy data into the blueprints data * added **copyComponents** helper to copy components from one entity to another, excluding existing components on the target entity, & some bells & whistles * **Name** is now optional when spawning a blueprint: closes #97 * **Transform** is now optional when spawning a blueprint: closes #98 * removed transform from bundle (BREAKING change) * added (optional) **NoInBlueprint** component to have finer control over whether to inject the **InBlueprint** component inside spawned blueprint entities * added (optional) **Library** component, so we can override library path when we want * added (optional) **AddToGameWorld** component for convenience to automatically add entities to the game world, if there is one * chore(bevy_gltf_components): removed verbose output, cleaned it up a bit * feat(tools/auto_export): added option to split out "dynamic" objects in main scenes * ie if a collection instance (or its original collection) has a "dynamic" (aka mutable, saveable etc) flag it can get exported to a seperate gltf file (essentially acting like an "initial save") * the rest of the levels (the "static" data) is exported without the dynamic objects and can be reused with save files !
2024-01-10 13:49:29 +00:00
from .preferences import (AutoExportGltfPreferenceNames)
from .helpers_scenes import (generate_hollow_scene, clear_hollow_scene)
from .helpers_collections import (recurLayerCollection)
from .blueprints import clear_blueprint_hollow_scene, generate_blueprint_hollow_scene
from .helpers import (traverse_tree)
feat(Save & load): new crate bevy_gltf_save_load + lots of upgrades & improvements (#95) * feat(bevy_gltf_save_load): saving & loading implemented * created new crate for save & load features, uses & filters out blueprints for efficient loading * saving & loading, even with dynamically spawned nested hierarchies works * component filter , resource filter & save path root are configurable * for saving: added removal & cleanup logic for children component with children that have been filtered out: ie no more invalid children getting stored in the save files ! * added sending of event once saving is done * feat(examples/save-load): example for the new crate * loading level static & dynamic data as blueprints * added a bit of ui when entering saving & loading states & cleanup when exiting * feat(bevy_gltf_blueprints): significant rewrite of how the crate works * simplified spawning process, no more spawning children containing blueprints etc * simplified post process : instead of copying original entity into blueprint root we now copy blueprint root data (components & children) into the original entity ! fixes #96 * much simpler code wise * solves issue with needing to register components that we only use on the bevy side (not gltf) since we are not copying the bevy data into the blueprints data * added **copyComponents** helper to copy components from one entity to another, excluding existing components on the target entity, & some bells & whistles * **Name** is now optional when spawning a blueprint: closes #97 * **Transform** is now optional when spawning a blueprint: closes #98 * removed transform from bundle (BREAKING change) * added (optional) **NoInBlueprint** component to have finer control over whether to inject the **InBlueprint** component inside spawned blueprint entities * added (optional) **Library** component, so we can override library path when we want * added (optional) **AddToGameWorld** component for convenience to automatically add entities to the game world, if there is one * chore(bevy_gltf_components): removed verbose output, cleaned it up a bit * feat(tools/auto_export): added option to split out "dynamic" objects in main scenes * ie if a collection instance (or its original collection) has a "dynamic" (aka mutable, saveable etc) flag it can get exported to a seperate gltf file (essentially acting like an "initial save") * the rest of the levels (the "static" data) is exported without the dynamic objects and can be reused with save files !
2024-01-10 13:49:29 +00:00
from .dynamic import (is_object_dynamic, is_object_static)
######################################################
#### Export logic #####
def generate_gltf_export_preferences(addon_prefs):
# default values
gltf_export_preferences = dict(
export_format= 'GLB', #'GLB', 'GLTF_SEPARATE', 'GLTF_EMBEDDED'
check_existing=False,
use_selection=False,
use_visible=True, # Export visible and hidden objects. See Object/Batch Export to skip.
use_renderable=False,
use_active_collection= False,
use_active_collection_with_nested=False,
use_active_scene = False,
export_texcoords=True,
export_normals=True,
# here add draco settings
export_draco_mesh_compression_enable = False,
export_tangents=False,
#export_materials
export_colors=True,
export_attributes=True,
#use_mesh_edges
#use_mesh_vertices
export_cameras=True,
export_extras=True, # For custom exported properties.
export_lights=True,
export_yup=True,
export_skins=True,
export_morph=False,
export_apply=False,
export_animations=False
)
for key in addon_prefs.__annotations__.keys():
if str(key) not in AutoExportGltfPreferenceNames:
#print("overriding setting", key, "value", getattr(addon_prefs,key))
gltf_export_preferences[key] = getattr(addon_prefs,key)
return gltf_export_preferences
# find which of the library scenes the given collection stems from
# TODO: does not seem efficient at all ?
def get_source_scene(collection_name, library_scenes):
match = None
for scene in library_scenes:
root_collection = scene.collection
found = False
for cur_collection in traverse_tree(root_collection):
if cur_collection.name == collection_name:
found = True
break
if found:
match = scene
break
return match
# export collections: all the collections that have an instance in the main scene AND any marked collections, even if they do not have instances
def export_collections(collections, folder_path, library_scene, addon_prefs, gltf_export_preferences, blueprint_hierarchy, library_collections):
# set active scene to be the library scene (hack for now)
bpy.context.window.scene = library_scene
# save current active collection
active_collection = bpy.context.view_layer.active_layer_collection
export_materials_library = getattr(addon_prefs,"export_materials_library")
for collection_name in collections:
print("exporting collection", collection_name)
layer_collection = bpy.context.view_layer.layer_collection
layerColl = recurLayerCollection(layer_collection, collection_name)
# set active collection to the collection
bpy.context.view_layer.active_layer_collection = layerColl
gltf_output_path = os.path.join(folder_path, collection_name)
export_settings = { **gltf_export_preferences, 'use_active_scene': True, 'use_active_collection': True, 'use_active_collection_with_nested':True}
# if we are using the material library option, do not export materials, use placeholder instead
if export_materials_library:
export_settings['export_materials'] = 'PLACEHOLDER'
#if relevant we replace sub collections instances with placeholders too
# this is not needed if a collection/blueprint does not have sub blueprints or sub collections
collection_in_blueprint_hierarchy = collection_name in blueprint_hierarchy and len(blueprint_hierarchy[collection_name]) > 0
collection_has_child_collections = len(bpy.data.collections[collection_name].children) > 0
if collection_in_blueprint_hierarchy or collection_has_child_collections:
#print("generate hollow scene for nested blueprints", library_collections)
backup = bpy.context.window.scene
collection = bpy.data.collections[collection_name]
(hollow_scene, temporary_collections, root_objects, special_properties) = generate_blueprint_hollow_scene(collection, library_collections, addon_prefs)
export_gltf(gltf_output_path, export_settings)
clear_blueprint_hollow_scene(hollow_scene, collection, temporary_collections, root_objects, special_properties)
bpy.context.window.scene = backup
else:
#print("standard export")
export_gltf(gltf_output_path, export_settings)
# reset active collection to the one we save before
bpy.context.view_layer.active_layer_collection = active_collection
def export_blueprints_from_collections(collections, library_scene, folder_path, addon_prefs, blueprint_hierarchy, library_collections):
export_output_folder = getattr(addon_prefs,"export_output_folder")
gltf_export_preferences = generate_gltf_export_preferences(addon_prefs)
export_blueprints_path = os.path.join(folder_path, export_output_folder, getattr(addon_prefs,"export_blueprints_path")) if getattr(addon_prefs,"export_blueprints_path") != '' else folder_path
#print("-----EXPORTING BLUEPRINTS----")
#print("LIBRARY EXPORT", export_blueprints_path )
try:
export_collections(collections, export_blueprints_path, library_scene, addon_prefs, gltf_export_preferences, blueprint_hierarchy, library_collections)
except Exception as error:
print("failed to export collections to gltf: ", error)
# TODO : rethrow
# export all main scenes
def export_main_scenes(scenes, folder_path, addon_prefs):
for scene in scenes:
export_main_scene(scene, folder_path, addon_prefs)
def export_main_scene(scene, folder_path, addon_prefs, library_collections):
gltf_export_preferences = generate_gltf_export_preferences(addon_prefs)
export_output_folder = getattr(addon_prefs,"export_output_folder")
export_blueprints = getattr(addon_prefs,"export_blueprints")
feat(Save & load): new crate bevy_gltf_save_load + lots of upgrades & improvements (#95) * feat(bevy_gltf_save_load): saving & loading implemented * created new crate for save & load features, uses & filters out blueprints for efficient loading * saving & loading, even with dynamically spawned nested hierarchies works * component filter , resource filter & save path root are configurable * for saving: added removal & cleanup logic for children component with children that have been filtered out: ie no more invalid children getting stored in the save files ! * added sending of event once saving is done * feat(examples/save-load): example for the new crate * loading level static & dynamic data as blueprints * added a bit of ui when entering saving & loading states & cleanup when exiting * feat(bevy_gltf_blueprints): significant rewrite of how the crate works * simplified spawning process, no more spawning children containing blueprints etc * simplified post process : instead of copying original entity into blueprint root we now copy blueprint root data (components & children) into the original entity ! fixes #96 * much simpler code wise * solves issue with needing to register components that we only use on the bevy side (not gltf) since we are not copying the bevy data into the blueprints data * added **copyComponents** helper to copy components from one entity to another, excluding existing components on the target entity, & some bells & whistles * **Name** is now optional when spawning a blueprint: closes #97 * **Transform** is now optional when spawning a blueprint: closes #98 * removed transform from bundle (BREAKING change) * added (optional) **NoInBlueprint** component to have finer control over whether to inject the **InBlueprint** component inside spawned blueprint entities * added (optional) **Library** component, so we can override library path when we want * added (optional) **AddToGameWorld** component for convenience to automatically add entities to the game world, if there is one * chore(bevy_gltf_components): removed verbose output, cleaned it up a bit * feat(tools/auto_export): added option to split out "dynamic" objects in main scenes * ie if a collection instance (or its original collection) has a "dynamic" (aka mutable, saveable etc) flag it can get exported to a seperate gltf file (essentially acting like an "initial save") * the rest of the levels (the "static" data) is exported without the dynamic objects and can be reused with save files !
2024-01-10 13:49:29 +00:00
export_separate_dynamic_and_static_objects = getattr(addon_prefs, "export_separate_dynamic_and_static_objects")
gltf_output_path = os.path.join(folder_path, export_output_folder, scene.name)
export_settings = { **gltf_export_preferences,
'use_active_scene': True,
'use_active_collection':True,
'use_active_collection_with_nested':True,
'use_visible': False,
'use_renderable': False,
'export_apply':True
}
if export_blueprints :
feat(Save & load): new crate bevy_gltf_save_load + lots of upgrades & improvements (#95) * feat(bevy_gltf_save_load): saving & loading implemented * created new crate for save & load features, uses & filters out blueprints for efficient loading * saving & loading, even with dynamically spawned nested hierarchies works * component filter , resource filter & save path root are configurable * for saving: added removal & cleanup logic for children component with children that have been filtered out: ie no more invalid children getting stored in the save files ! * added sending of event once saving is done * feat(examples/save-load): example for the new crate * loading level static & dynamic data as blueprints * added a bit of ui when entering saving & loading states & cleanup when exiting * feat(bevy_gltf_blueprints): significant rewrite of how the crate works * simplified spawning process, no more spawning children containing blueprints etc * simplified post process : instead of copying original entity into blueprint root we now copy blueprint root data (components & children) into the original entity ! fixes #96 * much simpler code wise * solves issue with needing to register components that we only use on the bevy side (not gltf) since we are not copying the bevy data into the blueprints data * added **copyComponents** helper to copy components from one entity to another, excluding existing components on the target entity, & some bells & whistles * **Name** is now optional when spawning a blueprint: closes #97 * **Transform** is now optional when spawning a blueprint: closes #98 * removed transform from bundle (BREAKING change) * added (optional) **NoInBlueprint** component to have finer control over whether to inject the **InBlueprint** component inside spawned blueprint entities * added (optional) **Library** component, so we can override library path when we want * added (optional) **AddToGameWorld** component for convenience to automatically add entities to the game world, if there is one * chore(bevy_gltf_components): removed verbose output, cleaned it up a bit * feat(tools/auto_export): added option to split out "dynamic" objects in main scenes * ie if a collection instance (or its original collection) has a "dynamic" (aka mutable, saveable etc) flag it can get exported to a seperate gltf file (essentially acting like an "initial save") * the rest of the levels (the "static" data) is exported without the dynamic objects and can be reused with save files !
2024-01-10 13:49:29 +00:00
if export_separate_dynamic_and_static_objects:
# first export all dynamic objects
(hollow_scene, temporary_collections, root_objects, special_properties) = generate_hollow_scene(scene, library_collections, addon_prefs, is_object_dynamic)
gltf_output_path = os.path.join(folder_path, export_output_folder, scene.name+ "_dynamic")
# set active scene to be the given scene
bpy.context.window.scene = hollow_scene
print(" exporting gltf to", gltf_output_path, ".gltf/glb")
export_gltf(gltf_output_path, export_settings)
clear_hollow_scene(hollow_scene, scene, temporary_collections, root_objects, special_properties)
# now export static objects
(hollow_scene, temporary_collections, root_objects, special_properties) = generate_hollow_scene(scene, library_collections, addon_prefs, is_object_static)
gltf_output_path = os.path.join(folder_path, export_output_folder, scene.name)
# set active scene to be the given scene
bpy.context.window.scene = hollow_scene
print(" exporting gltf to", gltf_output_path, ".gltf/glb")
export_gltf(gltf_output_path, export_settings)
clear_hollow_scene(hollow_scene, scene, temporary_collections, root_objects, special_properties)
else:
print("NO SPLIT")
# todo: add exception handling
(hollow_scene, temporary_collections, root_objects, special_properties) = generate_hollow_scene(scene, library_collections, addon_prefs)
# set active scene to be the given scene
bpy.context.window.scene = hollow_scene
print(" exporting gltf to", gltf_output_path, ".gltf/glb")
export_gltf(gltf_output_path, export_settings)
clear_hollow_scene(hollow_scene, scene, temporary_collections, root_objects, special_properties)
else:
print(" exporting gltf to", gltf_output_path, ".gltf/glb")
export_gltf(gltf_output_path, export_settings)
#https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf
def export_gltf (path, export_settings):
settings = {**export_settings, "filepath": path}
os.makedirs(os.path.dirname(path), exist_ok=True)
bpy.ops.export_scene.gltf(**settings)