00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <mvsim/World.h>
00011 #include <mvsim/Block.h>
00012
00013 #include "JointXMLnode.h"
00014 #include "XMLClassesRegistry.h"
00015 #include "xml_utils.h"
00016
00017 #include <rapidxml.hpp>
00018 #include <rapidxml_utils.hpp>
00019 #include <rapidxml_print.hpp>
00020 #include <mrpt/poses/CPose2D.h>
00021 #include <mrpt/opengl/CPolyhedron.h>
00022
00023 #include <mrpt/version.h>
00024 #if MRPT_VERSION < 0x199
00025 #include <mrpt/utils/utils_defs.h>
00026 using namespace mrpt::utils;
00027 #else
00028 #include <mrpt/math/TPose2D.h>
00029 #include <mrpt/core/format.h>
00030 #include <mrpt/core/bits_math.h>
00031 using namespace mrpt::img;
00032 using namespace mrpt;
00033 #endif
00034
00035 #include <sstream>
00036 #include <map>
00037 #include <string>
00038
00039 using namespace mvsim;
00040 using namespace std;
00041
00042 XmlClassesRegistry block_classes_registry("block:class");
00043
00044
00045 Block::Block(World* parent)
00046 : VisualObject(parent),
00047 m_block_index(0),
00048 m_b2d_block_body(NULL),
00049 m_q(0, 0, 0, 0, 0, 0),
00050 m_dq(0, 0, 0),
00051 m_mass(30.0),
00052 m_block_z_min(0.0),
00053 m_block_z_max(1.0),
00054 m_block_color(0x00, 0x00, 0xff),
00055 m_block_com(.0, .0),
00056 m_lateral_friction(0.5),
00057 m_ground_friction(0.5),
00058 m_restitution(0.01)
00059 {
00060 using namespace mrpt::math;
00061
00062 m_block_poly.push_back(TPoint2D(-0.5, -0.5));
00063 m_block_poly.push_back(TPoint2D(-0.5, 0.5));
00064 m_block_poly.push_back(TPoint2D(0.5, 0.5));
00065 m_block_poly.push_back(TPoint2D(0.5, -0.5));
00066 updateMaxRadiusFromPoly();
00067 }
00068
00069 void Block::simul_post_timestep_common(const TSimulContext& )
00070 {
00071 if (m_b2d_block_body)
00072 {
00073
00074 const b2Vec2& pos = m_b2d_block_body->GetPosition();
00075 const float32 angle = m_b2d_block_body->GetAngle();
00076 m_q.x = pos(0);
00077 m_q.y = pos(1);
00078 m_q.yaw = angle;
00079
00080
00081
00082
00083 const b2Vec2& vel = m_b2d_block_body->GetLinearVelocity();
00084 const float32 w = m_b2d_block_body->GetAngularVelocity();
00085 m_dq.vals[0] = vel(0);
00086 m_dq.vals[1] = vel(1);
00087 m_dq.vals[2] = w;
00088 }
00089 }
00090
00093 void Block::register_block_class(const rapidxml::xml_node<char>* xml_node)
00094 {
00095
00096 if (!xml_node)
00097 throw runtime_error("[Block::register_vehicle_class] XML node is NULL");
00098 if (0 != strcmp(xml_node->name(), "block:class"))
00099 throw runtime_error(mrpt::format(
00100 "[Block::register_block_class] XML element is '%s' "
00101 "('block:class' expected)",
00102 xml_node->name()));
00103
00104
00105
00106 std::stringstream ss;
00107 ss << *xml_node;
00108
00109 block_classes_registry.add(ss.str());
00110 }
00111
00114 Block* Block::factory(World* parent, const rapidxml::xml_node<char>* root)
00115 {
00116 using namespace std;
00117 using namespace rapidxml;
00118
00119 if (!root) throw runtime_error("[Block::factory] XML node is NULL");
00120 if (0 != strcmp(root->name(), "block"))
00121 throw runtime_error(mrpt::format(
00122 "[Block::factory] XML root element is '%s' ('block' expected)",
00123 root->name()));
00124
00125
00126
00127
00128 JointXMLnode<> block_root_node;
00129 const rapidxml::xml_node<char>* class_root = NULL;
00130 {
00131 block_root_node.add(
00132 root);
00133 const xml_attribute<>* block_class = root->first_attribute("class");
00134 if (block_class)
00135 {
00136 const string sClassName = block_class->value();
00137 class_root = block_classes_registry.get(sClassName);
00138 if (!class_root)
00139 throw runtime_error(mrpt::format(
00140 "[Block::factory] Block class '%s' undefined",
00141 sClassName.c_str()));
00142 block_root_node.add(class_root);
00143 }
00144 }
00145
00146
00147
00148 Block* block = new Block(parent);
00149
00150
00151
00152
00153 {
00154 const xml_attribute<>* attrib_name = root->first_attribute("name");
00155 if (attrib_name && attrib_name->value())
00156 {
00157 block->m_name = attrib_name->value();
00158 }
00159 else
00160 {
00161
00162 static int cnt = 0;
00163 block->m_name = mrpt::format("block%i", ++cnt);
00164 }
00165 }
00166
00167
00168 {
00169 const xml_node<>* node = block_root_node.first_node("init_pose");
00170 if (!node)
00171 throw runtime_error(
00172 "[Block::factory] Missing XML node <init_pose>");
00173
00174 if (3 != ::sscanf(
00175 node->value(), "%lf %lf %lf", &block->m_q.x, &block->m_q.y,
00176 &block->m_q.yaw))
00177 throw runtime_error(
00178 "[Block::factory] Error parsing <init_pose>...</init_pose>");
00179 block->m_q.yaw *= M_PI / 180.0;
00180 }
00181
00182
00183 {
00184 const xml_node<>* node = block_root_node.first_node("init_vel");
00185 if (node)
00186 {
00187 if (3 != ::sscanf(
00188 node->value(), "%lf %lf %lf", &block->m_dq.vals[0],
00189 &block->m_dq.vals[1], &block->m_dq.vals[2]))
00190 throw runtime_error(
00191 "[Block::factory] Error parsing <init_vel>...</init_vel>");
00192 block->m_dq.vals[2] *= M_PI / 180.0;
00193
00194
00195 const mrpt::poses::CPose2D pose(
00196 0, 0, block->m_q.yaw);
00197 pose.composePoint(
00198 block->m_dq.vals[0], block->m_dq.vals[1], block->m_dq.vals[0],
00199 block->m_dq.vals[1]);
00200 }
00201 }
00202
00203
00204 std::map<std::string, TParamEntry> params;
00205 params["mass"] = TParamEntry("%lf", &block->m_mass);
00206 params["zmin"] = TParamEntry("%lf", &block->m_block_z_min);
00207 params["zmax"] = TParamEntry("%lf", &block->m_block_z_max);
00208 params["ground_friction"] = TParamEntry("%lf", &block->m_ground_friction);
00209 params["lateral_friction"] = TParamEntry("%lf", &block->m_lateral_friction);
00210 params["restitution"] = TParamEntry("%lf", &block->m_restitution);
00211 params["color"] = TParamEntry("%color", &block->m_block_color);
00212
00213 parse_xmlnode_children_as_param(*root, params, "[Block::factory]");
00214 if (class_root)
00215 parse_xmlnode_children_as_param(
00216 *class_root, params, "[Block::factory]");
00217
00218
00219 const rapidxml::xml_node<char>* xml_shape =
00220 block_root_node.first_node("shape");
00221 if (xml_shape)
00222 {
00223 mvsim::parse_xmlnode_shape(
00224 *xml_shape, block->m_block_poly, "[Block::factory]");
00225 block->updateMaxRadiusFromPoly();
00226 }
00227
00228
00229
00230 b2World* b2world = parent->getBox2DWorld();
00231 block->create_multibody_system(b2world);
00232
00233 if (block->m_b2d_block_body)
00234 {
00235
00236 block->m_b2d_block_body->SetTransform(
00237 b2Vec2(block->m_q.x, block->m_q.y), block->m_q.yaw);
00238
00239 block->m_b2d_block_body->SetLinearVelocity(
00240 b2Vec2(block->m_dq.vals[0], block->m_dq.vals[1]));
00241 block->m_b2d_block_body->SetAngularVelocity(block->m_dq.vals[2]);
00242 }
00243
00244 return block;
00245 }
00246
00247 Block* Block::factory(World* parent, const std::string& xml_text)
00248 {
00249
00250 std::stringstream s;
00251 s.str(xml_text);
00252
00253 char* input_str = const_cast<char*>(xml_text.c_str());
00254 rapidxml::xml_document<> xml;
00255 try
00256 {
00257 xml.parse<0>(input_str);
00258 }
00259 catch (rapidxml::parse_error& e)
00260 {
00261 unsigned int line =
00262 static_cast<long>(std::count(input_str, e.where<char>(), '\n') + 1);
00263 throw std::runtime_error(mrpt::format(
00264 "[Block::factory] XML parse error (Line %u): %s",
00265 static_cast<unsigned>(line), e.what()));
00266 }
00267 return Block::factory(parent, xml.first_node());
00268 }
00269
00270 void Block::simul_pre_timestep(const TSimulContext& context) {}
00273 void Block::simul_post_timestep(const TSimulContext& context) {}
00275 vec3 Block::getVelocityLocal() const
00276 {
00277 vec3 local_vel;
00278 local_vel.vals[2] = m_dq.vals[2];
00279
00280 const mrpt::poses::CPose2D p(0, 0, -m_q.yaw);
00281 p.composePoint(
00282 m_dq.vals[0], m_dq.vals[1], local_vel.vals[0], local_vel.vals[1]);
00283 return local_vel;
00284 }
00285
00286 mrpt::poses::CPose2D Block::getCPose2D() const
00287 {
00288 return mrpt::poses::CPose2D(mrpt::math::TPose2D(m_q));
00289 }
00290
00292 void Block::gui_update(mrpt::opengl::COpenGLScene& scene)
00293 {
00294
00295
00296 if (!m_gl_block)
00297 {
00298 m_gl_block = mrpt::opengl::CSetOfObjects::Create();
00299
00300
00301 auto gl_poly = mrpt::opengl::CPolyhedron::CreateCustomPrism(
00302 m_block_poly, m_block_z_max - m_block_z_min);
00303 gl_poly->setLocation(0, 0, m_block_z_min);
00304 gl_poly->setColor(TColorf(m_block_color));
00305 m_gl_block->insert(gl_poly);
00306
00307 SCENE_INSERT_Z_ORDER(scene, 1, m_gl_block);
00308
00309
00310 m_gl_forces = mrpt::opengl::CSetOfLines::Create();
00311 m_gl_forces->setLineWidth(3.0);
00312 m_gl_forces->setColor_u8(TColor(0xff, 0xff, 0xff));
00313
00314 SCENE_INSERT_Z_ORDER(
00315 scene, 3, m_gl_forces);
00316 }
00317
00318
00319 m_gl_block->setPose(m_q);
00320
00321
00322 internal_gui_update_forces(scene);
00323 }
00324
00325 void Block::internal_gui_update_forces(mrpt::opengl::COpenGLScene& scene)
00326 {
00327 if (m_world->m_gui_options.show_forces)
00328 {
00329 std::lock_guard<std::mutex> csl(m_force_segments_for_rendering_cs);
00330 m_gl_forces->clear();
00331 m_gl_forces->appendLines(m_force_segments_for_rendering);
00332 m_gl_forces->setVisibility(true);
00333 }
00334 else
00335 {
00336 m_gl_forces->setVisibility(false);
00337 }
00338 }
00339
00340 void Block::updateMaxRadiusFromPoly()
00341 {
00342 using namespace mrpt::math;
00343
00344 m_max_radius = 0.001f;
00345 for (TPolygon2D::const_iterator it = m_block_poly.begin();
00346 it != m_block_poly.end(); ++it)
00347 {
00348 const float n = it->norm();
00349 keep_max(m_max_radius, n);
00350 }
00351 }
00352
00354 void Block::create_multibody_system(b2World* world)
00355 {
00356
00357 b2BodyDef bodyDef;
00358 bodyDef.type = b2_dynamicBody;
00359
00360 m_b2d_block_body = world->CreateBody(&bodyDef);
00361
00362
00363
00364 {
00365
00366 const size_t nPts = m_block_poly.size();
00367 ASSERT_(nPts >= 3);
00368 ASSERT_BELOWEQ_(nPts, (size_t)b2_maxPolygonVertices);
00369 std::vector<b2Vec2> pts(nPts);
00370 for (size_t i = 0; i < nPts; i++)
00371 pts[i] = b2Vec2(m_block_poly[i].x, m_block_poly[i].y);
00372
00373 b2PolygonShape blockPoly;
00374 blockPoly.Set(&pts[0], nPts);
00375
00376
00377
00378 b2FixtureDef fixtureDef;
00379 fixtureDef.shape = &blockPoly;
00380 fixtureDef.restitution = m_restitution;
00381
00382
00383 b2MassData mass;
00384 blockPoly.ComputeMass(&mass, 1);
00385 fixtureDef.density = m_mass / mass.mass;
00386
00387
00388 fixtureDef.friction = m_lateral_friction;
00389
00390
00391 m_fixture_block = m_b2d_block_body->CreateFixture(&fixtureDef);
00392
00393
00394 b2MassData vehMass;
00395 m_fixture_block->GetMassData(&vehMass);
00396 m_block_com.x = vehMass.center.x;
00397 m_block_com.y = vehMass.center.y;
00398 }
00399
00400
00401
00402 const size_t nContactPoints = 2;
00403 const double weight_per_contact_point =
00404 m_mass * getWorldObject()->get_gravity() / nContactPoints;
00405 const double mu = m_ground_friction;
00406 const double max_friction = mu * weight_per_contact_point;
00407
00408
00409 const vec2 pt_loc[nContactPoints] = {vec2(m_max_radius, 0),
00410 vec2(-m_max_radius, 0)};
00411
00412 b2FrictionJointDef fjd;
00413
00414 fjd.bodyA = m_world->getBox2DGroundBody();
00415 fjd.bodyB = m_b2d_block_body;
00416
00417 for (size_t i = 0; i < nContactPoints; i++)
00418 {
00419 const b2Vec2 local_pt = b2Vec2(pt_loc[i].vals[0], pt_loc[i].vals[1]);
00420
00421 fjd.localAnchorA = m_b2d_block_body->GetWorldPoint(local_pt);
00422 fjd.localAnchorB = local_pt;
00423 fjd.maxForce = max_friction;
00424 fjd.maxTorque = 0;
00425
00426 b2FrictionJoint* b2_friction = dynamic_cast<b2FrictionJoint*>(
00427 m_world->getBox2DWorld()->CreateJoint(&fjd));
00428 m_friction_joints.push_back(b2_friction);
00429 }
00430 }
00431
00432 void Block::apply_force(
00433 double fx, double fy, double local_ptx, double local_pty)
00434 {
00435 ASSERT_(m_b2d_block_body);
00436 const b2Vec2 wPt = m_b2d_block_body->GetWorldPoint(
00437 b2Vec2(local_ptx, local_pty));
00438 m_b2d_block_body->ApplyForce(b2Vec2(fx, fy), wPt, true );
00439 }