mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-11-27 13:47:00 +00:00
feat(bevy_components): set of fixes & improvements (#128)
* fixed issue with "reload registry" not clearing previous data * added watcher/ poll system to automatically updated the registry & components list when the registry file has been changed * BREAKING CHANGE ! changed internal representation of components, incompatible with v0.1, breaks UI values. * added buttons to regenerate UI to account for/fix the above and to offer the ability to regenerate UI values from custom property values * lots of cleanups * added tests * closes #127 * closes #124 * closes #121 * closes #130
This commit is contained in:
parent
0083295a4d
commit
20b6fa6077
@ -8,16 +8,15 @@ members = [
|
||||
"examples/common/",
|
||||
|
||||
"examples/bevy_gltf_components/basic/",
|
||||
|
||||
"examples/bevy_gltf_blueprints/basic/",
|
||||
"examples/bevy_gltf_blueprints/basic_xpbd_physics/",
|
||||
"examples/bevy_gltf_blueprints/animation/",
|
||||
"examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles",
|
||||
"examples/bevy_gltf_blueprints/materials/",
|
||||
|
||||
"examples/bevy_gltf_save_load/basic/",
|
||||
|
||||
"examples/bevy_registry_export/basic"
|
||||
"examples/bevy_registry_export/basic",
|
||||
|
||||
"testing/bevy_registry_export/basic"
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
16
testing/bevy_registry_export/basic/Cargo.toml
Normal file
16
testing/bevy_registry_export/basic/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "bevy_bevy_registry_export_basic_testing"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
bevy="0.12"
|
||||
bevy_gltf_blueprints = { path = "../../../crates/bevy_gltf_blueprints" }
|
||||
bevy_registry_export = { path = "../../../crates/bevy_registry_export" }
|
||||
bevy_gltf_worlflow_examples_common = { path = "../../../examples/common" }
|
||||
|
||||
bevy_rapier3d = { version = "0.23.0", features = [ "serde-serialize", "debug-render-3d", "enhanced-determinism"] }
|
||||
bevy_asset_loader = { version = "0.18", features = ["standard_dynamic_assets" ]}
|
||||
bevy_editor_pls = { version = "0.6" }
|
||||
rand = "0.8.5"
|
15
testing/bevy_registry_export/basic/README.md
Normal file
15
testing/bevy_registry_export/basic/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Bevy registry export example/demo
|
||||
|
||||
This example showcases
|
||||
* the use of the bevy_registry_export crate to extract all components & types information into a json file.
|
||||
* That file is then used by the [Blender addon](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/bevy_components) to create Uis for each component,
|
||||
to be able to add & edit Bevy components easilly in Blender !
|
||||
|
||||
|
||||
## Running this example
|
||||
|
||||
```
|
||||
cargo run --features bevy/dynamic_linking
|
||||
```
|
||||
|
||||
Running the example also regenerates the registry.json file.
|
@ -0,0 +1 @@
|
||||
({})
|
@ -0,0 +1,6 @@
|
||||
({
|
||||
"world":File (path: "models/World.glb"),
|
||||
"models": Folder (
|
||||
path: "models/library",
|
||||
),
|
||||
})
|
BIN
testing/bevy_registry_export/basic/assets/basic.blend
Normal file
BIN
testing/bevy_registry_export/basic/assets/basic.blend
Normal file
Binary file not shown.
BIN
testing/bevy_registry_export/basic/assets/models/World.glb
Normal file
BIN
testing/bevy_registry_export/basic/assets/models/World.glb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
10732
testing/bevy_registry_export/basic/assets/registry.json
Normal file
10732
testing/bevy_registry_export/basic/assets/registry.json
Normal file
File diff suppressed because it is too large
Load Diff
22
testing/bevy_registry_export/basic/src/core/mod.rs
Normal file
22
testing/bevy_registry_export/basic/src/core/mod.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_gltf_blueprints::*;
|
||||
use bevy_registry_export::*;
|
||||
|
||||
pub struct CorePlugin;
|
||||
impl Plugin for CorePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_plugins((
|
||||
ExportRegistryPlugin {
|
||||
save_path: "assets/registry.json".into(),
|
||||
..Default::default()
|
||||
},
|
||||
BlueprintsPlugin {
|
||||
legacy_mode: false,
|
||||
library_folder: "models/library".into(),
|
||||
format: GltfFormat::GLB,
|
||||
aabbs: true,
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
128
testing/bevy_registry_export/basic/src/game/in_game.rs
Normal file
128
testing/bevy_registry_export/basic/src/game/in_game.rs
Normal file
@ -0,0 +1,128 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag};
|
||||
use bevy_gltf_worlflow_examples_common::{assets::GameAssets, GameState, InAppRunning};
|
||||
|
||||
use bevy_rapier3d::prelude::Velocity;
|
||||
use rand::Rng;
|
||||
|
||||
pub fn setup_game(
|
||||
mut commands: Commands,
|
||||
game_assets: Res<GameAssets>,
|
||||
models: Res<Assets<bevy::gltf::Gltf>>,
|
||||
mut next_game_state: ResMut<NextState<GameState>>,
|
||||
) {
|
||||
println!("setting up all stuff");
|
||||
commands.insert_resource(AmbientLight {
|
||||
color: Color::WHITE,
|
||||
brightness: 0.2,
|
||||
});
|
||||
// here we actually spawn our game world/level
|
||||
|
||||
commands.spawn((
|
||||
SceneBundle {
|
||||
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
|
||||
scene: models
|
||||
.get(game_assets.world.id())
|
||||
.expect("main level should have been loaded")
|
||||
.scenes[0]
|
||||
.clone(),
|
||||
..default()
|
||||
},
|
||||
bevy::prelude::Name::from("world"),
|
||||
GameWorldTag,
|
||||
InAppRunning,
|
||||
));
|
||||
|
||||
next_game_state.set(GameState::InGame)
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
struct UnregisteredComponent;
|
||||
|
||||
pub fn spawn_test(
|
||||
keycode: Res<Input<KeyCode>>,
|
||||
mut commands: Commands,
|
||||
|
||||
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
|
||||
) {
|
||||
if keycode.just_pressed(KeyCode::T) {
|
||||
let world = game_world.single_mut();
|
||||
let world = world.1[0];
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let range = 5.5;
|
||||
let x: f32 = rng.gen_range(-range..range);
|
||||
let y: f32 = rng.gen_range(-range..range);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let range = 0.8;
|
||||
let vel_x: f32 = rng.gen_range(-range..range);
|
||||
let vel_y: f32 = rng.gen_range(2.0..2.5);
|
||||
let vel_z: f32 = rng.gen_range(-range..range);
|
||||
|
||||
let name_index: u64 = rng.gen();
|
||||
|
||||
let new_entity = commands
|
||||
.spawn((
|
||||
BluePrintBundle {
|
||||
blueprint: BlueprintName("Health_Pickup".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
bevy::prelude::Name::from(format!("test{}", name_index)),
|
||||
// BlueprintName("Health_Pickup".to_string()),
|
||||
// SpawnHere,
|
||||
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
|
||||
Velocity {
|
||||
linvel: Vec3::new(vel_x, vel_y, vel_z),
|
||||
angvel: Vec3::new(0.0, 0.0, 0.0),
|
||||
},
|
||||
))
|
||||
.id();
|
||||
commands.entity(world).add_child(new_entity);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_test_unregisted_components(
|
||||
keycode: Res<Input<KeyCode>>,
|
||||
mut commands: Commands,
|
||||
|
||||
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
|
||||
) {
|
||||
if keycode.just_pressed(KeyCode::U) {
|
||||
let world = game_world.single_mut();
|
||||
let world = world.1[0];
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let range = 5.5;
|
||||
let x: f32 = rng.gen_range(-range..range);
|
||||
let y: f32 = rng.gen_range(-range..range);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let range = 0.8;
|
||||
let vel_x: f32 = rng.gen_range(-range..range);
|
||||
let vel_y: f32 = rng.gen_range(2.0..2.5);
|
||||
let vel_z: f32 = rng.gen_range(-range..range);
|
||||
|
||||
let name_index: u64 = rng.gen();
|
||||
|
||||
let new_entity = commands
|
||||
.spawn((
|
||||
BluePrintBundle {
|
||||
blueprint: BlueprintName("Health_Pickup".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
bevy::prelude::Name::from(format!("test{}", name_index)),
|
||||
// BlueprintName("Health_Pickup".to_string()),
|
||||
// SpawnHere,
|
||||
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
|
||||
Velocity {
|
||||
linvel: Vec3::new(vel_x, vel_y, vel_z),
|
||||
angvel: Vec3::new(0.0, 0.0, 0.0),
|
||||
},
|
||||
UnregisteredComponent,
|
||||
))
|
||||
.id();
|
||||
commands.entity(world).add_child(new_entity);
|
||||
}
|
||||
}
|
112
testing/bevy_registry_export/basic/src/game/in_main_menu.rs
Normal file
112
testing/bevy_registry_export/basic/src/game/in_main_menu.rs
Normal file
@ -0,0 +1,112 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_gltf_worlflow_examples_common::{AppState, InMainMenu};
|
||||
|
||||
pub fn setup_main_menu(mut commands: Commands) {
|
||||
commands.spawn((Camera2dBundle::default(), InMainMenu));
|
||||
|
||||
commands.spawn((
|
||||
TextBundle::from_section(
|
||||
"SOME GAME TITLE !!",
|
||||
TextStyle {
|
||||
//font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 18.0,
|
||||
color: Color::WHITE,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(100.0),
|
||||
left: Val::Px(200.0),
|
||||
..default()
|
||||
}),
|
||||
InMainMenu,
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
TextBundle::from_section(
|
||||
"New Game (press Enter to start, press T once the game is started for demo spawning)",
|
||||
TextStyle {
|
||||
//font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 18.0,
|
||||
color: Color::WHITE,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(200.0),
|
||||
left: Val::Px(200.0),
|
||||
..default()
|
||||
}),
|
||||
InMainMenu,
|
||||
));
|
||||
|
||||
/*
|
||||
commands.spawn((
|
||||
TextBundle::from_section(
|
||||
"Load Game",
|
||||
TextStyle {
|
||||
//font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 18.0,
|
||||
color: Color::WHITE,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(250.0),
|
||||
left: Val::Px(200.0),
|
||||
..default()
|
||||
}),
|
||||
InMainMenu
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
TextBundle::from_section(
|
||||
"Exit Game",
|
||||
TextStyle {
|
||||
//font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 18.0,
|
||||
color: Color::WHITE,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(300.0),
|
||||
left: Val::Px(200.0),
|
||||
..default()
|
||||
}),
|
||||
InMainMenu
|
||||
));*/
|
||||
}
|
||||
|
||||
pub fn teardown_main_menu(bla: Query<Entity, With<InMainMenu>>, mut commands: Commands) {
|
||||
for bli in bla.iter() {
|
||||
commands.entity(bli).despawn_recursive();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main_menu(
|
||||
keycode: Res<Input<KeyCode>>,
|
||||
|
||||
mut next_app_state: ResMut<NextState<AppState>>,
|
||||
// mut next_game_state: ResMut<NextState<GameState>>,
|
||||
// mut save_requested_events: EventWriter<SaveRequest>,
|
||||
// mut load_requested_events: EventWriter<LoadRequest>,
|
||||
) {
|
||||
if keycode.just_pressed(KeyCode::Return) {
|
||||
next_app_state.set(AppState::AppLoading);
|
||||
// next_game_state.set(GameState::None);
|
||||
}
|
||||
|
||||
if keycode.just_pressed(KeyCode::L) {
|
||||
next_app_state.set(AppState::AppLoading);
|
||||
// load_requested_events.send(LoadRequest { path: "toto".into() })
|
||||
}
|
||||
|
||||
if keycode.just_pressed(KeyCode::S) {
|
||||
// save_requested_events.send(SaveRequest { path: "toto".into() })
|
||||
}
|
||||
}
|
22
testing/bevy_registry_export/basic/src/game/mod.rs
Normal file
22
testing/bevy_registry_export/basic/src/game/mod.rs
Normal file
@ -0,0 +1,22 @@
|
||||
pub mod in_game;
|
||||
pub use in_game::*;
|
||||
|
||||
pub mod in_main_menu;
|
||||
pub use in_main_menu::*;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_gltf_worlflow_examples_common::{AppState, GameState};
|
||||
|
||||
pub struct GamePlugin;
|
||||
impl Plugin for GamePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(
|
||||
Update,
|
||||
(spawn_test, spawn_test_unregisted_components).run_if(in_state(GameState::InGame)),
|
||||
)
|
||||
.add_systems(OnEnter(AppState::MenuRunning), setup_main_menu)
|
||||
.add_systems(OnExit(AppState::MenuRunning), teardown_main_menu)
|
||||
.add_systems(Update, main_menu.run_if(in_state(AppState::MenuRunning)))
|
||||
.add_systems(OnEnter(AppState::AppRunning), setup_game);
|
||||
}
|
||||
}
|
27
testing/bevy_registry_export/basic/src/main.rs
Normal file
27
testing/bevy_registry_export/basic/src/main.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_editor_pls::prelude::*;
|
||||
use bevy_gltf_worlflow_examples_common::CommonPlugin;
|
||||
|
||||
mod core;
|
||||
use crate::core::*;
|
||||
|
||||
mod game;
|
||||
use game::*;
|
||||
|
||||
mod test_components;
|
||||
use test_components::*;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins((
|
||||
DefaultPlugins.set(AssetPlugin::default()),
|
||||
// editor
|
||||
EditorPlugin::default(),
|
||||
// our custom plugins
|
||||
CommonPlugin,
|
||||
CorePlugin, // reusable plugins
|
||||
GamePlugin, // specific to our game
|
||||
ComponentsTestPlugin, // Showcases different type of components /structs
|
||||
))
|
||||
.run();
|
||||
}
|
190
testing/bevy_registry_export/basic/src/test_components.rs
Normal file
190
testing/bevy_registry_export/basic/src/test_components.rs
Normal file
@ -0,0 +1,190 @@
|
||||
use bevy::{
|
||||
pbr::{ExtendedMaterial, MaterialExtension},
|
||||
prelude::*,
|
||||
render::render_resource::*,
|
||||
};
|
||||
use std::ops::Range;
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
struct UnitTest;
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
|
||||
#[reflect(Component)]
|
||||
struct TupleTestF32(f32);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
|
||||
#[reflect(Component)]
|
||||
struct TupleTestU64(u64);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
|
||||
#[reflect(Component)]
|
||||
pub struct TupleTestStr(String);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
struct TupleTest2(f32, u64, String);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
struct TupleTestBool(bool);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
struct TupleVec2(Vec2);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
struct TupleVec3(Vec3);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
struct TupleVec(Vec<String>);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
struct TupleVecF32F32(Vec<(f32, f32)>);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
struct TupleTestColor(Color);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct BasicTest {
|
||||
a: f32,
|
||||
b: u64,
|
||||
c: String,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub enum EnumTest {
|
||||
Metal,
|
||||
Wood,
|
||||
Rock,
|
||||
Cloth,
|
||||
Squishy,
|
||||
#[default]
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct NestingTestLevel2 {
|
||||
text: String,
|
||||
enable: bool,
|
||||
enum_inner: EnumTest,
|
||||
color: TupleTestColor,
|
||||
toggle: TupleTestBool,
|
||||
basic: BasicTest,
|
||||
pub nested: NestingTestLevel3,
|
||||
colors_list: VecOfColors,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct NestingTestLevel3 {
|
||||
vec: TupleVec3,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct NestedTupleStuff(f32, u64, NestingTestLevel2);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub enum EnumComplex {
|
||||
Float(f32),
|
||||
Wood(String),
|
||||
Vec(BasicTest),
|
||||
SomeThing,
|
||||
StructLike {
|
||||
a: f32,
|
||||
b: u32,
|
||||
c: String,
|
||||
},
|
||||
#[default]
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct VecOfVec3s2(Vec<TupleVec3>);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct VecOfColors(Vec<Color>);
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct AAAAddedCOMPONENT;
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct AComponentWithAnExtremlyExageratedOrMaybeNotButCouldBeNameOrWut;
|
||||
|
||||
/* fn toto(){
|
||||
let bla:core::ops::Range<f32> = Range { start: 0.1, end: 5.0};
|
||||
} */
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct VecOfF32s(Vec<f32>);
|
||||
|
||||
// test for extended materials
|
||||
#[derive(Asset, AsBindGroup, Reflect, Debug, Clone)]
|
||||
struct MyExtension {
|
||||
// We need to ensure that the bindings of the base material and the extension do not conflict,
|
||||
// so we start from binding slot 100, leaving slots 0-99 for the base material.
|
||||
#[uniform(100)]
|
||||
quantize_steps: u32,
|
||||
}
|
||||
|
||||
impl MaterialExtension for MyExtension {
|
||||
fn fragment_shader() -> ShaderRef {
|
||||
"shaders/extended_material.wgsl".into()
|
||||
}
|
||||
|
||||
fn deferred_fragment_shader() -> ShaderRef {
|
||||
"shaders/extended_material.wgsl".into()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ComponentsTestPlugin;
|
||||
impl Plugin for ComponentsTestPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.register_type::<BasicTest>()
|
||||
.register_type::<UnitTest>()
|
||||
.register_type::<TupleTestF32>()
|
||||
.register_type::<TupleTestU64>()
|
||||
.register_type::<TupleTestStr>()
|
||||
.register_type::<TupleTestBool>()
|
||||
.register_type::<TupleTest2>()
|
||||
.register_type::<TupleVec2>()
|
||||
.register_type::<TupleVec3>()
|
||||
.register_type::<EnumTest>()
|
||||
.register_type::<TupleTestColor>()
|
||||
.register_type::<TupleVec>()
|
||||
.register_type::<Vec<String>>()
|
||||
.register_type::<NestingTestLevel2>()
|
||||
.register_type::<NestingTestLevel3>()
|
||||
.register_type::<NestedTupleStuff>()
|
||||
.register_type::<EnumComplex>()
|
||||
.register_type::<VecOfVec3s2>()
|
||||
.register_type::<TupleVecF32F32>()
|
||||
.register_type::<(f32, f32)>()
|
||||
.register_type::<Vec<(f32, f32)>>()
|
||||
.register_type::<Vec<TupleVec3>>()
|
||||
.register_type::<Vec<Color>>()
|
||||
.register_type::<VecOfColors>()
|
||||
.register_type::<Range<f32>>()
|
||||
.register_type::<VecOfF32s>()
|
||||
.register_type::<Vec<f32>>()
|
||||
// .register_type::<AAAAddedCOMPONENT>()
|
||||
.register_type::<AComponentWithAnExtremlyExageratedOrMaybeNotButCouldBeNameOrWut>()
|
||||
.add_plugins(MaterialPlugin::<
|
||||
ExtendedMaterial<StandardMaterial, MyExtension>,
|
||||
>::default());
|
||||
}
|
||||
}
|
@ -15,6 +15,9 @@ of your Bevy components you get a nicely packed custom_property to use with ...
|
||||
> Important:
|
||||
the tooling is still in the early stages, even if it is feature complete : use with caution!.
|
||||
|
||||
> IMPORTANT !! if you have previously used v0.1 , v0.2 had a breaking change, please see [this](#regenerate-ui-values) section on how to upgrade your data to v0.2.\
|
||||
This problem should not be present going forward
|
||||
|
||||
## Installation:
|
||||
|
||||
* grab the latest release zip file from the releases tab (choose the bevy_components releases !)
|
||||
@ -95,6 +98,24 @@ it will automatically update the value of the corresponding custom property
|
||||
|
||||
![edit component](./docs/edit_component2.png)
|
||||
|
||||
### Create components from custom properties
|
||||
|
||||
- IF you have a valid component type and the correct corresponding RON string in the custom_property value (this button will not appear if not), this add-on can automatically
|
||||
generate the corresponding component for you:
|
||||
|
||||
- Fill/check your custom property (here for Aabb)
|
||||
|
||||
![generate_components 2](./docs/generate_components2.png)
|
||||
|
||||
- click on the button
|
||||
|
||||
![generate_components](./docs/generate_components.png)
|
||||
|
||||
-voila !
|
||||
|
||||
![generate_components 3](./docs/generate_components3.png)
|
||||
|
||||
|
||||
### copy & pasting
|
||||
|
||||
- you can also copy & paste components between objects
|
||||
@ -161,21 +182,39 @@ It will add the component to the select object
|
||||
|
||||
## advanced configuration
|
||||
|
||||
- there are also additional QOL features, that you should not need most of the time
|
||||
### registry file polling
|
||||
|
||||
- "update custom properties of current object" : will go over **all components** that you have defined for the **currently selected object**, and re-generate the
|
||||
|
||||
* by default, the add-on will check for changes in your registry file every second, and refresh the UI accordingly
|
||||
* you can set the polling frequency or turn it off if you do not want auto-refresh
|
||||
|
||||
![registry file polling](./docs/registry_polling.png)
|
||||
|
||||
|
||||
### regenerate custom property values
|
||||
|
||||
- "update custom properties of current object" : will go over **all components** that you have defined for the **currently selected object**, and re-generate the
|
||||
|
||||
corresponding custom property values
|
||||
|
||||
![update custom properties](./docs/other_options.png)
|
||||
|
||||
|
||||
- "update custom properties of ALL objects" : same as above but it will do so for the **ALL objects in your blend file** (so can be slow!), and re-generate the
|
||||
- "update custom properties of ALL objects" : same as above but it will do so for the **ALL objects in your blend file** (so can be slow!), and re-generate the
|
||||
|
||||
corresponding custom property values
|
||||
|
||||
![update custom properties for all](./docs/other_options2.png)
|
||||
|
||||
|
||||
### regenerate UI values
|
||||
|
||||
- since v0.2, you have the option to regenerate (for the selected object or all objects, as above) to regenerate your UI values from the custom property values
|
||||
|
||||
![update UI FROM custom properties](./docs/update_ui_from_custom_properties.png)
|
||||
|
||||
> IMPORTANT !! use this if you have previously used v0.1 , as v0.2 had a breaking change, that makes it **necessary** to use this **once** to upgrade the UI data
|
||||
|
||||
|
||||
## Additional important information
|
||||
|
||||
@ -191,6 +230,11 @@ Please see the documentation of those crates for more information.
|
||||
|
||||
you can find an example [here](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_registry_export/)
|
||||
|
||||
## Known issues & limitations:
|
||||
|
||||
* **Range** data (ie ```Range<f32>``` etc) are not handled at this time (issue seems to be on the Bevy side)
|
||||
* **Entity** structs are always set to 0 (setting entity values on the Blender side at this time does not make much sense anyway)
|
||||
|
||||
## License
|
||||
|
||||
This tool, all its code, contents & assets is Dual-licensed under either of
|
||||
|
@ -133,7 +133,77 @@ UI:
|
||||
- [x] update version
|
||||
- [x] add ability to set legacy mode for bevy_gltf_components ?
|
||||
|
||||
- [ ] release all versions
|
||||
- [ ] update main documentation, add compatibility version grid
|
||||
- [x] release all versions
|
||||
- [x] update main documentation, add compatibility version grid
|
||||
|
||||
|
||||
|
||||
## Phase 2
|
||||
|
||||
- [x] fix handling of long component names
|
||||
- [x] fix nesting level handling issue for new system : ie basic component DOES NOT work, but nestedLevel2 does
|
||||
- add goddam tests !
|
||||
- [ ] verify some weird prop => custom property values (Calculated Clip for example)
|
||||
|
||||
- [x] fix "reload registry" not clearing all previous data (reloading registry does not seem to account for added/removed components in the registry )
|
||||
- add file watcher for registry
|
||||
- [x] have the watcher work as expected
|
||||
- [ ] add handling of removed registry file
|
||||
- [ ] clear & reset handler when the file browser for the registry is used
|
||||
- [ ] re-enable watcher
|
||||
|
||||
- tests
|
||||
clear && pytest -svv --blender-executable <path_to_blender>/blender/blender-4.0.2-linux-x64/blender
|
||||
|
||||
- [x] load registry
|
||||
- just check list of components vs lists in registry
|
||||
- [x] try adding all components
|
||||
- [x] select an object
|
||||
- [x] call the add_component operator
|
||||
|
||||
- [x] change params
|
||||
- use field names + component definitions to set values
|
||||
- [x] find a way to shuffle params of ALL components based on a reliable, repeatable seed
|
||||
|
||||
- [x] test propgroup values => custom property values
|
||||
- [x] test custom property value => propgroup value
|
||||
|
||||
- check if all went well
|
||||
|
||||
- [x] fix issues with incorect custom_property generation
|
||||
- [x] fix issue with object variants for enums
|
||||
|
||||
- [ ] add handling for core::ops::Range<f32> & other ranges
|
||||
- [x] add handling for alloc::borrow::Cow<str>
|
||||
- [x] add handling of isize
|
||||
|
||||
- [x] indirection level
|
||||
- currently
|
||||
- short_name +_"ui => direct lookup
|
||||
- problem : max 64 chars for propertyGroupNames
|
||||
- possible solution
|
||||
- propertyGroupName storage: simple , incremented INT (call it propGroupId for ex)
|
||||
- lookup shortName => propGroupId
|
||||
|
||||
- do a first pass, by replacing manual propGroupNames creation with a function
|
||||
- in a second pass, replace the innards
|
||||
|
||||
- add button to regenerate cutom prop values from custom properties (allows us to sidestep any future issues with internals changing)
|
||||
- [x] fix lists
|
||||
- [x] fix enums (see Clusterconfig)
|
||||
- [x] need an example with one tupple one struct
|
||||
- [x] projection
|
||||
- [ ] additionalmassproperties
|
||||
- [x] fix tupleStructs (see TupleVecF32F32) => always the same problem of having us pre-parse data without knowing what we have inside
|
||||
- find a way to only split by level 0 (highest level) nesting "," seperators, ignoring any level of nesting until we dig one level deeper
|
||||
- solve nesting level use issues
|
||||
|
||||
- [x] remove metadata when deleting components
|
||||
- [x] add try catch around custom_prop => propGroup
|
||||
- [x] enhance the GenerateComponent_From_custom_property_Operator to use the new system to actually generate the stuff
|
||||
|
||||
- coherence in operators:
|
||||
- component_name vs component_type
|
||||
- [x] delete => remove
|
||||
|
||||
- [x] clean up reloading of registry settings
|
||||
- [x] clean up file watcher
|
@ -1,7 +1,7 @@
|
||||
bl_info = {
|
||||
"name": "bevy_components",
|
||||
"author": "kaosigh",
|
||||
"version": (0, 1, 0),
|
||||
"version": (0, 2, 0),
|
||||
"blender": (3, 4, 0),
|
||||
"location": "VIEW_3D",
|
||||
"description": "UI to help create Bevy blueprints and components",
|
||||
@ -16,13 +16,13 @@ from bpy.props import (StringProperty)
|
||||
|
||||
from .helpers import load_settings
|
||||
from .blueprints import CreateBlueprintOperator
|
||||
from .components.operators import CopyComponentOperator, DeleteComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, AddComponentOperator, Toggle_ComponentVisibility
|
||||
from .components.operators import CopyComponentOperator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, AddComponentOperator, Toggle_ComponentVisibility
|
||||
|
||||
from .registry.registry import ComponentsRegistry,MissingBevyType
|
||||
from .registry.operators import (COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT, ReloadRegistryOperator, OT_OpenFilebrowser)
|
||||
from .registry.operators import (COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT, ReloadRegistryOperator, OT_OpenFilebrowser)
|
||||
from .registry.ui import (BEVY_COMPONENTS_PT_Configuration, BEVY_COMPONENTS_PT_MissingTypesPanel, MISSING_TYPES_UL_List)
|
||||
|
||||
from .components.metadata import (ComponentInfos, ComponentsMeta, ensure_metadata_for_all_objects)
|
||||
from .components.metadata import (ComponentMetadata, ComponentsMeta, ensure_metadata_for_all_objects)
|
||||
from .propGroups.prop_groups import (generate_propertyGroups_for_components)
|
||||
from .components.lists import GENERIC_LIST_OT_actions, Generic_LIST_OT_AddItem, Generic_LIST_OT_RemoveItem, Generic_LIST_OT_SelectItem
|
||||
from .components.definitions_list import (ComponentDefinitionsList, ClearComponentDefinitionsList)
|
||||
@ -86,14 +86,14 @@ classes = [
|
||||
AddComponentOperator,
|
||||
CopyComponentOperator,
|
||||
PasteComponentOperator,
|
||||
DeleteComponentOperator,
|
||||
RemoveComponentOperator,
|
||||
GenerateComponent_From_custom_property_Operator,
|
||||
Toggle_ComponentVisibility,
|
||||
|
||||
ComponentDefinitionsList,
|
||||
ClearComponentDefinitionsList,
|
||||
|
||||
ComponentInfos,
|
||||
ComponentMetadata,
|
||||
ComponentsMeta,
|
||||
MissingBevyType,
|
||||
ComponentsRegistry,
|
||||
@ -102,6 +102,9 @@ classes = [
|
||||
ReloadRegistryOperator,
|
||||
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL,
|
||||
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT,
|
||||
|
||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
|
||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT,
|
||||
|
||||
BEVY_COMPONENTS_PT_MainPanel,
|
||||
BEVY_COMPONENTS_PT_ComponentsPanel,
|
||||
@ -119,19 +122,14 @@ from bpy.app.handlers import persistent
|
||||
|
||||
@persistent
|
||||
def post_load(file_name):
|
||||
print("post load", file_name)
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
registry.schemaPath = load_settings(registry.settings_save_path)["schemaPath"]
|
||||
registry.load_schema()
|
||||
generate_propertyGroups_for_components()
|
||||
ensure_metadata_for_all_objects()
|
||||
|
||||
|
||||
if registry != None:
|
||||
registry.load_settings()
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
bpy.types.WindowManager.blueprint_name = StringProperty()
|
||||
|
||||
bpy.app.handlers.load_post.append(post_load)
|
||||
|
||||
def unregister():
|
||||
|
@ -1,9 +1,10 @@
|
||||
import bpy
|
||||
from bpy.props import (StringProperty, BoolProperty, PointerProperty)
|
||||
from bpy_types import (PropertyGroup)
|
||||
from ..propGroups.conversions import property_group_value_from_custom_property_value, property_group_value_to_custom_property_value
|
||||
from ..propGroups.conversions_from_prop_group import property_group_value_to_custom_property_value
|
||||
from ..propGroups.conversions_to_prop_group import property_group_value_from_custom_property_value
|
||||
|
||||
class ComponentInfos(bpy.types.PropertyGroup):
|
||||
class ComponentMetadata(bpy.types.PropertyGroup):
|
||||
name : bpy.props.StringProperty(
|
||||
name = "name",
|
||||
default = ""
|
||||
@ -51,7 +52,7 @@ class ComponentsMeta(PropertyGroup):
|
||||
name="infos per component",
|
||||
description="component"
|
||||
)
|
||||
components: bpy.props.CollectionProperty(type = ComponentInfos)
|
||||
components: bpy.props.CollectionProperty(type = ComponentMetadata)
|
||||
|
||||
@classmethod
|
||||
def register(cls):
|
||||
@ -131,12 +132,12 @@ def add_metadata_to_components_without_metadata(object):
|
||||
def add_component_to_object(object, component_definition, value=None):
|
||||
cleanup_invalid_metadata(object)
|
||||
if object is not None:
|
||||
print("add_component_to_object", component_definition)
|
||||
# print("add_component_to_object", component_definition)
|
||||
long_name = component_definition["title"]
|
||||
short_name = component_definition["short_name"]
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
if registry.type_infos == None:
|
||||
raise Exception('registry type infos have not been loaded yet or ar missing !')
|
||||
if not registry.has_type_infos():
|
||||
raise Exception('registry type infos have not been loaded yet or are missing !')
|
||||
definition = registry.type_infos[long_name]
|
||||
# now we use our pre_generated property groups to set the initial value of our custom property
|
||||
(_, propertyGroup) = upsert_component_in_object(object, component_name=short_name, registry=registry)
|
||||
@ -157,7 +158,7 @@ def upsert_component_in_object(object, component_name, registry):
|
||||
if component_definition != None:
|
||||
short_name = component_definition["short_name"]
|
||||
long_name = component_definition["title"]
|
||||
property_group_name = short_name+"_ui"
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
propertyGroup = None
|
||||
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
@ -175,7 +176,7 @@ def upsert_component_in_object(object, component_name, registry):
|
||||
if property_group_name in registry.component_propertyGroups:
|
||||
# we have found a matching property_group, so try to inject it
|
||||
# now inject property group
|
||||
setattr(ComponentInfos, property_group_name, registry.component_propertyGroups[property_group_name]) # FIXME: not ideal as all ComponentInfos get the propGroup, but have not found a way to assign it per instance
|
||||
setattr(ComponentMetadata, property_group_name, registry.component_propertyGroups[property_group_name]) # FIXME: not ideal as all ComponentMetadata get the propGroup, but have not found a way to assign it per instance
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
# now deal with property groups details
|
||||
@ -196,13 +197,14 @@ def upsert_component_in_object(object, component_name, registry):
|
||||
return(None, None)
|
||||
|
||||
|
||||
def copy_propertyGroup_values_to_another_object(source_object, target_object, component_name):
|
||||
def copy_propertyGroup_values_to_another_object(source_object, target_object, component_name, registry):
|
||||
if source_object == None or target_object == None or component_name == None:
|
||||
raise Exception('missing input data, cannot copy component propertryGroup')
|
||||
|
||||
component_definition = find_component_definition_from_short_name(component_name)
|
||||
short_name = component_definition["short_name"]
|
||||
property_group_name = short_name+"_ui"
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
|
||||
source_components_metadata = source_object.components_meta.components
|
||||
@ -234,3 +236,46 @@ def apply_propertyGroup_values_to_object_customProperties(object):
|
||||
if component_definition != None:
|
||||
value = property_group_value_to_custom_property_value(propertyGroup, component_definition, registry, None)
|
||||
object[component_name] = value
|
||||
|
||||
|
||||
|
||||
def apply_customProperty_values_to_object_propertyGroups(object):
|
||||
print("apply custom properties to ", object.name)
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
for component_name in dict(object) :
|
||||
if component_name == "components_meta":
|
||||
continue
|
||||
component_definition = find_component_definition_from_short_name(component_name)
|
||||
if component_definition != None:
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(component_name)
|
||||
components_metadata = object.components_meta.components
|
||||
source_componentMeta = next(filter(lambda component: component["name"] == component_name, components_metadata), None)
|
||||
# matching component means we already have this type of component
|
||||
propertyGroup = getattr(source_componentMeta, property_group_name, None)
|
||||
customProperty_value = object[component_name]
|
||||
#value = property_group_value_to_custom_property_value(propertyGroup, component_definition, registry, None)
|
||||
|
||||
object["__disable__update"] = True # disable update callback while we set the values of the propertyGroup "tree" (as a propertyGroup can contain other propertyGroups)
|
||||
property_group_value_from_custom_property_value(propertyGroup, component_definition, registry, customProperty_value)
|
||||
del object["__disable__update"]
|
||||
|
||||
# removes the given component from the object: removes both the custom property and the matching metadata from the object
|
||||
def remove_component_from_object(object, component_name):
|
||||
del object[component_name]
|
||||
|
||||
components_metadata = getattr(object, "components_meta", None)
|
||||
if components_metadata == None:
|
||||
return False
|
||||
|
||||
components_metadata = components_metadata.components
|
||||
to_remove = []
|
||||
for index, component_meta in enumerate(components_metadata):
|
||||
short_name = component_meta.name
|
||||
if short_name == component_name:
|
||||
to_remove.append(index)
|
||||
break
|
||||
for index in to_remove:
|
||||
components_metadata.remove(index)
|
||||
return True
|
||||
|
||||
|
@ -3,22 +3,22 @@ import json
|
||||
import bpy
|
||||
from bpy_types import Operator
|
||||
from bpy.props import (StringProperty)
|
||||
from .metadata import add_component_to_object, add_metadata_to_components_without_metadata, copy_propertyGroup_values_to_another_object, find_component_definition_from_short_name
|
||||
from .metadata import add_component_to_object, add_metadata_to_components_without_metadata, apply_customProperty_values_to_object_propertyGroups, copy_propertyGroup_values_to_another_object, find_component_definition_from_short_name, remove_component_from_object
|
||||
|
||||
class AddComponentOperator(Operator):
|
||||
"""Add component to blueprint"""
|
||||
bl_idname = "object.addblueprint_to_component"
|
||||
bl_idname = "object.add_bevy_component"
|
||||
bl_label = "Add component to blueprint Operator"
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
component_type: StringProperty(
|
||||
name="component_type",
|
||||
description="component type to add",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def execute(self, context):
|
||||
print("adding component to blueprint", self.component_type)
|
||||
object = context.object
|
||||
print("adding component ", self.component_type, "to object '"+object.name+"'")
|
||||
|
||||
has_component_type = self.component_type != ""
|
||||
if has_component_type and object != None:
|
||||
@ -30,19 +30,19 @@ class AddComponentOperator(Operator):
|
||||
|
||||
class CopyComponentOperator(Operator):
|
||||
"""Copy component from blueprint"""
|
||||
bl_idname = "object.copy_component"
|
||||
bl_idname = "object.copy_bevy_component"
|
||||
bl_label = "Copy component Operator"
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
source_component_name: StringProperty(
|
||||
name="source component_name",
|
||||
description="name of the component to copy",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
source_object_name: StringProperty(
|
||||
name="source object name",
|
||||
description="name of the object to copy the component from",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def register(cls):
|
||||
@ -67,7 +67,7 @@ class CopyComponentOperator(Operator):
|
||||
|
||||
class PasteComponentOperator(Operator):
|
||||
"""Paste component to blueprint"""
|
||||
bl_idname = "object.paste_component"
|
||||
bl_idname = "object.paste_bevy_component"
|
||||
bl_label = "Paste component to blueprint Operator"
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
@ -80,67 +80,79 @@ class PasteComponentOperator(Operator):
|
||||
else:
|
||||
component_name = context.window_manager.copied_source_component_name
|
||||
if not component_name in source_object:
|
||||
self.report({"ERROR"}, "The source component to copy a component from does not exist")
|
||||
self.report({"ERROR"}, "The source component to copy from does not exist")
|
||||
else:
|
||||
component_value = source_object[component_name]
|
||||
print("pasting component to object: component name:", str(component_name), "component value:" + str(component_value))
|
||||
print (context.object)
|
||||
copy_propertyGroup_values_to_another_object(source_object, context.object, component_name)
|
||||
registry = context.window_manager.components_registry
|
||||
copy_propertyGroup_values_to_another_object(source_object, context.object, component_name, registry)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
||||
class DeleteComponentOperator(Operator):
|
||||
class RemoveComponentOperator(Operator):
|
||||
"""Delete component from blueprint"""
|
||||
bl_idname = "object.delete_component"
|
||||
bl_idname = "object.remove_bevy_component"
|
||||
bl_label = "Delete component from blueprint Operator"
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
component_name: StringProperty(
|
||||
name="component name",
|
||||
description="component to delete",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def execute(self, context):
|
||||
object = context.object
|
||||
|
||||
print("removing component ", self.component_name, "from object '"+object.name+"'")
|
||||
|
||||
if object is not None and self.component_name in object:
|
||||
del object[self.component_name]
|
||||
remove_component_from_object(object, self.component_name)
|
||||
else:
|
||||
self.report({"ERROR"}, "The object/ component to remove does not exist")
|
||||
self.report({"ERROR"}, "The object/ component to remove ("+ self.component_name +") does not exist")
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class GenerateComponent_From_custom_property_Operator(Operator):
|
||||
"""generate components from custom property"""
|
||||
bl_idname = "object.generate_component"
|
||||
bl_idname = "object.generate_bevy_component_from_custom_property"
|
||||
bl_label = "Generate component from custom_property Operator"
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
component_name: StringProperty(
|
||||
name="component name",
|
||||
description="component to generate custom properties for",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def execute(self, context):
|
||||
object = context.object
|
||||
add_metadata_to_components_without_metadata(object)
|
||||
|
||||
error = False
|
||||
try:
|
||||
add_metadata_to_components_without_metadata(object)
|
||||
apply_customProperty_values_to_object_propertyGroups(object)
|
||||
except Exception as error:
|
||||
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||
error = True
|
||||
self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Error:" + str(error))
|
||||
if not error:
|
||||
self.report({'INFO'}, "Sucessfully generated UI values for custom properties for selected object")
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
||||
class Toggle_ComponentVisibility(Operator):
|
||||
"""toggles components visibility"""
|
||||
bl_idname = "object.toggle_component_visibility"
|
||||
bl_idname = "object.toggle_bevy_component_visibility"
|
||||
bl_label = "Toggle component visibility"
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
component_name: StringProperty(
|
||||
name="component name",
|
||||
description="component to toggle",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def execute(self, context):
|
||||
object = context.object
|
||||
|
@ -1,12 +1,11 @@
|
||||
import json
|
||||
import bpy
|
||||
from .metadata import do_object_custom_properties_have_missing_metadata
|
||||
from .operators import AddComponentOperator, CopyComponentOperator, DeleteComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, Toggle_ComponentVisibility
|
||||
from .operators import AddComponentOperator, CopyComponentOperator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, Toggle_ComponentVisibility
|
||||
|
||||
def draw_propertyGroup( propertyGroup, layout, nesting =[], rootName=None):
|
||||
is_enum = getattr(propertyGroup, "with_enum")
|
||||
is_list = getattr(propertyGroup, "with_list")
|
||||
#current_short_name = getattr(propertyGroup, "short_name", "") + "_ui"
|
||||
#nesting = nesting + [current_short_name] # we need this convoluted "nested path strings " workaround so that operators working on a given
|
||||
# item in our components hierarchy can get the correct propertyGroup by STRINGS because of course, we cannot pass objects to operators...sigh
|
||||
|
||||
@ -119,7 +118,7 @@ class BEVY_COMPONENTS_PT_ComponentsPanel(bpy.types.Panel):
|
||||
# we get & load our component registry
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
available_components = bpy.context.window_manager.components_list
|
||||
|
||||
registry_has_type_infos = registry.has_type_infos()
|
||||
|
||||
if object is not None:
|
||||
row = layout.row(align=True)
|
||||
@ -137,12 +136,12 @@ class BEVY_COMPONENTS_PT_ComponentsPanel(bpy.types.Panel):
|
||||
# paste components
|
||||
row = layout.row(align=True)
|
||||
row.operator(PasteComponentOperator.bl_idname, text="Paste component ("+bpy.context.window_manager.copied_source_component_name+")", icon="PASTEDOWN")
|
||||
row.enabled = registry.type_infos != None and context.window_manager.copied_source_object != ''
|
||||
row.enabled = registry_has_type_infos and context.window_manager.copied_source_object != ''
|
||||
|
||||
layout.separator()
|
||||
|
||||
# upgrate custom props to components
|
||||
upgradeable_customProperties = registry.type_infos != None and do_object_custom_properties_have_missing_metadata(context.object)
|
||||
upgradeable_customProperties = registry.has_type_infos() and do_object_custom_properties_have_missing_metadata(context.object)
|
||||
if upgradeable_customProperties:
|
||||
row = layout.row(align=True)
|
||||
op = row.operator(GenerateComponent_From_custom_property_Operator.bl_idname, text="generate components from custom properties" , icon="LOOP_FORWARDS")
|
||||
@ -172,28 +171,29 @@ class BEVY_COMPONENTS_PT_ComponentsPanel(bpy.types.Panel):
|
||||
row.label(text=component_name)
|
||||
|
||||
# we fetch the matching ui property group
|
||||
root_propertyGroup_name = component_name+"_ui"
|
||||
propertyGroup = getattr(component_meta, root_propertyGroup_name, None)
|
||||
if propertyGroup:
|
||||
# if the component has only 0 or 1 field names, display inline, otherwise change layout
|
||||
single_field = len(propertyGroup.field_names) < 2
|
||||
prop_group_location = box.row(align=True).column()
|
||||
if single_field:
|
||||
prop_group_location = row.column(align=True)#.split(factor=0.9)#layout.row(align=False)
|
||||
|
||||
if component_visible:
|
||||
if component_invalid:
|
||||
error_message = invalid_details if component_invalid else "Missing component UI data, please reload registry !"
|
||||
prop_group_location.label(text=error_message)
|
||||
draw_propertyGroup(propertyGroup, prop_group_location, [root_propertyGroup_name], component_name)
|
||||
else :
|
||||
row.label(text="details hidden, click on toggle to display")
|
||||
else:
|
||||
error_message = invalid_details if component_invalid else "Missing component UI data, please reload registry !"
|
||||
row.label(text=error_message)
|
||||
root_propertyGroup_name = registry.get_propertyGroupName_from_shortName(component_name)
|
||||
if root_propertyGroup_name:
|
||||
propertyGroup = getattr(component_meta, root_propertyGroup_name, None)
|
||||
if propertyGroup:
|
||||
# if the component has only 0 or 1 field names, display inline, otherwise change layout
|
||||
single_field = len(propertyGroup.field_names) < 2
|
||||
prop_group_location = box.row(align=True).column()
|
||||
if single_field:
|
||||
prop_group_location = row.column(align=True)#.split(factor=0.9)#layout.row(align=False)
|
||||
|
||||
if component_visible:
|
||||
if component_invalid:
|
||||
error_message = invalid_details if component_invalid else "Missing component UI data, please reload registry !"
|
||||
prop_group_location.label(text=error_message)
|
||||
draw_propertyGroup(propertyGroup, prop_group_location, [root_propertyGroup_name], component_name)
|
||||
else :
|
||||
row.label(text="details hidden, click on toggle to display")
|
||||
else:
|
||||
error_message = invalid_details if component_invalid else "Missing component UI data, please reload registry !"
|
||||
row.label(text=error_message)
|
||||
|
||||
# "footer" with additional controls
|
||||
op = row.operator(DeleteComponentOperator.bl_idname, text="", icon="X")
|
||||
op = row.operator(RemoveComponentOperator.bl_idname, text="", icon="X")
|
||||
op.component_name = component_name
|
||||
row.separator()
|
||||
|
||||
|
BIN
tools/bevy_components/docs/generate_components.png
Normal file
BIN
tools/bevy_components/docs/generate_components.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
BIN
tools/bevy_components/docs/generate_components2.png
Normal file
BIN
tools/bevy_components/docs/generate_components2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
BIN
tools/bevy_components/docs/generate_components3.png
Normal file
BIN
tools/bevy_components/docs/generate_components3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
BIN
tools/bevy_components/docs/registry_polling.png
Normal file
BIN
tools/bevy_components/docs/registry_polling.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
tools/bevy_components/docs/update_ui_from_custom_properties.png
Normal file
BIN
tools/bevy_components/docs/update_ui_from_custom_properties.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
@ -18,7 +18,6 @@ def make_empty(name, location, rotation, scale, collection):
|
||||
#bpy.context.view_layer.update()
|
||||
return empty_obj
|
||||
|
||||
#".gltf_auto_export_settings"
|
||||
def upsert_settings(name, data):
|
||||
stored_settings = bpy.data.texts[name] if name in bpy.data.texts else bpy.data.texts.new(name)
|
||||
stored_settings.clear()
|
||||
|
@ -1,7 +1,5 @@
|
||||
import json
|
||||
from bpy_types import PropertyGroup
|
||||
|
||||
|
||||
conversion_tables = {
|
||||
"bool": lambda value: value,
|
||||
|
||||
@ -99,9 +97,9 @@ def property_group_value_to_custom_property_value(property_group, definition, re
|
||||
value = getattr(property_group, variant_name)
|
||||
is_property_group = isinstance(value, PropertyGroup)
|
||||
child_property_group = value if is_property_group else None
|
||||
|
||||
|
||||
value = property_group_value_to_custom_property_value(child_property_group, variant_definition, registry, parent=component_name, value=value)
|
||||
value = selected + str(value,)
|
||||
value = selected + str(value,) #"{}{},".format(selected ,value)
|
||||
elif "properties" in variant_definition:
|
||||
value = getattr(property_group, variant_name)
|
||||
is_property_group = isinstance(value, PropertyGroup)
|
||||
@ -110,8 +108,14 @@ def property_group_value_to_custom_property_value(property_group, definition, re
|
||||
value = property_group_value_to_custom_property_value(child_property_group, variant_definition, registry, parent=component_name, value=value)
|
||||
value = selected + str(value,)
|
||||
else:
|
||||
print("basic enum stuff")
|
||||
value = selected # here the value of the enum is just the name of the variant
|
||||
value = getattr(property_group, variant_name)
|
||||
is_property_group = isinstance(value, PropertyGroup)
|
||||
child_property_group = value if is_property_group else None
|
||||
if child_property_group:
|
||||
value = property_group_value_to_custom_property_value(child_property_group, variant_definition, registry, parent=component_name, value=value)
|
||||
value = selected + str(value,)
|
||||
else:
|
||||
value = selected # here the value of the enum is just the name of the variant
|
||||
else:
|
||||
value = selected
|
||||
|
||||
@ -131,9 +135,13 @@ def property_group_value_to_custom_property_value(property_group, definition, re
|
||||
value.append(item_value)
|
||||
else:
|
||||
value = conversion_tables[type_name](value) if is_value_type else value
|
||||
value = '""' if isinstance(value, PropertyGroup) else value
|
||||
|
||||
|
||||
#print("VALUE", value, type(value))
|
||||
#print("generating custom property value", value, type(value))
|
||||
if isinstance(value, str):
|
||||
value = value.replace("'", "")
|
||||
|
||||
if parent == None:
|
||||
value = str(value).replace("'", "")
|
||||
value = value.replace(",)",")")
|
||||
@ -141,81 +149,3 @@ def property_group_value_to_custom_property_value(property_group, definition, re
|
||||
value = value.replace("True", "true").replace("False", "false")
|
||||
return value
|
||||
|
||||
import re
|
||||
#converts the value of a single custom property into a value (values) of a property group
|
||||
def property_group_value_from_custom_property_value(property_group, definition, registry, custom_property_value):
|
||||
#print(" ")
|
||||
#print("setting property group value", property_group, definition, custom_property_value)
|
||||
type_infos = registry.type_infos
|
||||
value_types_defaults = registry.value_types_defaults
|
||||
print("custom_property_value", custom_property_value)
|
||||
|
||||
def parse_field(item, property_group, definition, field_name):
|
||||
type_info = definition["typeInfo"] if "typeInfo" in definition else None
|
||||
type_def = definition["type"] if "type" in definition else None
|
||||
properties = definition["properties"] if "properties" in definition else {}
|
||||
prefixItems = definition["prefixItems"] if "prefixItems" in definition else []
|
||||
has_properties = len(properties.keys()) > 0
|
||||
has_prefixItems = len(prefixItems) > 0
|
||||
is_enum = type_info == "Enum"
|
||||
is_list = type_info == "List"
|
||||
is_value_type = type_def in value_types_defaults
|
||||
|
||||
print("parsing field", item, "type infos", type_info, "type_def", type_def)
|
||||
if type_info == "Struct":
|
||||
print("is object")
|
||||
for field_name in property_group.field_names:
|
||||
print("field name", field_name)
|
||||
# sub field
|
||||
if isinstance(getattr(property_group, field_name), PropertyGroup):
|
||||
sub_prop_group = getattr(property_group, field_name)
|
||||
ref_name = properties[field_name]["type"]["$ref"].replace("#/$defs/", "")
|
||||
sub_definition = type_infos[ref_name]
|
||||
parse_field(item[field_name], sub_prop_group, sub_definition, field_name)
|
||||
else:
|
||||
setattr(property_group, field_name, item[field_name])
|
||||
|
||||
if has_prefixItems:
|
||||
if len(property_group.field_names) == 1:
|
||||
setattr(property_group, "0", item) # FIXME: not ideal
|
||||
else:
|
||||
for field_name in property_group.field_names:
|
||||
setattr(property_group, field_name, item)
|
||||
if is_enum:
|
||||
if type_def == "object":
|
||||
regexp = re.search('(^[^\(]+)(\((.*)\))', item)
|
||||
chosen_variant = regexp.group(1)
|
||||
chosen_variant_value = regexp.group(3).replace("'", '"').replace("(", "[").replace(")","]")
|
||||
chosen_variant_value = json.loads(chosen_variant_value)
|
||||
|
||||
# first set chosen selection
|
||||
field_name = property_group.field_names[0]
|
||||
setattr(property_group, field_name, chosen_variant)
|
||||
|
||||
# thenlook for the information about the matching variant
|
||||
sub_definition= None
|
||||
for variant in definition["oneOf"]:
|
||||
if variant["title"] == chosen_variant:
|
||||
ref_name = variant["prefixItems"][0]["type"]["$ref"].replace("#/$defs/", "")
|
||||
sub_definition = type_infos[ref_name]
|
||||
break
|
||||
variant_name = "variant_"+chosen_variant
|
||||
if isinstance(getattr(property_group, variant_name), PropertyGroup):
|
||||
sub_prop_group = getattr(property_group, variant_name)
|
||||
parse_field(chosen_variant_value, sub_prop_group, sub_definition, variant_name)
|
||||
else:
|
||||
setattr(property_group, variant_name, chosen_variant_value)
|
||||
else:
|
||||
field_name = property_group.field_names[0]
|
||||
setattr(property_group, field_name, item)
|
||||
|
||||
if is_list:
|
||||
print("is list")
|
||||
|
||||
if is_value_type:
|
||||
print("is value type")
|
||||
|
||||
try:
|
||||
parse_field(custom_property_value, property_group, definition, None)
|
||||
except Exception as error:
|
||||
print("failed to parse raw custom property data", error)
|
320
tools/bevy_components/propGroups/conversions_to_prop_group.py
Normal file
320
tools/bevy_components/propGroups/conversions_to_prop_group.py
Normal file
@ -0,0 +1,320 @@
|
||||
from bpy_types import PropertyGroup
|
||||
import re
|
||||
|
||||
def parse_struct_string(string, start_nesting=0):
|
||||
#print("processing struct string", string, "start_nesting", start_nesting)
|
||||
fields = {}
|
||||
buff = []
|
||||
current_fieldName = None
|
||||
nesting_level = 0
|
||||
|
||||
start_offset = 0
|
||||
end_offset = 0
|
||||
|
||||
for index, char in enumerate(string):
|
||||
buff.append(char)
|
||||
if char == "," and nesting_level == start_nesting:
|
||||
#print("first case", end_offset)
|
||||
end_offset = index
|
||||
end_offset = len(string) if end_offset == 0 else end_offset
|
||||
|
||||
val = "".join(string[start_offset:end_offset])
|
||||
fields[current_fieldName] = val.strip()
|
||||
start_offset = index + 1
|
||||
#print("done with field name", current_fieldName, "value", fields[current_fieldName])
|
||||
|
||||
if char == "[" or char == "(":
|
||||
nesting_level += 1
|
||||
if nesting_level == start_nesting:
|
||||
start_offset = index + 1
|
||||
#print("nesting & setting start offset", start_offset)
|
||||
#print("nesting down", nesting_level)
|
||||
|
||||
if char == "]" or char == ")" :
|
||||
#print("nesting up", nesting_level)
|
||||
if nesting_level == start_nesting:
|
||||
end_offset = index
|
||||
#print("unesting & setting end offset", end_offset)
|
||||
nesting_level -= 1
|
||||
|
||||
|
||||
if char == ":" and nesting_level == start_nesting:
|
||||
end_offset = index
|
||||
fieldName = "".join(string[start_offset:end_offset])
|
||||
current_fieldName = fieldName.strip()
|
||||
start_offset = index + 1
|
||||
end_offset = 0 #hack
|
||||
#print("starting field name", fieldName, "index", index)
|
||||
buff = []
|
||||
|
||||
end_offset = len(string) if end_offset == 0 else end_offset
|
||||
#print("final start and end offset", start_offset, end_offset, "total length", len(string))
|
||||
|
||||
val = "".join(string[start_offset:end_offset])
|
||||
|
||||
fields[current_fieldName] = val.strip()
|
||||
#print("done with all fields", fields)
|
||||
return fields
|
||||
|
||||
def parse_tuplestruct_string(string, start_nesting=0):
|
||||
#print("processing tuppleStruct", string, "start_nesting", start_nesting)
|
||||
fields = []
|
||||
buff = []
|
||||
nesting_level = 0
|
||||
field_index = 0
|
||||
|
||||
start_offset = 0
|
||||
end_offset = 0
|
||||
# todo: strip all stuff before start_nesting
|
||||
|
||||
for index, char in enumerate(string):
|
||||
buff.append(char)
|
||||
if char == "," and nesting_level == start_nesting:
|
||||
end_offset = index
|
||||
end_offset = len(string) if end_offset == 0 else end_offset
|
||||
|
||||
val = "".join(string[start_offset:end_offset])
|
||||
fields.append(val.strip())
|
||||
field_index += 1
|
||||
#print("start and end offset", start_offset, end_offset, "total length", len(string))
|
||||
#print("done with field name", field_index, "value", fields)
|
||||
start_offset = index + 1
|
||||
end_offset = 0 # hack
|
||||
|
||||
if char == "[" or char == "(":
|
||||
nesting_level += 1
|
||||
if nesting_level == start_nesting:
|
||||
start_offset = index + 1
|
||||
#print("nesting & setting start offset", start_offset)
|
||||
#print("nesting down", nesting_level)
|
||||
|
||||
if char == "]" or char == ")" :
|
||||
if nesting_level == start_nesting:
|
||||
end_offset = index
|
||||
#print("unesting & setting end offset", end_offset)
|
||||
#print("nesting up", nesting_level)
|
||||
nesting_level -= 1
|
||||
|
||||
|
||||
end_offset = len(string) if end_offset == 0 else end_offset
|
||||
#print("final start and end offset", start_offset, end_offset, "total length", len(string))
|
||||
|
||||
val = "".join(string[start_offset:end_offset]) #if end_offset != 0 else buff)
|
||||
fields.append(val.strip())
|
||||
fields = list(filter(lambda entry: entry != '', fields))
|
||||
#print("done with all fields", fields)
|
||||
return fields
|
||||
|
||||
|
||||
def parse_vec2(value, caster, typeName):
|
||||
parsed = parse_struct_string(value.replace(typeName,"").replace("(", "").replace(")","") )
|
||||
return [caster(parsed['x']), caster(parsed['y'])]
|
||||
|
||||
def parse_vec3(value, caster, typeName):
|
||||
parsed = parse_struct_string(value.replace(typeName,"").replace("(", "").replace(")","") )
|
||||
return [caster(parsed['x']), caster(parsed['y']), caster(parsed['z'])]
|
||||
|
||||
def parse_vec4(value, caster, typeName):
|
||||
parsed = parse_struct_string(value.replace(typeName,"").replace("(", "").replace(")","") )
|
||||
return [caster(parsed['x']), caster(parsed['y']), caster(parsed['z']), caster(parsed['w'])]
|
||||
|
||||
def parse_color(value, caster, typeName):
|
||||
parsed = parse_struct_string(value.replace(typeName,"").replace("(", "").replace(")","") )
|
||||
return [caster(parsed['red']), caster(parsed['green']), caster(parsed['blue']), caster(parsed['alpha'])]
|
||||
|
||||
def to_int(input):
|
||||
return int(float(input))
|
||||
|
||||
type_mappings = {
|
||||
"bool": lambda value: True if value == "true" else False,
|
||||
|
||||
"u8": lambda value: int(value),
|
||||
"u16": lambda value: int(value),
|
||||
"u32": lambda value: int(value),
|
||||
"u64": lambda value: int(value),
|
||||
"u128": lambda value: int(value),
|
||||
"u64": lambda value: int(value),
|
||||
"usize": lambda value: int(value),
|
||||
|
||||
"i8": lambda value: int(value),
|
||||
"i16": lambda value: int(value),
|
||||
"i32": lambda value: int(value),
|
||||
"i64": lambda value: int(value),
|
||||
"i128": lambda value: int(value),
|
||||
"isize": lambda value: int(value),
|
||||
|
||||
'f32': lambda value: float(value),
|
||||
'f64': lambda value: float(value),
|
||||
|
||||
"glam::Vec2": lambda value: parse_vec2(value, float, "Vec2"),
|
||||
"glam::DVec2": lambda value: parse_vec2(value, float, "DVec2"),
|
||||
"glam::UVec2": lambda value: parse_vec2(value, to_int, "UVec2"),
|
||||
|
||||
'glam::Vec3': lambda value: parse_vec3(value, float, "Vec3"),
|
||||
"glam::Vec3A": lambda value: parse_vec3(value, float, "Vec3A"),
|
||||
"glam::UVec3": lambda value: parse_vec3(value, to_int, "UVec3"),
|
||||
|
||||
"glam::Vec4": lambda value: parse_vec4(value, float, "Vec4"),
|
||||
"glam::DVec4": lambda value: parse_vec4(value, float, "DVec4"),
|
||||
"glam::UVec4": lambda value: parse_vec4(value, to_int, "UVec4"),
|
||||
|
||||
"glam::Quat": lambda value: parse_vec4(value, float, "Quat"),
|
||||
|
||||
'alloc::string::String': lambda value: str(value.replace('"', "")),
|
||||
'bevy_render::color::Color': lambda value: parse_color(value, float, "Rgba"),
|
||||
'bevy_ecs::Entity': lambda value: int(value),
|
||||
}
|
||||
|
||||
def is_def_value_type(definition, registry):
|
||||
if definition == None:
|
||||
return True
|
||||
value_types_defaults = registry.value_types_defaults
|
||||
type_name = definition["title"]
|
||||
is_value_type = type_name in value_types_defaults
|
||||
return is_value_type
|
||||
|
||||
#converts the value of a single custom property into a value (values) of a property group
|
||||
def property_group_value_from_custom_property_value(property_group, definition, registry, value, nesting = []):
|
||||
value_types_defaults = registry.value_types_defaults
|
||||
|
||||
type_info = definition["typeInfo"] if "typeInfo" in definition else None
|
||||
type_def = definition["type"] if "type" in definition else None
|
||||
properties = definition["properties"] if "properties" in definition else {}
|
||||
prefixItems = definition["prefixItems"] if "prefixItems" in definition else []
|
||||
has_properties = len(properties.keys()) > 0
|
||||
has_prefixItems = len(prefixItems) > 0
|
||||
is_enum = type_info == "Enum"
|
||||
is_list = type_info == "List"
|
||||
type_name = definition["title"]
|
||||
|
||||
#is_value_type = type_def in value_types_defaults or type_name in value_types_defaults
|
||||
is_value_type = type_name in value_types_defaults
|
||||
nesting = nesting + [definition["short_name"]]
|
||||
|
||||
"""print(" ")
|
||||
print("raw value", value, "nesting", nesting)
|
||||
print("nesting", len(nesting))
|
||||
print("definition", definition)"""
|
||||
|
||||
if is_value_type:
|
||||
value = value.replace("(", "").replace(")", "")# FIXME: temporary, incoherent use of nesting levels between parse_tuplestruct_string & parse_struct_string
|
||||
value = type_mappings[type_name](value) if type_name in type_mappings else value
|
||||
return value
|
||||
elif type_info == "Struct":
|
||||
if len(property_group.field_names) != 0 :
|
||||
custom_property_values = parse_struct_string(value, start_nesting=1 if value.startswith("(") else 0)
|
||||
for index, field_name in enumerate(property_group.field_names):
|
||||
item_type_name = definition["properties"][field_name]["type"]["$ref"].replace("#/$defs/", "")
|
||||
item_definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None
|
||||
|
||||
custom_prop_value = custom_property_values[field_name]
|
||||
#print("field name", field_name, "value", custom_prop_value)
|
||||
propGroup_value = getattr(property_group, field_name)
|
||||
is_property_group = isinstance(propGroup_value, PropertyGroup)
|
||||
child_property_group = propGroup_value if is_property_group else None
|
||||
if item_definition != None:
|
||||
custom_prop_value = property_group_value_from_custom_property_value(child_property_group, item_definition, registry, value=custom_prop_value, nesting=nesting)
|
||||
else:
|
||||
custom_prop_value = custom_prop_value
|
||||
|
||||
if is_def_value_type(item_definition, registry):
|
||||
setattr(property_group , field_name, custom_prop_value)
|
||||
|
||||
|
||||
else:
|
||||
pass
|
||||
#print("struct with zero fields")
|
||||
|
||||
elif type_info == "Tuple":
|
||||
custom_property_values = parse_tuplestruct_string(value, start_nesting=1 if len(nesting) == 1 else 1)
|
||||
|
||||
for index, field_name in enumerate(property_group.field_names):
|
||||
item_type_name = definition["prefixItems"][index]["type"]["$ref"].replace("#/$defs/", "")
|
||||
item_definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None
|
||||
|
||||
custom_property_value = custom_property_values[index]
|
||||
|
||||
propGroup_value = getattr(property_group, field_name)
|
||||
is_property_group = isinstance(propGroup_value, PropertyGroup)
|
||||
child_property_group = propGroup_value if is_property_group else None
|
||||
if item_definition != None:
|
||||
custom_property_value = property_group_value_from_custom_property_value(child_property_group, item_definition, registry, value=custom_property_value, nesting=nesting)
|
||||
if is_def_value_type(item_definition, registry):
|
||||
setattr(property_group , field_name, custom_property_value)
|
||||
|
||||
elif type_info == "TupleStruct":
|
||||
custom_property_values = parse_tuplestruct_string(value, start_nesting=1 if len(nesting) == 1 else 0)
|
||||
for index, field_name in enumerate(property_group.field_names):
|
||||
item_type_name = definition["prefixItems"][index]["type"]["$ref"].replace("#/$defs/", "")
|
||||
item_definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None
|
||||
|
||||
custom_prop_value = custom_property_values[index]
|
||||
|
||||
value = getattr(property_group, field_name)
|
||||
is_property_group = isinstance(value, PropertyGroup)
|
||||
child_property_group = value if is_property_group else None
|
||||
if item_definition != None:
|
||||
custom_prop_value = property_group_value_from_custom_property_value(child_property_group, item_definition, registry, value=custom_prop_value, nesting=nesting)
|
||||
|
||||
if is_def_value_type(item_definition, registry):
|
||||
setattr(property_group , field_name, custom_prop_value)
|
||||
|
||||
elif type_info == "Enum":
|
||||
field_names = property_group.field_names
|
||||
if type_def == "object":
|
||||
regexp = re.search('(^[^\(]+)(\((.*)\))', value)
|
||||
try:
|
||||
chosen_variant_raw = regexp.group(1)
|
||||
chosen_variant_value = regexp.group(3)
|
||||
chosen_variant_name = "variant_" + chosen_variant_raw
|
||||
except:
|
||||
chosen_variant_raw = value
|
||||
chosen_variant_value = ""
|
||||
chosen_variant_name = "variant_" + chosen_variant_raw
|
||||
selection_index = property_group.field_names.index(chosen_variant_name)
|
||||
variant_definition = definition["oneOf"][selection_index-1]
|
||||
# first we set WHAT variant is selected
|
||||
setattr(property_group, field_names[0], chosen_variant_raw)
|
||||
|
||||
# and then we set the value of the variant
|
||||
if "prefixItems" in variant_definition:
|
||||
value = getattr(property_group, chosen_variant_name)
|
||||
is_property_group = isinstance(value, PropertyGroup)
|
||||
child_property_group = value if is_property_group else None
|
||||
|
||||
chosen_variant_value = "(" +chosen_variant_value +")" # needed to handle nesting correctly
|
||||
value = property_group_value_from_custom_property_value(child_property_group, variant_definition, registry, value=chosen_variant_value, nesting=nesting)
|
||||
|
||||
elif "properties" in variant_definition:
|
||||
value = getattr(property_group, chosen_variant_name)
|
||||
is_property_group = isinstance(value, PropertyGroup)
|
||||
child_property_group = value if is_property_group else None
|
||||
|
||||
value = property_group_value_from_custom_property_value(child_property_group, variant_definition, registry, value=chosen_variant_value, nesting=nesting)
|
||||
|
||||
else:
|
||||
chosen_variant_raw = value
|
||||
setattr(property_group, field_names[0], chosen_variant_raw)
|
||||
|
||||
elif type_info == "List":
|
||||
item_list = getattr(property_group, "list")
|
||||
item_type_name = getattr(property_group, "type_name_short")
|
||||
custom_property_values = parse_tuplestruct_string(value, start_nesting=2 if item_type_name.startswith("wrapper_") and value.startswith('(') else 1) # TODO : the additional check here is wrong, there is an issue somewhere in higher level stuff
|
||||
# clear list first
|
||||
item_list.clear()
|
||||
#print("custom_property_values", custom_property_values, "value", value, "item_type_name", item_type_name)
|
||||
|
||||
for raw_value in custom_property_values:
|
||||
new_entry = item_list.add()
|
||||
item_type_name = getattr(new_entry, "type_name") # we get the REAL type name
|
||||
definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None
|
||||
|
||||
if definition != None:
|
||||
property_group_value_from_custom_property_value(new_entry, definition, registry, value=raw_value, nesting=nesting)
|
||||
else:
|
||||
try:
|
||||
value = value.replace("(", "").replace(")", "")# FIXME: temporary, incoherent use of nesting levels between parse_tuplestruct_string & parse_struct_string
|
||||
value = type_mappings[type_name](value) if type_name in type_mappings else value
|
||||
return value
|
||||
except:
|
||||
pass
|
@ -59,7 +59,6 @@ def process_component(registry, definition, update, extras=None, nesting = []):
|
||||
root_component = nesting[0] if len(nesting) > 0 else component_name
|
||||
# print("DONE:",short_name,"__annotations__", __annotations__)
|
||||
# print("")
|
||||
# property_group_name = short_name+"_ui"
|
||||
property_group_params = {
|
||||
**extras,
|
||||
'__annotations__': __annotations__,
|
||||
@ -75,8 +74,7 @@ def process_component(registry, definition, update, extras=None, nesting = []):
|
||||
-BasicTest => the registration & update callback of this one overwrites the first "basicTest"
|
||||
have not found a cleaner workaround so far
|
||||
"""
|
||||
property_group_name = str(hash(str(nesting))) + short_name+"_ui" if len(nesting) > 0 else short_name+"_ui"
|
||||
|
||||
property_group_name = registry.generate_propGroup_name(nesting, short_name)
|
||||
(property_group_pointer, property_group_class) = property_group_from_infos(property_group_name, property_group_params)
|
||||
# add our component propertyGroup to the registry
|
||||
registry.register_component_propertyGroup(property_group_name, property_group_pointer)
|
||||
|
@ -7,7 +7,7 @@ def process_enum(registry, definition, update, nesting):
|
||||
type_def = definition["type"] if "type" in definition else None
|
||||
values = definition["oneOf"]
|
||||
|
||||
nesting = nesting+ [short_name]
|
||||
nesting = nesting + [short_name]
|
||||
__annotations__ = {}
|
||||
original_type_name = "enum"
|
||||
|
||||
|
@ -11,6 +11,7 @@ def process_list(registry, definition, update, nesting=[]):
|
||||
|
||||
item_definition = type_infos[ref_name]
|
||||
item_long_name = item_definition["title"]
|
||||
item_short_name = item_definition["short_name"]
|
||||
is_item_value_type = item_long_name in value_types_defaults
|
||||
|
||||
property_group_class = None
|
||||
@ -24,10 +25,11 @@ def process_list(registry, definition, update, nesting=[]):
|
||||
nesting = nesting+[short_name]
|
||||
item_collection = CollectionProperty(type=property_group_class)
|
||||
|
||||
item_short_name = item_short_name if not is_item_value_type else "wrapper_" + item_short_name
|
||||
__annotations__ = {
|
||||
"list": item_collection,
|
||||
"list_index": IntProperty(name = "Index for list", default = 0, update=update),
|
||||
"type_name_short": StringProperty(default=short_name)
|
||||
"type_name_short": StringProperty(default=item_short_name)
|
||||
}
|
||||
|
||||
return __annotations__
|
@ -1,5 +1,5 @@
|
||||
import bpy
|
||||
from .conversions import property_group_value_to_custom_property_value
|
||||
from .conversions_from_prop_group import property_group_value_to_custom_property_value
|
||||
from .process_component import process_component
|
||||
from .utils import update_calback_helper
|
||||
|
||||
@ -8,21 +8,23 @@ def update_component(self, context, definition, component_name):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
current_object = bpy.context.object
|
||||
update_disabled = current_object["__disable__update"] if "__disable__update" in current_object else False
|
||||
update_disabled = registry.disable_all_object_updates or update_disabled # global settings
|
||||
if update_disabled:
|
||||
return
|
||||
print("")
|
||||
print("update in component", component_name, self)
|
||||
print("update in component", component_name, self, "current_object", current_object.name)
|
||||
components_in_object = current_object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == component_name, components_in_object), None)
|
||||
if component_meta != None:
|
||||
self = getattr(component_meta, component_name+"_ui")
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(component_name)
|
||||
self = getattr(component_meta, property_group_name)
|
||||
# we use our helper to set the values
|
||||
context.object[component_name] = property_group_value_to_custom_property_value(self, definition, registry, None)
|
||||
|
||||
|
||||
def generate_propertyGroups_for_components():
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
if registry.type_infos == None:
|
||||
if not registry.has_type_infos():
|
||||
registry.load_type_infos()
|
||||
|
||||
type_infos = registry.type_infos
|
||||
@ -33,6 +35,6 @@ def generate_propertyGroups_for_components():
|
||||
is_component = definition['isComponent'] if "isComponent" in definition else False
|
||||
root_property_name = short_name if is_component else None
|
||||
process_component(registry, definition, update_calback_helper(definition, update_component, root_property_name), None, [])
|
||||
|
||||
|
||||
# if we had to add any wrapper types on the fly, process them now
|
||||
registry.process_custom_types()
|
3
tools/bevy_components/pytest.ini
Normal file
3
tools/bevy_components/pytest.ini
Normal file
@ -0,0 +1,3 @@
|
||||
[pytest]
|
||||
testpaths =
|
||||
tests
|
@ -5,8 +5,7 @@ from bpy.props import (StringProperty)
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
|
||||
from ..helpers import upsert_settings
|
||||
from ..components.metadata import apply_propertyGroup_values_to_object_customProperties, ensure_metadata_for_all_objects
|
||||
from ..components.operators import GenerateComponent_From_custom_property_Operator
|
||||
from ..components.metadata import apply_customProperty_values_to_object_propertyGroups, apply_propertyGroup_values_to_object_customProperties, ensure_metadata_for_all_objects
|
||||
from ..propGroups.prop_groups import generate_propertyGroups_for_components
|
||||
|
||||
class ReloadRegistryOperator(Operator):
|
||||
@ -18,7 +17,7 @@ class ReloadRegistryOperator(Operator):
|
||||
component_type: StringProperty(
|
||||
name="component_type",
|
||||
description="component type to add",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def execute(self, context):
|
||||
print("reload registry")
|
||||
@ -28,7 +27,12 @@ class ReloadRegistryOperator(Operator):
|
||||
print("")
|
||||
print("")
|
||||
ensure_metadata_for_all_objects()
|
||||
#add_metadata_to_components_without_metadata(context.object)
|
||||
|
||||
# now force refresh the ui
|
||||
for area in context.screen.areas:
|
||||
for region in area.regions:
|
||||
if region.type == "UI":
|
||||
region.tag_redraw()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@ -47,7 +51,7 @@ class COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL(Operator):
|
||||
return {'FINISHED'}
|
||||
|
||||
class COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT(Operator):
|
||||
"""Apply registry to CURRENT object: update the custom property values of all objects based on their definition, if any"""
|
||||
"""Apply registry to CURRENT object: update the custom property values of current object based on their definition, if any"""
|
||||
bl_idname = "object.refresh_custom_properties_current"
|
||||
bl_label = "Apply Registry to current object"
|
||||
bl_options = {"UNDO"}
|
||||
@ -57,6 +61,53 @@ class COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT(Operator):
|
||||
object = context.object
|
||||
apply_propertyGroup_values_to_object_customProperties(object)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT(Operator):
|
||||
"""Update UI values from custom properties to CURRENT object"""
|
||||
bl_idname = "object.refresh_ui_from_custom_properties_current"
|
||||
bl_label = "Apply custom_properties to current object"
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
print("apply custom properties to current object")
|
||||
object = context.object
|
||||
error = False
|
||||
try:
|
||||
apply_customProperty_values_to_object_propertyGroups(object)
|
||||
except Exception as error:
|
||||
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||
error = True
|
||||
self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Error:" + str(error))
|
||||
if not error:
|
||||
self.report({'INFO'}, "Sucessfully generated UI values for custom properties for selected object")
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL(Operator):
|
||||
"""Update UI values from custom properties to ALL object"""
|
||||
bl_idname = "object.refresh_ui_from_custom_properties_all"
|
||||
bl_label = "Apply custom_properties to all objects"
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
print("apply custom properties to all object")
|
||||
bpy.context.window_manager.components_registry.disable_all_object_updates = True
|
||||
errors = []
|
||||
for object in bpy.data.objects:
|
||||
try:
|
||||
apply_customProperty_values_to_object_propertyGroups(object)
|
||||
except Exception as error:
|
||||
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||
errors.append( "object: '" + object.name + "', error: " + str(error))
|
||||
if len(errors) > 0:
|
||||
self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Errors:" + str(errors))
|
||||
else:
|
||||
self.report({'INFO'}, "Sucessfully generated UI values for custom properties for all objects")
|
||||
bpy.context.window_manager.components_registry.disable_all_object_updates = False
|
||||
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class OT_OpenFilebrowser(Operator, ImportHelper):
|
||||
"""Browse for registry json file"""
|
||||
@ -66,7 +117,7 @@ class OT_OpenFilebrowser(Operator, ImportHelper):
|
||||
filter_glob: StringProperty(
|
||||
default='*.json',
|
||||
options={'HIDDEN'}
|
||||
)
|
||||
) # type: ignore
|
||||
def execute(self, context):
|
||||
"""Do something with the selected file(s)."""
|
||||
#filename, extension = os.path.splitext(self.filepath)
|
||||
|
@ -1,16 +1,58 @@
|
||||
import bpy
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from bpy_types import (PropertyGroup)
|
||||
from bpy.props import (StringProperty, BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, EnumProperty, PointerProperty, CollectionProperty)
|
||||
from ..components.metadata import ComponentInfos
|
||||
|
||||
from ..helpers import load_settings
|
||||
from ..propGroups.prop_groups import generate_propertyGroups_for_components
|
||||
from ..components.metadata import ComponentMetadata, ensure_metadata_for_all_objects
|
||||
|
||||
# helper class to store missing bevy types information
|
||||
class MissingBevyType(bpy.types.PropertyGroup):
|
||||
type_name: bpy.props.StringProperty(
|
||||
name="type",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
# helper function to deal with timer
|
||||
def toggle_watcher(self, context):
|
||||
print("toggling watcher", self.watcher_enabled, watch_schema, self, bpy.app.timers)
|
||||
if not self.watcher_enabled:
|
||||
try:
|
||||
bpy.app.timers.unregister(watch_schema)
|
||||
except Exception as error:
|
||||
print("failed to unregister", error)
|
||||
pass
|
||||
else:
|
||||
self.watcher_active = True
|
||||
bpy.app.timers.register(watch_schema)
|
||||
|
||||
def watch_schema():
|
||||
self = bpy.context.window_manager.components_registry
|
||||
print("watching schema file for changes")
|
||||
try:
|
||||
stamp = os.stat(self.schemaFullPath).st_mtime
|
||||
stamp = str(stamp)
|
||||
if stamp != self.schemaTimeStamp and self.schemaTimeStamp != "":
|
||||
print("FILE CHANGED !!", stamp, self.schemaTimeStamp)
|
||||
# see here for better ways : https://stackoverflow.com/questions/11114492/check-if-a-file-is-not-open-nor-being-used-by-another-process
|
||||
"""try:
|
||||
os.rename(path, path)
|
||||
#return False
|
||||
except OSError: # file is in use
|
||||
print("in use")
|
||||
#return True"""
|
||||
#bpy.ops.object.reload_registry()
|
||||
# we need to add an additional delay as the file might not have loaded yet
|
||||
bpy.app.timers.register(lambda: bpy.ops.object.reload_registry(), first_interval=1)
|
||||
|
||||
self.schemaTimeStamp = stamp
|
||||
except Exception as error:
|
||||
pass
|
||||
return self.watcher_poll_frequency if self.watcher_enabled else None
|
||||
|
||||
|
||||
# this is where we store the information for all available components
|
||||
class ComponentsRegistry(PropertyGroup):
|
||||
@ -21,20 +63,45 @@ class ComponentsRegistry(PropertyGroup):
|
||||
name="schema path",
|
||||
description="path to the registry schema file",
|
||||
default="registry.json"
|
||||
)
|
||||
)# type: ignore
|
||||
schemaFullPath : bpy.props.StringProperty(
|
||||
name="schema full path",
|
||||
description="path to the registry schema file",
|
||||
)# type: ignore
|
||||
|
||||
registry: bpy.props. StringProperty(
|
||||
name="registry",
|
||||
description="component registry"
|
||||
)
|
||||
)# type: ignore
|
||||
|
||||
missing_type_infos: StringProperty(
|
||||
name="missing type infos",
|
||||
description="unregistered/missing type infos"
|
||||
)
|
||||
)# type: ignore
|
||||
|
||||
missing_types_list: CollectionProperty(name="missing types list", type=MissingBevyType)
|
||||
missing_types_list_index: IntProperty(name = "Index for missing types list", default = 0)
|
||||
disable_all_object_updates: BoolProperty(name="disable_object_updates", default=False) # type: ignore
|
||||
|
||||
## file watcher
|
||||
watcher_enabled: BoolProperty(name="Watcher_enabled", default=True, update=toggle_watcher)# type: ignore
|
||||
watcher_active: BoolProperty(name = "Flag for watcher status", default = False)# type: ignore
|
||||
|
||||
watcher_poll_frequency: IntProperty(
|
||||
name="watcher poll frequency",
|
||||
description="frequency (s) at wich to poll for changes to the registry file",
|
||||
min=1,
|
||||
max=10,
|
||||
default=1
|
||||
)# type: ignore
|
||||
|
||||
schemaTimeStamp: StringProperty(
|
||||
name="last timestamp of schema file",
|
||||
description="",
|
||||
default=""
|
||||
)# type: ignore
|
||||
|
||||
|
||||
missing_types_list: CollectionProperty(name="missing types list", type=MissingBevyType)# type: ignore
|
||||
missing_types_list_index: IntProperty(name = "Index for missing types list", default = 0)# type: ignore
|
||||
|
||||
blender_property_mapping = {
|
||||
"bool": dict(type=BoolProperty, presets=dict()),
|
||||
@ -52,6 +119,7 @@ class ComponentsRegistry(PropertyGroup):
|
||||
"i32":dict(type=IntProperty, presets=dict()),
|
||||
"i64":dict(type=IntProperty, presets=dict()),
|
||||
"i128":dict(type=IntProperty, presets=dict()),
|
||||
"isize": dict(type=IntProperty, presets=dict()),
|
||||
|
||||
"f32": dict(type=FloatProperty, presets=dict()),
|
||||
"f64": dict(type=FloatProperty, presets=dict()),
|
||||
@ -77,9 +145,14 @@ class ComponentsRegistry(PropertyGroup):
|
||||
"char": dict(type=StringProperty, presets=dict()),
|
||||
"str": dict(type=StringProperty, presets=dict()),
|
||||
"alloc::string::String": dict(type=StringProperty, presets=dict()),
|
||||
"alloc::borrow::Cow<str>": dict(type=StringProperty, presets=dict()),
|
||||
|
||||
|
||||
"enum": dict(type=EnumProperty, presets=dict()),
|
||||
|
||||
#"alloc::vec::Vec<alloc::string::String>": dict(type=CollectionProperty, presets=dict(type=PointerProperty(StringProperty))), #FIXME: we need more generic stuff
|
||||
'bevy_ecs::Entity': {"type": IntProperty, "presets": {"min":0} },
|
||||
'bevy_utils::Uuid': dict(type=StringProperty, presets=dict()),
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -98,12 +171,14 @@ class ComponentsRegistry(PropertyGroup):
|
||||
"u32":0,
|
||||
"u64":0,
|
||||
"u128":0,
|
||||
"usize":0,
|
||||
|
||||
"i8": 0,
|
||||
"i16":0,
|
||||
"i32":0,
|
||||
"i64":0,
|
||||
"i128":0,
|
||||
"isize":0,
|
||||
|
||||
"f32": 0.0,
|
||||
"f64":0.0,
|
||||
@ -111,6 +186,7 @@ class ComponentsRegistry(PropertyGroup):
|
||||
"char": " ",
|
||||
"str": " ",
|
||||
"alloc::string::String": " ",
|
||||
"alloc::borrow::Cow<str>": " ",
|
||||
|
||||
"glam::Vec2": [0.0, 0.0],
|
||||
"glam::DVec2": [0.0, 0.0],
|
||||
@ -127,40 +203,66 @@ class ComponentsRegistry(PropertyGroup):
|
||||
"glam::Quat": [0.0, 0.0, 0.0, 0.0],
|
||||
|
||||
"bevy_render::color::Color": [1.0, 1.0, 0.0, 1.0],
|
||||
|
||||
'bevy_ecs::Entity': 0,#4294967295, # this is the same as Bevy's Entity::Placeholder, too big for Blender..sigh
|
||||
'bevy_utils::Uuid': '"'+str(uuid.uuid4())+'"'
|
||||
|
||||
}
|
||||
|
||||
type_infos = None
|
||||
type_infos = {}
|
||||
type_infos_missing = []
|
||||
component_propertyGroups = {}
|
||||
short_names_to_long_names = {}
|
||||
|
||||
custom_types_to_add = {}
|
||||
invalid_components = []
|
||||
|
||||
@classmethod
|
||||
def register(cls):
|
||||
bpy.types.WindowManager.components_registry = PointerProperty(type=ComponentsRegistry)
|
||||
bpy.context.window_manager.components_registry.watcher_active = False
|
||||
|
||||
@classmethod
|
||||
def unregister(cls):
|
||||
bpy.context.window_manager.components_registry.watcher_active = False
|
||||
|
||||
for propgroup_name in cls.component_propertyGroups.keys():
|
||||
try:
|
||||
delattr(ComponentInfos, propgroup_name)
|
||||
print("unregistered propertyGroup", propgroup_name)
|
||||
delattr(ComponentMetadata, propgroup_name)
|
||||
#print("unregistered propertyGroup", propgroup_name)
|
||||
except Exception as error:
|
||||
pass
|
||||
#print("failed to remove", error, "ComponentInfos")
|
||||
#print("failed to remove", error, "ComponentMetadata")
|
||||
|
||||
del bpy.types.WindowManager.components_registry
|
||||
|
||||
def load_schema(self):
|
||||
# cleanup missing types list
|
||||
self.missing_types_list.clear()
|
||||
self.type_infos = None
|
||||
self.type_infos_missing.clear()
|
||||
file_path = bpy.data.filepath
|
||||
try:
|
||||
bpy.app.timers.unregister(watch_schema)
|
||||
except Exception as error:
|
||||
print("failed to unregister", error)
|
||||
pass
|
||||
|
||||
del bpy.types.WindowManager.components_registry
|
||||
|
||||
|
||||
|
||||
|
||||
def load_schema(self):
|
||||
print("load schema", self)
|
||||
# cleanup previous data if any
|
||||
self.propGroupIdCounter = 0
|
||||
self.short_names_to_propgroup_names.clear()
|
||||
self.missing_types_list.clear()
|
||||
self.type_infos.clear()
|
||||
self.type_infos_missing.clear()
|
||||
self.component_propertyGroups.clear()
|
||||
self.short_names_to_long_names.clear()
|
||||
self.custom_types_to_add.clear()
|
||||
self.invalid_components.clear()
|
||||
|
||||
# now prepare paths to load data
|
||||
file_path = bpy.data.filepath
|
||||
# Get the folder
|
||||
folder_path = os.path.dirname(file_path)
|
||||
path = os.path.join(folder_path, self.schemaPath)
|
||||
self.schemaFullPath = path
|
||||
|
||||
f = Path(bpy.path.abspath(path)) # make a path object of abs path
|
||||
with open(path) as f:
|
||||
@ -168,9 +270,32 @@ class ComponentsRegistry(PropertyGroup):
|
||||
defs = data["$defs"]
|
||||
self.registry = json.dumps(defs) # FIXME:meh ?
|
||||
|
||||
# start timer
|
||||
if not self.watcher_active and self.watcher_enabled:
|
||||
self.watcher_active = True
|
||||
print("registering function", watch_schema)
|
||||
bpy.app.timers.register(watch_schema)
|
||||
|
||||
|
||||
# we load the json once, so we do not need to do it over & over again
|
||||
def load_type_infos(self):
|
||||
print("load type infos")
|
||||
ComponentsRegistry.type_infos = json.loads(self.registry)
|
||||
|
||||
def has_type_infos(self):
|
||||
return len(self.type_infos.keys()) != 0
|
||||
|
||||
def load_settings(self):
|
||||
print("loading settings")
|
||||
settings = load_settings(self.settings_save_path)
|
||||
|
||||
if settings!= None:
|
||||
print("settings", settings)
|
||||
self.schemaPath = settings["schemaPath"]
|
||||
self.load_schema()
|
||||
generate_propertyGroups_for_components()
|
||||
ensure_metadata_for_all_objects()
|
||||
|
||||
|
||||
# we keep a list of component propertyGroup around
|
||||
def register_component_propertyGroup(self, name, propertyGroup):
|
||||
@ -188,7 +313,6 @@ class ComponentsRegistry(PropertyGroup):
|
||||
item = self.missing_types_list.add()
|
||||
item.type_name = type_name
|
||||
|
||||
custom_types_to_add = {}
|
||||
def add_custom_type(self, type_name, type_definition):
|
||||
self.custom_types_to_add[type_name] = type_definition
|
||||
|
||||
@ -197,9 +321,41 @@ class ComponentsRegistry(PropertyGroup):
|
||||
self.type_infos[type_name] = self.custom_types_to_add[type_name]
|
||||
self.custom_types_to_add.clear()
|
||||
|
||||
invalid_components = []
|
||||
def add_invalid_component(self, component_name):
|
||||
self.invalid_components.append(component_name)
|
||||
|
||||
|
||||
###########
|
||||
|
||||
propGroupIdCounter: IntProperty(
|
||||
name="propGroupIdCounter",
|
||||
description="",
|
||||
min=0,
|
||||
max=1000000000,
|
||||
default=0
|
||||
) # type: ignore
|
||||
|
||||
short_names_to_propgroup_names = {}
|
||||
|
||||
# generate propGroup name from nesting level & shortName: each shortName + nesting is unique
|
||||
def generate_propGroup_name(self, nesting, shortName):
|
||||
#print("gen propGroup name for", shortName, nesting)
|
||||
#if shortName in self.short_names_to_propgroup_names and len(nesting) == 0:
|
||||
# return self.get_propertyGroupName_from_shortName(shortName)
|
||||
|
||||
self.propGroupIdCounter += 1
|
||||
|
||||
propGroupIndex = str(self.propGroupIdCounter)
|
||||
propGroupName = propGroupIndex + "_ui"
|
||||
key = str(nesting) + shortName if len(nesting) > 0 else shortName
|
||||
self.short_names_to_propgroup_names[key] = propGroupName
|
||||
return propGroupName
|
||||
|
||||
def get_propertyGroupName_from_shortName(self, shortName):
|
||||
|
||||
return self.short_names_to_propgroup_names.get(shortName, None)
|
||||
|
||||
|
||||
"""
|
||||
object[component_definition.name] = 0.5
|
||||
property_manager = object.id_properties_ui(component_definition.name)
|
||||
|
@ -1,6 +1,11 @@
|
||||
import bpy
|
||||
from bpy_types import (UIList)
|
||||
from .operators import(OT_OpenFilebrowser, ReloadRegistryOperator, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT)
|
||||
from .operators import(
|
||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
|
||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT,
|
||||
OT_OpenFilebrowser, ReloadRegistryOperator,
|
||||
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL,
|
||||
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT)
|
||||
|
||||
class BEVY_COMPONENTS_PT_Configuration(bpy.types.Panel):
|
||||
bl_idname = "BEVY_COMPONENTS_PT_Configuration"
|
||||
@ -16,6 +21,7 @@ class BEVY_COMPONENTS_PT_Configuration(bpy.types.Panel):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
registry = context.window_manager.components_registry
|
||||
registry_has_type_infos = registry.has_type_infos()
|
||||
selected_object = context.selected_objects[0] if len(context.selected_objects) > 0 else None
|
||||
|
||||
row = layout.row()
|
||||
@ -28,6 +34,12 @@ class BEVY_COMPONENTS_PT_Configuration(bpy.types.Panel):
|
||||
layout.separator()
|
||||
layout.operator(ReloadRegistryOperator.bl_idname, text="reload registry" , icon="FILE_REFRESH")
|
||||
|
||||
layout.separator()
|
||||
row = layout.row()
|
||||
|
||||
row.prop(registry, "watcher_enabled", text="enable registry file polling")
|
||||
row.prop(registry, "watcher_poll_frequency", text="registry file poll frequency (s)")
|
||||
|
||||
layout.separator()
|
||||
layout.separator()
|
||||
|
||||
@ -36,13 +48,26 @@ class BEVY_COMPONENTS_PT_Configuration(bpy.types.Panel):
|
||||
row.alert = True
|
||||
|
||||
row = layout.row()
|
||||
row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT.bl_idname, text="update custom properties of current object" , icon="FILE_REFRESH")
|
||||
row.enabled = registry.type_infos != None and selected_object is not None
|
||||
row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT.bl_idname, text="update custom properties of current object" , icon="LOOP_FORWARDS")
|
||||
row.enabled = registry_has_type_infos and selected_object is not None
|
||||
|
||||
layout.separator()
|
||||
row = layout.row()
|
||||
row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL.bl_idname, text="update custom properties of ALL objects" , icon="FILE_REFRESH")
|
||||
row.enabled = registry.type_infos != None
|
||||
row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL.bl_idname, text="update custom properties of ALL objects" , icon="LOOP_FORWARDS")
|
||||
row.enabled = registry_has_type_infos
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="WARNING ! The following operations will try to overwrite your existing ui values if they have matching types on the bevy side !")
|
||||
row.alert = True
|
||||
|
||||
row = layout.row()
|
||||
row.operator(COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT.bl_idname, text="update UI FROM custom properties of current object" , icon="LOOP_BACK")
|
||||
row.enabled = registry_has_type_infos and selected_object is not None
|
||||
|
||||
layout.separator()
|
||||
row = layout.row()
|
||||
row.operator(COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL.bl_idname, text="update UI FROM custom properties of ALL objects" , icon="LOOP_BACK")
|
||||
row.enabled = registry_has_type_infos
|
||||
|
||||
|
||||
class BEVY_COMPONENTS_PT_MissingTypesPanel(bpy.types.Panel):
|
||||
|
0
tools/bevy_components/tests/__init__.py
Normal file
0
tools/bevy_components/tests/__init__.py
Normal file
216
tools/bevy_components/tests/component_values_shuffler.py
Normal file
216
tools/bevy_components/tests/component_values_shuffler.py
Normal file
@ -0,0 +1,216 @@
|
||||
|
||||
import random
|
||||
import string
|
||||
import uuid
|
||||
from bpy_types import PropertyGroup
|
||||
|
||||
def random_bool():
|
||||
return bool(random.getrandbits(1))
|
||||
|
||||
def rand_int():
|
||||
return random.randint(0, 100)
|
||||
|
||||
def rand_float():
|
||||
return random.random()
|
||||
|
||||
def random_word(length):
|
||||
letters = string.ascii_lowercase
|
||||
return ''.join(random.choice(letters) for i in range(length))
|
||||
|
||||
def random_vec(length, type,):
|
||||
value = []
|
||||
for i in range(0, length):
|
||||
if type == 'float':
|
||||
value.append(rand_float())
|
||||
if type == 'int':
|
||||
value.append(rand_int())
|
||||
return value
|
||||
|
||||
type_mappings = {
|
||||
"bool": random_bool,
|
||||
|
||||
"u8": rand_int,
|
||||
"u16": rand_int,
|
||||
"u32": rand_int,
|
||||
"u64": rand_int,
|
||||
"u128": rand_int,
|
||||
"u64": rand_int,
|
||||
"usize": rand_int,
|
||||
|
||||
"i8": rand_int,
|
||||
"i16": rand_int,
|
||||
"i32": rand_int,
|
||||
"i64": rand_int,
|
||||
"i128": rand_int,
|
||||
"isize": rand_int,
|
||||
|
||||
'f32': rand_float,
|
||||
'f64': rand_float,
|
||||
|
||||
"glam::Vec2": lambda : random_vec(2, 'float'),
|
||||
"glam::DVec2": lambda : random_vec(2, 'float'),
|
||||
"glam::UVec2": lambda : random_vec(2, 'int'),
|
||||
|
||||
'glam::Vec3': lambda : random_vec(3, 'float'),
|
||||
"glam::Vec3A": lambda : random_vec(3, 'float'),
|
||||
"glam::UVec3": lambda : random_vec(3, 'int'),
|
||||
|
||||
"glam::Vec4": lambda : random_vec(4, 'float'),
|
||||
"glam::DVec4": lambda : random_vec(4, 'float'),
|
||||
"glam::UVec4": lambda : random_vec(4, 'int'),
|
||||
|
||||
"glam::Quat": lambda : random_vec(4, 'float'),
|
||||
|
||||
'bevy_render::color::Color': lambda : random_vec(4, 'float'),
|
||||
'alloc::string::String': lambda : random_word(8),
|
||||
'alloc::borrow::Cow<str>': lambda : random_word(8),
|
||||
|
||||
'bevy_ecs::Entity': lambda: 0, #4294967295, #
|
||||
'bevy_utils::Uuid': lambda: '"'+str( uuid.UUID("73b3b118-7d01-4778-8bcc-4e79055f5d22") )+'"'
|
||||
}
|
||||
#
|
||||
|
||||
def is_def_value_type(definition, registry):
|
||||
if definition == None:
|
||||
return True
|
||||
value_types_defaults = registry.value_types_defaults
|
||||
type_name = definition["title"]
|
||||
is_value_type = type_name in value_types_defaults
|
||||
return is_value_type
|
||||
|
||||
# see https://docs.python.org/3/library/random.html
|
||||
def component_values_shuffler(seed=1, property_group=None, definition=None, registry=None, parent=None):
|
||||
if parent == None:
|
||||
random.seed(seed)
|
||||
|
||||
value_types_defaults = registry.value_types_defaults
|
||||
component_name = definition["short_name"]
|
||||
type_info = definition["typeInfo"] if "typeInfo" in definition else None
|
||||
type_def = definition["type"] if "type" in definition else None
|
||||
properties = definition["properties"] if "properties" in definition else {}
|
||||
prefixItems = definition["prefixItems"] if "prefixItems" in definition else []
|
||||
has_properties = len(properties.keys()) > 0
|
||||
has_prefixItems = len(prefixItems) > 0
|
||||
is_enum = type_info == "Enum"
|
||||
is_list = type_info == "List"
|
||||
type_name = definition["title"]
|
||||
|
||||
#is_value_type = type_def in value_types_defaults or type_name in value_types_defaults
|
||||
is_value_type = type_name in value_types_defaults
|
||||
|
||||
if is_value_type:
|
||||
fieldValue = type_mappings[type_name]()
|
||||
return fieldValue
|
||||
|
||||
elif type_info == "Struct":
|
||||
for index, field_name in enumerate(property_group.field_names):
|
||||
item_type_name = definition["properties"][field_name]["type"]["$ref"].replace("#/$defs/", "")
|
||||
item_definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None
|
||||
|
||||
value = getattr(property_group, field_name)
|
||||
is_property_group = isinstance(value, PropertyGroup)
|
||||
child_property_group = value if is_property_group else None
|
||||
if item_definition != None:
|
||||
value = component_values_shuffler(seed, child_property_group, item_definition, registry, parent=component_name)
|
||||
else:
|
||||
value = '""'
|
||||
is_item_value_type = is_def_value_type(item_definition, registry)
|
||||
if is_item_value_type:
|
||||
#print("setting attr", field_name , "for", component_name, "to", value, "value type", is_item_value_type)
|
||||
setattr(property_group , field_name, value)
|
||||
|
||||
elif type_info == "Tuple":
|
||||
#print("tup")
|
||||
|
||||
for index, field_name in enumerate(property_group.field_names):
|
||||
item_type_name = definition["prefixItems"][index]["type"]["$ref"].replace("#/$defs/", "")
|
||||
item_definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None
|
||||
|
||||
value = getattr(property_group, field_name)
|
||||
is_property_group = isinstance(value, PropertyGroup)
|
||||
child_property_group = value if is_property_group else None
|
||||
if item_definition != None:
|
||||
value = component_values_shuffler(seed, child_property_group, item_definition, registry, parent=component_name)
|
||||
else:
|
||||
value = '""'
|
||||
|
||||
is_item_value_type = is_def_value_type(item_definition, registry)
|
||||
if is_item_value_type:
|
||||
#print("setting attr", field_name , "for", component_name, "to", value, "value type", is_item_value_type)
|
||||
setattr(property_group , field_name, value)
|
||||
|
||||
elif type_info == "TupleStruct":
|
||||
#print("tupstruct")
|
||||
for index, field_name in enumerate(property_group.field_names):
|
||||
item_type_name = definition["prefixItems"][index]["type"]["$ref"].replace("#/$defs/", "")
|
||||
item_definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None
|
||||
|
||||
value = getattr(property_group, field_name)
|
||||
is_property_group = isinstance(value, PropertyGroup)
|
||||
child_property_group = value if is_property_group else None
|
||||
if item_definition != None:
|
||||
value = component_values_shuffler(seed, child_property_group, item_definition, registry, parent=component_name)
|
||||
else:
|
||||
value = '""'
|
||||
|
||||
is_item_value_type = is_def_value_type(item_definition, registry)
|
||||
if is_item_value_type:
|
||||
setattr(property_group , field_name, value)
|
||||
|
||||
elif type_info == "Enum":
|
||||
available_variants = definition["oneOf"] if type_def != "object" else list(map(lambda x: x["title"], definition["oneOf"]))
|
||||
selected = random.choice(available_variants)
|
||||
|
||||
# set selected variant
|
||||
setattr(property_group , component_name, selected)
|
||||
|
||||
if type_def == "object":
|
||||
selection_index = property_group.field_names.index("variant_"+selected)
|
||||
variant_name = property_group.field_names[selection_index]
|
||||
variant_definition = definition["oneOf"][selection_index-1]
|
||||
if "prefixItems" in variant_definition:
|
||||
value = getattr(property_group, variant_name)
|
||||
is_property_group = isinstance(value, PropertyGroup)
|
||||
child_property_group = value if is_property_group else None
|
||||
|
||||
value = component_values_shuffler(seed, child_property_group, variant_definition, registry, parent=component_name)
|
||||
value = selected + str(value,)
|
||||
elif "properties" in variant_definition:
|
||||
value = getattr(property_group, variant_name)
|
||||
is_property_group = isinstance(value, PropertyGroup)
|
||||
child_property_group = value if is_property_group else None
|
||||
|
||||
value = component_values_shuffler(seed, child_property_group, variant_definition, registry, parent=component_name)
|
||||
value = selected + str(value,)
|
||||
else:
|
||||
value = selected # here the value of the enum is just the name of the variant
|
||||
else:
|
||||
value = selected
|
||||
|
||||
|
||||
|
||||
elif type_info == "List":
|
||||
item_list = getattr(property_group, "list")
|
||||
item_list.clear()
|
||||
|
||||
item_type_name = getattr(property_group, "type_name_short")
|
||||
number_of_list_items_to_add = random.randint(1, 2)
|
||||
|
||||
for i in range(0, number_of_list_items_to_add):
|
||||
new_entry = item_list.add()
|
||||
item_type_name = getattr(new_entry, "type_name") # we get the REAL type name
|
||||
definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None
|
||||
|
||||
if definition != None:
|
||||
component_values_shuffler(seed, new_entry, definition, registry, parent=component_name)
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
print("something else")
|
||||
fieldValue = type_mappings[type_name]()
|
||||
return fieldValue
|
||||
|
||||
#return value
|
||||
|
||||
|
||||
|
427
tools/bevy_components/tests/expected_component_values.py
Normal file
427
tools/bevy_components/tests/expected_component_values.py
Normal file
@ -0,0 +1,427 @@
|
||||
|
||||
|
||||
expected_custom_property_values = {'AComponentWithAnExtremlyExageratedOrMaybeNotButCouldBeNameOrWut': '',
|
||||
'Aabb': '(center: Vec3A(x:0.0, y:0.0, z:0.0), half_extents: Vec3A(x:0.0, y:0.0, z:0.0))',
|
||||
'AdditionalMassProperties': 'Mass(0.0)',
|
||||
'AmbientLightSettings': '(brightness: 0.0, color: Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0))',
|
||||
'AnimationPlayer': '(animation: "", paused: true)',
|
||||
'Animations': '(named_animations: "")',
|
||||
'AutoAABBCollider': 'Cuboid',
|
||||
'BackgroundColor': '(Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0))',
|
||||
'BasicTest': '(a: 0.0, b: 0, c: " ")',
|
||||
'BloomSettings': '(composite_mode: EnergyConserving, high_pass_frequency: 0.0, intensity: 0.0, low_frequency_boost: '
|
||||
'0.0, low_frequency_boost_curvature: 0.0, prefilter_settings: (threshold: 0.0, threshold_softness: '
|
||||
'0.0))',
|
||||
'BlueprintName': '(" ")',
|
||||
'BorderColor': '(Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0))',
|
||||
'Button': '',
|
||||
'CalculatedClip': '(clip: (max: Vec2(x:0.0, y:0.0), min: Vec2(x:0.0, y:0.0)))',
|
||||
'Camera': '(hdr: true, is_active: true, msaa_writeback: true, order: 0, viewport: None)',
|
||||
'Camera2d': '(clear_color: Default)',
|
||||
'Camera3d': '(clear_color: Default, depth_load_op: Clear(0.0), depth_texture_usages: "", '
|
||||
'screen_space_specular_transmission_quality: "", screen_space_specular_transmission_steps: 0)',
|
||||
'CameraRenderGraph': '( )',
|
||||
'CameraTrackable': '',
|
||||
'CameraTracking': '(offset: Vec3(x:0.0, y:0.0, z:0.0))',
|
||||
'CameraTrackingOffset': '(Vec3(x:0.0, y:0.0, z:0.0))',
|
||||
'CascadeShadowConfig': '(bounds: [], minimum_distance: 0.0, overlap_proportion: 0.0)',
|
||||
'Cascades': '(cascades: "")',
|
||||
'CascadesFrusta': '',
|
||||
'CascadesVisibleEntities': '',
|
||||
'Ccd': '(enabled: true)',
|
||||
'Children': '([])',
|
||||
'ClusterConfig': 'None',
|
||||
'Collider': 'Ball(0.0)',
|
||||
'CollidingEntities': '("")',
|
||||
'CollisionGroups': '(filters: (0), memberships: (0))',
|
||||
'ColorGrading': '(exposure: 0.0, gamma: 0.0, post_saturation: 0.0, pre_saturation: 0.0)',
|
||||
'ContactForceEventThreshold': '(0.0)',
|
||||
'ContentSize': '',
|
||||
'ContrastAdaptiveSharpeningSettings': '(denoise: true, enabled: true, sharpening_strength: 0.0)',
|
||||
'CubemapFrusta': '',
|
||||
'CubemapVisibleEntities': '',
|
||||
'Damping': '(angular_damping: 0.0, linear_damping: 0.0)',
|
||||
'DebandDither': 'Disabled',
|
||||
'DirectionalLight': '(color: Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0), illuminance: 0.0, shadow_depth_bias: 0.0, '
|
||||
'shadow_normal_bias: 0.0, shadows_enabled: true)',
|
||||
'Dominance': '(groups: 0)',
|
||||
'EnumComplex': 'Float(0.0)',
|
||||
'EnumTest': 'Metal',
|
||||
'ExternalForce': '(force: Vec3(x:0.0, y:0.0, z:0.0), torque: Vec3(x:0.0, y:0.0, z:0.0))',
|
||||
'ExternalImpulse': '(impulse: Vec3(x:0.0, y:0.0, z:0.0), torque_impulse: Vec3(x:0.0, y:0.0, z:0.0))',
|
||||
'FocusPolicy': 'Block',
|
||||
'FogSettings': '(color: Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0), directional_light_color: Rgba(red:1.0, '
|
||||
'green:1.0, blue:0.0, alpha:1.0), directional_light_exponent: 0.0, falloff: "")',
|
||||
'Friction': '(coefficient: 0.0, combine_rule: "")',
|
||||
'Frustum': '',
|
||||
'Fxaa': '(edge_threshold: "", edge_threshold_min: "", enabled: true)',
|
||||
'GlobalTransform': '((matrix3: (x_axis: Vec3A(x:0.0, y:0.0, z:0.0), y_axis: Vec3A(x:0.0, y:0.0, z:0.0), z_axis: '
|
||||
'Vec3A(x:0.0, y:0.0, z:0.0)), translation: Vec3A(x:0.0, y:0.0, z:0.0)))',
|
||||
'GltfExtras': '(value: " ")',
|
||||
'GravityScale': '(0.0)',
|
||||
'Group': '(0)',
|
||||
'Handle<()>': 'Strong("")',
|
||||
'Handle<AnimationClip>': 'Strong("")',
|
||||
'Handle<AudioSource>': 'Strong("")',
|
||||
'Handle<ColorMaterial>': 'Strong("")',
|
||||
'Handle<DynamicScene>': 'Strong("")',
|
||||
'Handle<ExtendedMaterial<StandardMaterial, MyExtension>>': 'Strong("")',
|
||||
'Handle<Font>': 'Strong("")',
|
||||
'Handle<Gltf>': 'Strong("")',
|
||||
'Handle<GltfMesh>': 'Strong("")',
|
||||
'Handle<GltfNode>': 'Strong("")',
|
||||
'Handle<GltfPrimitive>': 'Strong("")',
|
||||
'Handle<Image>': 'Strong("")',
|
||||
'Handle<LineGizmo>': 'Strong("")',
|
||||
'Handle<LoadedFolder>': 'Strong("")',
|
||||
'Handle<LoadedUntypedAsset>': 'Strong("")',
|
||||
'Handle<Mesh>': 'Strong("")',
|
||||
'Handle<Pitch>': 'Strong("")',
|
||||
'Handle<Scene>': 'Strong("")',
|
||||
'Handle<Shader>': 'Strong("")',
|
||||
'Handle<SkinnedMeshInverseBindposes>': 'Strong("")',
|
||||
'Handle<StandardDynamicAssetCollection>': 'Strong("")',
|
||||
'Handle<StandardMaterial>': 'Strong("")',
|
||||
'Handle<TextureAtlas>': 'Strong("")',
|
||||
'Handle<WireframeMaterial>': 'Strong("")',
|
||||
'InheritedVisibility': '(true)',
|
||||
'Interaction': 'Pressed',
|
||||
'Label': '',
|
||||
'LockedAxes': '(0)',
|
||||
'MaterialInfo': '(name: " ", source: " ")',
|
||||
'Mesh2dHandle': '(Strong(""))',
|
||||
'MeshMorphWeights': '(weights: [])',
|
||||
'MorphWeights': '(first_mesh: "", weights: [])',
|
||||
'Name': '(hash: 0, name: )',
|
||||
'NestedTupleStuff': '(0.0, 0, (basic: (a: 0.0, b: 0, c: " "), color: (Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0)), '
|
||||
'colors_list: ([]), enable: true, enum_inner: Metal, nested: (vec: (Vec3(x:0.0, y:0.0, z:0.0))), '
|
||||
'text: " ", toggle: (true)))',
|
||||
'NestingTestLevel2': '(basic: (a: 0.0, b: 0, c: " "), color: (Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0)), '
|
||||
'colors_list: ([]), enable: true, enum_inner: Metal, nested: (vec: (Vec3(x:0.0, y:0.0, z:0.0))), '
|
||||
'text: " ", toggle: (true))',
|
||||
'NestingTestLevel3': '(vec: (Vec3(x:0.0, y:0.0, z:0.0)))',
|
||||
'NoFrustumCulling': '',
|
||||
'NoWireframe': '',
|
||||
'Node': '(calculated_size: Vec2(x:0.0, y:0.0), outline_offset: 0.0, outline_width: 0.0, stack_index: 0, '
|
||||
'unrounded_size: Vec2(x:0.0, y:0.0))',
|
||||
'NotShadowCaster': '',
|
||||
'NotShadowReceiver': '',
|
||||
'OrthographicProjection': '(area: (max: Vec2(x:0.0, y:0.0), min: Vec2(x:0.0, y:0.0)), far: 0.0, near: 0.0, scale: '
|
||||
'0.0, scaling_mode: Fixed(height: 0.0, width: 0.0), viewport_origin: Vec2(x:0.0, y:0.0))',
|
||||
'Outline': '(color: Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0), offset: Auto, width: Auto)',
|
||||
'Parent': '(0)',
|
||||
'PerspectiveProjection': '(aspect_ratio: 0.0, far: 0.0, fov: 0.0, near: 0.0)',
|
||||
'Pickable': '',
|
||||
'Player': '',
|
||||
'PointLight': '(color: Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0), intensity: 0.0, radius: 0.0, range: 0.0, '
|
||||
'shadow_depth_bias: 0.0, shadow_normal_bias: 0.0, shadows_enabled: true)',
|
||||
'PrimaryWindow': '',
|
||||
'Projection': 'Perspective((aspect_ratio: 0.0, far: 0.0, fov: 0.0, near: 0.0))',
|
||||
'RelativeCursorPosition': '(normalized: "", normalized_visible_node_rect: (max: Vec2(x:0.0, y:0.0), min: Vec2(x:0.0, '
|
||||
'y:0.0)))',
|
||||
'RenderLayers': '(0)',
|
||||
'Restitution': '(coefficient: 0.0, combine_rule: "")',
|
||||
'RigidBody': 'Dynamic',
|
||||
'SSAOSettings': '',
|
||||
'ScreenSpaceAmbientOcclusionSettings': '(quality_level: "")',
|
||||
'Sensor': '',
|
||||
'ShadowFilteringMethod': 'Hardware2x2',
|
||||
'ShadowmapSettings': '(size: 0)',
|
||||
'SkinnedMesh': '(inverse_bindposes: Strong(""), joints: [])',
|
||||
'Sleeping': '(angular_threshold: 0.0, linear_threshold: 0.0, sleeping: true)',
|
||||
'SolverGroups': '(filters: (0), memberships: (0))',
|
||||
'SpawnHere': '',
|
||||
'SpotLight': '(color: Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0), inner_angle: 0.0, intensity: 0.0, outer_angle: '
|
||||
'0.0, radius: 0.0, range: 0.0, shadow_depth_bias: 0.0, shadow_normal_bias: 0.0, shadows_enabled: true)',
|
||||
'Sprite': '(anchor: Center, color: Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0), custom_size: "", flip_x: true, '
|
||||
'flip_y: true, rect: "")',
|
||||
'Style': '(align_content: Default, align_items: Default, align_self: Auto, aspect_ratio: None, border: (bottom: Auto, '
|
||||
'left: Auto, right: Auto, top: Auto), bottom: Auto, column_gap: Auto, direction: Inherit, display: Flex, '
|
||||
'flex_basis: Auto, flex_direction: Row, flex_grow: 0.0, flex_shrink: 0.0, flex_wrap: NoWrap, '
|
||||
'grid_auto_columns: "", grid_auto_flow: Row, grid_auto_rows: "", grid_column: (end: "", span: "", start: '
|
||||
'""), grid_row: (end: "", span: "", start: ""), grid_template_columns: "", grid_template_rows: "", height: '
|
||||
'Auto, justify_content: Default, justify_items: Default, justify_self: Auto, left: Auto, margin: (bottom: '
|
||||
'Auto, left: Auto, right: Auto, top: Auto), max_height: Auto, max_width: Auto, min_height: Auto, min_width: '
|
||||
'Auto, overflow: (x: Visible, y: Visible), padding: (bottom: Auto, left: Auto, right: Auto, top: Auto), '
|
||||
'position_type: Relative, right: Auto, row_gap: Auto, top: Auto, width: Auto)',
|
||||
'Text': '(alignment: Left, linebreak_behavior: WordBoundary, sections: [])',
|
||||
'Text2dBounds': '(size: Vec2(x:0.0, y:0.0))',
|
||||
'TextFlags': '(needs_new_measure_func: true, needs_recompute: true)',
|
||||
'TextLayoutInfo': '(glyphs: "", logical_size: Vec2(x:0.0, y:0.0))',
|
||||
'TextureAtlasSprite': '(anchor: Center, color: Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0), custom_size: "", '
|
||||
'flip_x: true, flip_y: true, index: 0)',
|
||||
'Tonemapping': 'None',
|
||||
'Transform': '(rotation: Quat(x:0.0, y:0.0, z:0.0, w:0.0), scale: Vec3(x:0.0, y:0.0, z:0.0), translation: Vec3(x:0.0, '
|
||||
'y:0.0, z:0.0))',
|
||||
'TupleTest2': '(0.0, 0, " ")',
|
||||
'TupleTestBool': '(true)',
|
||||
'TupleTestColor': '(Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0))',
|
||||
'TupleTestF32': '(0.0)',
|
||||
'TupleTestStr': '(" ")',
|
||||
'TupleTestU64': '(0)',
|
||||
'TupleVec': '([])',
|
||||
'TupleVec2': '(Vec2(x:0.0, y:0.0))',
|
||||
'TupleVec3': '(Vec3(x:0.0, y:0.0, z:0.0))',
|
||||
'TupleVecF32F32': '([])',
|
||||
'UiCameraConfig': '(show_ui: true)',
|
||||
'UiImage': '(flip_x: true, flip_y: true, texture: Strong(""))',
|
||||
'UiImageSize': '(size: Vec2(x:0.0, y:0.0))',
|
||||
'UiTextureAtlasImage': '(flip_x: true, flip_y: true, index: 0)',
|
||||
'UnitTest': '',
|
||||
'VecOfColors': '([])',
|
||||
'VecOfF32s': '([])',
|
||||
'VecOfVec3s2': '([])',
|
||||
'Velocity': '(angvel: Vec3(x:0.0, y:0.0, z:0.0), linvel: Vec3(x:0.0, y:0.0, z:0.0))',
|
||||
'ViewVisibility': '(true)',
|
||||
'Visibility': 'Inherited',
|
||||
'VisibleEntities': '',
|
||||
'Window': '(canvas: None, composite_alpha_mode: Auto, cursor: (grab_mode: None, hit_test: true, icon: Default, '
|
||||
'visible: true), decorations: true, enabled_buttons: (close: true, maximize: true, minimize: true), '
|
||||
'fit_canvas_to_parent: true, focused: true, ime_enabled: true, ime_position: Vec2(x:0.0, y:0.0), internal: '
|
||||
'(maximize_request: None, minimize_request: None, physical_cursor_position: None), mode: Windowed, '
|
||||
'position: Automatic, present_mode: AutoVsync, prevent_default_event_handling: true, resizable: true, '
|
||||
'resize_constraints: (max_height: 0.0, max_width: 0.0, min_height: 0.0, min_width: 0.0), resolution: '
|
||||
'(physical_height: 0, physical_width: 0, scale_factor: 0.0, scale_factor_override: None), title: " ", '
|
||||
'transparent: true, visible: true, window_level: AlwaysOnBottom, window_theme: "")',
|
||||
'Wireframe': '',
|
||||
'ZIndex': 'Local(0)'}
|
||||
|
||||
|
||||
|
||||
expected_custom_property_values_randomized = {'AComponentWithAnExtremlyExageratedOrMaybeNotButCouldBeNameOrWut': '',
|
||||
'Aabb': '(center: Vec3A(x:0.5714026093482971, y:0.42888906598091125, z:0.5780913233757019), half_extents: '
|
||||
'Vec3A(x:0.20609822869300842, y:0.8133212327957153, z:0.8235888481140137))',
|
||||
'AdditionalMassProperties': 'Mass(0.42888906598091125)',
|
||||
'AmbientLightSettings': '(brightness: 0.5714026093482971, color: Rgba(red:0.42888906598091125, '
|
||||
'green:0.5780913233757019, blue:0.20609822869300842, alpha:0.8133212327957153))',
|
||||
'AnimationPlayer': '(animation: "", paused: true)',
|
||||
'Animations': '(named_animations: "")',
|
||||
'AutoAABBCollider': 'Capsule',
|
||||
'BackgroundColor': '(Rgba(red:0.5714026093482971, green:0.42888906598091125, blue:0.5780913233757019, '
|
||||
'alpha:0.20609822869300842))',
|
||||
'BasicTest': '(a: 0.5714026093482971, b: 54, c: "psagopiu")',
|
||||
'BloomSettings': '(composite_mode: EnergyConserving, high_pass_frequency: 0.42888906598091125, intensity: '
|
||||
'0.5780913233757019, low_frequency_boost: 0.20609822869300842, low_frequency_boost_curvature: '
|
||||
'0.8133212327957153, prefilter_settings: (threshold: 0.8235888481140137, threshold_softness: '
|
||||
'0.6534725427627563))',
|
||||
'BlueprintName': '("sbnpsago")',
|
||||
'BorderColor': '(Rgba(red:0.5714026093482971, green:0.42888906598091125, blue:0.5780913233757019, '
|
||||
'alpha:0.20609822869300842))',
|
||||
'Button': '',
|
||||
'CalculatedClip': '(clip: (max: Vec2(x:0.5714026093482971, y:0.42888906598091125), min: Vec2(x:0.5780913233757019, '
|
||||
'y:0.20609822869300842)))',
|
||||
'Camera': '(hdr: true, is_active: false, msaa_writeback: false, order: 61, viewport: None)',
|
||||
'Camera2d': '(clear_color: None)',
|
||||
'Camera3d': '(clear_color: None, depth_load_op: Clear(0.42888906598091125), depth_texture_usages: "", '
|
||||
'screen_space_specular_transmission_quality: "", screen_space_specular_transmission_steps: 73)',
|
||||
'CameraRenderGraph': '(sbnpsago)',
|
||||
'CameraTrackable': '',
|
||||
'CameraTracking': '(offset: Vec3(x:0.5714026093482971, y:0.42888906598091125, z:0.5780913233757019))',
|
||||
'CameraTrackingOffset': '(Vec3(x:0.5714026093482971, y:0.42888906598091125, z:0.5780913233757019))',
|
||||
'CascadeShadowConfig': '(bounds: [0.42888906598091125], minimum_distance: 0.5780913233757019, overlap_proportion: '
|
||||
'0.20609822869300842)',
|
||||
'Cascades': '(cascades: "")',
|
||||
'CascadesFrusta': '',
|
||||
'CascadesVisibleEntities': '',
|
||||
'Ccd': '(enabled: true)',
|
||||
'Children': '([0])',
|
||||
'ClusterConfig': 'None',
|
||||
'Collider': 'Ball(0.42888906598091125)',
|
||||
'CollidingEntities': '("")',
|
||||
'CollisionGroups': '(filters: (73), memberships: (4))',
|
||||
'ColorGrading': '(exposure: 0.5714026093482971, gamma: 0.42888906598091125, post_saturation: 0.5780913233757019, '
|
||||
'pre_saturation: 0.20609822869300842)',
|
||||
'ContactForceEventThreshold': '(0.5714026093482971)',
|
||||
'ContentSize': '',
|
||||
'ContrastAdaptiveSharpeningSettings': '(denoise: true, enabled: false, sharpening_strength: 0.42888906598091125)',
|
||||
'CubemapFrusta': '',
|
||||
'CubemapVisibleEntities': '',
|
||||
'Damping': '(angular_damping: 0.5714026093482971, linear_damping: 0.42888906598091125)',
|
||||
'DebandDither': 'Disabled',
|
||||
'DirectionalLight': '(color: Rgba(red:0.5714026093482971, green:0.42888906598091125, blue:0.5780913233757019, '
|
||||
'alpha:0.20609822869300842), illuminance: 0.8133212327957153, shadow_depth_bias: '
|
||||
'0.8235888481140137, shadow_normal_bias: 0.6534725427627563, shadows_enabled: false)',
|
||||
'Dominance': '(groups: 73)',
|
||||
'EnumComplex': 'StructLike(a: 0.03258506581187248, b: 61, c: "sagopiuz")',
|
||||
'EnumTest': 'Squishy',
|
||||
'ExternalForce': '(force: Vec3(x:0.5714026093482971, y:0.42888906598091125, z:0.5780913233757019), torque: '
|
||||
'Vec3(x:0.20609822869300842, y:0.8133212327957153, z:0.8235888481140137))',
|
||||
'ExternalImpulse': '(impulse: Vec3(x:0.5714026093482971, y:0.42888906598091125, z:0.5780913233757019), '
|
||||
'torque_impulse: Vec3(x:0.20609822869300842, y:0.8133212327957153, z:0.8235888481140137))',
|
||||
'FocusPolicy': 'Block',
|
||||
'FogSettings': '(color: Rgba(red:0.5714026093482971, green:0.42888906598091125, blue:0.5780913233757019, '
|
||||
'alpha:0.20609822869300842), directional_light_color: Rgba(red:0.8133212327957153, '
|
||||
'green:0.8235888481140137, blue:0.6534725427627563, alpha:0.16022956371307373), '
|
||||
'directional_light_exponent: 0.5206693410873413, falloff: "")',
|
||||
'Friction': '(coefficient: 0.5714026093482971, combine_rule: "")',
|
||||
'Frustum': '',
|
||||
'Fxaa': '(edge_threshold: "", edge_threshold_min: "", enabled: true)',
|
||||
'GlobalTransform': '((matrix3: (x_axis: Vec3A(x:0.5714026093482971, y:0.42888906598091125, z:0.5780913233757019), '
|
||||
'y_axis: Vec3A(x:0.20609822869300842, y:0.8133212327957153, z:0.8235888481140137), z_axis: '
|
||||
'Vec3A(x:0.6534725427627563, y:0.16022956371307373, z:0.5206693410873413)), translation: '
|
||||
'Vec3A(x:0.3277728259563446, y:0.24999667704105377, z:0.952816903591156)))',
|
||||
'GltfExtras': '(value: "sbnpsago")',
|
||||
'GravityScale': '(0.5714026093482971)',
|
||||
'Group': '(73)',
|
||||
'Handle<()>': 'Strong("")',
|
||||
'Handle<AnimationClip>': 'Strong("")',
|
||||
'Handle<AudioSource>': 'Strong("")',
|
||||
'Handle<ColorMaterial>': 'Strong("")',
|
||||
'Handle<DynamicScene>': 'Strong("")',
|
||||
'Handle<ExtendedMaterial<StandardMaterial, MyExtension>>': 'Strong("")',
|
||||
'Handle<Font>': 'Strong("")',
|
||||
'Handle<Gltf>': 'Strong("")',
|
||||
'Handle<GltfMesh>': 'Strong("")',
|
||||
'Handle<GltfNode>': 'Strong("")',
|
||||
'Handle<GltfPrimitive>': 'Strong("")',
|
||||
'Handle<Image>': 'Strong("")',
|
||||
'Handle<LineGizmo>': 'Strong("")',
|
||||
'Handle<LoadedFolder>': 'Strong("")',
|
||||
'Handle<LoadedUntypedAsset>': 'Strong("")',
|
||||
'Handle<Mesh>': 'Strong("")',
|
||||
'Handle<Pitch>': 'Strong("")',
|
||||
'Handle<Scene>': 'Strong("")',
|
||||
'Handle<Shader>': 'Strong("")',
|
||||
'Handle<SkinnedMeshInverseBindposes>': 'Strong("")',
|
||||
'Handle<StandardDynamicAssetCollection>': 'Strong("")',
|
||||
'Handle<StandardMaterial>': 'Strong("")',
|
||||
'Handle<TextureAtlas>': 'Strong("")',
|
||||
'Handle<WireframeMaterial>': 'Strong("")',
|
||||
'InheritedVisibility': '(true)',
|
||||
'Interaction': 'None',
|
||||
'Label': '',
|
||||
'LockedAxes': '(73)',
|
||||
'MaterialInfo': '(name: "sbnpsago", source: "piuzfbqp")',
|
||||
'Mesh2dHandle': '(Strong(""))',
|
||||
'MeshMorphWeights': '(weights: [0.42888906598091125])',
|
||||
'MorphWeights': '(first_mesh: "", weights: [0.42888906598091125])',
|
||||
'Name': '(hash: 73, name: bnpsagop)',
|
||||
'NestedTupleStuff': '(0.5714026093482971, 54, (basic: (a: 0.4825616776943207, b: 1, c: "gopiuzfb"), color: '
|
||||
'(Rgba(red:0.5206693410873413, green:0.3277728259563446, blue:0.24999667704105377, '
|
||||
'alpha:0.952816903591156)), colors_list: ([Rgba(red:0.0445563830435276, green:0.8601610660552979, '
|
||||
'blue:0.6031906008720398, alpha:0.38160598278045654), Rgba(red:0.2836182117462158, '
|
||||
'green:0.6749648451805115, blue:0.456831157207489, alpha:0.6858614683151245)]), enable: true, '
|
||||
'enum_inner: Rock, nested: (vec: (Vec3(x:0.1329781413078308, y:0.7678378224372864, '
|
||||
'z:0.9824132323265076))), text: "otmbsahe", toggle: (false)))',
|
||||
'NestingTestLevel2': '(basic: (a: 0.5714026093482971, b: 54, c: "psagopiu"), color: (Rgba(red:0.8106188178062439, '
|
||||
'green:0.03440357372164726, blue:0.49008557200431824, alpha:0.07608934491872787)), colors_list: '
|
||||
'([Rgba(red:0.0445563830435276, green:0.8601610660552979, blue:0.6031906008720398, '
|
||||
'alpha:0.38160598278045654), Rgba(red:0.2836182117462158, green:0.6749648451805115, '
|
||||
'blue:0.456831157207489, alpha:0.6858614683151245)]), enable: true, enum_inner: Rock, nested: '
|
||||
'(vec: (Vec3(x:0.1329781413078308, y:0.7678378224372864, z:0.9824132323265076))), text: '
|
||||
'"otmbsahe", toggle: (false))',
|
||||
'NestingTestLevel3': '(vec: (Vec3(x:0.5714026093482971, y:0.42888906598091125, z:0.5780913233757019)))',
|
||||
'NoFrustumCulling': '',
|
||||
'NoWireframe': '',
|
||||
'Node': '(calculated_size: Vec2(x:0.5714026093482971, y:0.42888906598091125), outline_offset: 0.5780913233757019, '
|
||||
'outline_width: 0.20609822869300842, stack_index: 62, unrounded_size: Vec2(x:0.8235888481140137, '
|
||||
'y:0.6534725427627563))',
|
||||
'NotShadowCaster': '',
|
||||
'NotShadowReceiver': '',
|
||||
'OrthographicProjection': '(area: (max: Vec2(x:0.5714026093482971, y:0.42888906598091125), min: '
|
||||
'Vec2(x:0.5780913233757019, y:0.20609822869300842)), far: 0.8133212327957153, near: '
|
||||
'0.8235888481140137, scale: 0.6534725427627563, scaling_mode: '
|
||||
'WindowSize(0.03440357372164726), viewport_origin: Vec2(x:0.49008557200431824, '
|
||||
'y:0.07608934491872787))',
|
||||
'Outline': '(color: Rgba(red:0.5714026093482971, green:0.42888906598091125, blue:0.5780913233757019, '
|
||||
'alpha:0.20609822869300842), offset: VMax(0.4912964105606079), width: Percent(0.6534725427627563))',
|
||||
'Parent': '(0)',
|
||||
'PerspectiveProjection': '(aspect_ratio: 0.5714026093482971, far: 0.42888906598091125, fov: 0.5780913233757019, near: '
|
||||
'0.20609822869300842)',
|
||||
'Pickable': '',
|
||||
'Player': '',
|
||||
'PointLight': '(color: Rgba(red:0.5714026093482971, green:0.42888906598091125, blue:0.5780913233757019, '
|
||||
'alpha:0.20609822869300842), intensity: 0.8133212327957153, radius: 0.8235888481140137, range: '
|
||||
'0.6534725427627563, shadow_depth_bias: 0.16022956371307373, shadow_normal_bias: 0.5206693410873413, '
|
||||
'shadows_enabled: false)',
|
||||
'PrimaryWindow': '',
|
||||
'Projection': 'Perspective((aspect_ratio: 0.42888906598091125, far: 0.5780913233757019, fov: 0.20609822869300842, '
|
||||
'near: 0.8133212327957153))',
|
||||
'RelativeCursorPosition': '(normalized: "", normalized_visible_node_rect: (max: Vec2(x:0.5714026093482971, '
|
||||
'y:0.42888906598091125), min: Vec2(x:0.5780913233757019, y:0.20609822869300842)))',
|
||||
'RenderLayers': '(73)',
|
||||
'Restitution': '(coefficient: 0.5714026093482971, combine_rule: "")',
|
||||
'RigidBody': 'Dynamic',
|
||||
'SSAOSettings': '',
|
||||
'ScreenSpaceAmbientOcclusionSettings': '(quality_level: "")',
|
||||
'Sensor': '',
|
||||
'ShadowFilteringMethod': 'Jimenez14',
|
||||
'ShadowmapSettings': '(size: 73)',
|
||||
'SkinnedMesh': '(inverse_bindposes: Strong(""), joints: [0, 0])',
|
||||
'Sleeping': '(angular_threshold: 0.5714026093482971, linear_threshold: 0.42888906598091125, sleeping: true)',
|
||||
'SolverGroups': '(filters: (73), memberships: (4))',
|
||||
'SpawnHere': '',
|
||||
'SpotLight': '(color: Rgba(red:0.5714026093482971, green:0.42888906598091125, blue:0.5780913233757019, '
|
||||
'alpha:0.20609822869300842), inner_angle: 0.8133212327957153, intensity: 0.8235888481140137, '
|
||||
'outer_angle: 0.6534725427627563, radius: 0.16022956371307373, range: 0.5206693410873413, '
|
||||
'shadow_depth_bias: 0.3277728259563446, shadow_normal_bias: 0.24999667704105377, shadows_enabled: true)',
|
||||
'Sprite': '(anchor: Custom(Vec2(x:0.03258506581187248, y:0.4825616776943207)), color: Rgba(red:0.014832446351647377, '
|
||||
'green:0.46258050203323364, blue:0.4912964105606079, alpha:0.27752065658569336), custom_size: "", flip_x: '
|
||||
'true, flip_y: false, rect: "")',
|
||||
'Style': '(align_content: SpaceAround, align_items: Default, align_self: Baseline, aspect_ratio: '
|
||||
'Some(0.5780913233757019), border: (bottom: Px(0.46258050203323364), left: Vw(0.8235888481140137), right: '
|
||||
'VMin(0.8106188178062439), top: Auto), bottom: Vh(0.49008557200431824), column_gap: Auto, direction: '
|
||||
'Inherit, display: None, flex_basis: Percent(0.0445563830435276), flex_direction: Column, flex_grow: '
|
||||
'0.6031906008720398, flex_shrink: 0.38160598278045654, flex_wrap: Wrap, grid_auto_columns: "", '
|
||||
'grid_auto_flow: RowDense, grid_auto_rows: "", grid_column: (end: "", span: "", start: ""), grid_row: (end: '
|
||||
'"", span: "", start: ""), grid_template_columns: "", grid_template_rows: "", height: '
|
||||
'Vw(0.17467059195041656), justify_content: FlexEnd, justify_items: Stretch, justify_self: End, left: '
|
||||
'Px(0.45692843198776245), margin: (bottom: VMax(0.9824132323265076), left: Vw(0.6133268475532532), right: '
|
||||
'Auto, top: Vh(0.004055144265294075)), max_height: Px(0.1949533075094223), max_width: '
|
||||
'Percent(0.5363451838493347), min_height: VMax(0.8981962203979492), min_width: Percent(0.666689932346344), '
|
||||
'overflow: (x: Clip, y: Clip), padding: (bottom: Vw(0.06499417871236801), left: Vh(0.32468828558921814), '
|
||||
'right: Vh(0.15641891956329346), top: Px(0.9697836637496948)), position_type: Relative, right: Auto, '
|
||||
'row_gap: Auto, top: Vw(0.3011642396450043), width: Vh(0.6578909158706665))',
|
||||
'Text': '(alignment: Right, linebreak_behavior: WordBoundary, sections: [(style: (color: Rgba(red:0.4825616776943207, '
|
||||
'green:0.014832446351647377, blue:0.46258050203323364, alpha:0.4912964105606079), font: Weak(Index(index: '
|
||||
'"")), font_size: 0.03440357372164726), value: "pkchxlbn"), (style: (color: Rgba(red:0.8601610660552979, '
|
||||
'green:0.6031906008720398, blue:0.38160598278045654, alpha:0.2836182117462158), font: Weak(Uuid(uuid: '
|
||||
'"73b3b118-7d01-4778-8bcc-4e79055f5d22")), font_size: 0.17467059195041656), value: "jvleoyho")])',
|
||||
'Text2dBounds': '(size: Vec2(x:0.5714026093482971, y:0.42888906598091125))',
|
||||
'TextFlags': '(needs_new_measure_func: true, needs_recompute: false)',
|
||||
'TextLayoutInfo': '(glyphs: "", logical_size: Vec2(x:0.5714026093482971, y:0.42888906598091125))',
|
||||
'TextureAtlasSprite': '(anchor: Custom(Vec2(x:0.03258506581187248, y:0.4825616776943207)), color: '
|
||||
'Rgba(red:0.014832446351647377, green:0.46258050203323364, blue:0.4912964105606079, '
|
||||
'alpha:0.27752065658569336), custom_size: "", flip_x: true, flip_y: false, index: 4)',
|
||||
'Tonemapping': 'None',
|
||||
'Transform': '(rotation: Quat(x:0.5714026093482971, y:0.42888906598091125, z:0.5780913233757019, '
|
||||
'w:0.20609822869300842), scale: Vec3(x:0.8133212327957153, y:0.8235888481140137, z:0.6534725427627563), '
|
||||
'translation: Vec3(x:0.16022956371307373, y:0.5206693410873413, z:0.3277728259563446))',
|
||||
'TupleTest2': '(0.5714026093482971, 54, "psagopiu")',
|
||||
'TupleTestBool': '(true)',
|
||||
'TupleTestColor': '(Rgba(red:0.5714026093482971, green:0.42888906598091125, blue:0.5780913233757019, '
|
||||
'alpha:0.20609822869300842))',
|
||||
'TupleTestF32': '(0.5714026093482971)',
|
||||
'TupleTestStr': '("sbnpsago")',
|
||||
'TupleTestU64': '(73)',
|
||||
'TupleVec': '(["npsagopi"])',
|
||||
'TupleVec2': '(Vec2(x:0.5714026093482971, y:0.42888906598091125))',
|
||||
'TupleVec3': '(Vec3(x:0.5714026093482971, y:0.42888906598091125, z:0.5780913233757019))',
|
||||
'TupleVecF32F32': '([(0.42888906598091125, 0.5780913233757019)])',
|
||||
'UiCameraConfig': '(show_ui: true)',
|
||||
'UiImage': '(flip_x: true, flip_y: false, texture: Weak(Uuid(uuid: "73b3b118-7d01-4778-8bcc-4e79055f5d22")))',
|
||||
'UiImageSize': '(size: Vec2(x:0.5714026093482971, y:0.42888906598091125))',
|
||||
'UiTextureAtlasImage': '(flip_x: true, flip_y: false, index: 54)',
|
||||
'UnitTest': '',
|
||||
'VecOfColors': '([Rgba(red:0.42888906598091125, green:0.5780913233757019, blue:0.20609822869300842, '
|
||||
'alpha:0.8133212327957153)])',
|
||||
'VecOfF32s': '([0.42888906598091125])',
|
||||
'VecOfVec3s2': '([(Vec3(x:0.42888906598091125, y:0.5780913233757019, z:0.20609822869300842))])',
|
||||
'Velocity': '(angvel: Vec3(x:0.5714026093482971, y:0.42888906598091125, z:0.5780913233757019), linvel: '
|
||||
'Vec3(x:0.20609822869300842, y:0.8133212327957153, z:0.8235888481140137))',
|
||||
'ViewVisibility': '(true)',
|
||||
'Visibility': 'Visible',
|
||||
'VisibleEntities': '',
|
||||
'Window': '(canvas: None, composite_alpha_mode: PostMultiplied, cursor: (grab_mode: Confined, hit_test: true, icon: '
|
||||
'Default, visible: false), decorations: false, enabled_buttons: (close: true, maximize: false, minimize: '
|
||||
'true), fit_canvas_to_parent: false, focused: true, ime_enabled: true, ime_position: '
|
||||
'Vec2(x:0.16022956371307373, y:0.5206693410873413), internal: (maximize_request: Some(false), '
|
||||
'minimize_request: None, physical_cursor_position: Some(DVec2(x:0.0445563830435276, '
|
||||
'y:0.8601610660552979))), mode: SizedFullscreen, position: Centered(Primary), present_mode: Fifo, '
|
||||
'prevent_default_event_handling: true, resizable: true, resize_constraints: (max_height: '
|
||||
'0.2623211145401001, max_width: 0.17467059195041656, min_height: 0.30310511589050293, min_width: '
|
||||
'0.36258742213249207), resolution: (physical_height: 58, physical_width: 98, scale_factor: '
|
||||
'0.8600491285324097, scale_factor_override: None), title: "otmbsahe", transparent: false, visible: true, '
|
||||
'window_level: Normal, window_theme: "")',
|
||||
'Wireframe': '',
|
||||
'ZIndex': 'Local(54)'}
|
297
tools/bevy_components/tests/test_components.py
Normal file
297
tools/bevy_components/tests/test_components.py
Normal file
@ -0,0 +1,297 @@
|
||||
import bpy
|
||||
import pytest
|
||||
import pprint
|
||||
|
||||
from ..propGroups.conversions_to_prop_group import property_group_value_from_custom_property_value
|
||||
from ..propGroups.conversions_from_prop_group import property_group_value_to_custom_property_value
|
||||
from .component_values_shuffler import component_values_shuffler
|
||||
from .expected_component_values import (expected_custom_property_values, expected_custom_property_values_randomized)
|
||||
|
||||
@pytest.fixture
|
||||
def setup_data(request):
|
||||
print("\nSetting up resources...")
|
||||
|
||||
def finalizer():
|
||||
print("\nPerforming teardown...")
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
#registry.schemaPath = "../../testing/bevy_registry_export/basic/assets/registry.json"
|
||||
#bpy.ops.object.reload_registry()
|
||||
|
||||
type_infos = registry.type_infos
|
||||
object = bpy.context.object
|
||||
remove_component_operator = bpy.ops.object.remove_bevy_component
|
||||
|
||||
for type_name in type_infos:
|
||||
definition = type_infos[type_name]
|
||||
component_name = definition["short_name"]
|
||||
if component_name in object:
|
||||
try:
|
||||
remove_component_operator(component_name=component_name)
|
||||
except Exception as error:
|
||||
pass
|
||||
|
||||
request.addfinalizer(finalizer)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def test_components_should_generate_correct_custom_properties(setup_data):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
registry.schemaPath = "../../testing/bevy_registry_export/basic/assets/registry.json"
|
||||
bpy.ops.object.reload_registry()
|
||||
|
||||
type_infos = registry.type_infos
|
||||
object = bpy.context.object
|
||||
|
||||
add_component_operator = bpy.ops.object.add_bevy_component
|
||||
errors = []
|
||||
addable_components = []
|
||||
added_components = []
|
||||
|
||||
custom_property_values = {}
|
||||
|
||||
for type_name in type_infos:
|
||||
definition = type_infos[type_name]
|
||||
component_type = definition["title"]
|
||||
short_name = definition["short_name"]
|
||||
is_component = definition['isComponent'] if "isComponent" in definition else False
|
||||
if not is_component:
|
||||
continue
|
||||
|
||||
addable_components.append(component_type)
|
||||
|
||||
try:
|
||||
add_component_operator(component_type=component_type)
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
added_components.append(component_type)
|
||||
custom_property_values[short_name] = object[short_name]
|
||||
assert object[short_name] == expected_custom_property_values[short_name]
|
||||
|
||||
except Exception as error:
|
||||
errors.append(error)
|
||||
|
||||
"""pp = pprint.PrettyPrinter(depth=14, width=120)
|
||||
print("CUSTOM PROPERTY VALUES")
|
||||
pp.pprint(custom_property_values)"""
|
||||
|
||||
assert len(errors) == 0
|
||||
assert len(added_components) == 152
|
||||
|
||||
|
||||
def test_components_should_generate_correct_custom_properties_with_randomozied_values(setup_data):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
registry.schemaPath = "../../testing/bevy_registry_export/basic/assets/registry.json"
|
||||
bpy.ops.object.reload_registry()
|
||||
|
||||
type_infos = registry.type_infos
|
||||
object = bpy.context.object
|
||||
|
||||
add_component_operator = bpy.ops.object.add_bevy_component
|
||||
errors = []
|
||||
error_components = []
|
||||
addable_components = []
|
||||
added_components = []
|
||||
|
||||
custom_property_values = {}
|
||||
|
||||
for type_name in type_infos:
|
||||
definition = type_infos[type_name]
|
||||
component_type = definition["title"]
|
||||
short_name = definition["short_name"]
|
||||
is_component = definition['isComponent'] if "isComponent" in definition else False
|
||||
if not is_component:
|
||||
continue
|
||||
|
||||
addable_components.append(component_type)
|
||||
|
||||
try:
|
||||
add_component_operator(component_type=component_type)
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
component_values_shuffler(seed= 10, property_group=propertyGroup, definition=definition, registry=registry)
|
||||
|
||||
added_components.append(component_type)
|
||||
custom_property_values[short_name] = object[short_name]
|
||||
assert object[short_name] == expected_custom_property_values_randomized[short_name]
|
||||
|
||||
except Exception as error:
|
||||
errors.append(error)
|
||||
error_components.append(short_name)
|
||||
|
||||
pp = pprint.PrettyPrinter(depth=14, width=120)
|
||||
print("CUSTOM PROPERTY VALUES")
|
||||
pp.pprint(custom_property_values)
|
||||
|
||||
print("error_components", error_components)
|
||||
assert len(errors) == 0
|
||||
assert len(added_components) == 152
|
||||
|
||||
def test_components_should_generate_correct_propertyGroup_values_from_custom_properties(setup_data):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
registry.schemaPath = "../../testing/bevy_registry_export/basic/assets/registry.json"
|
||||
bpy.ops.object.reload_registry()
|
||||
|
||||
type_infos = registry.type_infos
|
||||
object = bpy.context.object
|
||||
|
||||
add_component_operator = bpy.ops.object.add_bevy_component
|
||||
errors = []
|
||||
addable_components = []
|
||||
added_components = []
|
||||
failing_components = []
|
||||
|
||||
for type_name in type_infos:
|
||||
definition = type_infos[type_name]
|
||||
component_type = definition["title"]
|
||||
short_name = definition["short_name"]
|
||||
is_component = definition['isComponent'] if "isComponent" in definition else False
|
||||
if not is_component:
|
||||
continue
|
||||
|
||||
addable_components.append(component_type)
|
||||
|
||||
try:
|
||||
add_component_operator(component_type=component_type)
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
added_components.append(component_type)
|
||||
# randomise values
|
||||
component_values_shuffler(seed= 10, property_group=propertyGroup, definition=definition, registry=registry)
|
||||
custom_property_value = object[short_name]
|
||||
|
||||
# first check if custom property value matches what we expect
|
||||
assert custom_property_value == expected_custom_property_values_randomized[short_name]
|
||||
|
||||
# we update propgroup values from custom property values
|
||||
property_group_value_from_custom_property_value(propertyGroup, definition, registry, custom_property_value, nesting = [])
|
||||
# and then generate it back
|
||||
custom_property_value_regen = property_group_value_to_custom_property_value(propertyGroup, definition, registry, None)
|
||||
assert custom_property_value_regen == expected_custom_property_values_randomized[short_name]
|
||||
|
||||
# custom_property_values[short_name] = object[short_name]
|
||||
#assert object[short_name] == expected_custom_property_values[short_name]
|
||||
#print("CUSTOM PROPERTY ", object[short_name])
|
||||
|
||||
except Exception as error:
|
||||
errors.append(error)
|
||||
failing_components.append(short_name)
|
||||
|
||||
for index, error in enumerate(errors):
|
||||
print("ERROR", error, failing_components[index])
|
||||
assert len(errors) == 0
|
||||
assert len(added_components) == 152
|
||||
|
||||
|
||||
def test_remove_components(setup_data):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
registry.schemaPath = "../../testing/bevy_registry_export/basic/assets/registry.json"
|
||||
bpy.ops.object.reload_registry()
|
||||
|
||||
type_infos = registry.type_infos
|
||||
|
||||
add_component_operator = bpy.ops.object.add_bevy_component
|
||||
errors = []
|
||||
addable_components = []
|
||||
added_components = []
|
||||
|
||||
for type_name in type_infos:
|
||||
definition = type_infos[type_name]
|
||||
component_type = definition["title"]
|
||||
short_name = definition["short_name"]
|
||||
is_component = definition['isComponent'] if "isComponent" in definition else False
|
||||
if not is_component:
|
||||
continue
|
||||
|
||||
addable_components.append(component_type)
|
||||
|
||||
try:
|
||||
add_component_operator(component_type=component_type)
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
object = bpy.context.object
|
||||
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
# print("propertyGroup", propertyGroup, propertyGroup.field_names)
|
||||
added_components.append(component_type)
|
||||
except Exception as error:
|
||||
errors.append(error)
|
||||
assert len(errors) == 0
|
||||
|
||||
# now test component removal
|
||||
errors.clear()
|
||||
remove_component_operator = bpy.ops.object.remove_bevy_component
|
||||
for component_type in added_components:
|
||||
component_name = type_infos[component_type]["short_name"]
|
||||
try:
|
||||
remove_component_operator(component_name=component_name)
|
||||
except Exception as error:
|
||||
errors.append(error)
|
||||
assert len(errors) == 0
|
||||
|
||||
def test_copy_paste_components(setup_data):
|
||||
context = bpy.context
|
||||
registry = context.window_manager.components_registry
|
||||
registry.schemaPath = "../../testing/bevy_registry_export/basic/assets/registry.json"
|
||||
bpy.ops.object.reload_registry()
|
||||
|
||||
#component_type = "bevy_bevy_registry_export_basic_example::test_components::BasicTest"
|
||||
short_name = "BasicTest"
|
||||
component_type = registry.short_names_to_long_names[short_name]
|
||||
|
||||
# SOURCE object setup
|
||||
add_component_operator = bpy.ops.object.add_bevy_component
|
||||
add_component_operator(component_type=component_type)
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
object = context.object
|
||||
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
setattr(propertyGroup, propertyGroup.field_names[0], 25.0)
|
||||
|
||||
copy_component_operator = bpy.ops.object.copy_bevy_component
|
||||
copy_component_operator(source_component_name=short_name, source_object_name=object.name)
|
||||
|
||||
# ---------------------------------------
|
||||
# TARGET object
|
||||
bpy.ops.mesh.primitive_cube_add()
|
||||
new_cube = bpy.context.selected_objects[0]
|
||||
# change name
|
||||
new_cube.name = "TargetCube"
|
||||
target_components_metadata = new_cube.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
|
||||
# first check that there is no component currently
|
||||
assert component_meta == None
|
||||
|
||||
paste_component_operator = bpy.ops.object.paste_bevy_component
|
||||
paste_component_operator()
|
||||
|
||||
target_components_metadata = new_cube.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
|
||||
# now after pasting to the new object, it should have component meta
|
||||
assert component_meta != None
|
||||
|
||||
# and then check if the propertyGroup of the target object is correct
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
assert propertyGroup.field_names == ['a', 'b', 'c']
|
||||
|
||||
a_fieldValue = getattr(propertyGroup, propertyGroup.field_names[0])
|
||||
assert a_fieldValue == 25.0
|
||||
|
51
tools/bevy_components/tests/test_conversions.py
Normal file
51
tools/bevy_components/tests/test_conversions.py
Normal file
@ -0,0 +1,51 @@
|
||||
from ..propGroups.conversions_to_prop_group import parse_struct_string, parse_tuplestruct_string
|
||||
|
||||
|
||||
def test_parse_tuplestruct_string():
|
||||
assert parse_tuplestruct_string("(A)", start_nesting=1) == ['A']
|
||||
assert parse_tuplestruct_string("[(A)]", start_nesting=1) == ['(A)']
|
||||
|
||||
assert parse_tuplestruct_string("(a: 45, b: 65)", start_nesting=1) == ['a: 45', 'b: 65']
|
||||
assert parse_tuplestruct_string("[(a: 45, b: 65)]", start_nesting=1) == ['(a: 45, b: 65)']
|
||||
assert parse_tuplestruct_string("45, 65, 'bla'", start_nesting=0) == ['45', '65', "'bla'"]
|
||||
|
||||
assert parse_tuplestruct_string("[(A), (B)]", start_nesting=1) == ['(A)', '(B)']
|
||||
|
||||
assert parse_tuplestruct_string("([(-1.8, 2.9), (0.0, -62)])", start_nesting=1) == ['[(-1.8, 2.9), (0.0, -62)]']
|
||||
assert parse_tuplestruct_string("([(-1.8, 2.9), (0.0, -62)])", start_nesting=2) == ['(-1.8, 2.9)', '(0.0, -62)']
|
||||
assert parse_tuplestruct_string("([(-1.8, 2.9), (0.0, -62), (25)])", start_nesting=2) == ['(-1.8, 2.9)', '(0.0, -62)', '(25)']
|
||||
|
||||
assert parse_tuplestruct_string("(Vec3(x:-2.0, y:120.0, z:1.0))", start_nesting=2) == ['x:-2.0', 'y:120.0', 'z:1.0']
|
||||
|
||||
assert parse_tuplestruct_string("(9)", start_nesting=1) == ['9']
|
||||
assert parse_tuplestruct_string('("toto")', start_nesting=1) == ['"toto"']
|
||||
|
||||
assert parse_tuplestruct_string("(Rgba(red:0.0, green:0.2, blue:0.9, alpha:1.0))", start_nesting=1) == ['Rgba(red:0.0, green:0.2, blue:0.9, alpha:1.0)']
|
||||
assert parse_tuplestruct_string("(Rgba(red:0.0, green:0.2, blue:0.9, alpha:1.0))", start_nesting=2) == ['red:0.0', 'green:0.2', 'blue:0.9', 'alpha:1.0']
|
||||
|
||||
assert parse_tuplestruct_string("([(-1.2, 2.9), (0.0, -62)])", start_nesting=2) == ['(-1.2, 2.9)', '(0.0, -62)']
|
||||
|
||||
assert parse_tuplestruct_string("([Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0), Rgba(red:1.0, green:0.0, blue:0.5, alpha:1.0)])", start_nesting=2) == ['Rgba(red:1.0, green:1.0, blue:0.0, alpha:1.0)', 'Rgba(red:1.0, green:0.0, blue:0.5, alpha:1.0)']
|
||||
assert parse_tuplestruct_string('(7.2, 2607, "sdf")', start_nesting=1) == ['7.2', '2607', '"sdf"']
|
||||
|
||||
assert parse_tuplestruct_string('[a, b]', start_nesting=1) == ['a', 'b']
|
||||
assert parse_tuplestruct_string('[]', start_nesting=1) == []
|
||||
|
||||
def test_parse_struct_string():
|
||||
assert parse_struct_string("a: 45, b:65") == {'a': '45', 'b':'65'}
|
||||
assert parse_struct_string("x:-2.0, y:120.0, z:1.0") == {'x': '-2.0', 'y':'120.0', 'z':'1.0'}
|
||||
|
||||
assert parse_struct_string("enabled: true") == {'enabled': 'true'}
|
||||
assert parse_struct_string("(enabled: true)", start_nesting=1) == {'enabled': 'true'}
|
||||
|
||||
|
||||
assert parse_struct_string("(filters: (25), memberships: (5))", start_nesting=1) == {'filters': '(25)', 'memberships':'(5)'}
|
||||
assert parse_struct_string("groups: 0", start_nesting=0) == {'groups': '0'}
|
||||
assert parse_struct_string("(groups: 0)", start_nesting=1) == {'groups': '0'}
|
||||
|
||||
assert parse_struct_string("(composite_mode: EnergyConserving, high_pass_frequency: 4.0, intensity: 0.0, low_frequency_boost: -6.0, low_frequency_boost_curvature: 4.1, prefilter_settings: (threshold: -5.1, threshold_softness: 2.1))", start_nesting=1) == {'composite_mode': 'EnergyConserving', 'high_pass_frequency': '4.0', 'intensity': '0.0', 'low_frequency_boost': '-6.0', 'low_frequency_boost_curvature': '4.1', 'prefilter_settings': '(threshold: -5.1, threshold_softness: 2.1)'}
|
||||
|
||||
|
||||
assert parse_struct_string("dimensions: UVec3(x:0.0, y:0.0, z:0.0), dynamic_resizing: true, z_config: (far_z_mode: MaxLightRange, first_slice_depth: 0.0)") == {'dimensions': 'UVec3(x:0.0, y:0.0, z:0.0)', 'dynamic_resizing': 'true', 'z_config': '(far_z_mode: MaxLightRange, first_slice_depth: 0.0)'}
|
||||
|
||||
assert parse_struct_string('(inverse_bindposes: Strong(""), joints: [4294967295, 4294967295, 4294967295])', start_nesting=1) == {'inverse_bindposes': 'Strong("")', 'joints': '[4294967295, 4294967295, 4294967295]'}
|
29
tools/bevy_components/tests/test_registry.py
Normal file
29
tools/bevy_components/tests/test_registry.py
Normal file
@ -0,0 +1,29 @@
|
||||
import bpy
|
||||
|
||||
|
||||
def test_blend():
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
registry.schemaPath = "../../testing/bevy_registry_export/basic/assets/registry.json"
|
||||
bpy.ops.object.reload_registry()
|
||||
#print("registry type infos", registry.type_infos)
|
||||
|
||||
short_name = "BasicTest"
|
||||
component_type = registry.short_names_to_long_names[short_name]
|
||||
|
||||
add_component_operator = bpy.ops.object.add_bevy_component
|
||||
add_component_operator(component_type=component_type)
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
object = bpy.context.object
|
||||
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
print("propertyGroup", propertyGroup, propertyGroup.field_names)
|
||||
|
||||
|
||||
"""copy_component_operator = bpy.ops.object.copy_bevy_component
|
||||
copy_component_operator()"""
|
||||
|
||||
|
||||
assert propertyGroup.field_names == ['a', 'b', 'c']
|
141
tools/bevy_components/tests/test_shuffler.py
Normal file
141
tools/bevy_components/tests/test_shuffler.py
Normal file
@ -0,0 +1,141 @@
|
||||
import bpy
|
||||
from .component_values_shuffler import component_values_shuffler
|
||||
|
||||
def test_shuffler():
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
registry.schemaPath = "../../testing/bevy_registry_export/basic/assets/registry.json"
|
||||
bpy.ops.object.reload_registry()
|
||||
|
||||
type_infos = registry.type_infos
|
||||
object = bpy.context.object
|
||||
|
||||
add_component_operator = bpy.ops.object.add_bevy_component
|
||||
|
||||
short_name = "BasicTest"
|
||||
component_type = registry.short_names_to_long_names[short_name]
|
||||
|
||||
add_component_operator(component_type=component_type)
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
component_values_shuffler(seed= 10, property_group=propertyGroup, definition=definition, registry=registry)
|
||||
|
||||
assert getattr(propertyGroup, 'a') == 0.5714026093482971
|
||||
assert getattr(propertyGroup, 'b') == 54
|
||||
assert getattr(propertyGroup, 'c') == "psagopiu"
|
||||
|
||||
|
||||
# Testing a more complex component
|
||||
short_name = "NestingTestLevel2"
|
||||
component_type = registry.short_names_to_long_names[short_name]
|
||||
add_component_operator(component_type=component_type)
|
||||
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
component_values_shuffler(seed= 17, property_group=propertyGroup, definition=definition, registry=registry)
|
||||
|
||||
print("propertyGroup", object[short_name])
|
||||
# cheating / making things easier for us for complex types: we use the custom property value
|
||||
assert object[short_name] == '(basic: (a: 0.5219839215278625, b: 38, c: "ljfywwrv"), color: (Rgba(red:0.2782765030860901, green:0.9174930453300476, blue:0.24890311062335968, alpha:0.815186083316803)), colors_list: ([Rgba(red:0.2523837685585022, green:0.5016026496887207, blue:0.317435085773468, alpha:0.8463277816772461), Rgba(red:0.945193886756897, green:0.4015909433364868, blue:0.9984470009803772, alpha:0.06219279021024704)]), enable: true, enum_inner: Wood, nested: (vec: (Vec3(x:0.1509154736995697, y:0.7055686116218567, z:0.5588918924331665))), text: "vgkrdwuc", toggle: (false))'
|
||||
|
||||
|
||||
# And another complex component
|
||||
short_name = "EnumComplex"
|
||||
component_type = registry.short_names_to_long_names[short_name]
|
||||
add_component_operator(component_type=component_type)
|
||||
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
component_values_shuffler(seed= 17, property_group=propertyGroup, definition=definition, registry=registry)
|
||||
|
||||
print("propertyGroup", object[short_name])
|
||||
# cheating / making things easier for us for complex types: we use the custom property value
|
||||
assert object[short_name] == 'StructLike(a: 0.41416797041893005, b: 38, c: "ljfywwrv")'
|
||||
|
||||
# And another complex component
|
||||
short_name = "AnimationPlayer"
|
||||
component_type = registry.short_names_to_long_names[short_name]
|
||||
add_component_operator(component_type=component_type)
|
||||
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
component_values_shuffler(seed= 17, property_group=propertyGroup, definition=definition, registry=registry)
|
||||
|
||||
print("propertyGroup", object[short_name])
|
||||
# cheating / making things easier for us for complex types: we use the custom property value
|
||||
assert object[short_name] == '(animation: "", paused: true)'
|
||||
|
||||
|
||||
|
||||
# And another complex component
|
||||
short_name = "VecOfColors"
|
||||
component_type = registry.short_names_to_long_names[short_name]
|
||||
add_component_operator(component_type=component_type)
|
||||
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
component_values_shuffler(seed= 17, property_group=propertyGroup, definition=definition, registry=registry)
|
||||
|
||||
print("propertyGroup", object[short_name])
|
||||
# cheating / making things easier for us for complex types: we use the custom property value
|
||||
assert object[short_name] == '([Rgba(red:0.8066907525062561, green:0.9604947566986084, blue:0.2896253764629364, alpha:0.766107439994812), Rgba(red:0.7042198777198792, green:0.6613830327987671, blue:0.11016204953193665, alpha:0.02693677879869938)])'
|
||||
|
||||
|
||||
# And another complex component
|
||||
short_name = "VecOfF32s"
|
||||
component_type = registry.short_names_to_long_names[short_name]
|
||||
add_component_operator(component_type=component_type)
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
component_values_shuffler(seed= 17, property_group=propertyGroup, definition=definition, registry=registry)
|
||||
|
||||
print("propertyGroup", object[short_name])
|
||||
# cheating / making things easier for us for complex types: we use the custom property value
|
||||
assert object[short_name] == '([0.8066907525062561, 0.9604947566986084])'
|
||||
|
||||
# And another complex component
|
||||
short_name = "SkinnedMesh"
|
||||
component_type = registry.short_names_to_long_names[short_name]
|
||||
add_component_operator(component_type=component_type)
|
||||
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(short_name)
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == short_name, target_components_metadata), None)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
|
||||
definition = type_infos[component_type]
|
||||
component_values_shuffler(seed= 17, property_group=propertyGroup, definition=definition, registry=registry)
|
||||
|
||||
print("propertyGroup", object[short_name])
|
||||
# cheating / making things easier for us for complex types: we use the custom property value
|
||||
assert object[short_name] == '(inverse_bindposes: Weak(Uuid(uuid: "73b3b118-7d01-4778-8bcc-4e79055f5d22")), joints: [0, 0])'
|
||||
|
Loading…
Reference in New Issue
Block a user