123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "RobotImporter/URDF/VisualsMaker.h"
- #include "RobotImporter/URDF/PrefabMakerUtils.h"
- #include "RobotImporter/Utils/TypeConversions.h"
- #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
- #include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
- #include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConfig.h>
- #include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
- #include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
- #include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
- #include <AzCore/Component/NonUniformScaleBus.h>
- #include <AzCore/Component/TransformBus.h>
- #include <AzToolsFramework/Entity/EditorEntityHelpers.h>
- #include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
- #include <sdf/Material.hh>
- #include <sdf/Pbr.hh>
- namespace ROS2RobotImporter
- {
- VisualsMaker::VisualsMaker() = default;
- VisualsMaker::VisualsMaker(const AZStd::shared_ptr<Utils::UrdfAssetMap>& urdfAssetsMapping)
- : m_urdfAssetsMapping(urdfAssetsMapping)
- {
- }
- AZStd::vector<AZ::EntityId> VisualsMaker::AddVisuals(const sdf::Link* link, AZ::EntityId entityId) const
- {
- AZStd::vector<AZ::EntityId> createdEntities;
- const AZStd::string typeString = "visual";
- if (link->VisualCount() < 1)
- {
- // For zero visuals, element is used
- auto createdEntity = AddVisual(nullptr, entityId, PrefabMakerUtils::MakeEntityName(link->Name().c_str(), typeString));
- if (createdEntity.IsValid())
- {
- createdEntities.emplace_back(createdEntity);
- }
- }
- else
- {
- // For one or more visuals, an array is used
- size_t nameSuffixIndex = 0; // For disambiguation when multiple unnamed visuals are present. The order does not matter here
- for (uint64_t index = 0; index < link->VisualCount(); index++)
- {
- auto createdEntity = AddVisual(
- link->VisualByIndex(index),
- entityId,
- PrefabMakerUtils::MakeEntityName(link->Name().c_str(), typeString, nameSuffixIndex));
- if (createdEntity.IsValid())
- {
- createdEntities.emplace_back(createdEntity);
- }
- nameSuffixIndex++;
- }
- }
- return createdEntities;
- }
- AZ::EntityId VisualsMaker::AddVisual(const sdf::Visual* visual, AZ::EntityId entityId, const AZStd::string& generatedName) const
- {
- if (!visual)
- { // It is ok not to have a visual in a link
- return AZ::EntityId();
- }
- if (!visual->Geom())
- { // Non-empty visual should have a geometry. Warn if no geometry present
- AZ_Warning("AddVisual", false, "No Geometry for a visual");
- return AZ::EntityId();
- }
- AZ_Trace("AddVisual", "Processing visual for entity id:%s\n", entityId.ToString().c_str());
- // Use a name generated from the link unless specific name is defined for this visual
- AZStd::string subEntityName = visual->Name().empty() ? generatedName.c_str() : visual->Name().c_str();
- // Since O3DE does not allow origin for visuals, we need to create a sub-entity and store visual there
- auto createEntityResult = PrefabMakerUtils::CreateEntity(entityId, subEntityName);
- if (!createEntityResult.IsSuccess())
- {
- AZ_Error("AddVisual", false, "Unable to create a sub-entity for visual element %s\n", subEntityName.c_str());
- return AZ::EntityId();
- }
- auto visualEntityId = createEntityResult.GetValue();
- auto visualAssetId = AddVisualToEntity(visual, visualEntityId);
- AddMaterialForVisual(visual, visualEntityId, visualAssetId);
- return visualEntityId;
- }
- AZ::Data::AssetId VisualsMaker::AddVisualToEntity(const sdf::Visual* visual, AZ::EntityId entityId) const
- {
- // Asset ID for the asset added to the visual entity, if any.
- AZ::Data::AssetId assetId;
- // Apply transform as per origin
- PrefabMakerUtils::SetEntityTransformLocal(visual->RawPose(), entityId);
- auto geometry = visual->Geom();
- switch (geometry->Type())
- {
- case sdf::GeometryType::SPHERE:
- {
- auto sphereGeometry = geometry->SphereShape();
- AZ_Assert(sphereGeometry, "geometry is not Sphere");
- // Convert radius to diameter: the `_sphere_1x1.fbx.azmodel` model has a diameter of 1
- const AZ::Vector3 sphereDimensions(sphereGeometry->Radius() * 2.0f);
- // The `_sphere_1x1.fbx.azmodel` is created by Asset Processor based on O3DE `PrimitiveAssets` Gem source.
- const char* sphereAssetRelPath = "objects/_primitives/_sphere_1x1.fbx.azmodel"; // relative path to cache folder.
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(
- assetId,
- &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
- sphereAssetRelPath,
- AZ::Data::s_invalidAssetType,
- false);
- AZ_Warning("AddVisual", assetId.IsValid(), "There is no product asset for %s.", sphereAssetRelPath);
- AddVisualAssetToEntity(entityId, assetId, sphereDimensions);
- }
- break;
- case sdf::GeometryType::CYLINDER:
- {
- auto cylinderGeometry = geometry->CylinderShape();
- AZ_Assert(cylinderGeometry, "geometry is not Cylinder");
- // Convert radius to diameter: the `_cylinder_1x1.fbx.azmodel` model has a diameter of 1
- const AZ::Vector3 cylinderDimensions(
- cylinderGeometry->Radius() * 2.0f, cylinderGeometry->Radius() * 2.0f, cylinderGeometry->Length());
- // The `_cylinder_1x1.fbx.azmodel` is created by Asset Processor based on O3DE `PrimitiveAssets` Gem source.
- const char* cylinderAssetRelPath = "objects/_primitives/_cylinder_1x1.fbx.azmodel"; // relative path to cache folder.
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(
- assetId,
- &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
- cylinderAssetRelPath,
- AZ::Data::s_invalidAssetType,
- false);
- AZ_Warning("AddVisual", assetId.IsValid(), "There is no product asset for %s.", cylinderAssetRelPath);
- AddVisualAssetToEntity(entityId, assetId, cylinderDimensions);
- }
- break;
- case sdf::GeometryType::BOX:
- {
- auto boxGeometry = geometry->BoxShape();
- AZ_Assert(boxGeometry, "geometry is not Box");
- const AZ::Vector3 boxDimensions = URDF::TypeConversions::ConvertVector3(boxGeometry->Size());
- // The `_box_1x1.fbx.azmodel` is created by Asset Processor based on O3DE `PrimitiveAssets` Gem source.
- const char* boxAssetRelPath = "objects/_primitives/_box_1x1.fbx.azmodel"; // relative path to cache folder.
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(
- assetId,
- &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
- boxAssetRelPath,
- AZ::Data::s_invalidAssetType,
- false);
- AZ_Warning("AddVisual", assetId.IsValid(), "There is no product asset for %s.", boxAssetRelPath);
- AddVisualAssetToEntity(entityId, assetId, boxDimensions);
- }
- break;
- case sdf::GeometryType::MESH:
- {
- auto meshGeometry = geometry->MeshShape();
- AZ_Assert(meshGeometry, "geometry is not Mesh");
- const AZ::Vector3 scaleVector = URDF::TypeConversions::ConvertVector3(meshGeometry->Scale());
- const auto asset = PrefabMakerUtils::GetAssetFromPath(*m_urdfAssetsMapping, AZStd::string(meshGeometry->Uri().c_str()));
- AZ_Warning("AddVisual", asset, "There is no source asset for %s.", meshGeometry->Uri().c_str());
- if (asset)
- {
- assetId = Utils::GetModelProductAssetId(asset->m_sourceGuid);
- AZ_Warning(
- "AddVisual", assetId.IsValid(), "There is no product asset for %s.", asset->m_sourceAssetRelativePath.c_str());
- }
- AddVisualAssetToEntity(entityId, assetId, scaleVector);
- }
- break;
- default:
- AZ_Warning("AddVisual", false, "Unsupported visual geometry type, %d", (int)geometry->Type());
- break;
- }
- return assetId;
- }
- void VisualsMaker::AddVisualAssetToEntity(AZ::EntityId entityId, const AZ::Data::AssetId& assetId, const AZ::Vector3& scale) const
- {
- if (!assetId.IsValid())
- {
- return;
- }
- AZ::Entity* entity = AzToolsFramework::GetEntityById(entityId);
- bool isUniformScale = AZ::IsClose(scale.GetMaxElement(), scale.GetMinElement(), AZ::Constants::FloatEpsilon);
- if (isUniformScale)
- {
- auto* transformComponent = entity->FindComponent(AZ::EditorTransformComponentTypeId);
- AZ_Assert(transformComponent, "Entity doesn't have a transform component.");
- auto* transformInterface = azrtti_cast<AZ::TransformInterface*>(transformComponent);
- AZ_Assert(transformInterface, "Found component has no transformInterface");
- transformInterface->SetLocalUniformScale(scale.GetX());
- }
- else
- {
- auto component = entity->CreateComponent<AzToolsFramework::Components::EditorNonUniformScaleComponent>();
- AZ_Assert(component, "EditorNonUniformScaleComponent was not created");
- component->SetScale(scale);
- }
- auto editorMeshComponent = entity->CreateComponent(AZ::Render::EditorMeshComponentTypeId);
- if (editorMeshComponent)
- {
- auto editorBaseComponent = azrtti_cast<AzToolsFramework::Components::EditorComponentBase*>(editorMeshComponent);
- AZ_Assert(editorBaseComponent, "EditorMeshComponent didn't derive from EditorComponentBase.");
- editorBaseComponent->SetPrimaryAsset(assetId);
- }
- }
- static void OverrideScriptMaterial(const sdf::Material* material, AZ::Render::MaterialAssignmentMap& overrides)
- {
- AZStd::string materialName(material->ScriptName().c_str(), material->ScriptName().size());
- if (materialName.empty())
- {
- return;
- }
- // Make sure the material name is lowercased before checking the path in the Asset Cache
- AZStd::to_lower(materialName);
- // If a material has a <script> element we'll treat the name as a path and name to an O3DE material.
- // For example, "Gazebo/Wood" will look for a product material in "<cache>/gazebo/wood.azmaterial"
- AZ::IO::Path materialProductPath(materialName);
- materialProductPath.ReplaceExtension(".azmaterial");
- // Try getting an asset ID for the given name.
- constexpr bool AutoGenerateId = false;
- AZ::Data::AssetId assetId;
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(
- assetId,
- &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
- materialProductPath.String().c_str(),
- azrtti_typeid<AZ::RPI::MaterialAsset>(),
- AutoGenerateId);
- // No asset was found, we can't convert the material script.
- if (!assetId.IsValid())
- {
- AZ_Warning("AddMaterial", false, "Failed to find product material for %s", materialProductPath.c_str());
- return;
- }
- // The asset was found, so replace all the material assets in the given material assignment map.
- AZ::Data::Asset<AZ::RPI::MaterialAsset> materialAsset(assetId, azrtti_typeid<AZ::RPI::MaterialAsset>());
- for (auto& [id, override] : overrides)
- {
- if (id == AZ::Render::DefaultMaterialAssignmentId)
- {
- override.m_defaultMaterialAsset = materialAsset;
- }
- else
- {
- override.m_materialAsset = materialAsset;
- }
- }
- AZ_Info("AddMaterial", "Added product material %s\n", materialProductPath.c_str());
- }
- static void OverrideMaterialPbrSettings(
- const sdf::Material* material,
- const AZStd::shared_ptr<Utils::UrdfAssetMap>& assetMapping,
- AZ::Render::MaterialAssignmentMap& overrides)
- {
- if (auto pbr = material->PbrMaterial(); pbr)
- {
- // Start by trying to get the Metal workflow, since this is the workflow that O3DE uses.
- auto pbrWorkflow = pbr->Workflow(sdf::PbrWorkflowType::METAL);
- if (!pbrWorkflow)
- {
- // If the Metal workflow doesn't exist, try to get the Specular workflow.
- // Even though O3DE uses a Metal workflow, the vast majority of the Specular workflow data can still be used.
- // It's only the specular/glossiness maps that won't be converted.
- pbrWorkflow = pbr->Workflow(sdf::PbrWorkflowType::SPECULAR);
- if (!pbrWorkflow)
- {
- AZ_Error(
- "AddMaterial",
- false,
- "Material has a PBR definition, but it is neither a Metal nor a Specular workflow. Cannot convert.");
- return;
- }
- }
- for (auto& [id, materialAssignment] : overrides)
- {
- auto GetImageAssetIdFromPath = [&assetMapping](const std::string& uri) -> AZ::Data::AssetId
- {
- AZ::Data::AssetId assetId;
- const auto asset = PrefabMakerUtils::GetAssetFromPath(*assetMapping, uri);
- AZ_Warning("AddVisual", asset, "There is no source image asset for %s.", uri.c_str());
- if (asset)
- {
- assetId = Utils::GetImageProductAssetId(asset->m_sourceGuid);
- AZ_Warning(
- "AddVisual",
- assetId.IsValid(),
- "There is no product image asset for %s.",
- asset->m_sourceAssetRelativePath.c_str());
- }
- return assetId;
- };
- if (auto texture = pbrWorkflow->AlbedoMap(); !texture.empty())
- {
- materialAssignment.m_propertyOverrides.emplace(
- AZ::Name("baseColor.textureMap"), AZStd::any(GetImageAssetIdFromPath(texture)));
- }
- if (auto texture = pbrWorkflow->NormalMap(); !texture.empty())
- {
- materialAssignment.m_propertyOverrides.emplace(
- AZ::Name("normal.textureMap"), AZStd::any(GetImageAssetIdFromPath(texture)));
- }
- if (auto texture = pbrWorkflow->AmbientOcclusionMap(); !texture.empty())
- {
- materialAssignment.m_propertyOverrides.emplace(
- AZ::Name("occlusion.diffuseTextureMap"), AZStd::any(GetImageAssetIdFromPath(texture)));
- }
- if (auto texture = pbrWorkflow->EmissiveMap(); !texture.empty())
- {
- materialAssignment.m_propertyOverrides.emplace(AZ::Name("emissive.enable"), AZStd::any(true));
- materialAssignment.m_propertyOverrides.emplace(
- AZ::Name("emissive.textureMap"), AZStd::any(GetImageAssetIdFromPath(texture)));
- }
- if (pbrWorkflow->Type() == sdf::PbrWorkflowType::METAL)
- {
- if (auto texture = pbrWorkflow->RoughnessMap(); !texture.empty())
- {
- materialAssignment.m_propertyOverrides.emplace(
- AZ::Name("roughness.textureMap"), AZStd::any(GetImageAssetIdFromPath(texture)));
- }
- if (auto texture = pbrWorkflow->MetalnessMap(); !texture.empty())
- {
- materialAssignment.m_propertyOverrides.emplace(
- AZ::Name("metallic.textureMap"), AZStd::any(GetImageAssetIdFromPath(texture)));
- }
- if (pbrWorkflow->Element()->HasElement("roughness"))
- {
- materialAssignment.m_propertyOverrides.emplace(
- AZ::Name("roughness.factor"), AZStd::any(static_cast<float>(pbrWorkflow->Roughness())));
- }
- if (pbrWorkflow->Element()->HasElement("metalness"))
- {
- materialAssignment.m_propertyOverrides.emplace(
- AZ::Name("metallic.factor"), AZStd::any(static_cast<float>(pbrWorkflow->Metalness())));
- }
- }
- else
- {
- AZ_Warning(
- "AddMaterial",
- pbrWorkflow->GlossinessMap().empty(),
- "PBR material has a Glossiness map (%s), which is a Specular PBR workflow, not a Metal PBR workflow. It will not "
- "be converted.",
- pbrWorkflow->GlossinessMap().c_str());
- AZ_Warning(
- "AddMaterial",
- pbrWorkflow->SpecularMap().empty(),
- "PBR material has a Specular map (%s), which is a Specular PBR workflow, not a Metal PBR workflow. It will not be "
- "converted.",
- pbrWorkflow->SpecularMap().c_str());
- }
- }
- }
- }
- static void OverrideMaterialBaseColor(const sdf::Material* material, AZ::Render::MaterialAssignmentMap& overrides)
- {
- // Base Color: Try to use the diffuse color if the material has one, or fall back to using the ambient color as a backup option.
- if (material->Element()->HasElement("diffuse") || material->Element()->HasElement("ambient"))
- {
- // Get the material's diffuse color as the preferred option for the PBR material base color.
- // If a diffuse color didn't exist but ambient does, try to use that instead as the base color.
- // It will likely be too dark, but that's still probably better than not setting the color at all.
- // Convert from gamma to linear to try and account for the different color spaces between phong and PBR rendering.
- const auto materialColor = material->Element()->HasElement("diffuse") ? material->Diffuse() : material->Ambient();
- const AZ::Color baseColor = URDF::TypeConversions::ConvertColor(materialColor).GammaToLinear();
- for (auto& [id, materialAssignment] : overrides)
- {
- materialAssignment.m_propertyOverrides.emplace(AZ::Name("baseColor.color"), AZStd::any(baseColor));
- }
- }
- }
- static void OverrideMaterialTransparency(const sdf::Visual* visual, AZ::Render::MaterialAssignmentMap& overrides)
- {
- // Opacity: Use visual->transparency to set the material's opacity.
- if (visual->Element()->HasElement("transparency"))
- {
- const auto transparency = visual->Transparency();
- if (transparency > 0.0f)
- {
- // Override the material properties for every material used by the model.
- for (auto& [id, materialAssignment] : overrides)
- {
- materialAssignment.m_propertyOverrides.emplace(AZ::Name("opacity.mode"), AZStd::any(AZStd::string("Blended")));
- materialAssignment.m_propertyOverrides.emplace(AZ::Name("opacity.alphaSource"), AZStd::any(AZStd::string("None")));
- materialAssignment.m_propertyOverrides.emplace(AZ::Name("opacity.factor"), AZStd::any(1.0f - transparency));
- }
- }
- }
- }
- static void OverrideMaterialEmissiveSettings(const sdf::Material* material, AZ::Render::MaterialAssignmentMap& overrides)
- {
- // Emissive: If an emissive color has been specified, enable emissive on the material and set the emissive color to the provided
- // one.
- if (material->Element()->HasElement("emissive"))
- {
- // Get the color and convert from gamma to linear to try and account for the different color spaces between phong and PBR
- // rendering.
- const auto materialColor = material->Emissive();
- const AZ::Color emissiveColor = URDF::TypeConversions::ConvertColor(materialColor).GammaToLinear();
- // It seems to be fairly common to have an emissive entry of black, which isn't emissive at all.
- // Only enable the emissive color if it's a non-black value.
- if ((emissiveColor.GetR() > 0.0f) || (emissiveColor.GetG() > 0.0f) || (emissiveColor.GetB() > 0.0f))
- {
- for (auto& [id, materialAssignment] : overrides)
- {
- materialAssignment.m_propertyOverrides.emplace(AZ::Name("emissive.enable"), AZStd::any(true));
- materialAssignment.m_propertyOverrides.emplace(AZ::Name("emissive.color"), AZStd::any(emissiveColor));
- // The URDF/SDF file doesn't specify an emissive intensity, just a color.
- // We're arbitrarily using a value slightly higher than the default emissive intensity.
- // This value was picked based on observations of emissive color behaviors in Gazebo.
- // This intensity mostly preserves the color (though it lightens it a little) and
- // potentially adds a little bit of lighting to the scene if Bloom or Diffuse Probe Grid also exist in the world.
- materialAssignment.m_propertyOverrides.emplace(AZ::Name("emissive.intensity"), AZStd::any(5.5f));
- }
- }
- }
- }
- static void OverrideMaterialRoughness(const sdf::Material* material, AZ::Render::MaterialAssignmentMap& overrides)
- {
- // Metallic/Roughness: Try to use the shininess value for roughness if we have one, otherwise fall back to using the specular
- // brightness.
- if (material->Element()->HasElement("shininess") || material->Element()->HasElement("specular"))
- {
- float shininess = 0.0f;
- float roughness = 0.0f;
- if (material->Element()->HasElement("shininess"))
- {
- // If we have a shininess value, we'll use it to set both metallic and roughness.
- // The shinier it is, the more metallic and less rough we'll make the result.
- shininess = material->Shininess();
- roughness = 1.0f - shininess;
- }
- else
- {
- // We don't have shininess, so use the specular color to estimate metallic/roughness values.
- // Convert the specular color into a brightness value. To get the brightness, we'll use the average of the three
- // color values. This isn't the most perceptually accurate choice, but specular brightness isn't the same as roughness
- // anyways, so it's hard to say what perceptual brightness model would cause any better or worse results here.
- // Another possibility to consider would be taking the max of the RGB values.
- const auto materialColor = material->Specular();
- const AZ::Color specularColor = URDF::TypeConversions::ConvertColor(materialColor);
- const float specularBrightness = (specularColor.GetR() + specularColor.GetG() + specularColor.GetB()) / 3.0f;
- roughness = 1.0f - specularBrightness;
- // Since specular color doesn't really speak to shininess, we'll arbitrarily scale down the specular brightness to
- // 1/4 of the total brightness to modulate the metallic reflectiveness a little, but not too much. Without this scaling,
- // a white specular color would always become fully metallic, perfectly smooth, and therefore fully reflective.
- // With the scaling, a white specular color will be perfectly smooth but only 25% metallic, so it will have some
- // reflectivity but not a lot.
- shininess = specularBrightness * 0.25f;
- }
- for (auto& [id, materialAssignment] : overrides)
- {
- materialAssignment.m_propertyOverrides.emplace(AZ::Name("metallic.factor"), AZStd::any(shininess));
- materialAssignment.m_propertyOverrides.emplace(AZ::Name("roughness.factor"), AZStd::any(roughness));
- }
- }
- }
- static void OverrideMaterialDoubleSided(const sdf::Material* material, AZ::Render::MaterialAssignmentMap& overrides)
- {
- // DoubleSided: The double_sided element converts directly over to the O3DE doubleSided material attribute.
- if (material->Element()->HasElement("double_sided"))
- {
- // The default material property value is one-sided, so only override the value if it should be double-sided.
- if (material->DoubleSided())
- {
- for (auto& [id, materialAssignment] : overrides)
- {
- materialAssignment.m_propertyOverrides.emplace(AZ::Name("general.doubleSided"), AZStd::any(true));
- }
- }
- }
- }
- void VisualsMaker::AddMaterialForVisual(const sdf::Visual* visual, AZ::EntityId entityId, const AZ::Data::AssetId& assetId) const
- {
- auto material = visual->Material();
- if (!material)
- {
- // Material is optional, it might not appear on all visuals.
- return;
- }
- AZ_Assert(material->Element(), "Material has data but no Element pointer. Something unexpected has happened with the SDF parsing.");
- // Conversions from <material> in the file to O3DE are extremely imprecise because the data is going from a Phong model to PBR,
- // and there are no direct translations from one type of lighting model to the other. All of the conversions will create some
- // rough approximations of the source lighting data, but should hopefully provide a reasonable starting point for tuning the look.
- // Also, URDF/SDF files don't have a concept of overriding specific materials, so every material override generated
- // below will get applied to *all* materials for a mesh file.
- // First, force the model asset to get loaded into memory before adding the material component.
- // This is required so that we can get the default material map that will be used to override properties for each material.
- auto modelAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::ModelAsset>(assetId, AZ::Data::AssetLoadBehavior::Default);
- modelAsset.BlockUntilLoadComplete();
- AZ_Error(
- "AddMaterial",
- modelAsset.IsReady(),
- "Trying to create materials for a model that couldn't load. The generated material overrides may not work correctly.");
- // Initialize the material component configuration to contain all of the material mappings from the model.
- AZ::Render::MaterialComponentConfig config;
- config.m_materials = AZ::Render::GetDefaultMaterialMapFromModelAsset(modelAsset);
- // Try to override all of the various material settings based on what's contained in the <material> and <visual> elements in the
- // source file.
- OverrideScriptMaterial(material, config.m_materials);
- OverrideMaterialPbrSettings(material, m_urdfAssetsMapping, config.m_materials);
- OverrideMaterialBaseColor(material, config.m_materials);
- OverrideMaterialTransparency(visual, config.m_materials);
- OverrideMaterialEmissiveSettings(material, config.m_materials);
- OverrideMaterialRoughness(material, config.m_materials);
- OverrideMaterialDoubleSided(material, config.m_materials);
- // All the material overrides are in place, so get the entity, add the material component, and set its configuration to use the
- // material overrides.
- AZ::Entity* entity = AzToolsFramework::GetEntityById(entityId);
- AZ_Assert(entity, "Entity ID for visual %s couldn't be found.", visual->Name().c_str());
- auto component = entity->CreateComponent(AZ::Render::EditorMaterialComponentTypeId);
- component->SetConfiguration(config);
- }
- } // namespace ROS2RobotImporter
|