Compare commits

...

3 Commits

Author SHA1 Message Date
kaosat.dev
73441f34a4 feat(auto_export): restructured & improved changed parameters detection
* now all centralized in the operator's did_export_settings_change() function
 * now works correctly ! ie only compares the parameters for auto & gltf
at the time of exporting ! ie it does not matter anymore how many parameters you changed, until you save/export
 * this solves a lot of randomness bugs in change detection
 * related & various cleanups
2024-04-02 16:45:01 +02:00
kaosat.dev
73e81c2b64 feat(auto_export): continued cleaning up overhaul
* added both gltf exporter button AND auto export button to side bar
 * added storage of gltf settings
 * auto_export now uses stored gltf settings
 * updated override strategies of defaults vs gltf settings
 * removed all gltf specific (not auto export) parameters from preferences
 * updated auto export operator params changed logic to use updated system
 * updated bevy integration test
 * ui improvements
 * various tweaks
2024-04-02 14:15:56 +02:00
kaosat.dev
4a0479fbf0 feat(auto_export): further experimenting with standard gltf exporter integration 2024-04-02 00:46:09 +02:00
12 changed files with 976 additions and 495 deletions

View File

@ -10,10 +10,15 @@ bl_info = {
"tracker_url": "https://github.com/kaosat-dev/Blender_bevy_components_workflow/issues/new",
"category": "Import-Export"
}
import os
from pathlib import Path
import json
import bpy
from bpy.types import Context
from bpy.props import (StringProperty, BoolProperty, PointerProperty)
# from .extension import ExampleExtensionProperties, GLTF_PT_UserExtensionPanel, unregister_panel
from .auto_export.operators import AutoExportGLTF
from .auto_export.tracker import AutoExportTracker
from .auto_export.preferences import (AutoExportGltfAddonPreferences)
@ -26,10 +31,18 @@ from .auto_export.internals import (SceneLink,
)
from .ui.main import (GLTF_PT_auto_export_main,
GLTF_PT_auto_export_root,
GLTF_PT_auto_export_general,
GLTF_PT_auto_export_scenes,
GLTF_PT_auto_export_blueprints,
GLTF_PT_auto_export_collections_list,
GLTF_PT_auto_export_gltf,
SCENE_UL_GLTF_auto_export,
HelloWorldOperator,
#GLTF_PT_export_data,
#GLTF_PT_export_data_scene
)
from .ui.operators import (SCENES_LIST_OT_actions)
@ -40,8 +53,8 @@ from .ui.operators import (SCENES_LIST_OT_actions)
- in auto_export.py -> auto_export
This is a workaround needed because of the way the settings are stored , perhaps there is a better way to deal with it ? ie by calling the AutoExportGLTF operator from the auto_export function ?
"""
from io_scene_gltf2 import (ExportGLTF2, GLTF_PT_export_main, GLTF_PT_export_include)
from io_scene_gltf2 import (GLTF_PT_export_main, GLTF_PT_export_include)
class Testing_PT_MainPanel(bpy.types.Panel):
bl_idname = "Testing_PT_MainPanel"
@ -54,16 +67,12 @@ class Testing_PT_MainPanel(bpy.types.Panel):
def draw_header(self, context):
layout = self.layout
name = context.object.name if context.object != None else ''
layout.label(text="Gltf auto export ")
def draw(self, context):
layout = self.layout
object = context.object
collection = context.collection
layout.label(text="MAKE SURE TO CLICK ON 'REMEMBER EXPORT SETTINGS' !!")
op = layout.operator("EXPORT_SCENE_OT_gltf", text='Gltf settings')#'glTF 2.0 (.glb/.gltf)')
layout.label(text="MAKE SURE TO KEEP 'REMEMBER EXPORT SETTINGS' !!")
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
@ -74,62 +83,13 @@ class Testing_PT_MainPanel(bpy.types.Panel):
op.use_active_scene = True
op.filepath="dummy"
op = layout.operator("EXPORT_SCENES_OT_auto_gltf", text="Auto Export Settings")
op.will_save_settings=True
#print("GLTF_PT_export_main", GLTF_PT_export_main.bl_parent_id)
# wrapper operator
"""class WrapperOperator(bpy.types.Operator):
bl_idname = "node.delete"
bl_label = "Delete"
@classmethod
def poll(cls, context):
return True
def execute(self, context):
# unregister wrapper replacement operator
bpy.utils.unregister_class(DeleteOperator)
# call original native operator
bpy.ops.node.delete()
# register back the wrapper
bpy.utils.register_class(DeleteOperator)
return {'FINISHED'}"""
class GLTF_PT_export_main2(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_label = ""
bl_parent_id = "Testing_PT_MainPanel"
bl_options = {'HIDE_HEADER'}
'''@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "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.prop(operator, 'export_format')
if operator.export_format == 'GLTF_SEPARATE':
layout.prop(operator, 'export_keep_originals')
if operator.export_keep_originals is False:
layout.prop(operator, 'export_texture_dir', icon='FILE_FOLDER')
if operator.export_format == 'GLTF_EMBEDDED':
layout.label(text="This is the least efficient of the available forms, and should only be used when required.", icon='ERROR')
layout.prop(operator, 'export_copyright')
layout.prop(operator, 'will_save_settings')"""
#see here for original gltf exporter infos https://github.com/KhronosGroup/glTF-Blender-IO/blob/main/addons/io_scene_gltf2/__init__.py
classes = [
SceneLink,
@ -141,20 +101,25 @@ classes = [
AutoExportGLTF,
#AutoExportGltfAddonPreferences,
HelloWorldOperator,
CollectionToExport,
CollectionsToExport,
GLTF_PT_auto_export_main,
GLTF_PT_auto_export_root,
GLTF_PT_auto_export_general,
GLTF_PT_auto_export_scenes,
GLTF_PT_auto_export_blueprints,
GLTF_PT_auto_export_collections_list,
GLTF_PT_auto_export_gltf,
#GLTF_PT_auto_export_collections_list,
#GLTF_PT_auto_export_gltf,
#GLTF_PT_export_data,
#GLTF_PT_export_data_scene,
AutoExportTracker,
Testing_PT_MainPanel,
GLTF_PT_export_main2,
]
def menu_func_import(self, context):
@ -170,6 +135,37 @@ def post_save(scene, depsgraph):
bpy.context.window_manager.auto_export_tracker.save_handler( scene, depsgraph)
def invoke_override(self, context, event):
settings = context.scene.get(self.scene_key)
self.will_save_settings = False
if settings:
try:
for (k, v) in settings.items():
setattr(self, k, v)
self.will_save_settings = True
# Update filter if user saved settings
if hasattr(self, 'export_format'):
self.filter_glob = '*.glb' if self.export_format == 'GLB' else '*.gltf'
except (AttributeError, TypeError):
self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
del context.scene[self.scene_key]
import sys
preferences = bpy.context.preferences
for addon_name in preferences.addons.keys():
try:
if hasattr(sys.modules[addon_name], 'glTF2ExportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ExportUserExtensions'):
pass #exporter_extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel())
except Exception:
pass
# self.has_active_exporter_extensions = len(exporter_extension_panel_unregister_functors) > 0
print("ovverride")
wm = context.window_manager
wm.fileselect_add(self)
return {'RUNNING_MODAL'}
def register():
@ -181,18 +177,7 @@ def register():
# add our addon to the toolbar
bpy.types.TOPBAR_MT_file_export.append(menu_func_import)
bpy.types.WindowManager.was_good_operator = BoolProperty(default=False)
bpy.types.Scene.was_good_operator = BoolProperty(default=False)
#GLTF_PT_export_main.bl_parent_id = "Testing_PT_MainPanel"
#GLTF_PT_export_include.bl_parent_id = "Testing_PT_MainPanel"
"""try:
bpy.app.timers.unregister(watch_schema)
except Exception as error:
print("failed to unregister", error)
pass"""
bpy.types.WindowManager.gltf_settings_backup = StringProperty(default="")
def unregister():
for cls in classes:
@ -202,8 +187,44 @@ def unregister():
bpy.app.handlers.depsgraph_update_post.remove(post_update)
bpy.app.handlers.save_post.remove(post_save)
global gltf_settings_backup
def glTF2_pre_export_callback(data):
pass #print("pre_export", data)
# we backup any existing gltf export settings, if there where any
scene = bpy.context.scene
if "glTF2ExportSettings" in scene:
existing_setting = scene["glTF2ExportSettings"]
gltf_settings_backup = existing_setting
def glTF2_post_export_callback(data):
gltf_settings_backup = ""
#print("post_export", data)
gltf_filepath = data["gltf_filepath"]
filename = Path(gltf_filepath).stem
if filename == "dummy": # TODO: potentially use a hidden gltf exporter extension with an additional parameter instead of this hack
if os.path.exists(gltf_filepath):
print("removing dummy file")
os.unlink(gltf_filepath)
# get the parameters
scene = bpy.context.scene
if "glTF2ExportSettings" in scene:
settings = scene["glTF2ExportSettings"]
export_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings")
# now write new settings
export_settings.clear()
export_settings.write(json.dumps(dict(settings)))
# now reset the original gltf_settings
if gltf_settings_backup != "":
print("resetting original gltf settings")
#scene["glTF2ExportSettings"] = gltf_settings_backup
else:
print("no pre_existing settings")
if "glTF2ExportSettings" in scene:
del scene["glTF2ExportSettings"]
gltf_settings_backup = ""
if "gltf_auto_export" == "__main__":
register()

View File

@ -0,0 +1,173 @@
import bpy
from bpy.props import (BoolProperty,
IntProperty,
StringProperty,
EnumProperty,
CollectionProperty
)
bl_info = {
"name": "auto_export",
"category": "Generic",
"version": (1, 0, 0),
"blender": (2, 80, 0),
'location': 'File > Export > glTF 2.0',
'description': 'Example addon to add a custom extension to an exported glTF file.',
'tracker_url': "https://github.com/KhronosGroup/glTF-Blender-IO/issues/", # Replace with your issue tracker
'isDraft': False,
'developer': "(Your name here)", # Replace this
'url': 'https://your_url_here', # Replace this
}
# glTF extensions are named following a convention with known prefixes.
# See: https://github.com/KhronosGroup/glTF/tree/main/extensions#about-gltf-extensions
# also: https://github.com/KhronosGroup/glTF/blob/main/extensions/Prefixes.md
glTF_extension_name = "EXT_auto_export"
# Support for an extension is "required" if a typical glTF viewer cannot be expected
# to load a given model without understanding the contents of the extension.
# For example, a compression scheme or new image format (with no fallback included)
# would be "required", but physics metadata or app-specific settings could be optional.
extension_is_required = False
from io_scene_gltf2 import (GLTF_PT_export_main, GLTF_PT_export_include)
class ExampleExtensionProperties(bpy.types.PropertyGroup):
enabled: bpy.props.BoolProperty(
name=bl_info["name"],
description='Include this extension in the exported glTF file.',
default=True
)
auto_export_main_scene_name: StringProperty(
name='Main scene',
description='The name of the main scene/level/world to auto export',
default='Scene'
)
auto_export_output_folder: StringProperty(
name='Export folder (relative)',
description='The root folder for all exports(relative to current file) Defaults to current folder',
default=''
)
auto_export_library_scene_name: StringProperty(
name='Library scene',
description='The name of the library scene to auto export',
default='Library'
)
# scene components
auto_export_scene_settings: BoolProperty(
name='Export scene settings',
description='Export scene settings ie AmbientLighting, Bloom, AO etc',
default=False
)
# blueprint settings
auto_export_blueprints: BoolProperty(
name='Export Blueprints',
description='Replaces collection instances with an Empty with a BlueprintName custom property',
default=True
)
auto_export_blueprints_path: StringProperty(
name='Blueprints path',
description='path to export the blueprints to (relative to the Export folder)',
default='library'
)
auto_export_materials_library: BoolProperty(
name='Export materials library',
description='remove materials from blueprints and use the material library instead',
default=False
)
auto_export_materials_path: StringProperty(
name='Materials path',
description='path to export the materials libraries to (relative to the root folder)',
default='materials'
)
def register():
bpy.utils.register_class(ExampleExtensionProperties)
bpy.types.Scene.ExampleExtensionProperties = bpy.props.PointerProperty(type=ExampleExtensionProperties)
def register_panel():
# Register the panel on demand, we need to be sure to only register it once
# This is necessary because the panel is a child of the extensions panel,
# which may not be registered when we try to register this extension
try:
bpy.utils.register_class(GLTF_PT_UserExtensionPanel)
except Exception:
pass
# If the glTF exporter is disabled, we need to unregister the extension panel
# Just return a function to the exporter so it can unregister the panel
return unregister_panel
def unregister_panel():
# Since panel is registered on demand, it is possible it is not registered
try:
bpy.utils.unregister_class(GLTF_PT_UserExtensionPanel)
except Exception:
pass
def unregister():
unregister_panel()
bpy.utils.unregister_class(ExampleExtensionProperties)
del bpy.types.Scene.ExampleExtensionProperties
class GLTF_PT_UserExtensionPanel(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Enabled"
bl_parent_id = "GLTF_PT_export_user_extensions"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
def draw_header(self, context):
props = bpy.context.scene.ExampleExtensionProperties
self.layout.prop(props, 'enabled')
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
props = bpy.context.scene.ExampleExtensionProperties
layout.active = props.enabled
props = bpy.context.scene.ExampleExtensionProperties
for bla in props.__annotations__:
layout.prop(props, bla)
class glTF2ExportUserExtension:
def __init__(self):
# We need to wait until we create the gltf2UserExtension to import the gltf2 modules
# Otherwise, it may fail because the gltf2 may not be loaded yet
from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
self.Extension = Extension
self.properties = bpy.context.scene.ExampleExtensionProperties
def gather_node_hook(self, gltf2_object, blender_object, export_settings):
if self.properties.enabled:
if gltf2_object.extensions is None:
gltf2_object.extensions = {}
print("bla bla")
gltf2_object.extensions[glTF_extension_name] = self.Extension(
name=glTF_extension_name,
extension={"auto_export_blueprints": self.properties.auto_export_blueprints},
required=extension_is_required
)
def pre_export_hook(self):
print("pre export callback")
def glTF2_pre_export_callback(data):
print("pre_export", data)
def glTF2_post_export_callback(data):
print("post_export", data)

View File

@ -1,3 +1,4 @@
import json
import os
import bpy
import traceback
@ -33,6 +34,11 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
[main_scene_names, level_scenes, library_scene_names, library_scenes] = get_scenes(addon_prefs)
# standard gltf export settings are stored differently
standard_gltf_exporter_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings")
print("standard_gltf_exporter_settings", standard_gltf_exporter_settings.as_string())
standard_gltf_exporter_settings = json.loads(standard_gltf_exporter_settings.as_string())
print("main scenes", main_scene_names, "library_scenes", library_scene_names)
print("export_output_folder", export_output_folder)
@ -63,7 +69,8 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
export_blueprints_path = os.path.join(folder_path, export_output_folder, getattr(addon_prefs,"export_blueprints_path")) if getattr(addon_prefs,"export_blueprints_path") != '' else folder_path
export_levels_path = os.path.join(folder_path, export_output_folder)
gltf_extension = getattr(addon_prefs, "export_format")
gltf_extension = standard_gltf_exporter_settings.get("export_format", 'GLB')
gltf_extension = '.glb' if gltf_extension == 'GLB' else '.gltf'
collections_not_on_disk = check_if_blueprints_exist(collections, export_blueprints_path, gltf_extension)
changed_collections = []

View File

@ -1,3 +1,4 @@
import json
import os
import bpy
@ -7,7 +8,7 @@ from .preferences import (AutoExportGltfPreferenceNames)
def generate_gltf_export_preferences(addon_prefs):
# default values
gltf_export_preferences = dict(
export_format= 'GLB', #'GLB', 'GLTF_SEPARATE', 'GLTF_EMBEDDED'
# export_format= 'GLB', #'GLB', 'GLTF_SEPARATE', 'GLTF_EMBEDDED'
check_existing=False,
use_selection=False,
@ -17,41 +18,48 @@ def generate_gltf_export_preferences(addon_prefs):
use_active_collection_with_nested=False,
use_active_scene = False,
export_texcoords=True,
export_normals=True,
# here add draco settings
export_draco_mesh_compression_enable = False,
export_tangents=False,
#export_materials
export_colors=True,
export_attributes=True,
#use_mesh_edges
#use_mesh_vertices
export_cameras=True,
export_extras=True, # For custom exported properties.
export_lights=True,
export_yup=True,
export_skins=True,
export_morph=False,
export_apply=False,
export_animations=False,
export_optimize_animation_size=False
#export_texcoords=True,
#export_normals=True,
# here add draco settings
#export_draco_mesh_compression_enable = False,
#export_tangents=False,
#export_materials
#export_colors=True,
#export_attributes=True,
#use_mesh_edges
#use_mesh_vertices
#export_yup=True,
#export_skins=True,
#export_morph=False,
#export_apply=False,
#export_animations=False,
#export_optimize_animation_size=False
)
for key in addon_prefs.__annotations__.keys():
if str(key) not in AutoExportGltfPreferenceNames:
#print("overriding setting", key, "value", getattr(addon_prefs,key))
gltf_export_preferences[key] = getattr(addon_prefs, key)
"""standard_gltf_exporter_settings = get_standard_exporter_settings()
standard_gltf_exporter_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings")
standard_gltf_exporter_settings = json.loads(standard_gltf_exporter_settings.as_string())
"""standard_gltf_exporter_settings = get_standard_exporter_settings()"""
print("standard settings", standard_gltf_exporter_settings)
constant_keys = [
'use_selection',
'use_visible',
'use_active_collection',
'use_active_collection_with_nested',
'use_active_scene',
'export_cameras',
'export_extras', # For custom exported properties.
'export_lights',
@ -61,8 +69,8 @@ def generate_gltf_export_preferences(addon_prefs):
for key in standard_gltf_exporter_settings.keys():
if str(key) not in constant_keys:
gltf_export_preferences[key] = standard_gltf_exporter_settings.get(key)
print("final export preferences", gltf_export_preferences)"""
print("")
print("final export preferences", gltf_export_preferences)
return gltf_export_preferences

View File

@ -8,6 +8,8 @@ from ..helpers.helpers_scenes import (get_scenes)
from ..helpers.helpers_collections import (get_exportable_collections)
from .auto_export import auto_export
from io_scene_gltf2 import (ExportGLTF2, GLTF_PT_export_main,ExportGLTF2_Base, GLTF_PT_export_include)
class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper):
"""auto export gltf"""
#bl_idname = "object.xxx"
@ -66,7 +68,7 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper):
except:
return True
def save_settings(self, context):
def format_settings(self):
# find all props to save
exceptional = [
# options that don't start with 'export_'
@ -84,10 +86,6 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper):
# FIXME: really weird having to do this
if k == "collection_instances_combine_mode":
value = self.collection_instances_combine_mode
if k == "export_format":
value = self.export_format
if k == "export_image_format":
value = self.export_image_format
if k == "export_materials":
value = self.export_materials
export_props[k] = value
@ -95,6 +93,10 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper):
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
def save_settings(self, context):
export_props = self.format_settings()
self.properties['main_scene_names'] = export_props['main_scene_names']
self.properties['library_scene_names'] = export_props['library_scene_names']
@ -144,35 +146,65 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper):
self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
bpy.data.texts.remove(bpy.data.texts[".gltf_auto_export_settings"])
"""
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(self):
previous_export_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else None
print("comparing settings")
# 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 was no setting before, it is new, we need export
if previous_export_settings == None:
export_settings = {}
for (k, v) in self.properties.items():
if k not in AutoExportGltfPreferenceNames:
export_settings[k] = v
export_settings = str(export_settings)
# the actual gltf export settings, not those of auto export
stored_export_settings = bpy.data.texts.new(".gltf_auto_export_gltf_settings")
stored_export_settings.write(export_settings)
return True
# if there were no setting before, it is new, we need export
changed = False
if previous_auto_settings == None or previous_gltf_settings == None:
print("previous settings missing, exporting")
changed = True
else:
export_settings = {}
for (k, v) in self.properties.items():
if k in self.white_list or k not in AutoExportGltfPreferenceNames:
export_settings[k] = v
if len(export_settings.keys()) == 0: # first time after we already used the addon, since we already have export settings, but they have not yet been applied
return False
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
export_settings = str(export_settings)
changed = export_settings != previous_export_settings.as_string()
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)
previous_export_settings.clear()
previous_export_settings.write(export_settings)
return 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
# now write the current settings to the "previous settings"
previous_auto_settings = bpy.data.texts[".gltf_auto_export_settings_previous"] if ".gltf_auto_export_settings_previous" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings_previous")
previous_auto_settings.clear()
previous_auto_settings.write(current_auto_settings.as_string()) # TODO : check if this is always valid
previous_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings_previous"] if ".gltf_auto_export_gltf_settings_previous" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings_previous")
previous_gltf_settings.clear()
previous_gltf_settings.write(current_gltf_settings.as_string())
print("changed", changed)
return changed
"""# if there was no setting before, it is new, we need export
print("changed settings IN OPERATOR", changed_gltf_settings, previous_export_settings)
if previous_export_settings == None:
return True # we can disregard the gltf settings, we need to save either way
else:
export_settings = self.format_settings()
if len(export_settings.keys()) == 0: # first time after we already used the addon, since we already have export settings, but they have not yet been applied
return changed_gltf_settings
print("previous", sorted(json.loads(previous_export_settings.as_string()).items()))
print("current", sorted(export_settings.items()))
changed = sorted(json.loads(previous_export_settings.as_string()).items()) != sorted(export_settings.items())
print("changed FINAL: auto_settings", changed, "gltf_settings", changed_gltf_settings, "combo", changed or changed_gltf_settings)
return changed and changed_gltf_settings"""
def execute(self, context):
# disable change detection while the operator runs
@ -180,13 +212,15 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper):
if self.direct_mode:
self.load_settings(context)
if self.will_save_settings:
print("SAVING SETTINGS")
self.save_settings(context)
changes_per_scene = context.window_manager.auto_export_tracker.changed_objects_per_scene
#determine changed parameters
params_changed = self.did_export_settings_change()
#& do the export
if self.direct_mode: #Do not auto export when applying settings in the menu, do it on save only
#determine changed parameters
params_changed = self.did_export_settings_change()
auto_export(changes_per_scene, params_changed, self)
# cleanup
bpy.app.timers.register(bpy.context.window_manager.auto_export_tracker.enable_change_detection, first_interval=1)

View File

@ -39,7 +39,6 @@ AutoExportGltfPreferenceNames = [
'main_scene_names',
'library_scene_names',
'previous_export_settings',
'filter_glob',
'will_save_settings',
]
@ -50,11 +49,6 @@ class AutoExportGltfAddonPreferences(AddonPreferences):
bl_options = {'PRESET'}
#### these are for the operator
filter_glob: StringProperty(
default='*.glb;*.gltf',
options={'HIDDEN'}
)
will_save_settings: BoolProperty(
name='Remember Export Settings',
description='Store glTF export settings in the Blender project',
@ -166,252 +160,3 @@ class AutoExportGltfAddonPreferences(AddonPreferences):
library_scenes: CollectionProperty(name="library scenes", type=CUSTOM_PG_sceneName)
library_scenes_index: IntProperty(name = "Index for library scenes list", default = 0)
#####
export_format: EnumProperty(
name='Format',
items=(
('GLB', 'glTF Binary (.glb)',
'Exports a single file, with all data packed in binary form. '
'Most efficient and portable, but more difficult to edit later'),
('GLTF_SEPARATE', 'glTF Separate (.gltf + .bin + textures)',
'Exports multiple files, with separate JSON, binary and texture data. '
'Easiest to edit later')
),
description=(
'Output format and embedding options. Binary is most efficient, '
'but JSON (embedded or separate) may be easier to edit later'
),
default='GLB'
)
export_copyright: StringProperty(
name='Copyright',
description='Legal rights and conditions for the model',
default=''
)
export_image_format: EnumProperty(
name='Images',
items=(('AUTO', 'Automatic',
'Save PNGs as PNGs and JPEGs as JPEGs. '
'If neither one, use PNG'),
('JPEG', 'JPEG Format (.jpg)',
'Save images as JPEGs. (Images that need alpha are saved as PNGs though.) '
'Be aware of a possible loss in quality'),
('NONE', 'None',
'Don\'t export images'),
),
description=(
'Output format for images. PNG is lossless and generally preferred, but JPEG might be preferable for web '
'applications due to the smaller file size. Alternatively they can be omitted if they are not needed'
),
default='AUTO'
)
export_texture_dir: StringProperty(
name='Textures',
description='Folder to place texture files in. Relative to the .gltf file',
default='',
)
"""
export_jpeg_quality: IntProperty(
name='JPEG quality',
description='Quality of JPEG export',
default=75,
min=0,
max=100
)
"""
export_keep_originals: BoolProperty(
name='Keep original',
description=('Keep original textures files if possible. '
'WARNING: if you use more than one texture, '
'where pbr standard requires only one, only one texture will be used. '
'This can lead to unexpected results'
),
default=False,
)
export_texcoords: BoolProperty(
name='UVs',
description='Export UVs (texture coordinates) with meshes',
default=True
)
export_normals: BoolProperty(
name='Normals',
description='Export vertex normals with meshes',
default=True
)
export_draco_mesh_compression_enable: BoolProperty(
name='Draco mesh compression',
description='Compress mesh using Draco',
default=False
)
export_draco_mesh_compression_level: IntProperty(
name='Compression level',
description='Compression level (0 = most speed, 6 = most compression, higher values currently not supported)',
default=6,
min=0,
max=10
)
export_draco_position_quantization: IntProperty(
name='Position quantization bits',
description='Quantization bits for position values (0 = no quantization)',
default=14,
min=0,
max=30
)
export_draco_normal_quantization: IntProperty(
name='Normal quantization bits',
description='Quantization bits for normal values (0 = no quantization)',
default=10,
min=0,
max=30
)
export_draco_texcoord_quantization: IntProperty(
name='Texcoord quantization bits',
description='Quantization bits for texture coordinate values (0 = no quantization)',
default=12,
min=0,
max=30
)
export_draco_color_quantization: IntProperty(
name='Color quantization bits',
description='Quantization bits for color values (0 = no quantization)',
default=10,
min=0,
max=30
)
export_draco_generic_quantization: IntProperty(
name='Generic quantization bits',
description='Quantization bits for generic coordinate values like weights or joints (0 = no quantization)',
default=12,
min=0,
max=30
)
export_tangents: BoolProperty(
name='Tangents',
description='Export vertex tangents with meshes',
default=False
)
export_materials: EnumProperty(
name='Materials',
items=(('EXPORT', 'Export',
'Export all materials used by included objects'),
('PLACEHOLDER', 'Placeholder',
'Do not export materials, but write multiple primitive groups per mesh, keeping material slot information'),
('NONE', 'No export',
'Do not export materials, and combine mesh primitive groups, losing material slot information')),
description='Export materials',
default='EXPORT'
)
export_original_specular: BoolProperty(
name='Export original PBR Specular',
description=(
'Export original glTF PBR Specular, instead of Blender Principled Shader Specular'
),
default=False,
)
export_colors: BoolProperty(
name='Vertex Colors',
description='Export vertex colors with meshes',
default=True
)
export_attributes: BoolProperty(
name='Attributes',
description='Export Attributes (when starting with underscore)',
default=False
)
use_mesh_edges: BoolProperty(
name='Loose Edges',
description=(
'Export loose edges as lines, using the material from the first material slot'
),
default=False,
)
use_mesh_vertices: BoolProperty(
name='Loose Points',
description=(
'Export loose points as glTF points, using the material from the first material slot'
),
default=False,
)
export_cameras: BoolProperty(
name='Cameras',
description='Export cameras',
default=True
)
use_selection: BoolProperty(
name='Selected Objects',
description='Export selected objects only',
default=False
)
use_visible: BoolProperty(
name='Visible Objects',
description='Export visible objects only',
default=True
)
use_renderable: BoolProperty(
name='Renderable Objects',
description='Export renderable objects only',
default=False
)
export_apply: BoolProperty(
name='Export Apply Modifiers',
description='Apply modifiers (excluding Armatures) to mesh objects -'
'WARNING: prevents exporting shape keys',
default=True
)
export_yup: BoolProperty(
name='+Y Up',
description='Export using glTF convention, +Y up',
default=True
)
use_visible: BoolProperty(
name='Visible Objects',
description='Export visible objects only',
default=False
)
use_renderable: BoolProperty(
name='Renderable Objects',
description='Export renderable objects only',
default=False
)
export_extras: BoolProperty(
name='Custom Properties',
description='Export custom properties as glTF extras',
default=True
)
export_animations: BoolProperty(
name='Animations',
description='Exports active actions and NLA tracks as glTF animations',
default=False
)

View File

@ -1,3 +1,4 @@
import json
import bpy
from bpy.types import (PropertyGroup)
from bpy.props import (PointerProperty)
@ -47,100 +48,18 @@ class AutoExportTracker(PropertyGroup):
# all our logic is done, mark this as done
print("EXPORT DONE")
@classmethod
def gltf_exporter_handler(cls):
# FOr some reason, the active operator here is always None, so using a workaround
# active_operator = bpy.context.active_operator
print("here", bpy.context.window_manager.was_good_operator)
if bpy.context.window_manager.was_good_operator:
dummy_file_path = "/home/ckaos/projects/bevy/Blender_bevy_components_worklflow/testing/bevy_example/assets/dummy.glb"
import os
if os.path.exists(dummy_file_path):
print("dummy file exists, assuming it worked")
os.unlink(dummy_file_path)
# get the parameters
scene = bpy.context.scene
print("scene", scene["glTF2ExportSettings"])
if "glTF2ExportSettings" in scene:
settings = scene["glTF2ExportSettings"]
print("gltf settings", dict(settings))
# now reset the original gltf_settings
if getattr(cls, "existing_gltf_settings", None) != None:
print("resetting original gltf settings")
scene["glTF2ExportSettings"] = cls.existing_gltf_settings
else:
print("no pre_existing settings")
if "glTF2ExportSettings" in scene:
del scene["glTF2ExportSettings"]
cls.existing_gltf_settings = None
bpy.context.window_manager.was_good_operator = False
return None
else:
try:
bpy.app.timers.unregister(cls.gltf_exporter_handler)
except:pass
return None
return 1
@classmethod
def deps_update_handler(cls, scene, depsgraph):
# print("change detection enabled", cls.change_detection_enabled)
active_operator = bpy.context.active_operator
if active_operator:
#print("Operator", active_operator.bl_label, active_operator.bl_idname)
if active_operator.bl_idname == "EXPORT_SCENE_OT_gltf" and not bpy.context.window_manager.was_good_operator:
try:
bpy.app.timers.unregister(cls.gltf_exporter_handler)
except:pass
bpy.app.timers.register(cls.gltf_exporter_handler, first_interval=3)
# print("Operator", active_operator.bl_label, active_operator.bl_idname)
if active_operator.bl_idname == "EXPORT_SCENE_OT_gltf" :
# we force saving params
active_operator.will_save_settings = True
# we backup any existing gltf export settings, if there where any
scene = bpy.context.scene
if "glTF2ExportSettings" in scene:
existing_setting = scene["glTF2ExportSettings"]
cls.existing_gltf_settings = existing_setting
bpy.context.window_manager.was_good_operator = True
else:
if bpy.context.window_manager.was_good_operator:
bpy.context.window_manager.was_good_operator = False
"""if active_operator.bl_idname == "EXPORT_SCENE_OT_gltf":
scene = bpy.context.scene
if "glTF2ExportSettings" in scene:
existing_setting = scene["glTF2ExportSettings"]
cls.existing_gltf_settings = existing_setting
print("we just executed the correct operator")
if active_operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf":
# we force saving params
active_operator.will_save_settings = True
else:
import os
dummy_file_path = "/home/ckaos/projects/bevy/Blender_bevy_components_worklflow/testing/bevy_example/assets/dummy.glb"
if os.path.exists(dummy_file_path):
print("dummy file exists")
os.unlink(dummy_file_path)
# get the parameters
scene = bpy.context.scene
settings = scene["glTF2ExportSettings"]
print("gltf settings", dict(settings))
# now reset the original gltf_settings
if hasattr(cls, "existing_gltf_settings"):
print("resetting original gltf settings")
scene["glTF2ExportSettings"] = cls.existing_gltf_settings
else:
del scene["glTF2ExportSettings"]"""
if scene.name != "temp_scene":
# print("depsgraph_update_post", scene.name)
@ -154,7 +73,7 @@ class AutoExportTracker(PropertyGroup):
# depsgraph = bpy.context.evaluated_depsgraph_get()
for obj in depsgraph.updates:
print("depsgraph update", obj)
# print("depsgraph update", obj)
if isinstance(obj.id, bpy.types.Object):
# get the actual object
object = bpy.data.objects[obj.id.name]
@ -178,9 +97,9 @@ class AutoExportTracker(PropertyGroup):
else:
cls.changed_objects_per_scene.clear()
depsgraph = bpy.context.evaluated_depsgraph_get()
"""depsgraph = bpy.context.evaluated_depsgraph_get()
for update in depsgraph.updates:
print("update", update)
print("update", update)"""
def disable_change_detection(self,):
self.change_detection_enabled = False

View File

@ -30,6 +30,7 @@ glTF_extension_name = "EXT_auto_export"
# For example, a compression scheme or new image format (with no fallback included)
# would be "required", but physics metadata or app-specific settings could be optional.
extension_is_required = False
from io_scene_gltf2 import (GLTF_PT_export_main, GLTF_PT_export_include)
class ExampleExtensionProperties(bpy.types.PropertyGroup):
enabled: bpy.props.BoolProperty(
@ -239,4 +240,91 @@ def duplicate_object2(object, original_name):
print("OJECT ANIMATION")
new_obj.animation_data.action = object.animation_data.action.copy()
return new_obj
return new_obj
if active_operator:
# print("Operator", active_operator.bl_label, active_operator.bl_idname, "bla", bpy.context.window_manager.gltf_exporter_running)
if active_operator.bl_idname == "EXPORT_SCENE_OT_gltf" : #and not bpy.context.window_manager.gltf_exporter_running:
# we force saving params
active_operator.will_save_settings = True
if active_operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf":
# we force saving params
active_operator.will_save_settings = True
"""
print("matching")
try:
bpy.app.timers.unregister(cls.gltf_exporter_handler)
except:pass
bpy.app.timers.register(cls.gltf_exporter_handler, first_interval=3)
# we backup any existing gltf export settings, if there where any
scene = bpy.context.scene
if "glTF2ExportSettings" in scene:
existing_setting = scene["glTF2ExportSettings"]
cls.existing_gltf_settings = existing_setting
bpy.context.window_manager.gltf_exporter_running = True
else:
if bpy.context.window_manager.gltf_exporter_running:
bpy.context.window_manager.gltf_exporter_running = False"""
"""@classmethod
def gltf_exporter_handler(cls):
# FOr some reason, the active operator here is always None, so using a workaround
# active_operator = bpy.context.active_operator
print("here", bpy.context.window_manager.gltf_exporter_running)
if bpy.context.window_manager.gltf_exporter_running:
try:
dummy_file_path = "/home/ckaos/projects/bevy/Blender_bevy_components_worklflow/testing/bevy_example/assets/dummy.glb"
import os
if os.path.exists(dummy_file_path):
print("dummy file exists, assuming it worked")
os.unlink(dummy_file_path)
# get the parameters
scene = bpy.context.scene
if "glTF2ExportSettings" in scene:
settings = scene["glTF2ExportSettings"]
formatted_settings = dict(settings)
export_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings")
#check if params have changed
bpy.context.window_manager.gltf_settings_changed = sorted(json.loads(export_settings.as_string()).items()) != sorted(formatted_settings.items())
print("gltf NEW settings", formatted_settings, "OLD settings", export_settings, "CHANGED ?", bpy.context.window_manager.gltf_settings_changed)
# now write new settings
export_settings.clear()
export_settings.write(json.dumps(formatted_settings))
# now reset the original gltf_settings
if getattr(cls, "existing_gltf_settings", None) != None:
print("resetting original gltf settings")
scene["glTF2ExportSettings"] = cls.existing_gltf_settings
else:
print("no pre_existing settings")
if "glTF2ExportSettings" in scene:
del scene["glTF2ExportSettings"]
cls.existing_gltf_settings = None
except:pass
bpy.context.window_manager.gltf_exporter_running = False
return None
else:
try:
bpy.app.timers.unregister(cls.gltf_exporter_handler)
except:pass
return None
return 1"""

View File

@ -64,9 +64,19 @@ def test_export_complex(setup_data):
"library_scene_names": ['Library'],
# "export_format":'GLTF_SEPARATE'
}
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.write(json.dumps(export_props))
gltf_settings = {
"export_animations": True
}
# store settings for the auto_export part
stored_auto_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_auto_settings.clear()
stored_auto_settings.write(json.dumps(export_props))
# and store settings for the gltf part
stored_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings")
stored_gltf_settings.clear()
stored_gltf_settings.write(str(gltf_settings))
# move the main cube
bpy.data.objects["Cube"].location = [1, 0, 0]
@ -79,7 +89,6 @@ def test_export_complex(setup_data):
export_scene_settings=True,
export_blueprints=True,
export_legacy_mode=False,
export_animations=True,
export_materials_library=True
)
# blueprint1 => has an instance, got changed, should export

View File

@ -1,5 +1,6 @@
from typing import Set
import bpy
from bpy.types import Operator
from bpy.types import Context, Event, Operator
from bpy_extras.io_utils import ExportHelper
from bpy.props import (BoolProperty,
IntProperty,
@ -65,11 +66,61 @@ class GLTF_PT_auto_export_root(bpy.types.Panel):
layout.active = operator.auto_export
layout.prop(operator, 'will_save_settings')
layout.prop(operator, "export_change_detection")
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_output_folder")
layout.prop(operator, "export_change_detection")
layout.prop(operator, "export_scene_settings")
layout.prop(operator, "export_legacy_mode")
class GLTF_PT_auto_export_scenes(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Scenes"
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.label(text="export scenes")#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
# scene selectors
row = layout.row()
col = row.column(align=True)
@ -80,8 +131,9 @@ class GLTF_PT_auto_export_root(bpy.types.Panel):
rows = 2
# main/level scenes
layout.label(text="main scenes")
layout.prop(context.window_manager, "main_scene", text='')
row = layout.row()
row.label(text="main scenes")
row.prop(context.window_manager, "main_scene", text='')
row = layout.row()
row.template_list("SCENE_UL_GLTF_auto_export", "level scenes", source, "main_scenes", source, "main_scenes_index", rows=rows)
@ -105,8 +157,9 @@ class GLTF_PT_auto_export_root(bpy.types.Panel):
#col.operator("scene_list.list_action", icon='TRIA_DOWN', text="").action = 'DOWN'
# library scenes
layout.label(text="library scenes")
layout.prop(context.window_manager, "library_scene", text='')
row = layout.row()
row.label(text="library scenes")
row.prop(context.window_manager, "library_scene", text='')
row = layout.row()
row.template_list("SCENE_UL_GLTF_auto_export", "library scenes", source, "library_scenes", source, "library_scenes_index", rows=rows)
@ -124,7 +177,7 @@ class GLTF_PT_auto_export_root(bpy.types.Panel):
remove_operator.action = 'REMOVE'
remove_operator.scene_type = 'library'
col.separator()
class GLTF_PT_auto_export_blueprints(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
@ -194,6 +247,37 @@ class GLTF_PT_auto_export_collections_list(bpy.types.Panel):
row = layout.row()
row.label(text=collection.name)
class HelloWorldOperator(bpy.types.Operator):
bl_idname = "export_scenes.wrapper"
bl_label = "Minimal Operator"
def execute(self, context):
print("Hello World")
return {'FINISHED'}
def invoke(self, context: Context, event: Event):
wm = context.window_manager
wm.fileselect_add(self)
return {'RUNNING_MODAL'}
def draw(self, context: Context):
layout = self.layout
op = layout.operator("EXPORT_SCENE_OT_gltf", text='Gltf settings')#'glTF 2.0 (.glb/.gltf)')
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"
#export_scenes.auto_gltf
class GLTF_PT_auto_export_gltf(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
@ -214,18 +298,25 @@ class GLTF_PT_auto_export_gltf(bpy.types.Panel):
sfile = context.space_data
operator = sfile.active_operator
addon_prefs = operator
# we get the addon preferences from the standard gltf exporter & use those :
addon_prefs_gltf = preferences.addons["io_scene_gltf2"].preferences
op = layout.operator("EXPORT_SCENES_OT_wrapper", text='Gltf settings')#'glTF 2.0 (.glb/.gltf)')
#self.layout.operator("EXPORT_SCENE_OT_gltf", text='glTF 2.0 (.glb/.gltf)')
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"
#bpy.ops.export_scene.gltf
for key in addon_prefs.__annotations__.keys():
"""for key in addon_prefs.__annotations__.keys():
if key not in AutoExportGltfPreferenceNames:
layout.prop(operator, key)
layout.prop(operator, key)"""
class SCENE_UL_GLTF_auto_export(bpy.types.UIList):
# The draw_item function is called for each item of the collection that is visible in the list.
@ -260,3 +351,39 @@ class SCENE_UL_GLTF_auto_export(bpy.types.UIList):
layout.label(text="", icon_value=icon)
from io_scene_gltf2 import (ExportGLTF2, GLTF_PT_export_main,ExportGLTF2_Base, GLTF_PT_export_include)
import io_scene_gltf2 as gltf_exporter_original
#import io_scene_gltf2.GLTF_PT_export_data_scene as GLTF_PT_export_data_scene_original
"""
class GLTF_PT_export_data(gltf_exporter_original.GLTF_PT_export_data):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Data"
bl_parent_id = "GLTF_PT_auto_export_gltf"
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"
class GLTF_PT_export_data_scene(gltf_exporter_original.GLTF_PT_export_data_scene):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Scene Graph"
bl_parent_id = "GLTF_PT_export_data"
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(self, context):
return super().draw(context)"""

View File

@ -0,0 +1,267 @@
import bpy
from bpy.types import Operator
from bpy_extras.io_utils import ExportHelper
from bpy.props import (BoolProperty,
IntProperty,
StringProperty,
EnumProperty,
CollectionProperty
)
from ..auto_export import auto_export
from ..auto_export.preferences import (AutoExportGltfAddonPreferences, AutoExportGltfPreferenceNames)
from ..helpers.helpers_scenes import (get_scenes)
from ..helpers.helpers_collections import (get_exportable_collections)
######################################################
## ui logic & co
class GLTF_PT_auto_export_main(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_label = ""
#bl_options = {'HIDE_HEADER'}
bl_category = "Gltf auto_export"
bl_context = "objectmode"
""" @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 = 'VIEW_3D'
bl_region_type = 'UI'
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):
pass
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
sfile = context.space_data
operator = layout.operator("export_scenes.auto_gltf")#bpy.ops.export_scenes.auto_gltf #sfile.active_operator
layout.prop(context.window_manager.operator_properties_last("export_scenes.auto_gltf"), "export_change_detection")
layout.prop(context.window_manager.operator_properties_last("export_scenes.auto_gltf"), "export_output_folder")
#layout.active = operator.auto_export
layout.prop(operator, 'will_save_settings')
layout.prop(operator, "export_change_detection")
layout.prop(operator, "export_output_folder")
layout.prop(operator, "export_scene_settings")
layout.prop(operator, "export_legacy_mode")
# scene selectors
row = layout.row()
col = row.column(align=True)
col.separator()
source = operator
rows = 2
# main/level scenes
layout.label(text="main scenes")
layout.prop(context.window_manager, "main_scene", text='')
row = layout.row()
row.template_list("SCENE_UL_GLTF_auto_export", "level scenes", source, "main_scenes", source, "main_scenes_index", rows=rows)
col = row.column(align=True)
sub_row = col.row()
add_operator = sub_row.operator("scene_list.list_action", icon='ADD', text="")
add_operator.action = 'ADD'
add_operator.scene_type = 'level'
#add_operator.source = operator
sub_row.enabled = context.window_manager.main_scene is not None
sub_row = col.row()
remove_operator = sub_row.operator("scene_list.list_action", icon='REMOVE', text="")
remove_operator.action = 'REMOVE'
remove_operator.scene_type = 'level'
col.separator()
#up_operator = col.operator("scene_list.list_action", icon='TRIA_UP', text="")
#up_operator.action = 'UP'
#col.operator("scene_list.list_action", icon='TRIA_DOWN', text="").action = 'DOWN'
# library scenes
layout.label(text="library scenes")
layout.prop(context.window_manager, "library_scene", text='')
row = layout.row()
row.template_list("SCENE_UL_GLTF_auto_export", "library scenes", source, "library_scenes", source, "library_scenes_index", rows=rows)
col = row.column(align=True)
sub_row = col.row()
add_operator = sub_row.operator("scene_list.list_action", icon='ADD', text="")
add_operator.action = 'ADD'
add_operator.scene_type = 'library'
sub_row.enabled = context.window_manager.library_scene is not None
sub_row = col.row()
remove_operator = sub_row.operator("scene_list.list_action", icon='REMOVE', text="")
remove_operator.action = 'REMOVE'
remove_operator.scene_type = 'library'
col.separator()
class GLTF_PT_auto_export_blueprints(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_label = "Blueprints"
bl_parent_id = "GLTF_PT_auto_export_main"
"""@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="")"""
#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 = layout.operator("export_scenes.auto_gltf")#bpy.ops.export_scenes.auto_gltf #sfile.active_operator
#sfile.active_operator
layout.active = 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")
class GLTF_PT_auto_export_collections_list(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Blueprints: Exported Collections"
bl_parent_id = "GLTF_PT_auto_export_blueprints"
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" #"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
for collection in bpy.context.window_manager.exportedCollections:
row = layout.row()
row.label(text=collection.name)
class GLTF_PT_auto_export_gltf(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_label = "Gltf"
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" #"EXPORT_SCENE_OT_gltf"
"""
def draw(self, context):
preferences = context.preferences
layout = self.layout
layout.label(text="MAKE SURE TO KEEP 'REMEMBER EXPORT SETTINGS' !!")
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"
class SCENE_UL_GLTF_auto_export(bpy.types.UIList):
# The draw_item function is called for each item of the collection that is visible in the list.
# data is the RNA object containing the collection,
# item is the current drawn item of the collection,
# icon is the "computed" icon for the item (as an integer, because some objects like materials or textures
# have custom icons ID, which are not available as enum items).
# active_data is the RNA object containing the active property for the collection (i.e. integer pointing to the
# active item of the collection).
# active_propname is the name of the active property (use 'getattr(active_data, active_propname)').
# index is index of the current item in the collection.
# flt_flag is the result of the filtering process for this item.
# Note: as index and flt_flag are optional arguments, you do not have to use/declare them here if you don't
# need them.
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
ob = data
# draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
if self.layout_type in {'DEFAULT', 'COMPACT'}:
# You should always start your row layout by a label (icon + text), or a non-embossed text field,
# this will also make the row easily selectable in the list! The later also enables ctrl-click rename.
# We use icon_value of label, as our given icon is an integer value, not an enum ID.
# Note "data" names should never be translated!
#if ma:
# layout.prop(ma, "name", text="", emboss=False, icon_value=icon)
#else:
# layout.label(text="", translate=False, icon_value=icon)
layout.label(text=item.name, icon_value=icon)
#layout.prop(item, "name", text="", emboss=False, icon_value=icon)
# 'GRID' layout type should be as compact as possible (typically a single icon!).
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.label(text="", icon_value=icon)

View File

@ -0,0 +1,83 @@
import bpy
from bpy.types import Operator
class SCENES_LIST_OT_actions(Operator):
"""Move items up and down, add and remove"""
bl_idname = "scene_list.list_action"
bl_label = "List Actions"
bl_description = "Move items up and down, add and remove"
bl_options = {'REGISTER'}
action: bpy.props.EnumProperty(
items=(
('UP', "Up", ""),
('DOWN', "Down", ""),
('REMOVE', "Remove", ""),
('ADD', "Add", "")))
scene_type: bpy.props.StringProperty()#TODO: replace with enum
def invoke(self, context, event):
source = context.space_data.active_operator
target_name = "library_scenes"
target_index = "library_scenes_index"
if self.scene_type == "level":
target_name = "main_scenes"
target_index = "main_scenes_index"
target = getattr(source, target_name)
idx = getattr(source, target_index)
current_index = getattr(source, target_index)
try:
item = target[idx]
except IndexError:
pass
else:
if self.action == 'DOWN' and idx < len(target) - 1:
target.move(idx, idx + 1)
setattr(source, target_index, current_index +1 )
info = 'Item "%s" moved to position %d' % (item.name, current_index + 1)
self.report({'INFO'}, info)
elif self.action == 'UP' and idx >= 1:
target.move(idx, idx - 1)
setattr(source, target_index, current_index -1 )
info = 'Item "%s" moved to position %d' % (item.name, current_index + 1)
self.report({'INFO'}, info)
elif self.action == 'REMOVE':
info = 'Item "%s" removed from list' % (target[idx].name)
setattr(source, target_index, current_index -1 )
target.remove(idx)
self.report({'INFO'}, info)
if self.action == 'ADD':
new_scene_name = None
if self.scene_type == "level":
if context.window_manager.main_scene:
new_scene_name = context.window_manager.main_scene.name
else:
if context.window_manager.library_scene:
new_scene_name = context.window_manager.library_scene.name
if new_scene_name:
item = target.add()
item.name = new_scene_name#f"Rule {idx +1}"
if self.scene_type == "level":
context.window_manager.main_scene = None
else:
context.window_manager.library_scene = None
#name = f"Rule {idx +1}"
#target.append({"name": name})
setattr(source, target_index, len(target) - 1)
#source[target_index] = len(target) - 1
info = '"%s" added to list' % (item.name)
self.report({'INFO'}, info)
return {"FINISHED"}