mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-11-22 20:00:53 +00:00
feat(Blenvy): a lot of verious tweaks & fixes
* fixed/ overhauld asset & blueprint scanning * added polling for blueprints (not 100% sure yet) * add-on-prefs => settings * removed the now obsolete auto-export operator & preperences * a lot of other minor changes
This commit is contained in:
parent
008f6b3a88
commit
c00c9908eb
@ -71,7 +71,7 @@ Components:
|
|||||||
General things to solve:
|
General things to solve:
|
||||||
- [x] save settings
|
- [x] save settings
|
||||||
- [x] load settings
|
- [x] load settings
|
||||||
- [ ] add_blueprints_data
|
- [ ] add blueprints data
|
||||||
|
|
||||||
- [x] rename all path stuff using the old naming convention : "blueprints_path_full"
|
- [x] rename all path stuff using the old naming convention : "blueprints_path_full"
|
||||||
- [x] generate the full paths directly when setting them in the UI
|
- [x] generate the full paths directly when setting them in the UI
|
||||||
@ -83,6 +83,5 @@ General issues:
|
|||||||
- "parents" can only be blueprints
|
- "parents" can only be blueprints
|
||||||
- they normally need/have unique export paths (otherwise, user error, perhaps show it ?)
|
- they normally need/have unique export paths (otherwise, user error, perhaps show it ?)
|
||||||
- perhaps a simple hashing of the parent's path would be enought
|
- perhaps a simple hashing of the parent's path would be enought
|
||||||
- [ ] addon-prefs => settings
|
- [x] addon-prefs => settings
|
||||||
|
- [x] generate_gltf_export_preferences => should not use add-on prefs at all ? since we are not overriding gltf settings that way anymore ?
|
||||||
- [ ] generate_gltf_export_preferences => should not use add-on prefs at all ? since we are not overriding gltf settings that way anymore ?
|
|
@ -31,7 +31,6 @@ from .bevy_components.components.ui import (BEVY_COMPONENTS_PT_ComponentsPanel)
|
|||||||
|
|
||||||
# auto export
|
# auto export
|
||||||
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.tracker import AutoExportTracker
|
from .gltf_auto_export.auto_export.tracker import AutoExportTracker
|
||||||
from .gltf_auto_export.settings import AutoExportSettings
|
from .gltf_auto_export.settings import AutoExportSettings
|
||||||
|
|
||||||
@ -114,7 +113,6 @@ classes = [
|
|||||||
GENERIC_MAP_OT_actions,
|
GENERIC_MAP_OT_actions,
|
||||||
|
|
||||||
# gltf auto export
|
# gltf auto export
|
||||||
AutoExportGLTF,
|
|
||||||
AutoExportTracker,
|
AutoExportTracker,
|
||||||
AutoExportSettings,
|
AutoExportSettings,
|
||||||
|
|
||||||
@ -146,6 +144,7 @@ def post_save(scene, depsgraph):
|
|||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
def post_load(file_name):
|
def post_load(file_name):
|
||||||
|
print("POST LOAD")
|
||||||
registry = bpy.context.window_manager.components_registry
|
registry = bpy.context.window_manager.components_registry
|
||||||
if registry is not None:
|
if registry is not None:
|
||||||
registry.load_settings()
|
registry.load_settings()
|
||||||
|
@ -4,12 +4,12 @@ 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, settings):
|
||||||
project_root_path = getattr(addon_prefs, "project_root_path")
|
project_root_path = getattr(settings, "project_root_path")
|
||||||
export_output_folder = getattr(addon_prefs,"export_output_folder")
|
export_output_folder = getattr(settings,"export_output_folder")
|
||||||
levels_path = getattr(addon_prefs,"levels_path")
|
levels_path = getattr(settings,"levels_path")
|
||||||
blueprints_path = getattr(addon_prefs, "blueprints_path")
|
blueprints_path = getattr(settings, "blueprints_path")
|
||||||
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
|
export_gltf_extension = getattr(settings, "export_gltf_extension")
|
||||||
|
|
||||||
relative_blueprints_path = os.path.relpath(blueprints_path, project_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)
|
||||||
@ -61,9 +61,9 @@ def get_userTextures():
|
|||||||
textures.extend([x.image.filepath for x in mat_slot.material.node_tree.nodes if x.type=='TEX_IMAGE'])
|
textures.extend([x.image.filepath for x in mat_slot.material.node_tree.nodes if x.type=='TEX_IMAGE'])
|
||||||
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, settings):
|
||||||
blueprints_path = getattr(addon_prefs, "blueprints_path")
|
blueprints_path = getattr(settings, "blueprints_path")
|
||||||
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
|
export_gltf_extension = getattr(settings, "export_gltf_extension")
|
||||||
assets_list = []
|
assets_list = []
|
||||||
|
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ def get_blueprint_assets_tree(blueprint, blueprints_data, parent, addon_prefs):
|
|||||||
assets_list.append({"name": child_blueprint.name, "path": blueprint_exported_path, "type": "MODEL", "generated": True,"internal":blueprint.local, "parent": blueprint.name})
|
assets_list.append({"name": child_blueprint.name, "path": blueprint_exported_path, "type": "MODEL", "generated": True,"internal":blueprint.local, "parent": blueprint.name})
|
||||||
|
|
||||||
# and add sub stuff
|
# and add sub stuff
|
||||||
sub_assets_lists = get_blueprint_assets_tree(child_blueprint, blueprints_data, parent=child_blueprint.name, addon_prefs=addon_prefs)
|
sub_assets_lists = get_blueprint_assets_tree(child_blueprint, blueprints_data, parent=child_blueprint.name, settings=settings)
|
||||||
assets_list += sub_assets_lists
|
assets_list += sub_assets_lists
|
||||||
|
|
||||||
direct_assets = get_user_assets_as_list(blueprint.collection)
|
direct_assets = get_user_assets_as_list(blueprint.collection)
|
||||||
@ -90,9 +90,9 @@ def get_blueprint_assets_tree(blueprint, blueprints_data, parent, addon_prefs):
|
|||||||
assets_list += direct_assets
|
assets_list += direct_assets
|
||||||
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, settings):
|
||||||
blueprints_path = getattr(addon_prefs, "blueprints_path")
|
blueprints_path = getattr(settings, "blueprints_path")
|
||||||
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
|
export_gltf_extension = getattr(settings, "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)
|
||||||
|
|
||||||
assets_list = get_user_assets_as_list(main_scene)
|
assets_list = get_user_assets_as_list(main_scene)
|
||||||
@ -109,7 +109,7 @@ def get_main_scene_assets_tree(main_scene, blueprints_data, addon_prefs):
|
|||||||
if blueprint_exported_path is not None and not does_asset_exist(assets_list, blueprint_exported_path):
|
if blueprint_exported_path is not None and not does_asset_exist(assets_list, blueprint_exported_path):
|
||||||
assets_list.append({"name": blueprint.name, "path": blueprint_exported_path, "type": "MODEL", "generated": True, "internal":blueprint.local, "parent": None})
|
assets_list.append({"name": blueprint.name, "path": blueprint_exported_path, "type": "MODEL", "generated": True, "internal":blueprint.local, "parent": None})
|
||||||
|
|
||||||
assets_list += get_blueprint_assets_tree(blueprint, blueprints_data, parent=blueprint.name, addon_prefs=addon_prefs)
|
assets_list += get_blueprint_assets_tree(blueprint, blueprints_data, parent=blueprint.name, settings=settings)
|
||||||
|
|
||||||
print("TOTAL ASSETS", assets_list)
|
print("TOTAL ASSETS", assets_list)
|
||||||
return assets_list
|
return assets_list
|
||||||
|
@ -52,7 +52,6 @@ class OT_add_bevy_asset(Operator):
|
|||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
assets = []
|
|
||||||
blueprint_assets = self.target_type == 'BLUEPRINT'
|
blueprint_assets = self.target_type == 'BLUEPRINT'
|
||||||
target = None
|
target = None
|
||||||
if blueprint_assets:
|
if blueprint_assets:
|
||||||
@ -61,7 +60,9 @@ class OT_add_bevy_asset(Operator):
|
|||||||
target = bpy.data.scenes[self.target_name]
|
target = bpy.data.scenes[self.target_name]
|
||||||
assets = get_user_assets(target)
|
assets = get_user_assets(target)
|
||||||
asset = {"name": self.asset_name, "type": self.asset_type, "path": self.asset_path}
|
asset = {"name": self.asset_name, "type": self.asset_type, "path": self.asset_path}
|
||||||
|
print('assets', assets, target)
|
||||||
if not does_asset_exist(target, asset):
|
if not does_asset_exist(target, asset):
|
||||||
|
print("add asset", target, asset)
|
||||||
upsert_asset(target, asset)
|
upsert_asset(target, asset)
|
||||||
|
|
||||||
#assets.append({"name": self.asset_name, "type": self.asset_type, "path": self.asset_path, "internal": False})
|
#assets.append({"name": self.asset_name, "type": self.asset_type, "path": self.asset_path, "internal": False})
|
||||||
@ -202,7 +203,7 @@ class OT_test_bevy_assets(Operator):
|
|||||||
blenvy = context.window_manager.blenvy
|
blenvy = context.window_manager.blenvy
|
||||||
settings = blenvy
|
settings = blenvy
|
||||||
blueprints_registry = context.window_manager.blueprints_registry
|
blueprints_registry = context.window_manager.blueprints_registry
|
||||||
blueprints_registry.add_blueprints_data()
|
blueprints_registry.refresh_blueprints()
|
||||||
blueprints_data = blueprints_registry.blueprints_data
|
blueprints_data = blueprints_registry.blueprints_data
|
||||||
|
|
||||||
for scene in blenvy.main_scenes:
|
for scene in blenvy.main_scenes:
|
||||||
|
@ -74,7 +74,7 @@ class Blenvy_assets(bpy.types.Panel):
|
|||||||
|
|
||||||
asset_registry = context.window_manager.assets_registry
|
asset_registry = context.window_manager.assets_registry
|
||||||
blueprints_registry = context.window_manager.blueprints_registry
|
blueprints_registry = context.window_manager.blueprints_registry
|
||||||
blueprints_registry.add_blueprints_data()
|
#blueprints_registry.refresh_blueprints()
|
||||||
blueprints_data = blueprints_registry.blueprints_data
|
blueprints_data = blueprints_registry.blueprints_data
|
||||||
|
|
||||||
name = "world"
|
name = "world"
|
||||||
@ -85,9 +85,12 @@ class Blenvy_assets(bpy.types.Panel):
|
|||||||
settings = SimpleNamespace(**settings)
|
settings = SimpleNamespace(**settings)
|
||||||
|
|
||||||
if panel:
|
if panel:
|
||||||
for scene in blenvy.main_scenes:
|
#print("dfs")
|
||||||
|
for scene_selector in blenvy.main_scenes:
|
||||||
|
scene = bpy.data.scenes[scene_selector.name]
|
||||||
#get_main_scene_assets_tree(scene, blueprints_data, settings)
|
#get_main_scene_assets_tree(scene, blueprints_data, settings)
|
||||||
user_assets = get_user_assets(scene)
|
user_assets = get_user_assets(scene)
|
||||||
|
#print("user assets", user_assets, scene)
|
||||||
row = panel.row()
|
row = panel.row()
|
||||||
scene_assets_panel = draw_assets(layout=row, name=scene.name, title=f"{scene.name} Assets", asset_registry=asset_registry, user_assets=user_assets, target_type="SCENE", target_name=scene.name)
|
scene_assets_panel = draw_assets(layout=row, name=scene.name, title=f"{scene.name} Assets", asset_registry=asset_registry, user_assets=user_assets, target_type="SCENE", target_name=scene.name)
|
||||||
"""if scene.name in blueprints_data.blueprint_instances_per_main_scene:
|
"""if scene.name in blueprints_data.blueprint_instances_per_main_scene:
|
||||||
|
@ -206,14 +206,14 @@ class OT_select_object(Operator):
|
|||||||
bl_label = "Select object"
|
bl_label = "Select object"
|
||||||
bl_options = {"UNDO"}
|
bl_options = {"UNDO"}
|
||||||
|
|
||||||
object_name: StringProperty(
|
target_name: StringProperty(
|
||||||
name="object_name",
|
name="target name",
|
||||||
description="object to select's name ",
|
description="target to select's name ",
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if self.object_name:
|
if self.target_name:
|
||||||
object = bpy.data.objects[self.object_name]
|
object = bpy.data.objects[self.target_name]
|
||||||
scenes_of_object = list(object.users_scene)
|
scenes_of_object = list(object.users_scene)
|
||||||
if len(scenes_of_object) > 0:
|
if len(scenes_of_object) > 0:
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
@ -3,6 +3,8 @@ import bpy
|
|||||||
from bpy_types import (UIList)
|
from bpy_types import (UIList)
|
||||||
from bpy.props import (StringProperty)
|
from bpy.props import (StringProperty)
|
||||||
|
|
||||||
|
from ..utils import get_selection_type
|
||||||
|
|
||||||
from ..components.operators import OT_rename_component, RemoveComponentFromAllItemsOperator, RemoveComponentOperator
|
from ..components.operators import OT_rename_component, RemoveComponentFromAllItemsOperator, RemoveComponentOperator
|
||||||
from .operators import(
|
from .operators import(
|
||||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
|
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
|
||||||
@ -78,7 +80,7 @@ class BEVY_COMPONENTS_PT_AdvancedToolsPanel(bpy.types.Panel):
|
|||||||
col.label(text=item)
|
col.label(text=item)
|
||||||
|
|
||||||
|
|
||||||
def draw_invalid_or_unregistered(self, layout, status, component_name, object):
|
def draw_invalid_or_unregistered(self, layout, status, component_name, target):
|
||||||
available_components = bpy.context.window_manager.components_list
|
available_components = bpy.context.window_manager.components_list
|
||||||
registry = bpy.context.window_manager.components_registry
|
registry = bpy.context.window_manager.components_registry
|
||||||
registry_has_type_infos = registry.has_type_infos()
|
registry_has_type_infos = registry.has_type_infos()
|
||||||
@ -89,8 +91,8 @@ class BEVY_COMPONENTS_PT_AdvancedToolsPanel(bpy.types.Panel):
|
|||||||
col.label(text=component_name)
|
col.label(text=component_name)
|
||||||
|
|
||||||
col = row.column()
|
col = row.column()
|
||||||
operator = col.operator(OT_select_object.bl_idname, text=object.name)
|
operator = col.operator("object.select", text=target.name)
|
||||||
operator.object_name = object.name
|
operator.target_name = target.name
|
||||||
|
|
||||||
col = row.column()
|
col = row.column()
|
||||||
col.label(text=status)
|
col.label(text=status)
|
||||||
@ -99,22 +101,24 @@ class BEVY_COMPONENTS_PT_AdvancedToolsPanel(bpy.types.Panel):
|
|||||||
col.prop(available_components, "list", text="")
|
col.prop(available_components, "list", text="")
|
||||||
|
|
||||||
col = row.column()
|
col = row.column()
|
||||||
operator = col.operator(OT_rename_component.bl_idname, text="", icon="SHADERFX") #rename
|
operator = col.operator("object.rename_bevy_component", text="", icon="SHADERFX") #rename
|
||||||
new_name = registry.type_infos[available_components.list]['long_name'] if available_components.list in registry.type_infos else ""
|
new_name = registry.type_infos[available_components.list]['long_name'] if available_components.list in registry.type_infos else ""
|
||||||
operator.original_name = component_name
|
operator.original_name = component_name
|
||||||
operator.target_objects = json.dumps([object.name])
|
operator.target_objects = json.dumps([target.name])
|
||||||
operator.new_name = new_name
|
operator.new_name = new_name
|
||||||
col.enabled = registry_has_type_infos and component_name != "" and component_name != new_name
|
col.enabled = registry_has_type_infos and component_name != "" and component_name != new_name
|
||||||
|
|
||||||
|
|
||||||
col = row.column()
|
col = row.column()
|
||||||
operator = col.operator(RemoveComponentOperator.bl_idname, text="", icon="X")
|
operator = col.operator("object.remove_bevy_component", text="", icon="X")
|
||||||
operator.object_name = object.name
|
operator.item_name = target.name
|
||||||
operator.component_name = component_name
|
operator.component_name = component_name
|
||||||
|
operator.item_type = get_selection_type(target)
|
||||||
|
|
||||||
|
|
||||||
col = row.column()
|
col = row.column()
|
||||||
col = row.column()
|
col = row.column()
|
||||||
operator = col.operator(OT_select_component_name_to_replace.bl_idname, text="", icon="EYEDROPPER") #text="select for rename",
|
operator = col.operator("object.select_component_name_to_replace", text="", icon="EYEDROPPER") #text="select for rename",
|
||||||
operator.component_name = component_name
|
operator.component_name = component_name
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
|
@ -25,12 +25,12 @@ def inject_export_path_into_internal_blueprints(internal_blueprints, blueprints_
|
|||||||
blueprint_exported_path = os.path.join(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, settings):
|
||||||
project_root_path = getattr(addon_prefs, "project_root_path")
|
project_root_path = getattr(settings, "project_root_path")
|
||||||
assets_path = getattr(addon_prefs,"assets_path")
|
assets_path = getattr(settings,"assets_path")
|
||||||
levels_path = getattr(addon_prefs,"levels_path")
|
levels_path = getattr(settings,"levels_path")
|
||||||
blueprints_path = getattr(addon_prefs, "blueprints_path")
|
blueprints_path = getattr(settings, "blueprints_path")
|
||||||
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
|
export_gltf_extension = getattr(settings, "export_gltf_extension")
|
||||||
|
|
||||||
# print("injecting assets/blueprints data into scene")
|
# print("injecting assets/blueprints data into scene")
|
||||||
assets_list_name = f"assets_list_{scene.name}_components"
|
assets_list_name = f"assets_list_{scene.name}_components"
|
||||||
|
@ -8,9 +8,20 @@ from bpy_types import (PropertyGroup)
|
|||||||
from bpy.props import (StringProperty, BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, EnumProperty, PointerProperty, CollectionProperty)
|
from bpy.props import (StringProperty, BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, EnumProperty, PointerProperty, CollectionProperty)
|
||||||
|
|
||||||
from ..settings import load_settings
|
from ..settings import load_settings
|
||||||
from ..gltf_auto_export.helpers.helpers_scenes import get_scenes
|
from ..core.scene_helpers import get_main_and_library_scenes
|
||||||
from .blueprints_scan import blueprints_scan
|
from .blueprints_scan import blueprints_scan
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def refresh_blueprints():
|
||||||
|
try:
|
||||||
|
blueprints_registry = bpy.context.window_manager.blueprints_registry
|
||||||
|
blueprints_registry.refresh_blueprints()
|
||||||
|
#print('refresh blueprints')
|
||||||
|
except:pass
|
||||||
|
|
||||||
|
return 3
|
||||||
|
|
||||||
# this is where we store the information for all available Blueprints
|
# this is where we store the information for all available Blueprints
|
||||||
class BlueprintsRegistry(PropertyGroup):
|
class BlueprintsRegistry(PropertyGroup):
|
||||||
blueprints_data = {}
|
blueprints_data = {}
|
||||||
@ -40,17 +51,25 @@ class BlueprintsRegistry(PropertyGroup):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def register(cls):
|
def register(cls):
|
||||||
bpy.types.WindowManager.blueprints_registry = PointerProperty(type=BlueprintsRegistry)
|
bpy.types.WindowManager.blueprints_registry = PointerProperty(type=BlueprintsRegistry)
|
||||||
|
bpy.app.timers.register(refresh_blueprints)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def unregister(cls):
|
def unregister(cls):
|
||||||
|
try:
|
||||||
|
bpy.app.timers.unregister(refresh_blueprints)
|
||||||
|
except: pass
|
||||||
|
|
||||||
del bpy.types.WindowManager.blueprints_registry
|
del bpy.types.WindowManager.blueprints_registry
|
||||||
|
|
||||||
|
|
||||||
def add_blueprint(self, blueprint):
|
def add_blueprint(self, blueprint):
|
||||||
self.blueprints_list.append(blueprint)
|
self.blueprints_list.append(blueprint)
|
||||||
|
|
||||||
def add_blueprints_data(self):
|
def refresh_blueprints(self):
|
||||||
blenvy = bpy.context.window_manager.blenvy
|
blenvy = bpy.context.window_manager.blenvy
|
||||||
addon_prefs = blenvy
|
settings = blenvy
|
||||||
[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_main_and_library_scenes(settings)
|
||||||
blueprints_data = blueprints_scan(level_scenes, library_scenes, addon_prefs)
|
blueprints_data = blueprints_scan(level_scenes, library_scenes, settings)
|
||||||
self.blueprints_data = blueprints_data
|
self.blueprints_data = blueprints_data
|
||||||
|
"""for blueprint in blueprints_data.blueprints:
|
||||||
|
self.add_blueprint(blueprint)"""
|
||||||
|
@ -7,8 +7,8 @@ from .blueprint import Blueprint
|
|||||||
# - marked as asset
|
# - marked as asset
|
||||||
# - with the "auto_export" flag
|
# - with the "auto_export" flag
|
||||||
# https://blender.stackexchange.com/questions/167878/how-to-get-all-collections-of-the-current-scene
|
# https://blender.stackexchange.com/questions/167878/how-to-get-all-collections-of-the-current-scene
|
||||||
def blueprints_scan(main_scenes, library_scenes, addon_prefs):
|
def blueprints_scan(main_scenes, library_scenes, settings):
|
||||||
export_marked_assets = getattr(addon_prefs.auto_export,"export_marked_assets")
|
export_marked_assets = getattr(settings.auto_export, "export_marked_assets")
|
||||||
|
|
||||||
blueprints = {}
|
blueprints = {}
|
||||||
blueprints_from_objects = {}
|
blueprints_from_objects = {}
|
||||||
|
@ -14,15 +14,18 @@ class GLTF_PT_auto_export_blueprints_list(bpy.types.Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.window_manager.blenvy.mode == 'BLUEPRINTS'
|
return context.window_manager.blenvy.mode == 'BLUEPRINTS' if 'blenvy' in context.window_manager else False
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.use_property_split = True
|
layout.use_property_split = True
|
||||||
layout.use_property_decorate = False # No animation.
|
layout.use_property_decorate = False # No animation.
|
||||||
asset_registry = context.window_manager.assets_registry
|
asset_registry = context.window_manager.assets_registry
|
||||||
|
blueprint_registry = context.window_manager.blueprints_registry
|
||||||
|
|
||||||
for blueprint in context.window_manager.blueprints_registry.blueprints_list:
|
blueprint_registry.refresh_blueprints()
|
||||||
|
|
||||||
|
for blueprint in blueprint_registry.blueprints_data.blueprints:
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.label(icon="RIGHTARROW")
|
row.label(icon="RIGHTARROW")
|
||||||
row.label(text=blueprint.name)
|
row.label(text=blueprint.name)
|
||||||
|
@ -132,6 +132,7 @@ class BlenvyManager(PropertyGroup):
|
|||||||
del bpy.types.WindowManager.library_scene
|
del bpy.types.WindowManager.library_scene
|
||||||
|
|
||||||
def load_settings(self):
|
def load_settings(self):
|
||||||
|
print("LOAD SETTINGS")
|
||||||
settings = load_settings(self.settings_save_path)
|
settings = load_settings(self.settings_save_path)
|
||||||
if settings is not None:
|
if settings is not None:
|
||||||
if "mode" in settings:
|
if "mode" in settings:
|
||||||
|
@ -8,7 +8,7 @@ class OT_switch_bevy_tooling(Operator):
|
|||||||
"""Switch bevy tooling"""
|
"""Switch bevy tooling"""
|
||||||
bl_idname = "bevy.tooling_switch"
|
bl_idname = "bevy.tooling_switch"
|
||||||
bl_label = "Switch bevy tooling"
|
bl_label = "Switch bevy tooling"
|
||||||
bl_options = {"UNDO"}
|
#bl_options = {}
|
||||||
|
|
||||||
|
|
||||||
tool: EnumProperty(
|
tool: EnumProperty(
|
||||||
|
@ -7,6 +7,24 @@ class SceneSelector(bpy.types.PropertyGroup):
|
|||||||
display: bpy.props.BoolProperty() # type: ignore
|
display: bpy.props.BoolProperty() # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
# convenience utility to get lists of scenes
|
||||||
|
def get_main_and_library_scenes(settings):
|
||||||
|
level_scene_names= getattr(settings, "main_scene_names", []) #list(map(lambda scene: scene.name, getattr(settings,"main_scenes")))
|
||||||
|
library_scene_names = getattr(settings, "library_scene_names", []) #list(map(lambda scene: scene.name, getattr(settings,"library_scenes")))
|
||||||
|
|
||||||
|
level_scene_names= list(map(lambda scene: scene.name, getattr(settings,"main_scenes")))
|
||||||
|
library_scene_names = list(map(lambda scene: scene.name, getattr(settings,"library_scenes")))
|
||||||
|
|
||||||
|
print("level_scene_names", level_scene_names)
|
||||||
|
level_scene_names = list(filter(lambda name: name in bpy.data.scenes, level_scene_names))
|
||||||
|
library_scene_names = list(filter(lambda name: name in bpy.data.scenes, library_scene_names))
|
||||||
|
|
||||||
|
level_scenes = list(map(lambda name: bpy.data.scenes[name], level_scene_names))
|
||||||
|
library_scenes = list(map(lambda name: bpy.data.scenes[name], library_scene_names))
|
||||||
|
|
||||||
|
return [level_scene_names, level_scenes, library_scene_names, library_scenes]
|
||||||
|
|
||||||
|
|
||||||
def add_scene_property(scene, scene_component_name, property_data, limit_to=None):
|
def add_scene_property(scene, scene_component_name, property_data, limit_to=None):
|
||||||
root_collection = scene.collection
|
root_collection = scene.collection
|
||||||
scene_property = None
|
scene_property = None
|
||||||
|
@ -31,12 +31,12 @@ def gltf_post_export_callback(data):
|
|||||||
scene = bpy.context.scene
|
scene = bpy.context.scene
|
||||||
if "glTF2ExportSettings" in scene:
|
if "glTF2ExportSettings" in scene:
|
||||||
settings = scene["glTF2ExportSettings"]
|
settings = scene["glTF2ExportSettings"]
|
||||||
export_settings = bpy.data.texts[".blenvy_gltf_settings"] if ".blenvy_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".blenvy_gltf_settings")
|
gltf_export_settings = bpy.data.texts[".blenvy_gltf_settings"] if ".blenvy_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".blenvy_gltf_settings")
|
||||||
# now write new settings
|
# now write new settings
|
||||||
export_settings.clear()
|
gltf_export_settings.clear()
|
||||||
|
|
||||||
current_gltf_settings = generate_complete_preferences_dict_gltf(dict(settings))
|
current_gltf_settings = generate_complete_preferences_dict_gltf(dict(settings))
|
||||||
export_settings.write(json.dumps(current_gltf_settings))
|
gltf_export_settings.write(json.dumps(current_gltf_settings))
|
||||||
# now reset the original gltf_settings
|
# now reset the original gltf_settings
|
||||||
if gltf_settings_backup != "":
|
if gltf_settings_backup != "":
|
||||||
scene["glTF2ExportSettings"] = json.loads(gltf_settings_backup)
|
scene["glTF2ExportSettings"] = json.loads(gltf_settings_backup)
|
||||||
|
@ -3,7 +3,7 @@ import os
|
|||||||
import bpy
|
import bpy
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from ..helpers.helpers_scenes import (get_scenes, )
|
from ...core.scene_helpers import get_main_and_library_scenes
|
||||||
|
|
||||||
from .get_blueprints_to_export import get_blueprints_to_export
|
from .get_blueprints_to_export import get_blueprints_to_export
|
||||||
from .get_levels_to_export import get_levels_to_export
|
from .get_levels_to_export import get_levels_to_export
|
||||||
@ -19,7 +19,7 @@ from ...blueprints.blueprint_helpers import inject_export_path_into_internal_blu
|
|||||||
|
|
||||||
|
|
||||||
"""this is the main 'central' function for all auto export """
|
"""this is the main 'central' function for all auto export """
|
||||||
def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
|
def auto_export(changes_per_scene, changed_export_parameters, settings):
|
||||||
# have the export parameters (not auto export, just gltf export) have changed: if yes (for example switch from glb to gltf, compression or not, animations or not etc), we need to re-export everything
|
# have the export parameters (not auto export, just gltf export) have changed: if yes (for example switch from glb to gltf, compression or not, animations or not etc), we need to re-export everything
|
||||||
print ("changed_export_parameters", changed_export_parameters)
|
print ("changed_export_parameters", changed_export_parameters)
|
||||||
try:
|
try:
|
||||||
@ -27,41 +27,34 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
|
|||||||
file_path = bpy.data.filepath
|
file_path = bpy.data.filepath
|
||||||
# Get the folder
|
# Get the folder
|
||||||
blend_file_path = os.path.dirname(file_path)
|
blend_file_path = os.path.dirname(file_path)
|
||||||
# get the preferences for our addon
|
|
||||||
project_root_path = getattr(addon_prefs, "project_root_path")
|
|
||||||
assets_path = getattr(addon_prefs,"assets_path")
|
|
||||||
|
|
||||||
#should we use change detection or not
|
#should we use change detection or not
|
||||||
change_detection = getattr(addon_prefs.auto_export, "change_detection")
|
change_detection = getattr(settings.auto_export, "change_detection")
|
||||||
export_scene_settings = getattr(addon_prefs.auto_export,"export_scene_settings")
|
export_scene_settings = getattr(settings.auto_export,"export_scene_settings")
|
||||||
do_export_blueprints = getattr(addon_prefs.auto_export,"export_blueprints")
|
do_export_blueprints = getattr(settings.auto_export,"export_blueprints")
|
||||||
export_materials_library = getattr(addon_prefs.auto_export,"export_materials_library")
|
export_materials_library = getattr(settings.auto_export,"export_materials_library")
|
||||||
|
|
||||||
# standard gltf export settings are stored differently
|
# standard gltf export settings are stored differently
|
||||||
standard_gltf_exporter_settings = get_standard_exporter_settings()
|
standard_gltf_exporter_settings = get_standard_exporter_settings()
|
||||||
gltf_extension = standard_gltf_exporter_settings.get("export_format", 'GLB')
|
gltf_extension = standard_gltf_exporter_settings.get("export_format", 'GLB')
|
||||||
gltf_extension = '.glb' if gltf_extension == 'GLB' else '.gltf'
|
gltf_extension = '.glb' if gltf_extension == 'GLB' else '.gltf'
|
||||||
|
settings.export_gltf_extension = gltf_extension
|
||||||
|
|
||||||
# generate the actual complete output paths
|
[main_scene_names, level_scenes, library_scene_names, library_scenes] = get_main_and_library_scenes(settings)
|
||||||
"""addon_prefs.assets_path_full = os.path.join(blend_file_path, project_root_path, assets_path)
|
|
||||||
addon_prefs.blueprints_path_full = os.path.join(addon_prefs.assets_path_full, getattr(addon_prefs,"blueprints_path"))
|
|
||||||
addon_prefs.levels_path_full = os.path.join(addon_prefs.assets_path_full, getattr(addon_prefs,"levels_path"))
|
|
||||||
addon_prefs.materials_path_full = os.path.join(addon_prefs.assets_path_full, getattr(addon_prefs,"materials_path"))"""
|
|
||||||
addon_prefs.export_gltf_extension = gltf_extension
|
|
||||||
|
|
||||||
[main_scene_names, level_scenes, library_scene_names, library_scenes] = get_scenes(addon_prefs)
|
bpy.context.window_manager.blueprints_registry.refresh_blueprints()
|
||||||
|
blueprints_data = bpy.context.window_manager.blueprints_registry.blueprints_data
|
||||||
blueprints_data = blueprints_scan(level_scenes, library_scenes, addon_prefs)
|
|
||||||
blueprints_per_scene = blueprints_data.blueprints_per_scenes
|
blueprints_per_scene = blueprints_data.blueprints_per_scenes
|
||||||
internal_blueprints = [blueprint.name for blueprint in blueprints_data.internal_blueprints]
|
internal_blueprints = [blueprint.name for blueprint in blueprints_data.internal_blueprints]
|
||||||
external_blueprints = [blueprint.name for blueprint in blueprints_data.external_blueprints]
|
external_blueprints = [blueprint.name for blueprint in blueprints_data.external_blueprints]
|
||||||
|
|
||||||
# we inject the blueprints export path
|
# we inject the blueprints export path
|
||||||
blueprints_path = getattr(addon_prefs,"blueprints_path")
|
blueprints_path = getattr(settings,"blueprints_path")
|
||||||
inject_export_path_into_internal_blueprints(internal_blueprints=blueprints_data.internal_blueprints, blueprints_path=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.refresh_blueprints()
|
||||||
|
print("YO YO")
|
||||||
|
|
||||||
if export_scene_settings:
|
if export_scene_settings:
|
||||||
# inject/ update scene components
|
# inject/ update scene components
|
||||||
@ -75,15 +68,15 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
|
|||||||
if do_export_blueprints:
|
if do_export_blueprints:
|
||||||
print("EXPORTING")
|
print("EXPORTING")
|
||||||
# get blueprints/collections infos
|
# get blueprints/collections infos
|
||||||
(blueprints_to_export) = get_blueprints_to_export(changes_per_scene, changed_export_parameters, blueprints_data, addon_prefs)
|
(blueprints_to_export) = get_blueprints_to_export(changes_per_scene, changed_export_parameters, blueprints_data, settings)
|
||||||
|
|
||||||
# get level/main scenes infos
|
# get level/main scenes infos
|
||||||
(main_scenes_to_export) = get_levels_to_export(changes_per_scene, changed_export_parameters, blueprints_data, addon_prefs)
|
(main_scenes_to_export) = get_levels_to_export(changes_per_scene, changed_export_parameters, blueprints_data, settings)
|
||||||
|
|
||||||
# since materials export adds components we need to call this before blueprints are exported
|
# since materials export adds components we need to call this before blueprints are exported
|
||||||
# export materials & inject materials components into relevant objects
|
# export materials & inject materials components into relevant objects
|
||||||
if export_materials_library:
|
if export_materials_library:
|
||||||
export_materials(blueprints_data.blueprint_names, library_scenes, addon_prefs)
|
export_materials(blueprints_data.blueprint_names, library_scenes, settings)
|
||||||
|
|
||||||
# update the list of tracked exports
|
# update the list of tracked exports
|
||||||
exports_total = len(blueprints_to_export) + len(main_scenes_to_export) + (1 if export_materials_library else 0)
|
exports_total = len(blueprints_to_export) + len(main_scenes_to_export) + (1 if export_materials_library else 0)
|
||||||
@ -115,13 +108,13 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
|
|||||||
print("export MAIN scenes")
|
print("export MAIN scenes")
|
||||||
for scene_name in main_scenes_to_export:
|
for scene_name in main_scenes_to_export:
|
||||||
print(" exporting scene:", scene_name)
|
print(" exporting scene:", scene_name)
|
||||||
export_main_scene(bpy.data.scenes[scene_name], blend_file_path, addon_prefs, blueprints_data)
|
export_main_scene(bpy.data.scenes[scene_name], blend_file_path, settings, blueprints_data)
|
||||||
|
|
||||||
# now deal with blueprints/collections
|
# now deal with blueprints/collections
|
||||||
do_export_library_scene = not change_detection or changed_export_parameters or len(blueprints_to_export) > 0
|
do_export_library_scene = not change_detection or changed_export_parameters or len(blueprints_to_export) > 0
|
||||||
if do_export_library_scene:
|
if do_export_library_scene:
|
||||||
print("export LIBRARY")
|
print("export LIBRARY")
|
||||||
export_blueprints(blueprints_to_export, addon_prefs, blueprints_data)
|
export_blueprints(blueprints_to_export, settings, blueprints_data)
|
||||||
|
|
||||||
# reset current scene from backup
|
# reset current scene from backup
|
||||||
bpy.context.window.scene = old_current_scene
|
bpy.context.window.scene = old_current_scene
|
||||||
@ -134,7 +127,7 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
for scene_name in main_scene_names:
|
for scene_name in main_scene_names:
|
||||||
export_main_scene(bpy.data.scenes[scene_name], blend_file_path, addon_prefs, [])
|
export_main_scene(bpy.data.scenes[scene_name], blend_file_path, settings, [])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -148,7 +141,7 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
|
|||||||
|
|
||||||
finally:
|
finally:
|
||||||
# FIXME: error handling ? also redundant
|
# FIXME: error handling ? also redundant
|
||||||
[main_scene_names, main_scenes, library_scene_names, library_scenes] = get_scenes(addon_prefs)
|
[main_scene_names, main_scenes, library_scene_names, library_scenes] = get_main_and_library_scenes(settings)
|
||||||
|
|
||||||
if export_scene_settings:
|
if export_scene_settings:
|
||||||
# inject/ update scene components
|
# inject/ update scene components
|
||||||
|
@ -7,32 +7,32 @@ from .export_gltf import (generate_gltf_export_preferences)
|
|||||||
from ..helpers.helpers_scenes import clear_hollow_scene, copy_hollowed_collection_into
|
from ..helpers.helpers_scenes import clear_hollow_scene, copy_hollowed_collection_into
|
||||||
|
|
||||||
|
|
||||||
def export_blueprints(blueprints, addon_prefs, blueprints_data):
|
def export_blueprints(blueprints, settings, blueprints_data):
|
||||||
blueprints_path_full = getattr(addon_prefs, "blueprints_path_full")
|
blueprints_path_full = getattr(settings, "blueprints_path_full")
|
||||||
gltf_export_preferences = generate_gltf_export_preferences(addon_prefs)
|
gltf_export_preferences = generate_gltf_export_preferences(settings)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# save current active collection
|
# save current active collection
|
||||||
active_collection = bpy.context.view_layer.active_layer_collection
|
active_collection = bpy.context.view_layer.active_layer_collection
|
||||||
export_materials_library = getattr(addon_prefs.auto_export, "export_materials_library")
|
export_materials_library = getattr(settings.auto_export, "export_materials_library")
|
||||||
|
|
||||||
for blueprint in blueprints:
|
for blueprint in blueprints:
|
||||||
print("exporting collection", blueprint.name)
|
print("exporting collection", blueprint.name)
|
||||||
gltf_output_path = os.path.join(blueprints_path_full, blueprint.name)
|
gltf_output_path = os.path.join(blueprints_path_full, blueprint.name)
|
||||||
export_settings = { **gltf_export_preferences, 'use_active_scene': True, 'use_active_collection': True, 'use_active_collection_with_nested':True}
|
gltf_export_settings = { **gltf_export_preferences, 'use_active_scene': True, 'use_active_collection': True, 'use_active_collection_with_nested':True}
|
||||||
|
|
||||||
# if we are using the material library option, do not export materials, use placeholder instead
|
# if we are using the material library option, do not export materials, use placeholder instead
|
||||||
if export_materials_library:
|
if export_materials_library:
|
||||||
export_settings['export_materials'] = 'PLACEHOLDER'
|
gltf_export_settings['export_materials'] = 'PLACEHOLDER'
|
||||||
|
|
||||||
collection = bpy.data.collections[blueprint.name]
|
collection = bpy.data.collections[blueprint.name]
|
||||||
# do the actual export
|
# do the actual export
|
||||||
generate_and_export(
|
generate_and_export(
|
||||||
addon_prefs,
|
settings,
|
||||||
temp_scene_name=TEMPSCENE_PREFIX+collection.name,
|
temp_scene_name=TEMPSCENE_PREFIX+collection.name,
|
||||||
export_settings=export_settings,
|
gltf_export_settings=gltf_export_settings,
|
||||||
gltf_output_path=gltf_output_path,
|
gltf_output_path=gltf_output_path,
|
||||||
tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(collection, temp_collection, blueprints_data=blueprints_data, addon_prefs=addon_prefs),
|
tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(collection, temp_collection, blueprints_data=blueprints_data, settings=settings),
|
||||||
tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=collection, temp_scene=temp_scene, **params)
|
tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=collection, temp_scene=temp_scene, **params)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -3,9 +3,8 @@ import os
|
|||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
from .get_standard_exporter_settings import get_standard_exporter_settings
|
from .get_standard_exporter_settings import get_standard_exporter_settings
|
||||||
from .preferences import (AutoExportGltfPreferenceNames)
|
|
||||||
|
|
||||||
def generate_gltf_export_preferences(addon_prefs):
|
def generate_gltf_export_preferences(settings):
|
||||||
# default values
|
# default values
|
||||||
gltf_export_preferences = dict(
|
gltf_export_preferences = dict(
|
||||||
# export_format= 'GLB', #'GLB', 'GLTF_SEPARATE', 'GLTF_EMBEDDED'
|
# export_format= 'GLB', #'GLB', 'GLTF_SEPARATE', 'GLTF_EMBEDDED'
|
||||||
@ -43,10 +42,10 @@ def generate_gltf_export_preferences(addon_prefs):
|
|||||||
#export_optimize_animation_size=False
|
#export_optimize_animation_size=False
|
||||||
)
|
)
|
||||||
|
|
||||||
for key in addon_prefs.__annotations__.keys():
|
"""for key in settings.__annotations__.keys():
|
||||||
if str(key) not in AutoExportGltfPreferenceNames:
|
if str(key) not in AutoExportGltfPreferenceNames:
|
||||||
#print("overriding setting", key, "value", getattr(addon_prefs,key))
|
#print("overriding setting", key, "value", getattr(settings,key))
|
||||||
pass#gltf_export_preferences[key] = getattr(addon_prefs, key)
|
pass#gltf_export_preferences[key] = getattr(settings, key)"""
|
||||||
|
|
||||||
|
|
||||||
standard_gltf_exporter_settings = get_standard_exporter_settings()
|
standard_gltf_exporter_settings = get_standard_exporter_settings()
|
||||||
@ -70,8 +69,8 @@ def generate_gltf_export_preferences(addon_prefs):
|
|||||||
|
|
||||||
|
|
||||||
#https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf
|
#https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf
|
||||||
def export_gltf (path, export_settings):
|
def export_gltf (path, gltf_export_settings):
|
||||||
settings = {**export_settings, "filepath": path}
|
settings = {**gltf_export_settings, "filepath": path}
|
||||||
# print("export settings",settings)
|
# print("export settings",settings)
|
||||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||||
bpy.ops.export_scene.gltf(**settings)
|
bpy.ops.export_scene.gltf(**settings)
|
||||||
|
@ -9,15 +9,15 @@ from ..modules.bevy_dynamic import is_object_dynamic, is_object_static
|
|||||||
from ..helpers.helpers_scenes import clear_hollow_scene, copy_hollowed_collection_into
|
from ..helpers.helpers_scenes import clear_hollow_scene, copy_hollowed_collection_into
|
||||||
from ...blueprints.blueprint_helpers import inject_blueprints_list_into_main_scene, remove_blueprints_list_from_main_scene
|
from ...blueprints.blueprint_helpers import inject_blueprints_list_into_main_scene, remove_blueprints_list_from_main_scene
|
||||||
|
|
||||||
def export_main_scene(scene, blend_file_path, addon_prefs, blueprints_data):
|
def export_main_scene(scene, blend_file_path, settings, blueprints_data):
|
||||||
gltf_export_preferences = generate_gltf_export_preferences(addon_prefs)
|
gltf_export_preferences = generate_gltf_export_preferences(settings)
|
||||||
assets_path_full = getattr(addon_prefs,"assets_path_full")
|
assets_path_full = getattr(settings,"assets_path_full")
|
||||||
levels_path_full = getattr(addon_prefs,"levels_path_full")
|
levels_path_full = getattr(settings,"levels_path_full")
|
||||||
|
|
||||||
export_blueprints = getattr(addon_prefs.auto_export,"export_blueprints")
|
export_blueprints = getattr(settings.auto_export,"export_blueprints")
|
||||||
export_separate_dynamic_and_static_objects = getattr(addon_prefs.auto_export, "export_separate_dynamic_and_static_objects")
|
export_separate_dynamic_and_static_objects = getattr(settings.auto_export, "export_separate_dynamic_and_static_objects")
|
||||||
|
|
||||||
export_settings = { **gltf_export_preferences,
|
gltf_export_settings = { **gltf_export_preferences,
|
||||||
'use_active_scene': True,
|
'use_active_scene': True,
|
||||||
'use_active_collection':True,
|
'use_active_collection':True,
|
||||||
'use_active_collection_with_nested':True,
|
'use_active_collection_with_nested':True,
|
||||||
@ -29,45 +29,46 @@ def export_main_scene(scene, blend_file_path, addon_prefs, blueprints_data):
|
|||||||
if export_blueprints :
|
if export_blueprints :
|
||||||
gltf_output_path = os.path.join(levels_path_full, scene.name)
|
gltf_output_path = os.path.join(levels_path_full, scene.name)
|
||||||
|
|
||||||
#inject_blueprints_list_into_main_scene(scene, blueprints_data, addon_prefs)
|
#inject_blueprints_list_into_main_scene(scene, blueprints_data, settings)
|
||||||
if export_separate_dynamic_and_static_objects:
|
if export_separate_dynamic_and_static_objects:
|
||||||
#print("SPLIT STATIC AND DYNAMIC")
|
#print("SPLIT STATIC AND DYNAMIC")
|
||||||
# first export static objects
|
# first export static objects
|
||||||
generate_and_export(
|
generate_and_export(
|
||||||
addon_prefs,
|
settings,
|
||||||
temp_scene_name=TEMPSCENE_PREFIX,
|
temp_scene_name=TEMPSCENE_PREFIX,
|
||||||
export_settings=export_settings,
|
gltf_export_settings=gltf_export_settings,
|
||||||
gltf_output_path=gltf_output_path,
|
gltf_output_path=gltf_output_path,
|
||||||
tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(scene.collection, temp_collection, blueprints_data=blueprints_data, filter=is_object_static, addon_prefs=addon_prefs),
|
tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(scene.collection, temp_collection, blueprints_data=blueprints_data, filter=is_object_static, settings=settings),
|
||||||
tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=scene.collection, temp_scene=temp_scene, **params)
|
tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=scene.collection, temp_scene=temp_scene, **params)
|
||||||
)
|
)
|
||||||
|
|
||||||
# then export all dynamic objects
|
# then export all dynamic objects
|
||||||
gltf_output_path = os.path.join(levels_path_full, scene.name+ "_dynamic")
|
gltf_output_path = os.path.join(levels_path_full, scene.name+ "_dynamic")
|
||||||
generate_and_export(
|
generate_and_export(
|
||||||
addon_prefs,
|
settings,
|
||||||
temp_scene_name=TEMPSCENE_PREFIX,
|
temp_scene_name=TEMPSCENE_PREFIX,
|
||||||
export_settings=export_settings,
|
gltf_export_settings=gltf_export_settings,
|
||||||
gltf_output_path=gltf_output_path,
|
gltf_output_path=gltf_output_path,
|
||||||
tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(scene.collection, temp_collection, blueprints_data=blueprints_data, filter=is_object_dynamic, addon_prefs=addon_prefs),
|
tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(scene.collection, temp_collection, blueprints_data=blueprints_data, filter=is_object_dynamic, settings=settings),
|
||||||
tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=scene.collection, temp_scene=temp_scene, **params)
|
tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=scene.collection, temp_scene=temp_scene, **params)
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
#print("NO SPLIT")
|
#print("NO SPLIT")
|
||||||
generate_and_export(
|
generate_and_export(
|
||||||
addon_prefs,
|
settings,
|
||||||
temp_scene_name=TEMPSCENE_PREFIX,
|
temp_scene_name=TEMPSCENE_PREFIX,
|
||||||
export_settings=export_settings,
|
gltf_export_settings=gltf_export_settings,
|
||||||
gltf_output_path=gltf_output_path,
|
gltf_output_path=gltf_output_path,
|
||||||
tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(scene.collection, temp_collection, blueprints_data=blueprints_data, addon_prefs=addon_prefs),
|
tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(scene.collection, temp_collection, blueprints_data=blueprints_data, settings=settings),
|
||||||
tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=scene.collection, temp_scene=temp_scene, **params)
|
tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=scene.collection, temp_scene=temp_scene, **params)
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
gltf_output_path = os.path.join(assets_path_full, scene.name)
|
gltf_output_path = os.path.join(assets_path_full, scene.name)
|
||||||
print(" exporting gltf to", gltf_output_path, ".gltf/glb")
|
print(" exporting gltf to", gltf_output_path, ".gltf/glb")
|
||||||
export_gltf(gltf_output_path, export_settings)
|
if settings.auto_export.dry_run == "DISABLED":
|
||||||
|
export_gltf(gltf_output_path, gltf_export_settings)
|
||||||
|
|
||||||
remove_blueprints_list_from_main_scene(scene)
|
remove_blueprints_list_from_main_scene(scene)
|
||||||
|
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
import bpy
|
|
||||||
import os
|
from ...core.scene_helpers import get_main_and_library_scenes
|
||||||
from ..helpers.helpers_scenes import (get_scenes, )
|
|
||||||
from ...blueprints.blueprint_helpers import find_blueprints_not_on_disk
|
from ...blueprints.blueprint_helpers import find_blueprints_not_on_disk
|
||||||
|
|
||||||
# TODO: this should also take the split/embed mode into account: if a nested collection changes AND embed is active, its container collection should also be exported
|
# TODO: this should also take the split/embed mode into account: if a nested collection changes AND embed is active, its container collection should also be exported
|
||||||
def get_blueprints_to_export(changes_per_scene, changed_export_parameters, blueprints_data, addon_prefs):
|
def get_blueprints_to_export(changes_per_scene, changed_export_parameters, blueprints_data, settings):
|
||||||
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension", ".glb")
|
export_gltf_extension = getattr(settings, "export_gltf_extension", ".glb")
|
||||||
blueprints_path_full = getattr(addon_prefs,"blueprints_path_full", "")
|
blueprints_path_full = getattr(settings,"blueprints_path_full", "")
|
||||||
change_detection = getattr(addon_prefs.auto_export, "change_detection")
|
change_detection = getattr(settings.auto_export, "change_detection")
|
||||||
collection_instances_combine_mode = getattr(addon_prefs.auto_export, "collection_instances_combine_mode")
|
collection_instances_combine_mode = getattr(settings.auto_export, "collection_instances_combine_mode")
|
||||||
|
|
||||||
[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_main_and_library_scenes(settings)
|
||||||
internal_blueprints = blueprints_data.internal_blueprints
|
internal_blueprints = blueprints_data.internal_blueprints
|
||||||
blueprints_to_export = internal_blueprints # just for clarity
|
blueprints_to_export = internal_blueprints # just for clarity
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import bpy
|
import bpy
|
||||||
|
from ...core.scene_helpers import get_main_and_library_scenes
|
||||||
from ...blueprints.blueprint_helpers import check_if_blueprint_on_disk
|
from ...blueprints.blueprint_helpers import check_if_blueprint_on_disk
|
||||||
from ..helpers.helpers_scenes import (get_scenes, )
|
|
||||||
|
|
||||||
# IF collection_instances_combine_mode is not 'split' check for each scene if any object in changes_per_scene has an instance in the scene
|
# IF collection_instances_combine_mode is not 'split' check for each scene if any object in changes_per_scene has an instance in the scene
|
||||||
def changed_object_in_scene(scene_name, changes_per_scene, blueprints_data, collection_instances_combine_mode):
|
def changed_object_in_scene(scene_name, changes_per_scene, blueprints_data, collection_instances_combine_mode):
|
||||||
@ -36,14 +36,14 @@ def changed_object_in_scene(scene_name, changes_per_scene, blueprints_data, coll
|
|||||||
|
|
||||||
|
|
||||||
# this also takes the split/embed mode into account: if a collection instance changes AND embed is active, its container level/world should also be exported
|
# this also takes the split/embed mode into account: if a collection instance changes AND embed is active, its container level/world should also be exported
|
||||||
def get_levels_to_export(changes_per_scene, changed_export_parameters, blueprints_data, addon_prefs):
|
def get_levels_to_export(changes_per_scene, changed_export_parameters, blueprints_data, settings):
|
||||||
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
|
export_gltf_extension = getattr(settings, "export_gltf_extension")
|
||||||
levels_path_full = getattr(addon_prefs, "levels_path_full")
|
levels_path_full = getattr(settings, "levels_path_full")
|
||||||
|
|
||||||
change_detection = getattr(addon_prefs.auto_export, "change_detection")
|
change_detection = getattr(settings.auto_export, "change_detection")
|
||||||
collection_instances_combine_mode = getattr(addon_prefs.auto_export, "collection_instances_combine_mode")
|
collection_instances_combine_mode = getattr(settings.auto_export, "collection_instances_combine_mode")
|
||||||
|
|
||||||
[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_main_and_library_scenes(settings)
|
||||||
|
|
||||||
# determine list of main scenes to export
|
# determine list of main scenes to export
|
||||||
# we have more relaxed rules to determine if the main scenes have changed : any change is ok, (allows easier handling of changes, render settings etc)
|
# we have more relaxed rules to determine if the main scenes have changed : any change is ok, (allows easier handling of changes, render settings etc)
|
||||||
|
@ -1,228 +0,0 @@
|
|||||||
import json
|
|
||||||
import bpy
|
|
||||||
from bpy.types import Operator
|
|
||||||
#from ..ui.main import GLTF_PT_auto_export_general, GLTF_PT_auto_export_main, GLTF_PT_auto_export_root
|
|
||||||
|
|
||||||
from .preferences import (AutoExportGltfAddonPreferences, AutoExportGltfPreferenceNames)
|
|
||||||
from .auto_export import auto_export
|
|
||||||
from ..helpers.generate_complete_preferences_dict import generate_complete_preferences_dict_auto
|
|
||||||
|
|
||||||
class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences):#, ExportHelper):
|
|
||||||
"""auto export gltf"""
|
|
||||||
#bl_idname = "object.xxx"
|
|
||||||
bl_idname = "export_scenes.auto_gltf"
|
|
||||||
bl_label = "Apply settings"
|
|
||||||
bl_options = {'PRESET'} # we do not add UNDO otherwise it leads to an invisible operation that resets the state of the saved serialized scene, breaking compares for normal undo/redo operations
|
|
||||||
# ExportHelper mixin class uses this
|
|
||||||
#filename_ext = ''
|
|
||||||
#filepath: bpy.props.StringProperty(subtype="FILE_PATH", default="") # type: ignore
|
|
||||||
|
|
||||||
#list of settings (other than purely gltf settings) whose change should trigger a re-generation of gltf files
|
|
||||||
white_list = [
|
|
||||||
'auto_export',
|
|
||||||
'project_root_path',
|
|
||||||
'assets_path',
|
|
||||||
'change_detection',
|
|
||||||
'export_scene_settings',
|
|
||||||
|
|
||||||
'main_scene_names',
|
|
||||||
'library_scene_names',
|
|
||||||
|
|
||||||
'export_blueprints',
|
|
||||||
'blueprints_path',
|
|
||||||
'export_marked_assets',
|
|
||||||
'collection_instances_combine_mode',
|
|
||||||
|
|
||||||
'levels_path',
|
|
||||||
'export_separate_dynamic_and_static_objects',
|
|
||||||
|
|
||||||
'export_materials_library',
|
|
||||||
'materials_path',
|
|
||||||
]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def register(cls):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def unregister(cls):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def format_settings(self):
|
|
||||||
# find all props to save
|
|
||||||
exceptional = [
|
|
||||||
# options that don't start with 'export_'
|
|
||||||
'collection_instances_combine_mode',
|
|
||||||
]
|
|
||||||
all_props = self.properties
|
|
||||||
export_props = {
|
|
||||||
x: getattr(self, x) for x in dir(all_props)
|
|
||||||
if (x.startswith("export_") or x in exceptional) and all_props.get(x) is not None
|
|
||||||
}
|
|
||||||
# we inject all that we need, the above is not sufficient
|
|
||||||
for (k, v) in self.properties.items():
|
|
||||||
if k in self.white_list or k not in AutoExportGltfPreferenceNames:
|
|
||||||
value = v
|
|
||||||
# FIXME: really weird having to do this
|
|
||||||
if k == "collection_instances_combine_mode":
|
|
||||||
value = self.collection_instances_combine_mode
|
|
||||||
if k == "export_materials":
|
|
||||||
value = self.export_materials
|
|
||||||
export_props[k] = value
|
|
||||||
# we add main & library scene names to our preferences
|
|
||||||
|
|
||||||
return export_props
|
|
||||||
|
|
||||||
def save_settings(self, context):
|
|
||||||
print("save settings")
|
|
||||||
auto_export_settings = self.format_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()
|
|
||||||
|
|
||||||
auto_export_settings = generate_complete_preferences_dict_auto(auto_export_settings)
|
|
||||||
stored_settings.write(json.dumps(auto_export_settings))
|
|
||||||
print("saved settings", auto_export_settings)
|
|
||||||
#print("saving settings", bpy.data.texts[".gltf_auto_export_settings"].as_string(), "raw", json.dumps(export_props))
|
|
||||||
|
|
||||||
def load_settings(self, context):
|
|
||||||
print("loading settings")
|
|
||||||
settings = None
|
|
||||||
try:
|
|
||||||
settings = bpy.data.texts[".gltf_auto_export_settings"].as_string()
|
|
||||||
settings = json.loads(settings)
|
|
||||||
except: pass
|
|
||||||
|
|
||||||
self.will_save_settings = False
|
|
||||||
if settings:
|
|
||||||
#print("loading settings in invoke AutoExportGLTF", settings)
|
|
||||||
try:
|
|
||||||
for (k, v) in settings.items():
|
|
||||||
#print("loading setting", k, v)
|
|
||||||
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'
|
|
||||||
|
|
||||||
# inject scenes data
|
|
||||||
if hasattr(self, 'main_scene_names'):
|
|
||||||
main_scenes = self.main_scenes
|
|
||||||
main_scenes.clear()
|
|
||||||
for item_name in self.main_scene_names:
|
|
||||||
item = main_scenes.add()
|
|
||||||
item.name = item_name
|
|
||||||
|
|
||||||
if hasattr(self, 'library_scene_names'):
|
|
||||||
library_scenes = self.library_scenes
|
|
||||||
library_scenes.clear()
|
|
||||||
for item_name in self.library_scene_names:
|
|
||||||
item = library_scenes.add()
|
|
||||||
item.name = item_name
|
|
||||||
|
|
||||||
except Exception as error:
|
|
||||||
print("error", error)
|
|
||||||
self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
|
|
||||||
bpy.data.texts.remove(bpy.data.texts[".gltf_auto_export_settings"])
|
|
||||||
else:
|
|
||||||
self.will_save_settings = True
|
|
||||||
|
|
||||||
"""
|
|
||||||
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):
|
|
||||||
return True
|
|
||||||
# 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[".blenvy_gltf_settings_previous"] if ".blenvy_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[".blenvy_gltf_settings"] if ".blenvy_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")
|
|
||||||
previous_gltf_settings = bpy.data.texts.new(".blenvy_gltf_settings_previous")
|
|
||||||
previous_gltf_settings.write(json.dumps({}))
|
|
||||||
if current_gltf_settings == None:
|
|
||||||
current_gltf_settings = bpy.data.texts.new(".blenvy_gltf_settings")
|
|
||||||
current_gltf_settings.write(json.dumps({}))
|
|
||||||
|
|
||||||
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
|
|
||||||
# now write the current settings to the "previous settings"
|
|
||||||
if current_auto_settings != None:
|
|
||||||
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
|
|
||||||
|
|
||||||
if current_gltf_settings != None:
|
|
||||||
previous_gltf_settings = bpy.data.texts[".blenvy_gltf_settings_previous"] if ".blenvy_gltf_settings_previous" in bpy.data.texts else bpy.data.texts.new(".blenvy_gltf_settings_previous")
|
|
||||||
previous_gltf_settings.clear()
|
|
||||||
previous_gltf_settings.write(current_gltf_settings.as_string())
|
|
||||||
|
|
||||||
return changed
|
|
||||||
|
|
||||||
def did_objects_change(self):
|
|
||||||
# FIXME: add it back
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
print("execute auto export", context.window_manager.blenvy.auto_export)
|
|
||||||
blenvy = context.window_manager.blenvy
|
|
||||||
auto_export_settings = blenvy.auto_export
|
|
||||||
bpy.context.window_manager.auto_export_tracker.disable_change_detection()
|
|
||||||
if self.direct_mode:
|
|
||||||
self.load_settings(context)
|
|
||||||
if self.will_save_settings:
|
|
||||||
self.save_settings(context)
|
|
||||||
#print("self", self.auto_export)
|
|
||||||
if auto_export_settings.auto_export: # only do the actual exporting if auto export is actually enabled
|
|
||||||
print("auto export")
|
|
||||||
#changes_per_scene = context.window_manager.auto_export_tracker.changed_objects_per_scene
|
|
||||||
#& do the export
|
|
||||||
# determine changed objects
|
|
||||||
changes_per_scene = self.did_objects_change()
|
|
||||||
# determine changed parameters
|
|
||||||
params_changed = self.did_export_settings_change()
|
|
||||||
auto_export(changes_per_scene, params_changed, blenvy)
|
|
||||||
# cleanup
|
|
||||||
# reset the list of changes in the tracker
|
|
||||||
bpy.context.window_manager.auto_export_tracker.clear_changes()
|
|
||||||
print("AUTO EXPORT DONE")
|
|
||||||
bpy.app.timers.register(bpy.context.window_manager.auto_export_tracker.enable_change_detection, first_interval=0.1)
|
|
||||||
else:
|
|
||||||
print("auto export disabled, skipping")
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
print("invoke")
|
|
||||||
bpy.context.window_manager.auto_export_tracker.disable_change_detection()
|
|
||||||
self.load_settings(context)
|
|
||||||
return context.window_manager.invoke_props_dialog(self, title="Auto export", width=640)
|
|
||||||
|
|
||||||
def cancel(self, context):
|
|
||||||
print("cancel")
|
|
||||||
#bpy.context.window_manager.auto_export_tracker.enable_change_detection()
|
|
||||||
bpy.app.timers.register(bpy.context.window_manager.auto_export_tracker.enable_change_detection, first_interval=1)
|
|
||||||
|
|
@ -1,201 +0,0 @@
|
|||||||
|
|
||||||
import os
|
|
||||||
from bpy.types import AddonPreferences
|
|
||||||
from bpy.props import (BoolProperty,
|
|
||||||
IntProperty,
|
|
||||||
StringProperty,
|
|
||||||
EnumProperty,
|
|
||||||
CollectionProperty
|
|
||||||
)
|
|
||||||
|
|
||||||
AutoExportGltfPreferenceNames = [
|
|
||||||
'will_save_settings',
|
|
||||||
'direct_mode',# specific to main auto_export operator
|
|
||||||
|
|
||||||
'show_general_settings',
|
|
||||||
'auto_export',
|
|
||||||
'project_root_path',
|
|
||||||
'assets_path',
|
|
||||||
'export_scene_settings',
|
|
||||||
|
|
||||||
'show_change_detection_settings',
|
|
||||||
'change_detection',
|
|
||||||
|
|
||||||
'show_scene_settings',
|
|
||||||
'main_scenes',
|
|
||||||
'library_scenes',
|
|
||||||
'main_scenes_index',
|
|
||||||
'library_scenes_index',
|
|
||||||
'main_scene_names',
|
|
||||||
'library_scene_names',
|
|
||||||
|
|
||||||
'show_blueprint_settings',
|
|
||||||
'export_blueprints',
|
|
||||||
'blueprints_path',
|
|
||||||
'export_marked_assets',
|
|
||||||
'collection_instances_combine_mode',
|
|
||||||
|
|
||||||
'levels_path',
|
|
||||||
'export_separate_dynamic_and_static_objects',
|
|
||||||
|
|
||||||
'export_materials_library',
|
|
||||||
'materials_path',
|
|
||||||
]
|
|
||||||
|
|
||||||
def on_export_output_folder_updated(self, context):
|
|
||||||
#self.project_root_path = os.path.relpath(self.project_root_path)
|
|
||||||
#self.assets_path = os.path.join(self.project_root_path, self.assets_path)
|
|
||||||
print("on_foo_updated", self.project_root_path, self.assets_path)
|
|
||||||
|
|
||||||
class AutoExportGltfAddonPreferences(AddonPreferences):
|
|
||||||
# this must match the add-on name, use '__package__'
|
|
||||||
# when defining this in a submodule of a python package.
|
|
||||||
bl_idname = __package__
|
|
||||||
bl_options = {'PRESET'}
|
|
||||||
|
|
||||||
#### these are for the operator
|
|
||||||
will_save_settings: BoolProperty(
|
|
||||||
name='Remember Export Settings',
|
|
||||||
description='Store glTF export settings in the Blender project',
|
|
||||||
default=True
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
# 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
|
|
||||||
# for UI only, workaround for lacking panels
|
|
||||||
show_general_settings: BoolProperty(
|
|
||||||
name="show_general settings",
|
|
||||||
description="show/hide general settings (UI only: has no impact on exports)",
|
|
||||||
default=True
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
project_root_path: StringProperty(
|
|
||||||
name = "Project Root Path",
|
|
||||||
description="The root folder of your (Bevy) project (not assets!)",
|
|
||||||
# subtype='DIR_PATH',
|
|
||||||
default='../'
|
|
||||||
#update=on_export_output_folder_updated) # type: ignore
|
|
||||||
)
|
|
||||||
|
|
||||||
assets_path: StringProperty(
|
|
||||||
name='Export folder',
|
|
||||||
description='The root folder for all exports(relative to the root folder/path) Defaults to "assets" ',
|
|
||||||
default='./assets',
|
|
||||||
#subtype='DIR_PATH',
|
|
||||||
options={'HIDDEN'}
|
|
||||||
# update=on_export_output_folder_updated
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
# for UI only, workaround for lacking panels
|
|
||||||
show_change_detection_settings: BoolProperty(
|
|
||||||
name="show change detection settings",
|
|
||||||
description="show/hide change detection settings (UI only: has no impact on exports)",
|
|
||||||
default=True
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
change_detection: BoolProperty(
|
|
||||||
name='Change detection',
|
|
||||||
description='Use change detection to determine what/if should be exported',
|
|
||||||
default=True
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
# scenes
|
|
||||||
# for UI only, workaround for lacking panels
|
|
||||||
show_scene_settings: BoolProperty(
|
|
||||||
name="show scene settings",
|
|
||||||
description="show/hide scene settings (UI only: has no impact on exports)",
|
|
||||||
default=True
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
# 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
|
|
||||||
# for UI only, workaround for lacking panels
|
|
||||||
show_blueprint_settings: BoolProperty(
|
|
||||||
name="show blueprint settings",
|
|
||||||
description="show/hide blueprint settings (UI only: has no impact on exports)",
|
|
||||||
default=True
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
blueprints_path: StringProperty(
|
|
||||||
name='Blueprints path',
|
|
||||||
description='path to export the blueprints to (relative to the assets folder)',
|
|
||||||
default='blueprints',
|
|
||||||
#subtype='DIR_PATH'
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
levels_path: StringProperty(
|
|
||||||
name='Levels path',
|
|
||||||
description='path to export the levels (main scenes) to (relative to the assets folder)',
|
|
||||||
default='levels',
|
|
||||||
#subtype='DIR_PATH'
|
|
||||||
) # 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
|
|
||||||
|
|
||||||
materials_path: StringProperty(
|
|
||||||
name='Materials path',
|
|
||||||
description='path to export the materials libraries to (relative to the assets folder)',
|
|
||||||
default='materials',
|
|
||||||
#subtype='DIR_PATH'
|
|
||||||
) # 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
|
|
||||||
|
|
@ -8,8 +8,8 @@ from .settings_diff import get_setting_changes
|
|||||||
# prepare export by gather the changes to the scenes & settings
|
# prepare export by gather the changes to the scenes & settings
|
||||||
def prepare_and_export():
|
def prepare_and_export():
|
||||||
print("prepare and export")
|
print("prepare and export")
|
||||||
blenvy = bpy.context.window_manager.blenvy
|
|
||||||
bpy.context.window_manager.auto_export_tracker.disable_change_detection()
|
bpy.context.window_manager.auto_export_tracker.disable_change_detection()
|
||||||
|
blenvy = bpy.context.window_manager.blenvy
|
||||||
auto_export_settings = blenvy.auto_export
|
auto_export_settings = blenvy.auto_export
|
||||||
if auto_export_settings.auto_export: # only do the actual exporting if auto export is actually enabled
|
if auto_export_settings.auto_export: # only do the actual exporting if auto export is actually enabled
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@ from ...core.helpers_collections import (set_active_collection)
|
|||||||
generates a temporary scene, fills it with data, cleans up after itself
|
generates a temporary scene, fills it with data, cleans up after itself
|
||||||
* named using temp_scene_name
|
* named using temp_scene_name
|
||||||
* filled using the tempScene_filler
|
* filled using the tempScene_filler
|
||||||
* written on disk to gltf_output_path, with the gltf export parameters in export_settings
|
* written on disk to gltf_output_path, with the gltf export parameters in gltf_export_settings
|
||||||
* cleaned up using tempScene_cleaner
|
* cleaned up using tempScene_cleaner
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def generate_and_export(addon_prefs, export_settings, gltf_output_path, temp_scene_name="__temp_scene", tempScene_filler=None, tempScene_cleaner=None):
|
def generate_and_export(settings, gltf_export_settings, gltf_output_path, temp_scene_name="__temp_scene", tempScene_filler=None, tempScene_cleaner=None):
|
||||||
|
|
||||||
temp_scene = bpy.data.scenes.new(name=temp_scene_name)
|
temp_scene = bpy.data.scenes.new(name=temp_scene_name)
|
||||||
temp_root_collection = temp_scene.collection
|
temp_root_collection = temp_scene.collection
|
||||||
@ -41,7 +41,8 @@ def generate_and_export(addon_prefs, export_settings, gltf_output_path, temp_sce
|
|||||||
scene_filler_data = tempScene_filler(temp_root_collection)
|
scene_filler_data = tempScene_filler(temp_root_collection)
|
||||||
# export the temporary scene
|
# export the temporary scene
|
||||||
try:
|
try:
|
||||||
export_gltf(gltf_output_path, export_settings)
|
if settings.auto_export.dry_run == "DISABLED":
|
||||||
|
export_gltf(gltf_output_path, gltf_export_settings)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
print("failed to export gltf !", error)
|
print("failed to export gltf !", error)
|
||||||
raise error
|
raise error
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
|
|
||||||
from ..auto_export.preferences import AutoExportGltfAddonPreferences
|
|
||||||
from io_scene_gltf2 import (ExportGLTF2_Base)
|
from io_scene_gltf2 import (ExportGLTF2_Base)
|
||||||
|
|
||||||
# given the input (actual) gltf settings, filters out any invalid/useless params & params that are equal to defaults
|
# given the input (actual) gltf settings, filters out any invalid/useless params & params that are equal to defaults
|
||||||
@ -28,12 +27,12 @@ def generate_complete_preferences_dict_gltf(settings):
|
|||||||
return complete_preferences
|
return complete_preferences
|
||||||
|
|
||||||
# given the input (actual) auto settings, filters out any invalid/useless params & params that are equal to defaults
|
# given the input (actual) auto settings, filters out any invalid/useless params & params that are equal to defaults
|
||||||
def generate_complete_preferences_dict_auto(settings):
|
def generate_complete_preferences_dict_auto(settings, presets):
|
||||||
complete_preferences = {}
|
complete_preferences = {}
|
||||||
defaults = {}
|
defaults = {}
|
||||||
|
|
||||||
for k in AutoExportGltfAddonPreferences.__annotations__:
|
for k in presets.__annotations__:
|
||||||
item = AutoExportGltfAddonPreferences.__annotations__[k]
|
item = presets.__annotations__[k]
|
||||||
default = item.keywords.get('default', None)
|
default = item.keywords.get('default', None)
|
||||||
#complete_preferences[k] = default
|
#complete_preferences[k] = default
|
||||||
defaults[k] = default
|
defaults[k] = default
|
||||||
|
@ -145,8 +145,8 @@ def duplicate_object(object, parent, combine_mode, destination_collection, bluep
|
|||||||
duplicate_object(child, copy, combine_mode, destination_collection, blueprints_data, nester+" ")
|
duplicate_object(child, copy, combine_mode, destination_collection, blueprints_data, nester+" ")
|
||||||
|
|
||||||
# copies the contents of a collection into another one while replacing library instances with empties
|
# copies the contents of a collection into another one while replacing library instances with empties
|
||||||
def copy_hollowed_collection_into(source_collection, destination_collection, parent_empty=None, filter=None, blueprints_data=None, addon_prefs={}):
|
def copy_hollowed_collection_into(source_collection, destination_collection, parent_empty=None, filter=None, blueprints_data=None, settings={}):
|
||||||
collection_instances_combine_mode = getattr(addon_prefs.auto_export, "collection_instances_combine_mode")
|
collection_instances_combine_mode = getattr(settings.auto_export, "collection_instances_combine_mode")
|
||||||
|
|
||||||
for object in source_collection.objects:
|
for object in source_collection.objects:
|
||||||
if object.name.endswith("____bak"): # some objects could already have been handled, ignore them
|
if object.name.endswith("____bak"): # some objects could already have been handled, ignore them
|
||||||
@ -172,7 +172,7 @@ def copy_hollowed_collection_into(source_collection, destination_collection, par
|
|||||||
parent_empty = collection_placeholder,
|
parent_empty = collection_placeholder,
|
||||||
filter = filter,
|
filter = filter,
|
||||||
blueprints_data = blueprints_data,
|
blueprints_data = blueprints_data,
|
||||||
addon_prefs=addon_prefs
|
settings=settings
|
||||||
)
|
)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@ -204,20 +204,3 @@ def clear_hollow_scene(temp_scene, original_root_collection):
|
|||||||
|
|
||||||
# reset original names
|
# reset original names
|
||||||
restore_original_names(original_root_collection)
|
restore_original_names(original_root_collection)
|
||||||
|
|
||||||
# convenience utility to get lists of scenes
|
|
||||||
def get_scenes(addon_prefs):
|
|
||||||
level_scene_names= getattr(addon_prefs, "main_scene_names", []) #list(map(lambda scene: scene.name, getattr(addon_prefs,"main_scenes")))
|
|
||||||
library_scene_names = getattr(addon_prefs, "library_scene_names", []) #list(map(lambda scene: scene.name, getattr(addon_prefs,"library_scenes")))
|
|
||||||
|
|
||||||
level_scene_names= list(map(lambda scene: scene.name, getattr(addon_prefs,"main_scenes")))
|
|
||||||
library_scene_names = list(map(lambda scene: scene.name, getattr(addon_prefs,"library_scenes")))
|
|
||||||
|
|
||||||
print("level_scene_names", level_scene_names)
|
|
||||||
level_scene_names = list(filter(lambda name: name in bpy.data.scenes, level_scene_names))
|
|
||||||
library_scene_names = list(filter(lambda name: name in bpy.data.scenes, library_scene_names))
|
|
||||||
|
|
||||||
level_scenes = list(map(lambda name: bpy.data.scenes[name], level_scene_names))
|
|
||||||
library_scenes = list(map(lambda name: bpy.data.scenes[name], library_scene_names))
|
|
||||||
|
|
||||||
return [level_scene_names, level_scenes, library_scene_names, library_scenes]
|
|
||||||
|
@ -155,7 +155,7 @@ class glTF2ExportUserExtension:
|
|||||||
self.Extension = Extension
|
self.Extension = Extension
|
||||||
self.properties = bpy.context.scene.ExampleExtensionProperties
|
self.properties = bpy.context.scene.ExampleExtensionProperties
|
||||||
|
|
||||||
def gather_node_hook(self, gltf2_object, blender_object, export_settings):
|
def gather_node_hook(self, gltf2_object, blender_object, gltf_export_settings):
|
||||||
if self.properties.enabled:
|
if self.properties.enabled:
|
||||||
if gltf2_object.extensions is None:
|
if gltf2_object.extensions is None:
|
||||||
gltf2_object.extensions = {}
|
gltf2_object.extensions = {}
|
||||||
@ -193,7 +193,7 @@ def did_export_parameters_change(current_params, previous_params):
|
|||||||
bpy.context.window.scene = library_scene
|
bpy.context.window.scene = library_scene
|
||||||
with bpy.context.temp_override(scene=library_scene):
|
with bpy.context.temp_override(scene=library_scene):
|
||||||
print("active scene", bpy.context.scene)
|
print("active scene", bpy.context.scene)
|
||||||
export_gltf(gltf_output_path, export_settings)
|
export_gltf(gltf_output_path, gltf_export_settings)
|
||||||
bpy.context.window.scene = original_scene"""
|
bpy.context.window.scene = original_scene"""
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -296,16 +296,16 @@ def duplicate_object2(object, original_name):
|
|||||||
settings = scene["glTF2ExportSettings"]
|
settings = scene["glTF2ExportSettings"]
|
||||||
formatted_settings = dict(settings)
|
formatted_settings = dict(settings)
|
||||||
|
|
||||||
export_settings = bpy.data.texts[".blenvy_gltf_settings"] if ".blenvy_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".blenvy_gltf_settings")
|
gltf_export_settings = bpy.data.texts[".blenvy_gltf_settings"] if ".blenvy_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".blenvy_gltf_settings")
|
||||||
|
|
||||||
#check if params have changed
|
#check if params have changed
|
||||||
bpy.context.window_manager.gltf_settings_changed = sorted(json.loads(export_settings.as_string()).items()) != sorted(formatted_settings.items())
|
bpy.context.window_manager.gltf_settings_changed = sorted(json.loads(gltf_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)
|
print("gltf NEW settings", formatted_settings, "OLD settings", gltf_export_settings, "CHANGED ?", bpy.context.window_manager.gltf_settings_changed)
|
||||||
|
|
||||||
# now write new settings
|
# now write new settings
|
||||||
export_settings.clear()
|
gltf_export_settings.clear()
|
||||||
export_settings.write(json.dumps(formatted_settings))
|
gltf_export_settings.write(json.dumps(formatted_settings))
|
||||||
|
|
||||||
|
|
||||||
# now reset the original gltf_settings
|
# now reset the original gltf_settings
|
||||||
|
@ -59,14 +59,14 @@ def clear_materials_scene(temp_scene):
|
|||||||
|
|
||||||
# exports the materials used inside the current project:
|
# exports the materials used inside the current project:
|
||||||
# the name of the output path is <materials_folder>/<name_of_your_blend_file>_materials_library.gltf/glb
|
# the name of the output path is <materials_folder>/<name_of_your_blend_file>_materials_library.gltf/glb
|
||||||
def export_materials(collections, library_scenes, addon_prefs):
|
def export_materials(collections, library_scenes, settings):
|
||||||
gltf_export_preferences = generate_gltf_export_preferences(addon_prefs)
|
gltf_export_preferences = generate_gltf_export_preferences(settings)
|
||||||
materials_path_full = getattr(addon_prefs,"materials_path_full")
|
materials_path_full = getattr(settings,"materials_path_full")
|
||||||
|
|
||||||
used_material_names = get_all_materials(collections, library_scenes)
|
used_material_names = get_all_materials(collections, library_scenes)
|
||||||
current_project_name = Path(bpy.context.blend_data.filepath).stem
|
current_project_name = Path(bpy.context.blend_data.filepath).stem
|
||||||
|
|
||||||
export_settings = { **gltf_export_preferences,
|
gltf_export_settings = { **gltf_export_preferences,
|
||||||
'use_active_scene': True,
|
'use_active_scene': True,
|
||||||
'use_active_collection':True,
|
'use_active_collection':True,
|
||||||
'use_active_collection_with_nested':True,
|
'use_active_collection_with_nested':True,
|
||||||
@ -80,9 +80,9 @@ def export_materials(collections, library_scenes, addon_prefs):
|
|||||||
print(" exporting Materials to", gltf_output_path, ".gltf/glb")
|
print(" exporting Materials to", gltf_output_path, ".gltf/glb")
|
||||||
|
|
||||||
generate_and_export(
|
generate_and_export(
|
||||||
addon_prefs,
|
settings=settings,
|
||||||
|
gltf_export_settings=gltf_export_settings,
|
||||||
temp_scene_name="__materials_scene",
|
temp_scene_name="__materials_scene",
|
||||||
export_settings=export_settings,
|
|
||||||
gltf_output_path=gltf_output_path,
|
gltf_output_path=gltf_output_path,
|
||||||
tempScene_filler= lambda temp_collection: generate_materials_scene_content(temp_collection, used_material_names),
|
tempScene_filler= lambda temp_collection: generate_materials_scene_content(temp_collection, used_material_names),
|
||||||
tempScene_cleaner= lambda temp_scene, params: clear_materials_scene(temp_scene=temp_scene)
|
tempScene_cleaner= lambda temp_scene, params: clear_materials_scene(temp_scene=temp_scene)
|
||||||
|
@ -76,9 +76,11 @@ class AutoExportSettings(PropertyGroup):
|
|||||||
name="dry run",
|
name="dry run",
|
||||||
description="debug/ develop helper to enable everything but the actual exporting of files",
|
description="debug/ develop helper to enable everything but the actual exporting of files",
|
||||||
items=(
|
items=(
|
||||||
|
("DISABLED", "Disabled","Run exports normally (no Dry run)"),
|
||||||
("NO_EXPORT", "No export", "do not actually export gltf files"),
|
("NO_EXPORT", "No export", "do not actually export gltf files"),
|
||||||
("NO_PREPARE", "No prepare", "do not actually export gltf files AND do not prepare the exports either (ie no creating fake scenes etc)"),
|
("NO_PREPARE", "No prepare", "do not actually export gltf files AND do not prepare the exports either (ie no creating fake scenes etc)"),
|
||||||
)
|
),
|
||||||
|
default="DISABLED"
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
# special property for gltf settings
|
# special property for gltf settings
|
||||||
|
Loading…
Reference in New Issue
Block a user