mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-12-26 17:44:11 +00:00
feat(Blenvy): added basics of correct multi-material meshes support
* relying on the fact that the mesh-per-material generated by the gltf exporter is deterministic: ie always uses the ordering of materials in an object * added new component MaterialInfos (plural) with a vec of MaterialInfo's * modified how materials per object are gathered on the Blender side * and modified the processing on the Bevy side to also use the ordered approach * seems to work well so far !
This commit is contained in:
parent
e534917dca
commit
4865d432d9
@ -10,14 +10,18 @@ pub struct MaterialInfo {
|
|||||||
pub path: String,
|
pub path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct MaterialInfos(Vec<MaterialInfo>);
|
||||||
|
|
||||||
#[derive(Component, Default, Debug)]
|
#[derive(Component, Default, Debug)]
|
||||||
pub struct MaterialProcessed;
|
pub struct MaterialProcessed;
|
||||||
|
|
||||||
/// system that injects / replaces materials from material library
|
/// system that injects / replaces materials from material library
|
||||||
pub(crate) fn inject_materials(
|
pub(crate) fn inject_materials(
|
||||||
mut blenvy_config: ResMut<BlenvyConfig>,
|
mut blenvy_config: ResMut<BlenvyConfig>,
|
||||||
material_infos: Query<
|
material_infos_query: Query<
|
||||||
(Entity, &MaterialInfo, &Children),
|
(Entity, &MaterialInfos, &Children),
|
||||||
Without<MaterialProcessed>, // (With<BlueprintReadyForPostProcess>)
|
Without<MaterialProcessed>, // (With<BlueprintReadyForPostProcess>)
|
||||||
/*(
|
/*(
|
||||||
Added<BlueprintMaterialAssetsLoaded>,
|
Added<BlueprintMaterialAssetsLoaded>,
|
||||||
@ -37,57 +41,65 @@ pub(crate) fn inject_materials(
|
|||||||
|
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, material_info, children) in material_infos.iter() {
|
for (entity, material_infos, children) in material_infos_query.iter() {
|
||||||
let material_full_path = format!("{}#{}", material_info.path, material_info.name);
|
for (material_index, material_info) in material_infos.0.iter().enumerate() {
|
||||||
let mut material_found: Option<&Handle<StandardMaterial>> = None;
|
let material_full_path = format!("{}#{}", material_info.path, material_info.name);
|
||||||
|
let mut material_found: Option<&Handle<StandardMaterial>> = None;
|
||||||
|
|
||||||
if blenvy_config
|
if blenvy_config
|
||||||
.materials_cache
|
|
||||||
.contains_key(&material_full_path)
|
|
||||||
{
|
|
||||||
debug!("material is cached, retrieving");
|
|
||||||
let material = blenvy_config
|
|
||||||
.materials_cache
|
.materials_cache
|
||||||
.get(&material_full_path)
|
.contains_key(&material_full_path)
|
||||||
.expect("we should have the material available");
|
|
||||||
material_found = Some(material);
|
|
||||||
} else {
|
|
||||||
let model_handle: Handle<Gltf> = asset_server.load(material_info.path.clone()); // FIXME: kinda weird now
|
|
||||||
let mat_gltf = assets_gltf.get(model_handle.id()).unwrap_or_else(|| {
|
|
||||||
panic!(
|
|
||||||
"materials file {} should have been preloaded",
|
|
||||||
material_info.path
|
|
||||||
)
|
|
||||||
});
|
|
||||||
if mat_gltf
|
|
||||||
.named_materials
|
|
||||||
.contains_key(&material_info.name as &str)
|
|
||||||
{
|
{
|
||||||
let material = mat_gltf
|
debug!("material is cached, retrieving");
|
||||||
.named_materials
|
let material = blenvy_config
|
||||||
.get(&material_info.name as &str)
|
|
||||||
.expect("this material should have been loaded at this stage, please make sure you are correctly preloading them");
|
|
||||||
blenvy_config
|
|
||||||
.materials_cache
|
.materials_cache
|
||||||
.insert(material_full_path, material.clone());
|
.get(&material_full_path)
|
||||||
|
.expect("we should have the material available");
|
||||||
material_found = Some(material);
|
material_found = Some(material);
|
||||||
|
} else {
|
||||||
|
let model_handle: Handle<Gltf> = asset_server.load(material_info.path.clone()); // FIXME: kinda weird now
|
||||||
|
let mat_gltf = assets_gltf.get(model_handle.id()).unwrap_or_else(|| {
|
||||||
|
panic!(
|
||||||
|
"materials file {} should have been preloaded",
|
||||||
|
material_info.path
|
||||||
|
)
|
||||||
|
});
|
||||||
|
if mat_gltf
|
||||||
|
.named_materials
|
||||||
|
.contains_key(&material_info.name as &str)
|
||||||
|
{
|
||||||
|
let material = mat_gltf
|
||||||
|
.named_materials
|
||||||
|
.get(&material_info.name as &str)
|
||||||
|
.expect("this material should have been loaded at this stage, please make sure you are correctly preloading them");
|
||||||
|
blenvy_config
|
||||||
|
.materials_cache
|
||||||
|
.insert(material_full_path, material.clone());
|
||||||
|
material_found = Some(material);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
commands.entity(entity).insert(MaterialProcessed);
|
if let Some(material) = material_found {
|
||||||
|
for (child_index, child) in children.iter().enumerate() {
|
||||||
|
if child_index == material_index {
|
||||||
|
if with_materials_and_meshes.contains(*child) {
|
||||||
|
info!(
|
||||||
|
"injecting material {}, path: {:?}",
|
||||||
|
material_info.name,
|
||||||
|
material_info.path.clone()
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(material) = material_found {
|
commands.entity(*child).insert(material.clone());
|
||||||
for child in children.iter() {
|
}
|
||||||
if with_materials_and_meshes.contains(*child) {
|
}
|
||||||
info!(
|
|
||||||
"injecting material {}, path: {:?}",
|
|
||||||
material_info.name,
|
|
||||||
material_info.path.clone()
|
|
||||||
);
|
|
||||||
|
|
||||||
commands.entity(*child).insert(material.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
commands.entity(entity).insert(MaterialProcessed);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,6 +91,8 @@ impl Plugin for BlueprintsPlugin {
|
|||||||
.add_event::<BlueprintEvent>()
|
.add_event::<BlueprintEvent>()
|
||||||
.register_type::<BlueprintInfo>()
|
.register_type::<BlueprintInfo>()
|
||||||
.register_type::<MaterialInfo>()
|
.register_type::<MaterialInfo>()
|
||||||
|
.register_type::<MaterialInfos>()
|
||||||
|
|
||||||
.register_type::<SpawnBlueprint>()
|
.register_type::<SpawnBlueprint>()
|
||||||
.register_type::<BlueprintInstanceDisabled>()
|
.register_type::<BlueprintInstanceDisabled>()
|
||||||
.register_type::<HideUntilReady>()
|
.register_type::<HideUntilReady>()
|
||||||
|
BIN
tools/blenvy.zip
Normal file
BIN
tools/blenvy.zip
Normal file
Binary file not shown.
@ -102,7 +102,7 @@ def auto_export(changes_per_scene, changes_per_collection, changes_per_material,
|
|||||||
old_selections = bpy.context.selected_objects
|
old_selections = bpy.context.selected_objects
|
||||||
|
|
||||||
# deal with materials
|
# deal with materials
|
||||||
if export_materials_library:
|
if export_materials_library and len(materials_to_export) > 0:
|
||||||
print("export MATERIALS")
|
print("export MATERIALS")
|
||||||
export_materials(materials_to_export, settings, blueprints_data)
|
export_materials(materials_to_export, settings, blueprints_data)
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ def generate_gltf_export_settings(settings):
|
|||||||
if str(key) not in constant_keys:
|
if str(key) not in constant_keys:
|
||||||
gltf_export_settings[key] = standard_gltf_exporter_settings.get(key)
|
gltf_export_settings[key] = standard_gltf_exporter_settings.get(key)
|
||||||
|
|
||||||
print("GLTF EXPORT SETTINGS", gltf_export_settings)
|
#print("GLTF EXPORT SETTINGS", gltf_export_settings)
|
||||||
return gltf_export_settings
|
return gltf_export_settings
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,6 +46,13 @@ def generate_materials_scene_content(root_collection, used_material_names):
|
|||||||
make_material_object("Material_"+material_name, [index * 0.2,0,0], material=material, collection=root_collection)
|
make_material_object("Material_"+material_name, [index * 0.2,0,0], material=material, collection=root_collection)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
# generates a scene for a given material
|
||||||
|
def generate_material_scene_content(root_collection, material_name):
|
||||||
|
material = bpy.data.materials[material_name]
|
||||||
|
make_material_object(f"Material_{material_name}", [0,0,0], material=material, collection=root_collection)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def clear_materials_scene(temp_scene):
|
def clear_materials_scene(temp_scene):
|
||||||
root_collection = temp_scene.collection
|
root_collection = temp_scene.collection
|
||||||
scene_objects = [o for o in root_collection.objects]
|
scene_objects = [o for o in root_collection.objects]
|
||||||
@ -83,9 +90,23 @@ def export_materials(materials_to_export, settings, blueprints_data):
|
|||||||
'export_apply':True
|
'export_apply':True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for material in materials_to_export:
|
||||||
|
print("exporting material", material.name)
|
||||||
|
gltf_output_path = os.path.join(materials_path_full, material.name)
|
||||||
|
|
||||||
|
generate_temporary_scene_and_export(
|
||||||
|
settings=settings,
|
||||||
|
gltf_export_settings=gltf_export_settings,
|
||||||
|
temp_scene_name="__materials_scene",
|
||||||
|
gltf_output_path=gltf_output_path,
|
||||||
|
tempScene_filler= lambda temp_collection: generate_material_scene_content(temp_collection, material.name),
|
||||||
|
tempScene_cleaner= lambda temp_scene, params: clear_materials_scene(temp_scene=temp_scene)
|
||||||
|
)
|
||||||
|
|
||||||
current_project_name = Path(bpy.context.blend_data.filepath).stem
|
current_project_name = Path(bpy.context.blend_data.filepath).stem
|
||||||
gltf_output_path = os.path.join(materials_path_full, current_project_name + "_materials")
|
gltf_output_path = os.path.join(materials_path_full, current_project_name + "_materials")
|
||||||
|
|
||||||
print(" exporting Materials to", gltf_output_path, ".gltf/glb")
|
print(" exporting Materials to", gltf_output_path, ".gltf/glb")
|
||||||
|
|
||||||
generate_temporary_scene_and_export(
|
generate_temporary_scene_and_export(
|
||||||
|
@ -8,21 +8,26 @@ def get_materials_to_export(changes_per_material, changed_export_parameters, blu
|
|||||||
materials_path_full = getattr(settings,"materials_path_full", "")
|
materials_path_full = getattr(settings,"materials_path_full", "")
|
||||||
|
|
||||||
change_detection = getattr(settings.auto_export, "change_detection")
|
change_detection = getattr(settings.auto_export, "change_detection")
|
||||||
|
export_materials_library = getattr(settings.auto_export, "export_materials_library")
|
||||||
collection_instances_combine_mode = getattr(settings.auto_export, "collection_instances_combine_mode")
|
collection_instances_combine_mode = getattr(settings.auto_export, "collection_instances_combine_mode")
|
||||||
|
|
||||||
all_materials = bpy.data.materials
|
all_materials = bpy.data.materials
|
||||||
local_materials = [material for material in all_materials if material.library is None]
|
local_materials = [material for material in all_materials if material.library is None]
|
||||||
materials_to_export = []
|
materials_to_export = []
|
||||||
if change_detection and not changed_export_parameters:
|
|
||||||
changed_materials = [bpy.data.materials[material_name] for material_name in list(changes_per_material.keys())]
|
|
||||||
|
|
||||||
# first check if all materials have already been exported before (if this is the first time the exporter is run
|
if export_materials_library and change_detection:
|
||||||
# in your current Blender session for example)
|
if changed_export_parameters:
|
||||||
materials_not_on_disk = find_materials_not_on_disk(local_materials, materials_path_full, export_gltf_extension)
|
materials_to_export = [bpy.data.materials[material_name] for material_name in list(changes_per_material.keys())] # TODO: should be based on the list of materials in use
|
||||||
|
else :
|
||||||
|
changed_materials = [bpy.data.materials[material_name] for material_name in list(changes_per_material.keys())]
|
||||||
|
|
||||||
# also deal with blueprints that are always marked as "always_export"
|
# first check if all materials have already been exported before (if this is the first time the exporter is run
|
||||||
#materials_always_export = [material for material in internal_materials if is_material_always_export(material)]
|
# in your current Blender session for example)
|
||||||
materials_always_export = []
|
materials_not_on_disk = find_materials_not_on_disk(local_materials, materials_path_full, export_gltf_extension)
|
||||||
materials_to_export = list(set(changed_materials + materials_not_on_disk + materials_always_export))
|
|
||||||
|
|
||||||
|
# also deal with blueprints that are always marked as "always_export"
|
||||||
|
#materials_always_export = [material for material in internal_materials if is_material_always_export(material)]
|
||||||
|
materials_always_export = []
|
||||||
|
materials_to_export = list(set(changed_materials + materials_not_on_disk + materials_always_export))
|
||||||
|
print("materials_to_export", materials_to_export, local_materials)
|
||||||
return materials_to_export
|
return materials_to_export
|
||||||
|
@ -40,7 +40,9 @@ def get_materials(object, materials_per_object):
|
|||||||
# print(" slot", m, "material", material)
|
# print(" slot", m, "material", material)
|
||||||
used_materials_names.append(material.name)
|
used_materials_names.append(material.name)
|
||||||
# TODO:, also respect slots & export multiple materials if applicable !
|
# TODO:, also respect slots & export multiple materials if applicable !
|
||||||
materials_per_object[object] = material
|
if not object in materials_per_object:
|
||||||
|
materials_per_object[object] = []
|
||||||
|
materials_per_object[object].append(material)
|
||||||
return used_materials_names
|
return used_materials_names
|
||||||
|
|
||||||
|
|
||||||
@ -68,16 +70,18 @@ def add_material_info_to_objects(materials_per_object, settings):
|
|||||||
materials_exported_path = posixpath.join(materials_path, f"{materials_library_name}{export_gltf_extension}")
|
materials_exported_path = posixpath.join(materials_path, f"{materials_library_name}{export_gltf_extension}")
|
||||||
#print("ADDING MAERIAL INFOS")
|
#print("ADDING MAERIAL INFOS")
|
||||||
for object in materials_per_object.keys():
|
for object in materials_per_object.keys():
|
||||||
material = materials_per_object[object]
|
material_infos = []
|
||||||
|
for material in materials_per_object[object]:
|
||||||
|
# problem with using actual components: you NEED the type registry/component infos, so if there is none , or it is not loaded yet, it does not work
|
||||||
|
# for a few components we could hardcode this
|
||||||
|
material_info = f'(name: "{material.name}", path: "{materials_exported_path}")'
|
||||||
|
#bpy.ops.blenvy.component_add(target_item_name=object.name, target_item_type="OBJECT", component_type="blenvy::blueprints::materials::MaterialInfo", component_value=component_value)
|
||||||
|
|
||||||
# problem with using actual components: you NEED the type registry/component infos, so if there is none , or it is not loaded yet, it does not work
|
materials_exported_path = posixpath.join(materials_path, f"{materials_library_name}{export_gltf_extension}")
|
||||||
# for a few components we could hardcode this
|
#object['MaterialInfo'] = component_value
|
||||||
component_value = f'(name: "{material.name}", path: "{materials_exported_path}")'
|
material_infos.append(material_info)
|
||||||
#bpy.ops.blenvy.component_add(target_item_name=object.name, target_item_type="OBJECT", component_type="blenvy::blueprints::materials::MaterialInfo", component_value=component_value)
|
object['MaterialInfos'] = f"({material_infos})".replace("'","")
|
||||||
|
print("adding materialInfos to object", object, "material infos", material_infos)
|
||||||
materials_exported_path = posixpath.join(materials_path, f"{materials_library_name}{export_gltf_extension}")
|
|
||||||
object['MaterialInfo'] = component_value
|
|
||||||
print("adding materialInfo to object", object, "material info", component_value)
|
|
||||||
|
|
||||||
|
|
||||||
# get all the materials of all objects in a given scene
|
# get all the materials of all objects in a given scene
|
||||||
|
Loading…
Reference in New Issue
Block a user