mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-11-26 21:37:01 +00:00
Compare commits
6 Commits
a3d95b8425
...
dfe6096831
Author | SHA1 | Date | |
---|---|---|---|
|
dfe6096831 | ||
|
c44d82e7dc | ||
|
6c34ab8bd6 | ||
|
bb4b07b7af | ||
|
f438116ece | ||
|
ada54450a5 |
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bevy_gltf_blueprints"
|
name = "bevy_gltf_blueprints"
|
||||||
version = "0.10.0"
|
version = "0.10.2"
|
||||||
authors = ["Mark 'kaosat-dev' Moissette"]
|
authors = ["Mark 'kaosat-dev' Moissette"]
|
||||||
description = "Adds the ability to define Blueprints/Prefabs for Bevy inside gltf files and spawn them in Bevy."
|
description = "Adds the ability to define Blueprints/Prefabs for Bevy inside gltf files and spawn them in Bevy."
|
||||||
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
||||||
|
@ -224,7 +224,7 @@ Typically , the order of systems should be
|
|||||||
|
|
||||||
***bevy_gltf_components (GltfComponentsSet::Injection)*** => ***bevy_gltf_blueprints (GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)*** => ***replace_proxies***
|
***bevy_gltf_components (GltfComponentsSet::Injection)*** => ***bevy_gltf_blueprints (GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)*** => ***replace_proxies***
|
||||||
|
|
||||||
see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic for how to set it up correctly
|
see an example [here](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic) for how to set it up correctly
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -278,9 +278,9 @@ pub fn animation_change_on_proximity_foxes(
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation for how to set it up correctly
|
see [here](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation) for how to set it up correctly
|
||||||
|
|
||||||
particularly from https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation/game/in_game.rs
|
particularly from [here](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation/src/game/in_game.rs)
|
||||||
|
|
||||||
|
|
||||||
## Materials
|
## Materials
|
||||||
@ -301,7 +301,7 @@ material_library_folder: "materials".into() //defaults to "materials" the folder
|
|||||||
```bevy_gltf_blueprints``` currently does NOT take care of loading those at runtime
|
```bevy_gltf_blueprints``` currently does NOT take care of loading those at runtime
|
||||||
|
|
||||||
|
|
||||||
see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/materials for how to set it up correctly
|
see an example [here](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/materials) for how to set it up correctly
|
||||||
|
|
||||||
Generating optimised blueprints and material libraries can be automated using the latests version of the [Blender plugin](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/gltf_auto_export)
|
Generating optimised blueprints and material libraries can be automated using the latests version of the [Blender plugin](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/gltf_auto_export)
|
||||||
|
|
||||||
@ -326,15 +326,15 @@ As it create custom properties that are writen in real **ron** file format inste
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic
|
* [basic](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic)
|
||||||
|
|
||||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic_xpbd_physics
|
* [xbpd](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic_xpbd_physics)
|
||||||
|
|
||||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation
|
* [animation](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation)
|
||||||
|
|
||||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/materials
|
* [materials](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/materials)
|
||||||
|
|
||||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles
|
* [multiple_levels_multiple_blendfiles](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles)
|
||||||
|
|
||||||
|
|
||||||
## Compatible Bevy versions
|
## Compatible Bevy versions
|
||||||
|
@ -34,4 +34,14 @@ pub struct InstanceAnimations {
|
|||||||
/// so that the root entity knows which of its children contains an actualy `AnimationPlayer` component
|
/// so that the root entity knows which of its children contains an actualy `AnimationPlayer` component
|
||||||
/// this is for convenience, because currently , Bevy's gltf parsing inserts `AnimationPlayers` "one level down"
|
/// this is for convenience, because currently , Bevy's gltf parsing inserts `AnimationPlayers` "one level down"
|
||||||
/// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid
|
/// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid
|
||||||
pub struct InstanceAnimationPlayerLink(pub Entity);
|
pub struct InstanceAnimationPlayerLink(pub Entity);
|
||||||
|
|
||||||
|
|
||||||
|
pub struct AnimationMarker{
|
||||||
|
pub frame:u32,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct AnimationMarkers(pub HashMap<String, HashMap<u32, Vec<String> >>);
|
@ -19,7 +19,11 @@ pub use copy_components::*;
|
|||||||
use core::fmt;
|
use core::fmt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use bevy::{prelude::*, render::primitives::Aabb, utils::HashMap};
|
use bevy::{
|
||||||
|
prelude::*,
|
||||||
|
render::{primitives::Aabb, view::VisibilitySystems},
|
||||||
|
utils::HashMap,
|
||||||
|
};
|
||||||
use bevy_gltf_components::{ComponentsFromGltfPlugin, GltfComponentsSet};
|
use bevy_gltf_components::{ComponentsFromGltfPlugin, GltfComponentsSet};
|
||||||
|
|
||||||
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
|
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
@ -123,6 +127,10 @@ impl Plugin for BlueprintsPlugin {
|
|||||||
.register_type::<BlueprintAnimations>()
|
.register_type::<BlueprintAnimations>()
|
||||||
.register_type::<InstanceAnimations>()
|
.register_type::<InstanceAnimations>()
|
||||||
.register_type::<Animated>()
|
.register_type::<Animated>()
|
||||||
|
.register_type::<AnimationMarkers>()
|
||||||
|
.register_type::<HashMap<u32, Vec<String>>>()
|
||||||
|
.register_type::<HashMap<String, HashMap<u32, Vec<String> >>>()
|
||||||
|
|
||||||
|
|
||||||
.register_type::<BlueprintsList>()
|
.register_type::<BlueprintsList>()
|
||||||
.register_type::<Vec<String>>()
|
.register_type::<Vec<String>>()
|
||||||
@ -170,10 +178,11 @@ impl Plugin for BlueprintsPlugin {
|
|||||||
.in_set(GltfBlueprintsSet::Spawn),
|
.in_set(GltfBlueprintsSet::Spawn),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
PostUpdate,
|
||||||
(spawned_blueprint_post_process, apply_deferred)
|
(spawned_blueprint_post_process, apply_deferred)
|
||||||
.chain()
|
.chain()
|
||||||
.in_set(GltfBlueprintsSet::AfterSpawn),
|
.in_set(GltfBlueprintsSet::AfterSpawn)
|
||||||
|
.before(VisibilitySystems::CheckVisibility),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
use bevy::pbr::DirectionalLightShadowMap;
|
use bevy::pbr::DirectionalLightShadowMap;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use bevy::render::render_asset::RenderAssetUsages;
|
||||||
|
use bevy::render::render_resource::{
|
||||||
|
Extent3d, TextureDimension, TextureFormat, TextureViewDescriptor, TextureViewDimension,
|
||||||
|
};
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
use crate::GltfComponentsSet;
|
use crate::GltfComponentsSet;
|
||||||
|
|
||||||
@ -84,14 +89,66 @@ fn process_shadowmap(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn process_background_shader(
|
fn process_background_shader(
|
||||||
background_shaders: Query<&BlenderBackgroundShader, Added<BlenderBackgroundShader>>,
|
background_shaders: Query<Ref<BlenderBackgroundShader>>,
|
||||||
|
cameras: Query<(Entity, Ref<Camera3d>)>,
|
||||||
|
mut images: ResMut<Assets<Image>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
mut env_map_handle: Local<Option<Handle<Image>>>,
|
||||||
) {
|
) {
|
||||||
for background_shader in background_shaders.iter() {
|
let Ok(background_shader) = background_shaders.get_single() else {
|
||||||
commands.insert_resource(AmbientLight {
|
return;
|
||||||
color: background_shader.color,
|
};
|
||||||
// Just a guess, see <https://github.com/bevyengine/bevy/issues/12280>
|
|
||||||
brightness: background_shader.strength * 400.0,
|
let env_map_handle = env_map_handle.get_or_insert_with(|| {
|
||||||
|
let size = Extent3d {
|
||||||
|
width: 1,
|
||||||
|
height: 6,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
};
|
||||||
|
let dimension = TextureDimension::D2;
|
||||||
|
const SIDES_PER_CUBE: usize = 6;
|
||||||
|
let data: Vec<_> = iter::repeat(background_shader.color.as_rgba_u8())
|
||||||
|
.take(SIDES_PER_CUBE)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
let format = TextureFormat::Rgba8UnormSrgb;
|
||||||
|
let asset_usage = RenderAssetUsages::RENDER_WORLD;
|
||||||
|
|
||||||
|
let mut image = Image::new(size, dimension, data, format, asset_usage);
|
||||||
|
|
||||||
|
// Source: https://github.com/bevyengine/bevy/blob/85b488b73d6f6e75690962fba67a144d9beb6b88/examples/3d/skybox.rs#L152-L160
|
||||||
|
image.reinterpret_stacked_2d_as_array(image.height() / image.width());
|
||||||
|
image.texture_view_descriptor = Some(TextureViewDescriptor {
|
||||||
|
dimension: Some(TextureViewDimension::Cube),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
images.add(image)
|
||||||
|
});
|
||||||
|
// Don't need the handle to be &mut
|
||||||
|
let env_map_handle = &*env_map_handle;
|
||||||
|
|
||||||
|
if background_shader.is_added() {
|
||||||
|
// We're using an environment map, so we don't need the ambient light
|
||||||
|
commands.remove_resource::<AmbientLight>();
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_bg_outdated = background_shader.is_changed();
|
||||||
|
if is_bg_outdated {
|
||||||
|
let color = background_shader.color * background_shader.strength;
|
||||||
|
commands.insert_resource(ClearColor(color));
|
||||||
|
}
|
||||||
|
let camera_entities = cameras
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(entity, cam)| (is_bg_outdated || cam.is_changed()).then_some(entity));
|
||||||
|
|
||||||
|
for camera_entity in camera_entities {
|
||||||
|
// See https://github.com/KhronosGroup/glTF-Blender-IO/blob/8573cc0dfb612091bfc1bcf6df55c18a44b9668a/addons/io_scene_gltf2/blender/com/gltf2_blender_conversion.py#L19
|
||||||
|
const PBR_WATTS_TO_LUMENS: f32 = 683.0;
|
||||||
|
commands.entity(camera_entity).insert(EnvironmentMapLight {
|
||||||
|
diffuse_map: env_map_handle.clone(),
|
||||||
|
specular_map: env_map_handle.clone(),
|
||||||
|
intensity: background_shader.strength * PBR_WATTS_TO_LUMENS,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@ -3,7 +3,7 @@ use std::{
|
|||||||
collections::HashMap, fs, time::Duration
|
collections::HashMap, fs, time::Duration
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy_gltf_blueprints::{Animated, BlueprintAnimationPlayerLink, BlueprintAnimations, InstanceAnimationPlayerLink, InstanceAnimations, BlueprintName, BlueprintsList, GltfBlueprintsSet};
|
use bevy_gltf_blueprints::{Animated, AnimationMarkers, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, BlueprintsList, GltfBlueprintsSet, InstanceAnimationPlayerLink, InstanceAnimations};
|
||||||
pub use in_game::*;
|
pub use in_game::*;
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
@ -308,6 +308,39 @@ fn play_animations(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn trigger_event_based_on_animation_marker(
|
||||||
|
bla: Query<(Entity, &AnimationMarkers, &InstanceAnimationPlayerLink, &InstanceAnimations)>,
|
||||||
|
animation_players: Query<&AnimationPlayer>,
|
||||||
|
animation_clips: Res<Assets<AnimationClip>>
|
||||||
|
) {
|
||||||
|
for (entity, markers, link, animations) in bla.iter() {
|
||||||
|
let animation_player = animation_players.get(link.0).unwrap();
|
||||||
|
|
||||||
|
let animation_clip = animation_clips.get(animation_player.animation_clip());
|
||||||
|
|
||||||
|
if animation_clip.is_some(){
|
||||||
|
// println!("Entity {:?} markers {:?}", entity, markers);
|
||||||
|
// println!("Player {:?} {}", animation_player.elapsed(), animation_player.completions());
|
||||||
|
|
||||||
|
let animation_total_length = animation_clip.unwrap().duration();
|
||||||
|
let animation_total_frames = 80; // FIXME just for testing
|
||||||
|
// TODO: we also need to take playback speed into account
|
||||||
|
let time_in_animation = animation_player.elapsed() - (animation_player.completions() as f32) * animation_total_length;//(animation_player.elapsed() / (animation_player.completions() as f32 + 1.0)) ;// / animation_total_length;
|
||||||
|
let time_bla = (animation_total_frames as f32 / animation_total_length) * time_in_animation ;
|
||||||
|
let frame = time_bla as u32;
|
||||||
|
// println!("time_in_animation {} out of {}, completions {}, // frame {}",time_in_animation, animation_total_length, animation_player.completions(), frame);
|
||||||
|
//animation_player.animation_clip()
|
||||||
|
|
||||||
|
let matching_animation_marker = &markers.0[&"Blueprint1_jump".to_string()];
|
||||||
|
if matching_animation_marker.contains_key(&frame) {
|
||||||
|
let matching_markers_per_frame = matching_animation_marker.get(&frame).unwrap();
|
||||||
|
println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
/// flag component for testing
|
/// flag component for testing
|
||||||
@ -336,7 +369,7 @@ impl Plugin for GamePlugin {
|
|||||||
.add_systems(OnEnter(AppState::AppRunning), setup_game)
|
.add_systems(OnEnter(AppState::AppRunning), setup_game)
|
||||||
|
|
||||||
.add_systems(OnEnter(AppState::MenuRunning), setup_main_scene_animations)
|
.add_systems(OnEnter(AppState::MenuRunning), setup_main_scene_animations)
|
||||||
.add_systems(Update, animations
|
.add_systems(Update, (animations, trigger_event_based_on_animation_marker)
|
||||||
.run_if(in_state(AppState::AppRunning))
|
.run_if(in_state(AppState::AppRunning))
|
||||||
.after(GltfBlueprintsSet::AfterSpawn)
|
.after(GltfBlueprintsSet::AfterSpawn)
|
||||||
)
|
)
|
||||||
|
@ -56,8 +56,22 @@ def copy_animation_data(source, target):
|
|||||||
# sort animations alphabetically (case insensitive) so they have a defined order and match Blender's Action list
|
# sort animations alphabetically (case insensitive) so they have a defined order and match Blender's Action list
|
||||||
blender_actions.sort(key = lambda a: a.name.lower())
|
blender_actions.sort(key = lambda a: a.name.lower())
|
||||||
|
|
||||||
|
markers_per_animation = {}
|
||||||
for action in blender_actions:
|
for action in blender_actions:
|
||||||
animations.append(blender_tracks[action.name])
|
animation_name = blender_tracks[action.name]
|
||||||
|
animations.append(animation_name)
|
||||||
|
|
||||||
|
markers_per_animation[animation_name] = {}
|
||||||
|
|
||||||
|
print("markers", action.pose_markers, "for", action.name)
|
||||||
|
for marker in action.pose_markers:
|
||||||
|
|
||||||
|
if marker.frame not in markers_per_animation[animation_name]:
|
||||||
|
markers_per_animation[animation_name][marker.frame] = []
|
||||||
|
print(" marker", marker.name, marker.frame)
|
||||||
|
|
||||||
|
markers_per_animation[animation_name][marker.frame].append(marker.name)
|
||||||
|
|
||||||
print("animations", animations)
|
print("animations", animations)
|
||||||
|
|
||||||
"""if target.animation_data == None:
|
"""if target.animation_data == None:
|
||||||
@ -67,13 +81,29 @@ def copy_animation_data(source, target):
|
|||||||
with bpy.context.temp_override(active_object=source, selected_editable_objects=[target]):
|
with bpy.context.temp_override(active_object=source, selected_editable_objects=[target]):
|
||||||
bpy.ops.object.make_links_data(type='ANIMATION')
|
bpy.ops.object.make_links_data(type='ANIMATION')
|
||||||
# we add an "animated" flag component
|
# we add an "animated" flag component
|
||||||
target['Animated'] = f'(animations: {animations})'.replace("'", '"') #'(animations: [])' #
|
target['Animated'] = f'(animations: {animations})'.replace("'", '"')
|
||||||
|
|
||||||
|
markers_formated = '{'
|
||||||
|
for animation in markers_per_animation.keys():
|
||||||
|
markers_formated += f'"{animation}":'
|
||||||
|
markers_formated += "{"
|
||||||
|
for frame in markers_per_animation[animation].keys():
|
||||||
|
markers = markers_per_animation[animation][frame]
|
||||||
|
markers_formated += f"{frame}:{markers}, ".replace("'", '"')
|
||||||
|
markers_formated += '}, '
|
||||||
|
markers_formated += '}'
|
||||||
|
print("markers_formated", markers_formated)
|
||||||
|
target["AnimationMarkers"] = f'( {markers_formated} )'
|
||||||
|
#'({"animation_name": {5: ["Marker_1"]} })'
|
||||||
|
#f'({json.dumps(markers_per_animation)})'
|
||||||
|
|
||||||
"""print("copying animation data for", source.name, target.animation_data)
|
"""print("copying animation data for", source.name, target.animation_data)
|
||||||
properties = [p.identifier for p in source.animation_data.bl_rna.properties if not p.is_readonly]
|
properties = [p.identifier for p in source.animation_data.bl_rna.properties if not p.is_readonly]
|
||||||
for prop in properties:
|
for prop in properties:
|
||||||
print("copying stuff", prop)
|
print("copying stuff", prop)
|
||||||
setattr(target.animation_data, prop, getattr(source.animation_data, prop))"""
|
setattr(target.animation_data, prop, getattr(source.animation_data, prop))"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def duplicate_object(object, parent, combine_mode, destination_collection, library_collections, legacy_mode, nester=""):
|
def duplicate_object(object, parent, combine_mode, destination_collection, library_collections, legacy_mode, nester=""):
|
||||||
copy = None
|
copy = None
|
||||||
@ -95,6 +125,10 @@ def duplicate_object(object, parent, combine_mode, destination_collection, libra
|
|||||||
children_per_collection = {}
|
children_per_collection = {}
|
||||||
get_sub_collections([object.instance_collection], root_node, children_per_collection)
|
get_sub_collections([object.instance_collection], root_node, children_per_collection)
|
||||||
empty_obj["BlueprintsList"] = f"({json.dumps(dict(children_per_collection))})"
|
empty_obj["BlueprintsList"] = f"({json.dumps(dict(children_per_collection))})"
|
||||||
|
|
||||||
|
# empty_obj["AnimationMarkers"] = '({"animation_name": {5: "Marker_1"} })'
|
||||||
|
|
||||||
|
#'({5: "sdf"})'#.replace('"',"'") #f"({json.dumps(dict(animation_foo))})"
|
||||||
#empty_obj["Assets"] = {"Animations": [], "Materials": [], "Models":[], "Textures":[], "Audio":[], "Other":[]}
|
#empty_obj["Assets"] = {"Animations": [], "Materials": [], "Models":[], "Textures":[], "Audio":[], "Other":[]}
|
||||||
|
|
||||||
# we copy custom properties over from our original object to our empty
|
# we copy custom properties over from our original object to our empty
|
||||||
@ -109,10 +143,7 @@ def duplicate_object(object, parent, combine_mode, destination_collection, libra
|
|||||||
object.name = original_name + "____bak"
|
object.name = original_name + "____bak"
|
||||||
copy = object.copy()
|
copy = object.copy()
|
||||||
copy.name = original_name
|
copy.name = original_name
|
||||||
# FIXME: orphan data comes from this one, not even sure if this copying is needed at all
|
|
||||||
"""if object.data:
|
|
||||||
data = object.data.copy()
|
|
||||||
obj_copy.data = data"""
|
|
||||||
|
|
||||||
destination_collection.objects.link(copy)
|
destination_collection.objects.link(copy)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user