feat(Blenvy:Blender):

* coherence pass : "main" scenes & co renamed to "level" scenes & co
 * auto export now toggled ON by default, but bailing out early if there are no main & lib scenes added
 * UI improvement for adding level/lib scenes
 * minor cleanups
This commit is contained in:
kaosat.dev 2024-07-19 12:06:44 +02:00
parent 6bc0ec0782
commit eac93ce68f
26 changed files with 155 additions and 145 deletions

12
TODO.md
View File

@ -112,7 +112,6 @@ General issues:
- 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
- [x] addon-prefs => settings
- [x] generate_gltf_export_settings => should not use add-on prefs at all ? since we are not overriding gltf settings that way anymore ?
- [x] remove hard coded path for standard gltf settings
@ -121,7 +120,6 @@ General issues:
- [x] components
- [x] add handling of errors when trying to load settings
- [x] fix auto export workflow
- [x] add hashing of modifiers/ geometry nodes in serialize scene
- [x] add ability to FORCE export specific blueprints & levels
@ -240,6 +238,10 @@ Blender side:
- [ ] blueprint instances as children of blueprint instances
- [ ] blueprint instances as children of empties
- [x] check/ fix behaviour of blender plugin if all folders are the same (ie, all in assets for example)
- [x] rename all "main scene xx" to "level scene"
- [x] make sure the "add scene" button is not available unless you have actually selected one
- [x] make auto export be on by default, however bail out early by detecting if there are any level/blueprint scenes
Bevy Side:
- [x] deprecate BlueprintName & BlueprintPath & use BlueprintInfo instead
@ -309,9 +311,9 @@ Bevy Side:
- [ ] replace string in BlueprintInfo path with PathBuf ?
- [ ] update main docs
- [ ] rename project to Blenvy
- [x] rename project to Blenvy
- [ ] replace all references to the old 2 add-ons with those to Blenvy
- [ ] rename repo to "Blenvy"
- [ ] do a deprecation release of all bevy_gltf_xxx crates to point at the new Blenvy crate
- [x] rename repo to "Blenvy"
- [x] do a deprecation release of all bevy_gltf_xxx crates to point at the new Blenvy crate
clear && pytest -svv --blender-template ../../testing/bevy_example/art/testing_library.blend --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration_prepare.py && pytest -svv --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration.py

View File

@ -112,7 +112,7 @@ If you want to use multiple blend files, use Blender's asset library etc, we got
There are only a few things to keep in mind
#### Assets/library/blueprints files
- mark your library scenes as specified above, but **do NOT** specify a **main** scene
- mark your library scenes as specified above, but **do NOT** specify a **level** scene
- mark any collection in your scenes as "assets"
- choose "split" for the combine mode (as you want your gltf blueprints to be saved for external use)
- do your Blender things as normal
@ -120,7 +120,7 @@ There are only a few things to keep in mind
- (optional) activate the **material library** option, so you only have one set of material per asset library (recomended)
#### Level/world files
- mark your main scenes as specified above ( personally I recommended **NOT** specifying a **library** scene in this case to keep things tidy, but that is up to you)
- mark your level scenes as specified above ( personally I recommended **NOT** specifying a **library** scene in this case to keep things tidy, but that is up to you)
- configure your asset libraries as you would usually do, I recomend using the "link" mode so that any changes to asset files are reflected correctly
- drag & drop any assets from the blueprints library (as you would normally do in Blender as well)
- choose "split" for the combine mode (as you want your gltf blueprints to be external usually & use the gltf files generated from your assets library)
@ -259,7 +259,7 @@ ie this is an example scene...
![](./docs/workflow_original.jpg)
and what actually gets exported for the main scene/world/level
and what actually gets exported for the level scene/world/level
![](./docs/workflow_empties.jpg)

View File

@ -10,7 +10,7 @@ from ..blueprints.get_blueprints_to_export import get_blueprints_to_export
from ..levels.get_levels_to_export import get_levels_to_export
from .export_gltf import get_standard_exporter_settings
from ..levels.export_levels import export_main_scene
from ..levels.export_levels import export_level_scene
from ..blueprints.export_blueprints import export_blueprints
from .export_materials import cleanup_materials, export_materials
from ..levels.bevy_scene_components import remove_scene_components, upsert_scene_components
@ -52,7 +52,7 @@ def auto_export(changes_per_scene, changes_per_collection, changes_per_material,
if export_scene_settings:
# inject/ update scene components
upsert_scene_components(settings.main_scenes)
upsert_scene_components(settings.level_scenes)
#inject/ update light shadow information
for light in bpy.data.lights:
enabled = 'true' if light.use_shadow else 'false'
@ -65,8 +65,8 @@ def auto_export(changes_per_scene, changes_per_collection, changes_per_material,
# get blueprints/collections infos
(blueprints_to_export) = get_blueprints_to_export(changes_per_scene, changes_per_collection, changed_export_parameters, blueprints_data, settings)
# get level/main scenes infos
(main_scenes_to_export) = get_levels_to_export(changes_per_scene, changes_per_collection, changed_export_parameters, blueprints_data, settings)
# get level scenes infos
(level_scenes_to_export) = get_levels_to_export(changes_per_scene, changes_per_collection, changed_export_parameters, blueprints_data, settings)
# since materials export adds components we need to call this before blueprints are exported
# export materials & inject materials components into relevant objects
@ -75,7 +75,7 @@ def auto_export(changes_per_scene, changes_per_collection, changes_per_material,
export_materials(blueprints_data.blueprint_names, settings.library_scenes, settings)
# 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(level_scenes_to_export) + (1 if export_materials_library else 0)
bpy.context.window_manager.auto_export_tracker.exports_total = exports_total
bpy.context.window_manager.auto_export_tracker.exports_count = exports_total
@ -92,19 +92,19 @@ def auto_export(changes_per_scene, changes_per_collection, changes_per_material,
print("-------------------------------")
print("BLUEPRINTS: to export:", [blueprint.name for blueprint in blueprints_to_export])
print("-------------------------------")
print("MAIN SCENES: to export:", main_scenes_to_export)
print("MAIN SCENES: to export:", level_scenes_to_export)
print("-------------------------------")
# backup current active scene
old_current_scene = bpy.context.scene
# backup current selections
old_selections = bpy.context.selected_objects
# first export any main/level/world scenes
if len(main_scenes_to_export) > 0:
# first export any level/world scenes
if len(level_scenes_to_export) > 0:
print("export MAIN scenes")
for scene_name in main_scenes_to_export:
for scene_name in level_scenes_to_export:
print(" exporting scene:", scene_name)
export_main_scene(bpy.data.scenes[scene_name], settings, blueprints_data)
export_level_scene(bpy.data.scenes[scene_name], settings, blueprints_data)
# now deal with blueprints/collections
do_export_library_scene = not change_detection or changed_export_parameters or len(blueprints_to_export) > 0
@ -122,8 +122,8 @@ def auto_export(changes_per_scene, changes_per_collection, changes_per_material,
cleanup_materials(blueprints_data.blueprint_names, settings.library_scenes)
else:
for scene in settings.main_scenes:
export_main_scene(scene, settings, [])
for scene in settings.level_scenes:
export_level_scene(scene, settings, [])
@ -139,5 +139,5 @@ def auto_export(changes_per_scene, changes_per_collection, changes_per_material,
# FIXME: error handling ? also redundant
if export_scene_settings:
# inject/ update scene components
remove_scene_components(settings.main_scenes)
remove_scene_components(settings.level_scenes)

View File

@ -11,9 +11,13 @@ def prepare_and_export():
#bpy.context.window_manager.auto_export_tracker.disable_change_detection()
blenvy = bpy.context.window_manager.blenvy
auto_export_settings = blenvy.auto_export
# if there are no level or blueprint scenes, bail out early
if len(blenvy.level_scenes) == 0 and len(blenvy.library_scenes) == 0:
print("no level or library scenes, skipping auto export")
return
if auto_export_settings.auto_export: # only do the actual exporting if auto export is actually enabled
# determine changed objects
per_scene_changes, per_collection_changes, per_material_changes, project_hash = get_changes_per_scene(settings=blenvy)
# determine changed parameters

View File

@ -323,7 +323,7 @@ def serialize_project(settings):
print("serializing project")
per_scene = {}
for scene in settings.main_scenes + settings.library_scenes: #bpy.data.scenes:
for scene in settings.level_scenes + settings.library_scenes: #bpy.data.scenes:
print("scene", scene.name)
# ignore temporary scenes
if scene.name.startswith(TEMPSCENE_PREFIX):

View File

@ -10,7 +10,7 @@ parameter_names_whitelist_common = [
'blueprints_path',
'levels_path',
'materials_path',
'main_scene_names',
'level_scene_names',
'library_scene_names',
]

View File

@ -1,5 +1,5 @@
def upsert_scene_components(main_scenes):
for scene in main_scenes:
def upsert_scene_components(level_scenes):
for scene in level_scenes:
if scene.world is not None:
scene['BlenderBackgroundShader'] = ambient_color_to_component(scene.world)
scene['BlenderShadowSettings'] = scene_shadows_to_component(scene)
@ -17,7 +17,7 @@ def upsert_scene_components(main_scenes):
scene['BlenderToneMapping'] = scene_tonemapping_to_component(scene)
scene['BlenderColorGrading'] = scene_colorgrading_to_component(scene)
def remove_scene_components(main_scenes):
def remove_scene_components(level_scenes):
pass
def scene_tonemapping_to_component(scene):

View File

@ -1,10 +1,5 @@
import json
import os
from pathlib import Path
from types import SimpleNamespace
import bpy
from blenvy.blueprints.blueprint_helpers import inject_blueprints_list_into_main_scene, remove_blueprints_list_from_main_scene
from blenvy.blueprints.blueprint_helpers import inject_blueprints_list_into_level_scene, remove_blueprints_list_from_level_scene
from ..constants import TEMPSCENE_PREFIX
from ..common.generate_temporary_scene_and_export import generate_temporary_scene_and_export, copy_hollowed_collection_into, clear_hollow_scene
from ..common.export_gltf import (generate_gltf_export_settings, export_gltf)
@ -12,7 +7,7 @@ from .is_object_dynamic import is_object_dynamic, is_object_static
from ..utils import upsert_scene_assets
def export_main_scene(scene, settings, blueprints_data):
def export_level_scene(scene, settings, blueprints_data):
gltf_export_settings = generate_gltf_export_settings(settings)
assets_path_full = getattr(settings,"assets_path_full")
levels_path_full = getattr(settings,"levels_path_full")
@ -32,7 +27,7 @@ def export_main_scene(scene, settings, blueprints_data):
if export_blueprints :
gltf_output_path = os.path.join(levels_path_full, scene.name)
inject_blueprints_list_into_main_scene(scene, blueprints_data, settings)
inject_blueprints_list_into_level_scene(scene, blueprints_data, settings)
upsert_scene_assets(scene, blueprints_data=blueprints_data, settings=settings)
if export_separate_dynamic_and_static_objects:
@ -72,7 +67,7 @@ def export_main_scene(scene, settings, blueprints_data):
tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=scene.collection, temp_scene=temp_scene, **params)
)
remove_blueprints_list_from_main_scene(scene)
remove_blueprints_list_from_level_scene(scene)
else:
gltf_output_path = os.path.join(assets_path_full, scene.name)

View File

@ -6,7 +6,7 @@ def changed_object_in_scene(scene_name, changes_per_scene, blueprints_data, coll
# Embed / EmbedExternal
blueprints_from_objects = blueprints_data.blueprints_from_objects
blueprint_instances_in_scene = blueprints_data.blueprint_instances_per_main_scene.get(scene_name, None)
blueprint_instances_in_scene = blueprints_data.blueprint_instances_per_level_scene.get(scene_name, None)
if blueprint_instances_in_scene is not None:
changed_objects = [object_name for change in changes_per_scene.values() for object_name in change.keys()]
changed_blueprints = [blueprints_from_objects[changed] for changed in changed_objects if changed in blueprints_from_objects]
@ -27,7 +27,7 @@ def changed_object_in_scene(scene_name, changes_per_scene, blueprints_data, coll
elif combine_mode == 'EmbedExternal' and not blueprint.local:
level_needs_export = True
break
# changes => list of changed objects (regardless of wether they have been changed in main scene or in lib scene)
# changes => list of changed objects (regardless of wether they have been changed in level scene or in lib scene)
# wich of those objects are blueprint instances
# we need a list of changed objects that are blueprint instances
return level_needs_export
@ -56,8 +56,8 @@ def should_level_be_exported(scene_name, changed_export_parameters, changes_per_
# this also takes the split/embed mode into account: if a collection instance changes AND embed is active, its container level/world should also be exported
def get_levels_to_export(changes_per_scene, changes_per_collection, changed_export_parameters, blueprints_data, settings):
# determine list of main scenes to export
# we have more relaxed rules to determine if the main scenes have changed : any change is ok, (allows easier handling of changes, render settings etc)
main_scenes_to_export = [scene_name for scene_name in settings.main_scenes_names if should_level_be_exported(scene_name, changed_export_parameters, changes_per_scene, blueprints_data, settings)]
# determine list of level scenes to export
# we have more relaxed rules to determine if the level scenes have changed : any change is ok, (allows easier handling of changes, render settings etc)
level_scenes_to_export = [scene_name for scene_name in settings.level_scenes_names if should_level_be_exported(scene_name, changed_export_parameters, changes_per_scene, blueprints_data, settings)]
return (main_scenes_to_export)
return (level_scenes_to_export)

View File

@ -19,7 +19,7 @@ class AutoExportSettings(PropertyGroup):
auto_export: BoolProperty(
name='Auto export',
description='Automatically export to gltf on save',
default=False,
default=True,
update=save_settings
) # type: ignore

View File

@ -1,7 +1,7 @@
import bpy
import os
from pathlib import Path
from blenvy.assets.assets_scan import get_blueprint_asset_tree, get_main_scene_assets_tree2
from blenvy.assets.assets_scan import get_blueprint_asset_tree, get_level_scene_assets_tree2
def assets_to_fake_ron(list_like):
result = []
@ -15,14 +15,14 @@ def assets_to_fake_ron(list_like):
# TODO : move to assets
def upsert_scene_assets(scene, blueprints_data, settings):
"""print("main scene", scene)
"""print("level scene", scene)
for asset in scene.user_assets:
print(" user asset", asset.name, asset.path)
for asset in scene.generated_assets:
print(" generated asset", asset)"""
"""for blueprint in blueprints_data.blueprints_per_scenes[scene.name]:
print("BLUEPRINT", blueprint)"""
blueprint_instances_in_scene = blueprints_data.blueprint_instances_per_main_scene.get(scene.name, {}).keys()
blueprint_instances_in_scene = blueprints_data.blueprint_instances_per_level_scene.get(scene.name, {}).keys()
blueprints_in_scene = [blueprints_data.blueprints_per_name[blueprint_name] for blueprint_name in blueprint_instances_in_scene]
#yala = [blueprint.collection.user_assets for blueprint in blueprints_in_scene]
#print("dsfsdf", yala)
@ -61,7 +61,7 @@ def upsert_scene_assets(scene, blueprints_data, settings):
print("material_assets", material_assets, "extension", export_gltf_extension)
all_assets_raw = get_main_scene_assets_tree2(main_scene=scene, blueprints_data=blueprints_data, settings=settings)
all_assets_raw = get_level_scene_assets_tree2(level_scene=scene, blueprints_data=blueprints_data, settings=settings)
local_assets = [{"name": asset["name"], "path": asset["path"]} for asset in all_assets_raw if asset['parent'] is None and asset["path"] != "" ]
all_assets = [{"name": asset["name"], "path": asset["path"]} for asset in all_assets_raw if asset["path"] != "" ]
print("all_assets_raw", all_assets_raw)

View File

@ -12,7 +12,7 @@ def scan_assets(scene, blueprints_data, settings):
export_gltf_extension = getattr(settings, "export_gltf_extension")
relative_blueprints_path = os.path.relpath(blueprints_path, project_root_path)
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(scene.name, None)
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_level_scene.get(scene.name, None)
blueprint_assets_list = []
if blueprint_instance_names_for_scene:
@ -89,12 +89,12 @@ def get_blueprint_assets_tree(blueprint, blueprints_data, parent, settings):
assets_list += direct_assets
return assets_list
def get_main_scene_assets_tree(main_scene, blueprints_data, settings):
def get_level_scene_assets_tree(level_scene, blueprints_data, settings):
blueprints_path = getattr(settings, "blueprints_path")
export_gltf_extension = getattr(settings, "export_gltf_extension", ".glb")
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(main_scene.name, None)
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_level_scene.get(level_scene.name, None)
assets_list = get_user_assets_as_list(main_scene)
assets_list = get_user_assets_as_list(level_scene)
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)
@ -112,7 +112,7 @@ def get_main_scene_assets_tree(main_scene, blueprints_data, settings):
print("TOTAL ASSETS", assets_list)
# FIXME: do not do it here !!
scene = bpy.data.scenes[main_scene.name]
scene = bpy.data.scenes[level_scene.name]
scene.generated_assets.clear()
for asset in assets_list:
if asset.get("generated", False):
@ -122,12 +122,12 @@ def get_main_scene_assets_tree(main_scene, blueprints_data, settings):
return assets_list
# same as the above, withouth the clutter below : TODO: unify
def get_main_scene_assets_tree2(main_scene, blueprints_data, settings):
def get_level_scene_assets_tree2(level_scene, blueprints_data, settings):
blueprints_path = getattr(settings, "blueprints_path")
export_gltf_extension = getattr(settings, "export_gltf_extension", ".glb")
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(main_scene.name, None)
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_level_scene.get(level_scene.name, None)
assets_list = get_user_assets_as_list(main_scene)
assets_list = get_user_assets_as_list(level_scene)
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)

View File

@ -5,7 +5,7 @@ from bpy_types import (Operator)
from bpy.props import (BoolProperty, StringProperty, EnumProperty)
from .asset_helpers import does_asset_exist, get_user_assets, remove_asset, upsert_asset
from .assets_scan import get_main_scene_assets_tree
from .assets_scan import get_level_scene_assets_tree
from ..core.path_helpers import absolute_path_from_blend_file
from .generate_asset_file import write_ron_assets_file
@ -186,8 +186,8 @@ class BLENVY_OT_assets_generate_files(Operator):
blueprints_registry.refresh_blueprints()
blueprints_data = blueprints_registry.blueprints_data
for scene in blenvy.main_scenes:
assets_hierarchy = get_main_scene_assets_tree(scene, blueprints_data, settings)
for scene in blenvy.level_scenes:
assets_hierarchy = get_level_scene_assets_tree(scene, blueprints_data, settings)
# scene["assets"] = json.dumps(assets_hierarchy)
write_ron_assets_file(scene.name, assets_hierarchy, internal_only = False, output_path_full = blenvy.levels_path_full)

View File

@ -1,6 +1,6 @@
from types import SimpleNamespace
import bpy
from .assets_scan import get_main_scene_assets_tree
from .assets_scan import get_level_scene_assets_tree
from .asset_helpers import get_user_assets, does_asset_exist
def draw_assets(layout, name, title, asset_registry, target_type, target_name, editable=True, user_assets= [], generated_assets = []):
@ -92,15 +92,15 @@ class BLENVY_PT_assets_panel(bpy.types.Panel):
settings = SimpleNamespace(**settings)
if panel:
for scene in blenvy.main_scenes:
for scene in blenvy.level_scenes:
user_assets = get_user_assets(scene)
row = panel.row()
row.prop(scene, "always_export")
scene_assets_panel = draw_assets(layout=row, name=scene.name, title=f"{scene.name} Assets", asset_registry=asset_registry, user_assets=user_assets, target_type="SCENE", target_name=scene.name)
"""if scene.name in blueprints_data.blueprint_instances_per_main_scene:
for blueprint_name in blueprints_data.blueprint_instances_per_main_scene[scene.name].keys():
"""if scene.name in blueprints_data.blueprint_instances_per_level_scene:
for blueprint_name in blueprints_data.blueprint_instances_per_level_scene[scene.name].keys():
blueprint = blueprints_data.blueprints_per_name[blueprint_name]
blueprint_assets = get_user_assets(blueprint.collection)
if scene_assets_panel:

View File

@ -38,7 +38,7 @@ def inject_export_path_into_internal_blueprints(internal_blueprints, blueprints_
blueprint.collection["materials_path"] = materials_exported_path
def inject_blueprints_list_into_main_scene(scene, blueprints_data, settings):
def inject_blueprints_list_into_level_scene(scene, blueprints_data, settings):
project_root_path = getattr(settings, "project_root_path")
assets_path = getattr(settings,"assets_path")
levels_path = getattr(settings,"levels_path")
@ -49,7 +49,7 @@ def inject_blueprints_list_into_main_scene(scene, blueprints_data, settings):
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)
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_level_scene.get(scene.name, None)
blueprint_assets_list = []
if blueprint_instance_names_for_scene:
for blueprint_name in blueprint_instance_names_for_scene:
@ -74,7 +74,7 @@ def inject_blueprints_list_into_main_scene(scene, blueprints_data, settings):
#print("blueprint assets", blueprint_assets_list)
def remove_blueprints_list_from_main_scene(scene):
def remove_blueprints_list_from_level_scene(scene):
assets_list = None
assets_list_name = f"assets_list_{scene.name}_components"

View File

@ -67,6 +67,6 @@ class BlueprintsRegistry(PropertyGroup):
#print("titi", self)
blenvy = bpy.context.window_manager.blenvy
settings = blenvy
blueprints_data = blueprints_scan(settings.main_scenes, settings.library_scenes, settings)
blueprints_data = blueprints_scan(settings.level_scenes, settings.library_scenes, settings)
self.blueprints_data = blueprints_data
return blueprints_data

View File

@ -7,14 +7,14 @@ from .blueprint import Blueprint
# - marked as asset
# - with the "auto_export" flag
# https://blender.stackexchange.com/questions/167878/how-to-get-all-collections-of-the-current-scene
def blueprints_scan(main_scenes, library_scenes, settings):
def blueprints_scan(level_scenes, library_scenes, settings):
blueprints = {}
blueprints_from_objects = {}
blueprint_name_from_instances = {}
collections = []
# main scenes
blueprint_instances_per_main_scene = {}
# level scenes
blueprint_instances_per_level_scene = {}
internal_collection_instances = {}
external_collection_instances = {}
@ -26,7 +26,7 @@ def blueprints_scan(main_scenes, library_scenes, settings):
collection_category[collection_name] = [] #.append(collection_name)
collection_category[collection_name].append(object)
for scene in main_scenes:# should it only be main scenes ? what about collection instances inside other scenes ?
for scene in level_scenes:# should it only be level scenes ? what about collection instances inside other scenes ?
for object in scene.objects:
#print("object", object.name)
if object.instance_type == 'COLLECTION':
@ -52,19 +52,19 @@ def blueprints_scan(main_scenes, library_scenes, settings):
# blueprints[collection_name].instances.append(object)
# FIXME: this only account for direct instances of blueprints, not for any nested blueprint inside a blueprint
if scene.name not in blueprint_instances_per_main_scene.keys():
blueprint_instances_per_main_scene[scene.name] = {}
if collection_name not in blueprint_instances_per_main_scene[scene.name].keys():
blueprint_instances_per_main_scene[scene.name][collection_name] = []
blueprint_instances_per_main_scene[scene.name][collection_name].append(object)
if scene.name not in blueprint_instances_per_level_scene.keys():
blueprint_instances_per_level_scene[scene.name] = {}
if collection_name not in blueprint_instances_per_level_scene[scene.name].keys():
blueprint_instances_per_level_scene[scene.name][collection_name] = []
blueprint_instances_per_level_scene[scene.name][collection_name].append(object)
blueprint_name_from_instances[object] = collection_name
"""# add any indirect ones
# FIXME: needs to be recursive, either here or above
for nested_blueprint in blueprints[collection_name].nested_blueprints:
if not nested_blueprint in blueprint_instances_per_main_scene[scene.name]:
blueprint_instances_per_main_scene[scene.name].append(nested_blueprint)"""
if not nested_blueprint in blueprint_instances_per_level_scene[scene.name]:
blueprint_instances_per_level_scene[scene.name].append(nested_blueprint)"""
for collection in bpy.data.collections:
#print("collection", collection, collection.name_full, "users", collection.users)
@ -83,7 +83,7 @@ def blueprints_scan(main_scenes, library_scenes, settings):
if (
'AutoExport' in collection and collection['AutoExport'] == True # get marked collections
or collection.asset_data is not None # or if you have marked collections as assets you can auto export them too
or collection.name in list(internal_collection_instances.keys()) # or if the collection has an instance in one of the main scenes
or collection.name in list(internal_collection_instances.keys()) # or if the collection has an instance in one of the level scenes
):
blueprint = Blueprint(collection.name)
blueprint.local = True
@ -108,7 +108,7 @@ def blueprints_scan(main_scenes, library_scenes, settings):
#
collections.append(collection)
# 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)
# EXTERNAL COLLECTIONS: add any collection that has an instance in the level scenes, but is not present in any of the scenes (IE NON LOCAL/ EXTERNAL)
for collection_name in external_collection_instances:
collection = bpy.data.collections[collection_name]
blueprint = Blueprint(collection.name)
@ -165,7 +165,7 @@ def blueprints_scan(main_scenes, library_scenes, settings):
print(blueprints_from_objects)"""
print("BLUEPRINT INSTANCES PER MAIN SCENE")
print(blueprint_instances_per_main_scene)'''
print(blueprint_instances_per_level_scene)'''
"""changes_test = {'Library': {
@ -173,11 +173,11 @@ def blueprints_scan(main_scenes, library_scenes, settings):
'Fox_mesh': bpy.data.objects['Fox_mesh'],
'External_blueprint2_Cylinder': bpy.data.objects['External_blueprint2_Cylinder']}
}
# which main scene has been impacted by this
# does one of the main scenes contain an INSTANCE of an impacted blueprint
for scene in main_scenes:
# which level scene has been impacted by this
# does one of the level scenes contain an INSTANCE of an impacted blueprint
for scene in level_scenes:
changed_objects = list(changes_test["Library"].keys()) # just a hack for testing
#bluprint_instances_in_scene = blueprint_instances_per_main_scene[scene.name]
#bluprint_instances_in_scene = blueprint_instances_per_level_scene[scene.name]
#print("instances per scene", bluprint_instances_in_scene, "changed_objects", changed_objects)
changed_blueprints_with_instances_in_scene = [blueprints_from_objects[changed] for changed in changed_objects if changed in blueprints_from_objects]
@ -226,7 +226,7 @@ def blueprints_scan(main_scenes, library_scenes, settings):
"external_blueprints": external_blueprints,
"blueprints_per_scenes": blueprints_per_scenes,
"blueprint_instances_per_main_scene": blueprint_instances_per_main_scene,
"blueprint_instances_per_level_scene": blueprint_instances_per_level_scene,
"blueprint_instances_per_library_scene": blueprint_instances_per_library_scene,
# not sure about these two

View File

@ -8,14 +8,14 @@ import blenvy.add_ons.bevy_components.settings as component_settings
# list of settings we do NOT want to save
settings_black_list = ['settings_save_enabled', 'main_scene_selector', 'library_scene_selector']
settings_black_list = ['settings_save_enabled', 'level_scene_selector', 'library_scene_selector']
def save_settings(settings, context):
if settings.settings_save_enabled:
settings_dict = generate_complete_settings_dict(settings, BlenvyManager, [])
raw_settings = {key: settings_dict[key] for key in settings_dict.keys() if key not in settings_black_list}
# we need to inject the main & library scene names as they are computed properties, not blender ones
raw_settings['main_scenes_names'] = settings.main_scenes_names
raw_settings['level_scenes_names'] = settings.level_scenes_names
raw_settings['library_scenes_names'] = settings.library_scenes_names
upsert_settings(settings.settings_save_path, raw_settings, overwrite=True)
@ -29,10 +29,10 @@ def update_asset_folders(settings, context):
def is_scene_already_in_use(self, scene):
try:
current_main_scene_names = list(map(lambda x: x.name, self.main_scenes))
current_level_scene_names = list(map(lambda x: x.name, self.level_scenes))
current_library_scene_names = list(map(lambda x: x.name, self.library_scenes))
#print("scene ", scene.name, current_main_scene_names, current_library_scene_names)
return scene.name not in current_main_scene_names and scene.name not in current_library_scene_names
#print("scene ", scene.name, current_level_scene_names, current_library_scene_names)
return scene.name not in current_level_scene_names and scene.name not in current_library_scene_names
except:
return True
@ -93,7 +93,7 @@ class BlenvyManager(PropertyGroup):
levels_path: StringProperty(
name='Levels path',
description='path to export the levels (main scenes) to (relative to the assets folder)',
description='path to export the levels (level scenes) to (relative to the assets folder)',
default='levels',
update= save_settings
) # type: ignore
@ -119,16 +119,16 @@ class BlenvyManager(PropertyGroup):
auto_export: PointerProperty(type=auto_export_settings.AutoExportSettings) # type: ignore
components: PointerProperty(type=component_settings.ComponentsSettings) # type: ignore
main_scene_selector: PointerProperty(type=bpy.types.Scene, name="main scene", description="main_scene_picker", poll=is_scene_already_in_use, update=save_settings)# type: ignore
level_scene_selector: PointerProperty(type=bpy.types.Scene, name="level scene", description="level_scene_picker", poll=is_scene_already_in_use, update=save_settings)# type: ignore
library_scene_selector: PointerProperty(type=bpy.types.Scene, name="library scene", description="library_scene_picker", poll=is_scene_already_in_use, update=save_settings)# type: ignore
@property
def main_scenes(self):
def level_scenes(self):
return [scene for scene in bpy.data.scenes if scene.blenvy_scene_type == 'Level']
@property
def main_scenes_names(self):
return [scene.name for scene in self.main_scenes]
def level_scenes_names(self):
return [scene.name for scene in self.level_scenes]
@property
def library_scenes(self):
@ -148,7 +148,7 @@ class BlenvyManager(PropertyGroup):
bpy.types.Scene.blenvy_scene_type = EnumProperty(
items= (
('None', 'None', 'No blenvy type specified'),
('Level', 'Level','Main/ Level scene'),
('Level', 'Level','Level scene'),
('Library', 'Library', 'Library scene'),
),
default='None'

View File

@ -28,8 +28,8 @@ class BLENVY_OT_scenes_list_actions(Operator):
def invoke(self, context, event):
if self.action == 'REMOVE':
bpy.data.scenes[self.scene_name].blenvy_scene_type = 'None'
context.window_manager.blenvy.main_scene_selector = None # we use these to force update/save the list of main/library scenes
context.window_manager.blenvy.library_scene_selector = None # we use these to force update/save the list of main/library scenes
context.window_manager.blenvy.level_scene_selector = None # we use these to force update/save the list of level/library scenes
context.window_manager.blenvy.library_scene_selector = None # we use these to force update/save the list of level/library scenes
"""info = 'Item "%s" removed from list' % (target[idx].name)
target.remove(idx)
@ -39,8 +39,8 @@ class BLENVY_OT_scenes_list_actions(Operator):
if self.action == 'ADD':
scene_to_add = None
if self.scene_type == "LEVEL":
if context.window_manager.blenvy.main_scene_selector:
scene_to_add = context.window_manager.blenvy.main_scene_selector
if context.window_manager.blenvy.level_scene_selector:
scene_to_add = context.window_manager.blenvy.level_scene_selector
scene_to_add.blenvy_scene_type = "Level"
else:
if context.window_manager.blenvy.library_scene_selector:
@ -51,9 +51,9 @@ class BLENVY_OT_scenes_list_actions(Operator):
print("adding scene", scene_to_add)
if self.scene_type == "LEVEL":
context.window_manager.blenvy.main_scene_selector = None # we use these to force update/save the list of main/library scenes
context.window_manager.blenvy.level_scene_selector = None # we use these to force update/save the list of level/library scenes
else:
context.window_manager.blenvy.library_scene_selector = None # we use these to force update/save the list of main/library scenes
context.window_manager.blenvy.library_scene_selector = None # we use these to force update/save the list of level/library scenes
#setattr(source, target_index, len(target) - 1)

View File

@ -95,27 +95,31 @@ class BLENVY_PT_SidePanel(bpy.types.Panel):
panel.separator()
# scenes selection
if len(blenvy.main_scenes) == 0 and len(blenvy.library_scenes) == 0:
if len(blenvy.level_scenes) == 0 and len(blenvy.library_scenes) == 0:
row = panel.row()
row.alert = True
panel.alert = True
row.label(text="NO library or main scenes specified! at least one main scene or library scene is required!")
row.label(text="NO library or level scenes specified! at least one level scene or library scene is required!")
row = panel.row()
row.label(text="Please select and add one using the UI below")
section = panel
rows = 2
row = section.row()
row.label(text="main scenes")
row.prop(blenvy, "main_scene_selector", text='')
add_operator = row.operator("blenvy.scenes_list_actions", icon='ADD', text="")
col = row.column()
col.label(text="level scenes")
col = row.column()
col.prop(blenvy, "level_scene_selector", text='')
col = row.column()
add_operator = col.operator("blenvy.scenes_list_actions", icon='ADD', text="")
add_operator.action = 'ADD'
add_operator.scene_type = 'LEVEL'
#sub_row.enabled = blenvy.main_scene_selector is not None
col.enabled = blenvy.level_scene_selector is not None
row = section.row()
col = row.column()
for scene in blenvy.main_scenes:
for scene in blenvy.level_scenes:
sub_row = col.box().row()
sub_row.label(text=scene.name)
remove_operator = sub_row.operator("blenvy.scenes_list_actions", icon='TRASH', text="")
@ -127,11 +131,16 @@ class BLENVY_PT_SidePanel(bpy.types.Panel):
# library scenes
row = section.row()
row.label(text="library scenes")
row.prop(blenvy, "library_scene_selector", text='')
add_operator = row.operator("blenvy.scenes_list_actions", icon='ADD', text="")
col = row.column()
col.label(text="library scenes")
col = row.column()
col.prop(blenvy, "library_scene_selector", text='')
col = row.column()
add_operator = col.operator("blenvy.scenes_list_actions", icon='ADD', text="")
add_operator.action = 'ADD'
add_operator.scene_type = 'LIBRARY'
col.enabled = blenvy.library_scene_selector is not None
row = section.row()
col = row.column()

View File

@ -27,7 +27,7 @@ class BLENVY_PT_levels_panel(bpy.types.Panel):
#blueprints_registry.refresh_blueprints()
blueprints_data = blueprints_registry.blueprints_data
for scene in blenvy.main_scenes:
for scene in blenvy.level_scenes:
header, panel = layout.box().panel(f"level_assets{scene.name}", default_closed=False)
if header:
header.label(text=scene.name)#, icon="HIDE_OFF")

View File

@ -98,23 +98,23 @@ This issue has been resolved in v0.9.
### Blueprints
You can enable this option to automatically replace all the **collection instances** inside your main scene with blueprints
- whenever you change your main scene (or your library scene , if that option is enabled), all your collection instances
You can enable this option to automatically replace all the **collection instances** inside your level scene with blueprints
- whenever you change your level scene (or your library scene , if that option is enabled), all your collection instances
* will be replaced with empties (this will not be visible to you)
* those empties will have additional custom properties / components : ```BlueprintInfo``` & ```SpawnBlueprint```
* your main scene/ level will be exported to a much more trimmed down gltf file (see next point)
* your level scene/ level will be exported to a much more trimmed down gltf file (see next point)
* all the original collections (that you used to create the instances) will be exported as **seperate gltf files** into the "library" folder
- this means you will have
* one small main gltf file (your level/world)
* as many gltf files as you have used collections in the main scene , in the library path you specified :
* as many gltf files as you have used collections in the level scene , in the library path you specified :
for the included [basic](../../examples/bevy_gltf_blueprints/basic/) example's [assets](../../examples/bevy_gltf_blueprints/basic/assets/), it looks something like this:
![library](./docs/exported_library_files.png)
the .blend file that they are generated from can be found [here](../../examples/bevy_gltf_blueprints/basic/assets/advanced.blend)
- the above only applies to collections that have **instances** in your main scene!
- the above only applies to collections that have **instances** in your level scene!
if you want a specific collection in your library to always get exported regardless of its use, you need to add
a **COLLECTION** (boolean) custom property called ```AutoExport``` set to true
> not at the object level ! the collection level !

View File

@ -63,9 +63,9 @@ def test_export_change_tracking_custom_properties(setup_data):
prepare_auto_export()
def first_change():
# now add a custom property to the cube in the main scene & export again
# now add a custom property to the cube in the level scene & export again
print("----------------")
print("main scene change (custom property)")
print("level scene change (custom property)")
print("----------------")
bpy.data.objects["Cube"]["test_property"] = 42
@ -86,7 +86,7 @@ def test_export_change_tracking_custom_properties_collection_instances_combine_m
def second_change():
# add a custom property to the cube in the library scene & export again
# this should trigger changes in the main scene as well since the mode is embed & this blueprints has an instance in the main scene
# this should trigger changes in the level scene as well since the mode is embed & this blueprints has an instance in the level scene
print("----------------")
print("library change (custom property)")
print("----------------")
@ -94,7 +94,7 @@ def test_export_change_tracking_custom_properties_collection_instances_combine_m
def third_change():
# now we set the _combine mode of the instance to "split", so auto_export should:
# * not take the changes into account in the main scene
# * not take the changes into account in the level scene
# * export the blueprint (so file for Blueprint1 will be changed)
bpy.data.objects["Blueprint1"]["_combine"] = "Split"
@ -117,9 +117,9 @@ def test_export_change_tracking_light_properties(setup_data):
prepare_auto_export()
def first_change():
# now add a custom property to the cube in the main scene & export again
# now add a custom property to the cube in the level scene & export again
print("----------------")
print("main scene change (light, energy)")
print("level scene change (light, energy)")
print("----------------")
bpy.data.lights["Light"].energy = 100
@ -128,14 +128,14 @@ def test_export_change_tracking_light_properties(setup_data):
def second_change():
print("----------------")
print("main scene change (light, shadow_cascade_count)")
print("level scene change (light, shadow_cascade_count)")
print("----------------")
bpy.data.lights["Light"].shadow_cascade_count = 2
def third_change():
print("----------------")
print("main scene change (light, use_shadow)")
print("level scene change (light, use_shadow)")
print("----------------")
bpy.data.lights["Light"].use_shadow = False
@ -153,7 +153,7 @@ def test_export_change_tracking_camera_properties(setup_data):
def first_change():
print("----------------")
print("main scene change (camera)")
print("level scene change (camera)")
print("----------------")
bpy.data.cameras["Camera"].angle = 0.5
@ -170,20 +170,20 @@ def test_export_change_tracking_material_properties(setup_data):
def first_change():
print("----------------")
print("main scene change (material, clip)")
print("level scene change (material, clip)")
print("----------------")
bpy.data.materials["Material.001"].blend_method = 'CLIP'
def second_change():
print("----------------")
print("main scene change (material, alpha_threshold)")
print("level scene change (material, alpha_threshold)")
print("----------------")
bpy.data.materials["Material.001"].alpha_threshold = 0.2
def third_change():
print("----------------")
print("main scene change (material, diffuse_color)")
print("level scene change (material, diffuse_color)")
print("----------------")
bpy.data.materials["Material.001"].diffuse_color[0] = 0.2
@ -200,7 +200,7 @@ def test_export_change_tracking_material_properties(setup_data):
- setup gltf parameters & auto_export parameters
- calls exporter on the testing scene
- saves timestamps of generated files
- changes things in the main scene and/or library
- changes things in the level scene and/or library
- checks if timestamps have changed
- if all worked => test is a-ok
- removes generated files
@ -219,7 +219,7 @@ def test_export_various_chained_changes(setup_data):
def second_change():
# now move the main cube & export again
print("----------------")
print("main scene change")
print("level scene change")
print("----------------")
bpy.context.window_manager.auto_export_tracker.enable_change_detection() # FIXME: should not be needed, but ..

View File

@ -92,7 +92,7 @@ def test_export_no_parameters(setup_data):
def test_export_auto_export_parameters_only(setup_data):
auto_export_operator = bpy.ops.export_scenes.auto_gltf
export_props = {
"main_scene_names" : ['World'],
"level_scene_names" : ['World'],
"library_scene_names": ['Library'],
}
@ -119,7 +119,7 @@ def test_export_changed_parameters(setup_data):
# first, configure things
# we use the global settings for that
export_props = {
"main_scene_names" : ['World'],
"level_scene_names" : ['World'],
"library_scene_names": ['Library'],
}
@ -216,7 +216,7 @@ def test_export_changed_parameters(setup_data):
print("fourth export, changed auto parameters")
export_props = {
"main_scene_names" : ['World'],
"level_scene_names" : ['World'],
"library_scene_names": ['Library'],
"export_materials_library": False # we need to add it here, as the direct settings set on the operator will only be used for the NEXT run
}

View File

@ -70,7 +70,7 @@ def test_export_do_not_export_blueprints(setup_data):
# first, configure things
# we use the global settings for that
export_props = {
"main_scene_names" : ['World'],
"level_scene_names" : ['World'],
"library_scene_names": ['Library']
}
stored_auto_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings")
@ -96,7 +96,7 @@ def test_export_custom_blueprints_path(setup_data):
# first, configure things
# we use the global settings for that
export_props = {
"main_scene_names" : ['World'],
"level_scene_names" : ['World'],
"library_scene_names": ['Library']
}
@ -123,7 +123,7 @@ def test_export_materials_library(setup_data):
# first, configure things
# we use the global settings for that
export_props = {
"main_scene_names" : ['World'],
"level_scene_names" : ['World'],
"library_scene_names": ['Library']
}
stored_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings")
@ -150,7 +150,7 @@ def test_export_materials_library_custom_path(setup_data):
# first, configure things
# we use the global settings for that
export_props = {
"main_scene_names" : ['World'],
"level_scene_names" : ['World'],
"library_scene_names": ['Library']
}
stored_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings")
@ -179,7 +179,7 @@ def test_export_collection_instances_combine_mode(setup_data): # There is more i
# first, configure things
# we use the global settings for that
export_props = {
"main_scene_names" : ['World'],
"level_scene_names" : ['World'],
"library_scene_names": ['Library']
}
stored_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings")
@ -209,7 +209,7 @@ def test_export_do_not_export_marked_assets(setup_data):
# first, configure things
# we use the global settings for that
export_props = {
"main_scene_names" : ['World'],
"level_scene_names" : ['World'],
"library_scene_names": ['Library']
}
stored_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings")
@ -239,7 +239,7 @@ def test_export_separate_dynamic_and_static_objects(setup_data):
# first, configure things
# we use the global settings for that
export_props = {
"main_scene_names" : ['World'],
"level_scene_names" : ['World'],
"library_scene_names": ['Library']
}
stored_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings")
@ -270,7 +270,7 @@ def test_export_should_not_generate_orphan_data(setup_data):
# first, configure things
# we use the global settings for that
export_props = {
"main_scene_names" : ['World'],
"level_scene_names" : ['World'],
"library_scene_names": ['Library']
}
stored_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings")

View File

@ -8,7 +8,7 @@ def prepare_auto_export(auto_export_overrides={}, gltf_export_settings = {"expor
# first, configure things
# we use the global settings for that
export_props = {
"main_scene_names" : ['World'],
"level_scene_names" : ['World'],
"library_scene_names": ['Library'],
**auto_export_overrides
}