Skip to content

Commit 36cfd3e

Browse files
committed
Fixed URDFPrefabMaker to also create entities or SDF models with no direct links
This is needed for the scenario of importing an SDF world with a nested model. The nested models needed to be parented to its parent model using the Transform hierarchy to ensure correct placement in the world. Signed-off-by: lumberyard-employee-dm <[email protected]>
1 parent 98b471f commit 36cfd3e

File tree

1 file changed

+91
-86
lines changed

1 file changed

+91
-86
lines changed

Gems/ROS2/Code/Source/RobotImporter/URDF/URDFPrefabMaker.cpp

Lines changed: 91 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -129,33 +129,111 @@ namespace ROS2
129129
};
130130
LinksMapper linksMapper;
131131

132-
auto GetAllLinksFromModel = [&linksMapper](const sdf::Model& model, const Utils::ModelStack&) -> Utils::VisitModelResponse
132+
struct ModelMapper
133+
{
134+
struct NestedModelToAttachedModel
135+
{
136+
AZStd::string m_fullyQualifiedName;
137+
const sdf::Model* m_nestedModel;
138+
const sdf::Model* m_attachedModel;
139+
};
140+
// this is a unique ordered vector
141+
AZStd::vector<NestedModelToAttachedModel> m_models;
142+
};
143+
ModelMapper modelMapper;
144+
145+
auto GetAllLinksAndSetModelHierarchy = [&linksMapper, &modelMapper](const sdf::Model& model, const Utils::ModelStack& modelStack) -> Utils::VisitModelResponse
133146
{
134147
// As the VisitModels function visits nested models by default, gatherNestedModelLinks is set to false
135148
constexpr bool gatherNestedModelLinks = false;
136149
auto linksForModel = Utils::GetAllLinks(model, gatherNestedModelLinks);
137150
for (const auto& [fullyQualifiedName, link] : linksForModel)
138151
{
139152
// Push back the mapping of link to attached model into the ordered vector
140-
LinksMapper::LinkToAttachedModel linkToAttachedModel{ AZStd::string(fullyQualifiedName.c_str(), fullyQualifiedName.size()),
141-
link,
142-
&model };
153+
AZStd::string fullLinkName(fullyQualifiedName.c_str(), fullyQualifiedName.size());
154+
LinksMapper::LinkToAttachedModel linkToAttachedModel{ AZStd::move(fullLinkName), link, &model };
143155
linksMapper.m_links.push_back(AZStd::move(linkToAttachedModel));
144156
}
157+
158+
// Use the model stack to create a mapping from the current model
159+
// to the any model it is attached or nullptr if the model is at the top level SDF Root
160+
std::string stdFullModelName;
161+
for (const sdf::Model& ancestorModel : modelStack)
162+
{
163+
stdFullModelName = sdf::JoinName(stdFullModelName, ancestorModel.Name());
164+
}
165+
stdFullModelName = sdf::JoinName(stdFullModelName, model.Name());
166+
AZStd::string fullModelName(stdFullModelName.c_str(), stdFullModelName.size());
167+
ModelMapper::NestedModelToAttachedModel nestedModelToAttachedModel;
168+
nestedModelToAttachedModel.m_fullyQualifiedName = AZStd::move(fullModelName);
169+
nestedModelToAttachedModel.m_nestedModel = &model;
170+
nestedModelToAttachedModel.m_attachedModel = !modelStack.empty() ? &modelStack.back().get() : nullptr;
171+
modelMapper.m_models.push_back(AZStd::move(nestedModelToAttachedModel));
172+
145173
return Utils::VisitModelResponse::VisitNestedAndSiblings;
146174
};
147175

148-
// Gather all links from all the models in the SDF
149-
Utils::VisitModels(*m_root, GetAllLinksFromModel, visitNestedModels);
176+
// Gather all links and direct nested from all the models in the SDF
177+
Utils::VisitModels(*m_root, GetAllLinksAndSetModelHierarchy, visitNestedModels);
150178

151179
// Build up a list of all entities created as a part of processing the file.
152180
AZStd::vector<AZ::EntityId> createdEntities;
153-
AZStd::unordered_map<const sdf::Link*, AzToolsFramework::Prefab::PrefabEntityResult> createdLinks;
154181
AZStd::unordered_map<const sdf::Model*, AzToolsFramework::Prefab::PrefabEntityResult> createdModels;
182+
183+
AZStd::unordered_map<const sdf::Link*, AzToolsFramework::Prefab::PrefabEntityResult> createdLinks;
155184
AZStd::unordered_map<AZStd::string, const sdf::Link*> links;
156-
for ([[maybe_unused]] const auto& [fullLinkName, linkPtr, attachedModel] : linksMapper.m_links)
185+
186+
// Create an entity for each model
187+
for ([[maybe_unused]] const auto& [fullModelName, modelPtr, _] : modelMapper.m_models)
188+
{
189+
// Create entities for each model in the SDF
190+
if (AzToolsFramework::Prefab::PrefabEntityResult createModelEntityResult = CreateEntityForModel(*modelPtr);
191+
createModelEntityResult)
192+
{
193+
AZ::EntityId createdModelEntityId = createModelEntityResult.GetValue();
194+
// Add the model entity to the created entity list so that it gets added to the prefab
195+
createdEntities.emplace_back(createdModelEntityId);
196+
197+
std::string modelName = modelPtr->Name();
198+
AZStd::string azModelName(modelName.c_str(), modelName.size());
199+
AZStd::lock_guard<AZStd::mutex> lck(m_statusLock);
200+
m_status.emplace(azModelName, AZStd::string::format("[model] created as: %s", createdModelEntityId.ToString().c_str()));
201+
createdModels.emplace(modelPtr, createModelEntityResult);
202+
}
203+
}
204+
205+
//! Setup the parent hierarchy for the nested models
206+
for ([[maybe_unused]] const auto& [_, modelPtr, parentModelPtr] : modelMapper.m_models)
207+
{
208+
// If there is no parent model, then the model would be at the top level of the hiearachy
209+
if (parentModelPtr == nullptr || modelPtr == nullptr)
210+
{
211+
continue;
212+
}
213+
// Create entities for each model in the SDF
214+
AZ::EntityId modelEntityId;
215+
if (auto modelIt = createdModels.find(modelPtr); modelIt != createdModels.end() && modelIt->second)
216+
{
217+
modelEntityId = modelIt->second.GetValue();
218+
}
219+
220+
AZ::EntityId parentModelEntityId;
221+
if (auto parentModelIt = createdModels.find(parentModelPtr); parentModelIt != createdModels.end() && parentModelIt->second)
222+
{
223+
parentModelEntityId = parentModelIt->second.GetValue();
224+
}
225+
226+
// If the both the parent model and current model entity exist
227+
// set the current model transform component parent to the parent model
228+
if (parentModelEntityId.IsValid() && modelEntityId.IsValid())
229+
{
230+
PrefabMakerUtils::SetEntityParent(modelEntityId, parentModelEntityId);
231+
}
232+
}
233+
234+
// Create an entity for each link and set the parent to be the model entity where the link is attached
235+
for ([[maybe_unused]] const auto& [_, linkPtr, attachedModel] : linksMapper.m_links)
157236
{
158-
// Create entities for the model containing the link
159237
AZ::EntityId modelEntityId;
160238
if (attachedModel != nullptr)
161239
{
@@ -166,26 +244,7 @@ namespace ROS2
166244
modelEntityId = modelIt->second.GetValue();
167245
}
168246
}
169-
else
170-
{
171-
if (AzToolsFramework::Prefab::PrefabEntityResult createModelEntityResult = CreateEntityForModel(*attachedModel);
172-
createModelEntityResult)
173-
{
174-
modelEntityId = createModelEntityResult.GetValue();
175-
// Add the model entity to the created entity list
176-
// so that it gets added to the prefab
177-
createdEntities.emplace_back(modelEntityId);
178-
179-
std::string modelName = attachedModel->Name();
180-
AZStd::string azModelName(modelName.c_str(), modelName.size());
181-
AZStd::lock_guard<AZStd::mutex> lck(m_statusLock);
182-
m_status.emplace(azModelName, AZStd::string::format("created as: %s", modelEntityId.ToString().c_str()));
183-
createdModels.emplace(attachedModel, createModelEntityResult);
184-
}
185-
}
186-
187247
}
188-
189248
// Add all link as children of their attached model entity by default
190249
createdLinks[linkPtr] = AddEntitiesForLink(*linkPtr, attachedModel, modelEntityId, createdEntities);
191250
}
@@ -202,11 +261,11 @@ namespace ROS2
202261
AZStd::lock_guard<AZStd::mutex> lck(m_statusLock);
203262
if (result.IsSuccess())
204263
{
205-
m_status.emplace(azLinkName, AZStd::string::format("created as: %s", result.GetValue().ToString().c_str()));
264+
m_status.emplace(azLinkName, AZStd::string::format("[link] created as: %s", result.GetValue().ToString().c_str()));
206265
}
207266
else
208267
{
209-
m_status.emplace(azLinkName, AZStd::string::format("failed : %s", result.GetError().c_str()));
268+
m_status.emplace(azLinkName, AZStd::string::format("[link] failed : %s", result.GetError().c_str()));
210269
}
211270
}
212271

@@ -390,7 +449,7 @@ namespace ROS2
390449
AZStd::lock_guard<AZStd::mutex> lck(m_statusLock);
391450
auto result = m_jointsMaker.AddJointComponent(jointPtr, childEntity.GetValue(), leadEntity.GetValue());
392451
m_status.emplace(
393-
azJointName, AZStd::string::format(" %s: %llu", result.IsSuccess() ? "created as" : "failed", result.GetValue()));
452+
azJointName, AZStd::string::format("[joint] %s: %llu", result.IsSuccess() ? "created as" : "failed", result.GetValue()));
394453
}
395454
else
396455
{
@@ -508,63 +567,9 @@ namespace ROS2
508567
if (auto* transformComponent = entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
509568
transformComponent != nullptr)
510569
{
511-
gz::math::Pose3d modelPose;
512-
if (sdf::Errors poseResolveErrors = model.SemanticPose().Resolve(modelPose); !poseResolveErrors.empty())
513-
{
514-
AZStd::string poseErrorMessages = Utils::JoinSdfErrorsToString(poseResolveErrors);
515-
516-
auto poseErrorsForModel = AZStd::string::format(
517-
R"(Unable to resolve semantic pose for model %s. Creation of Model entity has failed. Errors: "%s")",
518-
model.Name().c_str(),
519-
poseErrorMessages.c_str());
520-
521-
return AZ::Failure(poseErrorsForModel);
522-
}
523-
570+
gz::math::Pose3d modelPose = model.RawPose();
524571
AZ::Transform modelTransform = URDF::TypeConversions::ConvertPose(modelPose);
525572

526-
// If the model is nested below another model, check if it has a placement frame
527-
if (const sdf::Model* parentModel = Utils::GetModelContainingModel(*m_root, model); parentModel != nullptr)
528-
{
529-
if (const sdf::Frame* modelPlacementFrame = parentModel->FrameByName(model.PlacementFrameName()); modelPlacementFrame != nullptr)
530-
{
531-
gz::math::Pose3d placementFramePose;
532-
if (sdf::Errors poseResolveErrors = modelPlacementFrame->SemanticPose().Resolve(placementFramePose);
533-
!poseResolveErrors.empty())
534-
{
535-
AZStd::string poseErrorMessages = Utils::JoinSdfErrorsToString(poseResolveErrors);
536-
537-
auto poseErrorsForModel = AZStd::string::format(
538-
R"(Unable to resolve semantic pose for model %s parent model %s. Creation of Model entity has failed. Errors: "%s")",
539-
model.Name().c_str(),
540-
parentModel->Name().c_str(),
541-
poseErrorMessages.c_str());
542-
543-
return AZ::Failure(poseErrorsForModel);
544-
}
545-
546-
modelTransform = modelTransform * URDF::TypeConversions::ConvertPose(placementFramePose);
547-
}
548-
}
549-
550-
if (const sdf::Frame* implicitFrame = model.FrameByName("__model__"); implicitFrame != nullptr)
551-
{
552-
gz::math::Pose3d implicitFramePose;
553-
if (sdf::Errors poseResolveErrors = implicitFrame->SemanticPose().Resolve(implicitFramePose); !poseResolveErrors.empty())
554-
{
555-
AZStd::string poseErrorMessages = Utils::JoinSdfErrorsToString(poseResolveErrors);
556-
557-
auto poseErrorsForModel = AZStd::string::format(
558-
R"(Unable to resolve semantic pose for model's implicit frame %s. Creation of Model entity has failed. Errors: "%s")",
559-
implicitFrame->Name().c_str(),
560-
poseErrorMessages.c_str());
561-
562-
return AZ::Failure(poseErrorsForModel);
563-
}
564-
565-
modelTransform = modelTransform * URDF::TypeConversions::ConvertPose(implicitFramePose);
566-
}
567-
568573
transformComponent->SetWorldTM(AZStd::move(modelTransform));
569574
}
570575

0 commit comments

Comments
 (0)