Skip to content

Commit

Permalink
use PrimitiveAssets Gem to visualize primitives (o3de#485)
Browse files Browse the repository at this point in the history
Use PrimitiveAssets Gem to visualize primitives

---------

Signed-off-by: Jan Hanca <jan.hanca@robotec.ai>
  • Loading branch information
jhanca-robotecai authored and mbalfour-amzn committed Sep 7, 2023
1 parent fe57176 commit 553128d
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 88 deletions.
181 changes: 96 additions & 85 deletions Gems/ROS2/Code/Source/RobotImporter/URDF/VisualsMaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,11 @@
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
#include <LmbrCentral/Shape/CylinderShapeComponentBus.h>
#include <LmbrCentral/Shape/EditorShapeComponentBus.h>
#include <LmbrCentral/Shape/SphereShapeComponentBus.h>

namespace ROS2
{
VisualsMaker::VisualsMaker() = default;
VisualsMaker::VisualsMaker(
MaterialNameMap materials, const AZStd::shared_ptr<Utils::UrdfAssetMap>& urdfAssetsMapping)
VisualsMaker::VisualsMaker(MaterialNameMap materials, const AZStd::shared_ptr<Utils::UrdfAssetMap>& urdfAssetsMapping)
: m_materials(AZStd::move(materials))
, m_urdfAssetsMapping(urdfAssetsMapping)
{
Expand All @@ -54,7 +49,10 @@ namespace ROS2

for (uint64_t index = 0; index < link->VisualCount(); index++)
{
auto createdEntity = AddVisual(link->VisualByIndex(index), entityId, PrefabMakerUtils::MakeEntityName(link->Name().c_str(), typeString, nameSuffixIndex));
auto createdEntity = AddVisual(
link->VisualByIndex(index),
entityId,
PrefabMakerUtils::MakeEntityName(link->Name().c_str(), typeString, nameSuffixIndex));
if (createdEntity.IsValid())
{
createdEntities.emplace_back(createdEntity);
Expand Down Expand Up @@ -100,99 +98,83 @@ namespace ROS2
// Apply transform as per origin
PrefabMakerUtils::SetEntityTransformLocal(visual->RawPose(), entityId);

AZ::Entity* entity = AzToolsFramework::GetEntityById(entityId);
auto geometry = visual->Geom();
switch (geometry->Type())
{
case sdf::GeometryType::SPHERE:
{
auto sphereGeometry = geometry->SphereShape();
AZ_Assert(sphereGeometry, "geometry is not Sphere");
entity->CreateComponent(LmbrCentral::EditorSphereShapeComponentTypeId);
entity->Activate();
LmbrCentral::SphereShapeComponentRequestsBus::Event(
entityId, &LmbrCentral::SphereShapeComponentRequests::SetRadius, sphereGeometry->Radius());
LmbrCentral::EditorShapeComponentRequestsBus::Event(
entityId, &LmbrCentral::EditorShapeComponentRequests::SetVisibleInGame, true);
entity->Deactivate();
// 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.
AZ::Data::AssetId assetId;
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");
entity->CreateComponent(LmbrCentral::EditorCylinderShapeComponentTypeId);
entity->Activate();
LmbrCentral::CylinderShapeComponentRequestsBus::Event(
entityId, &LmbrCentral::CylinderShapeComponentRequests::SetRadius, cylinderGeometry->Radius());
LmbrCentral::CylinderShapeComponentRequestsBus::Event(
entityId, &LmbrCentral::CylinderShapeComponentRequests::SetHeight, cylinderGeometry->Length());
LmbrCentral::EditorShapeComponentRequestsBus::Event(
entityId, &LmbrCentral::EditorShapeComponentRequests::SetVisibleInGame, true);
entity->Deactivate();
// 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.
AZ::Data::AssetId assetId;
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");
entity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId);
AZ::Vector3 boxDimensions = URDF::TypeConversions::ConvertVector3(boxGeometry->Size());
entity->Activate();
LmbrCentral::BoxShapeComponentRequestsBus::Event(
entityId, &LmbrCentral::BoxShapeComponentRequests::SetBoxDimensions, boxDimensions);
LmbrCentral::EditorShapeComponentRequestsBus::Event(
entityId, &LmbrCentral::EditorShapeComponentRequests::SetVisibleInGame, true);
entity->Deactivate();
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.
AZ::Data::AssetId assetId;
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::Data::AssetId assetId = Utils::GetModelProductAssetId(asset->m_sourceGuid);
AZ_Warning("AddVisual", assetId.IsValid(), "There is no product asset for %s.", asset->m_sourceAssetRelativePath.c_str());

if (asset)
{
auto editorMeshComponent = entity->CreateComponent(AZ::Render::EditorMeshComponentTypeId);

// Prepare scale
AZ::Vector3 scaleVector = URDF::TypeConversions::ConvertVector3(meshGeometry->Scale());
bool isUniformScale =
AZ::IsClose(scaleVector.GetMaxElement(), scaleVector.GetMinElement(), AZ::Constants::FloatEpsilon);
if (!isUniformScale)
{
entity->CreateComponent<AzToolsFramework::Components::EditorNonUniformScaleComponent>();
}

if (editorMeshComponent)
{
auto editorBaseComponent = azrtti_cast<AzToolsFramework::Components::EditorComponentBase*>(editorMeshComponent);
AZ_Assert(editorBaseComponent, "EditorMeshComponent didn't derive from EditorComponentBase.");

AZ::Data::AssetId modelId = Utils::GetModelProductAssetId(asset->m_sourceGuid);
AZ_Warning(
"AddVisual",
modelId.IsValid(),
"There is no product asset for %s.",
asset->m_sourceAssetRelativePath.c_str());
editorBaseComponent->SetPrimaryAsset(modelId);
}

entity->Activate();

// Set scale, uniform or non-uniform
if (isUniformScale)
{
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalUniformScale, scaleVector.GetX());
}
else
{
AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::SetScale, scaleVector);
}
entity->Deactivate();
}
AddVisualAssetToEntity(entityId, assetId, scaleVector);
}
break;
default:
Expand All @@ -201,10 +183,48 @@ namespace ROS2
}
}

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);
auto editorMeshComponent = entity->CreateComponent(AZ::Render::EditorMeshComponentTypeId);

// Prepare scale
bool isUniformScale = AZ::IsClose(scale.GetMaxElement(), scale.GetMinElement(), AZ::Constants::FloatEpsilon);
if (!isUniformScale)
{
entity->CreateComponent<AzToolsFramework::Components::EditorNonUniformScaleComponent>();
}

if (editorMeshComponent)
{
auto editorBaseComponent = azrtti_cast<AzToolsFramework::Components::EditorComponentBase*>(editorMeshComponent);
AZ_Assert(editorBaseComponent, "EditorMeshComponent didn't derive from EditorComponentBase.");
editorBaseComponent->SetPrimaryAsset(assetId);
}

entity->Activate();

// Set scale, uniform or non-uniform
if (isUniformScale)
{
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalUniformScale, scale.GetX());
}
else
{
AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::SetScale, scale);
}
entity->Deactivate();
}

void VisualsMaker::AddMaterialForVisual(const sdf::Visual* visual, AZ::EntityId entityId) const
{
// URDF does not include information from <gazebo> tags with specific materials, diffuse, specular and emissive params
if (!visual->Material() || !visual->Geom())
if (!visual->Material())
{
// Material is optional, and it requires geometry
return;
Expand All @@ -218,21 +238,12 @@ namespace ROS2
const AZStd::string azMaterialName{ materialName.c_str(), materialName.size() };

// If present in map, take map color definition as priority, otherwise apply local node definition
const auto materialColorUrdf = m_materials.contains(azMaterialName) ? m_materials.at(azMaterialName)->Diffuse() : visual->Material()->Diffuse();

const auto materialColorUrdf =
m_materials.contains(azMaterialName) ? m_materials.at(azMaterialName)->Diffuse() : visual->Material()->Diffuse();
const AZ::Color materialColor = URDF::TypeConversions::ConvertColor(materialColorUrdf);
bool isPrimitive = visual->Geom()->Type() != sdf::GeometryType::MESH;
if (isPrimitive)
{ // For primitives, set the color in the shape component
entity->Activate();
LmbrCentral::EditorShapeComponentRequestsBus::Event(
entityId, &LmbrCentral::EditorShapeComponentRequests::SetShapeColor, materialColor);
entity->Deactivate();
return;
}

entity->CreateComponent(AZ::Render::EditorMaterialComponentTypeId);
AZ_Printf("AddVisual", "Setting color for material %s\n", azMaterialName.c_str());
AZ_Trace("AddVisual", "Setting color for material %s\n", azMaterialName.c_str());
entity->Activate();
AZ::Render::MaterialComponentRequestBus::Event(
entityId,
Expand Down
6 changes: 3 additions & 3 deletions Gems/ROS2/Code/Source/RobotImporter/URDF/VisualsMaker.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "UrdfParser.h"
#include <AzCore/Component/EntityId.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <RobotImporter/Utils/SourceAssetsStorage.h>
Expand All @@ -24,9 +25,7 @@ namespace ROS2
using MaterialNameMap = AZStd::unordered_map<AZStd::string, const sdf::Material*>;

VisualsMaker();
VisualsMaker(
MaterialNameMap materials,
const AZStd::shared_ptr<Utils::UrdfAssetMap>& urdfAssetsMapping);
VisualsMaker(MaterialNameMap materials, const AZStd::shared_ptr<Utils::UrdfAssetMap>& urdfAssetsMapping);

//! Add zero, one or many visual elements to a given entity (depending on link content).
//! Note that a sub-entity will be added to hold each visual (since they can have different transforms).
Expand All @@ -38,6 +37,7 @@ namespace ROS2
private:
AZ::EntityId AddVisual(const sdf::Visual* visual, AZ::EntityId entityId, const AZStd::string& generatedName) const;
void AddVisualToEntity(const sdf::Visual* visual, AZ::EntityId entityId) const;
void AddVisualAssetToEntity(AZ::EntityId entityId, const AZ::Data::AssetId& assetId, const AZ::Vector3& scale) const;
void AddMaterialForVisual(const sdf::Visual* visual, AZ::EntityId entityId) const;

MaterialNameMap m_materials;
Expand Down
2 changes: 2 additions & 0 deletions Gems/ROS2/gem.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
"Atom_Component_DebugCamera",
"CommonFeaturesAtom",
"PhysX",
"PrimitiveAssets",
"StartingPointInput"

],
"restricted": "ROS2",
"repo_uri": "https://raw.githubusercontent.com/o3de/o3de-extras/development",
Expand Down

0 comments on commit 553128d

Please sign in to comment.