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