Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use PrimitiveAssets Gem to visualize primitives #485

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
jhanca-robotecai marked this conversation as resolved.
Show resolved Hide resolved
{
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