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