VisualizationRobot.cpp
Go to the documentation of this file.
1 #include "VisualizationRobot.h"
2 
3 #include <VirtualRobot/SceneObject.h>
4 #include <VirtualRobot/Visualization/CoinVisualization/CoinVisualization.h>
5 #include <VirtualRobot/XML/RobotIO.h>
6 
12 
13 #include <Inventor/SbColor.h>
14 #include <Inventor/nodes/SoMaterial.h>
15 
16 namespace armarx::viz::coin
17 {
18  namespace
19  {
21  loadRobot(std::string const& project, std::string const& filename)
22  {
24 
25  if (filename.empty())
26  {
27  ARMARX_INFO << deactivateSpam() << "No filename provided for robot.";
28  return nullptr;
29  }
30 
31  std::string fullFilename;
32  // Suppress warnings when full path is specified (is discouraged, but may happen).
33  if (project.empty() && std::filesystem::path(filename).is_absolute())
34  {
36  << "You specified the absolute path to the robot file:"
37  << "\n\t'" << filename << "'"
38  << "\nConsider specifying the containing ArmarX package and relative "
39  "data path instead to "
40  << "improve portability to other systems.";
41  }
42  // We need to always check that the file is readable otherwise, VirtualRobot::RobotIO::loadRobot crashes
44  if (!ArmarXDataPath::SearchReadableFile(filename, fullFilename))
45  {
47  << "Unable to find readable file for name: " << filename;
48  return nullptr;
49  }
50 
51  try
52  {
53  // Always load full model. Visualization can choose between full and collision model
54  VirtualRobot::RobotIO::RobotDescription loadMode = VirtualRobot::RobotIO::eFull;
55  // This check is always true since it does not compare the flag to anything ==> if (true)
56  // Deactivated for now
57  // if (data::ModelDrawStyle::COLLISION)
58  // {
59  // loadMode = VirtualRobot::RobotIO::eCollisionModel;
60  // }
61  ARMARX_INFO << "Loading robot from " << fullFilename;
62  result = VirtualRobot::RobotIO::loadRobot(fullFilename, loadMode);
63  if (result)
64  {
65  result->setThreadsafe(false);
66  result->setPropagatingJointValuesEnabled(true);
67  }
68  else
69  {
70  ARMARX_WARNING << "Could not load robot from file: " << fullFilename
71  << "\nReason: loadRobot() returned nullptr";
72  }
73  }
74  catch (std::exception const& ex)
75  {
76  ARMARX_WARNING << "Could not load robot from file: " << fullFilename
77  << "\nReason: " << ex.what();
78  }
79 
80  return result;
81  }
82 
83  struct RobotInstancePool
84  {
85  std::string project;
86  std::string filename;
87  std::size_t usedInstances = 0;
88  std::vector<VirtualRobot::RobotPtr> robots;
89  };
90 
91  static std::vector<RobotInstancePool> robotCache;
92 
93  LoadedRobot
94  getRobotFromCache(std::string const& project, std::string const& filename)
95  {
96  // We can use a global variable, since this code is only executed in the GUI thread
97 
98  LoadedRobot result;
99  result.project = project;
100  result.filename = filename;
101 
102  for (RobotInstancePool& instancePool : robotCache)
103  {
104  if (instancePool.project == project && instancePool.filename == filename)
105  {
106  // There are two possibilities here:
107  if (instancePool.usedInstances < instancePool.robots.size())
108  {
109  // 1) We have still unused instances in the pool ==> Just return one
110  ARMARX_DEBUG << "Reusing robot instance from cache " << VAROUT(project)
111  << ", " << VAROUT(filename);
112  result.robot = instancePool.robots[instancePool.usedInstances];
113  instancePool.usedInstances += 1;
114  }
115 
116  else
117  {
118  // 2) We do not have unused instances in the pool ==> Clone one
119  ARMARX_INFO << "Cloning robot from cache " << VAROUT(project) << ", "
120  << VAROUT(filename);
121 
122  if (!instancePool.robots.empty())
123  {
124  VirtualRobot::RobotPtr const& robotToClone =
125  instancePool.robots.front();
126  float scaling = 1.0f;
127  bool preventCloningMeshesIfScalingIs1 = true;
128 
129  {
131  [](const armarx::Duration& duration) {
132  ARMARX_DEBUG << "Cloning took "
133  << duration.toSecondsDouble() << " seconds.";
134  });
135  result.robot = robotToClone->clone(
136  nullptr, scaling, preventCloningMeshesIfScalingIs1);
137  }
138 
139  // Insert the cloned robot into the instance pool
140  instancePool.robots.push_back(result.robot);
141  instancePool.usedInstances += 1;
142  }
143  else
144  {
145  ARMARX_WARNING << "Encountered empty robot instance pool while trying "
146  "to clone new instance"
147  << "\nRobot: " << VAROUT(project) << ", "
148  << VAROUT(filename)
149  << "\nUsed instances: " << instancePool.usedInstances
150  << "\nRobots: " << instancePool.robots.size();
151  }
152  }
153  return result;
154  }
155  }
156 
157  ARMARX_DEBUG << "Loading robot from file " << VAROUT(project) << ", "
158  << VAROUT(filename);
159  result.robot = loadRobot(project, filename);
160  if (result.robot)
161  {
162  RobotInstancePool& instancePool = robotCache.emplace_back();
163  instancePool.project = project;
164  instancePool.filename = filename;
165  instancePool.robots.push_back(result.robot);
166  instancePool.usedInstances = 1;
167  }
168  else
169  {
170  ARMARX_WARNING << deactivateSpam(5) << "Robot " << VAROUT(project) << ", "
171  << VAROUT(filename) << "could not be loaded!";
172  }
173  return result;
174  }
175  } // namespace
176 
178  {
179  for (RobotInstancePool& instancePool : robotCache)
180  {
181  if (instancePool.project == loaded.project && instancePool.filename == loaded.filename)
182  {
183  ARMARX_DEBUG << "Removing robot from chace " << VAROUT(loaded.project) << ", "
184  << VAROUT(loaded.filename);
185  std::vector<VirtualRobot::RobotPtr>& robots = instancePool.robots;
186  auto robotIter = std::find(robots.begin(), robots.end(), loaded.robot);
187  if (robotIter != robots.end())
188  {
189  // Do not erase the robot, but rather move it to the back
190  // We can later reuse the unused instances at the back of the vector
191  std::swap(*robotIter, robots.back());
192  if (instancePool.usedInstances > 0)
193  {
194  instancePool.usedInstances -= 1;
195  }
196  else
197  {
198  ARMARX_WARNING << "Expected there to be at least one used instance "
199  << "while trying to put robot instance back into the pool"
200  << "\nRobot: " << VAROUT(loaded.project) << ", "
202  << "\nUsed instances: " << instancePool.usedInstances;
203  }
204  }
205  }
206  }
207  }
208 
209  bool
211  {
212  IceUtil::Time time_start = IceUtil::Time::now();
213  (void)time_start;
214 
215  bool robotChanged =
216  loaded.project != element.project || loaded.filename != element.filename;
217  if (robotChanged)
218  {
219  // The robot file changed, so reload the robot
220  loaded = getRobotFromCache(element.project, element.filename);
221  }
222  if (!loaded.robot)
223  {
225  << "Robot will not visualized since it could not be loaded."
226  << "\nID: " << element.id << "\nProject: " << element.project
227  << "\nFilename: " << element.filename;
228  return true;
229  }
230  //IceUtil::Time time_load = IceUtil::Time::now();
231 
232  bool drawStyleChanged = loadedDrawStyle != element.drawStyle;
233  if (robotChanged || drawStyleChanged)
234  {
235  recreateVisualizationNodes(element.drawStyle);
236  loadedDrawStyle = element.drawStyle;
237  }
238  //IceUtil::Time time_style = IceUtil::Time::now();
239 
240  // Set pose, configuration and so on
241 
242  VirtualRobot::Robot& robot = *loaded.robot;
243 
244  // We do not need to update the global pose in the robot
245  // Since we only visualize the results, we can simply use the
246  // pose from the Element base class.
247 
248  // Eigen::Matrix4f pose = defrost(element.pose);
249  // robot.setGlobalPose(pose, false);
250 
251  // Check joint values for changes
252 
253  for (const auto& pair : element.jointValues)
254  {
255  std::string const& nodeName = pair.first;
256  float newJointValue = pair.second;
257  VirtualRobot::RobotNodePtr robotNode = robot.getRobotNode(nodeName);
258 
259  if (robotNode == nullptr)
260  {
261  continue;
262  }
263 
264  const float oldJointValue = robotNode->getJointValue();
265  const float diff = std::abs(newJointValue - oldJointValue);
266  const bool jointValuesChanged = diff > 0.001f;
267  if (jointValuesChanged)
268  {
269  // Only set the joint values if they changed
270  // This call causes internal updates to the visualization even if joint angles are the same!
271  robot.setJointValues(element.jointValues);
272  break;
273  }
274  }
275  //IceUtil::Time time_set = IceUtil::Time::now();
276 
277  if (loadedDrawStyle & data::ModelDrawStyle::OVERRIDE_COLOR)
278  {
279  if (loadedColor.r != element.color.r || loadedColor.g != element.color.g ||
280  loadedColor.b != element.color.b || loadedColor.a != element.color.a)
281  {
282  int numChildren = node->getNumChildren();
283  for (int i = 0; i < numChildren; i++)
284  {
285  SoSeparator* nodeSep = static_cast<SoSeparator*>(node->getChild(i));
286  // The first entry must be a SoMaterial (see recreateVisualizationNodes)
287  SoMaterial* m = dynamic_cast<SoMaterial*>(nodeSep->getChild(0));
288  if (!m)
289  {
290  ARMARX_WARNING << "Error at node with index: " << i;
291  continue;
292  }
293 
294  auto color = element.color;
295  const float conv = 1.0f / 255.0f;
296  float a = color.a * conv;
297  SbColor coinColor(conv * color.r, conv * color.g, conv * color.b);
298  m->diffuseColor = coinColor;
299  m->ambientColor = coinColor;
300  m->transparency = 1.0f - a;
301  m->setOverride(true);
302  }
303  loadedColor = element.color;
304  }
305  }
306  //IceUtil::Time time_color = IceUtil::Time::now();
307 
308  // Setting the joint angles takes > 0.5 ms! This is unexpected...
309  // ARMARX_INFO << "Total: " << (time_color - time_start).toMilliSecondsDouble()
310  // << "\nLoad: " << (time_load - time_start).toMilliSecondsDouble()
311  // << "\nStyle: " << (time_style - time_load).toMilliSecondsDouble()
312  // << "\nSet: " << (time_set - time_style).toMilliSecondsDouble()
313  // << "\nColor: " << (time_color - time_set).toMilliSecondsDouble();
314 
315  return true;
316  }
317 
318  void
320  {
321  VirtualRobot::SceneObject::VisualizationType visuType = VirtualRobot::SceneObject::Full;
322  if (drawStyle & data::ModelDrawStyle::COLLISION)
323  {
324  visuType = VirtualRobot::SceneObject::Collision;
325  }
326 
327  node->removeAllChildren();
328 
329  auto& robot = *loaded.robot;
330  for (size_t i = 0; i < robot.getRobotNodes().size(); ++i)
331  {
332  VirtualRobot::RobotNodePtr robNode = robot.getRobotNodes()[i];
333  SoSeparator* nodeSep = new SoSeparator;
334 
335  // This material is used to color the nodes individually
336  // We require it to be the first node in the separator for updates
337  SoMaterial* nodeMat = new SoMaterial;
338  nodeMat->setOverride(false);
339  nodeSep->addChild(nodeMat);
340 
341  auto robNodeVisu = robNode->getVisualization<VirtualRobot::CoinVisualization>(visuType);
342  if (robNodeVisu)
343  {
344  SoNode* sepRobNode = robNodeVisu->getCoinVisualization();
345 
346  if (sepRobNode)
347  {
348  nodeSep->addChild(sepRobNode);
349  }
350  }
351 
352  node->addChild(nodeSep);
353  }
354  }
355 
356  void
358  {
359  robotCache.clear();
360  }
361 } // namespace armarx::viz::coin
armarx::eFull
@ eFull
Definition: MorphingItem.h:35
armarx::core::time::Duration::toSecondsDouble
double toSecondsDouble() const
Returns the amount of seconds.
Definition: Duration.cpp:90
robots
std::vector< VirtualRobot::RobotPtr > robots
Definition: VisualizationRobot.cpp:88
armarx::ArmarXDataPath::FindPackageAndAddDataPath
static bool FindPackageAndAddDataPath(const std::string &packageName)
Search for the package and add its data path if it was found.
Definition: ArmarXDataPath.cpp:824
Duration.h
armarx::viz::coin::clearRobotCache
void clearRobotCache()
Definition: VisualizationRobot.cpp:357
armarx::viz::coin::VisualizationRobot::~VisualizationRobot
~VisualizationRobot()
Definition: VisualizationRobot.cpp:177
usedInstances
std::size_t usedInstances
Definition: VisualizationRobot.cpp:87
armarx::viz::coin::VisualizationRobot::recreateVisualizationNodes
void recreateVisualizationNodes(int drawStyle)
Definition: VisualizationRobot.cpp:319
project
std::string project
Definition: VisualizationRobot.cpp:85
armarx::viz::coin::VisualizationRobot::loadedDrawStyle
int loadedDrawStyle
Definition: VisualizationRobot.h:29
armarx::armem::client::util::swap
void swap(SubscriptionHandle &first, SubscriptionHandle &second)
Definition: SubscriptionHandle.cpp:66
deactivateSpam
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition: Logging.cpp:75
armarx::ctrlutil::a
double a(double t, double a0, double j)
Definition: CtrlUtil.h:45
armarx::abs
std::vector< T > abs(const std::vector< T > &v)
Definition: VectorHelpers.h:281
armarx::viz::coin::TypedElementVisualization< SoSeparator >::node
NodeType * node
Definition: ElementVisualizer.h:67
armarx::viz::coin::LoadedRobot::filename
std::string filename
Definition: VisualizationRobot.h:14
armarx::armem::human::Robot
@ Robot
Definition: util.h:17
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:184
armarx::viz::coin::VisualizationRobot::ElementType
data::ElementRobot ElementType
Definition: VisualizationRobot.h:20
armarx::viz::coin::VisualizationRobot::update
bool update(ElementType const &element)
Definition: VisualizationRobot.cpp:210
filename
std::string filename
Definition: VisualizationRobot.cpp:86
armarx::viz::coin::LoadedRobot::project
std::string project
Definition: VisualizationRobot.h:13
armarx::armem::Time
armarx::core::time::DateTime Time
Definition: forward_declarations.h:13
VisualizationRobot.h
armarx::ArmarXDataPath::SearchReadableFile
static bool SearchReadableFile(const std::string &querryFileName, std::string &resultFileName, bool verbose=true)
Definition: ArmarXDataPath.cpp:216
CMakePackageFinder.h
armarx::viz::coin::VisualizationRobot::loadedColor
armarx::viz::data::Color loadedColor
Definition: VisualizationRobot.h:30
armarx::viz::coin
Definition: ElementVisualizer.cpp:11
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
armarx::core::time::ScopedStopWatch
Measures the time this stop watch was inside the current scope.
Definition: ScopedStopWatch.h:31
VAROUT
#define VAROUT(x)
Definition: StringHelpers.h:198
armarx::viz::coin::LoadedRobot::robot
VirtualRobot::RobotPtr robot
Definition: VisualizationRobot.h:15
armarx::core::time::Duration
Represents a duration.
Definition: Duration.h:16
armarx::viz::coin::VisualizationRobot::loaded
LoadedRobot loaded
Definition: VisualizationRobot.h:28
Logging.h
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:193
ScopedStopWatch.h
ArmarXDataPath.h
VirtualRobot::RobotPtr
std::shared_ptr< class Robot > RobotPtr
Definition: Bus.h:19