|
| 1 | +/* |
| 2 | + * Copyright (C) 2022 Open Source Robotics Foundation |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + * |
| 16 | +*/ |
| 17 | + |
| 18 | +#include "sdf/usd/usd_parser/USDTransforms.hh" |
| 19 | + |
| 20 | +#include <optional> |
| 21 | +#include <utility> |
| 22 | + |
| 23 | +#include <ignition/math/Pose3.hh> |
| 24 | +#include <ignition/math/Vector3.hh> |
| 25 | + |
| 26 | +#include "sdf/usd/usd_parser/USDData.hh" |
| 27 | + |
| 28 | +namespace sdf |
| 29 | +{ |
| 30 | +// Inline bracket to help doxygen filtering. |
| 31 | +inline namespace SDF_VERSION_NAMESPACE { |
| 32 | +// |
| 33 | +namespace usd |
| 34 | +{ |
| 35 | + |
| 36 | +const char kXFormOpTranslate[] = {"xformOp:translate"}; |
| 37 | +const char kXFormOpOrient[] = {"xformOp:orient"}; |
| 38 | +const char kXFormOpTransform[] = {"xformOp:transform"}; |
| 39 | +const char kXFormOpScale[] = {"xformOp:scale"}; |
| 40 | +const char kXFormOpRotateXYZ[] = {"xformOp:rotateXYZ"}; |
| 41 | +const char kXFormOpRotateZYX[] = {"xformOp:rotateZYX"}; |
| 42 | + |
| 43 | +const char kGfVec3fString[] = {"GfVec3f"}; |
| 44 | +const char kGfVec3dString[] = {"GfVec3d"}; |
| 45 | +const char kGfQuatfString[] = {"GfQuatf"}; |
| 46 | +const char kGfQuatdString[] = {"GfQuatd"}; |
| 47 | + |
| 48 | +/// \brief Private altimeter data. |
| 49 | +class UDSTransforms::Implementation |
| 50 | +{ |
| 51 | + /// \brief Scale of the schema |
| 52 | + public: ignition::math::Vector3d scale{1, 1, 1}; |
| 53 | + |
| 54 | + /// \brief Rotation of the schema |
| 55 | + public: std::optional<ignition::math::Quaterniond> q = std::nullopt; |
| 56 | + |
| 57 | + /// \brief Translation of the schema |
| 58 | + public: ignition::math::Vector3d translate{0, 0, 0}; |
| 59 | +}; |
| 60 | + |
| 61 | +///////////////////////////////////////////////// |
| 62 | +UDSTransforms::UDSTransforms() |
| 63 | + : dataPtr(ignition::utils::MakeImpl<Implementation>()) |
| 64 | +{ |
| 65 | +} |
| 66 | + |
| 67 | +////////////////////////////////////////////////// |
| 68 | +const ignition::math::Vector3d UDSTransforms::Translation() const |
| 69 | +{ |
| 70 | + return this->dataPtr->translate; |
| 71 | +} |
| 72 | + |
| 73 | +////////////////////////////////////////////////// |
| 74 | +const ignition::math::Vector3d UDSTransforms::Scale() const |
| 75 | +{ |
| 76 | + return this->dataPtr->scale; |
| 77 | +} |
| 78 | + |
| 79 | +////////////////////////////////////////////////// |
| 80 | +const std::optional<ignition::math::Quaterniond> UDSTransforms::Rotation() const |
| 81 | +{ |
| 82 | + return this->dataPtr->q; |
| 83 | +} |
| 84 | + |
| 85 | +////////////////////////////////////////////////// |
| 86 | +void UDSTransforms::SetTranslation( |
| 87 | + const ignition::math::Vector3d &_translate) |
| 88 | +{ |
| 89 | + this->dataPtr->translate = _translate; |
| 90 | +} |
| 91 | + |
| 92 | +////////////////////////////////////////////////// |
| 93 | +void UDSTransforms::SetScale( |
| 94 | + const ignition::math::Vector3d &_scale) |
| 95 | +{ |
| 96 | + this->dataPtr->scale = _scale; |
| 97 | +} |
| 98 | + |
| 99 | +////////////////////////////////////////////////// |
| 100 | +void UDSTransforms::SetRotation( |
| 101 | + const ignition::math::Quaterniond &_q) |
| 102 | +{ |
| 103 | + this->dataPtr->q = _q; |
| 104 | +} |
| 105 | + |
| 106 | +////////////////////////////////////////////////// |
| 107 | +/// \brief This function will parse all the parent transforms of a prim. |
| 108 | +/// \param[in] _prim Initial prim to read the transform |
| 109 | +/// \param[in] _usdData USDData structure to get info about the prim, for |
| 110 | +/// example: metersperunit |
| 111 | +/// \param[out] _tfs A vector with all the transforms |
| 112 | +/// \param[out] _scale The scale of the prims |
| 113 | +/// \param[in] _schemaToStop Name of the prim where the loop will stop |
| 114 | +/// reading transforms |
| 115 | +void GetAllTransforms( |
| 116 | + const pxr::UsdPrim &_prim, |
| 117 | + const USDData &_usdData, |
| 118 | + std::vector<ignition::math::Pose3d> &_tfs, |
| 119 | + ignition::math::Vector3d &_scale, |
| 120 | + const std::string &_schemaToStop) |
| 121 | +{ |
| 122 | + pxr::UsdPrim parent = _prim; |
| 123 | + double metersPerUnit = 1.0; |
| 124 | + std::string upAxis = "Y"; |
| 125 | + |
| 126 | + // this assumes that there can only be one stage |
| 127 | + const auto stageData = _usdData.FindStage(parent.GetPath().GetName()); |
| 128 | + if (stageData.second) { |
| 129 | + metersPerUnit = stageData.second->MetersPerUnit(); |
| 130 | + upAxis = stageData.second->UpAxis(); |
| 131 | + } |
| 132 | + |
| 133 | + while (parent) |
| 134 | + { |
| 135 | + if (pxr::TfStringify(parent.GetPath()) == _schemaToStop) |
| 136 | + { |
| 137 | + return; |
| 138 | + } |
| 139 | + |
| 140 | + UDSTransforms t = ParseUSDTransform(parent); |
| 141 | + |
| 142 | + ignition::math::Pose3d pose; |
| 143 | + _scale *= t.Scale(); |
| 144 | + |
| 145 | + pose.Pos() = t.Translation() * metersPerUnit; |
| 146 | + // scaling is lost when we convert to pose, so we pre-scale the |
| 147 | + // translation to make them match the scaled values. |
| 148 | + if (!_tfs.empty()) { |
| 149 | + auto& child = _tfs.back(); |
| 150 | + child.Pos().Set( |
| 151 | + child.Pos().X() * t.Scale()[0], |
| 152 | + child.Pos().Y() * t.Scale()[1], |
| 153 | + child.Pos().Z() * t.Scale()[2]); |
| 154 | + } |
| 155 | + |
| 156 | + if (t.Rotation()) |
| 157 | + { |
| 158 | + pose.Rot() = t.Rotation().value(); |
| 159 | + } |
| 160 | + _tfs.push_back(pose); |
| 161 | + parent = parent.GetParent(); |
| 162 | + } |
| 163 | + |
| 164 | + if (upAxis == "Y") |
| 165 | + { |
| 166 | + // Add additional rotation to match with Z up Axis. |
| 167 | + // TODO(anyone) handle upAxis == "X". This is a case that is rarely |
| 168 | + // used by other renderers |
| 169 | + ignition::math::Pose3d poseUpAxis = ignition::math::Pose3d( |
| 170 | + ignition::math::Vector3d(0, 0, 0), |
| 171 | + ignition::math::Quaterniond(IGN_PI_2, 0, 0)); |
| 172 | + _tfs.push_back(poseUpAxis); |
| 173 | + } |
| 174 | +} |
| 175 | + |
| 176 | +////////////////////////////////////////////////// |
| 177 | +void GetTransform( |
| 178 | + const pxr::UsdPrim &_prim, |
| 179 | + const USDData &_usdData, |
| 180 | + ignition::math::Pose3d &_pose, |
| 181 | + ignition::math::Vector3d &_scale, |
| 182 | + const std::string &_schemaToStop) |
| 183 | +{ |
| 184 | + std::vector<ignition::math::Pose3d> tfs; |
| 185 | + GetAllTransforms(_prim, _usdData, tfs, _scale, _schemaToStop); |
| 186 | + for (auto & rt : tfs) |
| 187 | + { |
| 188 | + _pose = rt * _pose; |
| 189 | + } |
| 190 | +} |
| 191 | + |
| 192 | +////////////////////////////////////////////////// |
| 193 | +UDSTransforms ParseUSDTransform(const pxr::UsdPrim &_prim) |
| 194 | +{ |
| 195 | + auto variantGeom = pxr::UsdGeomGprim(_prim); |
| 196 | + auto transforms = variantGeom.GetXformOpOrderAttr(); |
| 197 | + |
| 198 | + pxr::GfVec3f scale(1, 1, 1); |
| 199 | + pxr::GfVec3f translate(0, 0, 0); |
| 200 | + pxr::GfQuatf rotationQuad(1, 0, 0, 0); |
| 201 | + |
| 202 | + UDSTransforms t; |
| 203 | + |
| 204 | + pxr::VtTokenArray xformOpOrder; |
| 205 | + transforms.Get(&xformOpOrder); |
| 206 | + for (const auto &op : xformOpOrder) |
| 207 | + { |
| 208 | + if (op == kXFormOpScale) |
| 209 | + { |
| 210 | + auto attribute = _prim.GetAttribute(pxr::TfToken(kXFormOpScale)); |
| 211 | + if (attribute.GetTypeName().GetCPPTypeName() == kGfVec3fString) |
| 212 | + { |
| 213 | + attribute.Get(&scale); |
| 214 | + } |
| 215 | + else if (attribute.GetTypeName().GetCPPTypeName() == kGfVec3dString) |
| 216 | + { |
| 217 | + pxr::GfVec3d scaleTmp(1, 1, 1); |
| 218 | + attribute.Get(&scaleTmp); |
| 219 | + scale[0] = static_cast<float>(scaleTmp[0]); |
| 220 | + scale[1] = static_cast<float>(scaleTmp[1]); |
| 221 | + scale[2] = static_cast<float>(scaleTmp[2]); |
| 222 | + } |
| 223 | + t.SetScale(ignition::math::Vector3d(scale[0], scale[1], scale[2])); |
| 224 | + } |
| 225 | + else if (op == kXFormOpRotateZYX || op == kXFormOpRotateXYZ) |
| 226 | + { |
| 227 | + pxr::GfVec3f rotationEuler(0, 0, 0); |
| 228 | + pxr::UsdAttribute attribute; |
| 229 | + if (op == kXFormOpRotateZYX) |
| 230 | + { |
| 231 | + attribute = _prim.GetAttribute(pxr::TfToken(kXFormOpRotateZYX)); |
| 232 | + } |
| 233 | + else |
| 234 | + { |
| 235 | + attribute = _prim.GetAttribute(pxr::TfToken(kXFormOpRotateXYZ)); |
| 236 | + } |
| 237 | + if (attribute.GetTypeName().GetCPPTypeName() == kGfVec3fString) |
| 238 | + { |
| 239 | + attribute.Get(&rotationEuler); |
| 240 | + } |
| 241 | + else if (attribute.GetTypeName().GetCPPTypeName() == kGfVec3dString) |
| 242 | + { |
| 243 | + pxr::GfVec3d rotationEulerTmp(0, 0, 0); |
| 244 | + attribute.Get(&rotationEulerTmp); |
| 245 | + rotationEuler[0] = static_cast<float>(rotationEulerTmp[0]); |
| 246 | + rotationEuler[1] = static_cast<float>(rotationEulerTmp[1]); |
| 247 | + rotationEuler[2] = static_cast<float>(rotationEulerTmp[2]); |
| 248 | + } |
| 249 | + ignition::math::Quaterniond qX, qY, qZ; |
| 250 | + ignition::math::Angle angleX(IGN_DTOR(rotationEuler[0])); |
| 251 | + ignition::math::Angle angleY(IGN_DTOR(rotationEuler[1])); |
| 252 | + ignition::math::Angle angleZ(IGN_DTOR(rotationEuler[2])); |
| 253 | + qX = ignition::math::Quaterniond(angleX.Normalized().Radian(), 0, 0); |
| 254 | + qY = ignition::math::Quaterniond(0, angleY.Normalized().Radian(), 0); |
| 255 | + qZ = ignition::math::Quaterniond(0, 0, angleZ.Normalized().Radian()); |
| 256 | + |
| 257 | + // TODO(ahcorde) This part should be reviewed, revisit how rotateXYZ |
| 258 | + // and rotateZYX are handle. |
| 259 | + // Related issue https://github.com/ignitionrobotics/sdformat/issues/926 |
| 260 | + // if (op == kXFormOpRotateZYX) |
| 261 | + // { |
| 262 | + // std::swap(angleX, angleZ); |
| 263 | + // } |
| 264 | + t.SetRotation((qX * qY) * qZ); |
| 265 | + } |
| 266 | + else if (op == kXFormOpTranslate) |
| 267 | + { |
| 268 | + auto attribute = _prim.GetAttribute(pxr::TfToken(kXFormOpTranslate)); |
| 269 | + if (attribute.GetTypeName().GetCPPTypeName() == kGfVec3fString) |
| 270 | + { |
| 271 | + attribute.Get(&translate); |
| 272 | + } |
| 273 | + else if (attribute.GetTypeName().GetCPPTypeName() == kGfVec3dString) |
| 274 | + { |
| 275 | + pxr::GfVec3d translateTmp(0, 0, 0); |
| 276 | + attribute.Get(&translateTmp); |
| 277 | + translate[0] = static_cast<float>(translateTmp[0]); |
| 278 | + translate[1] = static_cast<float>(translateTmp[1]); |
| 279 | + translate[2] = static_cast<float>(translateTmp[2]); |
| 280 | + } |
| 281 | + t.SetTranslation(ignition::math::Vector3d( |
| 282 | + translate[0], |
| 283 | + translate[1], |
| 284 | + translate[2])); |
| 285 | + } |
| 286 | + else if (op == kXFormOpOrient) |
| 287 | + { |
| 288 | + auto attribute = _prim.GetAttribute(pxr::TfToken(kXFormOpOrient)); |
| 289 | + if (attribute.GetTypeName().GetCPPTypeName() == kGfQuatfString) |
| 290 | + { |
| 291 | + attribute.Get(&rotationQuad); |
| 292 | + } |
| 293 | + else if (attribute.GetTypeName().GetCPPTypeName() == kGfQuatdString) |
| 294 | + { |
| 295 | + pxr::GfQuatd rotationQuadTmp; |
| 296 | + attribute.Get(&rotationQuadTmp); |
| 297 | + rotationQuad.SetImaginary( |
| 298 | + rotationQuadTmp.GetImaginary()[0], |
| 299 | + rotationQuadTmp.GetImaginary()[1], |
| 300 | + rotationQuadTmp.GetImaginary()[2]); |
| 301 | + rotationQuad.SetReal(rotationQuadTmp.GetReal()); |
| 302 | + } |
| 303 | + ignition::math::Quaterniond q( |
| 304 | + rotationQuad.GetReal(), |
| 305 | + rotationQuad.GetImaginary()[0], |
| 306 | + rotationQuad.GetImaginary()[1], |
| 307 | + rotationQuad.GetImaginary()[2]); |
| 308 | + t.SetRotation(q); |
| 309 | + } |
| 310 | + |
| 311 | + if (op == kXFormOpTransform) |
| 312 | + { |
| 313 | + // TODO(koonpeng) Shear is lost (does sdformat support it?). |
| 314 | + pxr::GfMatrix4d transform; |
| 315 | + _prim.GetAttribute(pxr::TfToken(kXFormOpTransform)).Get(&transform); |
| 316 | + const auto rot = transform.RemoveScaleShear(); |
| 317 | + const auto scaleShear = transform * rot.GetInverse(); |
| 318 | + |
| 319 | + t.SetScale(ignition::math::Vector3d( |
| 320 | + scaleShear[0][0], |
| 321 | + scaleShear[1][1], |
| 322 | + scaleShear[2][2])); |
| 323 | + |
| 324 | + const auto rotQuat = rot.ExtractRotationQuat(); |
| 325 | + t.SetTranslation(ignition::math::Vector3d( |
| 326 | + transform[3][0], |
| 327 | + transform[3][1], |
| 328 | + transform[3][2])); |
| 329 | + ignition::math::Quaterniond q( |
| 330 | + rotQuat.GetReal(), |
| 331 | + rotQuat.GetImaginary()[0], |
| 332 | + rotQuat.GetImaginary()[1], |
| 333 | + rotQuat.GetImaginary()[2] |
| 334 | + ); |
| 335 | + t.SetRotation(q); |
| 336 | + } |
| 337 | + } |
| 338 | + return t; |
| 339 | +} |
| 340 | +} |
| 341 | +} |
| 342 | +} |
0 commit comments