Compare commits

...

2 Commits

Author SHA1 Message Date
kaosat.dev
d99a7ccd45 feat(blenvy): more work done on asset management
* helpers
 * ui tweaks
 * scanning of the whole asset tree added
 * cleaner injection of data at the collection/ scene level (wip)
2024-05-15 23:32:01 +02:00
kaosat.dev
d0bc05fb96 feat(blenvy):
* added correct injection of assets + export path to blueprints
 * moved a few common elements to "core" for better reuse
 * more tweaks/ experiments with paths handling
 * moved & overhauled a few of the blueprint & assets tools
 * lots of minor tweaks & changes
2024-05-15 13:25:30 +02:00
31 changed files with 540 additions and 350 deletions

View File

@ -1,11 +1,55 @@
Auto export Auto export
- [ ] the original blueprints & levels path are now left as is, and there is an auto injection of xxxpath_full for absolute paths - [x] the original blueprints & levels path are now left as is, and there is an auto injection of xxxpath_full for absolute paths
- [ ] replace all uses of the paths with the correct ones above - [x] replace all uses of the paths with the correct ones above
- [x] levels - [x] levels
- [x] blueprints - [x] blueprints
- [ ] materials - [x] materials
- [x] move out the UI for "assets" folder out of "blueprints condition" - [x] move out the UI for "assets" folder out of "blueprints condition"
- [ ] fix asset path calculations - [ ] fix asset path calculations
- root path => relative to blend file path - root path => relative to blend file path
- asset path => relative to root path - asset path => relative to root path
- blueprints/levels/blueprints path => relative to assets path - blueprints/levels/blueprints path => relative to assets path
- move out some parameters from auto export to a higher level (as they are now used in multiple places)
- [ ] main/ library scene names
- [ ] paths
Data storage:
- for scenes (main scenes)
- at scene level
- for blueprints
- at collection level
- Note: these should be COPIED to the scene level when exporting, into the temp_scene's properties
> NOTE: UP until we manage to create a PR for Bevy to directly support the scene level gltf_extras, the auto exporter should automatically create (& remove)
an additional object with scene_<scene_name>_components to copy that data to
Assets:
- blueprint assets should be auto_generated & inserted into the list of assets : these assets are NOT removable by the user
- should not change the list of manually added assets
- [x] store assets
- [x] per main scene for level/world assets
- [x] per blueprint for blueprint in lib scene
- [ ] UI:
- [x] we need to display all direct assets (stored in the scene)
- [ ] indirect assets:
- QUESTION : do we want to include them in the list of assets per level ?
- this would enable pre-loading ALL the assets, but is not ideal in most other cases
- so add an option ?
- [ ] the assets of local blueprints
Blueprints:
- [x] on save: write IN THE COLLECTION PROPERTIES
- list of assets
- export path
- [ ] blueprint selection for nested blueprints is broken
General issues:
- there is no safeguard for naming collisions for naming across blender files
- this can cause an issue for assets list "parent"
- "parents" can only be blueprints
- 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

View File

@ -33,7 +33,6 @@ from .bevy_components.components.ui import (BEVY_COMPONENTS_PT_ComponentsPanel)
from .gltf_auto_export import gltf_post_export_callback from .gltf_auto_export import gltf_post_export_callback
from .gltf_auto_export.auto_export.operators import AutoExportGLTF from .gltf_auto_export.auto_export.operators import AutoExportGLTF
from .gltf_auto_export.auto_export.tracker import AutoExportTracker from .gltf_auto_export.auto_export.tracker import AutoExportTracker
from .gltf_auto_export.auto_export.preferences import (AutoExportGltfAddonPreferences)
from .gltf_auto_export.auto_export.internals import (SceneLink, from .gltf_auto_export.auto_export.internals import (SceneLink,
SceneLinks, SceneLinks,
@ -41,7 +40,7 @@ from .gltf_auto_export.auto_export.internals import (SceneLink,
BlueprintsToExport, BlueprintsToExport,
CUSTOM_PG_sceneName CUSTOM_PG_sceneName
) )
from .gltf_auto_export.ui.main import (GLTF_PT_auto_export_change_detection, GLTF_PT_auto_export_changes_list, GLTF_PT_auto_export_main, from .gltf_auto_export.ui.main import (GLTF_PT_auto_export_change_detection, GLTF_PT_auto_export_main,
GLTF_PT_auto_export_root, GLTF_PT_auto_export_root,
GLTF_PT_auto_export_general, GLTF_PT_auto_export_general,
GLTF_PT_auto_export_scenes, GLTF_PT_auto_export_scenes,
@ -53,9 +52,9 @@ from .gltf_auto_export.ui.main import (GLTF_PT_auto_export_change_detection, GLT
from .gltf_auto_export.ui.operators import (OT_OpenFolderbrowser, SCENES_LIST_OT_actions) from .gltf_auto_export.ui.operators import (OT_OpenFolderbrowser, SCENES_LIST_OT_actions)
# asset management # asset management
from .assets.ui import GLTF_PT_auto_export_assets from .assets.ui import Blenvy_assets
from .assets.assets_registry import AssetsRegistry from .assets.assets_registry import AssetsRegistry
from .assets.operators import OT_Add_asset_filebrowser, OT_add_bevy_asset, OT_remove_bevy_asset from .assets.operators import OT_Add_asset_filebrowser, OT_add_bevy_asset, OT_remove_bevy_asset, OT_test_bevy_assets
# blueprints management # blueprints management
from .blueprints.ui import GLTF_PT_auto_export_blueprints_list from .blueprints.ui import GLTF_PT_auto_export_blueprints_list
@ -150,8 +149,9 @@ classes = [
AssetsRegistry, AssetsRegistry,
OT_add_bevy_asset, OT_add_bevy_asset,
OT_remove_bevy_asset, OT_remove_bevy_asset,
OT_test_bevy_assets,
OT_Add_asset_filebrowser, OT_Add_asset_filebrowser,
GLTF_PT_auto_export_assets, Blenvy_assets,
BlueprintsRegistry, BlueprintsRegistry,
OT_select_blueprint, OT_select_blueprint,

View File

@ -0,0 +1,10 @@
import json
def get_assets(scene_or_collection):
assets = json.loads(scene_or_collection.get('assets')) if 'assets' in scene_or_collection else []
return assets
def does_asset_exist(assets, asset_path):
in_list = [asset for asset in assets if (asset["path"] == asset_path)]
in_list = len(in_list) > 0
return in_list

View File

@ -7,50 +7,6 @@ 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)
def get_assets(scene, blueprints_data, addon_prefs):
export_root_path = getattr(addon_prefs, "export_root_path")
export_output_folder = getattr(addon_prefs,"export_output_folder")
export_levels_path = getattr(addon_prefs,"export_levels_path")
export_blueprints_path = getattr(addon_prefs, "export_blueprints_path")
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
relative_blueprints_path = os.path.relpath(export_blueprints_path, export_root_path)
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(scene.name, None)
blueprint_assets_list = []
if blueprint_instance_names_for_scene:
for blueprint_name in blueprint_instance_names_for_scene:
blueprint = blueprints_data.blueprints_per_name.get(blueprint_name, None)
if blueprint is not None:
print("BLUEPRINT", blueprint)
blueprint_exported_path = None
if blueprint.local:
blueprint_exported_path = os.path.join(relative_blueprints_path, f"{blueprint.name}{export_gltf_extension}")
else:
# get the injected path of the external blueprints
blueprint_exported_path = blueprint.collection['Export_path'] if 'Export_path' in blueprint.collection else None
print("foo", dict(blueprint.collection))
if blueprint_exported_path is not None:
blueprint_assets_list.append({"name": blueprint.name, "path": blueprint_exported_path})
# fetch images/textures
# see https://blender.stackexchange.com/questions/139859/how-to-get-absolute-file-path-for-linked-texture-image
textures = []
for ob in bpy.data.objects:
if ob.type == "MESH":
for mat_slot in ob.material_slots:
if mat_slot.material:
if mat_slot.material.node_tree:
textures.extend([x.image.filepath for x in mat_slot.material.node_tree.nodes if x.type=='TEX_IMAGE'])
print("textures", textures)
assets_list_name = f"assets_{scene.name}"
assets_list_data = {"blueprints": json.dumps(blueprint_assets_list), "sounds":[], "images":[]}
print("blueprint assets", blueprint_assets_list)
# this is where we store the information for all available assets # this is where we store the information for all available assets
# #
class AssetsRegistry(PropertyGroup): class AssetsRegistry(PropertyGroup):
@ -95,3 +51,4 @@ class AssetsRegistry(PropertyGroup):
def remove_asset(self, path): def remove_asset(self, path):
self.assets_list[:] = [asset for asset in self.assets_list if (asset["path"] != path)] self.assets_list[:] = [asset for asset in self.assets_list if (asset["path"] != path)]

View File

@ -0,0 +1,101 @@
import os
import json
import bpy
from .asset_helpers import does_asset_exist, get_assets
def scan_assets(scene, blueprints_data, addon_prefs):
export_root_path = getattr(addon_prefs, "export_root_path")
export_output_folder = getattr(addon_prefs,"export_output_folder")
export_levels_path = getattr(addon_prefs,"export_levels_path")
export_blueprints_path = getattr(addon_prefs, "export_blueprints_path")
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
relative_blueprints_path = os.path.relpath(export_blueprints_path, export_root_path)
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(scene.name, None)
blueprint_assets_list = []
if blueprint_instance_names_for_scene:
for blueprint_name in blueprint_instance_names_for_scene:
blueprint = blueprints_data.blueprints_per_name.get(blueprint_name, None)
if blueprint is not None:
print("BLUEPRINT", blueprint)
blueprint_exported_path = None
if blueprint.local:
blueprint_exported_path = os.path.join(relative_blueprints_path, f"{blueprint.name}{export_gltf_extension}")
else:
# get the injected path of the external blueprints
blueprint_exported_path = blueprint.collection['Export_path'] if 'Export_path' in blueprint.collection else None
print("foo", dict(blueprint.collection))
if blueprint_exported_path is not None:
blueprint_assets_list.append({"name": blueprint.name, "path": blueprint_exported_path})
# fetch images/textures
# see https://blender.stackexchange.com/questions/139859/how-to-get-absolute-file-path-for-linked-texture-image
textures = []
for ob in bpy.data.objects:
if ob.type == "MESH":
for mat_slot in ob.material_slots:
if mat_slot.material:
if mat_slot.material.node_tree:
textures.extend([x.image.filepath for x in mat_slot.material.node_tree.nodes if x.type=='TEX_IMAGE'])
print("textures", textures)
assets_list_name = f"assets_{scene.name}"
assets_list_data = {"blueprints": json.dumps(blueprint_assets_list), "sounds":[], "images":[]}
print("blueprint assets", blueprint_assets_list)
def get_blueprint_assets_tree(blueprint, blueprints_data, parent, addon_prefs):
export_blueprints_path = getattr(addon_prefs, "export_blueprints_path")
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
assets_list = []
for blueprint_name in blueprint.nested_blueprints:
child_blueprint = blueprints_data.blueprints_per_name.get(blueprint_name, None)
if child_blueprint:
blueprint_exported_path = None
if blueprint.local:
blueprint_exported_path = os.path.join(export_blueprints_path, f"{child_blueprint.name}{export_gltf_extension}")
else:
# get the injected path of the external blueprints
blueprint_exported_path = child_blueprint.collection['export_path'] if 'export_path' in child_blueprint.collection else None
if blueprint_exported_path is not None:
assets_list.append({"name": child_blueprint.name, "path": blueprint_exported_path, "type": "MODEL", "internal": True, "parent": blueprint.name})
# and add sub stuff
sub_assets_lists = get_blueprint_assets_tree(child_blueprint, blueprints_data, parent=child_blueprint.name, addon_prefs=addon_prefs)
assets_list += sub_assets_lists
direct_assets = get_assets(blueprint.collection)
for asset in direct_assets:
asset["parent"] = parent
assets_list += direct_assets
return assets_list
def get_main_scene_assets_tree(main_scene, blueprints_data, addon_prefs):
export_blueprints_path = getattr(addon_prefs, "export_blueprints_path")
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(main_scene.name, None)
assets_list = get_assets(main_scene) # FIXME: problem, we already have the blueprint assets stored there
if blueprint_instance_names_for_scene:
for blueprint_name in blueprint_instance_names_for_scene:
blueprint = blueprints_data.blueprints_per_name.get(blueprint_name, None)
if blueprint is not None:
blueprint_exported_path = None
if blueprint.local:
blueprint_exported_path = os.path.join(export_blueprints_path, f"{blueprint.name}{export_gltf_extension}")
else:
# get the injected path of the external blueprints
blueprint_exported_path = blueprint.collection['export_path'] if 'export_path' in blueprint.collection else None
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", "internal": True, "parent": None})
assets_list += get_blueprint_assets_tree(blueprint, blueprints_data, parent=blueprint.name, addon_prefs=addon_prefs)
print("TOTAL ASSETS", assets_list)
return assets_list

View File

@ -4,6 +4,9 @@ import bpy
from bpy_types import (Operator) from bpy_types import (Operator)
from bpy.props import (BoolProperty, StringProperty, EnumProperty) from bpy.props import (BoolProperty, StringProperty, EnumProperty)
from .assets_scan import get_main_scene_assets_tree
from ..core.path_helpers import absolute_path_from_blend_file
from ..settings import load_settings from ..settings import load_settings
class OT_add_bevy_asset(Operator): class OT_add_bevy_asset(Operator):
@ -148,9 +151,10 @@ class OT_Add_asset_filebrowser(Operator, ImportHelper):
export_assets_path = current_auto_settings.get("export_assets_path", "assets") export_assets_path = current_auto_settings.get("export_assets_path", "assets")
# FIXME: not sure # FIXME: not sure
print("export_root_path", export_root_path, "export_assets_path", export_assets_path) print("export_root_path", export_root_path, "export_assets_path", export_assets_path)
export_assets_path_absolute = os.path.join(export_root_path, export_assets_path) export_assets_path_absolute = absolute_path_from_blend_file(os.path.join(export_root_path, export_assets_path))
asset_path = os.path.relpath(self.filepath, export_assets_path_absolute)
asset_path = os.path.relpath(self.filepath, export_assets_path_absolute)
print("asset path", asset_path)
assets_registry = context.window_manager.assets_registry assets_registry = context.window_manager.assets_registry
assets_registry.asset_path_selector = asset_path assets_registry.asset_path_selector = asset_path
@ -158,3 +162,26 @@ class OT_Add_asset_filebrowser(Operator, ImportHelper):
print("SELECTED ASSET PATH", asset_path) print("SELECTED ASSET PATH", asset_path)
return {'FINISHED'} return {'FINISHED'}
from types import SimpleNamespace
class OT_test_bevy_assets(Operator):
"""Test assets"""
bl_idname = "bevyassets.test"
bl_label = "test bevy assets"
bl_options = {"UNDO"}
def execute(self, context):
blueprints_registry = context.window_manager.blueprints_registry
blueprints_registry.add_blueprints_data()
blueprints_data = blueprints_registry.blueprints_data
settings = {"export_blueprints_path": "blueprints", "export_gltf_extension": ".glb"}
settings = SimpleNamespace(**settings)
for scene in bpy.data.scenes:
if scene.name != "Library":
assets_hierarchy = get_main_scene_assets_tree(scene, blueprints_data, settings)
scene["assets_hierarchy"] = json.dumps(assets_hierarchy)
return {'FINISHED'}

View File

@ -1,31 +1,34 @@
from types import SimpleNamespace
import bpy import bpy
import json from .assets_scan import get_main_scene_assets_tree
from .asset_helpers import get_assets
def draw_assets(layout, name, title, asset_registry, assets, target_type, target_name): def draw_assets(layout, name, title, asset_registry, assets, target_type, target_name, editable=True):
header, panel = layout.box().panel(f"assets{name}", default_closed=False) header, panel = layout.box().panel(f"assets{name}", default_closed=False)
header.label(text=title) header.label(text=title)
if panel: if panel:
row = panel.row() if editable:
row.prop(asset_registry, "asset_name_selector", text="") row = panel.row()
row.prop(asset_registry, "asset_type_selector", text="") row.prop(asset_registry, "asset_name_selector", text="")
asset_selector = row.operator(operator="asset.open_filebrowser", text="", icon="FILE_FOLDER") row.prop(asset_registry, "asset_type_selector", text="")
asset_selector = row.operator(operator="asset.open_filebrowser", text="", icon="FILE_FOLDER")
if asset_registry.asset_type_selector == 'IMAGE': if asset_registry.asset_type_selector == 'IMAGE':
asset_selector.filter_glob = '*.jpg;*.jpeg;*.png;*.bmp' asset_selector.filter_glob = '*.jpg;*.jpeg;*.png;*.bmp'
if asset_registry.asset_type_selector == 'MODEL': if asset_registry.asset_type_selector == 'MODEL':
asset_selector.filter_glob="*.glb;*.gltf" asset_selector.filter_glob="*.glb;*.gltf"
if asset_registry.asset_type_selector == 'TEXT': if asset_registry.asset_type_selector == 'TEXT':
asset_selector.filter_glob="*.txt;*.md;*.ron;*.json" asset_selector.filter_glob="*.txt;*.md;*.ron;*.json"
if asset_registry.asset_type_selector == 'AUDIO': if asset_registry.asset_type_selector == 'AUDIO':
asset_selector.filter_glob="*.mp3;*.wav;*.flac" asset_selector.filter_glob="*.mp3;*.wav;*.flac"
add_asset = row.operator(operator="bevyassets.add", text="", icon="ADD") add_asset = row.operator(operator="bevyassets.add", text="", icon="ADD")
add_asset.target_type = target_type add_asset.target_type = target_type
add_asset.target_name = target_name add_asset.target_name = target_name
add_asset.asset_name = asset_registry.asset_name_selector add_asset.asset_name = asset_registry.asset_name_selector
add_asset.asset_type = asset_registry.asset_type_selector add_asset.asset_type = asset_registry.asset_type_selector
add_asset.asset_path = asset_registry.asset_path_selector add_asset.asset_path = asset_registry.asset_path_selector
#assets = json.loads(blueprint.collection["assets"]) if "assets" in blueprint.collection else [] #assets = json.loads(blueprint.collection["assets"]) if "assets" in blueprint.collection else []
for asset in assets: for asset in assets:
@ -33,7 +36,7 @@ def draw_assets(layout, name, title, asset_registry, assets, target_type, target
row.label(text=asset["name"]) row.label(text=asset["name"])
row.label(text=asset["type"]) row.label(text=asset["type"])
row.label(text=asset["path"]) row.label(text=asset["path"])
if not asset["internal"]: if not asset["internal"] and editable:
remove_asset = row.operator(operator="bevyassets.remove", text="", icon="TRASH") remove_asset = row.operator(operator="bevyassets.remove", text="", icon="TRASH")
remove_asset.target_type = target_type remove_asset.target_type = target_type
remove_asset.target_name = target_name remove_asset.target_name = target_name
@ -41,7 +44,9 @@ def draw_assets(layout, name, title, asset_registry, assets, target_type, target
else: else:
row.label(text="") row.label(text="")
class GLTF_PT_auto_export_assets(bpy.types.Panel): return panel
class Blenvy_assets(bpy.types.Panel):
bl_space_type = 'VIEW_3D' bl_space_type = 'VIEW_3D'
bl_region_type = 'UI' bl_region_type = 'UI'
bl_label = "" bl_label = ""
@ -51,29 +56,37 @@ class GLTF_PT_auto_export_assets(bpy.types.Panel):
def poll(cls, context): def poll(cls, context):
return context.window_manager.blenvy.mode == 'ASSETS' return context.window_manager.blenvy.mode == 'ASSETS'
"""def draw_header(self, context):
layout = self.layout
name = ""
if context.collection is not None and context.collection.name == 'Scene Collection':
name = f"WORLD/LEVEL: {context.scene.name}"
else:
name = f"BLUEPRINT: {context.collection.name}"
layout.label(text=f"Assets For {name}")"""
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.
layout.operator(operator="bevyassets.test")
asset_registry = context.window_manager.assets_registry asset_registry = context.window_manager.assets_registry
blueprints_registry = context.window_manager.blueprints_registry
blueprints_registry.add_blueprints_data()
blueprints_data = blueprints_registry.blueprints_data
name = "world" name = "world"
header, panel = layout.box().panel(f"assets{name}", default_closed=False) header, panel = layout.box().panel(f"assets{name}", default_closed=False)
header.label(text="World/Level Assets") header.label(text="World/Level Assets")
settings = {"export_blueprints_path": "blueprints", "export_gltf_extension": ".glb"}
settings = SimpleNamespace(**settings)
if panel: if panel:
for scene in bpy.data.scenes: for scene in bpy.data.scenes:
if scene.name != "Library": # FIXME: hack for testing if scene.name != "Library": # FIXME: hack for testing
assets = json.loads(scene.get('assets')) if 'assets' in scene else [] get_main_scene_assets_tree(scene, blueprints_data, settings)
direct_assets = get_assets(scene)
row = panel.row() row = panel.row()
draw_assets(layout=row, name=scene.name, title=f"{scene.name} Assets", asset_registry=asset_registry, assets=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, assets=direct_assets, target_type="SCENE", target_name=scene.name)
if scene.name in blueprints_data.blueprint_instances_per_main_scene:
for blueprint_name in blueprints_data.blueprint_instances_per_main_scene[scene.name].keys():
blueprint = blueprints_data.blueprints_per_name[blueprint_name]
blueprint_assets = get_assets(blueprint.collection)
if scene_assets_panel:
row = scene_assets_panel.row()
draw_assets(layout=row, name=blueprint.name, title=f"{blueprint.name} Assets", asset_registry=asset_registry, assets=blueprint_assets, target_type="BLUEPRINT", target_name=blueprint.name)

View File

@ -1,19 +0,0 @@
import bpy
import json
# Makes an empty, at the specified location, rotation, scale stores it in existing collection, from https://blender.stackexchange.com/questions/51290/how-to-add-empty-object-not-using-bpy-ops
def make_empty(name, location, rotation, scale, collection):
object_data = None
empty_obj = bpy.data.objects.new( name, object_data )
empty_obj.empty_display_size = 2
empty_obj.empty_display_type = 'PLAIN_AXES'
empty_obj.name = name
empty_obj.location = location
empty_obj.scale = scale
empty_obj.rotation_euler = rotation
collection.objects.link( empty_obj )
#bpy.context.view_layer.update()
return empty_obj

View File

@ -0,0 +1,18 @@
class Blueprint:
def __init__(self, name):
self.name = name
self.local = True
self.marked = False # If marked as asset or with auto_export flag, always export if changed
self.scene = None # Not sure, could be usefull for tracking
self.instances = []
self.objects = []
self.nested_blueprints = []
self.collection = None # should we just sublclass ?
def __repr__(self):
return f'Name: {self.name} Local: {self.local}, Scene: {self.scene}, Instances: {self.instances}, Objects: {self.objects}, nested_blueprints: {self.nested_blueprints}'
def __str__(self):
return f'Name: "{self.name}", Local: {self.local}, Scene: {self.scene}, Instances: {self.instances}, Objects: {self.objects}, nested_blueprints: {self.nested_blueprints}'

View File

@ -0,0 +1,98 @@
import os
import json
import bpy
from ..core.scene_helpers import add_scene_property
def find_blueprints_not_on_disk(blueprints, folder_path, extension):
not_found_blueprints = []
for blueprint in blueprints:
gltf_output_path = os.path.join(folder_path, blueprint.name + extension)
# print("gltf_output_path", gltf_output_path)
found = os.path.exists(gltf_output_path) and os.path.isfile(gltf_output_path)
if not found:
not_found_blueprints.append(blueprint)
return not_found_blueprints
def check_if_blueprint_on_disk(scene_name, folder_path, extension):
gltf_output_path = os.path.join(folder_path, scene_name + extension)
found = os.path.exists(gltf_output_path) and os.path.isfile(gltf_output_path)
print("level", scene_name, "found", found, "path", gltf_output_path)
return found
def inject_export_path_into_internal_blueprints(internal_blueprints, export_blueprints_path, gltf_extension):
for blueprint in internal_blueprints:
blueprint_exported_path = os.path.join(export_blueprints_path, f"{blueprint.name}{gltf_extension}")
blueprint.collection["export_path"] = blueprint_exported_path
def inject_blueprints_list_into_main_scene(scene, blueprints_data, addon_prefs):
export_root_path = getattr(addon_prefs, "export_root_path")
export_assets_path = getattr(addon_prefs,"export_assets_path")
export_levels_path = getattr(addon_prefs,"export_levels_path")
export_blueprints_path = getattr(addon_prefs, "export_blueprints_path")
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
# print("injecting assets/blueprints data into scene")
assets_list_name = f"assets_list_{scene.name}_components"
assets_list_data = {}
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(scene.name, None)
# find all blueprints used in a scene
blueprints_in_scene = []
if blueprint_instance_names_for_scene: # what are the blueprints used in this scene, inject those into the assets list component
children_per_blueprint = {}
for blueprint_name in blueprint_instance_names_for_scene:
blueprint = blueprints_data.blueprints_per_name.get(blueprint_name, None)
if blueprint:
children_per_blueprint[blueprint_name] = blueprint.nested_blueprints
blueprints_in_scene += blueprint.nested_blueprints
assets_list_data["BlueprintsList"] = f"({json.dumps(dict(children_per_blueprint))})"
print(blueprint_instance_names_for_scene)
# add_scene_property(scene, assets_list_name, assets_list_data)
blueprint_assets_list = []
if blueprint_instance_names_for_scene:
for blueprint_name in blueprint_instance_names_for_scene:
blueprint = blueprints_data.blueprints_per_name.get(blueprint_name, None)
if blueprint is not None:
print("BLUEPRINT", blueprint)
blueprint_exported_path = None
if blueprint.local:
blueprint_exported_path = os.path.join(export_blueprints_path, f"{blueprint.name}{export_gltf_extension}")
else:
# get the injected path of the external blueprints
blueprint_exported_path = blueprint.collection['Export_path'] if 'Export_path' in blueprint.collection else None
print("foo", dict(blueprint.collection))
if blueprint_exported_path is not None:
blueprint_assets_list.append({"name": blueprint.name, "path": blueprint_exported_path, "type": "MODEL", "internal": True})
# fetch images/textures
# see https://blender.stackexchange.com/questions/139859/how-to-get-absolute-file-path-for-linked-texture-image
textures = []
for ob in bpy.data.objects:
if ob.type == "MESH":
for mat_slot in ob.material_slots:
if mat_slot.material:
if mat_slot.material.node_tree:
textures.extend([x.image.filepath for x in mat_slot.material.node_tree.nodes if x.type=='TEX_IMAGE'])
print("textures", textures)
assets_list_name = f"assets_{scene.name}"
assets_list_data = {"blueprints": json.dumps(blueprint_assets_list), "sounds":[], "images":[]}
scene["assets"] = json.dumps(blueprint_assets_list)
print("blueprint assets", blueprint_assets_list)
add_scene_property(scene, assets_list_name, assets_list_data)
for blueprint in blueprint_assets_list:
bpy.context.window_manager.assets_registry.add_asset(**blueprint)
def remove_blueprints_list_from_main_scene(scene):
assets_list = None
assets_list_name = f"assets_list_{scene.name}_components"
for object in scene.objects:
if object.name == assets_list_name:
assets_list = object
if assets_list is not None:
bpy.data.objects.remove(assets_list, do_unlink=True)

View File

@ -1,3 +1,4 @@
from types import SimpleNamespace
import bpy import bpy
import json import json
import os import os
@ -6,9 +7,11 @@ from pathlib import Path
from bpy_types import (PropertyGroup) 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 ..gltf_auto_export.helpers.helpers_scenes import get_scenes
from .blueprints_scan import blueprints_scan
# 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 = {}
blueprints_list = [] blueprints_list = []
@ -46,4 +49,12 @@ class BlueprintsRegistry(PropertyGroup):
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):
print("adding blueprints data")
addon_prefs = load_settings(".gltf_auto_export_settings")
print("addon_prefs", addon_prefs)
addon_prefs["export_marked_assets"] = False
addon_prefs = SimpleNamespace(**addon_prefs)
[main_scene_names, level_scenes, library_scene_names, library_scenes] = get_scenes(addon_prefs)
blueprints_data = blueprints_scan(level_scenes, library_scenes, addon_prefs)
self.blueprints_data = blueprints_data

View File

@ -1,43 +1,6 @@
import os
from types import SimpleNamespace from types import SimpleNamespace
import bpy import bpy
from .blueprint import Blueprint
class Blueprint:
def __init__(self, name):
self.name = name
self.local = True
self.marked = False # If marked as asset or with auto_export flag, always export if changed
self.scene = None # Not sure, could be usefull for tracking
self.instances = []
self.objects = []
self.nested_blueprints = []
self.collection = None # should we just sublclass ?
def __repr__(self):
return f'Name: {self.name} Local: {self.local}, Scene: {self.scene}, Instances: {self.instances}, Objects: {self.objects}, nested_blueprints: {self.nested_blueprints}'
def __str__(self):
return f'Name: "{self.name}", Local: {self.local}, Scene: {self.scene}, Instances: {self.instances}, Objects: {self.objects}, nested_blueprints: {self.nested_blueprints}'
def find_blueprints_not_on_disk(blueprints, folder_path, extension):
not_found_blueprints = []
for blueprint in blueprints:
gltf_output_path = os.path.join(folder_path, blueprint.name + extension)
# print("gltf_output_path", gltf_output_path)
found = os.path.exists(gltf_output_path) and os.path.isfile(gltf_output_path)
if not found:
not_found_blueprints.append(blueprint)
return not_found_blueprints
def check_if_blueprint_on_disk(scene_name, folder_path, extension):
gltf_output_path = os.path.join(folder_path, scene_name + extension)
found = os.path.exists(gltf_output_path) and os.path.isfile(gltf_output_path)
print("level", scene_name, "found", found, "path", gltf_output_path)
return found
# blueprints: any collection with either # blueprints: any collection with either
# - an instance # - an instance
@ -147,7 +110,7 @@ def blueprints_scan(main_scenes, library_scenes, addon_prefs):
# #
collections.append(collection) collections.append(collection)
# add any collection that has an instance in the main scenes, but is not present in any of the scenes (IE NON LOCAL/ EXTERNAL) # EXTERNAL COLLECTIONS: add any collection that has an instance in the main scenes, but is not present in any of the scenes (IE NON LOCAL/ EXTERNAL)
for collection_name in external_collection_instances: for collection_name in external_collection_instances:
collection = bpy.data.collections[collection_name] collection = bpy.data.collections[collection_name]
blueprint = Blueprint(collection.name) blueprint = Blueprint(collection.name)
@ -276,126 +239,3 @@ def blueprints_scan(main_scenes, library_scenes, addon_prefs):
} }
return SimpleNamespace(**data) return SimpleNamespace(**data)
import json
from .object_makers import (make_empty)
def add_scene_property(scene, property_name, property_data):
root_collection = scene.collection
scene_property = None
for object in scene.objects:
if object.name == property_name:
scene_property = object
break
if scene_property is None:
scene_property = make_empty(property_name, [0,0,0], [0,0,0], [0,0,0], root_collection)
for key in property_data.keys():
scene_property[key] = property_data[key]
def inject_blueprints_list_into_main_scene(scene, blueprints_data, addon_prefs):
export_root_path = getattr(addon_prefs, "export_root_path")
export_assets_path = getattr(addon_prefs,"export_assets_path")
export_levels_path = getattr(addon_prefs,"export_levels_path")
export_blueprints_path = getattr(addon_prefs, "export_blueprints_path")
export_gltf_extension = getattr(addon_prefs, "export_gltf_extension")
# print("injecting assets/blueprints data into scene")
assets_list_name = f"assets_list_{scene.name}_components"
assets_list_data = {}
# FIXME: temporary hack
for blueprint in blueprints_data.blueprints:
bpy.context.window_manager.blueprints_registry.add_blueprint(blueprint)
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(scene.name, None)
# find all blueprints used in a scene
blueprints_in_scene = []
if blueprint_instance_names_for_scene: # what are the blueprints used in this scene, inject those into the assets list component
children_per_blueprint = {}
for blueprint_name in blueprint_instance_names_for_scene:
blueprint = blueprints_data.blueprints_per_name.get(blueprint_name, None)
if blueprint:
children_per_blueprint[blueprint_name] = blueprint.nested_blueprints
blueprints_in_scene += blueprint.nested_blueprints
assets_list_data["BlueprintsList"] = f"({json.dumps(dict(children_per_blueprint))})"
print(blueprint_instance_names_for_scene)
add_scene_property(scene, assets_list_name, assets_list_data)
blueprints_path_full = os.path.join(export_assets_path, export_blueprints_path)
relative_blueprints_path = os.path.relpath(export_blueprints_path, export_root_path)
blueprint_assets_list = []
if blueprint_instance_names_for_scene:
for blueprint_name in blueprint_instance_names_for_scene:
blueprint = blueprints_data.blueprints_per_name.get(blueprint_name, None)
if blueprint is not None:
print("BLUEPRINT", blueprint)
blueprint_exported_path = None
if blueprint.local:
blueprint_exported_path = os.path.join(export_blueprints_path, f"{blueprint.name}{export_gltf_extension}")
else:
# get the injected path of the external blueprints
blueprint_exported_path = blueprint.collection['Export_path'] if 'Export_path' in blueprint.collection else None
print("foo", dict(blueprint.collection))
if blueprint_exported_path is not None:
blueprint_assets_list.append({"name": blueprint.name, "path": blueprint_exported_path, "type": "MODEL", "internal": True})
# fetch images/textures
# see https://blender.stackexchange.com/questions/139859/how-to-get-absolute-file-path-for-linked-texture-image
textures = []
for ob in bpy.data.objects:
if ob.type == "MESH":
for mat_slot in ob.material_slots:
if mat_slot.material:
if mat_slot.material.node_tree:
textures.extend([x.image.filepath for x in mat_slot.material.node_tree.nodes if x.type=='TEX_IMAGE'])
print("textures", textures)
assets_list_name = f"assets_{scene.name}"
assets_list_data = {"blueprints": json.dumps(blueprint_assets_list), "sounds":[], "images":[]}
scene["assets"] = json.dumps(blueprint_assets_list)
print("blueprint assets", blueprint_assets_list)
add_scene_property(scene, assets_list_name, assets_list_data)
for blueprint in blueprint_assets_list:
bpy.context.window_manager.assets_registry.add_asset(**blueprint)
'''root_collection = scene.collection
assets_list = None
for object in scene.objects:
if object.name == assets_list_name:
assets_list = object
break
if assets_list is None:
assets_list = make_empty(assets_list_name, [0,0,0], [0,0,0], [0,0,0], root_collection)
blueprint_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(scene.name, None)
# find all blueprints used in a scene
if blueprint_names_for_scene: # what are the blueprints used in this scene, inject those into the assets list component
children_per_blueprint = {}
for blueprint_name in blueprint_names_for_scene:
blueprint = blueprints_data.blueprints_per_name.get(blueprint_name, None)
if blueprint:
children_per_blueprint[blueprint_name] = blueprint.nested_blueprints
assets_list["BlueprintsList"] = f"({json.dumps(dict(children_per_blueprint))})"'''
def remove_blueprints_list_from_main_scene(scene):
assets_list = None
assets_list_name = f"assets_list_{scene.name}_components"
for object in scene.objects:
if object.name == assets_list_name:
assets_list = object
if assets_list is not None:
bpy.data.objects.remove(assets_list, do_unlink=True)

View File

@ -26,8 +26,11 @@ class GLTF_PT_auto_export_blueprints_list(bpy.types.Panel):
row.label(text=blueprint.name) row.label(text=blueprint.name)
if blueprint.local: if blueprint.local:
select_blueprint = row.operator(operator="blueprint.select", text="", icon="RESTRICT_SELECT_OFF") select_blueprint = row.operator(operator="blueprint.select", text="", icon="RESTRICT_SELECT_OFF")
select_blueprint.blueprint_collection_name = blueprint.collection.name
if blueprint.collection and blueprint.collection.name:
select_blueprint.blueprint_collection_name = blueprint.collection.name
select_blueprint.blueprint_scene_name = blueprint.scene.name select_blueprint.blueprint_scene_name = blueprint.scene.name
assets = json.loads(blueprint.collection["assets"]) if "assets" in blueprint.collection else [] assets = json.loads(blueprint.collection["assets"]) if "assets" in blueprint.collection else []
@ -35,8 +38,6 @@ class GLTF_PT_auto_export_blueprints_list(bpy.types.Panel):
draw_assets(layout=layout, name=blueprint.name, title="Assets", asset_registry=asset_registry, assets=assets, target_type="BLUEPRINT", target_name=blueprint.name) draw_assets(layout=layout, name=blueprint.name, title="Assets", asset_registry=asset_registry, assets=assets, target_type="BLUEPRINT", target_name=blueprint.name)
else: else:
assets = json.loads(blueprint.collection["assets"]) if "assets" in blueprint.collection else []
draw_assets(layout=layout, name=blueprint.name, title="Assets", asset_registry=asset_registry, assets=assets, target_type="BLUEPRINT", target_name=blueprint.name, editable=False)
row.label(text="External") row.label(text="External")
for collection in bpy.context.window_manager.exportedCollections:
row = layout.row()
row.label(text=collection.name)

View File

@ -1,6 +1,6 @@
import bpy import bpy
from bpy_types import (PropertyGroup) from bpy_types import (PropertyGroup)
from bpy.props import (EnumProperty, PointerProperty) from bpy.props import (EnumProperty, PointerProperty, StringProperty)
class BlenvyManager(PropertyGroup): class BlenvyManager(PropertyGroup):
@ -15,6 +15,38 @@ class BlenvyManager(PropertyGroup):
) )
) # type: ignore ) # type: ignore
export_root_path: StringProperty(
name = "Project Root Path",
description="The root folder of your (Bevy) project (not assets!)",
default='../'
) # type: ignore
export_assets_path: StringProperty(
name='Export folder',
description='The root folder for all exports(relative to the root folder/path) Defaults to "assets" ',
default='./assets',
options={'HIDDEN'}
) # type: ignore
export_blueprints_path: StringProperty(
name='Blueprints path',
description='path to export the blueprints to (relative to the assets folder)',
default='blueprints',
) # type: ignore
export_levels_path: StringProperty(
name='Levels path',
description='path to export the levels (main scenes) to (relative to the assets folder)',
default='levels',
) # type: ignore
export_materials_path: StringProperty(
name='Materials path',
description='path to export the materials libraries to (relative to the assets folder)',
default='materials',
) # type: ignore
@classmethod @classmethod
def register(cls): def register(cls):
bpy.types.WindowManager.blenvy = PointerProperty(type=BlenvyManager) bpy.types.WindowManager.blenvy = PointerProperty(type=BlenvyManager)

View File

@ -45,3 +45,26 @@ def make_cube(name, location=[0,0,0], rotation=[0,0,0], scale=[1,1,1], collectio
if collection != None: if collection != None:
collection.objects.link( new_object ) collection.objects.link( new_object )
return new_object return new_object
"""import bpy
import json
# Makes an empty, at the specified location, rotation, scale stores it in existing collection, from https://blender.stackexchange.com/questions/51290/how-to-add-empty-object-not-using-bpy-ops
def make_empty(name, location, rotation, scale, collection):
object_data = None
empty_obj = bpy.data.objects.new( name, object_data )
empty_obj.empty_display_size = 2
empty_obj.empty_display_type = 'PLAIN_AXES'
empty_obj.name = name
empty_obj.location = location
empty_obj.scale = scale
empty_obj.rotation_euler = rotation
collection.objects.link( empty_obj )
#bpy.context.view_layer.update()
return empty_obj
"""

View File

@ -0,0 +1,11 @@
import bpy
import os
def absolute_path_from_blend_file(path):
# path to the current blend file
blend_file_path = bpy.data.filepath
# Get the folder
blend_file_folder_path = os.path.dirname(blend_file_path)
# absolute path
return os.path.join(blend_file_folder_path, path)

View File

@ -0,0 +1,27 @@
def add_scene_property(scene, scene_component_name, property_data, limit_to=None):
root_collection = scene.collection
scene_property = None
for object in scene.objects:
if object.name == scene_component_name:
scene_property = object
break
if scene_property is None:
scene_property = make_empty(scene_component_name, [0,0,0], [0,0,0], [0,0,0], root_collection)
for key in property_data.keys():
if limit_to is not None:
if key in limit_to:
scene_property[key] = property_data[key]
else:
scene_property[key] = property_data[key]
# compatibility helper until we land gltf_extras at the scene level for Bevy
# it copies a custom property into an __components object's properties
def copy_scene_or_collection_property_to_object_component(scene, property_name, target_object_name):
property_value = scene.get(property_name, None)
if property_value is not None:
property_data = {}
property_data[property_name] = property_value
add_scene_property(scene=scene, scene_component_name=target_object_name, property_data=property_data)

View File

@ -3,6 +3,20 @@ from ..settings import load_settings
###################################################### ######################################################
## ui logic & co ## ui logic & co
def draw_folder_browser(layout, label, prop_origin, target_property):
row = layout.row()
row.label(text=label)
'''box = row.box()
box.scale_y = 0.5
box.label(text=value)'''
col = row.column()
col.enabled = False
col.prop(prop_origin, target_property, text="")
folder_selector = row.operator("generic.open_folderbrowser", icon="FILE_FOLDER", text="")
folder_selector.target_property = target_property #"export_root_path"
# side panel # side panel
class BLENVY_PT_SidePanel(bpy.types.Panel): class BLENVY_PT_SidePanel(bpy.types.Panel):
@ -66,6 +80,21 @@ class BLENVY_PT_SidePanel(bpy.types.Panel):
layout.label(text="Library scene active: "+ str(library_scene_active)) layout.label(text="Library scene active: "+ str(library_scene_active))
layout.label(text=blenvy.mode)""" layout.label(text=blenvy.mode)"""
if blenvy.mode == "SETTINGS":
header, panel = layout.panel("auto_export", default_closed=False)
header.label(text="Common")
if panel:
row = panel.row()
draw_folder_browser(layout=row, label="Root Folder", prop_origin=blenvy, target_property="export_root_path")
row = panel.row()
draw_folder_browser(layout=row, label="Assets Folder", prop_origin=blenvy, target_property="export_assets_path")
row = panel.row()
draw_folder_browser(layout=row, label="Blueprints Folder", prop_origin=blenvy, target_property="export_blueprints_path")
row = panel.row()
draw_folder_browser(layout=row, label="Levels Folder", prop_origin=blenvy, target_property="export_levels_path")
row = panel.row()
draw_folder_browser(layout=row, label="Materials Folder", prop_origin=blenvy, target_property="export_materials_path")
"""if blenvy.mode == "SETTINGS": """if blenvy.mode == "SETTINGS":
header, panel = layout.panel("auto_export", default_closed=False) header, panel = layout.panel("auto_export", default_closed=False)
header.label(text="Auto Export") header.label(text="Auto Export")

View File

@ -1,12 +1,9 @@
import copy
import json
import os import os
from types import SimpleNamespace
import bpy import bpy
import traceback import traceback
from ..helpers.helpers_scenes import (get_scenes, )
from .preferences import AutoExportGltfAddonPreferences
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
@ -14,13 +11,12 @@ from .get_standard_exporter_settings import get_standard_exporter_settings
from .export_main_scenes import export_main_scene from .export_main_scenes import export_main_scene
from .export_blueprints import export_blueprints from .export_blueprints import export_blueprints
from ..helpers.helpers_scenes import (get_scenes, )
from ..helpers.helpers_blueprints import blueprints_scan
from ..modules.export_materials import cleanup_materials, export_materials from ..modules.export_materials import cleanup_materials, export_materials
from ..modules.bevy_scene_components import remove_scene_components, upsert_scene_components from ..modules.bevy_scene_components import remove_scene_components, upsert_scene_components
from ...blueprints.blueprints_scan import blueprints_scan
from ...blueprints.blueprint_helpers import inject_export_path_into_internal_blueprints
"""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, addon_prefs):
@ -68,19 +64,24 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
addon_prefs.export_assets_path_full = os.path.join(blend_file_path, export_root_path, export_assets_path) addon_prefs.export_assets_path_full = os.path.join(blend_file_path, export_root_path, export_assets_path)
addon_prefs.export_blueprints_path_full = os.path.join(addon_prefs.export_assets_path_full, getattr(addon_prefs,"export_blueprints_path")) addon_prefs.export_blueprints_path_full = os.path.join(addon_prefs.export_assets_path_full, getattr(addon_prefs,"export_blueprints_path"))
addon_prefs.export_levels_path_full = os.path.join(addon_prefs.export_assets_path_full, getattr(addon_prefs,"export_levels_path")) addon_prefs.export_levels_path_full = os.path.join(addon_prefs.export_assets_path_full, getattr(addon_prefs,"export_levels_path"))
addon_prefs.export_materials_path = os.path.join(addon_prefs.export_assets_path_full, getattr(addon_prefs,"export_materials_path")) addon_prefs.export_materials_path_full = os.path.join(addon_prefs.export_assets_path_full, getattr(addon_prefs,"export_materials_path"))
addon_prefs.export_gltf_extension = gltf_extension addon_prefs.export_gltf_extension = gltf_extension
[main_scene_names, level_scenes, library_scene_names, library_scenes] = get_scenes(addon_prefs) [main_scene_names, level_scenes, library_scene_names, library_scenes] = get_scenes(addon_prefs)
print("main scenes", main_scene_names, "library_scenes", library_scene_names)
print("export_assets_path", export_assets_path)
blueprints_data = blueprints_scan(level_scenes, library_scenes, addon_prefs) 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
export_blueprints_path = getattr(addon_prefs,"export_blueprints_path")
inject_export_path_into_internal_blueprints(internal_blueprints=blueprints_data.internal_blueprints, export_blueprints_path=export_blueprints_path, gltf_extension=gltf_extension)
for blueprint in blueprints_data.blueprints:
bpy.context.window_manager.blueprints_registry.add_blueprint(blueprint)
bpy.context.window_manager.blueprints_registry.add_blueprints_data()
if export_scene_settings: if export_scene_settings:
# inject/ update scene components # inject/ update scene components
upsert_scene_components(level_scenes) upsert_scene_components(level_scenes)
@ -101,7 +102,7 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
# 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, blend_file_path, addon_prefs) export_materials(blueprints_data.blueprint_names, library_scenes, addon_prefs)
# 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)
@ -139,11 +140,7 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
do_export_library_scene = not export_change_detection or changed_export_parameters or len(blueprints_to_export) > 0 do_export_library_scene = not export_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")
# we only want to go through the library scenes where our blueprints to export are present export_blueprints(blueprints_to_export, addon_prefs, blueprints_data)
"""for (scene_name, blueprints_to_export) in blueprints_per_scene.items():
print(" exporting blueprints from scene:", scene_name)
print(" blueprints to export", blueprints_to_export)"""
export_blueprints(blueprints_to_export, blend_file_path, addon_prefs, 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

View File

@ -7,7 +7,7 @@ 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, blend_file_path, addon_prefs, blueprints_data): def export_blueprints(blueprints, addon_prefs, blueprints_data):
export_blueprints_path_full = getattr(addon_prefs,"export_blueprints_path_full") export_blueprints_path_full = getattr(addon_prefs,"export_blueprints_path_full")
gltf_export_preferences = generate_gltf_export_preferences(addon_prefs) gltf_export_preferences = generate_gltf_export_preferences(addon_prefs)
@ -26,6 +26,7 @@ def export_blueprints(blueprints, blend_file_path, addon_prefs, blueprints_data)
export_settings['export_materials'] = 'PLACEHOLDER' export_settings['export_materials'] = 'PLACEHOLDER'
collection = bpy.data.collections[blueprint.name] collection = bpy.data.collections[blueprint.name]
# do the actual export
generate_and_export( generate_and_export(
addon_prefs, addon_prefs,
temp_scene_name=TEMPSCENE_PREFIX+collection.name, temp_scene_name=TEMPSCENE_PREFIX+collection.name,

View File

@ -7,7 +7,7 @@ from ..helpers.generate_and_export import generate_and_export
from .export_gltf import (generate_gltf_export_preferences, export_gltf) from .export_gltf import (generate_gltf_export_preferences, export_gltf)
from ..modules.bevy_dynamic import is_object_dynamic, is_object_static 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 ..helpers.helpers_blueprints 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, addon_prefs, blueprints_data):
gltf_export_preferences = generate_gltf_export_preferences(addon_prefs) gltf_export_preferences = generate_gltf_export_preferences(addon_prefs)

View File

@ -1,7 +1,7 @@
import bpy import bpy
import os import os
from ..helpers.helpers_scenes import (get_scenes, ) from ..helpers.helpers_scenes import (get_scenes, )
from ..helpers.helpers_blueprints 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, addon_prefs):

View File

@ -1,5 +1,5 @@
import bpy import bpy
from ..helpers.helpers_blueprints import check_if_blueprint_on_disk from ...blueprints.blueprint_helpers import check_if_blueprint_on_disk
from ..helpers.helpers_scenes import (get_scenes, ) 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

View File

@ -1,12 +1,12 @@
import json import json
import bpy import bpy
from .object_makers import (make_empty) from ...core.object_makers import (make_empty)
# these are mostly for when using this add-on together with the bevy_components add-on # these are mostly for when using this add-on together with the bevy_components add-on
custom_properties_to_filter_out = ['_combine', 'template', 'components_meta'] custom_properties_to_filter_out = ['_combine', 'template', 'components_meta']
def is_component_valid(object, component_name): def is_component_valid_and_enabled(object, component_name):
if "components_meta" in object or hasattr(object, "components_meta"): if "components_meta" in object or hasattr(object, "components_meta"):
target_components_metadata = object.components_meta.components target_components_metadata = object.components_meta.components
component_meta = next(filter(lambda component: component["long_name"] == component_name, target_components_metadata), None) component_meta = next(filter(lambda component: component["long_name"] == component_name, target_components_metadata), None)
@ -18,7 +18,7 @@ def remove_unwanted_custom_properties(object):
to_remove = [] to_remove = []
component_names = list(object.keys()) # to avoid 'IDPropertyGroup changed size during iteration' issues component_names = list(object.keys()) # to avoid 'IDPropertyGroup changed size during iteration' issues
for component_name in component_names: for component_name in component_names:
if not is_component_valid(object, component_name): if not is_component_valid_and_enabled(object, component_name):
to_remove.append(component_name) to_remove.append(component_name)
for cp in custom_properties_to_filter_out + to_remove: for cp in custom_properties_to_filter_out + to_remove:
if cp in object: if cp in object:
@ -118,7 +118,7 @@ def duplicate_object(object, parent, combine_mode, destination_collection, bluep
# we copy custom properties over from our original object to our empty # we copy custom properties over from our original object to our empty
for component_name, component_value in object.items(): for component_name, component_value in object.items():
if component_name not in custom_properties_to_filter_out and is_component_valid(object, component_name): #copy only valid properties if component_name not in custom_properties_to_filter_out and is_component_valid_and_enabled(object, component_name): #copy only valid properties
empty_obj[component_name] = component_value empty_obj[component_name] = component_value
copy = empty_obj copy = empty_obj
else: else:
@ -174,9 +174,6 @@ def copy_hollowed_collection_into(source_collection, destination_collection, par
blueprints_data = blueprints_data, blueprints_data = blueprints_data,
addon_prefs=addon_prefs addon_prefs=addon_prefs
) )
return {} return {}
# clear & remove "hollow scene" # clear & remove "hollow scene"

View File

@ -1,6 +1,6 @@
import bpy import bpy
from ..helpers.object_makers import make_empty from ...core.object_makers import make_empty
# TODO: replace this with placing scene level custom properties once support for that has been added to bevy_gltf # TODO: replace this with placing scene level custom properties once support for that has been added to bevy_gltf
def upsert_scene_components(main_scenes): def upsert_scene_components(main_scenes):

View File

@ -3,10 +3,9 @@ import bpy
from pathlib import Path from pathlib import Path
from ..helpers.generate_and_export import generate_and_export from ..helpers.generate_and_export import generate_and_export
from ..helpers.helpers_collections import (traverse_tree) from ..helpers.helpers_collections import (traverse_tree)
from ..auto_export.export_gltf import (export_gltf, generate_gltf_export_preferences) from ..auto_export.export_gltf import (export_gltf, generate_gltf_export_preferences)
from ..helpers.object_makers import make_cube from ...core.object_makers import make_cube
# get materials per object, and injects the materialInfo component # get materials per object, and injects the materialInfo component
def get_materials(object): def get_materials(object):
@ -90,11 +89,9 @@ 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, folder_path, addon_prefs): def export_materials(collections, library_scenes, addon_prefs):
gltf_export_preferences = generate_gltf_export_preferences(addon_prefs) gltf_export_preferences = generate_gltf_export_preferences(addon_prefs)
export_materials_path = getattr(addon_prefs,"export_materials_path") export_materials_path_full = getattr(addon_prefs,"export_materials_path_full")
export_root_path = getattr(addon_prefs, "export_root_path")
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
@ -108,7 +105,7 @@ def export_materials(collections, library_scenes, folder_path, addon_prefs):
'export_apply':True 'export_apply':True
} }
gltf_output_path = os.path.join(export_root_path, export_materials_path, current_project_name + "_materials_library") gltf_output_path = os.path.join(export_materials_path_full, current_project_name + "_materials_library")
print(" exporting Materials to", gltf_output_path, ".gltf/glb") print(" exporting Materials to", gltf_output_path, ".gltf/glb")

View File

@ -13,7 +13,6 @@ class GLTF_PT_auto_export_SidePanel(bpy.types.Panel):
bl_context = "objectmode" bl_context = "objectmode"
bl_parent_id = "BLENVY_PT_SidePanel" bl_parent_id = "BLENVY_PT_SidePanel"
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return context.window_manager.blenvy.mode == 'SETTINGS' return context.window_manager.blenvy.mode == 'SETTINGS'
@ -40,26 +39,6 @@ class GLTF_PT_auto_export_SidePanel(bpy.types.Panel):
op = layout.operator("EXPORT_SCENES_OT_auto_gltf", text="Auto Export Settings") op = layout.operator("EXPORT_SCENES_OT_auto_gltf", text="Auto Export Settings")
op.auto_export = True op.auto_export = True
class GLTF_PT_auto_export_changes_list(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_label = "Changes per scene since last save "
bl_parent_id = "GLTF_PT_auto_export_SidePanel"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
#if "auto_export_tracker" in context.window_manager:
changed_objects_per_scene = context.window_manager.auto_export_tracker.changed_objects_per_scene
for scene_name in changed_objects_per_scene:
layout.label(text=f'{scene_name}')
for object_name in list(changed_objects_per_scene[scene_name].keys()):
row = layout.row()
row.label(text=f' {object_name}')
# main ui in the file => export # main ui in the file => export
class GLTF_PT_auto_export_main(bpy.types.Panel): class GLTF_PT_auto_export_main(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER' bl_space_type = 'FILE_BROWSER'

View File

@ -130,7 +130,7 @@ class OT_OpenFolderbrowser(Operator, ImportHelper):
print("blend_file_folder_path", blend_file_folder_path) print("blend_file_folder_path", blend_file_folder_path)
print("new_path", self.directory, self.target_property, operator) print("new_path", self.directory, self.target_property, operator)
path_names = ['export_assets_path', 'export_blueprints_path', 'export_levels_path', 'export_materials_path'] asset_path_names = ['export_assets_path', 'export_blueprints_path', 'export_levels_path', 'export_materials_path']
export_root_path = operator.export_root_path export_root_path = operator.export_root_path
export_assets_path = operator.export_assets_path export_assets_path = operator.export_assets_path
#export_root_path_absolute = os.path.join(blend_file_folder_path, export_root_path) #export_root_path_absolute = os.path.join(blend_file_folder_path, export_root_path)
@ -142,10 +142,11 @@ class OT_OpenFolderbrowser(Operator, ImportHelper):
new_root_path_relative = os.path.relpath(new_path, blend_file_folder_path) new_root_path_relative = os.path.relpath(new_path, blend_file_folder_path)
print("changing root new_path to", self.directory, blend_file_folder_path, new_root_path_relative) print("changing root new_path to", self.directory, blend_file_folder_path, new_root_path_relative)
# we need to change all other relative paths before setting the new absolute path # we need to change all other relative paths before setting the new absolute path
for path_name in path_names: for path_name in asset_path_names:
# get absolute path # get current relative path
relative_path = getattr(operator, path_name, None) relative_path = getattr(operator, path_name, None)
if relative_path is not None: if relative_path is not None:
# and now get absolute path of asset_path
absolute_path = os.path.join(export_assets_path_full, relative_path) absolute_path = os.path.join(export_assets_path_full, relative_path)
print("absolute path for", path_name, absolute_path) print("absolute path for", path_name, absolute_path)
relative_path = os.path.relpath(absolute_path, new_path) relative_path = os.path.relpath(absolute_path, new_path)
@ -153,7 +154,8 @@ class OT_OpenFolderbrowser(Operator, ImportHelper):
# store the root path as relative to the current blend file # store the root path as relative to the current blend file
setattr(operator, target_path_name, new_path) setattr(operator, target_path_name, new_path)
elif target_path_name == 'export_assets_path':
pass
else: else:
relative_path = os.path.relpath(new_path, export_assets_path_full) relative_path = os.path.relpath(new_path, export_assets_path_full)
setattr(operator, target_path_name, relative_path) setattr(operator, target_path_name, relative_path)

View File

@ -67,7 +67,3 @@ Change storage of 'blueprint' assets : (from BlueprintsList)
- make asset storage generic enough to allow adding additional asset types - make asset storage generic enough to allow adding additional asset types
- get inspired by bevy_asset_loader ? - get inspired by bevy_asset_loader ?
Assets:
- [ ] store assets
- [ ] per main scene for level/world assets
- [ ] per blueprint for blueprint in lib scene

View File

@ -308,7 +308,6 @@ def inject_blueprints_list_into_main_scene(scene, blueprints_data, addon_prefs):
assets_list_name = f"assets_list_{scene.name}_components" assets_list_name = f"assets_list_{scene.name}_components"
assets_list_data = {} assets_list_data = {}
# FIXME: temporary hack # FIXME: temporary hack
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)
@ -325,7 +324,7 @@ def inject_blueprints_list_into_main_scene(scene, blueprints_data, addon_prefs):
blueprints_in_scene += blueprint.nested_blueprints blueprints_in_scene += blueprint.nested_blueprints
assets_list_data["BlueprintsList"] = f"({json.dumps(dict(children_per_blueprint))})" assets_list_data["BlueprintsList"] = f"({json.dumps(dict(children_per_blueprint))})"
print(blueprint_instance_names_for_scene) print(blueprint_instance_names_for_scene)
add_scene_property(scene, assets_list_name, assets_list_data) #add_scene_property(scene, assets_list_name, assets_list_data)
relative_blueprints_path = os.path.relpath(export_blueprints_path, export_root_folder) relative_blueprints_path = os.path.relpath(export_blueprints_path, export_root_folder)
@ -346,7 +345,6 @@ def inject_blueprints_list_into_main_scene(scene, blueprints_data, addon_prefs):
if blueprint_exported_path is not None: if blueprint_exported_path is not None:
blueprint_assets_list.append({"name": blueprint.name, "path": blueprint_exported_path, "type": "MODEL", "internal": True}) blueprint_assets_list.append({"name": blueprint.name, "path": blueprint_exported_path, "type": "MODEL", "internal": True})
# fetch images/textures # fetch images/textures
# see https://blender.stackexchange.com/questions/139859/how-to-get-absolute-file-path-for-linked-texture-image # see https://blender.stackexchange.com/questions/139859/how-to-get-absolute-file-path-for-linked-texture-image
textures = [] textures = []