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,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct MaterialInfos(Vec<MaterialInfo>);
|
||||
|
||||
#[derive(Component, Default, Debug)]
|
||||
pub struct MaterialProcessed;
|
||||
|
||||
/// system that injects / replaces materials from material library
|
||||
pub(crate) fn inject_materials(
|
||||
mut blenvy_config: ResMut<BlenvyConfig>,
|
||||
material_infos: Query<
|
||||
(Entity, &MaterialInfo, &Children),
|
||||
material_infos_query: Query<
|
||||
(Entity, &MaterialInfos, &Children),
|
||||
Without<MaterialProcessed>, // (With<BlueprintReadyForPostProcess>)
|
||||
/*(
|
||||
Added<BlueprintMaterialAssetsLoaded>,
|
||||
|
@ -37,57 +41,65 @@ pub(crate) fn inject_materials(
|
|||
|
||||
mut commands: Commands,
|
||||
) {
|
||||
for (entity, material_info, children) in material_infos.iter() {
|
||||
let material_full_path = format!("{}#{}", material_info.path, material_info.name);
|
||||
let mut material_found: Option<&Handle<StandardMaterial>> = None;
|
||||
|
||||
if blenvy_config
|
||||
.materials_cache
|
||||
.contains_key(&material_full_path)
|
||||
{
|
||||
debug!("material is cached, retrieving");
|
||||
let material = blenvy_config
|
||||
for (entity, material_infos, children) in material_infos_query.iter() {
|
||||
for (material_index, material_info) in material_infos.0.iter().enumerate() {
|
||||
let material_full_path = format!("{}#{}", material_info.path, material_info.name);
|
||||
let mut material_found: Option<&Handle<StandardMaterial>> = None;
|
||||
|
||||
if blenvy_config
|
||||
.materials_cache
|
||||
.get(&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)
|
||||
.contains_key(&material_full_path)
|
||||
{
|
||||
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
|
||||
debug!("material is cached, retrieving");
|
||||
let material = blenvy_config
|
||||
.materials_cache
|
||||
.insert(material_full_path, material.clone());
|
||||
.get(&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
|
||||
.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 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());
|
||||
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()
|
||||
);
|
||||
|
||||
commands.entity(*child).insert(material.clone());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
commands.entity(entity).insert(MaterialProcessed);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@ impl Plugin for BlueprintsPlugin {
|
|||
.add_event::<BlueprintEvent>()
|
||||
.register_type::<BlueprintInfo>()
|
||||
.register_type::<MaterialInfo>()
|
||||
.register_type::<MaterialInfos>()
|
||||
|
||||
.register_type::<SpawnBlueprint>()
|
||||
.register_type::<BlueprintInstanceDisabled>()
|
||||
.register_type::<HideUntilReady>()
|
||||
|
|
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
|
||||
|
||||
# deal with materials
|
||||
if export_materials_library:
|
||||
if export_materials_library and len(materials_to_export) > 0:
|
||||
print("export MATERIALS")
|
||||
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:
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
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):
|
||||
root_collection = temp_scene.collection
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
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
|
||||
gltf_output_path = os.path.join(materials_path_full, current_project_name + "_materials")
|
||||
|
||||
print(" exporting Materials to", gltf_output_path, ".gltf/glb")
|
||||
|
||||
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", "")
|
||||
|
||||
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")
|
||||
|
||||
all_materials = bpy.data.materials
|
||||
local_materials = [material for material in all_materials if material.library is None]
|
||||
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
|
||||
# in your current Blender session for example)
|
||||
materials_not_on_disk = find_materials_not_on_disk(local_materials, materials_path_full, export_gltf_extension)
|
||||
if export_materials_library and change_detection:
|
||||
if changed_export_parameters:
|
||||
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"
|
||||
#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))
|
||||
# first check if all materials have already been exported before (if this is the first time the exporter is run
|
||||
# in your current Blender session for example)
|
||||
materials_not_on_disk = find_materials_not_on_disk(local_materials, materials_path_full, export_gltf_extension)
|
||||
|
||||
# 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
|
||||
|
|
|
@ -40,7 +40,9 @@ def get_materials(object, materials_per_object):
|
|||
# print(" slot", m, "material", material)
|
||||
used_materials_names.append(material.name)
|
||||
# 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
|
||||
|
||||
|
||||
|
@ -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}")
|
||||
#print("ADDING MAERIAL INFOS")
|
||||
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
|
||||
# for a few components we could hardcode this
|
||||
component_value = 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)
|
||||
|
||||
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)
|
||||
materials_exported_path = posixpath.join(materials_path, f"{materials_library_name}{export_gltf_extension}")
|
||||
#object['MaterialInfo'] = component_value
|
||||
material_infos.append(material_info)
|
||||
object['MaterialInfos'] = f"({material_infos})".replace("'","")
|
||||
print("adding materialInfos to object", object, "material infos", material_infos)
|
||||
|
||||
|
||||
# get all the materials of all objects in a given scene
|
||||
|
|
Loading…
Reference in New Issue