mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-11-26 05:26:42 +00:00
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
This commit is contained in:
parent
0fb35d6e6a
commit
d0bc05fb96
@ -1,11 +1,44 @@
|
|||||||
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:
|
||||||
|
- [ ] 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
|
@ -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,7 +52,7 @@ 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
|
||||||
|
|
||||||
@ -151,7 +150,7 @@ classes = [
|
|||||||
OT_add_bevy_asset,
|
OT_add_bevy_asset,
|
||||||
OT_remove_bevy_asset,
|
OT_remove_bevy_asset,
|
||||||
OT_Add_asset_filebrowser,
|
OT_Add_asset_filebrowser,
|
||||||
GLTF_PT_auto_export_assets,
|
Blenvy_assets,
|
||||||
|
|
||||||
BlueprintsRegistry,
|
BlueprintsRegistry,
|
||||||
OT_select_blueprint,
|
OT_select_blueprint,
|
||||||
|
5
tools/blenvy/assets/asset_helpers.py
Normal file
5
tools/blenvy/assets/asset_helpers.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
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
|
@ -4,6 +4,7 @@ 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 ..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 +149,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
|
||||||
|
@ -1,31 +1,32 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import json
|
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 +34,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 +42,7 @@ 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):
|
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 = ""
|
||||||
@ -74,6 +75,6 @@ class GLTF_PT_auto_export_assets(bpy.types.Panel):
|
|||||||
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 []
|
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)
|
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)
|
||||||
|
@ -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
|
|
18
tools/blenvy/blueprints/blueprint.py
Normal file
18
tools/blenvy/blueprints/blueprint.py
Normal 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}'
|
98
tools/blenvy/blueprints/blueprint_helpers.py
Normal file
98
tools/blenvy/blueprints/blueprint_helpers.py
Normal 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)
|
@ -6,9 +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)
|
||||||
|
|
||||||
|
|
||||||
# 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 = []
|
||||||
|
@ -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
|
||||||
@ -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)
|
|
@ -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)
|
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
"""
|
11
tools/blenvy/core/path_helpers.py
Normal file
11
tools/blenvy/core/path_helpers.py
Normal 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)
|
27
tools/blenvy/core/scene_helpers.py
Normal file
27
tools/blenvy/core/scene_helpers.py
Normal 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)
|
@ -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")
|
||||||
|
@ -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,23 @@ 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)
|
||||||
|
|
||||||
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 +101,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 +139,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
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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):
|
||||||
|
@ -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")
|
||||||
|
|
||||||
|
@ -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'
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
|
@ -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 = []
|
||||||
|
Loading…
Reference in New Issue
Block a user