mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-11-26 21:37:01 +00:00
Compare commits
12 Commits
2dae2c41b4
...
11e8786b59
Author | SHA1 | Date | |
---|---|---|---|
|
11e8786b59 | ||
|
86a1a4d717 | ||
|
62686ecb61 | ||
|
9645e812ef | ||
|
ccaeecf6f1 | ||
|
1b1fc31d5d | ||
|
e139a1496d | ||
|
ed09ab7d48 | ||
|
1cf47d36b1 | ||
|
0998decb39 | ||
|
0528286b12 | ||
|
b19e54b3bd |
@ -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,60 +155,62 @@ 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);
|
// println!("Entity {:?} markers {:?}", entity, markers);
|
||||||
break;
|
// 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
|
||||||
}
|
let animation_name =
|
||||||
if animation_clip.is_some() && markers.is_some() && animation_infos.is_some() {
|
animations.named_animations.iter().find_map(|(key, value)| {
|
||||||
let markers = markers.unwrap();
|
if value == animation_player.animation_clip() {
|
||||||
let animation_infos = animation_infos.unwrap();
|
Some(key)
|
||||||
|
} else {
|
||||||
// println!("Entity {:?} markers {:?}", entity, markers);
|
None
|
||||||
// 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
|
|
||||||
let animation_name = animations.named_animations.iter().find_map(|(key, value)| {
|
|
||||||
if value == animation_player.animation_clip() {
|
|
||||||
Some(key)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if animation_name.is_some() {
|
|
||||||
let animation_name = animation_name.unwrap();
|
|
||||||
let animation_length_seconds = animation_clip.unwrap().duration();
|
|
||||||
let animation_length_frames = animation_infos
|
|
||||||
.animations
|
|
||||||
.iter()
|
|
||||||
.find(|anim| &anim.name == animation_name)
|
|
||||||
.unwrap()
|
|
||||||
.frames_length;
|
|
||||||
// TODO: we also need to take playback speed into account
|
|
||||||
let time_in_animation = animation_player.elapsed()
|
|
||||||
- (animation_player.completions() as f32) * animation_length_seconds;
|
|
||||||
let frame_seconds =
|
|
||||||
(animation_length_frames as f32 / animation_length_seconds) * time_in_animation;
|
|
||||||
let frame = frame_seconds as u32;
|
|
||||||
|
|
||||||
let matching_animation_marker = &markers.0[animation_name];
|
|
||||||
if matching_animation_marker.contains_key(&frame) {
|
|
||||||
let matching_markers_per_frame = matching_animation_marker.get(&frame).unwrap();
|
|
||||||
// println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame);
|
|
||||||
// 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
|
|
||||||
for marker_name in matching_markers_per_frame {
|
|
||||||
animation_marker_events.send(AnimationMarkerReached {
|
|
||||||
entity: entity,
|
|
||||||
animation_name: animation_name.clone(),
|
|
||||||
frame: frame,
|
|
||||||
marker_name: marker_name.clone(),
|
|
||||||
});
|
});
|
||||||
|
if animation_name.is_some() {
|
||||||
|
let animation_name = animation_name.unwrap();
|
||||||
|
let animation_length_seconds = animation_clip.unwrap().duration();
|
||||||
|
let animation_length_frames = animation_infos
|
||||||
|
.animations
|
||||||
|
.iter()
|
||||||
|
.find(|anim| &anim.name == animation_name)
|
||||||
|
.unwrap()
|
||||||
|
.frames_length;
|
||||||
|
// TODO: we also need to take playback speed into account
|
||||||
|
let time_in_animation = animation_player.elapsed()
|
||||||
|
- (animation_player.completions() as f32) * animation_length_seconds;
|
||||||
|
let frame_seconds = (animation_length_frames / animation_length_seconds)
|
||||||
|
* time_in_animation;
|
||||||
|
// println!("frame seconds {}", frame_seconds);
|
||||||
|
let frame = frame_seconds.ceil() as u32; // FIXME , bad hack
|
||||||
|
|
||||||
|
let matching_animation_marker = &markers.0[animation_name];
|
||||||
|
|
||||||
|
if matching_animation_marker.contains_key(&frame) {
|
||||||
|
let matching_markers_per_frame =
|
||||||
|
matching_animation_marker.get(&frame).unwrap();
|
||||||
|
// println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame);
|
||||||
|
// emit an event AnimationMarkerReached(entity, animation_name, frame, marker_name)
|
||||||
|
// FIXME: complete hack-ish solution , otherwise this can fire multiple times in a row, depending on animation length , speed , etc
|
||||||
|
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 {
|
||||||
|
entity: entity,
|
||||||
|
animation_name: animation_name.clone(),
|
||||||
|
frame: frame,
|
||||||
|
marker_name: marker.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
Binary file not shown.
Before Width: | Height: | Size: 644 KiB |
@ -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>,
|
||||||
) {
|
) {
|
||||||
|
@ -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
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
|
@ -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"
|
||||||
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")
|
if current_auto_settings != None:
|
||||||
previous_auto_settings.clear()
|
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.write(current_auto_settings.as_string()) # TODO : check if this is always valid
|
previous_auto_settings.clear()
|
||||||
|
previous_auto_settings.write(current_auto_settings.as_string()) # TODO : check if this is always valid
|
||||||
|
|
||||||
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")
|
if current_gltf_settings != None:
|
||||||
previous_gltf_settings.clear()
|
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.write(current_gltf_settings.as_string())
|
previous_gltf_settings.clear()
|
||||||
|
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
|
||||||
bpy.app.timers.register(bpy.context.window_manager.auto_export_tracker.enable_change_detection, first_interval=1)
|
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)
|
||||||
|
else:
|
||||||
|
print("auto export disabled, skipping")
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
|
@ -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',
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -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)"""
|
@ -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"]}
|
BIN
tools/gltf_auto_export/tests/expected_screenshot.png
Normal file
BIN
tools/gltf_auto_export/tests/expected_screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 657 KiB |
@ -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)
|
||||||
|
219
tools/gltf_auto_export/tests/test_change_tracking.py
Normal file
219
tools/gltf_auto_export/tests/test_change_tracking.py
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
209
tools/gltf_auto_export/tests/test_changed_parameters.py
Normal file
209
tools/gltf_auto_export/tests/test_changed_parameters.py
Normal 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
|
||||||
|
|
||||||
|
|
@ -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)"""
|
|
@ -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)
|
|
||||||
|
|
||||||
|
|
@ -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"}
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user