feat(blenvy):

* improvements to assets ui
 * assets ui now specific to per level data, not based on selection anymore
 * blueprints ui now presents assets per blueprint
 * a lot of tweaks & improvements in the areas above
This commit is contained in:
kaosat.dev 2024-05-13 23:36:13 +02:00
parent f3dbf76ce6
commit b8a7eba71d
11 changed files with 185 additions and 90 deletions

View File

@ -53,7 +53,7 @@ from .gltf_auto_export.ui.operators import (OT_OpenFolderbrowser, SCENES_LIST_OT
# asset management # asset management
from .assets.ui import GLTF_PT_auto_export_assets from .assets.ui import GLTF_PT_auto_export_assets
from .assets.assets_registry import AssetsRegistry from .assets.assets_registry import AssetsRegistry
from .assets.operators import OT_add_bevy_asset, OT_remove_bevy_asset from .assets.operators import OT_Add_asset_filebrowser, OT_add_bevy_asset, OT_remove_bevy_asset
# blueprints management # blueprints management
from .blueprints.ui import GLTF_PT_auto_export_blueprints_list from .blueprints.ui import GLTF_PT_auto_export_blueprints_list
@ -143,6 +143,7 @@ classes = [
AssetsRegistry, AssetsRegistry,
OT_add_bevy_asset, OT_add_bevy_asset,
OT_remove_bevy_asset, OT_remove_bevy_asset,
OT_Add_asset_filebrowser,
GLTF_PT_auto_export_assets, GLTF_PT_auto_export_assets,
BlueprintsRegistry, BlueprintsRegistry,

View File

@ -68,6 +68,7 @@ class AssetsRegistry(PropertyGroup):
('MODEL', "Model", ""), ('MODEL', "Model", ""),
('AUDIO', "Audio", ""), ('AUDIO', "Audio", ""),
('IMAGE', "Image", ""), ('IMAGE', "Image", ""),
('TEXT', "Text", ""),
) )
) # type: ignore ) # type: ignore

View File

@ -2,7 +2,9 @@ import os
import json import json
import bpy import bpy
from bpy_types import (Operator) from bpy_types import (Operator)
from bpy.props import (StringProperty, EnumProperty) from bpy.props import (BoolProperty, StringProperty, EnumProperty)
from ..settings import load_settings
class OT_add_bevy_asset(Operator): class OT_add_bevy_asset(Operator):
"""Add asset""" """Add asset"""
@ -20,6 +22,7 @@ class OT_add_bevy_asset(Operator):
('MODEL', "Model", ""), ('MODEL', "Model", ""),
('AUDIO', "Audio", ""), ('AUDIO', "Audio", ""),
('IMAGE', "Image", ""), ('IMAGE', "Image", ""),
('TEXT', "Text", ""),
) )
) # type: ignore ) # type: ignore
@ -29,26 +32,39 @@ class OT_add_bevy_asset(Operator):
subtype='FILE_PATH' subtype='FILE_PATH'
) # type: ignore ) # type: ignore
def execute(self, context): # what are we targetting
assets_list = [] target_type: EnumProperty(
blueprint_assets = False name="target type",
if context.collection is not None and context.collection.name == 'Scene Collection': description="type of the target: scene or blueprint to add an asset to",
assets_list = json.loads(context.scene.get('assets')) items=(
blueprint_assets = False ('SCENE', "Scene", ""),
else: ('BLUEPRINT', "Blueprint", ""),
if 'assets' in context.collection: ),
assets_list = json.loads(context.collection.get('assets')) ) # type: ignore
blueprint_assets = True
in_list = [asset for asset in assets_list if (asset["path"] == self.asset_path)] target_name: StringProperty(
name="target name",
description="name of the target blueprint or scene to add asset to"
) # type: ignore
def execute(self, context):
assets = []
blueprint_assets = self.target_type == 'BLUEPRINT'
print("FOOO", self.target_name, self.target_type)
if blueprint_assets:
assets = json.loads(bpy.data.collections[self.target_name].get('assets')) if 'assets' in bpy.data.collections[self.target_name] else []
else:
assets = json.loads(bpy.data.scenes[self.target_name].get('assets')) if 'assets' in bpy.data.scenes[self.target_name] else []
in_list = [asset for asset in assets if (asset["path"] == self.asset_path)]
in_list = len(in_list) > 0 in_list = len(in_list) > 0
if not in_list: if not in_list:
assets_list.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})
if blueprint_assets: if blueprint_assets:
context.collection["assets"] = json.dumps(assets_list) bpy.data.collections[self.target_name]["assets"] = json.dumps(assets)
else: else:
context.scene["assets"] = json.dumps(assets_list) bpy.data.scenes[self.target_name]["assets"] = json.dumps(assets)
#context.window_manager.assets_registry.add_asset(self.asset_name, self.asset_type, self.asset_path, False) #context.window_manager.assets_registry.add_asset(self.asset_name, self.asset_type, self.asset_path, False)
return {'FINISHED'} return {'FINISHED'}
@ -65,21 +81,72 @@ class OT_remove_bevy_asset(Operator):
subtype='FILE_PATH' subtype='FILE_PATH'
) # type: ignore ) # type: ignore
def execute(self, context):
assets_list = []
blueprint_assets = False
if context.collection is not None and context.collection.name == 'Scene Collection':
assets_list = json.loads(context.scene.get('assets'))
blueprint_assets = False
else:
if 'assets' in context.collection:
assets_list = json.loads(context.collection.get('assets'))
blueprint_assets = True
assets_list = [asset for asset in assets_list if (asset["path"] != self.asset_path)] clear_all: BoolProperty (
name="clear all assets",
description="clear all assets",
default=False
) # type: ignore
# what are we targetting
target_type: EnumProperty(
name="target type",
description="type of the target: scene or blueprint to add an asset to",
items=(
('SCENE', "Scene", ""),
('BLUEPRINT', "Blueprint", ""),
),
) # type: ignore
target_name: StringProperty(
name="target name",
description="name of the target blueprint or scene to add asset to"
) # type: ignore
def execute(self, context):
assets = []
blueprint_assets = self.target_type == 'BLUEPRINT'
if blueprint_assets: if blueprint_assets:
context.collection["assets"] = json.dumps(assets_list) assets = json.loads(bpy.data.collections[self.target_name].get('assets')) if 'assets' in bpy.data.collections[self.target_name] else []
else: else:
context.scene["assets"] = json.dumps(assets_list) assets = json.loads(bpy.data.scenes[self.target_name].get('assets')) if 'assets' in bpy.data.scenes[self.target_name] else []
assets = [asset for asset in assets if (asset["path"] != self.asset_path)]
if blueprint_assets:
bpy.data.collections[self.target_name]["assets"] = json.dumps(assets)
else:
bpy.data.scenes[self.target_name]["assets"] = json.dumps(assets)
#context.window_manager.assets_registry.remove_asset(self.asset_path) #context.window_manager.assets_registry.remove_asset(self.asset_path)
return {'FINISHED'} return {'FINISHED'}
import os
from bpy_extras.io_utils import ImportHelper
class OT_Add_asset_filebrowser(Operator, ImportHelper):
"""Browse for asset files"""
bl_idname = "asset.open_filebrowser"
bl_label = "Select asset file"
# Define this to tell 'fileselect_add' that we want a directoy
filepath: bpy.props.StringProperty(
name="asset Path",
description="selected file",
subtype='FILE_PATH',
) # type: ignore
# Filters files
filter_glob: StringProperty(options={'HIDDEN'}, default='*.jpg;*.jpeg;*.png;*.bmp') # type: ignore
def execute(self, context):
current_auto_settings = load_settings(".gltf_auto_export_settings")
export_root_folder = current_auto_settings.get("export_root_folder")
asset_path = os.path.relpath(self.filepath, export_root_folder)
assets_registry = context.window_manager.assets_registry
assets_registry.asset_path_selector = asset_path
print("SELECTED ASSET PATH", asset_path)
return {'FINISHED'}

View File

@ -1,66 +1,79 @@
import bpy import bpy
import json import json
def draw_assets(layout, name, title, asset_registry, assets, target_type, target_name):
header, panel = layout.box().panel(f"assets{name}", default_closed=False)
header.label(text=title)
if panel:
row = panel.row()
row.prop(asset_registry, "asset_name_selector", text="")
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':
asset_selector.filter_glob = '*.jpg;*.jpeg;*.png;*.bmp'
if asset_registry.asset_type_selector == 'MODEL':
asset_selector.filter_glob="*.glb;*.gltf"
if asset_registry.asset_type_selector == 'TEXT':
asset_selector.filter_glob="*.txt;*.md;*.ron;*.json"
if asset_registry.asset_type_selector == 'AUDIO':
asset_selector.filter_glob="*.mp3;*.wav;*.flac"
add_asset = row.operator(operator="bevyassets.add", text="", icon="ADD")
add_asset.target_type = target_type
add_asset.target_name = target_name
add_asset.asset_name = asset_registry.asset_name_selector
add_asset.asset_type = asset_registry.asset_type_selector
add_asset.asset_path = asset_registry.asset_path_selector
#assets = json.loads(blueprint.collection["assets"]) if "assets" in blueprint.collection else []
for asset in assets:
row = panel.row()
row.label(text=asset["name"])
row.label(text=asset["type"])
row.label(text=asset["path"])
if not asset["internal"]:
remove_asset = row.operator(operator="bevyassets.remove", text="", icon="TRASH")
remove_asset.target_type = target_type
remove_asset.target_name = target_name
remove_asset.asset_path = asset["path"]
else:
row.label(text="")
class GLTF_PT_auto_export_assets(bpy.types.Panel): class GLTF_PT_auto_export_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 = ""
bl_parent_id = "BLENVY_PT_SidePanel" bl_parent_id = "BLENVY_PT_SidePanel"
bl_options = {'DEFAULT_CLOSED'} bl_options = {'DEFAULT_CLOSED','HIDE_HEADER'}
@classmethod @classmethod
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): """def draw_header(self, context):
layout = self.layout layout = self.layout
name = "" name = ""
if context.collection is not None and context.collection.name == 'Scene Collection': if context.collection is not None and context.collection.name == 'Scene Collection':
name = f"WORLD/LEVEL: {context.scene.name}" name = f"WORLD/LEVEL: {context.scene.name}"
else: else:
name = f"BLUEPRINT: {context.collection.name}" name = f"BLUEPRINT: {context.collection.name}"
layout.label(text=f"Assets For {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.
#if "auto_export_tracker" in context.window_manager:
asset_registry = context.window_manager.assets_registry asset_registry = context.window_manager.assets_registry
row = layout.row() name = "world"
row.prop(asset_registry, "asset_name_selector", text="") header, panel = layout.box().panel(f"assets{name}", default_closed=False)
row.prop(asset_registry, "asset_type_selector", text="") header.label(text="World/Level Assets")
row.prop(asset_registry, "asset_path_selector", text="")
add_assets = row.operator(operator="bevyassets.add", text="Add asset")
add_assets.asset_name = asset_registry.asset_name_selector
add_assets.asset_type = asset_registry.asset_type_selector
add_assets.asset_path = asset_registry.asset_path_selector
layout.separator() if panel:
row = layout.row() for scene in bpy.data.scenes:
row.label(text="Name") if scene.name != "Library": # FIXME: hack for testing
row.label(text="Type") assets = json.loads(scene.get('assets')) if 'assets' in scene else []
row.label(text="Path") row = panel.row()
row.label(text="Remove") 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)
#print("FOO", json.dumps([{"name":"foo", "type":"MODEL", "path":"bla", "internal":True}]))
# [{"name": "trigger_sound", "type": "AUDIO", "path": "assets/audio/trigger.mp3", "internal": true}]
assets_list = []
if context.collection is not None and context.collection.name == 'Scene Collection':
assets_list = json.loads(context.scene.get('assets')) #asset_registry.assets_list
else:
if 'assets' in context.collection:
assets_list = json.loads(context.collection.get('assets'))
for asset in assets_list:
row = layout.row()
row.label(text=asset["name"])
row.label(text=asset["type"])
row.label(text=asset["path"])
if not asset["internal"]:
remove_asset = row.operator(operator="bevyassets.remove", text="remove")
remove_asset.asset_path = asset["path"]
else:
row.label(text="")
#

View File

@ -17,14 +17,3 @@ def make_empty(name, location, rotation, scale, collection):
collection.objects.link( empty_obj ) collection.objects.link( empty_obj )
#bpy.context.view_layer.update() #bpy.context.view_layer.update()
return empty_obj return empty_obj
def upsert_settings(name, data):
stored_settings = bpy.data.texts[name] if name in bpy.data.texts else bpy.data.texts.new(name)
stored_settings.clear()
stored_settings.write(json.dumps(data))
def load_settings(name):
stored_settings = bpy.data.texts[name] if name in bpy.data.texts else None
if stored_settings != None:
return json.loads(stored_settings.as_string())
return None

View File

@ -4,7 +4,8 @@ from bpy_types import (Operator)
from bpy.props import (StringProperty) from bpy.props import (StringProperty)
from bpy_extras.io_utils import ImportHelper from bpy_extras.io_utils import ImportHelper
from ..helpers import upsert_settings from ...settings import upsert_settings
from ..components.metadata import apply_customProperty_values_to_object_propertyGroups, apply_propertyGroup_values_to_object_customProperties, ensure_metadata_for_all_objects from ..components.metadata import apply_customProperty_values_to_object_propertyGroups, apply_propertyGroup_values_to_object_customProperties, ensure_metadata_for_all_objects
from ..propGroups.prop_groups import generate_propertyGroups_for_components from ..propGroups.prop_groups import generate_propertyGroups_for_components

View File

@ -6,7 +6,7 @@ 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 ..helpers import load_settings from ...settings import load_settings
from ..propGroups.prop_groups import generate_propertyGroups_for_components from ..propGroups.prop_groups import generate_propertyGroups_for_components
from ..components.metadata import ComponentMetadata, ensure_metadata_for_all_objects from ..components.metadata import ComponentMetadata, ensure_metadata_for_all_objects

View File

@ -34,3 +34,4 @@ class OT_select_blueprint(Operator):
o.select_set(True)""" o.select_set(True)"""
return {'FINISHED'} return {'FINISHED'}

View File

@ -1,11 +1,14 @@
import bpy import bpy
import json
from ..assets.ui import draw_assets
class GLTF_PT_auto_export_blueprints_list(bpy.types.Panel): class GLTF_PT_auto_export_blueprints_list(bpy.types.Panel):
bl_space_type = 'VIEW_3D' bl_space_type = 'VIEW_3D'
bl_region_type = 'UI' bl_region_type = 'UI'
bl_label = "Blueprints" bl_label = "Blueprints"
bl_parent_id = "BLENVY_PT_SidePanel" bl_parent_id = "BLENVY_PT_SidePanel"
bl_options = {'DEFAULT_CLOSED'} bl_options = {'DEFAULT_CLOSED','HIDE_HEADER'}
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
@ -15,15 +18,22 @@ class GLTF_PT_auto_export_blueprints_list(bpy.types.Panel):
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
for blueprint in context.window_manager.blueprints_registry.blueprints_list: for blueprint in context.window_manager.blueprints_registry.blueprints_list:
row = layout.row() row = layout.row()
row.label(icon="RIGHTARROW")
row.label(text=blueprint.name) row.label(text=blueprint.name)
if blueprint.local: if blueprint.local:
select_blueprint = row.operator(operator="blueprint.select", text="Select") select_blueprint = row.operator(operator="blueprint.select", text="", icon="RESTRICT_SELECT_OFF")
select_blueprint.blueprint_collection_name = 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 []
draw_assets(layout=layout, name=blueprint.name, title="Assets", asset_registry=asset_registry, assets=assets, target_type="BLUEPRINT", target_name=blueprint.name)
else: else:
row.label(text="External") row.label(text="External")

View File

@ -1,5 +1,5 @@
import bpy import bpy
import json from ..settings import load_settings
###################################################### ######################################################
## ui logic & co ## ui logic & co
@ -27,11 +27,10 @@ class BLENVY_PT_SidePanel(bpy.types.Panel):
library_scene_active = False library_scene_active = False
active_collection = context.collection active_collection = context.collection
current_auto_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else None current_auto_settings = load_settings(".gltf_auto_export_settings")
current_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else None current_gltf_settings = load_settings(".gltf_auto_export_gltf_settings")
if current_auto_settings is not None: if current_auto_settings is not None:
current_auto_settings = json.loads(current_auto_settings.as_string())
#print("current_auto_settings", current_auto_settings) #print("current_auto_settings", current_auto_settings)
main_scene_names = current_auto_settings["main_scene_names"] main_scene_names = current_auto_settings["main_scene_names"]
library_scene_names = current_auto_settings["library_scene_names"] library_scene_names = current_auto_settings["library_scene_names"]

13
tools/blenvy/settings.py Normal file
View File

@ -0,0 +1,13 @@
import json
import bpy
def upsert_settings(name, data):
stored_settings = bpy.data.texts[name] if name in bpy.data.texts else bpy.data.texts.new(name)
stored_settings.clear()
stored_settings.write(json.dumps(data))
def load_settings(name):
stored_settings = bpy.data.texts[name] if name in bpy.data.texts else None
if stored_settings != None:
return json.loads(stored_settings.as_string())
return None