VisualsMaker.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "RobotImporter/URDF/VisualsMaker.h"
  9. #include "RobotImporter/URDF/PrefabMakerUtils.h"
  10. #include "RobotImporter/Utils/TypeConversions.h"
  11. #include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
  12. #include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
  13. #include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
  14. #include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
  15. #include <AzCore/Component/NonUniformScaleBus.h>
  16. #include <AzCore/Component/TransformBus.h>
  17. #include <AzToolsFramework/Entity/EditorEntityHelpers.h>
  18. #include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
  19. #include <LmbrCentral/Shape/BoxShapeComponentBus.h>
  20. #include <LmbrCentral/Shape/CylinderShapeComponentBus.h>
  21. #include <LmbrCentral/Shape/EditorShapeComponentBus.h>
  22. #include <LmbrCentral/Shape/SphereShapeComponentBus.h>
  23. namespace ROS2
  24. {
  25. VisualsMaker::VisualsMaker(
  26. const std::map<std::string, urdf::MaterialSharedPtr>& materials, const AZStd::shared_ptr<Utils::UrdfAssetMap>& urdfAssetsMapping)
  27. : m_urdfAssetsMapping(urdfAssetsMapping)
  28. {
  29. AZStd::ranges::for_each(
  30. materials,
  31. [&](const auto& p)
  32. {
  33. m_materials[AZStd::string(p.first.c_str(), p.first.size())] = p.second;
  34. });
  35. }
  36. void VisualsMaker::AddVisuals(urdf::LinkSharedPtr link, AZ::EntityId entityId) const
  37. {
  38. const AZStd::string typeString = "visual";
  39. if (link->visual_array.size() < 1)
  40. { // one or zero visuals - element is used
  41. AddVisual(link->visual, entityId, PrefabMakerUtils::MakeEntityName(link->name.c_str(), typeString));
  42. return;
  43. }
  44. size_t nameSuffixIndex = 0; // For disambiguation when multiple unnamed visuals are present. The order does not matter here
  45. for (auto visual : link->visual_array)
  46. { // one or more visuals - array is used
  47. AddVisual(visual, entityId, PrefabMakerUtils::MakeEntityName(link->name.c_str(), typeString, nameSuffixIndex));
  48. nameSuffixIndex++;
  49. }
  50. }
  51. void VisualsMaker::AddVisual(urdf::VisualSharedPtr visual, AZ::EntityId entityId, const AZStd::string& generatedName) const
  52. {
  53. if (!visual)
  54. { // it is ok not to have a visual in a link
  55. return;
  56. }
  57. if (!visual->geometry)
  58. { // non-empty visual should have a geometry
  59. AZ_Warning("AddVisual", false, "No Geometry for a visual");
  60. return;
  61. }
  62. AZ_TracePrintf("AddVisual", "Processing visual for entity id:%s\n", entityId.ToString().c_str());
  63. // Use a name generated from the link unless specific name is defined for this visual
  64. const char* subEntityName = visual->name.empty() ? generatedName.c_str() : visual->name.c_str();
  65. // Since O3DE does not allow origin for visuals, we need to create a sub-entity and store visual there
  66. auto createEntityResult = PrefabMakerUtils::CreateEntity(entityId, subEntityName);
  67. if (!createEntityResult.IsSuccess())
  68. {
  69. AZ_Error("AddVisual", false, "Unable to create a sub-entity for visual element %s\n", subEntityName);
  70. return;
  71. }
  72. auto visualEntityId = createEntityResult.GetValue();
  73. AddVisualToEntity(visual, visualEntityId);
  74. AddMaterialForVisual(visual, visualEntityId);
  75. }
  76. void VisualsMaker::AddVisualToEntity(urdf::VisualSharedPtr visual, AZ::EntityId entityId) const
  77. {
  78. // Apply transform as per origin
  79. PrefabMakerUtils::SetEntityTransformLocal(visual->origin, entityId);
  80. AZ::Entity* entity = AzToolsFramework::GetEntityById(entityId);
  81. auto geometry = visual->geometry;
  82. switch (geometry->type)
  83. {
  84. case urdf::Geometry::SPHERE:
  85. {
  86. auto sphereGeometry = std::dynamic_pointer_cast<urdf::Sphere>(geometry);
  87. AZ_Assert(sphereGeometry, "geometry is not Sphere");
  88. entity->CreateComponent(LmbrCentral::EditorSphereShapeComponentTypeId);
  89. entity->Activate();
  90. LmbrCentral::SphereShapeComponentRequestsBus::Event(
  91. entityId, &LmbrCentral::SphereShapeComponentRequests::SetRadius, sphereGeometry->radius);
  92. entity->Deactivate();
  93. }
  94. break;
  95. case urdf::Geometry::CYLINDER:
  96. {
  97. auto cylinderGeometry = std::dynamic_pointer_cast<urdf::Cylinder>(geometry);
  98. AZ_Assert(cylinderGeometry, "geometry is not Cylinder");
  99. entity->CreateComponent(LmbrCentral::EditorCylinderShapeComponentTypeId);
  100. entity->Activate();
  101. LmbrCentral::CylinderShapeComponentRequestsBus::Event(
  102. entityId, &LmbrCentral::CylinderShapeComponentRequests::SetRadius, cylinderGeometry->radius);
  103. LmbrCentral::CylinderShapeComponentRequestsBus::Event(
  104. entityId, &LmbrCentral::CylinderShapeComponentRequests::SetHeight, cylinderGeometry->length);
  105. entity->Deactivate();
  106. }
  107. break;
  108. case urdf::Geometry::BOX:
  109. {
  110. auto boxGeometry = std::dynamic_pointer_cast<urdf::Box>(geometry);
  111. AZ_Assert(boxGeometry, "geometry is not Box");
  112. entity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId);
  113. AZ::Vector3 boxDimensions = URDF::TypeConversions::ConvertVector3(boxGeometry->dim);
  114. entity->Activate();
  115. LmbrCentral::BoxShapeComponentRequestsBus::Event(
  116. entityId, &LmbrCentral::BoxShapeComponentRequests::SetBoxDimensions, boxDimensions);
  117. entity->Deactivate();
  118. }
  119. break;
  120. case urdf::Geometry::MESH:
  121. {
  122. auto meshGeometry = std::dynamic_pointer_cast<urdf::Mesh>(geometry);
  123. AZ_Assert(meshGeometry, "geometry is not Mesh");
  124. const auto asset = PrefabMakerUtils::GetAssetFromPath(*m_urdfAssetsMapping, meshGeometry->filename);
  125. if (asset)
  126. {
  127. entity->CreateComponent(AZ::Render::EditorMeshComponentTypeId);
  128. // Prepare scale
  129. AZ::Vector3 scaleVector = URDF::TypeConversions::ConvertVector3(meshGeometry->scale);
  130. bool isUniformScale =
  131. AZ::IsClose(scaleVector.GetMaxElement(), scaleVector.GetMinElement(), AZ::Constants::FloatEpsilon);
  132. if (!isUniformScale)
  133. {
  134. entity->CreateComponent<AzToolsFramework::Components::EditorNonUniformScaleComponent>();
  135. }
  136. entity->Activate();
  137. // Set asset path
  138. AZ::Render::MeshComponentRequestBus::Event(
  139. entityId,
  140. &AZ::Render::MeshComponentRequestBus::Events::SetModelAssetPath,
  141. asset->m_sourceAssetRelativePath.c_str());
  142. // Set scale, uniform or non-uniform
  143. if (isUniformScale)
  144. {
  145. AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalUniformScale, scaleVector.GetX());
  146. }
  147. else
  148. {
  149. AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::SetScale, scaleVector);
  150. }
  151. entity->Deactivate();
  152. }
  153. }
  154. break;
  155. default:
  156. AZ_Warning("AddVisual", false, "Unsupported visual geometry type, %d", geometry->type);
  157. return;
  158. }
  159. }
  160. void VisualsMaker::AddMaterialForVisual(urdf::VisualSharedPtr visual, AZ::EntityId entityId) const
  161. {
  162. // URDF does not include information from <gazebo> tags with specific materials, diffuse, specular and emissive params
  163. if (!visual->material || !visual->geometry)
  164. {
  165. // Material is optional, and it requires geometry
  166. return;
  167. }
  168. AZ::Entity* entity = AzToolsFramework::GetEntityById(entityId);
  169. const AZStd::string material_name{ visual->material->name.c_str() };
  170. // If present in map, take map color definition as priority, otherwise apply local node definition
  171. const auto materialColorUrdf = m_materials.contains(material_name) ? m_materials.at(material_name)->color : visual->material->color;
  172. const AZ::Color materialColor = URDF::TypeConversions::ConvertColor(materialColorUrdf);
  173. bool isPrimitive = visual->geometry->type != urdf::Geometry::MESH;
  174. if (isPrimitive)
  175. { // For primitives, set the color in the shape component
  176. entity->Activate();
  177. LmbrCentral::EditorShapeComponentRequestsBus::Event(
  178. entityId, &LmbrCentral::EditorShapeComponentRequests::SetShapeColor, materialColor);
  179. entity->Deactivate();
  180. return;
  181. }
  182. entity->CreateComponent(AZ::Render::EditorMaterialComponentTypeId);
  183. AZ_Printf("AddVisual", "Setting color for material %s\n", visual->material->name.c_str());
  184. entity->Activate();
  185. AZ::Render::MaterialComponentRequestBus::Event(
  186. entityId,
  187. &AZ::Render::MaterialComponentRequestBus::Events::SetPropertyValue,
  188. AZ::Render::DefaultMaterialAssignmentId,
  189. "settings.color",
  190. AZStd::any(materialColor));
  191. entity->Deactivate();
  192. }
  193. } // namespace ROS2