feat(blenvy): massive overhaul

* removed a lot of obsolete code
 * overhauled settings upsert
 * added save & load of blenvy common settings
 * removed most of the specific uis (mostly auto export)
 * moved change detection logic out of auto export (wip)
 * added helper to compare setting dicts
 * cleaned up some incoherent naming
 * stripped auto_export of a lot of its internals (wip)
 * main settings ui overhaul: now components & auto export provide their own settings UI function
This commit is contained in:
kaosat.dev 2024-05-17 14:30:15 +02:00
parent c476c05c55
commit 21a36f9878
32 changed files with 509 additions and 594 deletions

View File

@ -5,7 +5,7 @@ Auto export
- [x] blueprints - [x] blueprints
- [x] materials - [x] materials
- [x] move out the UI for "assets" folder out of "blueprints condition" - [x] move out the UI for "assets" folder out of "blueprints condition"
- [ ] fix asset path calculations - [x] fix asset path calculations
- root path => relative to blend file path - root path => relative to blend file path
- asset path => relative to root path - asset path => relative to root path
- blueprints/levels/blueprints path => relative to assets path - blueprints/levels/blueprints path => relative to assets path
@ -49,6 +49,10 @@ Blueprints:
- [ ] scan & inject on load - [ ] scan & inject on load
- [ ] scan & inject on save - [ ] scan & inject on save
General things to solve:
- [x] save settings
- [x] load settings
- [ ] add_blueprints_data
General issues: General issues:
- there is no safeguard for naming collisions for naming across blender files - there is no safeguard for naming collisions for naming across blender files

View File

@ -15,11 +15,12 @@ import bpy
from bpy.app.handlers import persistent from bpy.app.handlers import persistent
from bpy.props import (StringProperty) from bpy.props import (StringProperty)
# components management # components management
from .bevy_components.components.operators import CopyComponentOperator, Fix_Component_Operator, OT_rename_component, RemoveComponentFromAllObjectsOperator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, AddComponentOperator, RenameHelper, Toggle_ComponentVisibility from .bevy_components.components.operators import CopyComponentOperator, Fix_Component_Operator, OT_rename_component, RemoveComponentFromAllObjectsOperator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, AddComponentOperator, RenameHelper, Toggle_ComponentVisibility
from .bevy_components.registry.registry import ComponentsRegistry,MissingBevyType from .bevy_components.registry.registry import ComponentsRegistry,MissingBevyType
from .bevy_components.registry.operators import (COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT, OT_select_component_name_to_replace, OT_select_object, ReloadRegistryOperator, OT_OpenFilebrowser) from .bevy_components.registry.operators import (COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT, OT_select_component_name_to_replace, OT_select_object, ReloadRegistryOperator, OT_OpenSchemaFileBrowser)
from .bevy_components.registry.ui import (BEVY_COMPONENTS_PT_Configuration, BEVY_COMPONENTS_PT_AdvancedToolsPanel, BEVY_COMPONENTS_PT_MissingTypesPanel, MISSING_TYPES_UL_List) from .bevy_components.registry.ui import (BEVY_COMPONENTS_PT_Configuration, BEVY_COMPONENTS_PT_AdvancedToolsPanel, BEVY_COMPONENTS_PT_MissingTypesPanel, MISSING_TYPES_UL_List)
from .bevy_components.components.metadata import (ComponentMetadata, ComponentsMeta) from .bevy_components.components.metadata import (ComponentMetadata, ComponentsMeta)
@ -32,12 +33,8 @@ from .bevy_components.components.ui import (BEVY_COMPONENTS_PT_ComponentsPanel)
from .gltf_auto_export import gltf_post_export_callback from .gltf_auto_export import gltf_post_export_callback
from .gltf_auto_export.auto_export.operators import AutoExportGLTF from .gltf_auto_export.auto_export.operators import AutoExportGLTF
from .gltf_auto_export.auto_export.tracker import AutoExportTracker from .gltf_auto_export.auto_export.tracker import AutoExportTracker
from .gltf_auto_export.ui.main import (GLTF_PT_auto_export_change_detection, GLTF_PT_auto_export_main, from .gltf_auto_export.settings import AutoExportSettings
GLTF_PT_auto_export_root,
GLTF_PT_auto_export_general,
GLTF_PT_auto_export_blueprints,
GLTF_PT_auto_export_SidePanel
)
# asset management # asset management
from .assets.ui import Blenvy_assets from .assets.ui import Blenvy_assets
from .assets.assets_registry import Asset, AssetsRegistry from .assets.assets_registry import Asset, AssetsRegistry
@ -54,7 +51,7 @@ from .core.operators import OT_switch_bevy_tooling
from .core.scene_helpers import (SceneSelector) from .core.scene_helpers import (SceneSelector)
from .core.ui.ui import (BLENVY_PT_SidePanel) from .core.ui.ui import (BLENVY_PT_SidePanel)
from .core.ui.scenes_list import SCENES_LIST_OT_actions, SCENE_UL_Blenvy from .core.ui.scenes_list import SCENES_LIST_OT_actions, SCENE_UL_Blenvy
from .core.ui.folder_browser import OT_OpenFolderbrowser from .core.ui.folder_browser import OT_OpenAssetsFolderBrowser
# this needs to be here, as it is how Blender's gltf exporter callbacks are defined, at the add-on root level # this needs to be here, as it is how Blender's gltf exporter callbacks are defined, at the add-on root level
@ -67,7 +64,7 @@ classes = [
SceneSelector, SceneSelector,
SCENE_UL_Blenvy, SCENE_UL_Blenvy,
SCENES_LIST_OT_actions, SCENES_LIST_OT_actions,
OT_OpenFolderbrowser, OT_OpenAssetsFolderBrowser,
# blenvy # blenvy
BLENVY_PT_SidePanel, BLENVY_PT_SidePanel,
@ -92,7 +89,7 @@ classes = [
MissingBevyType, MissingBevyType,
ComponentsRegistry, ComponentsRegistry,
OT_OpenFilebrowser, OT_OpenSchemaFileBrowser,
ReloadRegistryOperator, ReloadRegistryOperator,
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL,
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT,
@ -105,7 +102,7 @@ classes = [
BEVY_COMPONENTS_PT_ComponentsPanel, BEVY_COMPONENTS_PT_ComponentsPanel,
BEVY_COMPONENTS_PT_AdvancedToolsPanel, BEVY_COMPONENTS_PT_AdvancedToolsPanel,
BEVY_COMPONENTS_PT_Configuration, #BEVY_COMPONENTS_PT_Configuration,
MISSING_TYPES_UL_List, MISSING_TYPES_UL_List,
BEVY_COMPONENTS_PT_MissingTypesPanel, BEVY_COMPONENTS_PT_MissingTypesPanel,
@ -118,15 +115,8 @@ classes = [
# gltf auto export # gltf auto export
AutoExportGLTF, AutoExportGLTF,
GLTF_PT_auto_export_main,
GLTF_PT_auto_export_root,
GLTF_PT_auto_export_general,
GLTF_PT_auto_export_change_detection,
GLTF_PT_auto_export_blueprints,
GLTF_PT_auto_export_SidePanel,
AutoExportTracker, AutoExportTracker,
AutoExportSettings,
# blenvy # blenvy
BlenvyManager, BlenvyManager,
@ -157,8 +147,11 @@ def post_save(scene, depsgraph):
@persistent @persistent
def post_load(file_name): def post_load(file_name):
registry = bpy.context.window_manager.components_registry registry = bpy.context.window_manager.components_registry
if registry != None: if registry is not None:
registry.load_settings() registry.load_settings()
blenvy = bpy.context.window_manager.blenvy
if blenvy is not None:
blenvy.load_settings()
def register(): def register():
for cls in classes: for cls in classes:

View File

@ -5,13 +5,13 @@ import bpy
from .asset_helpers import does_asset_exist, get_user_assets, get_user_assets_as_list from .asset_helpers import does_asset_exist, get_user_assets, get_user_assets_as_list
def scan_assets(scene, blueprints_data, addon_prefs): def scan_assets(scene, blueprints_data, addon_prefs):
export_root_path = getattr(addon_prefs, "export_root_path") project_root_path = getattr(addon_prefs, "project_root_path")
export_output_folder = getattr(addon_prefs,"export_output_folder") export_output_folder = getattr(addon_prefs,"export_output_folder")
export_levels_path = getattr(addon_prefs,"export_levels_path") levels_path = getattr(addon_prefs,"levels_path")
export_blueprints_path = getattr(addon_prefs, "export_blueprints_path") blueprints_path = getattr(addon_prefs, "blueprints_path")
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension") export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
relative_blueprints_path = os.path.relpath(export_blueprints_path, export_root_path) relative_blueprints_path = os.path.relpath(blueprints_path, project_root_path)
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(scene.name, None) blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(scene.name, None)
blueprint_assets_list = [] blueprint_assets_list = []
@ -62,7 +62,7 @@ def get_userTextures():
print("textures", textures) print("textures", textures)
def get_blueprint_assets_tree(blueprint, blueprints_data, parent, addon_prefs): def get_blueprint_assets_tree(blueprint, blueprints_data, parent, addon_prefs):
export_blueprints_path = getattr(addon_prefs, "export_blueprints_path") blueprints_path = getattr(addon_prefs, "blueprints_path")
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension") export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
assets_list = [] assets_list = []
@ -72,7 +72,7 @@ def get_blueprint_assets_tree(blueprint, blueprints_data, parent, addon_prefs):
if child_blueprint: if child_blueprint:
blueprint_exported_path = None blueprint_exported_path = None
if blueprint.local: if blueprint.local:
blueprint_exported_path = os.path.join(export_blueprints_path, f"{child_blueprint.name}{export_gltf_extension}") blueprint_exported_path = os.path.join(blueprints_path, f"{child_blueprint.name}{export_gltf_extension}")
else: else:
# get the injected path of the external blueprints # get the injected path of the external blueprints
blueprint_exported_path = child_blueprint.collection['export_path'] if 'export_path' in child_blueprint.collection else None blueprint_exported_path = child_blueprint.collection['export_path'] if 'export_path' in child_blueprint.collection else None
@ -91,7 +91,7 @@ def get_blueprint_assets_tree(blueprint, blueprints_data, parent, addon_prefs):
return assets_list return assets_list
def get_main_scene_assets_tree(main_scene, blueprints_data, addon_prefs): def get_main_scene_assets_tree(main_scene, blueprints_data, addon_prefs):
export_blueprints_path = getattr(addon_prefs, "export_blueprints_path") blueprints_path = getattr(addon_prefs, "blueprints_path")
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension") export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(main_scene.name, None) blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(main_scene.name, None)
@ -102,7 +102,7 @@ def get_main_scene_assets_tree(main_scene, blueprints_data, addon_prefs):
if blueprint is not None: if blueprint is not None:
blueprint_exported_path = None blueprint_exported_path = None
if blueprint.local: if blueprint.local:
blueprint_exported_path = os.path.join(export_blueprints_path, f"{blueprint.name}{export_gltf_extension}") blueprint_exported_path = os.path.join(blueprints_path, f"{blueprint.name}{export_gltf_extension}")
else: else:
# get the injected path of the external blueprints # get the injected path of the external blueprints
blueprint_exported_path = blueprint.collection['export_path'] if 'export_path' in blueprint.collection else None blueprint_exported_path = blueprint.collection['export_path'] if 'export_path' in blueprint.collection else None

View File

@ -145,11 +145,11 @@ class OT_Add_asset_filebrowser(Operator, ImportHelper):
def execute(self, context): def execute(self, context):
current_auto_settings = load_settings(".gltf_auto_export_settings") current_auto_settings = load_settings(".gltf_auto_export_settings")
export_root_path = current_auto_settings.get("export_root_path", "../") project_root_path = current_auto_settings.get("project_root_path", "../")
export_assets_path = current_auto_settings.get("export_assets_path", "assets") assets_path = current_auto_settings.get("assets_path", "assets")
# FIXME: not sure # FIXME: not sure
print("export_root_path", export_root_path, "export_assets_path", export_assets_path) print("project_root_path", project_root_path, "assets_path", assets_path)
export_assets_path_absolute = absolute_path_from_blend_file(os.path.join(export_root_path, export_assets_path)) export_assets_path_absolute = absolute_path_from_blend_file(os.path.join(project_root_path, assets_path))
asset_path = os.path.relpath(self.filepath, export_assets_path_absolute) asset_path = os.path.relpath(self.filepath, export_assets_path_absolute)
print("asset path", asset_path) print("asset path", asset_path)
@ -203,7 +203,7 @@ class OT_test_bevy_assets(Operator):
blueprints_registry.add_blueprints_data() blueprints_registry.add_blueprints_data()
blueprints_data = blueprints_registry.blueprints_data blueprints_data = blueprints_registry.blueprints_data
settings = {"export_blueprints_path": "blueprints", "export_gltf_extension": ".glb"} settings = {"blueprints_path": "blueprints", "export_gltf_extension": ".glb"}
settings = SimpleNamespace(**settings) settings = SimpleNamespace(**settings)
for scene in bpy.data.scenes: for scene in bpy.data.scenes:
if scene.name != "Library": if scene.name != "Library":

View File

@ -80,7 +80,7 @@ class Blenvy_assets(bpy.types.Panel):
header, panel = layout.box().panel(f"assets{name}", default_closed=False) header, panel = layout.box().panel(f"assets{name}", default_closed=False)
header.label(text="World/Level Assets") header.label(text="World/Level Assets")
settings = {"export_blueprints_path": "blueprints", "export_gltf_extension": ".glb"} settings = {"blueprints_path": "blueprints", "export_gltf_extension": ".glb"}
settings = SimpleNamespace(**settings) settings = SimpleNamespace(**settings)
if panel: if panel:

View File

@ -173,9 +173,9 @@ class COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL(Operator):
context.window_manager.components_from_custom_properties_progress_all = -1.0 context.window_manager.components_from_custom_properties_progress_all = -1.0
return {'FINISHED'} return {'FINISHED'}
class OT_OpenFilebrowser(Operator, ImportHelper): class OT_OpenSchemaFileBrowser(Operator, ImportHelper):
"""Browse for registry json file""" """Browse for registry json file"""
bl_idname = "generic.open_filebrowser" bl_idname = "blenvy.open_schemafilebrowser"
bl_label = "Open the file browser" bl_label = "Open the file browser"
filter_glob: StringProperty( filter_glob: StringProperty(
@ -194,7 +194,8 @@ class OT_OpenFilebrowser(Operator, ImportHelper):
registry = context.window_manager.components_registry registry = context.window_manager.components_registry
registry.schemaPath = relative_path registry.schemaPath = relative_path
upsert_settings(registry.settings_save_path, {"schemaPath": relative_path}) blenvy = context.window_manager.blenvy
upsert_settings(blenvy.settings_save_path, {"components_schemaPath": relative_path})
return {'FINISHED'} return {'FINISHED'}

View File

@ -283,8 +283,7 @@ class ComponentsRegistry(PropertyGroup):
settings = load_settings(self.settings_save_path) settings = load_settings(self.settings_save_path)
if settings!= None: if settings!= None:
print("settings", settings) self.schemaPath = settings["components_schemaPath"]
self.schemaPath = settings["schemaPath"]
self.load_schema() self.load_schema()
generate_propertyGroups_for_components() generate_propertyGroups_for_components()
ensure_metadata_for_all_objects() ensure_metadata_for_all_objects()

View File

@ -7,7 +7,7 @@ from ..components.operators import OT_rename_component, RemoveComponentFromAllOb
from .operators import( from .operators import(
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT,
OT_OpenFilebrowser, OT_OpenSchemaFileBrowser,
OT_select_component_name_to_replace, OT_select_component_name_to_replace,
OT_select_object, ReloadRegistryOperator, OT_select_object, ReloadRegistryOperator,
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL,
@ -39,7 +39,7 @@ class BEVY_COMPONENTS_PT_Configuration(bpy.types.Panel):
col.enabled = False col.enabled = False
col.prop(registry, "schemaPath", text="Registry Schema path") col.prop(registry, "schemaPath", text="Registry Schema path")
col = row.column() col = row.column()
col.operator(OT_OpenFilebrowser.bl_idname, text="Browse for registry schema file (json)") col.operator(OT_OpenSchemaFileBrowser.bl_idname, text="Browse for registry schema file (json)")
layout.separator() layout.separator()
layout.operator(ReloadRegistryOperator.bl_idname, text="reload registry" , icon="FILE_REFRESH") layout.operator(ReloadRegistryOperator.bl_idname, text="reload registry" , icon="FILE_REFRESH")

View File

@ -0,0 +1,19 @@
def draw_settings_ui(layout, registry):
row = layout.row()
col = row.column()
col.enabled = False
col.prop(registry, "schemaPath", text="Registry Schema path")
col = row.column()
col.operator(operator="blenvy.open_schemafilebrowser", text="Browse for registry schema file (json)")
layout.separator()
layout.operator(operator="object.reload_registry", text="reload registry" , icon="FILE_REFRESH")
layout.separator()
row = layout.row()
row.prop(registry, "watcher_enabled", text="enable registry file polling")
row.prop(registry, "watcher_poll_frequency", text="registry file poll frequency (s)")
layout.separator()
layout.separator()

View File

@ -20,16 +20,16 @@ def check_if_blueprint_on_disk(scene_name, folder_path, extension):
print("level", scene_name, "found", found, "path", gltf_output_path) print("level", scene_name, "found", found, "path", gltf_output_path)
return found return found
def inject_export_path_into_internal_blueprints(internal_blueprints, export_blueprints_path, gltf_extension): def inject_export_path_into_internal_blueprints(internal_blueprints, blueprints_path, gltf_extension):
for blueprint in internal_blueprints: for blueprint in internal_blueprints:
blueprint_exported_path = os.path.join(export_blueprints_path, f"{blueprint.name}{gltf_extension}") blueprint_exported_path = os.path.join(blueprints_path, f"{blueprint.name}{gltf_extension}")
blueprint.collection["export_path"] = blueprint_exported_path blueprint.collection["export_path"] = blueprint_exported_path
def inject_blueprints_list_into_main_scene(scene, blueprints_data, addon_prefs): def inject_blueprints_list_into_main_scene(scene, blueprints_data, addon_prefs):
export_root_path = getattr(addon_prefs, "export_root_path") project_root_path = getattr(addon_prefs, "project_root_path")
export_assets_path = getattr(addon_prefs,"export_assets_path") assets_path = getattr(addon_prefs,"assets_path")
export_levels_path = getattr(addon_prefs,"export_levels_path") levels_path = getattr(addon_prefs,"levels_path")
export_blueprints_path = getattr(addon_prefs, "export_blueprints_path") blueprints_path = getattr(addon_prefs, "blueprints_path")
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension") export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
# print("injecting assets/blueprints data into scene") # print("injecting assets/blueprints data into scene")
@ -45,7 +45,7 @@ def inject_blueprints_list_into_main_scene(scene, blueprints_data, addon_prefs):
print("BLUEPRINT", blueprint) print("BLUEPRINT", blueprint)
blueprint_exported_path = None blueprint_exported_path = None
if blueprint.local: if blueprint.local:
blueprint_exported_path = os.path.join(export_blueprints_path, f"{blueprint.name}{export_gltf_extension}") blueprint_exported_path = os.path.join(blueprints_path, f"{blueprint.name}{export_gltf_extension}")
else: else:
# get the injected path of the external blueprints # get the injected path of the external blueprints
blueprint_exported_path = blueprint.collection['Export_path'] if 'Export_path' in blueprint.collection else None blueprint_exported_path = blueprint.collection['Export_path'] if 'Export_path' in blueprint.collection else None
@ -61,8 +61,7 @@ def inject_blueprints_list_into_main_scene(scene, blueprints_data, addon_prefs):
print("blueprint assets", blueprint_assets_list) print("blueprint assets", blueprint_assets_list)
"""add_scene_property(scene, assets_list_name, assets_list_data) """add_scene_property(scene, assets_list_name, assets_list_data)
for blueprint in blueprint_assets_list: """
bpy.context.window_manager.assets_registry.add_asset(**blueprint)"""
def remove_blueprints_list_from_main_scene(scene): def remove_blueprints_list_from_main_scene(scene):
assets_list = None assets_list = None

View File

@ -52,6 +52,7 @@ class BlueprintsRegistry(PropertyGroup):
def add_blueprints_data(self): def add_blueprints_data(self):
print("adding blueprints data") print("adding blueprints data")
addon_prefs = load_settings(".gltf_auto_export_settings") addon_prefs = load_settings(".gltf_auto_export_settings")
if addon_prefs is not None:
print("addon_prefs", addon_prefs) print("addon_prefs", addon_prefs)
addon_prefs["export_marked_assets"] = False addon_prefs["export_marked_assets"] = False
addon_prefs = SimpleNamespace(**addon_prefs) addon_prefs = SimpleNamespace(**addon_prefs)

View File

@ -2,9 +2,28 @@ import bpy
from bpy_types import (PropertyGroup) from bpy_types import (PropertyGroup)
from bpy.props import (EnumProperty, PointerProperty, StringProperty, CollectionProperty, IntProperty) from bpy.props import (EnumProperty, PointerProperty, StringProperty, CollectionProperty, IntProperty)
from .scene_helpers import SceneSelector from .scene_helpers import SceneSelector
from ..settings import upsert_settings, load_settings
import blenvy.gltf_auto_export.settings as auto_export_settings
def update_scene_lists(self, context):
blenvy = self# context.window_manager.blenvy
upsert_settings(blenvy.settings_save_path, {"common_main_scene_names": [scene.name for scene in blenvy.main_scenes]})
upsert_settings(blenvy.settings_save_path, {"common_library_scene_names": [scene.name for scene in blenvy.library_scenes]})
def update_asset_folders(self, context):
blenvy = context.window_manager.blenvy
asset_path_names = ['project_root_path', 'assets_path', 'blueprints_path', 'levels_path', 'materials_path']
for asset_path_name in asset_path_names:
upsert_settings(blenvy.settings_save_path, {asset_path_name: getattr(blenvy, asset_path_name)})
def update_mode(self, context):
blenvy = self # context.window_manager.blenvy
upsert_settings(blenvy.settings_save_path, {"mode": blenvy.mode })
class BlenvyManager(PropertyGroup): class BlenvyManager(PropertyGroup):
settings_save_path = ".blenvy_settings" # where to store data in bpy.texts
mode: EnumProperty( mode: EnumProperty(
items=( items=(
('COMPONENTS', "Components", ""), ('COMPONENTS', "Components", ""),
@ -12,46 +31,56 @@ class BlenvyManager(PropertyGroup):
('ASSETS', "Assets", ""), ('ASSETS', "Assets", ""),
('SETTINGS', "Settings", ""), ('SETTINGS', "Settings", ""),
('TOOLS', "Tools", ""), ('TOOLS', "Tools", ""),
) ),
update=update_mode
) # type: ignore ) # type: ignore
export_root_path: StringProperty( project_root_path: StringProperty(
name = "Project Root Path", name = "Project Root Path",
description="The root folder of your (Bevy) project (not assets!)", description="The root folder of your (Bevy) project (not assets!)",
default='../' default='../',
update= update_asset_folders
) # type: ignore ) # type: ignore
export_assets_path: StringProperty( assets_path: StringProperty(
name='Export folder', name='Export folder',
description='The root folder for all exports(relative to the root folder/path) Defaults to "assets" ', description='The root folder for all exports(relative to the root folder/path) Defaults to "assets" ',
default='./assets', default='./assets',
options={'HIDDEN'} options={'HIDDEN'},
update= update_asset_folders
) # type: ignore ) # type: ignore
export_blueprints_path: StringProperty( blueprints_path: StringProperty(
name='Blueprints path', name='Blueprints path',
description='path to export the blueprints to (relative to the assets folder)', description='path to export the blueprints to (relative to the assets folder)',
default='blueprints', default='blueprints',
update= update_asset_folders
) # type: ignore ) # type: ignore
export_levels_path: StringProperty( levels_path: StringProperty(
name='Levels path', name='Levels path',
description='path to export the levels (main scenes) to (relative to the assets folder)', description='path to export the levels (main scenes) to (relative to the assets folder)',
default='levels', default='levels',
update= update_asset_folders
) # type: ignore ) # type: ignore
export_materials_path: StringProperty( materials_path: StringProperty(
name='Materials path', name='Materials path',
description='path to export the materials libraries to (relative to the assets folder)', description='path to export the materials libraries to (relative to the assets folder)',
default='materials', default='materials',
update= update_asset_folders
) # type: ignore ) # type: ignore
main_scenes: CollectionProperty(name="main scenes", type=SceneSelector) # type: ignore main_scenes: CollectionProperty(name="main scenes", type=SceneSelector) # type: ignore
main_scenes_index: IntProperty(name = "Index for main scenes list", default = 0) # type: ignore main_scenes_index: IntProperty(name = "Index for main scenes list", default = 0, update=update_scene_lists) # type: ignore
library_scenes: CollectionProperty(name="library scenes", type=SceneSelector) # type: ignore library_scenes: CollectionProperty(name="library scenes", type=SceneSelector) # type: ignore
library_scenes_index: IntProperty(name = "Index for library scenes list", default = 0) # type: ignore library_scenes_index: IntProperty(name = "Index for library scenes list", default = 0, update=update_scene_lists) # type: ignore
# sub ones
auto_export: PointerProperty(type=auto_export_settings.AutoExportSettings) # type: ignore
#components: PointerProperty(type=auto_export_settings.AutoExportSettings) # type: ignore
def is_scene_ok(self, scene): def is_scene_ok(self, scene):
try: try:
@ -65,9 +94,6 @@ class BlenvyManager(PropertyGroup):
bpy.types.WindowManager.main_scene = bpy.props.PointerProperty(type=bpy.types.Scene, name="main scene", description="main_scene_picker", poll=cls.is_scene_ok) bpy.types.WindowManager.main_scene = bpy.props.PointerProperty(type=bpy.types.Scene, name="main scene", description="main_scene_picker", poll=cls.is_scene_ok)
bpy.types.WindowManager.library_scene = bpy.props.PointerProperty(type=bpy.types.Scene, name="library scene", description="library_scene_picker", poll=cls.is_scene_ok) bpy.types.WindowManager.library_scene = bpy.props.PointerProperty(type=bpy.types.Scene, name="library scene", description="library_scene_picker", poll=cls.is_scene_ok)
bpy.types.WindowManager.main_scenes_list_index = IntProperty(name = "Index for main scenes list", default = 0)
bpy.types.WindowManager.library_scenes_list_index = IntProperty(name = "Index for library scenes list", default = 0)
bpy.types.WindowManager.blenvy = PointerProperty(type=BlenvyManager) bpy.types.WindowManager.blenvy = PointerProperty(type=BlenvyManager)
@classmethod @classmethod
@ -76,10 +102,21 @@ class BlenvyManager(PropertyGroup):
del bpy.types.WindowManager.main_scene del bpy.types.WindowManager.main_scene
del bpy.types.WindowManager.library_scene del bpy.types.WindowManager.library_scene
del bpy.types.WindowManager.main_scenes_list_index def load_settings(self):
del bpy.types.WindowManager.library_scenes_list_index settings = load_settings(self.settings_save_path)
if settings is not None:
if "mode" in settings:
def add_asset(self, name, type, path, internal): # internal means it cannot be edited by the user, aka auto generated self.mode = settings["mode"]
pass if "common_main_scene_names" in settings:
for main_scene_name in settings["common_main_scene_names"]:
added = self.main_scenes.add()
added.name = main_scene_name
if "common_library_scene_names" in settings:
for main_scene_name in settings["common_library_scene_names"]:
added = self.library_scenes.add()
added.name = main_scene_name
asset_path_names = ['project_root_path', 'assets_path', 'blueprints_path', 'levels_path', 'materials_path']
for asset_path_name in asset_path_names:
if asset_path_name in settings:
setattr(self, asset_path_name, settings[asset_path_name])

View File

@ -0,0 +1,78 @@
import json
import bpy
from ..helpers.serialize_scene import serialize_scene
def bubble_up_changes(object, changes_per_scene):
if object.parent:
changes_per_scene[object.parent.name] = bpy.data.objects[object.parent.name]
bubble_up_changes(object.parent, changes_per_scene)
def foo():
current = json.loads(current)
previous_stored = bpy.data.texts[".TESTING"] if ".TESTING" in bpy.data.texts else None # bpy.data.texts.new(".TESTING")
if previous_stored == None:
previous_stored = bpy.data.texts.new(".TESTING")
previous_stored.write(current)
return {}
previous = json.loads(previous_stored.as_string())
previous_stored.clear()
previous_stored.write(json.dumps(current))
def project_diff(previous, current):
# sigh... you need to save & reset the frame otherwise it saves the values AT THE CURRENT FRAME WHICH CAN DIFFER ACROSS SCENES
current_frames = [scene.frame_current for scene in bpy.data.scenes]
for scene in bpy.data.scenes:
scene.frame_set(0)
current_scene = bpy.context.window.scene
bpy.context.window.scene = bpy.data.scenes[0]
#serialize scene at frame 0
"""with bpy.context.temp_override(scene=bpy.data.scenes[1]):
bpy.context.scene.frame_set(0)"""
current = serialize_scene()
bpy.context.window.scene = current_scene
# reset previous frames
for (index, scene) in enumerate(bpy.data.scenes):
scene.frame_set(int(current_frames[index]))
changes_per_scene = {}
# TODO : how do we deal with changed scene names ???
for scene in current:
previous_object_names = list(previous[scene].keys())
current_object_names =list(current[scene].keys())
added = list(set(current_object_names) - set(previous_object_names))
removed = list(set(previous_object_names) - set(current_object_names))
for obj in added:
if not scene in changes_per_scene:
changes_per_scene[scene] = {}
changes_per_scene[scene][obj] = bpy.data.objects[obj]
# TODO: how do we deal with this, as we obviously do not have data for removed objects ?
for obj in removed:
if not scene in changes_per_scene:
changes_per_scene[scene] = {}
changes_per_scene[scene][obj] = None
for object_name in list(current[scene].keys()): # TODO : exclude directly added/removed objects
if object_name in previous[scene]:
current_obj = current[scene][object_name]
prev_obj = previous[scene][object_name]
same = str(current_obj) == str(prev_obj)
if not same:
if not scene in changes_per_scene:
changes_per_scene[scene] = {}
changes_per_scene[scene][object_name] = bpy.data.objects[object_name]
bubble_up_changes(bpy.data.objects[object_name], changes_per_scene[scene])
# now bubble up for instances & parents
print("changes per scene", changes_per_scene)
return changes_per_scene

View File

@ -6,8 +6,8 @@ from bpy.types import Operator
from ...core.path_helpers import absolute_path_from_blend_file from ...core.path_helpers import absolute_path_from_blend_file
class OT_OpenFolderbrowser(Operator, ImportHelper): class OT_OpenAssetsFolderBrowser(Operator, ImportHelper):
"""Browse for registry json file""" """Assets folder's browser"""
bl_idname = "generic.open_folderbrowser" bl_idname = "generic.open_folderbrowser"
bl_label = "Select folder" bl_label = "Select folder"
@ -41,31 +41,31 @@ class OT_OpenFolderbrowser(Operator, ImportHelper):
# Get the folder # Get the folder
blend_file_folder_path = os.path.dirname(blend_file_path) blend_file_folder_path = os.path.dirname(blend_file_path)
#print("blend_file_folder_path", blend_file_folder_path) #print("blend_file_folder_path", blend_file_folder_path)
print("new_path", self.directory, self.target_property, operator) #print("new_path", self.directory, self.target_property, operator)
asset_path_names = ['export_blueprints_path', 'export_levels_path', 'export_materials_path'] asset_path_names = ['blueprints_path', 'levels_path', 'materials_path']
export_root_path = absolute_path_from_blend_file(operator.export_root_path) project_root_path = absolute_path_from_blend_file(operator.project_root_path)
export_assets_path = operator.export_assets_path assets_path = operator.assets_path
export_assets_path_full = absolute_path_from_blend_file(os.path.join(export_root_path, export_assets_path)) #os.path.join(blend_file_folder_path, export_root_path, export_assets_path) export_assets_path_full = absolute_path_from_blend_file(os.path.join(project_root_path, assets_path)) #os.path.join(blend_file_folder_path, project_root_path, assets_path)
print("export_assets_path_full", export_assets_path_full) #print("export_assets_path_full", export_assets_path_full)
#new_root_path = os.path.join(blend_file_folder_path, new_path) #new_root_path = os.path.join(blend_file_folder_path, new_path)
if target_path_name == 'export_root_path': if target_path_name == 'project_root_path':
new_root_path_relative = os.path.relpath(new_path, blend_file_folder_path) new_root_path_relative = os.path.relpath(new_path, blend_file_folder_path)
new_root_path_absolute = new_path new_root_path_absolute = new_path
print("new_root_path_relative", new_root_path_relative, new_root_path_absolute) #print("new_root_path_relative", new_root_path_relative, new_root_path_absolute)
# first change the asset's path # first change the asset's path
old_assets_paths_relative = getattr(operator, "export_assets_path", None) old_assets_paths_relative = getattr(operator, "assets_path", None)
if old_assets_paths_relative is not None: if old_assets_paths_relative is not None:
old_assets_paths_absolute = os.path.abspath(os.path.join(export_root_path, old_assets_paths_relative)) old_assets_paths_absolute = os.path.abspath(os.path.join(project_root_path, old_assets_paths_relative))
new_assets_path_relative = os.path.relpath(old_assets_paths_absolute, new_root_path_absolute) new_assets_path_relative = os.path.relpath(old_assets_paths_absolute, new_root_path_absolute)
new_assets_path_absolute = os.path.abspath(os.path.join(new_root_path_absolute, new_assets_path_relative)) new_assets_path_absolute = os.path.abspath(os.path.join(new_root_path_absolute, new_assets_path_relative))
print("old_assets_paths_absolute", old_assets_paths_absolute) """print("old_assets_paths_absolute", old_assets_paths_absolute)
print("new_assets_path_relative", new_assets_path_relative) print("new_assets_path_relative", new_assets_path_relative)
print("new_assets_path_absolute", new_assets_path_absolute) print("new_assets_path_absolute", new_assets_path_absolute)"""
setattr(operator, "export_assets_path", new_assets_path_relative) setattr(operator, "assets_path", new_assets_path_relative)
# we need to change all other relative paths (root => assets => blueprints/levels/materials etc) # we need to change all other relative paths (root => assets => blueprints/levels/materials etc)
for path_name in asset_path_names: for path_name in asset_path_names:
@ -80,8 +80,8 @@ class OT_OpenFolderbrowser(Operator, ImportHelper):
# store the root path as relative to the current blend file # store the root path as relative to the current blend file
setattr(operator, target_path_name, new_root_path_relative) setattr(operator, target_path_name, new_root_path_relative)
elif target_path_name == 'export_assets_path': elif target_path_name == 'assets_path':
new_assets_path_relative = os.path.relpath(new_path, export_root_path) new_assets_path_relative = os.path.relpath(new_path, project_root_path)
new_assets_path_absolute = new_path new_assets_path_absolute = new_path
# we need to change all other relative paths (root => assets => blueprints/levels/materials etc) # we need to change all other relative paths (root => assets => blueprints/levels/materials etc)
for path_name in asset_path_names: for path_name in asset_path_names:
@ -98,7 +98,4 @@ class OT_OpenFolderbrowser(Operator, ImportHelper):
relative_path = os.path.relpath(new_path, export_assets_path_full) relative_path = os.path.relpath(new_path, export_assets_path_full)
setattr(operator, target_path_name, relative_path) setattr(operator, target_path_name, relative_path)
#filename, extension = os.path.splitext(self.filepath)
return {'FINISHED'} return {'FINISHED'}

View File

@ -46,16 +46,21 @@ class SCENES_LIST_OT_actions(Operator):
('UP', "Up", ""), ('UP', "Up", ""),
('DOWN', "Down", ""), ('DOWN', "Down", ""),
('REMOVE', "Remove", ""), ('REMOVE', "Remove", ""),
('ADD', "Add", ""))) # type: ignore ('ADD', "Add", ""))
) # type: ignore
scene_type: bpy.props.EnumProperty(
scene_type: bpy.props.StringProperty()# type: ignore #TODO: replace with enum items=(
('LEVEL', "Level", ""),
('LIBRARY', "Library", ""),
)
) # type: ignore
def invoke(self, context, event): def invoke(self, context, event):
source = context.window_manager.blenvy source = context.window_manager.blenvy
target_name = "library_scenes" target_name = "library_scenes"
target_index = "library_scenes_index" target_index = "library_scenes_index"
if self.scene_type == "level": if self.scene_type == "LEVEL":
target_name = "main_scenes" target_name = "main_scenes"
target_index = "main_scenes_index" target_index = "main_scenes_index"
@ -82,13 +87,14 @@ class SCENES_LIST_OT_actions(Operator):
elif self.action == 'REMOVE': elif self.action == 'REMOVE':
info = 'Item "%s" removed from list' % (target[idx].name) info = 'Item "%s" removed from list' % (target[idx].name)
setattr(source, target_index, current_index -1 )
target.remove(idx) target.remove(idx)
setattr(source, target_index, current_index -1 )
self.report({'INFO'}, info) self.report({'INFO'}, info)
if self.action == 'ADD': if self.action == 'ADD':
new_scene_name = None new_scene_name = None
if self.scene_type == "level": if self.scene_type == "LEVEL":
if context.window_manager.main_scene: if context.window_manager.main_scene:
new_scene_name = context.window_manager.main_scene.name new_scene_name = context.window_manager.main_scene.name
else: else:
@ -96,17 +102,17 @@ class SCENES_LIST_OT_actions(Operator):
new_scene_name = context.window_manager.library_scene.name new_scene_name = context.window_manager.library_scene.name
if new_scene_name: if new_scene_name:
item = target.add() item = target.add()
item.name = new_scene_name#f"Rule {idx +1}" item.name = new_scene_name
if self.scene_type == "level": if self.scene_type == "LEVEL":
context.window_manager.main_scene = None context.window_manager.main_scene = None
else: else:
context.window_manager.library_scene = None context.window_manager.library_scene = None
#name = f"Rule {idx +1}"
#target.append({"name": name})
setattr(source, target_index, len(target) - 1) setattr(source, target_index, len(target) - 1)
#source[target_index] = len(target) - 1
info = '"%s" added to list' % (item.name) info = '"%s" added to list' % (item.name)
self.report({'INFO'}, info) self.report({'INFO'}, info)

View File

@ -1,6 +1,8 @@
import bpy import bpy
from ...settings import load_settings import blenvy.bevy_components.ui as components_ui
import blenvy.gltf_auto_export.ui as auto_export_ui
from blenvy.settings import load_settings
# from ...bevy_components.components import ui# as components_ui
###################################################### ######################################################
## ui logic & co ## ui logic & co
@ -17,7 +19,7 @@ def draw_folder_browser(layout, label, prop_origin, target_property):
col.prop(prop_origin, target_property, text="") col.prop(prop_origin, target_property, text="")
folder_selector = row.operator("generic.open_folderbrowser", icon="FILE_FOLDER", text="") folder_selector = row.operator("generic.open_folderbrowser", icon="FILE_FOLDER", text="")
folder_selector.target_property = target_property #"export_root_path" folder_selector.target_property = target_property #"project_root_path"
# side panel # side panel
class BLENVY_PT_SidePanel(bpy.types.Panel): class BLENVY_PT_SidePanel(bpy.types.Panel):
@ -42,7 +44,7 @@ class BLENVY_PT_SidePanel(bpy.types.Panel):
library_scene_active = False library_scene_active = False
active_collection = context.collection active_collection = context.collection
current_auto_settings = load_settings(".gltf_auto_export_settings") """current_auto_settings = load_settings(".gltf_auto_export_settings")
current_gltf_settings = load_settings(".gltf_auto_export_gltf_settings") current_gltf_settings = load_settings(".gltf_auto_export_gltf_settings")
if current_auto_settings is not None: if current_auto_settings is not None:
@ -51,8 +53,7 @@ class BLENVY_PT_SidePanel(bpy.types.Panel):
library_scene_names = current_auto_settings["library_scene_names"] library_scene_names = current_auto_settings["library_scene_names"]
world_scene_active = context.scene.name in main_scene_names world_scene_active = context.scene.name in main_scene_names
library_scene_active = context.scene.name in library_scene_names library_scene_active = context.scene.name in library_scene_names"""
# Now to actual drawing of the UI # Now to actual drawing of the UI
target = row.box() if active_mode == 'COMPONENTS' else row target = row.box() if active_mode == 'COMPONENTS' else row
@ -82,19 +83,19 @@ class BLENVY_PT_SidePanel(bpy.types.Panel):
layout.label(text=blenvy.mode)""" layout.label(text=blenvy.mode)"""
if blenvy.mode == "SETTINGS": if blenvy.mode == "SETTINGS":
header, panel = layout.panel("auto_export", default_closed=False) header, panel = layout.panel("common", default_closed=False)
header.label(text="Common") header.label(text="Common")
if panel: if panel:
row = panel.row() row = panel.row()
draw_folder_browser(layout=row, label="Root Folder", prop_origin=blenvy, target_property="export_root_path") draw_folder_browser(layout=row, label="Root Folder", prop_origin=blenvy, target_property="project_root_path")
row = panel.row() row = panel.row()
draw_folder_browser(layout=row, label="Assets Folder", prop_origin=blenvy, target_property="export_assets_path") draw_folder_browser(layout=row, label="Assets Folder", prop_origin=blenvy, target_property="assets_path")
row = panel.row() row = panel.row()
draw_folder_browser(layout=row, label="Blueprints Folder", prop_origin=blenvy, target_property="export_blueprints_path") draw_folder_browser(layout=row, label="Blueprints Folder", prop_origin=blenvy, target_property="blueprints_path")
row = panel.row() row = panel.row()
draw_folder_browser(layout=row, label="Levels Folder", prop_origin=blenvy, target_property="export_levels_path") draw_folder_browser(layout=row, label="Levels Folder", prop_origin=blenvy, target_property="levels_path")
row = panel.row() row = panel.row()
draw_folder_browser(layout=row, label="Materials Folder", prop_origin=blenvy, target_property="export_materials_path") draw_folder_browser(layout=row, label="Materials Folder", prop_origin=blenvy, target_property="materials_path")
panel.separator() panel.separator()
# scenes selection # scenes selection
@ -111,14 +112,14 @@ class BLENVY_PT_SidePanel(bpy.types.Panel):
sub_row = col.row() sub_row = col.row()
add_operator = sub_row.operator("scene_list.list_action", icon='ADD', text="") add_operator = sub_row.operator("scene_list.list_action", icon='ADD', text="")
add_operator.action = 'ADD' add_operator.action = 'ADD'
add_operator.scene_type = 'level' add_operator.scene_type = 'LEVEL'
#add_operator.operator = operator #add_operator.operator = operator
sub_row.enabled = context.window_manager.main_scene is not None sub_row.enabled = context.window_manager.main_scene is not None
sub_row = col.row() sub_row = col.row()
remove_operator = sub_row.operator("scene_list.list_action", icon='REMOVE', text="") remove_operator = sub_row.operator("scene_list.list_action", icon='REMOVE', text="")
remove_operator.action = 'REMOVE' remove_operator.action = 'REMOVE'
remove_operator.scene_type = 'level' remove_operator.scene_type = 'LEVEL'
col.separator() col.separator()
# library scenes # library scenes
@ -133,41 +134,24 @@ class BLENVY_PT_SidePanel(bpy.types.Panel):
sub_row = col.row() sub_row = col.row()
add_operator = sub_row.operator("scene_list.list_action", icon='ADD', text="") add_operator = sub_row.operator("scene_list.list_action", icon='ADD', text="")
add_operator.action = 'ADD' add_operator.action = 'ADD'
add_operator.scene_type = 'library' add_operator.scene_type = 'LIBRARY'
sub_row.enabled = context.window_manager.library_scene is not None sub_row.enabled = context.window_manager.library_scene is not None
sub_row = col.row() sub_row = col.row()
remove_operator = sub_row.operator("scene_list.list_action", icon='REMOVE', text="") remove_operator = sub_row.operator("scene_list.list_action", icon='REMOVE', text="")
remove_operator.action = 'REMOVE' remove_operator.action = 'REMOVE'
remove_operator.scene_type = 'library' remove_operator.scene_type = 'LIBRARY'
col.separator() col.separator()
header, panel = layout.panel("components", default_closed=False)
header.label(text="Components")
if panel:
components_ui.draw_settings_ui(panel, context.window_manager.components_registry)
"""if blenvy.mode == "SETTINGS":
header, panel = layout.panel("auto_export", default_closed=False) header, panel = layout.panel("auto_export", default_closed=False)
header.label(text="Auto Export") header.label(text="Auto Export")
if panel: if panel:
layout = panel auto_export_ui.draw_settings_ui(panel, blenvy.auto_export)
layout.label(text="MAKE SURE TO KEEP 'REMEMBER EXPORT SETTINGS' TOGGLED !!")
op = layout.operator("EXPORT_SCENE_OT_gltf", text='Gltf Settings')#'glTF 2.0 (.glb/.gltf)')
#op.export_format = 'GLTF_SEPARATE'
op.use_selection=True
op.will_save_settings=True
op.use_visible=True # Export visible and hidden objects. See Object/Batch Export to skip.
op.use_renderable=True
op.use_active_collection = True
op.use_active_collection_with_nested=True
op.use_active_scene = True
op.filepath="____dummy____"
op.gltf_export_id = "gltf_auto_export" # we specify that we are in a special case
op = layout.operator("EXPORT_SCENES_OT_auto_gltf", text="Auto Export Settings")
op.auto_export = True"""
"""header, panel = layout.panel("components", default_closed=False)
header.label(text="Components")
if panel:
panel.label(text="YOOO")"""

View File

@ -29,8 +29,8 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
blend_file_path = os.path.dirname(file_path) blend_file_path = os.path.dirname(file_path)
# get the preferences for our addon # get the preferences for our addon
export_root_path = getattr(addon_prefs, "export_root_path") project_root_path = getattr(addon_prefs, "project_root_path")
export_assets_path = getattr(addon_prefs,"export_assets_path") assets_path = getattr(addon_prefs,"assets_path")
#should we use change detection or not #should we use change detection or not
export_change_detection = getattr(addon_prefs, "export_change_detection") export_change_detection = getattr(addon_prefs, "export_change_detection")
@ -61,10 +61,10 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
addon_prefs = SimpleNamespace(**tmp) #copy.deepcopy(addon_prefs) addon_prefs = SimpleNamespace(**tmp) #copy.deepcopy(addon_prefs)
addon_prefs.__annotations__ = tmp""" addon_prefs.__annotations__ = tmp"""
# generate the actual complete output paths # generate the actual complete output paths
addon_prefs.export_assets_path_full = os.path.join(blend_file_path, export_root_path, export_assets_path) addon_prefs.export_assets_path_full = os.path.join(blend_file_path, project_root_path, assets_path)
addon_prefs.export_blueprints_path_full = os.path.join(addon_prefs.export_assets_path_full, getattr(addon_prefs,"export_blueprints_path")) addon_prefs.export_blueprints_path_full = os.path.join(addon_prefs.export_assets_path_full, getattr(addon_prefs,"blueprints_path"))
addon_prefs.export_levels_path_full = os.path.join(addon_prefs.export_assets_path_full, getattr(addon_prefs,"export_levels_path")) addon_prefs.export_levels_path_full = os.path.join(addon_prefs.export_assets_path_full, getattr(addon_prefs,"levels_path"))
addon_prefs.export_materials_path_full = os.path.join(addon_prefs.export_assets_path_full, getattr(addon_prefs,"export_materials_path")) addon_prefs.export_materials_path_full = os.path.join(addon_prefs.export_assets_path_full, getattr(addon_prefs,"materials_path"))
addon_prefs.export_gltf_extension = gltf_extension addon_prefs.export_gltf_extension = gltf_extension
[main_scene_names, level_scenes, library_scene_names, library_scenes] = get_scenes(addon_prefs) [main_scene_names, level_scenes, library_scene_names, library_scenes] = get_scenes(addon_prefs)
@ -76,8 +76,8 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
# we inject the blueprints export path # we inject the blueprints export path
export_blueprints_path = getattr(addon_prefs,"export_blueprints_path") blueprints_path = getattr(addon_prefs,"blueprints_path")
inject_export_path_into_internal_blueprints(internal_blueprints=blueprints_data.internal_blueprints, export_blueprints_path=export_blueprints_path, gltf_extension=gltf_extension) inject_export_path_into_internal_blueprints(internal_blueprints=blueprints_data.internal_blueprints, blueprints_path=blueprints_path, gltf_extension=gltf_extension)
for blueprint in blueprints_data.blueprints: for blueprint in blueprints_data.blueprints:
bpy.context.window_manager.blueprints_registry.add_blueprint(blueprint) bpy.context.window_manager.blueprints_registry.add_blueprint(blueprint)
bpy.context.window_manager.blueprints_registry.add_blueprints_data() bpy.context.window_manager.blueprints_registry.add_blueprints_data()

View File

@ -1,39 +0,0 @@
import json
import bpy
"""
This should ONLY be run when actually doing exports/aka calling auto_export function, because we only care about the difference in settings between EXPORTS
"""
def did_export_settings_change():
# compare both the auto export settings & the gltf settings
previous_auto_settings = bpy.data.texts[".gltf_auto_export_settings_previous"] if ".gltf_auto_export_settings_previous" in bpy.data.texts else None
previous_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings_previous"] if ".gltf_auto_export_gltf_settings_previous" in bpy.data.texts else None
current_auto_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else None
current_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else None
#check if params have changed
# if there were no setting before, it is new, we need export
changed = False
if previous_auto_settings == None:
print("previous settings missing, exporting")
changed = True
elif previous_gltf_settings == None:
print("previous gltf settings missing, exporting")
changed = True
else:
auto_settings_changed = sorted(json.loads(previous_auto_settings.as_string()).items()) != sorted(json.loads(current_auto_settings.as_string()).items()) if current_auto_settings != None else False
gltf_settings_changed = sorted(json.loads(previous_gltf_settings.as_string()).items()) != sorted(json.loads(current_gltf_settings.as_string()).items()) if current_gltf_settings != None else False
"""print("auto settings previous", sorted(json.loads(previous_auto_settings.as_string()).items()))
print("auto settings current", sorted(json.loads(current_auto_settings.as_string()).items()))
print("auto_settings_changed", auto_settings_changed)"""
"""print("gltf settings previous", sorted(json.loads(previous_gltf_settings.as_string()).items()))
print("gltf settings current", sorted(json.loads(current_gltf_settings.as_string()).items()))
print("gltf_settings_changed", gltf_settings_changed)"""
changed = auto_settings_changed or gltf_settings_changed
return changed

View File

@ -6,13 +6,6 @@ from bpy.types import Operator
from .preferences import (AutoExportGltfAddonPreferences, AutoExportGltfPreferenceNames) from .preferences import (AutoExportGltfAddonPreferences, AutoExportGltfPreferenceNames)
from .auto_export import auto_export from .auto_export import auto_export
from ..helpers.generate_complete_preferences_dict import generate_complete_preferences_dict_auto from ..helpers.generate_complete_preferences_dict import generate_complete_preferences_dict_auto
from ..helpers.serialize_scene import serialize_scene
def bubble_up_changes(object, changes_per_scene):
if object.parent:
changes_per_scene[object.parent.name] = bpy.data.objects[object.parent.name]
bubble_up_changes(object.parent, changes_per_scene)
class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences):#, ExportHelper): class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences):#, ExportHelper):
"""auto export gltf""" """auto export gltf"""
@ -27,8 +20,8 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences):#, ExportHelper):
#list of settings (other than purely gltf settings) whose change should trigger a re-generation of gltf files #list of settings (other than purely gltf settings) whose change should trigger a re-generation of gltf files
white_list = [ white_list = [
'auto_export', 'auto_export',
'export_root_path', 'project_root_path',
'export_assets_path', 'assets_path',
'export_change_detection', 'export_change_detection',
'export_scene_settings', 'export_scene_settings',
@ -36,15 +29,15 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences):#, ExportHelper):
'library_scene_names', 'library_scene_names',
'export_blueprints', 'export_blueprints',
'export_blueprints_path', 'blueprints_path',
'export_marked_assets', 'export_marked_assets',
'collection_instances_combine_mode', 'collection_instances_combine_mode',
'export_levels_path', 'levels_path',
'export_separate_dynamic_and_static_objects', 'export_separate_dynamic_and_static_objects',
'export_materials_library', 'export_materials_library',
'export_materials_path', 'materials_path',
] ]
@classmethod @classmethod
@ -78,15 +71,11 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences):#, ExportHelper):
export_props[k] = value export_props[k] = value
# we add main & library scene names to our preferences # we add main & library scene names to our preferences
export_props['main_scene_names'] = list(map(lambda scene_data: scene_data.name, getattr(self,"main_scenes")))
export_props['library_scene_names'] = list(map(lambda scene_data: scene_data.name, getattr(self,"library_scenes")))
return export_props return export_props
def save_settings(self, context): def save_settings(self, context):
print("save settings") print("save settings")
auto_export_settings = self.format_settings() auto_export_settings = self.format_settings()
self.properties['main_scene_names'] = auto_export_settings['main_scene_names']
self.properties['library_scene_names'] = auto_export_settings['library_scene_names']
stored_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings") stored_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings")
stored_settings.clear() stored_settings.clear()
@ -194,88 +183,7 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences):#, ExportHelper):
return changed return changed
def did_objects_change(self): def did_objects_change(self):
# sigh... you need to save & reset the frame otherwise it saves the values AT THE CURRENT FRAME WHICH CAN DIFFER ACROSS SCENES pass
current_frames = [scene.frame_current for scene in bpy.data.scenes]
for scene in bpy.data.scenes:
scene.frame_set(0)
current_scene = bpy.context.window.scene
bpy.context.window.scene = bpy.data.scenes[0]
#serialize scene at frame 0
"""with bpy.context.temp_override(scene=bpy.data.scenes[1]):
bpy.context.scene.frame_set(0)"""
current = serialize_scene()
bpy.context.window.scene = current_scene
# reset previous frames
for (index, scene) in enumerate(bpy.data.scenes):
scene.frame_set(int(current_frames[index]))
previous_stored = bpy.data.texts[".TESTING"] if ".TESTING" in bpy.data.texts else None # bpy.data.texts.new(".TESTING")
if previous_stored == None:
previous_stored = bpy.data.texts.new(".TESTING")
previous_stored.write(current)
return {}
previous = json.loads(previous_stored.as_string())
current = json.loads(current)
changes_per_scene = {}
# TODO : how do we deal with changed scene names ???
for scene in current:
# print('scene', scene)
previous_object_names = list(previous[scene].keys())
current_object_names =list(current[scene].keys())
#print("previous_object_names", len(previous_object_names), previous_object_names)
#print("current_object_names", len(current_object_names), current_object_names)
"""if len(previous_object_names) > len(current_object_names):
print("removed")
if len(current_object_names) > len(previous_object_names):
print("added")"""
added = list(set(current_object_names) - set(previous_object_names))
removed = list(set(previous_object_names) - set(current_object_names))
"""print("removed", removed)
print("added",added)"""
for obj in added:
if not scene in changes_per_scene:
changes_per_scene[scene] = {}
changes_per_scene[scene][obj] = bpy.data.objects[obj]
# TODO: how do we deal with this, as we obviously do not have data for removed objects ?
for obj in removed:
if not scene in changes_per_scene:
changes_per_scene[scene] = {}
changes_per_scene[scene][obj] = None # bpy.data.objects[obj]
for object_name in list(current[scene].keys()): # todo : exclude directly added/removed objects
#print("ob", object_name)
if object_name in previous[scene]:
# print("object", object_name,"in previous scene, comparing")
current_obj = current[scene][object_name]
prev_obj = previous[scene][object_name]
same = str(current_obj) == str(prev_obj)
if "Camera" in object_name:
pass#print(" current", current_obj, prev_obj)
"""if "Fox" in object_name:
print(" current", current_obj)
print(" previou", prev_obj)
print(" same?", same)"""
#print("foo", same)
if not same:
""" print(" current", current_obj)
print(" previou", prev_obj)"""
if not scene in changes_per_scene:
changes_per_scene[scene] = {}
changes_per_scene[scene][object_name] = bpy.data.objects[object_name]
bubble_up_changes(bpy.data.objects[object_name], changes_per_scene[scene])
# now bubble up for instances & parents
previous_stored.clear()
previous_stored.write(json.dumps(current))
print("changes per scene alternative", changes_per_scene)
return changes_per_scene
def execute(self, context): def execute(self, context):
bpy.context.window_manager.auto_export_tracker.disable_change_detection() bpy.context.window_manager.auto_export_tracker.disable_change_detection()
@ -286,7 +194,6 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences):#, ExportHelper):
#print("self", self.auto_export) #print("self", self.auto_export)
if self.auto_export: # only do the actual exporting if auto export is actually enabled if self.auto_export: # only do the actual exporting if auto export is actually enabled
#changes_per_scene = context.window_manager.auto_export_tracker.changed_objects_per_scene #changes_per_scene = context.window_manager.auto_export_tracker.changed_objects_per_scene
#& do the export #& do the export
if self.direct_mode: #Do not auto export when applying settings in the menu, do it on save only if self.direct_mode: #Do not auto export when applying settings in the menu, do it on save only
# determine changed objects # determine changed objects
@ -310,67 +217,6 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences):#, ExportHelper):
wm = context.window_manager wm = context.window_manager
#wm.fileselect_add(self) #wm.fileselect_add(self)
return context.window_manager.invoke_props_dialog(self, title="Auto export", width=640) return context.window_manager.invoke_props_dialog(self, title="Auto export", width=640)
return {'RUNNING_MODAL'}
"""def modal(self, context, event):
if event.type == 'SPACE':
wm = context.window_manager
wm.invoke_popup(self)
#wm.invoke_props_dialog(self)
if event.type in {'ESC'}:
return {'CANCELLED'}
return {'RUNNING_MODAL'}"""
def draw(self, context):
layout = self.layout
operator = self
controls_enabled = self.auto_export
layout.prop(self, "auto_export")
layout.separator()
toggle_icon = "TRIA_DOWN" if self.show_general_settings else "TRIA_RIGHT"
layout.prop(self, "show_general_settings", text="General", icon=toggle_icon)
if self.show_general_settings:
section = layout.box()
section.enabled = controls_enabled
section.prop(operator, "export_scene_settings")
toggle_icon = "TRIA_DOWN" if self.show_change_detection_settings else "TRIA_RIGHT"
layout.prop(operator, "show_change_detection_settings", text="Change Detection", icon=toggle_icon)
if self.show_change_detection_settings:
section = layout.box()
section.enabled = controls_enabled
section.prop(operator, "export_change_detection", text="Use change detection")
toggle_icon = "TRIA_DOWN" if self.show_blueprint_settings else "TRIA_RIGHT"
layout.prop(operator, "show_blueprint_settings", text="Blueprints", icon=toggle_icon)
if self.show_blueprint_settings:
section = layout.box()
section.enabled = controls_enabled
section.prop(operator, "export_blueprints")
section = section.box()
section.enabled = controls_enabled and self.export_blueprints
# collections/blueprints
section.prop(operator, "collection_instances_combine_mode")
section.prop(operator, "export_marked_assets")
section.separator()
section.prop(operator, "export_separate_dynamic_and_static_objects")
section.separator()
# materials
section.prop(operator, "export_materials_library")
section = section.box()
section.enabled = controls_enabled and self.export_materials_library
#section.prop(operator, "export_materials_path")
def cancel(self, context): def cancel(self, context):
print("cancel") print("cancel")

View File

@ -14,8 +14,8 @@ AutoExportGltfPreferenceNames = [
'show_general_settings', 'show_general_settings',
'auto_export', 'auto_export',
'export_root_path', 'project_root_path',
'export_assets_path', 'assets_path',
'export_scene_settings', 'export_scene_settings',
'show_change_detection_settings', 'show_change_detection_settings',
@ -31,21 +31,21 @@ AutoExportGltfPreferenceNames = [
'show_blueprint_settings', 'show_blueprint_settings',
'export_blueprints', 'export_blueprints',
'export_blueprints_path', 'blueprints_path',
'export_marked_assets', 'export_marked_assets',
'collection_instances_combine_mode', 'collection_instances_combine_mode',
'export_levels_path', 'levels_path',
'export_separate_dynamic_and_static_objects', 'export_separate_dynamic_and_static_objects',
'export_materials_library', 'export_materials_library',
'export_materials_path', 'materials_path',
] ]
def on_export_output_folder_updated(self, context): def on_export_output_folder_updated(self, context):
#self.export_root_path = os.path.relpath(self.export_root_path) #self.project_root_path = os.path.relpath(self.project_root_path)
#self.export_assets_path = os.path.join(self.export_root_path, self.export_assets_path) #self.assets_path = os.path.join(self.project_root_path, self.assets_path)
print("on_foo_updated", self.export_root_path, self.export_assets_path) print("on_foo_updated", self.project_root_path, self.assets_path)
class AutoExportGltfAddonPreferences(AddonPreferences): class AutoExportGltfAddonPreferences(AddonPreferences):
# this must match the add-on name, use '__package__' # this must match the add-on name, use '__package__'
@ -80,7 +80,7 @@ class AutoExportGltfAddonPreferences(AddonPreferences):
default=True default=True
) # type: ignore ) # type: ignore
export_root_path: StringProperty( project_root_path: StringProperty(
name = "Project Root Path", name = "Project Root Path",
description="The root folder of your (Bevy) project (not assets!)", description="The root folder of your (Bevy) project (not assets!)",
# subtype='DIR_PATH', # subtype='DIR_PATH',
@ -88,7 +88,7 @@ class AutoExportGltfAddonPreferences(AddonPreferences):
#update=on_export_output_folder_updated) # type: ignore #update=on_export_output_folder_updated) # type: ignore
) )
export_assets_path: StringProperty( assets_path: StringProperty(
name='Export folder', name='Export folder',
description='The root folder for all exports(relative to the root folder/path) Defaults to "assets" ', description='The root folder for all exports(relative to the root folder/path) Defaults to "assets" ',
default='./assets', default='./assets',
@ -139,14 +139,14 @@ class AutoExportGltfAddonPreferences(AddonPreferences):
default=True default=True
) # type: ignore ) # type: ignore
export_blueprints_path: StringProperty( blueprints_path: StringProperty(
name='Blueprints path', name='Blueprints path',
description='path to export the blueprints to (relative to the assets folder)', description='path to export the blueprints to (relative to the assets folder)',
default='blueprints', default='blueprints',
#subtype='DIR_PATH' #subtype='DIR_PATH'
) # type: ignore ) # type: ignore
export_levels_path: StringProperty( levels_path: StringProperty(
name='Levels path', name='Levels path',
description='path to export the levels (main scenes) to (relative to the assets folder)', description='path to export the levels (main scenes) to (relative to the assets folder)',
default='levels', default='levels',
@ -167,7 +167,7 @@ class AutoExportGltfAddonPreferences(AddonPreferences):
default=False default=False
) # type: ignore ) # type: ignore
export_materials_path: StringProperty( materials_path: StringProperty(
name='Materials path', name='Materials path',
description='path to export the materials libraries to (relative to the assets folder)', description='path to export the materials libraries to (relative to the assets folder)',
default='materials', default='materials',

View File

@ -0,0 +1,78 @@
import bpy
from bpy_types import (PropertyGroup)
from bpy.props import (EnumProperty, PointerProperty, StringProperty, BoolProperty, CollectionProperty, IntProperty)
class AutoExportSettings(PropertyGroup):
# use when operator is called directly, works a bit differently than inside the ui
direct_mode: BoolProperty(
default=False
) # type: ignore
auto_export: BoolProperty(
name='Auto export',
description='Automatically export to gltf on save',
default=False
) # type: ignore
#### general
export_change_detection: BoolProperty(
name='Change detection',
description='Use change detection to determine what/if should be exported',
default=True
) # type: ignore
# scenes
# scene components
export_scene_settings: BoolProperty(
name='Export scene settings',
description='Export scene settings ie AmbientLighting, Bloom, AO etc',
default=False
) # type: ignore
# blueprint settings
export_blueprints: BoolProperty(
name='Export Blueprints',
description='Replaces collection instances with an Empty with a BlueprintName custom property, and enabled a lot more features !',
default=True
) # type: ignore
export_separate_dynamic_and_static_objects: BoolProperty(
name="Export levels' dynamic and static objects seperatly",
description="""For MAIN scenes only (aka levels), toggle this to generate 2 files per level:
- one with all dynamic data: collection or instances marked as dynamic/ saveable
- one with all static data: anything else that is NOT marked as dynamic""",
default=False
) # type: ignore
export_materials_library: BoolProperty(
name='Export materials library',
description='remove materials from blueprints and use the material library instead',
default=False
) # type: ignore
""" combine mode can be
- 'Split' (default): replace with an empty, creating links to sub blueprints
- 'Embed' : treat it as an embeded object and do not replace it with an empty
- 'EmbedExternal': embed any instance of a non local collection (ie external assets)
- 'Inject': inject components from sub collection instances into the curent object => this is now a seperate custom property that you can apply to a collecion instance
"""
collection_instances_combine_mode : EnumProperty(
name='Collection instances',
items=(
('Split', 'Split', 'replace collection instances with an empty + blueprint, creating links to sub blueprints (Default, Recomended)'),
('Embed', 'Embed', 'treat collection instances as embeded objects and do not replace them with an empty'),
('EmbedExternal', 'EmbedExternal', 'treat instances of external (not specifified in the current blend file) collections (aka assets etc) as embeded objects and do not replace them with empties'),
#('Inject', 'Inject', 'inject components from sub collection instances into the curent object')
),
default='Split'
) # type: ignore
export_marked_assets: BoolProperty(
name='Auto export marked assets',
description='Collections that have been marked as assets will be systematically exported, even if not in use in another scene',
default=True
) # type: ignore

View File

@ -0,0 +1,67 @@
def draw_settings_ui(layout, auto_export_settings):
controls_enabled = auto_export_settings.auto_export
layout.prop(auto_export_settings, "auto_export")
layout.separator()
header, panel = layout.panel("General", default_closed=False)
header.label(text="General")
if panel:
section = panel.box()
section.enabled = controls_enabled
op = section.operator("EXPORT_SCENE_OT_gltf", text="Gltf Settings (KEEP 'REMEMBER EXPORT SETTINGS' TOGGLED)")#'glTF 2.0 (.glb/.gltf)')
#op.export_format = 'GLTF_SEPARATE'
op.use_selection=True
op.will_save_settings=True
op.use_visible=True # Export visible and hidden objects. See Object/Batch Export to skip.
op.use_renderable=True
op.use_active_collection = True
op.use_active_collection_with_nested=True
op.use_active_scene = True
op.filepath="____dummy____"
op.gltf_export_id = "gltf_auto_export" # we specify that we are in a special case
section.prop(auto_export_settings, "export_scene_settings")
header, panel = layout.panel("Change Detection", default_closed=False)
header.label(text="Change Detection")
if panel:
section = panel.box()
section.enabled = controls_enabled
section.prop(auto_export_settings, "export_change_detection", text="Use change detection")
header, panel = layout.panel("Blueprints", default_closed=False)
header.label(text="Blueprints")
if panel:
section = layout.box()
section.enabled = controls_enabled
section.prop(auto_export_settings, "export_blueprints")
section = section.box()
section.enabled = controls_enabled and auto_export_settings.export_blueprints
# collections/blueprints
section.prop(auto_export_settings, "collection_instances_combine_mode")
section.prop(auto_export_settings, "export_marked_assets")
section.separator()
section.prop(auto_export_settings, "export_separate_dynamic_and_static_objects")
section.separator()
# materials
section.prop(auto_export_settings, "export_materials_library")
section = section.box()
section.enabled = controls_enabled and auto_export_settings.export_materials_library
#section.prop(auto_export_settings, "materials_path")

View File

@ -1,185 +0,0 @@
from typing import Set
import bpy
######################################################
## ui logic & co
# side panel that opens auto_export specific gltf settings & the auto export settings themselves
class GLTF_PT_auto_export_SidePanel(bpy.types.Panel):
bl_idname = "GLTF_PT_auto_export_SidePanel"
bl_label = "Auto export"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Auto Export"
bl_context = "objectmode"
bl_parent_id = "BLENVY_PT_SidePanel"
@classmethod
def poll(cls, context):
return context.window_manager.blenvy.mode == 'SETTINGS'
"""def draw_header(self, context):
layout = self.layout
layout.label(text="Auto export ")"""
def draw(self, context):
layout = self.layout
layout.label(text="MAKE SURE TO KEEP 'REMEMBER EXPORT SETTINGS' TOGGLED !!")
op = layout.operator("EXPORT_SCENE_OT_gltf", text='Gltf Settings')#'glTF 2.0 (.glb/.gltf)')
#op.export_format = 'GLTF_SEPARATE'
op.use_selection=True
op.will_save_settings=True
op.use_visible=True # Export visible and hidden objects. See Object/Batch Export to skip.
op.use_renderable=True
op.use_active_collection = True
op.use_active_collection_with_nested=True
op.use_active_scene = True
op.filepath="____dummy____"
op.gltf_export_id = "gltf_auto_export" # we specify that we are in a special case
op = layout.operator("EXPORT_SCENES_OT_auto_gltf", text="Auto Export Settings")
op.auto_export = True
# main ui in the file => export
class GLTF_PT_auto_export_main(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = ""
bl_parent_id = "FILE_PT_operator"
bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
class GLTF_PT_auto_export_root(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Auto export"
bl_parent_id = "GLTF_PT_auto_export_main"
#bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf"
def draw_header(self, context):
sfile = context.space_data
operator = sfile.active_operator
self.layout.prop(operator, "auto_export", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
sfile = context.space_data
operator = sfile.active_operator
layout.active = operator.auto_export
layout.prop(operator, 'will_save_settings')
class GLTF_PT_auto_export_general(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "General"
bl_parent_id = "GLTF_PT_auto_export_root"
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" #"EXPORT_SCENE_OT_gltf"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
sfile = context.space_data
operator = sfile.active_operator
layout.active = operator.auto_export
layout.prop(operator, "export_assets_path")
layout.prop(operator, "export_scene_settings")
class GLTF_PT_auto_export_change_detection(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Change detection"
bl_parent_id = "GLTF_PT_auto_export_root"
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" #"EXPORT_SCENE_OT_gltf"
def draw_header(self, context):
layout = self.layout
sfile = context.space_data
operator = sfile.active_operator
layout.prop(operator, "export_change_detection", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
sfile = context.space_data
operator = sfile.active_operator
layout.active = operator.auto_export
layout.prop(operator, "export_change_detection")
class GLTF_PT_auto_export_blueprints(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Blueprints"
bl_parent_id = "GLTF_PT_auto_export_root"
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" #"EXPORT_SCENE_OT_gltf"
def draw_header(self, context):
layout = self.layout
sfile = context.space_data
operator = sfile.active_operator
layout.prop(operator, "export_blueprints", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
sfile = context.space_data
operator = sfile.active_operator
layout.active = operator.auto_export and operator.export_blueprints
# collections/blueprints
layout.prop(operator, "export_blueprints_path")
layout.prop(operator, "collection_instances_combine_mode")
layout.prop(operator, "export_marked_assets")
layout.prop(operator, "export_separate_dynamic_and_static_objects")
layout.separator()
# materials
layout.prop(operator, "export_materials_library")
layout.prop(operator, "export_materials_path")

View File

@ -2,12 +2,42 @@ import json
import bpy import bpy
def upsert_settings(name, data): def upsert_settings(name, data):
stored_settings = bpy.data.texts[name] if name in bpy.data.texts else bpy.data.texts.new(name) stored_settings = bpy.data.texts[name] if name in bpy.data.texts else None#bpy.data.texts.new(name)
stored_settings.clear() if stored_settings is None:
stored_settings = bpy.data.texts.new(name)
stored_settings.write(json.dumps(data)) stored_settings.write(json.dumps(data))
else:
current_settings = json.loads(stored_settings.as_string())
current_settings = {**current_settings, **data}
stored_settings.clear()
stored_settings.write(json.dumps(current_settings))
def load_settings(name): def load_settings(name):
stored_settings = bpy.data.texts[name] if name in bpy.data.texts else None stored_settings = bpy.data.texts[name] if name in bpy.data.texts else None
if stored_settings != None: if stored_settings != None:
return json.loads(stored_settings.as_string()) return json.loads(stored_settings.as_string())
return None return None
# checks if old & new settings (dicts really) are identical
def are_settings_identical(old, new, white_list=None):
if old is None and new is None:
return True
if old is None and new is not None:
return False
if old is not None and new is None:
return False
old_items = sorted(old.items())
new_items = sorted(new.items())
if white_list is not None:
old_items_override = {}
new_items_override = {}
for key in white_list:
if key in old_items:
old_items_override[key] = old_items[key]
if key in new_items:
new_items_override[key] = new_items[key]
old_items = old_items_override
new_items = new_items_override
return old_items != new_items if new is not None else False

View File

@ -103,10 +103,10 @@ def test_export_complex(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(root_path), project_root_path = os.path.abspath(root_path),
#export_blueprints_path = os.path.join("assets", "models", "library"), #blueprints_path = os.path.join("assets", "models", "library"),
export_output_folder = os.path.join("assets", "models"), #"./models", export_output_folder = os.path.join("assets", "models"), #"./models",
#export_levels_path = os.path.join("assets", "models"), #levels_path = os.path.join("assets", "models"),
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,

View File

@ -53,10 +53,10 @@ def test_export_external_blueprints(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(root_path), project_root_path = os.path.abspath(root_path),
#export_blueprints_path = os.path.join("assets", "models", "library"), #blueprints_path = os.path.join("assets", "models", "library"),
#export_output_folder = os.path.join("assets", "models"), #"./models", #export_output_folder = os.path.join("assets", "models"), #"./models",
#export_levels_path = os.path.join("assets", "models"), #levels_path = os.path.join("assets", "models"),
export_scene_settings=False, export_scene_settings=False,
export_blueprints=True, export_blueprints=True,

View File

@ -82,7 +82,7 @@ def test_export_no_parameters(setup_data):
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_materials_library=True, export_materials_library=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
) )
@ -104,7 +104,7 @@ def test_export_auto_export_parameters_only(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_materials_library=True export_materials_library=True
) )
@ -140,7 +140,7 @@ def test_export_changed_parameters(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,
@ -159,7 +159,7 @@ def test_export_changed_parameters(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,
@ -184,7 +184,7 @@ def test_export_changed_parameters(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,
@ -200,7 +200,7 @@ def test_export_changed_parameters(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,
@ -229,7 +229,7 @@ def test_export_changed_parameters(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,
@ -245,7 +245,7 @@ def test_export_changed_parameters(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,

View File

@ -11,7 +11,7 @@ from .setup_data import setup_data
def test_components_should_generate_correct_custom_properties(setup_data): def test_components_should_generate_correct_custom_properties(setup_data):
registry = bpy.context.window_manager.components_registry registry = bpy.context.window_manager.components_registry
registry.schemaPath = setup_data["schema_path"] registry.schemaPath = setup_data["components_schemaPath"]
bpy.ops.object.reload_registry() bpy.ops.object.reload_registry()
type_infos = registry.type_infos type_infos = registry.type_infos
@ -58,7 +58,7 @@ def test_components_should_generate_correct_custom_properties(setup_data):
def test_components_should_generate_correct_custom_properties_with_randomized_values(setup_data): def test_components_should_generate_correct_custom_properties_with_randomized_values(setup_data):
registry = bpy.context.window_manager.components_registry registry = bpy.context.window_manager.components_registry
registry.schemaPath = setup_data["schema_path"] registry.schemaPath = setup_data["components_schemaPath"]
bpy.ops.object.reload_registry() bpy.ops.object.reload_registry()
type_infos = registry.type_infos type_infos = registry.type_infos
@ -108,7 +108,7 @@ def test_components_should_generate_correct_custom_properties_with_randomized_va
def test_components_should_generate_correct_propertyGroup_values_from_custom_properties(setup_data): def test_components_should_generate_correct_propertyGroup_values_from_custom_properties(setup_data):
registry = bpy.context.window_manager.components_registry registry = bpy.context.window_manager.components_registry
registry.schemaPath = setup_data["schema_path"] registry.schemaPath = setup_data["components_schemaPath"]
bpy.ops.object.reload_registry() bpy.ops.object.reload_registry()
type_infos = registry.type_infos type_infos = registry.type_infos
@ -166,7 +166,7 @@ def test_components_should_generate_correct_propertyGroup_values_from_custom_pro
def test_remove_components(setup_data): def test_remove_components(setup_data):
registry = bpy.context.window_manager.components_registry registry = bpy.context.window_manager.components_registry
registry.schemaPath = setup_data["schema_path"] registry.schemaPath = setup_data["components_schemaPath"]
bpy.ops.object.reload_registry() bpy.ops.object.reload_registry()
type_infos = registry.type_infos type_infos = registry.type_infos
@ -207,7 +207,7 @@ def test_remove_components(setup_data):
def test_copy_paste_components(setup_data): def test_copy_paste_components(setup_data):
context = bpy.context context = bpy.context
registry = context.window_manager.components_registry registry = context.window_manager.components_registry
registry.schemaPath = setup_data["schema_path"] registry.schemaPath = setup_data["components_schemaPath"]
bpy.ops.object.reload_registry() bpy.ops.object.reload_registry()
long_name = "bevy_example::test_components::BasicTest" long_name = "bevy_example::test_components::BasicTest"

View File

@ -80,7 +80,7 @@ def test_export_do_not_export_blueprints(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="assets/models", export_output_folder="assets/models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=False, export_blueprints=False,
@ -107,11 +107,11 @@ def test_export_custom_blueprints_path(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,
export_blueprints_path = "assets/other_blueprints" blueprints_path = "assets/other_blueprints"
) )
assert os.path.exists(os.path.join(setup_data["levels_path"], "World.glb")) == True assert os.path.exists(os.path.join(setup_data["levels_path"], "World.glb")) == True
assert os.path.exists(os.path.join(setup_data["root_path"],"assets", "other_blueprints", "Blueprint1.glb")) == True assert os.path.exists(os.path.join(setup_data["root_path"],"assets", "other_blueprints", "Blueprint1.glb")) == True
@ -133,7 +133,7 @@ def test_export_materials_library(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,
@ -160,12 +160,12 @@ def test_export_materials_library_custom_path(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,
export_materials_library = True, export_materials_library = True,
export_materials_path="assets/other_materials" materials_path="assets/other_materials"
) )
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "Blueprint1.glb")) == True assert os.path.exists(os.path.join(setup_data["blueprints_path"], "Blueprint1.glb")) == True
@ -192,7 +192,7 @@ def test_export_collection_instances_combine_mode(setup_data): # There is more i
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_blueprints=True, export_blueprints=True,
collection_instances_combine_mode = 'Embed' collection_instances_combine_mode = 'Embed'
@ -219,7 +219,7 @@ def test_export_do_not_export_marked_assets(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,
@ -253,7 +253,7 @@ def test_export_separate_dynamic_and_static_objects(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,
@ -281,7 +281,7 @@ def test_export_should_not_generate_orphan_data(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,

View File

@ -29,7 +29,7 @@ def run_auto_export(setup_data):
auto_export_operator( auto_export_operator(
auto_export=True, auto_export=True,
direct_mode=True, direct_mode=True,
export_root_path = os.path.abspath(setup_data["root_path"]), project_root_path = os.path.abspath(setup_data["root_path"]),
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
export_blueprints=True, export_blueprints=True,

View File

@ -3,7 +3,7 @@ from .setup_data import setup_data
def test_blend(setup_data): def test_blend(setup_data):
registry = bpy.context.window_manager.components_registry registry = bpy.context.window_manager.components_registry
registry.schemaPath = setup_data["schema_path"] registry.schemaPath = setup_data["components_schemaPath"]
bpy.ops.object.reload_registry() bpy.ops.object.reload_registry()
long_name = "bevy_example::test_components::BasicTest" long_name = "bevy_example::test_components::BasicTest"

View File

@ -24,7 +24,7 @@ def get_component_propGroup(registry, component_name, component_meta):
def test_rename_component_single_unit_struct(setup_data): def test_rename_component_single_unit_struct(setup_data):
registry = bpy.context.window_manager.components_registry registry = bpy.context.window_manager.components_registry
registry.schemaPath = setup_data["schema_path"] registry.schemaPath = setup_data["components_schemaPath"]
bpy.ops.object.reload_registry() bpy.ops.object.reload_registry()
rename_component_operator = bpy.ops.object.rename_bevy_component rename_component_operator = bpy.ops.object.rename_bevy_component
@ -47,7 +47,7 @@ def test_rename_component_single_unit_struct(setup_data):
def test_rename_component_single_complex_struct(setup_data): def test_rename_component_single_complex_struct(setup_data):
registry = bpy.context.window_manager.components_registry registry = bpy.context.window_manager.components_registry
registry.schemaPath = setup_data["schema_path"] registry.schemaPath = setup_data["components_schemaPath"]
bpy.ops.object.reload_registry() bpy.ops.object.reload_registry()
rename_component_operator = bpy.ops.object.rename_bevy_component rename_component_operator = bpy.ops.object.rename_bevy_component
@ -70,7 +70,7 @@ def test_rename_component_single_complex_struct(setup_data):
def test_rename_component_bulk(setup_data): def test_rename_component_bulk(setup_data):
registry = bpy.context.window_manager.components_registry registry = bpy.context.window_manager.components_registry
registry.schemaPath = setup_data["schema_path"] registry.schemaPath = setup_data["components_schemaPath"]
bpy.ops.object.reload_registry() bpy.ops.object.reload_registry()
rename_component_operator = bpy.ops.object.rename_bevy_component rename_component_operator = bpy.ops.object.rename_bevy_component
@ -95,7 +95,7 @@ def test_rename_component_bulk(setup_data):
def test_rename_component_single_error_handling(setup_data): def test_rename_component_single_error_handling(setup_data):
registry = bpy.context.window_manager.components_registry registry = bpy.context.window_manager.components_registry
registry.schemaPath = setup_data["schema_path"] registry.schemaPath = setup_data["components_schemaPath"]
bpy.ops.object.reload_registry() bpy.ops.object.reload_registry()
rename_component_operator = bpy.ops.object.rename_bevy_component rename_component_operator = bpy.ops.object.rename_bevy_component
@ -125,7 +125,7 @@ def test_rename_component_single_error_handling(setup_data):
def test_rename_component_single_error_handling_clean_errors(setup_data): def test_rename_component_single_error_handling_clean_errors(setup_data):
registry = bpy.context.window_manager.components_registry registry = bpy.context.window_manager.components_registry
registry.schemaPath = setup_data["schema_path"] registry.schemaPath = setup_data["components_schemaPath"]
bpy.ops.object.reload_registry() bpy.ops.object.reload_registry()
rename_component_operator = bpy.ops.object.rename_bevy_component rename_component_operator = bpy.ops.object.rename_bevy_component