00001 #include <stdexcept>
00002
00003 #include <boost/filesystem/fstream.hpp>
00004 #include <boost/format.hpp>
00005 #include <boost/scope_exit.hpp>
00006 #include <boost/version.hpp>
00007
00008 #include <dynamic_reconfigure/server.h>
00009 #include <geometry_msgs/PoseWithCovarianceStamped.h>
00010 #include <image_proc/advertisement_checker.h>
00011 #include <image_transport/image_transport.h>
00012 #include <ros/param.h>
00013 #include <ros/ros.h>
00014 #include <ros/transport_hints.h>
00015 #include <sensor_msgs/Image.h>
00016 #include <std_msgs/String.h>
00017 #include <tf/transform_broadcaster.h>
00018
00019 #include <boost/bind.hpp>
00020 #include <visp/vpExponentialMap.h>
00021 #include <visp/vpImage.h>
00022 #include <visp/vpImageConvert.h>
00023 #include <visp/vpCameraParameters.h>
00024
00025 #include "tracker.hh"
00026
00027 #include "conversion.hh"
00028 #include "callbacks.hh"
00029 #include "file.hh"
00030 #include "names.hh"
00031
00032
00033
00034
00035
00036 namespace visp_tracker
00037 {
00038 bool
00039 Tracker::initCallback(visp_tracker::Init::Request& req,
00040 visp_tracker::Init::Response& res)
00041 {
00042 ROS_INFO("Initialization request received.");
00043
00044 res.initialization_succeed = false;
00045
00046
00047 BOOST_SCOPE_EXIT((&res)(tracker_)(&state_)
00048 (&lastTrackedImage_)(&trackerType_))
00049 {
00050 if(!res.initialization_succeed)
00051 {
00052 tracker_->resetTracker();
00053 state_ = WAITING_FOR_INITIALIZATION;
00054 lastTrackedImage_ = 0;
00055
00056 }
00057 } BOOST_SCOPE_EXIT_END;
00058
00059 std::string fullModelPath;
00060 boost::filesystem::ofstream modelStream;
00061
00062
00063 if (!makeModelFile(modelStream, fullModelPath))
00064 return true;
00065
00066 tracker_->resetTracker();
00067
00068
00069 convertInitRequestToVpMbTracker(req, tracker_);
00070
00071 if(trackerType_!="klt"){
00072 convertInitRequestToVpMe(req, tracker_, movingEdge_);
00073 }
00074
00075 if(trackerType_!="mbt"){
00076 convertInitRequestToVpKltOpencv(req, tracker_, kltTracker_);
00077 }
00078
00079 if(trackerType_=="mbt+klt"){
00080 visp_tracker::ModelBasedSettingsConfig config;
00081 convertVpMbTrackerToModelBasedSettingsConfig<visp_tracker::ModelBasedSettingsConfig>(tracker_, config);
00082 reconfigureSrv_->updateConfig(config);
00083 convertVpMeToModelBasedSettingsConfig<visp_tracker::ModelBasedSettingsConfig>(movingEdge_, tracker_, config);
00084 reconfigureSrv_->updateConfig(config);
00085 convertVpKltOpencvToModelBasedSettingsConfig<visp_tracker::ModelBasedSettingsConfig>(kltTracker_, tracker_, config);
00086 reconfigureSrv_->updateConfig(config);
00087 }
00088 else if(trackerType_=="mbt"){
00089 visp_tracker::ModelBasedSettingsEdgeConfig config;
00090 convertVpMbTrackerToModelBasedSettingsConfig<visp_tracker::ModelBasedSettingsEdgeConfig>(tracker_, config);
00091 reconfigureEdgeSrv_->updateConfig(config);
00092 convertVpMeToModelBasedSettingsConfig<visp_tracker::ModelBasedSettingsEdgeConfig>(movingEdge_, tracker_, config);
00093 reconfigureEdgeSrv_->updateConfig(config);
00094 }
00095 else{
00096 visp_tracker::ModelBasedSettingsKltConfig config;
00097 convertVpMbTrackerToModelBasedSettingsConfig<visp_tracker::ModelBasedSettingsKltConfig>(tracker_, config);
00098 reconfigureKltSrv_->updateConfig(config);
00099 convertVpKltOpencvToModelBasedSettingsConfig<visp_tracker::ModelBasedSettingsKltConfig>(kltTracker_, tracker_, config);
00100 reconfigureKltSrv_->updateConfig(config);
00101 }
00102
00103 state_ = WAITING_FOR_INITIALIZATION;
00104 lastTrackedImage_ = 0;
00105
00106
00107 try
00108 {
00109 ROS_DEBUG_STREAM("Trying to load the model Tracker: " << fullModelPath);
00110 tracker_->loadModel(fullModelPath.c_str());
00111 modelStream.close();
00112 }
00113 catch(...)
00114 {
00115 ROS_ERROR_STREAM("Failed to load the model: " << fullModelPath);
00116 return true;
00117 }
00118 ROS_DEBUG("Model has been successfully loaded.");
00119
00120
00121 transformToVpHomogeneousMatrix(cMo_, req.initial_cMo);
00122
00123
00124 tracker_->setCovarianceComputation(true);
00125
00126
00127 ROS_INFO_STREAM("Initializing tracker with cMo:\n" << cMo_);
00128 try
00129 {
00130
00131 tracker_->initFromPose(image_, cMo_);
00132 ROS_INFO("Tracker successfully initialized.");
00133
00134
00135 ROS_INFO_STREAM(convertVpMbTrackerToRosMessage(tracker_));
00136
00137 if(trackerType_!="klt")
00138 ROS_INFO_STREAM(convertVpMeToRosMessage(tracker_, movingEdge_));
00139
00140 if(trackerType_!="mbt")
00141 ROS_INFO_STREAM(convertVpKltOpencvToRosMessage(tracker_,kltTracker_));
00142 }
00143 catch(const std::string& str)
00144 {
00145 ROS_ERROR_STREAM("Tracker initialization has failed: " << str);
00146 }
00147
00148
00149 res.initialization_succeed = true;
00150 state_ = TRACKING;
00151 return true;
00152 }
00153
00154 void
00155 Tracker::updateMovingEdgeSites(visp_tracker::MovingEdgeSitesPtr sites)
00156 {
00157 if (!sites)
00158 return;
00159
00160 std::list<vpMbtDistanceLine*> linesList;
00161
00162 if(trackerType_!="klt") {
00163 dynamic_cast<vpMbEdgeTracker*>(tracker_)->getLline(linesList, 0);
00164
00165 std::list<vpMbtDistanceLine*>::iterator linesIterator = linesList.begin();
00166
00167 bool noVisibleLine = true;
00168 for (; linesIterator != linesList.end(); ++linesIterator)
00169 {
00170 vpMbtDistanceLine* line = *linesIterator;
00171
00172 if (line && line->isVisible() && line->meline)
00173 {
00174 std::list<vpMeSite>::const_iterator sitesIterator =
00175 line->meline->list.begin();
00176 if (line->meline->list.empty())
00177 ROS_DEBUG_THROTTLE(10, "no moving edge for a line");
00178 for (; sitesIterator != line->meline->list.end(); ++sitesIterator)
00179 {
00180 visp_tracker::MovingEdgeSite movingEdgeSite;
00181 movingEdgeSite.x = sitesIterator->ifloat;
00182 movingEdgeSite.y = sitesIterator->jfloat;
00183 movingEdgeSite.suppress = sitesIterator->suppress;
00184 sites->moving_edge_sites.push_back (movingEdgeSite);
00185 }
00186 noVisibleLine = false;
00187 }
00188 }
00189 if (noVisibleLine)
00190 ROS_DEBUG_THROTTLE(10, "no distance lines");
00191 }
00192 }
00193
00194 void
00195 Tracker::updateKltPoints(visp_tracker::KltPointsPtr klt)
00196 {
00197 if (!klt)
00198 return;
00199
00200 #if VISP_VERSION_INT < VP_VERSION_INT(2,10,0) // ViSP < 2.10.0
00201 vpMbHiddenFaces<vpMbtKltPolygon> *poly_lst;
00202 std::map<int, vpImagePoint> *map_klt;
00203
00204 if(trackerType_!="mbt") {
00205 poly_lst = &dynamic_cast<vpMbKltTracker*>(tracker_)->getFaces();
00206
00207 for(unsigned int i = 0 ; i < poly_lst->size() ; i++)
00208 {
00209 if((*poly_lst)[i])
00210 {
00211 map_klt = &((*poly_lst)[i]->getCurrentPoints());
00212
00213 if(map_klt->size() > 3)
00214 {
00215 for (std::map<int, vpImagePoint>::iterator it=map_klt->begin(); it!=map_klt->end(); ++it)
00216 {
00217 visp_tracker::KltPoint kltPoint;
00218 kltPoint.id = it->first;
00219 kltPoint.i = it->second.get_i();
00220 kltPoint.j = it->second.get_j();
00221 klt->klt_points_positions.push_back (kltPoint);
00222 }
00223 }
00224 }
00225 }
00226 }
00227 #else // ViSP >= 2.10.0
00228 std::list<vpMbtDistanceKltPoints*> *poly_lst;
00229 std::map<int, vpImagePoint> *map_klt;
00230
00231 if(trackerType_!="mbt") {
00232 poly_lst = &dynamic_cast<vpMbKltTracker*>(tracker_)->getFeaturesKlt();
00233
00234 for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=poly_lst->begin(); it!=poly_lst->end(); ++it){
00235 map_klt = &((*it)->getCurrentPoints());
00236
00237 if((*it)->polygon->isVisible()){
00238 if(map_klt->size() > 3)
00239 {
00240 for (std::map<int, vpImagePoint>::iterator it=map_klt->begin(); it!=map_klt->end(); ++it)
00241 {
00242 visp_tracker::KltPoint kltPoint;
00243 kltPoint.id = it->first;
00244 kltPoint.i = it->second.get_i();
00245 kltPoint.j = it->second.get_j();
00246 klt->klt_points_positions.push_back (kltPoint);
00247 }
00248 }
00249 }
00250 }
00251 }
00252 #endif
00253 }
00254
00255 void Tracker::checkInputs()
00256 {
00257 ros::V_string topics;
00258 topics.push_back(rectifiedImageTopic_);
00259 checkInputs_.start(topics, 60.0);
00260 }
00261
00262 Tracker::Tracker(ros::NodeHandle& nh,
00263 ros::NodeHandle& privateNh,
00264 volatile bool& exiting,
00265 unsigned queueSize)
00266 : exiting_ (exiting),
00267 queueSize_(queueSize),
00268 nodeHandle_(nh),
00269 nodeHandlePrivate_(privateNh),
00270 imageTransport_(nodeHandle_),
00271 state_(WAITING_FOR_INITIALIZATION),
00272 image_(),
00273 cameraPrefix_(),
00274 rectifiedImageTopic_(),
00275 cameraInfoTopic_(),
00276 modelPath_(),
00277 cameraSubscriber_(),
00278 mutex_ (),
00279
00280 reconfigureSrv_(NULL),
00281 reconfigureKltSrv_(NULL),
00282 reconfigureEdgeSrv_(NULL),
00283 resultPublisher_(),
00284 transformationPublisher_(),
00285 movingEdgeSitesPublisher_(),
00286 kltPointsPublisher_(),
00287 initService_(),
00288 header_(),
00289 info_(),
00290 kltTracker_(),
00291 movingEdge_(),
00292 cameraParameters_(),
00293 lastTrackedImage_(),
00294 checkInputs_(nodeHandle_, ros::this_node::getName()),
00295 cMo_ (),
00296 listener_ (),
00297 worldFrameId_ (),
00298 compensateRobotMotion_ (false),
00299 transformBroadcaster_ (),
00300 childFrameId_ (),
00301 objectPositionHintSubscriber_ (),
00302 objectPositionHint_ ()
00303 {
00304
00305 cMo_.eye();
00306
00307
00308
00309 nodeHandlePrivate_.param<std::string>("camera_prefix", cameraPrefix_, "");
00310 nodeHandlePrivate_.param<std::string>("tracker_type", trackerType_, "mbt");
00311 if(trackerType_=="mbt")
00312 tracker_ = new vpMbEdgeTracker();
00313 else if(trackerType_=="klt")
00314 tracker_ = new vpMbKltTracker();
00315 else
00316 tracker_ = new vpMbEdgeKltTracker();
00317
00318 if (cameraPrefix_.empty ())
00319 {
00320 ROS_FATAL
00321 ("The camera_prefix parameter not set.\n"
00322 "Please relaunch the tracker while setting this parameter, i.e.\n"
00323 "$ rosrun visp_tracker tracker _camera_prefix:=/my/camera");
00324 ros::shutdown ();
00325 return;
00326 }
00327
00328 nodeHandle_.setParam("camera_prefix", cameraPrefix_);
00329
00330 nodeHandle_.param<std::string>("frame_id", childFrameId_, "object_position");
00331
00332
00333 nodeHandle_.param<std::string>("world_frame_id", worldFrameId_, "/odom");
00334 nodeHandle_.param<bool>
00335 ("compensate_robot_motion", compensateRobotMotion_, false);
00336
00337
00338 rectifiedImageTopic_ =
00339 ros::names::resolve(cameraPrefix_ + "/image_rect");
00340
00341
00342 checkInputs();
00343
00344
00345 resultPublisher_ =
00346 nodeHandle_.advertise<geometry_msgs::PoseWithCovarianceStamped>
00347 (visp_tracker::object_position_covariance_topic, queueSize_);
00348
00349 transformationPublisher_ =
00350 nodeHandle_.advertise<geometry_msgs::TransformStamped>
00351 (visp_tracker::object_position_topic, queueSize_);
00352
00353
00354 movingEdgeSitesPublisher_ =
00355 nodeHandle_.advertise<visp_tracker::MovingEdgeSites>
00356 (visp_tracker::moving_edge_sites_topic, queueSize_);
00357
00358
00359 kltPointsPublisher_ =
00360 nodeHandle_.advertise<visp_tracker::KltPoints>
00361 (visp_tracker::klt_points_topic, queueSize_);
00362
00363
00364 cameraSubscriber_ =
00365 imageTransport_.subscribeCamera
00366 (rectifiedImageTopic_, queueSize_,
00367 bindImageCallback(image_, header_, info_));
00368
00369
00370 typedef boost::function<
00371 void (const geometry_msgs::TransformStampedConstPtr&)>
00372 objectPositionHintCallback_t;
00373 objectPositionHintCallback_t callback =
00374 boost::bind (&Tracker::objectPositionHintCallback, this, _1);
00375 objectPositionHintSubscriber_ =
00376 nodeHandle_.subscribe<geometry_msgs::TransformStamped>
00377 ("object_position_hint", queueSize_, callback);
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393 if(trackerType_=="mbt+klt"){
00394 reconfigureSrv_ = new reconfigureSrvStruct<visp_tracker::ModelBasedSettingsConfig>::reconfigureSrv_t(mutex_, nodeHandlePrivate_);
00395 reconfigureSrvStruct<visp_tracker::ModelBasedSettingsConfig>::reconfigureSrv_t::CallbackType f =
00396 boost::bind(&reconfigureCallbackAndInitViewer,
00397 boost::ref(nodeHandle_), boost::ref(tracker_),
00398 boost::ref(image_), boost::ref(movingEdge_), boost::ref(kltTracker_),
00399 boost::ref(mutex_), _1, _2);
00400 reconfigureSrv_->setCallback(f);
00401 }
00402 else if(trackerType_=="mbt"){
00403 reconfigureEdgeSrv_ = new reconfigureSrvStruct<visp_tracker::ModelBasedSettingsEdgeConfig>::reconfigureSrv_t(mutex_, nodeHandlePrivate_);
00404 reconfigureSrvStruct<visp_tracker::ModelBasedSettingsEdgeConfig>::reconfigureSrv_t::CallbackType f =
00405 boost::bind(&reconfigureEdgeCallbackAndInitViewer,
00406 boost::ref(nodeHandle_), boost::ref(tracker_),
00407 boost::ref(image_), boost::ref(movingEdge_),
00408 boost::ref(mutex_), _1, _2);
00409 reconfigureEdgeSrv_->setCallback(f);
00410 }
00411 else{
00412 reconfigureKltSrv_ = new reconfigureSrvStruct<visp_tracker::ModelBasedSettingsKltConfig>::reconfigureSrv_t(mutex_, nodeHandlePrivate_);
00413 reconfigureSrvStruct<visp_tracker::ModelBasedSettingsKltConfig>::reconfigureSrv_t::CallbackType f =
00414 boost::bind(&reconfigureKltCallbackAndInitViewer,
00415 boost::ref(nodeHandle_), boost::ref(tracker_),
00416 boost::ref(image_), boost::ref(kltTracker_),
00417 boost::ref(mutex_), _1, _2);
00418 reconfigureKltSrv_->setCallback(f);
00419 }
00420
00421
00422 waitForImage();
00423 if (this->exiting())
00424 return;
00425 if (!image_.getWidth() || !image_.getHeight())
00426 throw std::runtime_error("failed to retrieve image");
00427
00428
00429 initializeVpCameraFromCameraInfo(cameraParameters_, info_);
00430
00431
00432 if (cameraParameters_.get_px () == 0.
00433 || cameraParameters_.get_px () == 1.
00434 || cameraParameters_.get_py () == 0.
00435 || cameraParameters_.get_py () == 1.
00436 || cameraParameters_.get_u0 () == 0.
00437 || cameraParameters_.get_u0 () == 1.
00438 || cameraParameters_.get_v0 () == 0.
00439 || cameraParameters_.get_v0 () == 1.)
00440 ROS_WARN ("Dubious camera parameters detected.\n"
00441 "\n"
00442 "It seems that the matrix P from your camera\n"
00443 "calibration topics is wrong.\n"
00444 "The tracker will continue anyway, but you\n"
00445 "should double check your calibration data,\n"
00446 "especially if the model re-projection fails.\n"
00447 "\n"
00448 "This warning is triggered is px, py, u0 or v0\n"
00449 "is set to 0. or 1. (exactly).");
00450
00451 tracker_->setCameraParameters(cameraParameters_);
00452 tracker_->setDisplayFeatures(false);
00453
00454 ROS_INFO_STREAM(cameraParameters_);
00455
00456
00457 initCallback_t initCallback =
00458 boost::bind(&Tracker::initCallback, this, _1, _2);
00459
00460 initService_ = nodeHandle_.advertiseService
00461 (visp_tracker::init_service, initCallback);
00462 }
00463
00464 Tracker::~Tracker()
00465 {
00466 delete tracker_;
00467
00468 if(reconfigureSrv_ != NULL)
00469 delete reconfigureSrv_;
00470
00471 if(reconfigureKltSrv_ != NULL)
00472 delete reconfigureKltSrv_;
00473
00474 if(reconfigureEdgeSrv_ != NULL)
00475 delete reconfigureEdgeSrv_;
00476 }
00477
00478 void Tracker::spin()
00479 {
00480 ros::Rate loopRateTracking(100);
00481 tf::Transform transform;
00482 std_msgs::Header lastHeader;
00483
00484 while (!exiting())
00485 {
00486
00487
00488
00489 if (header_.seq < lastHeader.seq)
00490 lastTrackedImage_ = 0;
00491
00492 if (lastTrackedImage_ < header_.seq)
00493 {
00494 lastTrackedImage_ = header_.seq;
00495
00496
00497
00498 if (compensateRobotMotion_)
00499 try
00500 {
00501 tf::StampedTransform stampedTransform;
00502 listener_.lookupTransform
00503 (header_.frame_id,
00504 header_.stamp,
00505 header_.frame_id,
00506 lastHeader.stamp,
00507 worldFrameId_,
00508 stampedTransform
00509 );
00510 vpHomogeneousMatrix newMold;
00511 transformToVpHomogeneousMatrix (newMold, stampedTransform);
00512 cMo_ = newMold * cMo_;
00513
00514 mutex_.lock();
00515 tracker_->setPose(image_, cMo_);
00516 mutex_.unlock();
00517 }
00518 catch(tf::TransformException& e)
00519 {
00520 mutex_.unlock();
00521 }
00522
00523
00524
00525 if (state_ == LOST)
00526 {
00527
00528
00529 if (ros::Time::now () - objectPositionHint_.header.stamp
00530 < ros::Duration (1.))
00531 transformToVpHomogeneousMatrix
00532 (cMo_, objectPositionHint_.transform);
00533
00534 mutex_.lock();
00535 tracker_->setPose(image_, cMo_);
00536 mutex_.unlock();
00537 }
00538
00539
00540
00541 if (state_ == TRACKING || state_ == LOST)
00542 try
00543 {
00544 mutex_.lock();
00545
00546 tracker_->track(image_);
00547 tracker_->getPose(cMo_);
00548 mutex_.unlock();
00549 }
00550 catch(...)
00551 {
00552 mutex_.unlock();
00553 ROS_WARN_THROTTLE(10, "tracking lost");
00554 state_ = LOST;
00555 }
00556
00557
00558 if (state_ == TRACKING)
00559 {
00560 geometry_msgs::Transform transformMsg;
00561 vpHomogeneousMatrixToTransform(transformMsg, cMo_);
00562
00563
00564 if (transformationPublisher_.getNumSubscribers () > 0)
00565 {
00566 geometry_msgs::TransformStampedPtr objectPosition
00567 (new geometry_msgs::TransformStamped);
00568 objectPosition->header = header_;
00569 objectPosition->child_frame_id = childFrameId_;
00570 objectPosition->transform = transformMsg;
00571 transformationPublisher_.publish(objectPosition);
00572 }
00573
00574
00575 if (resultPublisher_.getNumSubscribers () > 0)
00576 {
00577 geometry_msgs::PoseWithCovarianceStampedPtr result
00578 (new geometry_msgs::PoseWithCovarianceStamped);
00579 result->header = header_;
00580 result->pose.pose.position.x =
00581 transformMsg.translation.x;
00582 result->pose.pose.position.y =
00583 transformMsg.translation.y;
00584 result->pose.pose.position.z =
00585 transformMsg.translation.z;
00586
00587 result->pose.pose.orientation.x =
00588 transformMsg.rotation.x;
00589 result->pose.pose.orientation.y =
00590 transformMsg.rotation.y;
00591 result->pose.pose.orientation.z =
00592 transformMsg.rotation.z;
00593 result->pose.pose.orientation.w =
00594 transformMsg.rotation.w;
00595 const vpMatrix& covariance =
00596 tracker_->getCovarianceMatrix();
00597 for (unsigned i = 0; i < covariance.getRows(); ++i)
00598 for (unsigned j = 0; j < covariance.getCols(); ++j)
00599 {
00600 unsigned idx = i * covariance.getCols() + j;
00601 if (idx >= 36)
00602 continue;
00603 result->pose.covariance[idx] = covariance[i][j];
00604 }
00605 resultPublisher_.publish(result);
00606 }
00607
00608
00609 if (movingEdgeSitesPublisher_.getNumSubscribers () > 0)
00610 {
00611 visp_tracker::MovingEdgeSitesPtr sites
00612 (new visp_tracker::MovingEdgeSites);
00613 updateMovingEdgeSites(sites);
00614 sites->header = header_;
00615 movingEdgeSitesPublisher_.publish(sites);
00616 }
00617
00618 if (kltPointsPublisher_.getNumSubscribers () > 0)
00619 {
00620 visp_tracker::KltPointsPtr klt
00621 (new visp_tracker::KltPoints);
00622 updateKltPoints(klt);
00623 klt->header = header_;
00624 kltPointsPublisher_.publish(klt);
00625 }
00626
00627
00628 transform.setOrigin
00629 (tf::Vector3(transformMsg.translation.x,
00630 transformMsg.translation.y,
00631 transformMsg.translation.z));
00632 transform.setRotation
00633 (tf::Quaternion
00634 (transformMsg.rotation.x,
00635 transformMsg.rotation.y,
00636 transformMsg.rotation.z,
00637 transformMsg.rotation.w));
00638 transformBroadcaster_.sendTransform
00639 (tf::StampedTransform
00640 (transform,
00641 header_.stamp,
00642 header_.frame_id,
00643 childFrameId_));
00644 }
00645 }
00646
00647 lastHeader = header_;
00648 spinOnce();
00649 loopRateTracking.sleep();
00650
00651 }
00652 }
00653
00654
00655
00656 void
00657 Tracker::waitForImage()
00658 {
00659 ros::Rate loop_rate(10);
00660 while (!exiting()
00661 && (!image_.getWidth() || !image_.getHeight())
00662 && (!info_ || info_->K[0] == 0.))
00663 {
00664
00665 spinOnce();
00666 loop_rate.sleep();
00667 }
00668 }
00669
00670 void
00671 Tracker::objectPositionHintCallback
00672 (const geometry_msgs::TransformStampedConstPtr& transform)
00673 {
00674 objectPositionHint_ = *transform;
00675 }
00676
00677 }