Skip to content

Commit

Permalink
feat(gltf_auto_export): rewrite for more cleaner code & a lot of bug …
Browse files Browse the repository at this point in the history
…fixes (#109)

* fundamental rewrite of a lot of aspects of the exporter, for cleaner code & to solve a number of issues
* set export_separate_dynamic_and_static_objects default to FALSE, as it is an edge use case
* added central tracker class, to track the post save & depsgraph changes
* auto_export is now more operator centric, with functional undo
* now storing add-on configuration at the blend file level  (bpy.data.texts)
* found source of export issues ! mismatch between context.scene & context.window.scene (the last we can set, the other not)
* overhauled & cleaned up materials handling to avoid context issues & reliance on bpy.ops
* huge restructure of how temporary scenes are created & cleaned up
* added support for exports in edit_mode
* updated examples
* a lot more
* fixes #103 
* fixes #112 
* fixes #113 
* fixes #114 
* fixes #115 
* fixes #116
  • Loading branch information
kaosat-dev authored Feb 5, 2024
1 parent 7699e87 commit 2ae8351
Show file tree
Hide file tree
Showing 142 changed files with 1,145 additions and 1,033 deletions.
Binary file modified examples/bevy_gltf_blueprints/animation/assets/animation.blend
Binary file not shown.
Binary file modified examples/bevy_gltf_blueprints/animation/assets/models/Level1.glb
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified examples/bevy_gltf_blueprints/basic/assets/advanced.blend
Binary file not shown.
Binary file modified examples/bevy_gltf_blueprints/basic/assets/models/World.glb
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified examples/bevy_gltf_blueprints/basic_wasm/assets/advanced.blend
Binary file not shown.
Binary file modified examples/bevy_gltf_blueprints/basic_wasm/assets/models/World.glb
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified examples/bevy_gltf_blueprints/materials/assets/materials.blend
Binary file not shown.
Binary file not shown.
Binary file modified examples/bevy_gltf_blueprints/materials/assets/models/Level1.glb
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified examples/bevy_gltf_blueprints/nested_blueprints/assets/nested.blend
Binary file not shown.
Binary file modified examples/bevy_gltf_components/basic/assets/basic.blend
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion examples/bevy_gltf_components/basic/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub struct MyGltf(pub Handle<Gltf>);
// MyGltf is also just for the same purpose, you do not need it in a real scenario
// the states here are also for demo purposes only,
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.insert_resource(MyGltf(asset_server.load("models/level1.glb")));
commands.insert_resource(MyGltf(asset_server.load("models/Level1.glb")));
}

fn spawn_level(
Expand Down
Binary file modified examples/bevy_gltf_components/basic_wasm/assets/basic.blend
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion examples/bevy_gltf_components/basic_wasm/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub struct MyGltf(pub Handle<Gltf>);
// MyGltf is also just for the same purpose, you do not need it in a real scenario
// the states here are also for demo purposes only,
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.insert_resource(MyGltf(asset_server.load("models/level1.glb")));
commands.insert_resource(MyGltf(asset_server.load("models/Level1.glb")));
}

fn spawn_level(
Expand Down
Binary file modified examples/bevy_gltf_save_load/basic/assets/basic.blend
Binary file not shown.
Binary file modified examples/bevy_gltf_save_load/basic/assets/models/World.glb
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
179 changes: 26 additions & 153 deletions tools/gltf_auto_export/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,26 @@
"category": "Import-Export"
}
import bpy
import os

from bpy.app.handlers import persistent
from bpy.props import (IntProperty)

from .auto_export.operators import AutoExportGLTF
from .auto_export.tracker import AutoExportTracker
from .auto_export.preferences import (AutoExportGltfAddonPreferences)

from . import helpers
from .internals import (SceneLink,
from .auto_export.internals import (SceneLink,
SceneLinks,
CollectionToExport,
CollectionsToExport,
CUSTOM_PG_sceneName
)
from .auto_export import auto_export
from .preferences import (AutoExportGltfPreferenceNames,
AutoExportGltfAddonPreferences
)
from .ui.main import (GLTF_PT_auto_export_main,
GLTF_PT_auto_export_root,
GLTF_PT_auto_export_blueprints,
GLTF_PT_auto_export_collections_list,
GLTF_PT_auto_export_gltf,
SCENE_UL_GLTF_auto_export,
AutoExportGLTF
)
from .ui.various import (SCENES_LIST_OT_actions)
from .helpers_scenes import (is_scene_ok)
from .ui.operators import (SCENES_LIST_OT_actions)

bpy.context.window_manager['changed_objects_per_scene'] = {}
bpy.context.window_manager['previous_params'] = {}
bpy.context.window_manager['__gltf_auto_export_initialized'] = False
bpy.context.window_manager['__gltf_auto_export_gltf_params_changed'] = False
bpy.context.window_manager['__gltf_auto_export_saving'] = False

######################################################
""" there are two places where we load settings for auto_export from:
Expand All @@ -54,95 +41,6 @@


#see here for original gltf exporter infos https://github.com/KhronosGroup/glTF-Blender-IO/blob/main/addons/io_scene_gltf2/__init__.py
@persistent
def deps_update_handler(scene, depsgraph):
if scene.name != "temp_scene": # actually do we care about anything else than the main scene(s) ?
#print("depsgraph_update_post", scene.name)
print("-------------")
changed = scene.name or ""

# only deal with changes if we are no in the mids of saving/exporting
#if not bpy.context.window_manager['__gltf_auto_export_saving']:

# depsgraph = bpy.context.evaluated_depsgraph_get()
if not 'changed_objects_per_scene' in bpy.context.window_manager:
bpy.context.window_manager['changed_objects_per_scene'] = {}

if not changed in bpy.context.window_manager['changed_objects_per_scene']:
bpy.context.window_manager['changed_objects_per_scene'][changed] = {}

for obj in depsgraph.updates:
if isinstance(obj.id, bpy.types.Object):
# get the actual object
object = bpy.data.objects[obj.id.name]
print("changed object", obj.id.name)
bpy.context.window_manager['changed_objects_per_scene'][scene.name][obj.id.name] = object
elif isinstance(obj.id, bpy.types.Material): # or isinstance(obj.id, bpy.types.ShaderNodeTree):
print("changed material", obj.id, "scene", scene.name,)
material = bpy.data.materials[obj.id.name]
#now find which objects are using the material
for obj in bpy.data.objects:
for slot in obj.material_slots:
if slot.material == material:
bpy.context.window_manager['changed_objects_per_scene'][scene.name][obj.name] = obj

bpy.context.window_manager.changedScene = changed

@persistent
def save_handler(dummy):
print("-------------")
print("saved", bpy.data.filepath)
# mark saving as in progress, this is needed to ignore any changes from the depsgraph done during saving
# bpy.context.window_manager['__gltf_auto_export_saving'] = True

if not 'changed_objects_per_scene' in bpy.context.window_manager:
bpy.context.window_manager['changed_objects_per_scene'] = {}
changes_per_scene = bpy.context.window_manager['changed_objects_per_scene']

if not 'previous_params' in bpy.context.window_manager:
bpy.context.window_manager['previous_params'] = {}

#determine changed parameters
addon_prefs = bpy.context.preferences.addons["gltf_auto_export"].preferences

prefs = {}
for (k,v) in addon_prefs.items():
if k not in AutoExportGltfPreferenceNames:
prefs[k] = v

previous_params = bpy.context.window_manager['previous_params'] if 'previous_params' in bpy.context.window_manager else {}
set1 = set(previous_params.items())
set2 = set(prefs.items())
difference = dict(set1 ^ set2)

changed_param_names = list(set(difference.keys())- set(AutoExportGltfPreferenceNames))
changed_parameters = len(changed_param_names) > 0
# do the export
auto_export(changes_per_scene, changed_parameters)


# save the parameters
# todo add back
for (k, v) in prefs.items():
bpy.context.window_manager['previous_params'][k] = v

# reset a few things after exporting
# reset wether the gltf export paramters were changed since the last save
bpy.context.window_manager['__gltf_auto_export_gltf_params_changed'] = False
# reset whether there have been changed objects since the last save
bpy.context.window_manager['changed_objects_per_scene'] = {}

# all our logic is done, mark this as done
#bpy.context.window_manager['__gltf_auto_export_saving'] = False
print("EXPORT DONE")


def get_changedScene(self):
return self["changedScene"]

def set_changedScene(self, value):
self["changedScene"] = value

classes = [
SceneLink,
SceneLinks,
Expand All @@ -151,7 +49,7 @@ def set_changedScene(self, value):
SCENES_LIST_OT_actions,

AutoExportGLTF,
AutoExportGltfAddonPreferences,
#AutoExportGltfAddonPreferences,

CollectionToExport,
CollectionsToExport,
Expand All @@ -160,69 +58,44 @@ def set_changedScene(self, value):
GLTF_PT_auto_export_root,
GLTF_PT_auto_export_blueprints,
GLTF_PT_auto_export_collections_list,
GLTF_PT_auto_export_gltf
GLTF_PT_auto_export_gltf,

AutoExportTracker
]

def menu_func_import(self, context):
self.layout.operator(AutoExportGLTF.bl_idname, text="glTF auto Export (.glb/gltf)")
from bpy.app.handlers import persistent

def register():
for cls in classes:
bpy.utils.register_class(cls)

bpy.types.Scene.main_scene = bpy.props.PointerProperty(type=bpy.types.Scene, name="main scene", description="main_scene_chooser", poll=is_scene_ok)
bpy.types.Scene.library_scene = bpy.props.PointerProperty(type=bpy.types.Scene, name="library scene", description="library_scene_picker", poll=is_scene_ok)
@persistent
def post_update(scene, depsgraph):
bpy.context.window_manager.auto_export_tracker.deps_update_handler( scene, depsgraph)

# setup handlers for updates & saving
bpy.app.handlers.depsgraph_update_post.append(deps_update_handler)
bpy.app.handlers.save_post.append(save_handler)
@persistent
def post_save(scene, depsgraph):
bpy.context.window_manager.auto_export_tracker.save_handler( scene, depsgraph)


bpy.types.WindowManager.changedScene = bpy.props.StringProperty(get=get_changedScene, set=set_changedScene)
bpy.types.WindowManager.exportedCollections = bpy.props.CollectionProperty(type=CollectionsToExport)
def register():
print("registering")
for cls in classes:
bpy.utils.register_class(cls)
# for some reason, adding these directly to the tracker class in register() do not work reliably
bpy.app.handlers.depsgraph_update_post.append(post_update)
bpy.app.handlers.save_post.append(post_save)

# add our addon to the toolbar
bpy.types.TOPBAR_MT_file_export.append(menu_func_import)

## just experiments
bpy.types.Scene.main_scenes_list_index = IntProperty(name = "Index for main scenes list", default = 0)
bpy.types.Scene.library_scenes_list_index = IntProperty(name = "Index for library scenes list", default = 0)

"""
mock_main_scenes = []
main_scenes = bpy.context.preferences.addons["gltf_auto_export"].preferences.main_scenes
for item_name in mock_main_scenes:
item = main_scenes.add()
item.name = item_name
mock_library_scenes = []
library_scenes = bpy.context.preferences.addons["gltf_auto_export"].preferences.library_scenes
for item_name in mock_library_scenes:
item = library_scenes.add()
item.name = item_name"""

bpy.context.preferences.addons["gltf_auto_export"].preferences.main_scenes_index = 0
bpy.context.preferences.addons["gltf_auto_export"].preferences.library_scenes_index = 0

def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)

bpy.types.TOPBAR_MT_file_export.remove(menu_func_import)

# remove handlers & co
bpy.app.handlers.depsgraph_update_post.remove(deps_update_handler)
bpy.app.handlers.save_post.remove(save_handler)

del bpy.types.WindowManager.changedScene
del bpy.types.WindowManager.exportedCollections

del bpy.types.Scene.main_scene
del bpy.types.Scene.library_scene

del bpy.types.Scene.main_scenes_list_index
del bpy.types.Scene.library_scenes_list_index
bpy.app.handlers.depsgraph_update_post.remove(post_update)
bpy.app.handlers.save_post.remove(post_save)


if "gltf_auto_export" == "__main__":
print("foo")
register()
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,17 @@
import bpy
import traceback

from .helpers_scenes import (get_scenes, )
from .helpers_collections import (get_collections_in_library, get_exportable_collections, get_collections_per_scene, find_collection_ascendant_target_collection)
from .helpers_export import (export_main_scene, export_blueprints_from_collections)
from .helpers import (check_if_blueprints_exist, check_if_blueprint_on_disk)
from .materials import cleanup_materials, clear_material_info, clear_materials_scene, export_materials, generate_materials_scenes, get_all_materials
from .scene_components import upsert_scene_components
from .export_main_scenes import export_main_scene
from .export_blueprints import check_if_blueprint_on_disk, check_if_blueprints_exist, export_blueprints_from_collections

from .config import scene_key
from ..helpers.helpers_scenes import (get_scenes, )
from ..helpers.helpers_collections import (get_collections_in_library, get_exportable_collections, get_collections_per_scene, find_collection_ascendant_target_collection)
from ..modules.export_materials import cleanup_materials, export_materials
from ..modules.bevy_scene_components import upsert_scene_components

"""Main function"""
def auto_export(changes_per_scene, changed_export_parameters):
addon_prefs = bpy.context.preferences.addons["gltf_auto_export"].preferences

# a semi_hack to ensure we have the latest version of the settings
initialized = bpy.context.window_manager['__gltf_auto_export_initialized'] if '__gltf_auto_export_initialized' in bpy.context.window_manager else False
if not initialized:
print("not initialized, fetching settings if any")
# semi_hack to restore the correct settings if the add_on was installed before
settings = bpy.context.scene.get(scene_key)
if settings:
print("loading settings in main function")
try:
# Update filter if user saved settings
#if hasattr(self, 'export_format'):
# self.filter_glob = '*.glb' if self.export_format == 'GLB' else '*.gltf'
for (k, v) in settings.items():
setattr(addon_prefs, k, v)
# inject scenes data
if k == 'main_scene_names':
main_scenes = addon_prefs.main_scenes
for item_name in v:
item = main_scenes.add()
item.name = item_name

if k == 'library_scene_names':
library_scenes = addon_prefs.library_scenes
for item_name in v:
item = library_scenes.add()
item.name = item_name



except Exception as error:
print("error setting preferences from saved settings", error)
bpy.context.window_manager['__gltf_auto_export_initialized'] = True

"""Main function"""
def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
# have the export parameters (not auto export, just gltf export) have changed: if yes (for example switch from glb to gltf, compression or not, animations or not etc), we need to re-export everything
print ("changed_export_parameters", changed_export_parameters)
try:
Expand All @@ -69,7 +34,7 @@ def auto_export(changes_per_scene, changed_export_parameters):

if export_scene_settings:
# inject/ update scene components
upsert_scene_components(bpy.context.scene, world = bpy.context.scene.world)
upsert_scene_components(bpy.context.scene, bpy.context.scene.world, main_scene_names)

# export
if export_blueprints:
Expand Down Expand Up @@ -168,13 +133,12 @@ def auto_export(changes_per_scene, changed_export_parameters):
# reset selections
for obj in old_selections:
obj.select_set(True)

if export_materials_library:
cleanup_materials(collections, library_scenes)

else:
for scene_name in main_scene_names:
export_main_scene(bpy.data.scenes[scene_name], folder_path, addon_prefs)
export_main_scene(bpy.data.scenes[scene_name], folder_path, addon_prefs, [])

except Exception as error:
print(traceback.format_exc())
Expand Down
Loading

0 comments on commit 2ae8351

Please sign in to comment.