Compare commits

...

12 Commits

Author SHA1 Message Date
kaosat.dev
11e8786b59 fix(auto_export): first draft, working ! but horribly convoluted solution to the correct reseting of change detection state 2024-04-06 01:29:41 +02:00
kaosat.dev
86a1a4d717 test(auto_export): added tests for changed parameters tracking & change tracking
* added testing based on timestamps of generated files
 * found a (few ?) bugs in change tracking based on these, fixing next
2024-04-06 01:00:24 +02:00
kaosat.dev
62686ecb61 chore(): cleared some data in testing file 2024-04-06 00:21:55 +02:00
kaosat.dev
9645e812ef fix(auto_export): fixed changed gltf parameters handling 2024-04-05 23:32:41 +02:00
kaosat.dev
ccaeecf6f1 chore(auto_export): more minor tweaks 2024-04-05 23:21:39 +02:00
kaosat.dev
1b1fc31d5d chore(testing): test file tweaks 2024-04-05 23:19:46 +02:00
kaosat.dev
e139a1496d chore(auto_export): cleanups 2024-04-05 23:19:29 +02:00
kaosat.dev
ed09ab7d48 feat(tools):
* auto_export now defaults to being disabled (otherwise you will get export attempts etc even if you only
had the add-on installed !
 * modified logic accordingly
 * various related tweaks
 * adjusted tests
2024-04-05 23:14:38 +02:00
kaosat.dev
1cf47d36b1 refactor(): cleanups 2024-04-05 10:52:14 +02:00
kaosat.dev
0998decb39 feat(animation): changes & some reverts of the latests experiments 2024-04-05 00:17:07 +02:00
kaosat.dev
0528286b12 chore(): updated blend file 2024-04-03 13:10:44 +02:00
kaosat.dev
b19e54b3bd refactor(auto_export): cleanups 2024-04-03 13:07:13 +02:00
24 changed files with 663 additions and 856 deletions

View File

@ -48,9 +48,11 @@ pub struct AnimationInfos {
pub animations: Vec<AnimationInfo>, pub animations: Vec<AnimationInfo>,
} }
#[derive(Reflect, Default, Debug)]
pub struct AnimationMarker { pub struct AnimationMarker {
pub frame: u32, // pub frame: u32,
pub name: String, pub name: String,
pub handled_for_cycle: bool,
} }
/// Stores information about animation markers: practical for adding things like triggering events at specific keyframes etc /// Stores information about animation markers: practical for adding things like triggering events at specific keyframes etc
@ -59,18 +61,6 @@ pub struct AnimationMarker {
#[reflect(Component)] #[reflect(Component)]
pub struct AnimationMarkers(pub HashMap<String, HashMap<u32, Vec<String>>>); pub struct AnimationMarkers(pub HashMap<String, HashMap<u32, Vec<String>>>);
// FIXME: ugh, ugly, there has to be a better way to do this ?
#[derive(Component, Default, Debug)]
pub struct AnimationMarkerTrackers(pub HashMap<String, HashMap<u32, Vec<AnimationMarkerTracker>>>);
#[derive(Default, Debug)]
pub struct AnimationMarkerTracker {
// pub frame:u32,
// pub name: String,
// pub processed_for_cycle: bool,
pub prev_frame: u32,
}
/// Event that gets triggered once a specific marker inside an animation has been reached (frame based) /// Event that gets triggered once a specific marker inside an animation has been reached (frame based)
/// Provides some usefull information about which entity , wich animation, wich frame & which marker got triggered /// Provides some usefull information about which entity , wich animation, wich frame & which marker got triggered
#[derive(Event, Debug)] #[derive(Event, Debug)]
@ -81,7 +71,6 @@ pub struct AnimationMarkerReached {
pub marker_name: String, pub marker_name: String,
} }
///////////////////// /////////////////////
/// triggers events when a given animation marker is reached for INSTANCE animations /// triggers events when a given animation marker is reached for INSTANCE animations
@ -132,15 +121,18 @@ pub fn trigger_instance_animation_markers_events(
let matching_animation_marker = &markers.0[animation_name]; let matching_animation_marker = &markers.0[animation_name];
if matching_animation_marker.contains_key(&frame) { if matching_animation_marker.contains_key(&frame) {
let matching_markers_per_frame = matching_animation_marker.get(&frame).unwrap(); let matching_markers_per_frame = matching_animation_marker.get(&frame).unwrap();
let foo = animation_length_seconds - time_in_animation;
println!("foo {}", foo);
// println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame); // println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame);
// emit an event AnimationMarkerReached(entity, animation_name, frame, marker_name) // emit an event AnimationMarkerReached(entity, animation_name, frame, marker_name)
// FIXME: problem, this can fire multiple times in a row, depending on animation length , speed , etc // FIXME: problem, this can fire multiple times in a row, depending on animation length , speed , etc
for marker_name in matching_markers_per_frame { for marker in matching_markers_per_frame {
animation_marker_events.send(AnimationMarkerReached { animation_marker_events.send(AnimationMarkerReached {
entity: entity, entity: entity,
animation_name: animation_name.clone(), animation_name: animation_name.clone(),
frame: frame, frame: frame,
marker_name: marker_name.clone(), marker_name: marker.clone(),
}); });
} }
} }
@ -151,11 +143,7 @@ pub fn trigger_instance_animation_markers_events(
/// triggers events when a given animation marker is reached for BLUEPRINT animations /// triggers events when a given animation marker is reached for BLUEPRINT animations
pub fn trigger_blueprint_animation_markers_events( pub fn trigger_blueprint_animation_markers_events(
animation_infos: Query<( animation_infos: Query<(Entity, &BlueprintAnimationPlayerLink, &BlueprintAnimations)>,
Entity,
&BlueprintAnimationPlayerLink,
&BlueprintAnimations,
)>,
// FIXME: annoying hiearchy issue yet again: the Markers & AnimationInfos are stored INSIDE the blueprint, so we need to access them differently // FIXME: annoying hiearchy issue yet again: the Markers & AnimationInfos are stored INSIDE the blueprint, so we need to access them differently
all_animation_infos: Query<(Entity, &AnimationMarkers, &AnimationInfos, &Parent)>, all_animation_infos: Query<(Entity, &AnimationMarkers, &AnimationInfos, &Parent)>,
animation_players: Query<&AnimationPlayer>, animation_players: Query<&AnimationPlayer>,
@ -167,23 +155,14 @@ pub fn trigger_blueprint_animation_markers_events(
let animation_clip = animation_clips.get(animation_player.animation_clip()); let animation_clip = animation_clips.get(animation_player.animation_clip());
// FIXME: horrible code // FIXME: horrible code
let mut markers:Option<&AnimationMarkers>= None; for (_, markers, animation_infos, parent) in all_animation_infos.iter() {
let mut animation_infos:Option<&AnimationInfos>=None;
for (_, _markers, _animation_infos, parent) in all_animation_infos.iter(){
if parent.get() == entity { if parent.get() == entity {
markers = Some(_markers); if animation_clip.is_some() {
animation_infos = Some(_animation_infos);
break;
}
}
if animation_clip.is_some() && markers.is_some() && animation_infos.is_some() {
let markers = markers.unwrap();
let animation_infos = animation_infos.unwrap();
// println!("Entity {:?} markers {:?}", entity, markers); // println!("Entity {:?} markers {:?}", entity, markers);
// println!("Player {:?} {}", animation_player.elapsed(), animation_player.completions()); // println!("Player {:?} {}", animation_player.elapsed(), animation_player.completions());
// FIMXE: yikes ! very inneficient ! perhaps add boilerplate to the "start playing animation" code so we know what is playing // FIMXE: yikes ! very inneficient ! perhaps add boilerplate to the "start playing animation" code so we know what is playing
let animation_name = animations.named_animations.iter().find_map(|(key, value)| { let animation_name =
animations.named_animations.iter().find_map(|(key, value)| {
if value == animation_player.animation_clip() { if value == animation_player.animation_clip() {
Some(key) Some(key)
} else { } else {
@ -202,26 +181,37 @@ pub fn trigger_blueprint_animation_markers_events(
// TODO: we also need to take playback speed into account // TODO: we also need to take playback speed into account
let time_in_animation = animation_player.elapsed() let time_in_animation = animation_player.elapsed()
- (animation_player.completions() as f32) * animation_length_seconds; - (animation_player.completions() as f32) * animation_length_seconds;
let frame_seconds = let frame_seconds = (animation_length_frames / animation_length_seconds)
(animation_length_frames as f32 / animation_length_seconds) * time_in_animation; * time_in_animation;
let frame = frame_seconds as u32; // println!("frame seconds {}", frame_seconds);
let frame = frame_seconds.ceil() as u32; // FIXME , bad hack
let matching_animation_marker = &markers.0[animation_name]; let matching_animation_marker = &markers.0[animation_name];
if matching_animation_marker.contains_key(&frame) { if matching_animation_marker.contains_key(&frame) {
let matching_markers_per_frame = matching_animation_marker.get(&frame).unwrap(); let matching_markers_per_frame =
matching_animation_marker.get(&frame).unwrap();
// println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame); // println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame);
// emit an event AnimationMarkerReached(entity, animation_name, frame, marker_name) // emit an event AnimationMarkerReached(entity, animation_name, frame, marker_name)
// FIXME: problem, this can fire multiple times in a row, depending on animation length , speed , etc // FIXME: complete hack-ish solution , otherwise this can fire multiple times in a row, depending on animation length , speed , etc
for marker_name in matching_markers_per_frame { let diff = frame as f32 - frame_seconds;
println!("diff {}", diff);
if diff < 0.1 {
for marker in matching_markers_per_frame {
animation_marker_events.send(AnimationMarkerReached { animation_marker_events.send(AnimationMarkerReached {
entity: entity, entity: entity,
animation_name: animation_name.clone(), animation_name: animation_name.clone(),
frame: frame, frame: frame,
marker_name: marker_name.clone(), marker_name: marker.clone(),
}); });
} }
} }
} }
} }
} }
break;
}
}
}
} }

View File

@ -178,10 +178,12 @@ impl Plugin for BlueprintsPlugin {
.chain() .chain()
.in_set(GltfBlueprintsSet::AfterSpawn), .in_set(GltfBlueprintsSet::AfterSpawn),
) )
.add_systems(
Update,
.add_systems(Update, (trigger_instance_animation_markers_events, trigger_blueprint_animation_markers_events)) (
trigger_instance_animation_markers_events,
; trigger_blueprint_animation_markers_events,
),
);
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 644 KiB

View File

@ -1,13 +1,11 @@
use std::time::Duration; use std::time::Duration;
use bevy_gltf_blueprints::{ use bevy_gltf_blueprints::{
AnimationInfos, AnimationMarkerReached, AnimationMarkerTrackers, AnimationMarkers, AnimationInfos, AnimationMarkerReached, BlueprintAnimationPlayerLink, BlueprintAnimations,
BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, BlueprintsList, InstanceAnimationPlayerLink, InstanceAnimations,
GltfBlueprintsSet, InstanceAnimationPlayerLink, InstanceAnimations,
}; };
use bevy::{gltf::Gltf, prelude::*}; use bevy::{gltf::Gltf, prelude::*};
use bevy_gltf_worlflow_examples_common_rapier::{AppState, GameState};
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
@ -38,7 +36,7 @@ pub fn setup_main_scene_animations(asset_server: Res<AssetServer>, mut commands:
pub fn animations( pub fn animations(
added_animation_players: Query<(Entity, &Name, &AnimationPlayer)>, added_animation_players: Query<(Entity, &Name, &AnimationPlayer)>,
added_animation_infos: Query<(Entity, &Name, &AnimationInfos), (Added<AnimationInfos>)>, added_animation_infos: Query<(Entity, &Name, &AnimationInfos), Added<AnimationInfos>>,
animtest: Res<AnimTest>, animtest: Res<AnimTest>,
mut commands: Commands, mut commands: Commands,
assets_gltf: Res<Assets<Gltf>>, assets_gltf: Res<Assets<Gltf>>,
@ -96,10 +94,7 @@ pub fn play_animations(
(With<AnimationInfos>, With<Marker3>), (With<AnimationInfos>, With<Marker3>),
>, >,
animated_fox: Query< animated_fox: Query<(&BlueprintAnimationPlayerLink, &BlueprintAnimations), With<MarkerFox>>,
(&BlueprintAnimationPlayerLink, &BlueprintAnimations),
(With<MarkerFox>),
>,
mut animation_players: Query<&mut AnimationPlayer>, mut animation_players: Query<&mut AnimationPlayer>,
keycode: Res<ButtonInput<KeyCode>>, keycode: Res<ButtonInput<KeyCode>>,
@ -230,7 +225,6 @@ pub fn play_animations(
} }
} }
pub fn react_to_animation_markers( pub fn react_to_animation_markers(
mut animation_marker_events: EventReader<AnimationMarkerReached>, mut animation_marker_events: EventReader<AnimationMarkerReached>,
) { ) {

View File

@ -6,18 +6,15 @@ pub use in_game::*;
use std::{collections::HashMap, fs, time::Duration}; use std::{collections::HashMap, fs, time::Duration};
use bevy_gltf_blueprints::{ use bevy_gltf_blueprints::{
AnimationInfos, AnimationMarkerReached, AnimationMarkerTrackers, AnimationMarkers, BlueprintAnimationPlayerLink, BlueprintName, BlueprintsList,
BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, BlueprintsList, GltfBlueprintsSet,
GltfBlueprintsSet, InstanceAnimationPlayerLink, InstanceAnimations,
}; };
use bevy::{ use bevy::{
ecs::query, gltf::Gltf, prelude::*, render::view::screenshot::ScreenshotManager, prelude::*, render::view::screenshot::ScreenshotManager, time::common_conditions::on_timer, window::PrimaryWindow
time::common_conditions::on_timer, window::PrimaryWindow,
}; };
use bevy_gltf_worlflow_examples_common_rapier::{AppState, GameState}; use bevy_gltf_worlflow_examples_common_rapier::{AppState, GameState};
use crate::{TupleTestF32, UnitTest};
use json_writer::to_json_string; use json_writer::to_json_string;
fn start_game(mut next_app_state: ResMut<NextState<AppState>>) { fn start_game(mut next_app_state: ResMut<NextState<AppState>>) {
@ -145,11 +142,11 @@ impl Plugin for GamePlugin {
.add_systems(Update, play_animations) .add_systems(Update, play_animations)
.add_systems(Update, react_to_animation_markers) .add_systems(Update, react_to_animation_markers)
/* .add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once .add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once
.add_systems( .add_systems(
Update, Update,
exit_game.run_if(on_timer(Duration::from_secs_f32(0.5))), exit_game.run_if(on_timer(Duration::from_secs_f32(0.5))),
) // shut down the app after this time*/ ) // shut down the app after this time
; ;
} }
} }

View File

@ -15,7 +15,7 @@ from pathlib import Path
import json import json
import bpy import bpy
from bpy.types import Context from bpy.types import Context
from bpy.props import (StringProperty, BoolProperty, PointerProperty) from bpy.props import (StringProperty, BoolProperty, IntProperty, PointerProperty)
# from .extension import ExampleExtensionProperties, GLTF_PT_UserExtensionPanel, unregister_panel # from .extension import ExampleExtensionProperties, GLTF_PT_UserExtensionPanel, unregister_panel
@ -35,61 +35,16 @@ from .ui.main import (GLTF_PT_auto_export_main,
GLTF_PT_auto_export_scenes, GLTF_PT_auto_export_scenes,
GLTF_PT_auto_export_blueprints, GLTF_PT_auto_export_blueprints,
GLTF_PT_auto_export_collections_list, GLTF_PT_auto_export_collections_list,
GLTF_PT_auto_export_gltf,
SCENE_UL_GLTF_auto_export, SCENE_UL_GLTF_auto_export,
HelloWorldOperator, GLTF_PT_auto_export_SidePanel
#GLTF_PT_export_data,
#GLTF_PT_export_data_scene
) )
from .ui.operators import (SCENES_LIST_OT_actions) from .ui.operators import (SCENES_LIST_OT_actions)
###################################################### ######################################################
""" there are two places where we load settings for auto_export from:
- in ui/main AutoExportGLTF -> invoke
- in auto_export.py -> auto_export
This is a workaround needed because of the way the settings are stored , perhaps there is a better way to deal with it ? ie by calling the AutoExportGLTF operator from the auto_export function ?
""" """
from io_scene_gltf2 import (ExportGLTF2, GLTF_PT_export_main, GLTF_PT_export_include)
class Auto_Export_SidePanel(bpy.types.Panel):
bl_idname = "Auto_Export_SidePanel"
bl_label = ""
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Auto Export"
bl_context = "objectmode"
def draw_header(self, context):
layout = self.layout
layout.label(text="Gltf auto export ")
def draw(self, context):
layout = self.layout
layout.label(text="MAKE SURE TO KEEP 'REMEMBER EXPORT SETTINGS' !!")
op = layout.operator("EXPORT_SCENE_OT_gltf", text='Gltf Settings')#'glTF 2.0 (.glb/.gltf)')
#op.export_format = 'GLTF_SEPARATE'
op.use_selection=True
op.will_save_settings=True
op.use_visible=True # Export visible and hidden objects. See Object/Batch Export to skip.
op.use_renderable=True
op.use_active_collection = True
op.use_active_collection_with_nested=True
op.use_active_scene = True
op.filepath="____dummy____"
op.gltf_export_id = "gltf_auto_export" # we specify that we are in a special case
op = layout.operator("EXPORT_SCENES_OT_auto_gltf", text="Auto Export Settings")
#op.will_save_settings=True
#print("GLTF_PT_export_main", GLTF_PT_export_main.bl_parent_id)
# glTF extensions are named following a convention with known prefixes. # glTF extensions are named following a convention with known prefixes.
# See: https://github.com/KhronosGroup/glTF/tree/main/extensions#about-gltf-extensions # See: https://github.com/KhronosGroup/glTF/tree/main/extensions#about-gltf-extensions
# also: https://github.com/KhronosGroup/glTF/blob/main/extensions/Prefixes.md # also: https://github.com/KhronosGroup/glTF/blob/main/extensions/Prefixes.md
@ -135,8 +90,7 @@ class glTF2ExportUserExtension:
def gather_gltf_hook(self, active_scene_idx, scenes, animations, export_settings): def gather_gltf_hook(self, active_scene_idx, scenes, animations, export_settings):
if self.properties.enabled: if self.properties.enabled:
print("extension enabled") print("extension enabled")
#print("gather_gltf_hook", self, active_scene_idx, scenes, animations, export_settings)"""
#print("gather_gltf_hook", self, active_scene_idx, scenes, animations, export_settings)
#see here for original gltf exporter infos https://github.com/KhronosGroup/glTF-Blender-IO/blob/main/addons/io_scene_gltf2/__init__.py #see here for original gltf exporter infos https://github.com/KhronosGroup/glTF-Blender-IO/blob/main/addons/io_scene_gltf2/__init__.py
@ -150,8 +104,6 @@ classes = [
AutoExportGLTF, AutoExportGLTF,
#AutoExportGltfAddonPreferences, #AutoExportGltfAddonPreferences,
HelloWorldOperator,
CollectionToExport, CollectionToExport,
CollectionsToExport, CollectionsToExport,
@ -161,10 +113,10 @@ classes = [
GLTF_PT_auto_export_scenes, GLTF_PT_auto_export_scenes,
GLTF_PT_auto_export_blueprints, GLTF_PT_auto_export_blueprints,
GLTF_PT_auto_export_collections_list, GLTF_PT_auto_export_collections_list,
GLTF_PT_auto_export_SidePanel,
AutoExportTracker, AutoExportTracker,
Auto_Export_SidePanel,
] ]
def glTF2_pre_export_callback(data): def glTF2_pre_export_callback(data):
@ -174,7 +126,6 @@ def glTF2_pre_export_callback(data):
def cleanup_file(): def cleanup_file():
gltf_filepath = "/home/ckaos/projects/bevy/Blender_bevy_components_worklflow/testing/bevy_example/assets/____dummy____.glb" gltf_filepath = "/home/ckaos/projects/bevy/Blender_bevy_components_worklflow/testing/bevy_example/assets/____dummy____.glb"
if os.path.exists(gltf_filepath): if os.path.exists(gltf_filepath):
print("removing dummy file", gltf_filepath)
os.remove(gltf_filepath) os.remove(gltf_filepath)
return None return None
else: else:
@ -182,8 +133,9 @@ def cleanup_file():
def glTF2_post_export_callback(data): def glTF2_post_export_callback(data):
#print("post_export", data) #print("post_export", data)
bpy.context.window_manager.auto_export_tracker.export_finished()
gltf_settings_backup = bpy.context.window_manager.gltf_settings_backup gltf_settings_backup = bpy.context.window_manager.gltf_settings_backup
print("gltf_settings_backup", gltf_settings_backup)
gltf_filepath = data["gltf_filepath"] gltf_filepath = data["gltf_filepath"]
gltf_export_id = data['gltf_export_id'] gltf_export_id = data['gltf_export_id']
if gltf_export_id == "gltf_auto_export": if gltf_export_id == "gltf_auto_export":
@ -204,10 +156,8 @@ def glTF2_post_export_callback(data):
export_settings.write(json.dumps(dict(settings))) export_settings.write(json.dumps(dict(settings)))
# now reset the original gltf_settings # now reset the original gltf_settings
if gltf_settings_backup != "": if gltf_settings_backup != "":
print("resetting original gltf settings")
scene["glTF2ExportSettings"] = json.loads(gltf_settings_backup) scene["glTF2ExportSettings"] = json.loads(gltf_settings_backup)
else: else:
print("no pre_existing settings")
if "glTF2ExportSettings" in scene: if "glTF2ExportSettings" in scene:
del scene["glTF2ExportSettings"] del scene["glTF2ExportSettings"]
bpy.context.window_manager.gltf_settings_backup = "" bpy.context.window_manager.gltf_settings_backup = ""
@ -241,8 +191,12 @@ def register():
bpy.types.TOPBAR_MT_file_export.append(menu_func_import) bpy.types.TOPBAR_MT_file_export.append(menu_func_import)
bpy.types.WindowManager.gltf_settings_backup = StringProperty(default="") bpy.types.WindowManager.gltf_settings_backup = StringProperty(default="")
bpy.utils.register_class(AutoExportExtensionProperties) # FIXME: perhaps move this to tracker
bpy.types.Scene.AutoExportExtensionProperties = bpy.props.PointerProperty(type=AutoExportExtensionProperties) bpy.types.WindowManager.exports_count = IntProperty(default=0)
"""bpy.utils.register_class(AutoExportExtensionProperties)
bpy.types.Scene.AutoExportExtensionProperties = bpy.props.PointerProperty(type=AutoExportExtensionProperties)"""
def unregister(): def unregister():
for cls in classes: for cls in classes:
@ -252,7 +206,8 @@ def unregister():
bpy.app.handlers.depsgraph_update_post.remove(post_update) bpy.app.handlers.depsgraph_update_post.remove(post_update)
bpy.app.handlers.save_post.remove(post_save) bpy.app.handlers.save_post.remove(post_save)
bpy.utils.unregister_class(AutoExportExtensionProperties) """bpy.utils.unregister_class(AutoExportExtensionProperties)"""
del bpy.types.WindowManager.exports_count
if "gltf_auto_export" == "__main__": if "gltf_auto_export" == "__main__":
register() register()

View File

@ -1,172 +0,0 @@
import bpy
from bpy.props import (BoolProperty,
IntProperty,
StringProperty,
EnumProperty,
CollectionProperty
)
bl_info = {
"name": "auto_export",
"category": "Generic",
"version": (1, 0, 0),
"blender": (2, 80, 0),
'location': 'File > Export > glTF 2.0',
'description': 'Example addon to add a custom extension to an exported glTF file.',
'tracker_url': "https://github.com/KhronosGroup/glTF-Blender-IO/issues/", # Replace with your issue tracker
'isDraft': False,
'developer': "(Your name here)", # Replace this
'url': 'https://your_url_here', # Replace this
}
# glTF extensions are named following a convention with known prefixes.
# See: https://github.com/KhronosGroup/glTF/tree/main/extensions#about-gltf-extensions
# also: https://github.com/KhronosGroup/glTF/blob/main/extensions/Prefixes.md
glTF_extension_name = "EXT_auto_export"
# Support for an extension is "required" if a typical glTF viewer cannot be expected
# to load a given model without understanding the contents of the extension.
# For example, a compression scheme or new image format (with no fallback included)
# would be "required", but physics metadata or app-specific settings could be optional.
extension_is_required = False
from io_scene_gltf2 import (GLTF_PT_export_main, GLTF_PT_export_include)
class ExampleExtensionProperties(bpy.types.PropertyGroup):
enabled: bpy.props.BoolProperty(
name=bl_info["name"],
description='Include this extension in the exported glTF file.',
default=True
)
auto_export_main_scene_name: StringProperty(
name='Main scene',
description='The name of the main scene/level/world to auto export',
default='Scene'
)
auto_export_output_folder: StringProperty(
name='Export folder (relative)',
description='The root folder for all exports(relative to current file) Defaults to current folder',
default=''
)
auto_export_library_scene_name: StringProperty(
name='Library scene',
description='The name of the library scene to auto export',
default='Library'
)
# scene components
auto_export_scene_settings: BoolProperty(
name='Export scene settings',
description='Export scene settings ie AmbientLighting, Bloom, AO etc',
default=False
)
# blueprint settings
auto_export_blueprints: BoolProperty(
name='Export Blueprints',
description='Replaces collection instances with an Empty with a BlueprintName custom property',
default=True
)
auto_export_blueprints_path: StringProperty(
name='Blueprints path',
description='path to export the blueprints to (relative to the Export folder)',
default='library'
)
auto_export_materials_library: BoolProperty(
name='Export materials library',
description='remove materials from blueprints and use the material library instead',
default=False
)
auto_export_materials_path: StringProperty(
name='Materials path',
description='path to export the materials libraries to (relative to the root folder)',
default='materials'
)
def register():
bpy.utils.register_class(ExampleExtensionProperties)
bpy.types.Scene.ExampleExtensionProperties = bpy.props.PointerProperty(type=ExampleExtensionProperties)
def register_panel():
# Register the panel on demand, we need to be sure to only register it once
# This is necessary because the panel is a child of the extensions panel,
# which may not be registered when we try to register this extension
try:
bpy.utils.register_class(GLTF_PT_UserExtensionPanel)
except Exception:
pass
# If the glTF exporter is disabled, we need to unregister the extension panel
# Just return a function to the exporter so it can unregister the panel
return unregister_panel
def unregister_panel():
# Since panel is registered on demand, it is possible it is not registered
try:
bpy.utils.unregister_class(GLTF_PT_UserExtensionPanel)
except Exception:
pass
def unregister():
unregister_panel()
bpy.utils.unregister_class(ExampleExtensionProperties)
del bpy.types.Scene.ExampleExtensionProperties
class GLTF_PT_UserExtensionPanel(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Enabled"
bl_parent_id = "GLTF_PT_export_user_extensions"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
def draw_header(self, context):
props = bpy.context.scene.ExampleExtensionProperties
self.layout.prop(props, 'enabled')
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
props = bpy.context.scene.ExampleExtensionProperties
layout.active = props.enabled
props = bpy.context.scene.ExampleExtensionProperties
for bla in props.__annotations__:
layout.prop(props, bla)
class glTF2ExportUserExtension:
def __init__(self):
# We need to wait until we create the gltf2UserExtension to import the gltf2 modules
# Otherwise, it may fail because the gltf2 may not be loaded yet
from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
self.Extension = Extension
self.properties = bpy.context.scene.ExampleExtensionProperties
def gather_node_hook(self, gltf2_object, blender_object, export_settings):
if self.properties.enabled:
if gltf2_object.extensions is None:
gltf2_object.extensions = {}
print("bla bla")
gltf2_object.extensions[glTF_extension_name] = self.Extension(
name=glTF_extension_name,
extension={"auto_export_blueprints": self.properties.auto_export_blueprints},
required=extension_is_required
)
def glTF2_pre_export_callback(data):
print("pre_export", data)
def glTF2_post_export_callback(data):
print("post_export", data)

View File

@ -22,7 +22,6 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
# Get the folder # Get the folder
folder_path = os.path.dirname(file_path) folder_path = os.path.dirname(file_path)
# get the preferences for our addon # get the preferences for our addon
#should we use change detection or not #should we use change detection or not
export_change_detection = getattr(addon_prefs, "export_change_detection") export_change_detection = getattr(addon_prefs, "export_change_detection")
@ -108,6 +107,13 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
export_materials(collections, library_scenes, folder_path, addon_prefs) export_materials(collections, library_scenes, folder_path, addon_prefs)
main_scenes_to_export = [scene_name for scene_name in main_scene_names if not export_change_detection or changed_export_parameters or scene_name in changes_per_scene.keys() or not check_if_blueprint_on_disk(scene_name, export_levels_path, gltf_extension)]
bpy.context.window_manager.exports_count = len(collections_to_export)
bpy.context.window_manager.exports_count += len(main_scenes_to_export)
if export_materials_library:
bpy.context.window_manager.exports_count += 1
print("--------------") print("--------------")
print("collections: all:", collections) print("collections: all:", collections)
print("collections: changed:", changed_collections) print("collections: changed:", changed_collections)
@ -115,6 +121,9 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
print("collections: in library:", library_collections) print("collections: in library:", library_collections)
print("collections: to export:", collections_to_export) print("collections: to export:", collections_to_export)
print("collections: per_scene:", collections_per_scene) print("collections: per_scene:", collections_per_scene)
print("--------------")
print("MAIN SCENES TO EXPORT", main_scenes_to_export)
# backup current active scene # backup current active scene
old_current_scene = bpy.context.scene old_current_scene = bpy.context.scene

View File

@ -2,7 +2,6 @@ import json
import os import os
import bpy import bpy
from .get_standard_exporter_settings import get_standard_exporter_settings
from .preferences import (AutoExportGltfPreferenceNames) from .preferences import (AutoExportGltfPreferenceNames)
def generate_gltf_export_preferences(addon_prefs): def generate_gltf_export_preferences(addon_prefs):
@ -52,7 +51,7 @@ def generate_gltf_export_preferences(addon_prefs):
standard_gltf_exporter_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings") standard_gltf_exporter_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings")
standard_gltf_exporter_settings = json.loads(standard_gltf_exporter_settings.as_string()) standard_gltf_exporter_settings = json.loads(standard_gltf_exporter_settings.as_string())
"""standard_gltf_exporter_settings = get_standard_exporter_settings()""" """standard_gltf_exporter_settings = get_standard_exporter_settings()"""
print("standard settings", standard_gltf_exporter_settings) #print("standard settings", standard_gltf_exporter_settings)
constant_keys = [ constant_keys = [
'use_selection', 'use_selection',
@ -70,7 +69,7 @@ def generate_gltf_export_preferences(addon_prefs):
if str(key) not in constant_keys: if str(key) not in constant_keys:
gltf_export_preferences[key] = standard_gltf_exporter_settings.get(key) gltf_export_preferences[key] = standard_gltf_exporter_settings.get(key)
print("") print("")
print("final export preferences", gltf_export_preferences) print("export preferences for gltf exporter", gltf_export_preferences)
return gltf_export_preferences return gltf_export_preferences

View File

@ -1,9 +0,0 @@
import bpy
def get_standard_exporter_settings():
settings_key = 'glTF2ExportSettings'
for scene in bpy.data.scenes:
if settings_key in scene:
settings = scene[settings_key]
#print("standard exporter settings", settings, dict(settings))
return dict(settings)

View File

@ -115,10 +115,10 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper):
self.will_save_settings = False self.will_save_settings = False
if settings: if settings:
print("loading settings in invoke AutoExportGLTF", settings) #print("loading settings in invoke AutoExportGLTF", settings)
try: try:
for (k, v) in settings.items(): for (k, v) in settings.items():
print("loading setting", k, v) #print("loading setting", k, v)
setattr(self, k, v) setattr(self, k, v)
self.will_save_settings = True self.will_save_settings = True
@ -150,7 +150,6 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper):
This should ONLY be run when actually doing exports/aka calling auto_export function, because we only care about the difference in settings between EXPORTS This should ONLY be run when actually doing exports/aka calling auto_export function, because we only care about the difference in settings between EXPORTS
""" """
def did_export_settings_change(self): def did_export_settings_change(self):
print("comparing settings")
# compare both the auto export settings & the gltf settings # compare both the auto export settings & the gltf settings
previous_auto_settings = bpy.data.texts[".gltf_auto_export_settings_previous"] if ".gltf_auto_export_settings_previous" in bpy.data.texts else None previous_auto_settings = bpy.data.texts[".gltf_auto_export_settings_previous"] if ".gltf_auto_export_settings_previous" in bpy.data.texts else None
previous_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings_previous"] if ".gltf_auto_export_gltf_settings_previous" in bpy.data.texts else None previous_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings_previous"] if ".gltf_auto_export_gltf_settings_previous" in bpy.data.texts else None
@ -179,51 +178,42 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper):
changed = auto_settings_changed or gltf_settings_changed changed = auto_settings_changed or gltf_settings_changed
# now write the current settings to the "previous settings" # now write the current settings to the "previous settings"
if current_auto_settings != None:
previous_auto_settings = bpy.data.texts[".gltf_auto_export_settings_previous"] if ".gltf_auto_export_settings_previous" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings_previous") previous_auto_settings = bpy.data.texts[".gltf_auto_export_settings_previous"] if ".gltf_auto_export_settings_previous" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings_previous")
previous_auto_settings.clear() previous_auto_settings.clear()
previous_auto_settings.write(current_auto_settings.as_string()) # TODO : check if this is always valid previous_auto_settings.write(current_auto_settings.as_string()) # TODO : check if this is always valid
if current_gltf_settings != None:
previous_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings_previous"] if ".gltf_auto_export_gltf_settings_previous" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings_previous") previous_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings_previous"] if ".gltf_auto_export_gltf_settings_previous" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings_previous")
previous_gltf_settings.clear() previous_gltf_settings.clear()
previous_gltf_settings.write(current_gltf_settings.as_string()) previous_gltf_settings.write(current_gltf_settings.as_string())
print("changed", changed)
return changed return changed
"""# if there was no setting before, it is new, we need export
print("changed settings IN OPERATOR", changed_gltf_settings, previous_export_settings)
if previous_export_settings == None:
return True # we can disregard the gltf settings, we need to save either way
else:
export_settings = self.format_settings()
if len(export_settings.keys()) == 0: # first time after we already used the addon, since we already have export settings, but they have not yet been applied
return changed_gltf_settings
print("previous", sorted(json.loads(previous_export_settings.as_string()).items()))
print("current", sorted(export_settings.items()))
changed = sorted(json.loads(previous_export_settings.as_string()).items()) != sorted(export_settings.items())
print("changed FINAL: auto_settings", changed, "gltf_settings", changed_gltf_settings, "combo", changed or changed_gltf_settings)
return changed and changed_gltf_settings"""
def execute(self, context): def execute(self, context):
# disable change detection while the operator runs # disable change detection while the operator runs
bpy.context.window_manager.auto_export_tracker.disable_change_detection() bpy.context.window_manager.auto_export_tracker.disable_change_detection()
if self.direct_mode: if self.direct_mode:
self.load_settings(context) self.load_settings(context)
if self.will_save_settings: if self.will_save_settings:
print("SAVING SETTINGS")
self.save_settings(context) self.save_settings(context)
changes_per_scene = context.window_manager.auto_export_tracker.changed_objects_per_scene changes_per_scene = context.window_manager.auto_export_tracker.changed_objects_per_scene
if self.auto_export: # only do the actual exporting if auto export is actually enabled
#& do the export #& do the export
if self.direct_mode: #Do not auto export when applying settings in the menu, do it on save only if self.direct_mode: #Do not auto export when applying settings in the menu, do it on save only
#determine changed parameters #determine changed parameters
params_changed = self.did_export_settings_change() params_changed = self.did_export_settings_change()
auto_export(changes_per_scene, params_changed, self) auto_export(changes_per_scene, params_changed, self)
# cleanup # cleanup
if bpy.context.window_manager.exports_count == 0: # we need this in case there was nothing to export, to make sure change detection is enabled again
print("YOLOOO")
bpy.context.window_manager.auto_export_tracker.enable_change_detection()
#bpy.context.window_manager.auto_export_tracker.enable_change_detection()
# FIXME: wrong logic, this should be called only in an glTF2_post_export_callback
bpy.app.timers.register(bpy.context.window_manager.auto_export_tracker.enable_change_detection, first_interval=1) bpy.app.timers.register(bpy.context.window_manager.auto_export_tracker.enable_change_detection, first_interval=1)
else:
print("auto export disabled, skipping")
return {'FINISHED'} return {'FINISHED'}
def invoke(self, context, event): def invoke(self, context, event):

View File

@ -64,7 +64,7 @@ class AutoExportGltfAddonPreferences(AddonPreferences):
auto_export: BoolProperty( auto_export: BoolProperty(
name='Auto export', name='Auto export',
description='Automatically export to gltf on save', description='Automatically export to gltf on save',
default=True default=False
) )
export_main_scene_name: StringProperty( export_main_scene_name: StringProperty(
name='Main scene', name='Main scene',

View File

@ -50,11 +50,10 @@ class AutoExportTracker(PropertyGroup):
# reset whether there have been changed objects since the last save # reset whether there have been changed objects since the last save
cls.changed_objects_per_scene.clear() cls.changed_objects_per_scene.clear()
# all our logic is done, mark this as done # all our logic is done, mark this as done
print("EXPORT DONE")
@classmethod @classmethod
def deps_update_handler(cls, scene, depsgraph): def deps_update_handler(cls, scene, depsgraph):
# print("change detection enabled", cls.change_detection_enabled) print("change detection enabled", cls.change_detection_enabled)
active_operator = bpy.context.active_operator active_operator = bpy.context.active_operator
if active_operator: if active_operator:
# print("Operator", active_operator.bl_label, active_operator.bl_idname) # print("Operator", active_operator.bl_label, active_operator.bl_idname)
@ -69,10 +68,11 @@ class AutoExportTracker(PropertyGroup):
active_operator.will_save_settings = True active_operator.will_save_settings = True
# we set the last operator here so we can clear the specific settings (yeah for overly complex logic) # we set the last operator here so we can clear the specific settings (yeah for overly complex logic)
cls.last_operator = active_operator cls.last_operator = active_operator
print("active_operator", active_operator.has_active_exporter_extensions, active_operator.__annotations__.keys(), active_operator.filepath, active_operator.gltf_export_id) #print("active_operator", active_operator.has_active_exporter_extensions, active_operator.__annotations__.keys(), active_operator.filepath, active_operator.gltf_export_id)
if active_operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf": if active_operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf":
# we force saving params # we force saving params
active_operator.will_save_settings = True active_operator.will_save_settings = True
active_operator.auto_export = True
if scene.name != "temp_scene": if scene.name != "temp_scene":
# print("depsgraph_update_post", scene.name) # print("depsgraph_update_post", scene.name)
@ -90,7 +90,7 @@ class AutoExportTracker(PropertyGroup):
if isinstance(obj.id, bpy.types.Object): if isinstance(obj.id, bpy.types.Object):
# get the actual object # get the actual object
object = bpy.data.objects[obj.id.name] object = bpy.data.objects[obj.id.name]
# print("changed object", obj.id.name) #print("changed object", obj.id.name)
cls.changed_objects_per_scene[scene.name][obj.id.name] = object cls.changed_objects_per_scene[scene.name][obj.id.name] = object
elif isinstance(obj.id, bpy.types.Material): # or isinstance(obj.id, bpy.types.ShaderNodeTree): elif isinstance(obj.id, bpy.types.Material): # or isinstance(obj.id, bpy.types.ShaderNodeTree):
# print("changed material", obj.id, "scene", scene.name,) # print("changed material", obj.id, "scene", scene.name,)
@ -117,7 +117,16 @@ class AutoExportTracker(PropertyGroup):
def disable_change_detection(self,): def disable_change_detection(self,):
self.change_detection_enabled = False self.change_detection_enabled = False
self.__class__.change_detection_enabled = False self.__class__.change_detection_enabled = False
return None
def enable_change_detection(self): def enable_change_detection(self):
self.change_detection_enabled = True self.change_detection_enabled = True
self.__class__.change_detection_enabled = True self.__class__.change_detection_enabled = True
return None
def export_finished(self):
print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHHHHHHH export_finished")
bpy.context.window_manager.exports_count -= 1
if bpy.context.window_manager.exports_count == 0:
print("YOLOOO")
self.enable_change_detection()

View File

@ -205,7 +205,7 @@ def clear_hollow_scene(temp_scene, original_root_collection):
temp_root_collection = temp_scene.collection temp_root_collection = temp_scene.collection
temp_scene_objects = [o for o in temp_root_collection.all_objects] temp_scene_objects = [o for o in temp_root_collection.all_objects]
for object in temp_scene_objects: for object in temp_scene_objects:
print("removing", object.name) #print("removing", object.name)
bpy.data.objects.remove(object, do_unlink=True) bpy.data.objects.remove(object, do_unlink=True)
# remove the temporary scene # remove the temporary scene
bpy.data.scenes.remove(temp_scene, do_unlink=True) bpy.data.scenes.remove(temp_scene, do_unlink=True)

View File

@ -361,3 +361,42 @@ def invoke_override(self, context, event):
wm = context.window_manager wm = context.window_manager
wm.fileselect_add(self) wm.fileselect_add(self)
return {'RUNNING_MODAL'} return {'RUNNING_MODAL'}
from io_scene_gltf2 import (ExportGLTF2, GLTF_PT_export_main, GLTF_PT_export_include)
from io_scene_gltf2 import (ExportGLTF2, GLTF_PT_export_main,ExportGLTF2_Base, GLTF_PT_export_include)
import io_scene_gltf2 as gltf_exporter_original
#import io_scene_gltf2.GLTF_PT_export_data_scene as GLTF_PT_export_data_scene_original
"""
class GLTF_PT_export_data(gltf_exporter_original.GLTF_PT_export_data):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Data"
bl_parent_id = "GLTF_PT_auto_export_gltf"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf"
class GLTF_PT_export_data_scene(gltf_exporter_original.GLTF_PT_export_data_scene):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Scene Graph"
bl_parent_id = "GLTF_PT_export_data"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf"
def draw(self, context):
return super().draw(context)"""

View File

@ -1 +1 @@
{"b_Tail02_013":["b_Tail03_014"],"Blueprint4_nested.001":["Blueprint3"],"Collection 2 1":["Empty_in_sub_collection"],"b_Root_00":["b_Hip_01"],"b_LeftForeArm_010":["b_LeftHand_011"],"b_Spine01_02":["b_Spine02_03"],"Blueprint7_hierarchy.001":["Blueprint4_nested.001","Cube.001"],"b_RightLeg01_019":["b_RightLeg02_020"],"b_LeftUpperArm_09":["b_LeftForeArm_010"],"no_name":["Parent_Object","lighting_components_World","assets_list_World_components","Collection","Collection 2"],"Blueprint3":["Blueprint3_mesh","Blueprint3_mesh"],"world":["no_name"],"Parent_Object":["Cube.003","Blueprint1","Cylinder.001"],"Light":["Light","DirectionalLight Gizmo"],"Blueprint1.001":["Blueprint1_mesh"],"Blueprint7_hierarchy":["Cube.001"],"Spot":["Spot"],"b_Hip_01":["b_Spine01_02","b_Tail01_012","b_LeftLeg01_015","b_RightLeg01_019"],"Cylinder":["Cylinder.001","Cylinder.001"],"Collection 2":["Collection 2 1","Empty_in_collection","Spot"],"b_RightForeArm_07":["b_RightHand_08"],"Blueprint3_mesh":["Cylinder","Cylinder"],"Blueprint4_nested":["Blueprint3"],"Fox_mesh":["fox1"],"b_LeftLeg01_015":["b_LeftLeg02_016"],"b_Neck_04":["b_Head_05"],"b_RightFoot01_021":["b_RightFoot02_022"],"Blueprint1_mesh":["Cube.001","Cube.001"],"b_Tail01_012":["b_Tail02_013"],"Fox":["Fox_mesh","_rootJoint"],"Collection":["Blueprint1.001","Blueprint4_nested","Blueprint6_animated","Blueprint7_hierarchy","Camera","Cube","Empty","Light","Plane"],"Cube":["Cube"],"_rootJoint":["b_Root_00"],"b_RightLeg02_020":["b_RightFoot01_021"],"b_RightUpperArm_06":["b_RightForeArm_07"],"Plane":["Plane"],"Camera":["Camera Gizmo"],"Blueprint6_animated":["Fox"],"b_Spine02_03":["b_Neck_04","b_RightUpperArm_06","b_LeftUpperArm_09"],"b_LeftLeg02_016":["b_LeftFoot01_017"],"b_LeftFoot01_017":["b_LeftFoot02_018"],"Cube.001":["Cube.002","Cylinder","Cube.002","Cylinder"],"Cylinder.001":["Cylinder.002","Blueprint7_hierarchy.001","Empty_as_child"],"Blueprint1":["Blueprint1_mesh"]} {"Blueprint6_animated":["Fox"],"Collection 2 1":["Empty_in_sub_collection"],"Blueprint1.001":["Blueprint1_mesh"],"Blueprint3":["Blueprint3_mesh","Blueprint3_mesh"],"b_Spine02_03":["b_Neck_04","b_RightUpperArm_06","b_LeftUpperArm_09","b_Neck_04","b_RightUpperArm_06","b_LeftUpperArm_09"],"_rootJoint":["b_Root_00","b_Root_00"],"Cube.001":["Cube.002","Cylinder","Cube.002","Cylinder"],"b_Root_00":["b_Hip_01","b_Hip_01"],"Collection 2":["Blueprint8_animated_no_bones","Collection 2 1","Empty_in_collection","Spot"],"Blueprint6_animated.001":["Fox"],"Collection":["Blueprint1.001","Blueprint4_nested","Blueprint6_animated","Blueprint7_hierarchy","Camera","Cube","Empty","Light","Plane"],"Camera":["Camera Gizmo"],"Blueprint7_hierarchy.001":["Blueprint4_nested.001","Cube.001"],"b_Spine01_02":["b_Spine02_03","b_Spine02_03"],"Light":["Light","DirectionalLight Gizmo"],"Cylinder":["Cylinder.001","Cylinder.001"],"b_Tail01_012":["b_Tail02_013","b_Tail02_013"],"b_RightLeg01_019":["b_RightLeg02_020","b_RightLeg02_020"],"b_LeftFoot01_017":["b_LeftFoot02_018","b_LeftFoot02_018"],"Cube":["Cube"],"b_Hip_01":["b_Spine01_02","b_Tail01_012","b_LeftLeg01_015","b_RightLeg01_019","b_Spine01_02","b_Tail01_012","b_LeftLeg01_015","b_RightLeg01_019"],"b_LeftForeArm_010":["b_LeftHand_011","b_LeftHand_011"],"Fox":["Fox_mesh","_rootJoint","Fox_mesh","_rootJoint"],"Blueprint8_animated_no_bones":["Cylinder.002"],"Parent_Object":["Cube.003","Blueprint1","Cylinder.001"],"b_RightUpperArm_06":["b_RightForeArm_07","b_RightForeArm_07"],"world":["no_name"],"b_LeftLeg01_015":["b_LeftLeg02_016","b_LeftLeg02_016"],"Spot":["Spot"],"b_RightFoot01_021":["b_RightFoot02_022","b_RightFoot02_022"],"Blueprint7_hierarchy":["Cube.001"],"b_RightLeg02_020":["b_RightFoot01_021","b_RightFoot01_021"],"Blueprint4_nested":["Blueprint3"],"Fox_mesh":["fox1","fox1"],"b_RightForeArm_07":["b_RightHand_08","b_RightHand_08"],"Blueprint3_mesh":["Cylinder","Cylinder"],"Blueprint1_mesh":["Cube.001","Cube.001"],"b_Neck_04":["b_Head_05","b_Head_05"],"b_LeftUpperArm_09":["b_LeftForeArm_010","b_LeftForeArm_010"],"no_name":["Parent_Object","Blueprint6_animated.001","lighting_components_World","assets_list_World_components","Collection","Collection 2"],"Cylinder.002":["Cylinder.003"],"Blueprint4_nested.001":["Blueprint3"],"Blueprint1":["Blueprint1_mesh"],"Cylinder.001":["Cylinder.002","Blueprint7_hierarchy.001","Empty_as_child"],"b_Tail02_013":["b_Tail03_014","b_Tail03_014"],"b_LeftLeg02_016":["b_LeftFoot01_017","b_LeftFoot01_017"],"Plane":["Plane"]}

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 KiB

View File

@ -45,10 +45,11 @@ def setup_data(request):
""" """
- removes existing gltf files if needed
- calls exporter on the testing scene - calls exporter on the testing scene
- launches bevy app & checks for output - launches bevy app & checks for output
- checks screenshot, hierarchy & diagnostics files generated on the bevy side against reference files
- if all worked => test is a-ok - if all worked => test is a-ok
- removes generated files
""" """
def test_export_complex(setup_data): def test_export_complex(setup_data):
root_path = "../../testing/bevy_example" root_path = "../../testing/bevy_example"
@ -62,10 +63,10 @@ def test_export_complex(setup_data):
export_props = { export_props = {
"main_scene_names" : ['World'], "main_scene_names" : ['World'],
"library_scene_names": ['Library'], "library_scene_names": ['Library'],
# "export_format":'GLTF_SEPARATE'
} }
gltf_settings = { gltf_settings = {
"export_animations": True "export_animations": True,
"export_optimize_animation_size": False
} }
# store settings for the auto_export part # store settings for the auto_export part
@ -76,7 +77,7 @@ def test_export_complex(setup_data):
# and store settings for the gltf part # and store settings for the gltf part
stored_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings") stored_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings")
stored_gltf_settings.clear() stored_gltf_settings.clear()
stored_gltf_settings.write(str(gltf_settings)) stored_gltf_settings.write(json.dumps(gltf_settings))
# move the main cube # move the main cube
bpy.data.objects["Cube"].location = [1, 0, 0] bpy.data.objects["Cube"].location = [1, 0, 0]
@ -84,6 +85,7 @@ def test_export_complex(setup_data):
bpy.data.objects["Blueprint1_mesh"].location = [1, 2, 1] bpy.data.objects["Blueprint1_mesh"].location = [1, 2, 1]
auto_export_operator( auto_export_operator(
auto_export=True,
direct_mode=True, direct_mode=True,
export_output_folder="./models", export_output_folder="./models",
export_scene_settings=True, export_scene_settings=True,
@ -133,7 +135,7 @@ def test_export_complex(setup_data):
assert sorted(hierarchy.items()) == sorted(expected.items()) assert sorted(hierarchy.items()) == sorted(expected.items())
# last but not least, do a visual compare # last but not least, do a visual compare
screenshot_expected_path = os.path.join(root_path, "expected_screenshot.png") screenshot_expected_path = os.path.join(os.path.dirname(__file__), "expected_screenshot.png")
screenshot_observed_path = os.path.join(root_path, "screenshot.png") screenshot_observed_path = os.path.join(root_path, "screenshot.png")
img_a = Image.open(screenshot_expected_path) img_a = Image.open(screenshot_expected_path)
img_b = Image.open(screenshot_observed_path) img_b = Image.open(screenshot_observed_path)

View File

@ -0,0 +1,219 @@
import bpy
import os
import json
import pytest
import shutil
@pytest.fixture
def setup_data(request):
print("\nSetting up resources...")
def finalizer():
root_path = "../../testing/bevy_example"
assets_root_path = os.path.join(root_path, "assets")
models_path = os.path.join(assets_root_path, "models")
materials_path = os.path.join(assets_root_path, "materials")
#other_materials_path = os.path.join("../../testing", "other_materials")
print("\nPerforming teardown...")
if os.path.exists(models_path):
shutil.rmtree(models_path)
if os.path.exists(materials_path):
shutil.rmtree(materials_path)
diagnostics_file_path = os.path.join(root_path, "bevy_diagnostics.json")
if os.path.exists(diagnostics_file_path):
os.remove(diagnostics_file_path)
hierarchy_file_path = os.path.join(root_path, "bevy_hierarchy.json")
if os.path.exists(hierarchy_file_path):
os.remove(hierarchy_file_path)
screenshot_observed_path = os.path.join(root_path, "screenshot.png")
if os.path.exists(screenshot_observed_path):
os.remove(screenshot_observed_path)
request.addfinalizer(finalizer)
return None
import bpy
import os
import json
import pytest
import shutil
@pytest.fixture
def setup_data(request):
print("\nSetting up resources...")
def finalizer():
root_path = "../../testing/bevy_example"
assets_root_path = os.path.join(root_path, "assets")
models_path = os.path.join(assets_root_path, "models")
materials_path = os.path.join(assets_root_path, "materials")
#other_materials_path = os.path.join("../../testing", "other_materials")
print("\nPerforming teardown...")
if os.path.exists(models_path):
shutil.rmtree(models_path)
if os.path.exists(materials_path):
shutil.rmtree(materials_path)
diagnostics_file_path = os.path.join(root_path, "bevy_diagnostics.json")
if os.path.exists(diagnostics_file_path):
os.remove(diagnostics_file_path)
hierarchy_file_path = os.path.join(root_path, "bevy_hierarchy.json")
if os.path.exists(hierarchy_file_path):
os.remove(hierarchy_file_path)
screenshot_observed_path = os.path.join(root_path, "screenshot.png")
if os.path.exists(screenshot_observed_path):
os.remove(screenshot_observed_path)
request.addfinalizer(finalizer)
return None
"""
- 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
- checks if timestamps have changed
- if all worked => test is a-ok
- removes generated files
"""
def test_export_changed_parameters(setup_data):
root_path = "../../testing/bevy_example"
assets_root_path = os.path.join(root_path, "assets")
models_path = os.path.join(assets_root_path, "models")
auto_export_operator = bpy.ops.export_scenes.auto_gltf
# with change detection
# first, configure things
# we use the global settings for that
export_props = {
"main_scene_names" : ['World'],
"library_scene_names": ['Library'],
}
# store settings for the auto_export part
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")
stored_auto_settings.clear()
stored_auto_settings.write(json.dumps(export_props))
gltf_settings = {
"export_animations": False,
"export_optimize_animation_size": False
}
# and store settings for the gltf part
stored_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings")
stored_gltf_settings.clear()
stored_gltf_settings.write(json.dumps(gltf_settings))
auto_export_operator(
auto_export=True,
direct_mode=True,
export_output_folder="./models",
export_scene_settings=True,
export_blueprints=True,
export_legacy_mode=False,
export_materials_library=False
)
world_file_path = os.path.join(models_path, "World.glb")
assert os.path.exists(world_file_path) == True
models_library_path = os.path.join(models_path, "library")
model_library_file_paths = list(map(lambda file_name: os.path.join(models_library_path, file_name), sorted(os.listdir(models_library_path))))
modification_times_first = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths + [world_file_path]))
#print("files", model_library_file_paths)
#print("mod times", modification_times_first)
auto_export_operator(
auto_export=True,
direct_mode=True,
export_output_folder="./models",
export_scene_settings=True,
export_blueprints=True,
export_legacy_mode=False,
export_materials_library=False
)
modification_times_no_change = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths + [world_file_path]))
assert modification_times_no_change == modification_times_first
# now move the main cube & export again
print("----------------")
print("main scene change")
print("----------------")
#py.context.window_manager.auto_export_tracker.enable_change_detection() # FIXME: should not be needed, but ..
bpy.data.objects["Cube"].location = [1, 0, 0]
auto_export_operator(
auto_export=True,
direct_mode=True,
export_output_folder="./models",
export_scene_settings=True,
export_blueprints=True,
export_legacy_mode=False,
export_materials_library=False
)
modification_times = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths + [world_file_path]))
assert modification_times != modification_times_first
modification_times_first = modification_times
# now same, but move the cube in the library
print("----------------")
print("library change")
print("----------------")
bpy.data.objects["Blueprint1_mesh"].location = [1, 2, 1]
auto_export_operator(
auto_export=True,
direct_mode=True,
export_output_folder="./models",
export_scene_settings=True,
export_blueprints=True,
export_legacy_mode=False,
export_materials_library=False
)
modification_times = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths + [world_file_path]))
assert modification_times != modification_times_first
modification_times_first = modification_times
# now same, but using an operator
print("----------------")
print("change using operator")
print("----------------")
bpy.ops.transform.translate(value=(20.0, 0.0, 0.0))
auto_export_operator(
auto_export=True,
direct_mode=True,
export_output_folder="./models",
export_scene_settings=True,
export_blueprints=True,
export_legacy_mode=False,
export_materials_library=False
)
modification_times = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths + [world_file_path]))
assert modification_times != modification_times_first
modification_times_first = modification_times

View File

@ -0,0 +1,209 @@
import bpy
import os
import json
import pytest
import shutil
@pytest.fixture
def setup_data(request):
print("\nSetting up resources...")
def finalizer():
root_path = "../../testing/bevy_example"
assets_root_path = os.path.join(root_path, "assets")
models_path = os.path.join(assets_root_path, "models")
materials_path = os.path.join(assets_root_path, "materials")
#other_materials_path = os.path.join("../../testing", "other_materials")
print("\nPerforming teardown...")
if os.path.exists(models_path):
shutil.rmtree(models_path)
if os.path.exists(materials_path):
shutil.rmtree(materials_path)
diagnostics_file_path = os.path.join(root_path, "bevy_diagnostics.json")
if os.path.exists(diagnostics_file_path):
os.remove(diagnostics_file_path)
hierarchy_file_path = os.path.join(root_path, "bevy_hierarchy.json")
if os.path.exists(hierarchy_file_path):
os.remove(hierarchy_file_path)
screenshot_observed_path = os.path.join(root_path, "screenshot.png")
if os.path.exists(screenshot_observed_path):
os.remove(screenshot_observed_path)
request.addfinalizer(finalizer)
return None
"""
- setup gltf parameters & auto_export parameters
- calls exporter on the testing scene
- saves timestamps of generated files
- changes exporter parameters
- checks if timestamps have changed
- if all worked => test is a-ok
- removes generated files
"""
def test_export_changed_parameters(setup_data):
root_path = "../../testing/bevy_example"
assets_root_path = os.path.join(root_path, "assets")
models_path = os.path.join(assets_root_path, "models")
auto_export_operator = bpy.ops.export_scenes.auto_gltf
# with change detection
# first, configure things
# we use the global settings for that
export_props = {
"main_scene_names" : ['World'],
"library_scene_names": ['Library'],
}
# store settings for the auto_export part
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")
stored_auto_settings.clear()
stored_auto_settings.write(json.dumps(export_props))
gltf_settings = {
"export_animations": True,
"export_optimize_animation_size": False
}
# and store settings for the gltf part
stored_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings")
stored_gltf_settings.clear()
stored_gltf_settings.write(json.dumps(gltf_settings))
# move the main cube
bpy.data.objects["Cube"].location = [1, 0, 0]
# move the cube in the library
bpy.data.objects["Blueprint1_mesh"].location = [1, 2, 1]
auto_export_operator(
auto_export=True,
direct_mode=True,
export_output_folder="./models",
export_scene_settings=True,
export_blueprints=True,
export_legacy_mode=False,
export_materials_library=True
)
world_file_path = os.path.join(models_path, "World.glb")
assert os.path.exists(world_file_path) == True
models_library_path = os.path.join(models_path, "library")
model_library_file_paths = list(map(lambda file_name: os.path.join(models_library_path, file_name), sorted(os.listdir(models_library_path))))
modification_times_first = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths))
print("files", model_library_file_paths)
print("mod times", modification_times_first)
# export again, with no param changes: this should NOT export anything again, ie, modification times should be the same
print("second export")
auto_export_operator(
auto_export=True,
direct_mode=True,
export_output_folder="./models",
export_scene_settings=True,
export_blueprints=True,
export_legacy_mode=False,
export_materials_library=True
)
modification_times_no_change = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths))
assert modification_times_no_change == modification_times_first
# export again, this time changing the gltf settings
print("third export, changed gltf parameters")
gltf_settings = {
"export_animations": True,
"export_optimize_animation_size": True
}
stored_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings")
stored_gltf_settings.clear()
stored_gltf_settings.write(json.dumps(gltf_settings))
auto_export_operator(
auto_export=True,
direct_mode=True,
export_output_folder="./models",
export_scene_settings=True,
export_blueprints=True,
export_legacy_mode=False,
export_materials_library=True
)
modification_times_changed_gltf = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths))
assert modification_times_changed_gltf != modification_times_first
modification_times_first = modification_times_changed_gltf
# now run it again, withouth changes, timestamps should be identical
auto_export_operator(
auto_export=True,
direct_mode=True,
export_output_folder="./models",
export_scene_settings=True,
export_blueprints=True,
export_legacy_mode=False,
export_materials_library=True
)
modification_times_changed_gltf = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths))
assert modification_times_changed_gltf == modification_times_first
modification_times_first = modification_times_changed_gltf
# export again, this time changing the auto_export settings
print("fourth export, changed auto parameters")
export_props = {
"main_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
}
# store settings for the auto_export part
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")
stored_auto_settings.clear()
stored_auto_settings.write(json.dumps(export_props))
auto_export_operator(
auto_export=True,
direct_mode=True,
export_output_folder="./models",
export_scene_settings=True,
export_blueprints=True,
export_legacy_mode=False,
export_materials_library=False
)
modification_times_changed_auto = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths))
assert modification_times_changed_auto != modification_times_first
modification_times_first = modification_times_changed_auto
# now run it again, withouth changes, timestamps should be identical
auto_export_operator(
auto_export=True,
direct_mode=True,
export_output_folder="./models",
export_scene_settings=True,
export_blueprints=True,
export_legacy_mode=False,
export_materials_library=True
)
modification_times_changed_gltf = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths))
assert modification_times_changed_gltf == modification_times_first
modification_times_first = modification_times_changed_gltf

View File

@ -17,6 +17,40 @@ from ..helpers.helpers_collections import (get_exportable_collections)
###################################################### ######################################################
## ui logic & co ## ui logic & co
# side panel that opens auto_export specific gltf settings & the auto export settings themselves
class GLTF_PT_auto_export_SidePanel(bpy.types.Panel):
bl_idname = "GLTF_PT_auto_export_SidePanel"
bl_label = ""
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Auto Export"
bl_context = "objectmode"
def draw_header(self, context):
layout = self.layout
layout.label(text="Gltf auto export ")
def draw(self, context):
layout = self.layout
layout.label(text="MAKE SURE TO KEEP 'REMEMBER EXPORT SETTINGS' TOGGLED !!")
op = layout.operator("EXPORT_SCENE_OT_gltf", text='Gltf Settings')#'glTF 2.0 (.glb/.gltf)')
#op.export_format = 'GLTF_SEPARATE'
op.use_selection=True
op.will_save_settings=True
op.use_visible=True # Export visible and hidden objects. See Object/Batch Export to skip.
op.use_renderable=True
op.use_active_collection = True
op.use_active_collection_with_nested=True
op.use_active_scene = True
op.filepath="____dummy____"
op.gltf_export_id = "gltf_auto_export" # we specify that we are in a special case
op = layout.operator("EXPORT_SCENES_OT_auto_gltf", text="Auto Export Settings")
op.auto_export = True
#print("GLTF_PT_export_main", GLTF_PT_export_main.bl_parent_id)
# 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'
bl_region_type = 'TOOL_PROPS' bl_region_type = 'TOOL_PROPS'
@ -36,8 +70,6 @@ class GLTF_PT_auto_export_main(bpy.types.Panel):
layout.use_property_split = True layout.use_property_split = True
layout.use_property_decorate = False # No animation. layout.use_property_decorate = False # No animation.
sfile = context.space_data
class GLTF_PT_auto_export_root(bpy.types.Panel): class GLTF_PT_auto_export_root(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER' bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS' bl_region_type = 'TOOL_PROPS'
@ -126,8 +158,8 @@ class GLTF_PT_auto_export_scenes(bpy.types.Panel):
col = row.column(align=True) col = row.column(align=True)
col.separator() col.separator()
layout.active = operator.auto_export
source = operator source = operator
rows = 2 rows = 2
# main/level scenes # main/level scenes
@ -208,7 +240,7 @@ class GLTF_PT_auto_export_blueprints(bpy.types.Panel):
sfile = context.space_data sfile = context.space_data
operator = sfile.active_operator operator = sfile.active_operator
layout.active = operator.export_blueprints layout.active = operator.auto_export and operator.export_blueprints
# collections/blueprints # collections/blueprints
layout.prop(operator, "export_blueprints_path") layout.prop(operator, "export_blueprints_path")
@ -242,82 +274,12 @@ class GLTF_PT_auto_export_collections_list(bpy.types.Panel):
sfile = context.space_data sfile = context.space_data
operator = sfile.active_operator operator = sfile.active_operator
layout.active = operator.auto_export and operator.export_blueprints
for collection in bpy.context.window_manager.exportedCollections: for collection in bpy.context.window_manager.exportedCollections:
row = layout.row() row = layout.row()
row.label(text=collection.name) row.label(text=collection.name)
class HelloWorldOperator(bpy.types.Operator):
bl_idname = "export_scenes.wrapper"
bl_label = "Minimal Operator"
def execute(self, context):
print("Hello World")
return {'FINISHED'}
def invoke(self, context: Context, event: Event):
wm = context.window_manager
wm.fileselect_add(self)
return {'RUNNING_MODAL'}
def draw(self, context: Context):
layout = self.layout
op = layout.operator("EXPORT_SCENE_OT_gltf", text='Gltf settings')#'glTF 2.0 (.glb/.gltf)')
op.use_selection=True
op.will_save_settings=True
op.use_visible=True # Export visible and hidden objects. See Object/Batch Export to skip.
op.use_renderable=True
op.use_active_collection = True
op.use_active_collection_with_nested=True
op.use_active_scene = True
op.filepath="dummy"
#export_scenes.auto_gltf
class GLTF_PT_auto_export_gltf(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Gltf"
bl_parent_id = "GLTF_PT_auto_export_main"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" #"EXPORT_SCENE_OT_gltf"
def draw(self, context):
preferences = context.preferences
layout = self.layout
sfile = context.space_data
operator = sfile.active_operator
addon_prefs = operator
op = layout.operator("EXPORT_SCENES_OT_wrapper", text='Gltf settings')#'glTF 2.0 (.glb/.gltf)')
op = layout.operator("EXPORT_SCENE_OT_gltf", text='Gltf settings')#'glTF 2.0 (.glb/.gltf)')
#op.export_format = 'GLTF_SEPARATE'
op.use_selection=True
op.will_save_settings=True
op.use_visible=True # Export visible and hidden objects. See Object/Batch Export to skip.
op.use_renderable=True
op.use_active_collection = True
op.use_active_collection_with_nested=True
op.use_active_scene = True
op.filepath="dummy"
#bpy.ops.export_scene.gltf
"""for key in addon_prefs.__annotations__.keys():
if key not in AutoExportGltfPreferenceNames:
layout.prop(operator, key)"""
class SCENE_UL_GLTF_auto_export(bpy.types.UIList): class SCENE_UL_GLTF_auto_export(bpy.types.UIList):
# The draw_item function is called for each item of the collection that is visible in the list. # The draw_item function is called for each item of the collection that is visible in the list.
# data is the RNA object containing the collection, # data is the RNA object containing the collection,
@ -349,41 +311,3 @@ class SCENE_UL_GLTF_auto_export(bpy.types.UIList):
elif self.layout_type == 'GRID': elif self.layout_type == 'GRID':
layout.alignment = 'CENTER' layout.alignment = 'CENTER'
layout.label(text="", icon_value=icon) layout.label(text="", icon_value=icon)
from io_scene_gltf2 import (ExportGLTF2, GLTF_PT_export_main,ExportGLTF2_Base, GLTF_PT_export_include)
import io_scene_gltf2 as gltf_exporter_original
#import io_scene_gltf2.GLTF_PT_export_data_scene as GLTF_PT_export_data_scene_original
"""
class GLTF_PT_export_data(gltf_exporter_original.GLTF_PT_export_data):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Data"
bl_parent_id = "GLTF_PT_auto_export_gltf"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf"
class GLTF_PT_export_data_scene(gltf_exporter_original.GLTF_PT_export_data_scene):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Scene Graph"
bl_parent_id = "GLTF_PT_export_data"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf"
def draw(self, context):
return super().draw(context)"""

View File

@ -1,267 +0,0 @@
import bpy
from bpy.types import Operator
from bpy_extras.io_utils import ExportHelper
from bpy.props import (BoolProperty,
IntProperty,
StringProperty,
EnumProperty,
CollectionProperty
)
from ..auto_export import auto_export
from ..auto_export.preferences import (AutoExportGltfAddonPreferences, AutoExportGltfPreferenceNames)
from ..helpers.helpers_scenes import (get_scenes)
from ..helpers.helpers_collections import (get_exportable_collections)
######################################################
## ui logic & co
class GLTF_PT_auto_export_main(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_label = ""
#bl_options = {'HIDE_HEADER'}
bl_category = "Gltf auto_export"
bl_context = "objectmode"
""" @classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf"
"""
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
class GLTF_PT_auto_export_root(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_label = "Auto export"
bl_parent_id = "GLTF_PT_auto_export_main"
#bl_options = {'DEFAULT_CLOSED'}
"""@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf"
"""
"""def draw_header(self, context):
sfile = context.space_data
operator = sfile.active_operator
self.layout.prop(operator, "auto_export", text="")"""
def draw(self, context):
pass
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
sfile = context.space_data
operator = layout.operator("export_scenes.auto_gltf")#bpy.ops.export_scenes.auto_gltf #sfile.active_operator
layout.prop(context.window_manager.operator_properties_last("export_scenes.auto_gltf"), "export_change_detection")
layout.prop(context.window_manager.operator_properties_last("export_scenes.auto_gltf"), "export_output_folder")
#layout.active = operator.auto_export
layout.prop(operator, 'will_save_settings')
layout.prop(operator, "export_change_detection")
layout.prop(operator, "export_output_folder")
layout.prop(operator, "export_scene_settings")
layout.prop(operator, "export_legacy_mode")
# scene selectors
row = layout.row()
col = row.column(align=True)
col.separator()
source = operator
rows = 2
# main/level scenes
layout.label(text="main scenes")
layout.prop(context.window_manager, "main_scene", text='')
row = layout.row()
row.template_list("SCENE_UL_GLTF_auto_export", "level scenes", source, "main_scenes", source, "main_scenes_index", rows=rows)
col = row.column(align=True)
sub_row = col.row()
add_operator = sub_row.operator("scene_list.list_action", icon='ADD', text="")
add_operator.action = 'ADD'
add_operator.scene_type = 'level'
#add_operator.source = operator
sub_row.enabled = context.window_manager.main_scene is not None
sub_row = col.row()
remove_operator = sub_row.operator("scene_list.list_action", icon='REMOVE', text="")
remove_operator.action = 'REMOVE'
remove_operator.scene_type = 'level'
col.separator()
#up_operator = col.operator("scene_list.list_action", icon='TRIA_UP', text="")
#up_operator.action = 'UP'
#col.operator("scene_list.list_action", icon='TRIA_DOWN', text="").action = 'DOWN'
# library scenes
layout.label(text="library scenes")
layout.prop(context.window_manager, "library_scene", text='')
row = layout.row()
row.template_list("SCENE_UL_GLTF_auto_export", "library scenes", source, "library_scenes", source, "library_scenes_index", rows=rows)
col = row.column(align=True)
sub_row = col.row()
add_operator = sub_row.operator("scene_list.list_action", icon='ADD', text="")
add_operator.action = 'ADD'
add_operator.scene_type = 'library'
sub_row.enabled = context.window_manager.library_scene is not None
sub_row = col.row()
remove_operator = sub_row.operator("scene_list.list_action", icon='REMOVE', text="")
remove_operator.action = 'REMOVE'
remove_operator.scene_type = 'library'
col.separator()
class GLTF_PT_auto_export_blueprints(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_label = "Blueprints"
bl_parent_id = "GLTF_PT_auto_export_main"
"""@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" #"EXPORT_SCENE_OT_gltf"
"""
"""def draw_header(self, context):
layout = self.layout
sfile = context.space_data
operator = sfile.active_operator
layout.prop(operator, "export_blueprints", text="")"""
#self.layout.prop(operator, "auto_export", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
sfile = context.space_data
operator = layout.operator("export_scenes.auto_gltf")#bpy.ops.export_scenes.auto_gltf #sfile.active_operator
#sfile.active_operator
layout.active = operator.export_blueprints
# collections/blueprints
layout.prop(operator, "export_blueprints_path")
layout.prop(operator, "collection_instances_combine_mode")
layout.prop(operator, "export_marked_assets")
layout.prop(operator, "export_separate_dynamic_and_static_objects")
layout.separator()
# materials
layout.prop(operator, "export_materials_library")
layout.prop(operator, "export_materials_path")
class GLTF_PT_auto_export_collections_list(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Blueprints: Exported Collections"
bl_parent_id = "GLTF_PT_auto_export_blueprints"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" #"EXPORT_SCENE_OT_gltf"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
sfile = context.space_data
operator = sfile.active_operator
for collection in bpy.context.window_manager.exportedCollections:
row = layout.row()
row.label(text=collection.name)
class GLTF_PT_auto_export_gltf(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_label = "Gltf"
bl_parent_id = "GLTF_PT_auto_export_main"
bl_options = {'DEFAULT_CLOSED'}
"""@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" #"EXPORT_SCENE_OT_gltf"
"""
def draw(self, context):
preferences = context.preferences
layout = self.layout
layout.label(text="MAKE SURE TO KEEP 'REMEMBER EXPORT SETTINGS' !!")
op = layout.operator("EXPORT_SCENE_OT_gltf", text='Gltf settings')#'glTF 2.0 (.glb/.gltf)')
#op.export_format = 'GLTF_SEPARATE'
op.use_selection=True
op.will_save_settings=True
op.use_visible=True # Export visible and hidden objects. See Object/Batch Export to skip.
op.use_renderable=True
op.use_active_collection = True
op.use_active_collection_with_nested=True
op.use_active_scene = True
op.filepath="dummy"
class SCENE_UL_GLTF_auto_export(bpy.types.UIList):
# The draw_item function is called for each item of the collection that is visible in the list.
# data is the RNA object containing the collection,
# item is the current drawn item of the collection,
# icon is the "computed" icon for the item (as an integer, because some objects like materials or textures
# have custom icons ID, which are not available as enum items).
# active_data is the RNA object containing the active property for the collection (i.e. integer pointing to the
# active item of the collection).
# active_propname is the name of the active property (use 'getattr(active_data, active_propname)').
# index is index of the current item in the collection.
# flt_flag is the result of the filtering process for this item.
# Note: as index and flt_flag are optional arguments, you do not have to use/declare them here if you don't
# need them.
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
ob = data
# draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
if self.layout_type in {'DEFAULT', 'COMPACT'}:
# You should always start your row layout by a label (icon + text), or a non-embossed text field,
# this will also make the row easily selectable in the list! The later also enables ctrl-click rename.
# We use icon_value of label, as our given icon is an integer value, not an enum ID.
# Note "data" names should never be translated!
#if ma:
# layout.prop(ma, "name", text="", emboss=False, icon_value=icon)
#else:
# layout.label(text="", translate=False, icon_value=icon)
layout.label(text=item.name, icon_value=icon)
#layout.prop(item, "name", text="", emboss=False, icon_value=icon)
# 'GRID' layout type should be as compact as possible (typically a single icon!).
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.label(text="", icon_value=icon)

View File

@ -1,83 +0,0 @@
import bpy
from bpy.types import Operator
class SCENES_LIST_OT_actions(Operator):
"""Move items up and down, add and remove"""
bl_idname = "scene_list.list_action"
bl_label = "List Actions"
bl_description = "Move items up and down, add and remove"
bl_options = {'REGISTER'}
action: bpy.props.EnumProperty(
items=(
('UP', "Up", ""),
('DOWN', "Down", ""),
('REMOVE', "Remove", ""),
('ADD', "Add", "")))
scene_type: bpy.props.StringProperty()#TODO: replace with enum
def invoke(self, context, event):
source = context.space_data.active_operator
target_name = "library_scenes"
target_index = "library_scenes_index"
if self.scene_type == "level":
target_name = "main_scenes"
target_index = "main_scenes_index"
target = getattr(source, target_name)
idx = getattr(source, target_index)
current_index = getattr(source, target_index)
try:
item = target[idx]
except IndexError:
pass
else:
if self.action == 'DOWN' and idx < len(target) - 1:
target.move(idx, idx + 1)
setattr(source, target_index, current_index +1 )
info = 'Item "%s" moved to position %d' % (item.name, current_index + 1)
self.report({'INFO'}, info)
elif self.action == 'UP' and idx >= 1:
target.move(idx, idx - 1)
setattr(source, target_index, current_index -1 )
info = 'Item "%s" moved to position %d' % (item.name, current_index + 1)
self.report({'INFO'}, info)
elif self.action == 'REMOVE':
info = 'Item "%s" removed from list' % (target[idx].name)
setattr(source, target_index, current_index -1 )
target.remove(idx)
self.report({'INFO'}, info)
if self.action == 'ADD':
new_scene_name = None
if self.scene_type == "level":
if context.window_manager.main_scene:
new_scene_name = context.window_manager.main_scene.name
else:
if context.window_manager.library_scene:
new_scene_name = context.window_manager.library_scene.name
if new_scene_name:
item = target.add()
item.name = new_scene_name#f"Rule {idx +1}"
if self.scene_type == "level":
context.window_manager.main_scene = None
else:
context.window_manager.library_scene = None
#name = f"Rule {idx +1}"
#target.append({"name": name})
setattr(source, target_index, len(target) - 1)
#source[target_index] = len(target) - 1
info = '"%s" added to list' % (item.name)
self.report({'INFO'}, info)
return {"FINISHED"}