C++ Tutorials
tutorial_agxPlot.cpp
#if AGX_USE_WEBPLOT()
static bool g_isUnitTest = false;
namespace
{
{
public:
: m_hinge( hinge )
{
}
{
return result;
}
private:
};
{
public:
: m_hinge(hinge)
{
}
{
if (m_hinge) {
result.
value = m_hinge->getCurrentSpeed();
}
return result;
}
private:
};
}
{
osg::Group*
root =
new osg::Group();
body->add(box);
frame->setLocalTranslate(0, 0, 1);
angle_series->setUnit("radians");
speed_series->setUnit("rad/s");
time_data,
angle_series,
"Hinge angle"
);
time_data,
speed_series,
"Hinge speed"
);
auto plot_window = plot->getOrCreateWindow("Box");
plot_window->add(angle_curve);
plot_window->add(speed_curve);
if (!g_isUnitTest)
{
auto webplot = new agxPlot::WebPlot(true);
plot->add(webplot);
}
}
int main(int argc, char** argv)
{
std::cout <<
"\tTutorial agxPlot\n" <<
"\t--------------------------------\n\n" << std::endl;
g_isUnitTest = argc >= 2;
try {
application->addScene(buildTutorial1, '1');
if (application->init(argc, argv))
{
return application->run();
}
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
#else
int main(int, char**)
{
"\tTutorial agxPlot\n" <<
"AGX Dynamics built without support for agxPlot" <<
LOGGER_ENDL();
return 0;
}
#endif
A box shape for geometric intersection tests.
The geometry representation used by the collision detection engine.
Class that encapsulates rendering and simulation using OpenSceneGraph.
Definition of a plot curve.
Abstract base class for creating data to be plotted.
virtual Result getValue()=0
Class that encapsulates data to be plotted, including methods for accessing the data,...
Class that produces data in the form of a Real value.
virtual agx::Real get()=0
Implement and return the value to be plotted.
static agxRender::Color Red()
static agxRender::Color Blue()
Simulation is a class that bridges the collision space agxCollide::Space and the dynamic simulation s...
bool add(agx::Material *material)
Add a material to the simulation.
agxPlot::DataSeries * getTimeDataSeries() const
A data series that contains all time stamps of the simulation.
agxPlot::System * getPlotSystem() const
Convenience class to automatically call agx::init / agx::shutdown.
This class provides conversion services between Euler angles in any of the 24 conventions and corresp...
The object defining a frame of reference and providing transformations operations.
The hinge constraint between two rigid bodies or one rigid body and the world.
The rigid body class, combining a geometric model and a frame of reference.
AGXOSG_EXPORT agxOSG::GeometryNode * createVisual(const agxCollide::Geometry *geometry, osg::Group *root, float detailRatio=1.0f, bool createAxes=false, bool evenIfSensor=true)
Given a geometry, this function create visual representation of the shape.
constexpr Real degreesToRadians(Real angle)
AGXCORE_EXPORT Component * root()
AGXCORE_EXPORT const char * agxGetLibraryName()
Return the name of the library.
AGXCORE_EXPORT const char * agxGetVersion(bool includeRevision=true)
AGXCORE_EXPORT const char * agxGetCompanyName()
tutorial_agxTerrain.cpp
#if AGX_USE_AGXTERRAIN()
#include "terrain_shovel_utils.h"
namespace
{
template<size_t N>
size_t find(const char(&vs)[N], int v)
{
if ( v < 0 || v > std::numeric_limits<char>::max() ) {
}
auto c = static_cast<char>( v );
auto b = std::begin(vs);
auto e = std::end(vs);
auto it = std::find(b, e, c);
if ( it != e ) {
return it - b;
}
else {
}
}
{
public:
: m_renderer(renderer), m_state(0), m_text(text)
{
m_state.set(HEIGHTS, true);
m_state.set(SOIL_PARTICLES, true);
this->updateState();
}
virtual bool keyboard(
int key,
unsigned int ,
float ,
float ,
bool keydown)
{
if ( keydown ) {
return false;
}
const char keys[] = { 'a', 'z', 's', 'x' };
size_t index = find(keys, key);
return false;
}
m_state.set(index, !m_state.test(index));
this->updateState();
return true;
}
protected:
void updateState()
{
m_renderer->setRenderVoxelSolidMass(m_state.test(SOLID_VOXELS));
m_text->setText(1,
agx::String::format(
"RenderSolidVoxel[a]: %d", (
int)m_state.test(SOLID_VOXELS)));
m_renderer->setRenderVoxelFluidMass(m_state.test(FLUID_VOXELS));
m_text->setText(2,
agx::String::format(
"RenderFluidVoxel[z]: %d", (
int)m_state.test(FLUID_VOXELS)));
m_renderer->setRenderSoilParticles(m_state.test(SOIL_PARTICLES));
m_text->setText(3,
agx::String::format(
"RenderSoilParticles[s]: %d", (
int)m_state.test(SOIL_PARTICLES)));
m_renderer->setRenderHeightField(m_state.test(HEIGHTS));
m_text->setText(4,
agx::String::format(
"RenderHeightField[x]: %d", (
int)m_state.test(HEIGHTS)));
}
private:
enum { SOLID_VOXELS, FLUID_VOXELS, SOIL_PARTICLES, HEIGHTS };
};
{
public:
: StepEventListener( StepEventListener::POST_STEP )
, m_shovelBody(shovelBody)
{ }
{
if ( t < 0.5 )
else if ( t < 7.5 )
m_shovelBody->setVelocity(1.0, 0.0, 0.0);
else if ( t < 8 )
else if ( t < 14 )
m_shovelBody->setVelocity(-1.0, 0.0, 0.0);
else
m_shovelBody->setVelocity(0.0, 0.0, 0.0);
}
private:
};
{
public:
float maxSpeed = 1.0,
: m_maxSpeed(maxSpeed)
, m_maxRotSpeed(maxRotSpeed)
{
kinematicSphere->setEnableCollisions(false);
m_kinematicBody->setMotionControl(RigidBody::KINEMATICS);
m_kinematicBody->setRotation(shovelBody->
getRotation());
simulation->
add(m_kinematicBody);
m_joint->setEnableComputeForces(true);
simulation->
add(m_joint);
}
bool keyboard(
int key,
unsigned modkeyMask,
float ,
float ,
bool keyDown) {
bool handled = false;
if ( !keyDown ) {
m_kinematicBody->setVelocity(0, 0, 0);
m_kinematicBody->setAngularVelocity(0, 0, 0);
return handled;
}
switch ( key )
{
case 'a':
m_kinematicBody->setVelocity(-m_maxSpeed, 0, 0);
break;
case 'z':
m_kinematicBody->setVelocity(m_maxSpeed, 0, 0);
break;
case 's':
m_kinematicBody->setVelocity(0, m_maxSpeed, 0);
break;
case 'x':
m_kinematicBody->setVelocity(0, -m_maxSpeed, 0);
break;
if ( modkeyMask == GuiEventListener::MODKEY_LEFT_ALT )
m_kinematicBody->setAngularVelocity(0, m_maxRotSpeed, 0);
else
m_kinematicBody->setVelocity(0, 0, m_maxSpeed);
break;
if ( modkeyMask == GuiEventListener::MODKEY_LEFT_ALT )
m_kinematicBody->setAngularVelocity(0, -m_maxRotSpeed, 0);
else
m_kinematicBody->setVelocity(0, 0, -m_maxSpeed);
break;
default:
break;
}
return handled;
}
return m_joint;
}
private:
float m_maxSpeed;
float m_maxRotSpeed;
};
{
auto interface = terrain->getTerrainGridControl();
agx::Real numWidthPoints_2 = numWidthPoints * 0.5;
agx::Real numLenPoints_2 = numLenPoints * 0.5;
for ( int l=0; l < numLenPoints; l++ )
for ( int w=0; w < numLenPoints; w++ )
{
interface->addSolidOccupancyInColumnToHeight( gridPoint, h, static_cast<float>(compaction) );
}
}
{
public:
: m_sd(application->getSceneDecorator())
, m_soilSimulationInterface(terrain->getSoilSimulationInterface())
{
}
{
m_sd->setText(1,
agx::String::format(
"# Particles: %d", m_soilSimulationInterface->getNumSoilParticles()));
m_sd->setText(2,
agx::String::format(
"# Particles-particle contacts: %d", m_soilSimulationInterface->getParticleParticleContacts().size()));
m_sd->setText(3,
agx::String::format(
"# Particles-geometry contacts: %d", m_soilSimulationInterface->getParticleGeometryContacts().size()));
}
private:
};
}
{
osg::Group*
root =
new osg::Group();
size_t resolutionX = 51;
size_t resolutionY = 51;
simulation->
add( terrain );
terrain->loadLibraryMaterial( "dirt_1" );
simulation->
add(terrainMaterial);
terrain->setMaterial( terrainMaterial );
auto shovel = createSimpleShovelBody(simulation, root, shovelSize, shovelAngle);
agx::Vec3 cuttingPoint1 =
agx::Vec3{ 2 * shovelSize.
x() * std::cos(shovelAngle), -shovelSize.y(), 2 * shovelSize.x() * ( -0.5 - std::sin(shovelAngle) ) };
agx::Line cuttingEdge { cuttingPoint1, cuttingPoint2 };
agx::Vec3 cuttingVector{ cos( shovelAngle ), 0.0, -sin( shovelAngle ) };
simulation->
add( terrainShovel );
terrainShovel->getSettings()->setVerticalBladeSoilMergeDistance(0.4);
terrainShovel->getAdvancedSettings()->setNoMergeExtensionDistance(0.5);
terrainShovel->getSettings()->setPenetrationForceScaling(0);
shovel->setPosition(
agx::Vec3(-5.5, 0.0, shovelSize.x() * ( 1 + 2 * sin(shovelAngle) ) + 0.15));
simulation->
add(driver, 51);
renderer->setRenderVoxelSolidMass(false);
renderer->setRenderVoxelFluidMass(false);
renderer->setRenderVoxelBoundingBox(false);
renderer->setRenderHeightField(true);
renderer->setRenderVelocityField(false);
renderer->setVelocityFieldLineColor(
Vec4(1.0, 1.0, 0, 1));
renderer->setRenderSoilParticles(true);
simulation->
add(renderer);
}
{
osg::Group*
root =
new osg::Group();
size_t resolutionX = 71;
size_t resolutionY = 71;
simulation->
add(terrain);
terrain->loadLibraryMaterial("dirt_1");
auto compactionProperties = terrain->getTerrainMaterial()->getCompactionProperties();
compactionProperties->setCompactionTimeRelaxationConstant(0.5);
compactionProperties->setCompressionIndex(0.11);
compactionProperties->setAngleOfReposeCompactionRate(50.0);
auto bulkProperties = terrain->getTerrainMaterial()->getBulkProperties();
bulkProperties->setMaximumDensity(bulkProperties->getDensity() * 2.0);
bulkProperties->setSwellFactor(1.25);
simulation->
add(terrainMaterial);
terrain->setMaterial(terrainMaterial);
agx::Real length = 6.0, width = 6.0, height = 2.5;
createPile( terrain,
length,
width,
height,
location,
compaction );
simulation->
add(deformerMaterial);
sphereGeometry->setMaterial(deformerMaterial);
sphereBody->getMassProperties()->setMass(5e3);
sphereBody->setPosition(
agx::Vec3(0.6, -0.25, 4.0));
simulation->
add( sphereBody );
sphereBody->setAngularVelocityDamping(
agx::Vec3f(0.75,0.75,0.75));
boxGeometry->setMaterial(deformerMaterial);
boxBody->getMassProperties()->setMass(1e5);
boxBody->setPosition(
agx::Vec3(-0.85, -0.5, 4.0));
simulation->
add(boxBody);
renderer->setRenderVoxelSolidMass(false);
renderer->setRenderVoxelFluidMass(false);
renderer->setRenderVoxelBoundingBox(false);
renderer->setRenderHeightField(true);
renderer->setRenderSoilParticles(true);
simulation->
add(renderer);
}
{
osg::Group*
root =
new osg::Group();
size_t resolutionX = 81;
size_t resolutionY = 81;
simulation->
add(terrain);
terrain->loadLibraryMaterial("dirt_1");
terrain->getTerrainMaterial()->getBulkProperties()->setYoungsModulus(1e11);
terrain->getProperties()->setDeleteSoilParticlesOutsideBounds(true);
agx::Real length = 0.65, width = 0.6, height = 0.45, thickness = 0.05, topFraction = 0.25;
auto bucketShovelData = createSimpleBucket(simulation,
root,
length,
width,
height,
thickness,
topFraction);
bucketShovelData.shovelBody,
bucketShovelData.topEdge,
bucketShovelData.cuttingEdge,
bucketShovelData.cuttingDirection }
};
terrainShovel->getRigidBody()->setRotation({ 0, -
agx::PI / 10.0, 0 });
simulation->
add(terrainShovel);
agx::Real plength = 2.5, pwidth = 2.5, pheight = 1.25;
createPile(terrain,
plength,
pwidth,
pheight,
location,
compaction1);
createPile(terrain,
plength,
pwidth,
pheight,
location2,
compaction2);
createPile(terrain,
plength,
pwidth,
pheight,
location3,
compaction3);
simulation->
add(shovelMaterial);
simulation->
add(terrainMaterial);
terrain->setMaterial(terrainMaterial);
auto shovelTerrainContactMaterial = simulation->
getMaterialManager()->getContactMaterialOrCreateImplicit(shovelMaterial, terrainMaterial);
shovelTerrainContactMaterial->setYoungsModulus(1e8);
simulation->
add(shovelTerrainContactMaterial);
auto shovelParticleContactMaterial = simulation->
getMaterialManager()->getContactMaterialOrCreateImplicit(shovelMaterial, particleMaterial);
shovelParticleContactMaterial->setYoungsModulus(1e8);
shovelParticleContactMaterial->setFrictionCoefficient(0.3);
shovelParticleContactMaterial->setRollingResistanceCoefficient(0.1);
shovelParticleContactMaterial->setRestitution(0.0);
simulation->
add(shovelParticleContactMaterial);
simulation->
add(terrainShovel);
auto listener =
new ShovelKeyboardListener( simulation, terrainShovel->getRigidBody(),
Vec3(0.7, 0.0, -0.0), 1.0 );
simulation->
add(listener);
{
listener->getLock()->getLastForce( terrainShovel->getRigidBody(), force, torque );
}, simulation);
renderer->setRenderVoxelSolidMass(false);
renderer->setRenderVoxelFluidMass(false);
renderer->setRenderVoxelBoundingBox(false);
renderer->setRenderHeightField(true);
renderer->setRenderSoilParticles(true);
simulation->
add(renderer);
agx::Vec3 eye =
agx::Vec3(1.1446339652418182E+01, -4.0189526421746642E+00, 8.6762060961937877E+00);
agx::Vec3 center =
agx::Vec3(1.6077577107657193E+00, -7.9424674741572898E-01, 1.9513089499363527E+00);
agx::Vec3 up =
agx::Vec3(-5.1512025829023400E-01, 1.7720716398098579E-01, 8.3859927291455416E-01);
}
{
osg::Group*
root =
new osg::Group();
size_t resolution = 250;
auto hfIndexToPosition = [](
size_t index,
size_t resolution,
agx::Real size ) {
return ( (
static_cast<agx::Real>( index ) / (
static_cast<agx::Real>( resolution )) - 0.5) * size );
};
};
for ( size_t i = 0; i < resolution; i++)
{
for ( size_t j = 0; j < resolution; j++)
{
heightField->setHeight(i, j, heightFunction(hfIndexToPosition(i, resolution, size), hfIndexToPosition(j, resolution, size), height, size));
}
}
simulation->
add(terrain);
auto shovel = createSimpleShovelBody(simulation, root, shovelSize, shovelAngle);
terrain->loadLibraryMaterial("dirt_1");
shovelParticleContactMaterial->
setYoungsModulus(particleParticleCm->getYoungsModulus());
shovelParticleContactMaterial->
setRestitution(particleParticleCm->getRestitution());
simulation->
add(shovelParticleContactMaterial);
simulation->
add(shovelTerrainContactMaterial);
auto cuttingPoint1 =
agx::Vec3(2 * shovelSize.x() * cos(shovelAngle), -shovelSize.y(), 2 * shovelSize.x() * (-0.5 - std::sin(shovelAngle)));
auto cuttingPoint2 = cuttingPoint1 +
agx::Vec3(0.0, 2.0 * shovelSize.y(), 0.0);
auto topPoint1 =
agx::Vec3(0.0, -shovelSize.y(), shovelSize.x());
auto topPoint2 =
agx::Vec3(0.0, shovelSize.y(), shovelSize.x());
auto cuttingEdge =
agx::Line(cuttingPoint1, cuttingPoint2);
auto topEdge =
agx::Line(topPoint1, topPoint2);
auto forwardVector =
agx::Vec3(cos(shovelAngle), 0.0, -std::sin(shovelAngle));
terrainShovel->getSettings()->setVerticalBladeSoilMergeDistance(0.3);
terrainShovel->getAdvancedSettings()->setNoMergeExtensionDistance(0.5);
simulation->
add(terrainShovel);
shovel->setPosition(-12.0, 0.0, shovelSize.x() * (1 + 2 * std::sin(shovelAngle)) + heightFunction(-12.0, 0.0, height, size) - 0.1);
renderer->setRenderVoxelSolidMass(false);
renderer->setRenderVoxelFluidMass(false);
renderer->setRenderHeightField(true);
renderer->setRenderVoxelBoundingBox(false);
renderer->setRenderSoilParticles(true);
simulation->
add(renderer);
{
private:
public:
{
m_terrainShovel = terrainShovel;
m_bladeOffset =
agx::Vec3(shovelSize.
x() * 2 * cos(shovelAngle), 0.0, -shovelSize.
x() * (1 + 2 * sin(shovelAngle)));
m_height = height;
}
{
};
};
agx::Vec3 cuttingEdgePosition = pos + m_bladeOffset;
if (cuttingEdgePosition.
x() < 0)
{
if (cuttingEdgePosition.
z() > 0)
{
}
else
{
}
}
else if (cuttingEdgePosition.
x() >= 0)
{
if (heightFunction(cuttingEdgePosition.
x(), cuttingEdgePosition.
y(), m_height,
m_size) < -0.1)
{
}
else
{
}
}
return vel;
}
{
if (t < 24)
{
m_terrainShovel->getRigidBody()->setVelocity(this->getVelocity(m_terrainShovel->getRigidBody()->getPosition()));
}
else
{
m_terrainShovel->getRigidBody()->setVelocity(0.0, 0.0, 0.0);
}
}
};
simulation->
add(bladeDriver);
simulation->
add(measurementListener);
auto eye =
agx::Vec3(4.2554700794164848E+00, -1.8111588117481372E+01, 6.3025642249031311E+00);
auto center =
agx::Vec3(-9.4897856997624352E-01, -2.0127647520494536E+00, 7.1454264792766731E-01);
auto up =
agx::Vec3(-8.7901903076223503E-02, 3.0116556248169890E-01, 9.4951174790555393E-01);
}
{
osg::Group*
root =
new osg::Group();
size_t resolutionX = 51;
size_t resolutionY = 51;
simulation->
add(terrain);
terrain->loadLibraryMaterial("dirt_1");
simulation->
add(terrainMaterial);
terrain->setMaterial(terrainMaterial);
auto shovel = createSimpleShovelBody(simulation, root, shovelSize, shovelAngle);
agx::Vec3 cuttingPoint1 =
agx::Vec3{ 2 * shovelSize.
x() * std::cos(shovelAngle), -shovelSize.
y(), 2 * shovelSize.
x() * (-0.5 - std::sin(shovelAngle)) };
agx::Line cuttingEdge{ cuttingPoint1, cuttingPoint2 };
agx::Vec3 cuttingVector{ cos(shovelAngle), 0.0, -sin(shovelAngle) };
simulation->
add(terrainShovel);
terrainShovel->getSettings()->setVerticalBladeSoilMergeDistance(0.4);
terrainShovel->getAdvancedSettings()->setNoMergeExtensionDistance(0.5);
terrainShovel->getSettings()->setPenetrationForceScaling(0);
shovel->setPosition(
agx::Vec3(-5.5, 0.0, shovelSize.
x() * (1 + 2 * sin(shovelAngle)) + 0.15));
simulation->
add(driver, 51);
auto sandTerrainMaterial = terrain->getLibraryMaterial("sand_1");
terrain->addTerrainMaterial(sandTerrainMaterial, sandRegion);
renderer->setRenderVoxelSolidMass(false);
renderer->setRenderVoxelFluidMass(false);
renderer->setRenderVoxelBoundingBox(false);
renderer->setRenderHeightField(true);
renderer->setRenderVelocityField(false);
renderer->setVelocityFieldLineColor(
Vec4(1.0, 1.0, 0, 1));
renderer->setRenderSoilParticles(true);
renderer->setRenderTerrainMaterials(true);
renderer->setRenderDefaultTerrainMaterial(false);
simulation->
add(renderer);
}
{
osg::Group*
root =
new osg::Group();
int resolution = (int)std::floor((double)size / elementSize) + 1;
auto heightFunction = [](double x, double y, double h, double size) {
double height = 0.0;
x /= (size / 2.0);
y /= (size / 2.0);
x += 0.55;
double r = std::sqrt(x * x + y * y);
double theta = std::atan2(x, y);
double theta_threshold = M_PI / 12.0;
double theta_min = 50.0 * M_PI / 180.0;
double theta_max = M_PI - theta_min;
double deltaR = 0.2;
double R = 1.0;
double n = 1.0;
if (r > R - deltaR && theta > theta_min && theta < theta_max) {
if (r < R) {
height = h * std::pow(std::sin((r + deltaR - R) / (2 * deltaR) * M_PI), n);
}
else {
height = h;
}
}
if (theta < (theta_min + theta_threshold)) {
theta = theta - theta_min;
height *= std::sin(theta / (2 * theta_threshold) * M_PI);
}
if (theta > (theta_max - theta_threshold)) {
theta = theta_max - theta;
height *= std::sin(theta / (2 * theta_threshold) * M_PI);
}
return height;
};
terrain->setEnableMinimumHeights(true);
for (int x = 0; x < resolution; x++)
for (int y = 0; y < resolution; y++)
{
terrain->setMinimumHeight(ti, - heightFunction((double)x, (double)y, 4, size));
terrain->setHeight(ti, heightFunction((double)x, (double)y, 4, size));
}
simulation->
add(terrain);
renderer->setRenderVoxelSolidMass(false);
renderer->setRenderVoxelFluidMass(false);
renderer->setRenderVoxelBoundingBox(false);
renderer->setRenderHeightField(true);
renderer->setRenderVelocityField(false);
renderer->setVelocityFieldLineColor(
Vec4(1.0, 1.0, 0, 1));
renderer->setRenderSoilParticles(true);
renderer->setRenderTerrainMaterials(true);
renderer->setRenderDefaultTerrainMaterial(false);
simulation->
add(renderer);
}
{
osg::Group*
root =
new osg::Group();
int resX = 51;
int resY = 51;
double elementSize = 0.1;
double depth = 2.5;
simulation->
add( terrain );
localTransform,
);
terrain->addForbiddenBound( fb );
terrain->addForbiddenBound( ib );
auto si = terrain->getSoilSimulationInterface();
for ( int x = 22; x < 30; ++x ) {
for ( int y = 5; y < 45; ++y ) {
agx::Vec3 p = terrain->getSurfacePositionWorld( { x, y } );
si->createSoilParticle( p +
agx::Vec3( 0, 0, terrain->getElementSize() / 2 ) );
}
}
simulation->
add( renderer );
}
int main( int argc, char** argv )
{
std::cout <<
"\tTutorial agxTerrain\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
application->addScene( buildTutorial4, '4' );
application->addScene( buildTutorial5, '5');
application->addScene( buildTutorial6, '6');
application->addScene( buildTutorial7, '7' );
if ( application->init( argc, argv ) )
{
return application->run();
}
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
#else
int main(int , char** )
{
"\tTutorial agxTerrain\n" <<
"AGX Dynamics built without support for agxTerrain" <<
LOGGER_ENDL();
return 0;
}
#endif
Axis aligned bounding box implementation.
A HeightField is a collision shape that can be created from a grid structure, like an image.
A sphere class for geometric intersection tests.
agxOSG::SceneDecorator * getSceneDecorator()
void setCameraHome(const agx::Vec3 &eye, const agx::Vec3 ¢er, const agx::Vec3 &up=agx::Vec3(0, 0, 1))
bool setEnableDebugRenderer(bool flag)
Set enabling the debug renderer.
Decorates a scene with a specific AGX Rendering Style.
void setText(int row, const agx::String &text)
Print a row of text.
void setBackgroundColor(const agx::Vec4f &color)
Set a uniform background color using color.
Rendering class for the agxTerrain::Terrain.
static agxRender::Color Black()
static agxRender::Color Yellow()
Derive from this class to implement a listener for simulation GuiEvents.
virtual bool keyboard(int key, unsigned int modKeyMask, float x, float y, bool keydown)
MaterialManager * getMaterialManager()
Derive from this class to implement a listener for simulation step events.
virtual void post(const agx::TimeStamp &time)
Called after a step is taken in the simulation Implement this method in the derived class to get call...
Class for specifying a 3D bound where terrain operations like merging and avalanching is forbbiden.
Class for specifying a 3D terrain index bound where terrain operations like merging and avalanching i...
Shovel object used to interact with a terrain via an active zone that converts solid terrain to dynam...
agx::RigidBody * getRigidBody() const
Interface class for accessing data and functions for the internal soil particle simulation used in ag...
A terrain model based a 3D grid model with overlapping height field that can be deformed by interacti...
agx::Real getElementSize() const
Returns the element size of a cell in agxTerrain, which is incidentally the distance between the cent...
static Terrain * createFromHeightField(const agxCollide::HeightField *heightField, agx::Real maximumDepth)
Creates a terrain object by using an existing height field as a basis.
agx::Vec2i getClosestGridPoint(const agx::Vec3 &worldPosition, bool clampToBound=false) const
Finds the closes terrain grid point given any point in world.
static void post(FuncT callback, agxSDK::Simulation *simulation, Args &&... args)
Register a post step event callback.
static AgXString format(const char *format,...)
C printf formatting of a string.
Special class for small bitset, because minimum bitset size in clang/gcc is 8 bytes,...
A BoundT represents a range defined by a minimum and a maximum value.
agx::Vec3 transformPointToWorld(const agx::Vec3 &pointLocal) const
Transform point from this frame to the world frame.
Constraint that removes all 6 DOF between two bodies, or one body and the world.
Main material class which acts as a holder of a Surface Material and a Bulk material.
void setName(const agx::Name &name)
Set the name of the object.
@ KINEMATICS
This body's motion is scripted.
agx::Quat getRotation() const
Current model frame rotation, given in world coordinate frame.
void setVelocity(const agx::Vec3 &velocity)
Set the linear velocity of the center of mass of this rigid body.
void setMotionControl(agx::RigidBody::MotionControl control)
Assign new motion control state to this rigid body.
void setPosition(const agx::Vec3 &p)
Set the position of the model frame in world coordinates.
agx::Frame * getFrame()
Returns the model frame containing model the transformation and utilities to manipulate position,...
Real normalize()
Normalize the vector so that it has length unity.
const Vec3T cross(const Vec3T &rhs) const
Cross product method.
A class holding 4 dimensional vectors and providing basic arithmetic.
Smart pointer for observed objects, that automatically set pointers to them to null when they deleted...
AGXPHYSICS_EXPORT bool setBodyMaterial(agx::RigidBody *body, agx::Material *material)
Utility function to loop through a body and set the material for associated geometry.
The agx namespace contains the dynamics/math part of the AGX Dynamics API.
static constexpr Real PI_4
Vec3T< Real > Vec3
The object holding 3 dimensional vectors and providing basic arithmetic.
void AGXCORE_EXPORT setNumThreads(size_t numThreads)
Set the number of threads to use (including the main thread).
static constexpr Real PI_2
AGXCORE_EXPORT const InvalidIndexStruct InvalidIndex
tutorial_agxTerrain_ClamShellBucket.cpp
#if AGX_USE_AGXTERRAIN()
#include "terrain_shovel_utils.h"
namespace
{
{
public:
: Assembly()
{
this->
setName(
"ClamShellAssembly" );
const auto segmentSize =
agx::Vec3( 0.035, 1.0, 0.1 );
const auto numSegments = 20;
std::vector<std::pair<agx::Vec3, agx::Real>> shovelSizes;
shovelSizes.reserve( numSegments );
agx::Real deltaAngle = 180.0 / (numSegments - 1);
for (int i = 0; i < numSegments; ++i)
shovelSizes.push_back( { segmentSize, i * deltaAngle } );
bucketData.topEdge,
bucketData.cuttingEdge,
bucketData.cuttingDirection );
bucket1->getRigidBody()->setName( "Bucket1" );
bucketData2.topEdge,
bucketData2.cuttingEdge,
bucketData2.cuttingDirection );
bucket2->getRigidBody()->setName( "Bucket2" );
this->
add( clamShellBucket );
double radius = 0.15;
cylinderBody->setPosition(
agx::Vec3( 0, 0, radius ) );
cylinderBody->setName( "CylinderBody" );
this->
add( cylinderBody );
bucketHinge1 = createBucketHingeJoint( hf1, cylinderBody, bucket1->getRigidBody() );
bucketHinge1->setName( "BucketHinge1" );
this->
add( bucketHinge1 );
bucketHinge2 = createBucketHingeJoint( hf2, cylinderBody, bucket2->getRigidBody() );
bucketHinge2->setName( "BucketHinge2" );
this->
add( bucketHinge2 );
const auto connectorBodySize =
agx::Vec3( 1.0, 0.15, 0.25 );
connectorBody->setPosition(
agx::Vec3( 0, 0, 1.5 ) );
connectorBody->setName( "ConnectorBody" );
this->
add( connectorBody );
const auto topCraneBodySize =
agx::Vec3( 1.0, 4.0, 0.25 );
topCraneBody->setPosition(
agx::Vec3( 0, -topCraneBodySize.y(), 5 ) );
topCraneBody->setName( "TopBodyCrane" );
this->
add( topCraneBody );
distanceJoint1 = createCraneDistanceJoint( connectorBody,
agx::Vec3( connectorBodySize.x(), 0, connectorBodySize.z() ),
topCraneBody,
agx::Vec3( topCraneBodySize.x(), topCraneBodySize.y(), -topCraneBodySize.z() ) );
distanceJoint1->setName( "DistanceJoint1" );
this->
add( distanceJoint1 );
distanceJoint2 = createCraneDistanceJoint( connectorBody,
agx::Vec3( -connectorBodySize.x(), 0, connectorBodySize.z() ),
topCraneBody,
agx::Vec3( -topCraneBodySize.x(), topCraneBodySize.y(), -topCraneBodySize.z() ) );
distanceJoint2->setName( "DistanceJoint2" );
this->
add( distanceJoint2 );
topHubBody->setPosition( topCraneBody->getPosition() +
agx::Vec3( 0, -4, 0 ) );
topHubBody->setName( "TopHubBody" );
topBodyHinge =
new agx::Hinge( topHf, topCraneBody, topHubBody );
topBodyHinge->getMotor1D()->setEnable( true );
topBodyHinge->getMotor1D()->setLockedAtZeroSpeed( true );
topBodyHinge->setName( "TopHubBodyHinge" );
this->
add( topBodyHinge );
connectorBody->getMassProperties()->setMass( connectorBody->getMassProperties()->getMass() * 5 );
constaint->setCompliance( 1E-12 );
}
{
auto bucketHinge =
new agx::Hinge( frame, body1, body2 );
bucketHinge->getMotor1D()->setEnable( true );
bucketHinge->getMotor1D()->setLockedAtZeroSpeed( true );
bucketHinge->getRange1D()->setEnable( true );
bucketHinge->getRange1D()->setRange( { 0,
agx::PI } );
bucketHinge->getRange1D()->setCompliance( 1E-14 );
return bucketHinge;
}
{
distanceJoint->getLock1D()->setEnable( false );
distanceJoint->getMotor1D()->setEnable( true );
distanceJoint->getMotor1D()->setLockedAtZeroSpeed( true );
distanceJoint->setEnableComputeForces( true );
return distanceJoint;
}
void resetVelocities()
{
distanceJoint1->getMotor1D()->setSpeed( 0 );
distanceJoint2->getMotor1D()->setSpeed( 0 );
bucketHinge1->getMotor1D()->setSpeed( 0 );
bucketHinge2->getMotor1D()->setSpeed( 0 );
topBodyHinge->getMotor1D()->setSpeed( 0 );
}
void setDistanceJointSpeed(
agx::Real speed )
{
distanceJoint1->getMotor1D()->setSpeed( speed );
distanceJoint2->getMotor1D()->setSpeed( speed );
}
void setBucketHingeJointSpeed(
agx::Real speed )
{
bucketHinge1->getMotor1D()->setSpeed( speed );
bucketHinge2->getMotor1D()->setSpeed( speed );
}
{
topBodyHinge->getMotor1D()->setSpeed( speed );
}
{
return distanceJoint1->getMotor1D()->getCurrentForce()
+ distanceJoint1->getMotor1D()->getCurrentForce();
}
{
return bucket1->getSoilParticleAggregate()->getMass() + bucket2->getSoilParticleAggregate()->getMass();
}
{
return bucket1->getRigidBody()->getMassProperties()->getMass()
+ bucket2->getRigidBody()->getMassProperties()->getMass()
+ connectorBody->getMassProperties()->getMass()
+ cylinderBody->getMassProperties()->getMass();
}
public:
};
{
public:
ClamShellKeyboardListener( ClamShellBucketAssembly* clamShell,
float maxSpeed = 1.5,
: m_clamShellAssembly(clamShell)
, m_maxSpeed( maxSpeed )
, m_maxRotSpeed( maxRotSpeed )
{
}
bool keyboard(
int key,
unsigned ,
float ,
float ,
bool keyDown) {
bool handled = false;
if (!keyDown) {
m_clamShellAssembly->resetVelocities();
return handled;
}
switch (key)
{
case 'a':
m_clamShellAssembly->setDistanceJointSpeed( -m_maxSpeed );
break;
case 'z':
m_clamShellAssembly->setDistanceJointSpeed( m_maxSpeed );
break;
case 's':
m_clamShellAssembly->setBucketHingeJointSpeed( -m_maxRotSpeed );
break;
case 'x':
m_clamShellAssembly->setBucketHingeJointSpeed( m_maxRotSpeed );
break;
m_clamShellAssembly->setTopBodyHingeSpeed( -m_maxRotSpeed / 4 );
break;
m_clamShellAssembly->setTopBodyHingeSpeed( m_maxRotSpeed / 4 );
break;
default:
break;
}
return handled;
}
private:
ClamShellBucketAssembly* m_clamShellAssembly;
float m_maxSpeed;
float m_maxRotSpeed;
};
{
public:
ClamShellBucketAssembly* assembly)
: m_sd( application->getSceneDecorator() )
, m_assembly( assembly )
{
}
{
m_sd->setText( 1,
agx::String::format(
"ClamShell is closed: %d", m_assembly->clamShellBucket->isClosed() ), color );
m_sd->setText( 2,
agx::String::format(
"Soil Mass in Bucket %7.4f ton", m_assembly->getSoilMassInBucket() / 1e3 ), color );
m_sd->setText( 3,
agx::String::format(
"ClamShell Body Mass %7.4f ton", m_assembly->getClamShellBodyMass() / 1e3 ), color );
m_sd->setText( 4,
agx::String::format(
"Crane Load %7.4f kN", m_assembly->getDistanceJointForce() / 1e3 ), color );
}
private:
ClamShellBucketAssembly* m_assembly;
};
}
{
osg::Group*
root =
new osg::Group();
size_t resolutionX = 240;
size_t resolutionY = 240;
simulation->
add( terrain );
terrain->loadLibraryMaterial( "dirt_1" );
simulation->
add(terrainMaterial);
terrain->setMaterial( terrainMaterial );
auto clamShellAssembly = new ClamShellBucketAssembly(shovelMaterial);
simulation->
add( clamShellAssembly );
auto shovelTerrainContactMaterial = simulation->
getMaterialManager()->getContactMaterialOrCreateImplicit( shovelMaterial, terrainMaterial );
simulation->
add( shovelTerrainContactMaterial );
auto shovelParticleContactMaterial = simulation->
getMaterialManager()->getContactMaterialOrCreateImplicit( shovelMaterial, particleMaterial );
simulation->
add( shovelParticleContactMaterial );
clamShellAssembly->setPosition(
agx::Vec3(0, 0, 8.0));
auto keyListener = new ClamShellKeyboardListener( clamShellAssembly );
simulation->
add( keyListener );
auto mListener = new MeasurementListener( application, clamShellAssembly );
simulation->
add( mListener );
int startIndex = 8;
renderer->setRenderVoxelSolidMass(false);
renderer->setRenderVoxelFluidMass(false);
renderer->setRenderVoxelBoundingBox(false);
renderer->setRenderHeightField(true);
renderer->setRenderVelocityField(false);
renderer->setVelocityFieldLineColor(
Vec4(1.0, 1.0, 0, 1));
renderer->setRenderSoilParticles(true);
simulation->
add(renderer);
}
int main( int argc, char** argv )
{
std::cout <<
"\tTutorial agxTerrain\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
if ( application->init( argc, argv ) )
{
return application->run();
}
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
#else
int main(int , char** )
{
"\tTutorial agxTerrain\n" <<
"AGX Dynamics built without support for agxTerrain" <<
LOGGER_ENDL();
return 0;
}
#endif
A cylinder shape for geometric intersection tests.
Utility color class with "common colors".
An assembly is a collection of basic simulation objects, such as rigid bodies, constraints,...
const agx::ConstraintRefSetVector & getConstraints() const
void setName(const agx::Name &name)
Set the name of this Assembly.
bool add(agx::Constraint *constraint)
Add a constraint to the assembly.
const agx::RigidBodyRefSetVector & getRigidBodies() const
ClamShellBucket is a CompositeShovel class that models a clamshell bucket that consists of two opposi...
void setDensity(Real density)
Set the density of a material.
This joint will preserve the initial distance between a body and a point in world coordinate or betwe...
Specialization for the constraint frame class that add convenient accessors/mutator methods for manip...
BulkMaterial * getBulkMaterial()
@ STATIC
This body will never move.
AGXOSG_EXPORT void setDiffuseColor(osg::Node *node, const agx::Vec4f &color, bool override=false)
Set the diffuse part of a material for a node.
AGXTERRAIN_EXPORT ToolShapeData createCustomBucketData(const std::vector< std::pair< agx::Vec3, agx::Real > > &sizes, bool createWalls=true)
Utility function that creates a custom bucket from a list of box half-vectors and angles and associat...
AGXPHYSICS_EXPORT bool setEnableCollisions(agx::RigidBody *rb1, agx::RigidBody *rb2, bool enable)
Enable or disable the collision between all geometries in RigidBody rb1 and RigidBody rb2.
tutorial_ballJoint_secondaryConstraints.cpp
#include "tutorialUtils.h"
{
rodGeom->setPosition(0, 0, 0.5);
osg::Group* root = new osg::Group;
simulation->
add(ballJoint);
ballJoint->getConeLimit()->setEnable(true);
ballJoint->getConeLimit()->setLimitAngles(PI / 4, PI / 3);
return root;
}
{
rodGeom->setPosition(0, 0, 0.5);
osg::Group* root = new osg::Group;
simulation->
add(ballJoint);
ballJoint->getConeLimit()->setEnable(true);
ballJoint->getConeLimit()->setLimitAngles(PI / 4, PI / 3);
FrictionControllerRef rotationalFrictionController = ballJoint->getConeLimitFrictionControllerRotational();
limitFrictionController->setEnable(true);
limitFrictionController->setFrictionCoefficient(0.1);
rotationalFrictionController->setEnable(true);
rotationalFrictionController->setFrictionCoefficient(0.1);
ballJoint->setEnableConeLimitFriction(true);
ballJoint->setConeLimitFrictionCoefficients(0.1);
return root;
}
{
rodGeom->setPosition(0, 0, 0.5);
osg::Group*
root =
new osg::Group;
simulation->
add(ballJoint);
ballJoint->setEnableRotationalFriction(true);
ballJoint->setRotationalFrictionCoefficients(0.3);
RealVector frictionCoeffs = ballJoint->getRotationalFrictionCoefficients();
}
{
rodGeom->setPosition(0, 0, 0.5);
simulation->
add(ballJoint);
twistRange->setEnable(true);
twistRange->setRange(-PI_4, PI_4);
simulation->
add(hingeBox);
motor->setSpeed(-0.5);
motor->setForceRange(-100, 100);
return new osg::Group();
}
int main( int argc, char** argv )
{
std::cout <<
"\tBall Joint Limits tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
application->addScene(buildTutorial2, '2');
application->addScene(buildTutorial3, '3');
application->addScene(buildTutorial4, '4');
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
Constraint that removes the three translation DOF between two bodies (or one and the world).
agx::Motor1D * getMotor1D()
virtual void setEnable(agx::Bool enable)
Enable/disable this elementary constraint.
void setTranslate(const agx::Vec3 &translate)
Assign the final (world) translate of this frame.
void setAngularVelocity(const agx::Vec3 &angularVelocity)
Set the angular velocity of the center of mass of this rigid body.
bool add(agxCollide::Geometry *geometry, const agx::AffineMatrix4x4 &localTransform, bool incrementalMassCalculation=false)
Connect a geometry to this rigid body.
The agxOSG namespace provides functionality for visualizing AGX simulations with OpenSceneGraph.
tutorial_basicSimulation.cpp
int main( int , char** )
{
std::cout <<
"\tBasic simulation tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
std::cerr << "Creating a simulation" << std::endl;
std::cerr << "Creating a body" << std::endl;
std::cerr << "Adding body to the simulation" << std::endl;
mySimulation->add( rb );
mySimulation->getDynamicsSystem()->getTimeGovernor()->setTimeStep( dt );
std::cerr << "Step system 3 seconds forward in time" << std::endl;
mySimulation->stepForward();
mySimulation->stepTo( mySimulation->getTimeStamp() + 3 );
}
catch(std::runtime_error& e)
{
std::cerr << "Caught exception: " << e.what() << std::endl;
}
std::cerr << "Shutting down AGX" << std::endl;
return 0;
}
void AGXPHYSICS_EXPORT shutdown()
Shutdown of the AGX Dynamics API will be done when the number of shutdown matches the number of calls...
tutorial_beam.cpp
namespace utils
{
template<typename T>
void createVisual( T obj,
osg::Group* root,
{
}
}
{
auto root =
new osg::Group();
steelBeamProperties->setYoungsModulus( 210.0E9 );
steelBeamProperties->setPoissonsRatio( 0.310 );
0.15,
steelBeamProperties );
0.17,
7.0E-3,
steelBeamProperties );
0.14,
7.0E-3,
9.0E-3,
steelBeamProperties );
steelBeamProperties );
12.0E-3,
steelBeamProperties );
BeamModelPtrVector steelBeamModels {
steelRectangularModel,
steelHollowRectangularModel,
steelIBeamModel,
steelCircularModel,
steelHollowCircularModel
};
for ( auto steelBeamModel : steelBeamModels ) {
steelBeam_10->setPosition( 0.0, yOffset, 0.0 );
steelBeam_10->setMaterial( steelBeamMaterial );
simulation->
add( steelBeam_10 );
simulation->
add( steelBeam_10->attachBegin().
release() );
utils::createVisual( steelBeam_10, root, beamColor );
yOffset += 0.5;
steelBeam_15->setPosition( 0, yOffset, 0 );
steelBeam_15->setMaterial( steelBeamMaterial );
simulation->
add( steelBeam_15 );
simulation->
add( steelBeam_15->attachBegin().
release() );
utils::createVisual( steelBeam_15, root, beamColor );
yOffset += 0.5;
steelBeam_20->setMaterial( steelBeamMaterial );
simulation->
add( steelBeam_20 );
simulation->
add( steelBeam_20->attachBegin().
release() );
utils::createVisual( steelBeam_20, root, beamColor );
yOffset += 0.5;
steelBeamModel,
25u,
steelBeam_25->setMaterial( steelBeamMaterial );
simulation->
add( steelBeam_25 );
simulation->
add( steelBeam_25->attachBegin().
release() );
utils::createVisual( steelBeam_25, root, beamColor );
yOffset += 1.0;
}
for ( const auto beam : beams )
std::cout << beam->getModel()->getClassName() <<
": Mass = " << beam->getMass() << " kg, Resolution = " << beam->getNumSegments() << std::endl;
cameraData.center =
agx::Vec3( 4.1953, 5.8053, 0.2871 );
cameraData.up =
agx::Vec3( 0.1990, 0.1198, 0.9727 );
cameraData.nearClippingPlane = 0.1;
cameraData.farClippingPlane = 5000;
}
namespace utils
{
osg::Group* root )
{
const auto supportRadius = 0.85 * radius;
const auto guideThickness = 5.0E-2 * radius;
const auto supportThickness = 5.0E-2 * supportRadius;
0.31 );
guideThickness,
properties );
supportThickness,
properties );
guideModel,
resolution );
guideBeam->setName( "guide" );
assembly->add( guideBeam );
const auto supportBeginPosition = guideBeam->getBeginPosition() -
auto attachmentGuideSegment = guideBeam->getSegment( a * L );
const auto attachmentGuideFrame = attachmentGuideSegment->getRigidBody()->getFrame();
const auto localAttachmentLength = a * L -
guideBeam->findRestLengthTo( attachmentGuideSegment );
const auto supportEndPosition = attachmentGuideFrame->transformPointToWorld( guideModel->getRadius(),
0.0,
localAttachmentLength );
supportEndPosition,
supportModel,
supportResolution );
supportBeam->setName( "support" );
assembly->add( supportBeam );
if ( guideIsAttachmentParent ) {
guideSupportAttachment = guideBeam->attach( attachmentGuideSegment,
0,
localAttachmentLength ),
supportBeam->getSegments().back()->getRigidBody() );
guideBeam->calculateSegmentStiffnessDamping().apply( guideSupportAttachment, 2.0 );
}
else {
guideSupportAttachment = supportBeam->attachEnd( attachmentGuideSegment->getRigidBody() );
supportBeam->calculateSegmentStiffnessDamping().apply( guideSupportAttachment, 2.0 );
}
guideSupportAttachment->setName( "attachment" );
assembly->add( guideSupportAttachment );
return assembly;
}
}
{
auto root =
new osg::Group();
ground->setPosition( 0, 0, -0.15 );
simulation->
add( ground );
{
0.25,
0.25 ) ) );
beam->getSegments().front() :
beam->getSegments().back() );
simulation->
add( payload );
attachment->getElementaryConstraint( 1u )->setEnable( false );
simulation->
add( attachment );
};
10u );
simulation->
add( beamStartGround );
agx::LockJointRef beamStartGroundAttachment = beamStartGround->attachBegin(
nullptr,
true );
simulation->
add( beamStartGroundAttachment );
beamStartGround->setEnableCollisions( ground, false );
addPayload( beamStartGround, false );
beamStartGround->getModel(),
30u );
simulation->
add( beamEndGround );
agx::LockJointRef beamEndGroundAttachment = beamEndGround->attachEnd(
nullptr,
true );
simulation->
add( beamEndGroundAttachment );
beamEndGround->setEnableCollisions( ground, false );
addPayload( beamEndGround, true );
auto guideSupportAssembly = utils::createGuideSupport( 0.08,
20u,
2.5,
0.70,
false,
root );
simulation->
add( guideSupportAssembly );
auto guideBeam = guideSupportAssembly->getAssembly(
"guide" )->asSafe<
agxModel::Beam>();
auto supportBeam = guideSupportAssembly->getAssembly(
"support" )->asSafe<
agxModel::Beam>();
guideMaterial->getBulkMaterial()->setDensity( 2700 );
guideBeam->setMaterial( guideMaterial );
guideSupportAssembly->setPosition( -1, 2, 0 );
simulation->
add( guideBeam->attachBegin().
release() );
guideBeam->setEnableCollisions( ground, false );
simulation->
add( heavyBox );
guideHeavyBoxContactMaterial->setUseContactAreaApproach( true );
cameraData.center =
agx::Vec3( 0.5826, -0.4139, 2.5559 );
cameraData.up =
agx::Vec3( 0.1333, 0.2732, 0.9527 );
cameraData.nearClippingPlane = 0.1;
cameraData.farClippingPlane = 5000;
}
{
auto root =
new osg::Group();
const auto supportOutwardRotationDeg = 30.0;
ground->setPosition( 0, 0, -0.25 );
simulation->
add( ground );
auto guide1Assembly = utils::createGuideSupport( 0.07, 30u, 2.5, 0.6, true, root );
auto guide1 = guide1Assembly->getAssembly(
"guide" )->asSafe<
agxModel::Beam>();
guide1Assembly->getRotation() *
ground->getRotation() );
auto guide2Assembly = utils::createGuideSupport( 0.07, 30u, 2.5, 0.6, false, root );
auto guide2 = guide2Assembly->getAssembly(
"guide" )->asSafe<
agxModel::Beam>();
guide2Assembly->getRotation() *
ground->getRotation() );
simulation->
add( guide1Assembly );
simulation->
add( guide2Assembly );
auto guideSupportAttachmentA = guide1Assembly->getConstraint( "attachment" );
auto guideSupportAttachmentB = guide2Assembly->getConstraint( "attachment" );
simulation->
add( guide1->attachBegin().release() );
simulation->
add( guide2->attachBegin().release() );
guide1->setEnableCollisions( ground, false );
guide2->setEnableCollisions( ground, false );
sphere->
setPosition( ground->getFrame()->transformPointToWorld( 5.0, 0, 2.0 + 0.25 ) );
simulation->
add( sphere );
guide1->getModel()->setProperties( guideProperties );
guide2->getModel()->setProperties( guideProperties );
guide1->setMaterial( guideMaterial );
guide2->setMaterial( guideMaterial );
contactMaterial->setRestitution( 0.0 );
contactMaterial->setUseContactAreaApproach( true );
cameraData.center =
agx::Vec3( 1.3613, -0.9364, 1.9912 );
cameraData.up =
agx::Vec3( 0.0056, 0.1218, 0.9925 );
cameraData.nearClippingPlane = 0.1;
cameraData.farClippingPlane = 5000;
}
int main( int argc, char** argv )
{
"\tTutorial Beam (agxModel::Beam)\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
Bulk beam model properties present in all agxModel::BeamModel instances, which in turn propagates the...
void setProperties(BeamModelProperties *properties)
Assign properties instance, a new default will be created if properties is nullptr.
Beam segment in an agxModel::Beam containing a rigid body, a constraint and is of fixed length.
Beam is a segmented, lumped element structure of a given length and resolution.
static agx::AffineMatrix4x4 calculateTransform(agx::Vec3 startPosition, agx::Vec3 forward, agx::Vec3 worldUpAxis=agx::Vec3::Z_AXIS())
Calculates the transform of a beam given start position, forward direction and world up axis the beam...
static agxModel::BeamPtrVector findAll(const agxSDK::Simulation *simulation)
Finds all agxModel::Beam instances in the given simulation.
static agx::Quat calculateRotation(agx::Vec3 forward, agx::Vec3 worldUpAxis=agx::Vec3::Z_AXIS())
Calculates the rotation of a beam frame given forward direction and world up axis the beam frame up a...
agx::LockJointRef attachBegin(agx::RigidBody *otherRb=nullptr, agx::Bool adoptModelProperties=false) const
Utility method to attach the first segment of this beam at begin (of the segment) to another rigid bo...
BeamModel * getModel() const
static BeamRef create(agx::Vec3 startPosition, agx::Vec3 endPosition, BeamModel *model, agx::UInt resolution, agx::Vec3 worldUpAxis=agx::Vec3::Z_AXIS())
Creates an initialized beam between the given start and end positions, beam model,...
Circular cross section beam model with a given radius.
Hollow circular cross section beam model with a given (outer) radius and thickness.
Hollow rectangular cross section beam model with a given width, height and thickness.
I-beam model with a given width, height, flange thickness and web thickness.
Rectangular cross section beam model with a given width and height.
agxOSG::CameraData getCameraData() const
void applyCameraData(const agxOSG::CameraData &cameraData)
Apply camera data.
static agxRender::Color Goldenrod()
static agxRender::Color GhostWhite()
static agxRender::Color AliceBlue()
static agxRender::Color SkyBlue()
static agxRender::Color DodgerBlue()
static AGXPHYSICS_EXPORT agxRender::Color getColor(size_t)
static agxRender::Color RosyBrown()
static agxRender::Color LightSkyBlue()
static agxRender::Color DarkGreen()
void setPosition(const agx::Vec3 &p)
Set the position of the frame in world coordinates.
const agxSDK::Assembly * getAssembly(const agx::Name &name, bool recursive=true) const
Find (linear search) and return named Assembly.
agx::Vec3 getEndPosition() const
agx::Vec3 getBeginPosition() const
virtual void setMaterial(agx::Material *material)
Assign material to all segments in this linked structure.
virtual void setEnableCollisions(agx::RigidBody *rb, agx::Bool enable)
Enable/disable collisions between the rigid body and all segments of this linked structure.
agx::ContactMaterial * getOrCreateContactMaterial(const agx::Material *material1, const agx::Material *material2)
Returns or creates an explicit contact material.
The body fixed node is attached to a body at a given offset.
Interface and placeholder of controllers/helpers for wires.
bool setMass(Real m, bool autoGenerate=false)
Set the mass.
static QuatT< Real > rotate(Real angle, Real x, Real y, Real z)
Static method which constructs and returns a Quaternion from a rotation given as angle radians around...
T * asSafe()
Safe subclass casting, return nullptr if template type does not match.
void setLinearVelocityDamping(float damping)
Set linear velocity damping for the body in all directions x, y and z.
void setRotation(const agx::Quat &q)
Set the rotation of the body relative to world frame.
const agx::MassProperties * getMassProperties() const
T * release()
Release the reference (without decrementing) to the referenced object and return a native pointer.
AGXPHYSICS_EXPORT void debugRenderConstraintFrames(const agx::Constraint *constraint, float scale, agxRender::RenderManager *mgr=nullptr, agx::Bool overrideInvalid=false)
AGXCORE_EXPORT UInt32 hash(const T &key)
tutorial_bodies.cpp
#include "tutorialUtils.h"
{
osg::Group* root = new osg::Group();
dynamicRBGeometry->add( dynamicRBBox );
dynamicRB->
add( dynamicRBGeometry );
simulation->
add( dynamicRB );
simulation->
add( staticRB );
return root;
}
{
osg::Group* root = new osg::Group();
simulation->
add( dynamicRB );
simulation->
add( staticRB );
return root;
}
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 10, 10, 0.2 );
simulation->
add( boxGeometry );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( largeSphereRB );
simulation->
add( smallSphereRB );
}
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 10, 10, 0.2 );
simulation->
add( boxGeometry );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
agx::Vec3 dynamicsHalfExtent( 0.5, 0.5, 1.5 );
}
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 10, 10, 0.2 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
theABody->
add( side1Geometry );
agx::Vec3 sideLeftRightGeometryOffset( -1.0, 0, 0 );
side1Geometry->setLocalPosition( sideLeftRightGeometryOffset );
theABody->
add( side2Geometry );
side2Geometry->setLocalPosition( -sideLeftRightGeometryOffset );
theABody->
add( topGeometry );
topGeometry->setLocalPosition(
agx::Vec3( 0, 0, 1.2 ) );
theABody->
add( middleGeometry );
simulation->
add( theABody );
}
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 10, 10, 0.2 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
agx::Vec3 box1RightSurfaceLocalPoint( 2 * boxHalfExtent.x(), 0, 0 );
simulation->
add( box1Body );
simulation->
add( box2Body );
}
#define LEAF_SIZE 1.0
{
public:
LeafForceField(
const agx::Vec3& dropZone ) : m_torqueFactor(LEAF_SIZE*-0.0011), m_boyancyFactor(LEAF_SIZE*-0.2)
{
m_dropZone = dropZone;
}
private:
};
{
agx::Vec3( (0.1*0.1+LEAF_SIZE*LEAF_SIZE)*0.01,
2*LEAF_SIZE*LEAF_SIZE*0.01,
(0.1*0.1+LEAF_SIZE*LEAF_SIZE)*0.01));
agx::random( -m_dropZone[2]*0.1, m_dropZone[2]*0.1 ) ) );
}
{
{
continue;
N[0] = m(0,2);
N[1] = m(1,2);
N[2] = m(2,2);
boyancy *=boyancy*
sign*m_boyancyFactor;
agx::Vec3 torque = (N ^ v)*(m_torqueFactor*sign);
}
}
{
public:
AgeStepListener(
agx::Real maxAge ) : m_maxAge(maxAge) {}
{
{
{
double age = t - createTime;
if (age > m_maxAge)
{
LeafForceField::initBody(body);
createTime = t;
}
}
}
}
private:
};
{
osg::Group*
root =
new osg::Group();
srand(0);
for(size_t i=0; i < 500; i++)
{
boxGeometry->addGroup( 1 );
box1Body->
add( boxGeometry );
LeafForceField::initBody(box1Body);
simulation->
add( box1Body );
}
simulation->
add(
new AgeStepListener(10) );
}
{
return body;
}
{
osg::Group*
root =
new osg::Group();
ground->setPosition(0, 0, -0.1);
}
int main( int argc, char** argv )
{
std::cout <<
"\tRigidBody tutorial 1\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
application->addScene( buildTutorial4, '4' );
application->addScene( buildTutorial5, '5' );
application->addScene( buildTutorial6, '6' );
application->addScene(buildTutorial7, '7');
application->addScene( buildTutorial8, '8' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
void setEnablePair(agx::UInt32 group1, agx::UInt32 group2, bool flag)
Enable/disable collisions between two geometry group ID:s.
static agxRender::Color OrangeRed()
static agxRender::Color BlueViolet()
Simulation * getSimulation()
agx::DynamicsSystem * getDynamicsSystem()
agxCollide::Space * getSpace()
static AffineMatrix4x4T< Real > translate(const Vec3T< Real > &dv)
Return a matrix which translates according to dv.
The complete physical system with bodies, interactions, data layout, time stepper,...
const agx::RigidBodyRefVector & getRigidBodies() const
Fields produce force on physical bodies which have the appropriate charge.
void setLocalTranslate(const agx::Vec3 &translate)
Assign the parent relative translate of this frame.
virtual void updateForce(agx::DynamicsSystem *dynamicsSystem)
This virtual method is implemented so that it computes the interaction forces on all the physical bod...
void setAutoGenerateMask(uint32_t mask)
Specify which parameters should be automatically calculated from the geometries of the body given a b...
bool setInertiaTensor(const SPDMatrix3x3 &m, bool autoGenerate=false)
Set the inertia tensor as a full 3x3 tensor matrix.
@ AUTO_GENERATE_ALL
Specifies that the all of the attributes should be automatically updated.
@ INERTIA
Specifies that the inertia should be automatically updated.
@ CM_OFFSET
Specifies that the center of mass should be automatically updated.
@ MASS
Specifies that the mass should be automatically updated.
const agxCollide::GeometryRefVector & getGeometries() const
const agx::AffineMatrix4x4 & getTransform() const
Current model frame transform, given in world coordinate frame.
agx::Frame * getCmFrame()
Return the center of mass (CM) frame of this rigid body.
void setCmPosition(const agx::Vec3 &p, bool synchronizeModel=true)
Assign new center of mass position, given in world coordinate frame.
void addForce(const agx::Vec3 &force)
Add the specified force, given in world coordinate frame, that will be affecting this body in the nex...
bool hasPropertyContainer() const
agx::PropertyContainer * getPropertyContainer() const
Access property container of this rigid body.
void updateMassProperties()
Method to explicitly update mass properties.
agx::Vec3 getVelocity() const
Velocity of center of mass frame origin, in world coordinate frame.
void addTorque(const agx::Vec3 &torque)
Adds the specified torque, given in world coordinate frame, that will be affecting this body in the n...
static Vec3T random(T min=T(0), T max=T(1))
tutorial_bulldozer_with_terrain.cpp
#include <iostream>
#if AGX_USE_OIS() && AGX_USE_AGXTERRAIN()
#include <signal.h>
#include <cmath>
#include <functional>
#include <unordered_map>
#include <agxCFG/utils.h>
static bool g_isUnitTest = false;
namespace {
#ifdef _MSC_VER
BOOL WINAPI sighandler(DWORD )
{
std::cout << "Caught: CTRL-C or closing window. Shutting down agx." << std::endl;
}
#else
void sighandler(int )
{
std::cout << "Caught: CTRL-C or closing window. Shutting down agx." << std::endl;
}
#endif
}
namespace Utilities {
struct Foundation
{
Foundation(
osg::Group* root)
: simulation(simulation)
, application(application)
{
}
};
if (rb == nullptr) {
throw std::invalid_argument("RigidBody is null");
}
double radius = -1.0f;
double height = -1.0f;
for (const auto& geometry : geometries) {
if (!geometry->getEnableCollisions()) {
continue;
}
for (const auto& shape : geometry->getShapes()) {
if (cylinder) {
radius = std::max(radius, cylinder->getRadius());
height = std::max(height, cylinder->getHeight());
}
}
}
if (radius > 0) {
return std::make_pair(radius, height);
}
else {
throw std::runtime_error(
"Radius and height not found in body: " + rb->
getName());
}
}
void readSettings(const std::string& path, agxJson::Value& settings)
{
agxJson::Reader jsonReader;
auto filePath = filePathContainer.
find(path);
std::ifstream file_stream(filePath);
if (!file_stream.is_open()) {
throw std::runtime_error("We can't open the bulldozer json configuration file at: " + path);
}
std::stringstream buffer;
buffer << file_stream.rdbuf();
bool status = jsonReader.parse(buffer.str(), settings);
if (!status)
throw std::runtime_error("We can't parse the supplied bulldozer json configuration file at: " + path);
}
agx::Vec3 jsonArrayToVec(
const agxJson::Value& array)
{
return agx::Vec3(array[0].asDouble(), array[1].asDouble(), array[2].asDouble());
}
{
if (array.isArray())
{
for (auto d : array)
{
}
}
else
{
}
return vec;
}
std::vector<agx::Hinge*> filtered_hinges;
for (auto& constraint : constraints) {
if ((constraint->getBodyAt(0) == rigid_body || constraint->getBodyAt(1) == rigid_body)
&&
dynamic_cast<agx::Hinge*
>(constraint.get()) !=
nullptr) {
filtered_hinges.push_back(
dynamic_cast<agx::Hinge*
>(constraint.get()));
}
}
assert(filtered_hinges.size() == 1);
return filtered_hinges.front();
}
{
assert(hinge);
return hinge;
}
{
if (material == nullptr)
{
simulation->
add(material);
}
return material;
}
bool inTime(double currentTime, double time)
{
return std::abs(currentTime - time) <= 1.0 / 60;
}
double hillHeightFunction(double x, double y, double maxHeight, double size)
{
double height = 0;
x /= (size / 2.0);
y /= (size / 2.0);
x += 0.55;
double r = std::sqrt(x * x + y * y);
double theta = std::atan2(x, y);
const double theta_threshold = M_PI / 12.0;
const double theta_min = 50.0 * M_PI / 180.0;
const double theta_max = M_PI - theta_min;
const double deltaR = 0.15;
const double R = 1.25;
const int n = 1;
if (r > R - deltaR && theta > theta_min && theta < theta_max) {
if (r < R) {
height = maxHeight * std::pow(std::sin((r + deltaR - R) / (2 * deltaR) * M_PI), n);
}
else {
height = maxHeight;
}
}
if (theta < (theta_min + theta_threshold)) {
theta = theta - theta_min;
height *= std::sin(theta / (2 * theta_threshold) * M_PI);
}
if (theta > (theta_max - theta_threshold)) {
theta = theta_max - theta;
height *= std::sin(theta / (2 * theta_threshold) * M_PI);
}
return height;
}
double length = 4.0,
double width = 2.0,
double height = 2.0,
float compaction = 1.0,
{
int numLenPoints =
static_cast<int>((length) / terrain->
getElementSize()) + 1;
int numWidthPoints =
static_cast<int>((width) / terrain->
getElementSize()) + 1;
auto interface = terrain->getTerrainGridControl();
double l_a_inc = M_PI / numLenPoints;
double w_a_inc = M_PI / numWidthPoints;
for (int n = 0; n < numLenPoints; ++n) {
for (int w = 0; w < numWidthPoints; ++w) {
double w_inc = (w - numWidthPoints / 2.0) * terrain->
getElementSize();
auto gridPoint = terrain->
getClosestGridPoint(location + widthDirection * w_inc + normalizedDirection * l_inc);
double h = height * std::sin(l_a_inc * n) * std::cos(w_a_inc * w - M_PI / 2);
interface->addSolidOccupancyInColumnToHeight(gridPoint, h, compaction);
}
}
}
{
const double size = terrain_settings["size"].asDouble();
const double elementSize = terrain_settings["element_size"].asDouble();
const double height = terrain_settings["bump_height"].asDouble();
const int resolution = static_cast<int>(std::floor(size / elementSize)) + 1;
auto hfIndexToPosition = [resolution, size](int index) {
return ((double)index / (double)resolution - 0.5) * size;
};
for (int i = 0; i < resolution; ++i) {
for (int j = 0; j < resolution; ++j) {
heights[i + resolution * j] = hillHeightFunction(hfIndexToPosition(i), hfIndexToPosition(j), height, size);
}
}
heightField->setHeights(heights);
terrain_settings["angle_of_repose_compaction_rate"].asDouble());
terrain->
setMaterial(getOrCreateMaterial(foundation.simulation,
"Terrain"));
float elevationCompaction = terrain_settings["elevation"]["compaction"].asFloat();
int z = gridDataInterface->findSurfaceIndex(terrainIndex);
if (z > 0) {
int curr_z = z;
while (curr_z > 0) {
gridDataInterface->setCompaction(terrainIndex, curr_z, elevationCompaction);
--curr_z;
}
}
}
}
auto pile = terrain_settings["pile"];
agx::Vec3 pile_location(jsonArrayToVec(pile[
"location"]));
agx::Vec3 pile_direction(jsonArrayToVec(pile[
"direction"]));
createPile(terrain,
pile["length"].asDouble(),
pile["width"].asDouble(),
pile["height"].asDouble(),
pile["compaction"].asFloat(),
pile_direction, pile_location);
return terrain;
}
osg::Node* node = renderer->getNode();
auto filePath = filePathContainer.
find(
"data/textures/grid.png");
float scale = length / (20.0f * grid_size);
return renderer;
}
{
if (terrain_settings["use_grid_texture"].asBool()) {
float grid_resolution = terrain_settings["grid_cell_texture_size"].asFloat();
renderer = attachTerrainGridTexture(terrain, root, grid_resolution);
}
else {
}
return renderer;
}
void setupCamera(Foundation foundation) {
auto cameraData = foundation.application->getCameraData();
cameraData.eye =
agx::Vec3(-9.99429, -3.27762e+01, 9.606);
cameraData.center =
agx::Vec3(-7.1831e-01, 1.1284e-02, 1.47957);
cameraData.up =
agx::Vec3(6.32905e-02, 2.2320e-01, 9.727e-01);
cameraData.nearClippingPlane = 0.1f;
cameraData.farClippingPlane = 5000.0f;
foundation.application->applyCameraData(cameraData);
}
if (!constraint) {
return;
}
motor->setEnable(std::abs(speed) > 0);
motor->setSpeed(speed);
lock->setEnable(!motor->getEnable());
if (!lockWasEnabled) {
lock->setPosition(constraint->
getAngle());
}
}
assert(sim != nullptr);
auto material1 = getOrCreateMaterial(sim, contactMaterialData["material1"].asString());
auto material2 = getOrCreateMaterial(sim, contactMaterialData["material2"].asString());
contactMaterial->setRestitution(contactMaterialData["restitution"].asDouble());
contactMaterial->setYoungsModulus(contactMaterialData["youngs_modulus"].asDouble());
contactMaterial->setUseContactAreaApproach(contactMaterialData["use_contact_area_approach"].asBool());
if (contactMaterialData.hasMember("adhesion")) {
auto adhesion = jsonArrayToArray(contactMaterialData["adhesion"]);
contactMaterial->setAdhesion(adhesion[0], adhesion[1]);
}
std::string frictionModel = contactMaterialData["friction_model"].asString();
if (frictionModel.find("agx.FrictionModel.DIRECT_AND_ITERATIVE") != std::string::npos) {
}
else if (frictionModel.find("agx.FrictionModel.DIRECT") != std::string::npos) {
}
else if (frictionModel.find("agx.FrictionModel.ITERATIVE") != std::string::npos) {
}
if (frictionModel.find("IterativeProjectedConeFriction") != std::string::npos) {
}
else if (frictionModel.find("OrientedScaleBoxFrictionModel") != std::string::npos) {
assert(chassis != nullptr);
solver);
}
else if (frictionModel.find("ConstantNormalForceOrientedBoxFrictionModel") != std::string::npos) {
assert(chassis != nullptr);
double normal_force_scale = contactMaterialData["friction_model_normal_force_scale"].asDouble();
solver,
true);
}
if (model) {
contactMaterial->setFrictionModel(model);
}
return contactMaterial;
}
assert(sim != nullptr && terrain != nullptr);
auto shovelMaterial = getOrCreateMaterial(sim, contactMaterialData["material1"].asString());
sim->
add(particleMaterial);
auto shovelParticleContactMaterial = sim->
getMaterialManager()->getContactMaterialOrCreateImplicit(shovelMaterial, particleMaterial);
shovelParticleContactMaterial->
setRestitution(contactMaterialData[
"restitution"].asDouble());
shovelParticleContactMaterial->
setFrictionCoefficient(contactMaterialData[
"friction_coefficient"].asDouble());
shovelParticleContactMaterial->
setYoungsModulus(contactMaterialData[
"youngs_modulus"].asDouble());
sim->
add(shovelParticleContactMaterial);
return shovelParticleContactMaterial;
}
{
return body;
}
{
auto body = Utilities::getShovelBody(name, assembly);
auto topEdge =
agx::Line(upperRight, upperLeft);
return topEdge;
}
{
auto body = Utilities::getShovelBody(name, assembly);
auto cuttingEdge =
agx::Line(lowerRight, lowerLeft);
return cuttingEdge;
}
{
auto body = Utilities::getShovelBody(name, assembly);
return cuttingDirection;
}
}
{
public:
std::vector<agx::RigidBodyRef> rollers,
std::pair<double, double> size,
const agxJson::Value& trackSettings);
return m_sprocket_hinge;
}
double velocity() const {
double angular_velocity = m_sprocket_hinge->getMotor1D()->getSpeed();
return angular_velocity * m_sprocket_radius;
}
private:
double m_sprocket_radius;
double m_track_width;
};
class ControllerMotorData;
using Action = std::function<void(
bool,
bool,
agx::Real)>;
class EngineState {
public:
EngineState()
: rightTrackBrake(0.0),
leftTrackBrake(0.0),
parkBrake(true) {}
agx::Real getRightBrake()
const {
return rightTrackBrake; }
agx::Real getLeftBrake()
const {
return leftTrackBrake; }
bool getParkBrake() const { return parkBrake; }
void setRightBrake(
agx::Real value) { rightTrackBrake = value; }
void setLeftBrake(
agx::Real value) { leftTrackBrake = value; }
void toggleParkBrake() { parkBrake = !parkBrake; }
private:
bool parkBrake;
};
class Bulldozer;
class ThrottleController {
public:
double getCurrentRPM() const;
~ThrottleController();
private:
Bulldozer* m_bulldozer;
};
enum class Location {
LEFT,
RIGHT,
BOTH
};
{
public:
Bulldozer(Utilities::Foundation &foundation, const std::string& path, const agxJson::Value& settings);
void updateDrivetrain();
{
return m_foundation.root;
}
{
return m_foundation.simulation;
}
Track* getTrack(int index) { return m_tracks[index]; }
const EngineState& getEngineState() const {
return m_engineState;
}
return m_engine;
};
auto const forward = getChassisBody()->getFrame()->transformVectorToLocal(
agx::Vec3::X_AXIS());
return getChassisBody()->getVelocity() * getChassisBody()->getFrame()->transformVectorToWorld(forward);
}
bool getParkingBrake() const {
return m_engineState.getParkBrake();
}
return m_gearBox->getCurrentGearRatio();
}
return m_shovel;
}
return m_targetRPM;
}
~Bulldozer();
std::vector<agx::Constraint1DOFRef> getTrackConstraints() const {
return {
m_actuatorConstraints.at("LeftSprocketHinge"),
m_actuatorConstraints.at("RightSprocketHinge")
};
}
std::vector<agx::Constraint1DOFRef> getShovelLiftConstraints() const {
return {
m_actuatorConstraints.at("LeftLiftPrismatic"),
m_actuatorConstraints.at("RightLiftPrismatic")
};
}
std::vector<agx::Constraint1DOFRef> getShovelTiltConstraints() const {
return {
m_actuatorConstraints.at("LeftTiltPrismatic"),
m_actuatorConstraints.at("RightTiltPrismatic")
};
}
std::vector<agx::Constraint1DOFRef> getRipperLiftConstraints() const {
return {
m_actuatorConstraints.at("LeftRipperLiftPrismatic"),
m_actuatorConstraints.at("RightRipperLiftPrismatic")
};
}
std::vector<agx::Constraint1DOFRef> getRipperPitchConstraints() const {
return {
m_actuatorConstraints.at("LeftRipperPitchPrismatic"),
m_actuatorConstraints.at("RightRipperPitchPrismatic")
};
}
std::vector<agx::Constraint1DOFRef> getConstraintsFromTag(const std::string& tag) const;
Action* createAction(const std::string& tag);
private:
void initializeBodiesAndConstraints();
void createTracks();
void createDriveTrain();
void createAndAddShovels(const agxJson::Value& shovel_settings);
agxTerrain::Shovel* createShovel(
const std::string &name,
const agxJson::Value& shovel_settings);
private:
Utilities::Foundation m_foundation;
agxJson::Value m_settings;
std::vector<TrackRef> m_tracks;
EngineState m_engineState;
ThrottleController* m_throttleController;
std::unordered_map<std::string, agx::Constraint1DOFRef> m_actuatorConstraints = {
{"LeftLiftPrismatic", nullptr},
{"RightLiftPrismatic", nullptr},
{"LeftTiltPrismatic", nullptr},
{"RightTiltPrismatic", nullptr},
{"LeftRipperLiftPrismatic", nullptr},
{"RightRipperLiftPrismatic", nullptr},
{"LeftRipperPitchPrismatic", nullptr},
{"RightRipperPitchPrismatic", nullptr},
{"LeftSprocketHinge", nullptr},
{"RightSprocketHinge", nullptr} };
std::unordered_map<std::string, agx::RigidBodyRef> m_modelComponents = {
{"ChassisBody", nullptr},
{"Shovel", nullptr},
{"LeftRipper", nullptr},
{"RightRipper", nullptr},
{"LeftSprocket", nullptr},
{"RightSprocket", nullptr},
{"LeftRearRollerWheel", nullptr},
{"LeftFrontRollerWheel", nullptr},
{"RightRearRollerWheel", nullptr},
{"RightFrontRollerWheel", nullptr} };
};
#if AGX_USE_WEBPLOT()
namespace {
{
public:
: m_engine(engine)
{
}
{
return result;
}
private:
};
{
public:
EngineTargetRpmDataGenerator(Bulldozer* bulldozer)
: m_bulldozer(bulldozer)
{
}
{
return m_bulldozer->getTargetRpm();
}
private:
BulldozerRef m_bulldozer;
};
{
public:
: m_engine(engine)
{
}
{
}
private:
};
}
#endif
class EngineController {
public:
EngineController(BulldozerRef bulldozer) : m_bulldozer(bulldozer) {}
void updateEngine() {
m_bulldozer->updateDrivetrain();
}
private:
BulldozerRef m_bulldozer;
};
class ControllerMotorData {
public:
ControllerMotorData(std::string name,
bool invert,
std::vector<agx::Constraint1DOFRef> constraints) :
name(name),
maxSpeed(maxSpeed),
accelerationTime(accelerationTime),
decelerationRate(decelerationRate),
deadZone(deadZone),
invert(invert),
constraints(constraints)
{
if (maxSpeed[0] == 0)
maxSpeed[0] = 1;
}
ControllerMotorData(std::string name,
std::vector<agx::Constraint1DOFRef> constraints) :
name(name), maxSpeed(maxSpeed), accelerationTime(0), decelerationRate(0), deadZone(0), invert(false), constraints(constraints)
{
}
ControllerMotorData() : name(""), maxSpeed(0), accelerationTime(0), decelerationRate(0), deadZone(0), invert(false)
{
}
const std::string& getName() const { return name; }
double getAccelerationTime() const { return accelerationTime; }
double getDecelerationRate() const { return decelerationRate; }
const std::vector<agx::Constraint1DOFRef>& getConstraints() const { return constraints; }
double getDeadZone() const { return deadZone; }
const std::vector<double>& getTargetSpeed() const { return targetSpeed; }
void setTargetSpeed(std::vector<double>& target) { targetSpeed = target; }
void addTargetSpeed(const std::vector<double>& target)
{
for (size_t i = 0; i < target.size(); i++)
targetSpeed[i] += target[i];
}
std::string getId()
{
return constraints.
front()->getName() +
":" + constraints.back()->getName();
}
void updateSpeed(
agx::Real time,
double value = 1.0) {
double sign = invert ? -1 : 1;
value = abs(value) > deadZone ? std::copysign(1.0, value) * (abs(value) - deadZone) : 0;
for (
size_t i = 0; i < maxSpeed.
size(); ++i) {
if (accelerationTime == 0)
targetSpeed[i] =
sign * value * maxSpeed[i];
else
targetSpeed[i] =
sign * value * std::min(time / accelerationTime, 1.0) * maxSpeed[i];
}
}
void reset() {
for (size_t i = 0; i < targetSpeed.size(); i++)
targetSpeed[i] = 0;
}
void tick() {
for (
size_t i = 0; i < maxSpeed.
size(); ++i) {
if (decelerationRate > 1)
{
targetSpeed[i] /= decelerationRate;
if (std::abs(maxSpeed[i]) < 1.0E-3) {
targetSpeed[i] = 0.0;
}
}
else
{
targetSpeed[i] = 0.0;
}
}
}
private:
const std::string name;
const bool invert;
const std::vector<agx::Constraint1DOFRef> constraints;
std::vector<agx::Real> targetSpeed;
};
typedef std::shared_ptr<ControllerMotorData> ControllerMotorDataPtr;
class Motors {
public:
Motors()
{
}
void insert(std::vector<agx::Constraint1DOFRef> constraints)
{
for (auto constraint : constraints) {
m_constraintMap.
insert({ constraint, 0.0 });
}
}
void reset() {
for (auto& pair : m_constraintMap) {
pair.second = 0;
}
}
void setConstraintTargetSpeed() {
for (auto& pair : m_constraintMap) {
Utilities::setConstraintTargetSpeed(pair.second, pair.first);
}
}
void addTargetSpeed(ControllerMotorDataPtr motorData, const std::vector<double>& target)
{
size_t i = 0;
for (auto constraint : motorData->getConstraints() ) {
if (m_constraintMap.find(constraint) != m_constraintMap.end())
{
double value = target.size() > i ? target[i] : target.back();
m_constraintMap.find(constraint)->second += value;
i++;
}
}
}
private:
std::map<agx::Constraint1DOFRef, double> m_constraintMap;
};
typedef std::shared_ptr<Motors> MotorsPtr;
class Axis {
public:
Axis(std::string name, int key, ControllerMotorDataPtr motorData) :
m_name(name), m_key(key), m_motorData(motorData), m_isKeyDown(false), m_isKeyUp(false), m_down(false), m_timeDown(0), m_timeAtKeyDown(0), m_action(nullptr), m_value(0) {
};
Axis(std::string name, int key, ControllerMotorDataPtr motorData, Action* action) :
m_name(name), m_key(key), m_motorData(motorData), m_isKeyDown(false), m_isKeyUp(false), m_down(false), m_timeDown(0), m_timeAtKeyDown(0), m_action(action), m_value(0) {
};
std::string getName() const { return m_name; }
int getKey() const { return m_key; }
bool getDown() const { return m_down; }
agx::Real getTimeDown()
const {
return m_timeDown; }
agx::Real getValue()
const {
return m_value; }
ControllerMotorDataPtr getData() { return m_motorData; }
Action* getAction() { return m_action.get(); }
if (getData() == nullptr)
return;
if (m_down)
{
m_timeDown = (timestamp - m_timeAtKeyDown);
getData()->updateSpeed(m_timeDown, m_value);
}
else
{
getData()->tick();
}
}
m_value = value;
if (getData() == nullptr)
return;
bool keyDown = (abs(value) - m_motorData->getDeadZone()) > 0;
m_isKeyDown = keyDown && !m_down;
m_isKeyUp = !keyDown && m_down;
m_down = keyDown;
if (m_isKeyDown) {
m_timeAtKeyDown = timestamp;
m_timeDown = 0.0;
}
else {
m_timeDown = (timestamp - m_timeAtKeyDown);
}
}
private:
const std::string m_name;
const int m_key;
ControllerMotorDataPtr m_motorData;
bool m_isKeyDown = false;
bool m_isKeyUp = true;
bool m_down = false;
std::unique_ptr<Action> m_action;
double m_value;
};
typedef std::shared_ptr<Axis> AxisPtr;
class Button {
public:
Button(std::string name, int key, ControllerMotorDataPtr motorData) :
m_name(name), m_key(key), m_motorData(motorData), m_isKeyDown(false), m_isKeyUp(false), m_down(false), m_timeDown(0), m_timeAtKeyDown(0), m_action(nullptr){
};
Button(std::string name, int key, ControllerMotorDataPtr motorData, Action* action) :
m_name(name), m_key(key), m_motorData(motorData), m_isKeyDown(false), m_isKeyUp(false), m_down(false), m_timeDown(0), m_timeAtKeyDown(0), m_action(action) {
};
std::string getName() const { return m_name; }
int getKey() const { return m_key; }
bool getDown() const { return m_down; }
agx::Real getTimeDown()
const {
return m_timeDown; }
bool getKeyIsPressed() const { return m_isKeyDown; }
ControllerMotorDataPtr getData() { return m_motorData; }
Action* getAction() { return m_action.get(); }
if (getData() == nullptr)
return;
if (m_down)
{
m_timeDown = (timestamp - m_timeAtKeyDown);
getData()->updateSpeed(m_timeDown);
}
else
{
getData()->tick();
}
}
void processKeyboardData(
agx::Real timestamp,
unsigned ,
float ,
float ,
bool keyDown) {
m_isKeyDown = keyDown && !m_down;
m_isKeyUp = !keyDown && m_down;
m_down = keyDown;
if (m_isKeyDown) {
m_timeAtKeyDown = timestamp;
m_timeDown = 0.0;
}
else {
m_timeDown = (timestamp - m_timeAtKeyDown);
}
}
private:
const std::string m_name;
const int m_key;
ControllerMotorDataPtr m_motorData;
bool m_isKeyDown = false;
bool m_isKeyUp = true;
bool m_down = false;
std::unique_ptr<Action> m_action;
};
typedef std::shared_ptr<Button> ButtonPtr;
public:
SimulationJoystickListener(Utilities::Foundation foundation, BulldozerRef bulldozer, const agxJson::Value& controll_settings) :
JoystickListener(), m_foundation(foundation), m_bulldozer(bulldozer)
{
size_t index = 0;
for (auto& gamepad_control : controll_settings) {
auto name = controll_settings.getMemberNames()[index];
index++;
if (name.rfind("_comment", 0) == 0)
continue;
ControllerMotorDataPtr motorData = nullptr;
if (name.rfind("tracks", 0) != 0) {
std::vector<agx::Constraint1DOFRef> constraints = m_bulldozer->getConstraintsFromTag(name);
motorData = std::make_shared<ControllerMotorData>(
name,
Utilities::jsonArrayToArray(gamepad_control["max_speed"]),
gamepad_control["acceleration_time"].asDouble(),
gamepad_control["deceleration_rate"].asDouble(),
gamepad_control["deadzone"].asDouble(),
gamepad_control["invert"].asBool(),
constraints);
}
auto action = m_bulldozer->createAction(name);
if (gamepad_control.hasMember("axis") && m_axisMap.find(gamepad_control["axis"].asString()) != m_axisMap.end()) {
int symbol = m_axisMap.find(gamepad_control["axis"].asString())->second;
AxisPtr axis = std::make_shared<Axis>(name, symbol, motorData, action);
m_axisMotorMap.insert(std::make_pair(symbol, axis));
}
else if (gamepad_control.hasMember("button") && m_buttonMap.find(gamepad_control["button"].asString()) != m_buttonMap.end()) {
int symbol = m_buttonMap.find(gamepad_control["button"].asString())->second;
ButtonPtr button = std::make_shared<Button>(name, symbol, motorData, action);
m_buttonMotorMap.insert(std::make_pair(symbol, button));
}
else if (gamepad_control.hasMember("pov") && m_povMap.find(gamepad_control["pov"].asString()) != m_povMap.end()) {
int symbol = (int)m_povMap.find(gamepad_control["pov"].asString())->second;
ButtonPtr button = std::make_shared<Button>(name, symbol, motorData, action);
m_povMotorMap.insert(std::make_pair(symbol, button));
}
}
createStepEventCallbackUpdater();
}
virtual ~SimulationJoystickListener() {}
{
}
{
}
bool handled = false;
if (m_buttonMotorMap.find(button) == m_buttonMotorMap.end())
return handled;
auto b = m_buttonMotorMap.find(button)->second;
b->processKeyboardData(m_foundation.simulation->getTimeStamp(), 0, 0, 0, down);
if (b->getData() == nullptr) {
auto action = b->getAction();
(*action)(b->getKeyIsPressed(), b->getDown(), 0);
}
if (b->getDown() && b->getData())
b->getData()->updateSpeed(b->getTimeDown());
return handled;
}
bool handled = false;
if (m_axisMotorMap.find(axis) == m_axisMotorMap.end())
return handled;
auto a = m_axisMotorMap.find(axis)->second;
a->processKeyboardData(m_foundation.simulation->getTimeStamp(), val);
if (a->getData() == nullptr) {
auto action = a->getAction();
(*action)(0, 0, a->getValue());
}
if (a->getData() && a->getDown())
a->getData()->updateSpeed(a->getTimeDown(), val);
return handled;
}
bool handled = false;
for (auto pair : m_povMotorMap)
{
auto button = pair.second;
button->processKeyboardData(m_foundation.simulation->getTimeStamp(), 0, 0, 0, (pair.first & val));
if (button->getData() == nullptr) {
auto action = button->getAction();
(*action)(button->getKeyIsPressed(), button->getDown(), 0);
}
if (button->getData() && button->getDown())
button->getData()->updateSpeed(button->getTimeDown());
}
return handled;
}
return false;
}
private:
void createStepEventCallbackUpdater()
{
auto motors = std::make_shared<Motors>();
std::map<std::string, MotorsPtr> constraintMap;
for (auto pair : m_buttonMotorMap)
if (pair.second->getData())
motors->insert(pair.second->getData()->getConstraints());
for (auto pair : m_povMotorMap)
if (pair.second->getData())
motors->insert(pair.second->getData()->getConstraints());
for (auto pair : m_axisMotorMap)
if (pair.second->getData())
motors->insert(pair.second->getData()->getConstraints());
std::shared_ptr<EngineController> engineControl = std::make_shared<EngineController>(m_bulldozer);
{
motors->reset();
auto processKeyData = [motors, simulation](std::map<int, ButtonPtr>& keyMotorMap)
{
for (auto& keyData : keyMotorMap)
{
if (keyData.second->getData())
motors->addTargetSpeed(keyData.second->getData(), keyData.second->getData()->getTargetSpeed());
}
};
processKeyData(m_buttonMotorMap);
processKeyData(m_povMotorMap);
for (auto& axisData : m_axisMotorMap)
{
if (axisData.second->getData())
motors->addTargetSpeed(axisData.second->getData(), axisData.second->getData()->getTargetSpeed());
}
motors->setConstraintTargetSpeed();
engineControl->updateEngine();
}, m_foundation.simulation);
}
private:
Utilities::Foundation m_foundation;
BulldozerRef m_bulldozer;
std::map<int, ButtonPtr> m_buttonMotorMap;
std::map<int, AxisPtr> m_axisMotorMap;
std::map<int, ButtonPtr> m_povMotorMap;
std::map<std::string, int> m_axisMap{
{ "Gamepad.Axis.LeftVertical", 0 },
{ "Gamepad.Axis.LeftHorizontal", 1 },
{ "Gamepad.Axis.RightVertical", 2 },
{ "Gamepad.Axis.RightHorizontal", 3 },
{ "Gamepad.Axis.LeftTrigger", 4 },
{ "Gamepad.Axis.RightTrigger", 5 }
};
std::map<std::string, int> m_buttonMap{
{ "Gamepad.Button.A", 8 },
{ "Gamepad.Button.B", 9 },
{ "Gamepad.Button.X", 10 },
{ "Gamepad.Button.Y", 11 },
{ "Gamepad.Button.Start", 0 },
{ "Gamepad.Button.Back", 1 },
{ "Gamepad.Button.LeftBumper", 4 },
{ "Gamepad.Button.RightBumper", 5 },
{ "Gamepad.Button.LeftStick", 2 },
{ "Gamepad.Button.RightStick", 3 }
};
std::map<std::string, int> m_povMap{
};
};
{
public:
KeyboardListener(Utilities::Foundation foundation, BulldozerRef bulldozer, agxJson::Value &keyboard_settings) :
m_foundation(foundation), m_bulldozer(bulldozer)
{
m_turnValueLeft = keyboard_settings["tracks_turn_left"]["value"].asDouble();
m_turnValueRight = keyboard_settings["tracks_turn_right"]["value"].asDouble();
size_t index = 0;
for (auto& key_controll : keyboard_settings) {
auto name = keyboard_settings.getMemberNames()[index];
index++;
if (name.rfind("_comment", 0) == 0 || !key_controll.hasMember("key"))
continue;
int asciiKey = 0;
if (m_keyMap.find(key_controll["key"].asString()) != m_keyMap.end())
{
asciiKey = (int)keySymbol;
}
else if (key_controll["key"].asString().rfind("ord('", 0) == 0)
{
asciiKey = (int)static_cast<unsigned char>(key_controll["key"].asString()[5]);
}
if (name.rfind("tracks_", 0) == 0) {
ButtonPtr button = std::make_shared<Button>(name, asciiKey, nullptr, m_bulldozer->createAction(name));
m_keyMotorMap.insert(std::make_pair(asciiKey, button));
}
else {
std::vector<agx::Constraint1DOFRef> constraints = m_bulldozer->getConstraintsFromTag(name);
ControllerMotorDataPtr motorData = std::make_shared<ControllerMotorData>(
name,
Utilities::jsonArrayToArray(key_controll["max_speed"]),
key_controll["acceleration_time"].asDouble(),
key_controll["deceleration_rate"].asDouble(),
key_controll["deadzone"].asDouble(),
key_controll["invert"].asBool(),
constraints);
ButtonPtr button = std::make_shared<Button>(name, asciiKey, motorData);
m_keyMotorMap.insert(std::make_pair(asciiKey, button));
}
}
createStepEventCallbackUpdater();
}
bool keyboard(
int key,
unsigned modkeyMask,
float x,
float y,
bool keyDown) {
bool handled = false;
if (m_keyMotorMap.find(key) == m_keyMotorMap.end())
return handled;
auto button = m_keyMotorMap.find(key)->second;
button->processKeyboardData(
getSimulation()->getTimeStamp(), modkeyMask, x, y, keyDown);
if (button->getData() == nullptr) {
if (button->getName() == "tracks_turn_right")
value = m_turnValueRight;
else if (button->getName() == "tracks_turn_left")
value = m_turnValueLeft;
auto action = button->getAction();
(*action)(button->getKeyIsPressed(), button->getDown(), value);
}
else if (button->getDown())
button->getData()->updateSpeed(button->getTimeDown());
return handled;
}
private:
void createStepEventCallbackUpdater()
{
auto motors = std::make_shared<Motors>();
std::map<std::string, MotorsPtr> constraintMap;
for (auto pair : m_keyMotorMap)
if (pair.second->getData() != nullptr)
motors->insert(pair.second->getData()->getConstraints());
{
motors->reset();
for (auto keyData : m_keyMotorMap)
{
if (keyData.second->getData() == nullptr)
continue;
if (!keyData.second->getDown())
keyData.second->getData()->tick();
motors->addTargetSpeed(keyData.second->getData(), keyData.second->getData()->getTargetSpeed());
}
motors->setConstraintTargetSpeed();
m_bulldozer->updateDrivetrain();
}, m_foundation.simulation);
}
private:
Utilities::Foundation m_foundation;
BulldozerRef m_bulldozer;
std::map<int, ButtonPtr> m_keyMotorMap;
std::map<std::string, agxSDK::GuiEventListener::KeySymbol> m_keyMap {
};
};
std::vector<agx::RigidBodyRef> rollers,
std::pair<double, double> size,
const agxJson::Value& trackSettings)
:
agxVehicle::Track(trackSettings[
"number_of_nodes"].asInt(), size.second, trackSettings[
"thickness"].asDouble()),
m_sprocket(sprocket),
m_sprocket_hinge(sprocket_hinge)
{
m_sprocket_radius = size.first;
m_track_width = size.second;
#ifdef AGX_DEBUG
for (auto& geometry : m_sprocket->getGeometries()) {
if (geometry->getEnableCollisions() && geometry->getMaterial()->getName() != "Unknown Material") {
auto wheel_material = geometry->getMaterial();
assert(wheel_material->getName() == "Wheel" && "Name of sprocket material must be Wheel.");
}
}
#endif
add(sprocket_wheel);
std::vector<agxVehicle::TrackWheelRef> rollers_wheel;
rollers_wheel.reserve(rollers.size());
for (size_t i = 0; i < rollers.size(); ++i) {
add(wheel);
rollers_wheel.push_back(wheel);
}
}
void Track::trackSetup(
agxSDK::Simulation *simulation,
const agxJson::Value& trackSettings)
{
if (material == nullptr) {
simulation->
add(material);
}
setMaterial(material);
if (trackSettings["move_nodes_to_wheels"].asBool()) {
for (auto& wheel : getWheels()) {
}
}
if (trackSettings["sprocket_hinge_direct_solver"].asBool()) {
}
auto track_properties = getProperties();
std::vector<std::pair<agx::Real, agx::Hinge::DOF>> hinge_compliance = {
};
double default_hinge_damping = 2.0 * simulation->
getTimeStep();
std::vector<std::pair<agx::Real, agx::Hinge::DOF>> hinge_damping = {
};
for (const auto& c : hinge_compliance) {
track_properties->setHingeCompliance(c.first, c.second);
}
for (const auto& c : hinge_damping) {
track_properties->setHingeDamping(c.first, c.second);
}
auto merge_properties = getInternalMergeProperties();
if (trackSettings["enable_merge"].asBool()) {
merge_properties->setEnableMerge(true);
merge_properties->setNumNodesPerMergeSegment(trackSettings["num_nodes_per_merge_segment"].asInt());
merge_properties->setEnableLockToReachMergeCondition(false);
merge_properties->setLockToReachMergeConditionCompliance(trackSettings["merge_condition_compliance"].asDouble());
}
track_properties->setStabilizingHingeFrictionParameter(trackSettings["stabilizing_hinge_friction"].asDouble());
track_properties->setMinStabilizingHingeNormalForce(trackSettings["stabilizing_hinge_normal_force"].asDouble());
setProperties(track_properties);
}
void Track::createVisual(osg::Group *root)
{
if (root != nullptr) {
for (size_t i = 0; i < getNumNodes(); ++i) {
agxRender::Color color(100.0f / 255.0f, 100.0f / 255.0f, 100.0f / 255.0f, 1.0f);
}
}
}
Bulldozer::Bulldozer(Utilities::Foundation &foundation, const std::string& path, const agxJson::Value& settings) :
m_foundation(foundation), m_settings(settings), m_engineState()
{
auto filePath = filePathContainer.
find(path);
if (!success)
throw std::runtime_error("We can't open the agx bulldozer model: " + path);
if (getRigidBody("Ground") != nullptr)
{
remove(getRigidBody("Ground"), true);
}
initializeBodiesAndConstraints();
createAndAddShovels(settings["shovel_settings"]);
createTracks();
createDriveTrain();
}
void Bulldozer::updateDrivetrain() {
m_throttleController->update(simulation()->getTimeStamp());
if (m_engineState.getParkBrake()) {
m_engineState.setLeftBrake(1);
m_engineState.setRightBrake(1);
m_engine->setEnable(true);
}
m_throttleController->setTargetRPM(m_targetRPM);
setTrackDrivetrainState(m_engineState.getRightBrake(), m_clutches[int(Location::RIGHT)], m_brakes[int(Location::RIGHT)]);
setTrackDrivetrainState(m_engineState.getLeftBrake(), m_clutches[int(Location::LEFT)], m_brakes[int(Location::LEFT)]);
}
Bulldozer::~Bulldozer()
{
delete m_throttleController;
}
std::vector<agx::Constraint1DOFRef> Bulldozer::getConstraintsFromTag(const std::string& tag) const
{
if (tag.rfind("shovel_lift", 0) == 0)
{
return getShovelLiftConstraints();
}
else if (tag.rfind("shovel_tilt", 0) == 0)
{
return getShovelTiltConstraints();
}
else if (tag.rfind("shovel_twist", 0) == 0)
{
auto vec1 = getShovelLiftConstraints();
auto vec2 = getShovelTiltConstraints();
vec1.insert(vec1.end(), vec2.begin(), vec2.end());
return vec1;
}
else if (tag.rfind("ripper_lift", 0) == 0)
{
return getRipperLiftConstraints();
}
else if (tag.rfind("ripper_pitch", 0) == 0)
{
return getRipperPitchConstraints();
}
return std::vector<agx::Constraint1DOFRef>();
}
Action* Bulldozer::createAction(const std::string& tag)
{
if (tag == "tracks_park_brake")
{
return new Action([
this](
bool isPressed,
bool ,
agx::Real )
{
if (isPressed) {
m_engineState.toggleParkBrake();
if (!m_engineState.getParkBrake()) {
m_engineState.setRightBrake(0);
m_engineState.setLeftBrake(0);
}
}
});
}
else if (tag == "tracks_gear_increment") {
return new Action([
this](
bool isPressed,
bool ,
agx::Real )
{
if (isPressed)
m_gearBox->gearUp();
});
}
else if (tag == "tracks_gear_decrement") {
return new Action([
this](
bool isPressed,
bool ,
agx::Real )
{
if (isPressed)
m_gearBox->gearDown();
});
}
else if (tag == "tracks_turn_right") {
return new Action([
this](
bool ,
bool isDown,
agx::Real value)
{
if (isDown)
m_engineState.setRightBrake(value);
else
m_engineState.setRightBrake(0.0);
});
}
else if (tag == "tracks_turn_left") {
return new Action([
this](
bool ,
bool isDown,
agx::Real value)
{
if (isDown)
m_engineState.setLeftBrake(value);
else
m_engineState.setLeftBrake(0.0);
});
}
else if (tag == "tracks_throttle_inc") {
return new Action([
this](
bool isPressed,
bool ,
agx::Real )
{
if (isPressed) {
auto new_rpm = m_targetRPM + m_throttleStep;
m_targetRPM =
agx::clamp(new_rpm, m_idleRPM, m_maxRPM);
}
});
}
else if (tag == "tracks_throttle_dec") {
return new Action([
this](
bool isPressed,
bool ,
agx::Real )
{
if (isPressed) {
auto new_rpm = m_targetRPM - m_throttleStep;
m_targetRPM =
agx::clamp(new_rpm, m_idleRPM, m_maxRPM);
}
});
}
else if (tag == "tracks_right_brake") {
return new Action([
this](
bool ,
bool ,
agx::Real value)
{
m_engineState.setRightBrake(value);
});
}
else if (tag == "tracks_left_brake") {
return new Action([
this](
bool ,
bool ,
agx::Real value)
{
m_engineState.setLeftBrake(value);
});
}
else if (tag == "shovel_twist")
{
return new Action([](
bool ,
bool ,
agx::Real )
{
});
}
else
{
return nullptr;
}
}
void Bulldozer::createAndAddShovels(const agxJson::Value& shovel_settings)
{
m_shovel = createShovel("Shovel", shovel_settings);
add(m_shovel);
auto leftRipper = createShovel("LeftRipper", shovel_settings);
add(leftRipper);
auto rightRipper = createShovel("RightRipper", shovel_settings);
add(rightRipper);
}
agxTerrain::Shovel* Bulldozer::createShovel(
const std::string &name,
const agxJson::Value& shovel_settings)
{
Utilities::getShovelTopEdge(name, this),
Utilities::getShovelCuttingEdge(name, this),
Utilities::getShovelCuttingDirection(name, this));
terrain_shovel->getSettings()->setVerticalBladeSoilMergeDistance(shovel_settings["vertical_blade_soil_merge_distance"].asDouble());
terrain_shovel->getAdvancedSettings()->setNoMergeExtensionDistance(shovel_settings["merge_extension_distance"].asDouble());
terrain_shovel->getSettings()->setPenetrationDepthThreshold(shovel_settings["penetration_depth_threshold"].asDouble());
terrain_shovel->getSettings()->setPenetrationForceScaling(shovel_settings["penetration_force_scaling"].asDouble());
#ifdef AGX_DEBUG
auto it = std::find_if(terrain_shovel->getRigidBody()->getGeometries().begin(),
terrain_shovel->getRigidBody()->getGeometries().end(),
[](const auto& geometry) { return geometry->getEnableCollisions(); });
auto shovel_material = it->get()->getMaterial();
assert(shovel_material->getName() == "Shovel" && "Is the material on the shovel defined? Expected Shovel.");
#endif
return terrain_shovel;
}
void Bulldozer::createTracks()
{
auto trackSettings = m_settings["track_settings"];
auto size = Utilities::find_size(m_modelComponents ["LeftSprocket"]);
auto track = new Track(m_modelComponents ["LeftSprocket"],
Utilities::toHinge(m_actuatorConstraints["LeftSprocketHinge"]),
std::vector<agx::RigidBodyRef>{ m_modelComponents ["LeftRearRollerWheel"], m_modelComponents ["LeftFrontRollerWheel"] },
size,
trackSettings);
track->trackSetup(m_foundation.simulation, trackSettings);
m_tracks.push_back(track);
add(track);
track = new Track(m_modelComponents ["RightSprocket"],
Utilities::toHinge(m_actuatorConstraints["RightSprocketHinge"]),
std::vector<agx::RigidBodyRef>{ m_modelComponents ["RightRearRollerWheel"], m_modelComponents ["RightFrontRollerWheel"] },
size,
trackSettings);
track->trackSetup(m_foundation.simulation, trackSettings);
m_tracks.push_back(track);
add(track);
}
std::string side = "Left_";
if (location == Location::RIGHT)
side = "Right_";
clutch->setTorqueCapacity(drivetrainSettings["clutch_torque_capacity"].asDouble());
clutch->setEngage(false);
clutchBrakeShaft->getRotationalDimension()->getOrReserveBody()->setName(side + "clutch_brake_shaft");
brake->setTorqueCapacity(drivetrainSettings["brake_torque_capacity"].asDouble());
clutchBrakeShaft->connect(brake);
actuatorShaft->getRotationalDimension()->getOrReserveBody()->setName(side + "actuator_shaft");
brake->connect(actuatorShaft);
brake->connect(actuatorShaft);
m_clutches[int(location)] = clutch;
m_brakes[int(location)] = brake;
m_actuatorShafts[int(location)] = actuatorShaft;
}
if (0 <= input && input <= m_steeringLimits[0]) {
}
else if (m_steeringLimits[0] < input && input <= m_steeringLimits[1]) {
auto clutch_fraction = (input - m_steeringLimits[0]) / (m_steeringLimits[1] - m_steeringLimits[0]);
}
else if (m_steeringLimits[1] < input && input <= m_steeringLimits[2]) {
}
else if (m_steeringLimits[2] < input && input <= 1) {
auto brake_fraction = (input - m_steeringLimits[2]) / (1 - m_steeringLimits[2]);
}
}
void Bulldozer::createDriveTrain() {
auto drivetrainSettings = m_settings["drivetrain_settings"];
add(m_powerline);
m_targetRPM = engineParameters.maxTorqueRPM;
m_idleRPM = engineParameters.idleRPM;
m_maxRPM = engineParameters.maxRPM;
m_engine->setEnable(true);
m_steeringLimits = Utilities::jsonArrayToArray(drivetrainSettings["steering_limits"]);
m_throttleController = new ThrottleController(this, kp, ki, kd);
m_throttleController->setTargetRPM(m_targetRPM);
m_throttleStep = drivetrainSettings["throttle_step"].asDouble();
agx::RealVector centralGearRatios = Utilities::jsonArrayToArray(drivetrainSettings[
"gear_box"][
"gear_ratios"]);
m_gearBox->setGearRatios(centralGearRatios);
m_gearBox->setGear(drivetrainSettings["gear_box"]["start_gear_index"].asInt());
for (auto track : m_tracks) {
track->sprocket_hinge()->getMotor1D()->setEnable(false);
}
m_powerline->add(m_engine);
simulation()->
add(m_powerline);
m_engine->connect(engineTorqueConverterShaft);
auto body = engineTorqueConverterShaft->getRotationalDimension()->getOrReserveBody();
m_torqueConverter->setPumpDiameter(drivetrainSettings["torque_converter_pump_diameter"].asDouble());
m_torqueConverter->enableLockUp(false);
m_torqueConverter->setEfficiencyTable(velRatio_efficiency);
differential->setLock(true);
m_clutches = { nullptr, nullptr };
m_brakes = { nullptr, nullptr };
m_actuatorShafts = { nullptr, nullptr };
buildDrivetrainForTrack(differential, m_actuators[int(Location::RIGHT)], Location::RIGHT, drivetrainSettings);
buildDrivetrainForTrack(differential, m_actuators[int(Location::LEFT)], Location::LEFT, drivetrainSettings);
}
void Bulldozer::initializeBodiesAndConstraints()
{
for (auto& rb : getRigidBodies()) {
if (m_modelComponents .find(rb->
getName()) != m_modelComponents .end()) {
m_modelComponents [rb->
getName()] = rb;
}
}
for (auto& c : getConstraints()) {
if (m_actuatorConstraints.find(c->getName()) != m_actuatorConstraints.end()) {
}
}
m_actuatorConstraints["LeftSprocketHinge"] = Utilities::getHingeOnBody(m_modelComponents ["LeftSprocket"], getConstraints());
m_actuatorConstraints["RightSprocketHinge"] = Utilities::getHingeOnBody(m_modelComponents ["RightSprocket"], getConstraints());
auto hinge = Utilities::getHingeOnBody(m_modelComponents ["LeftRearRollerWheel"], getConstraints());
hinge = Utilities::getHingeOnBody(m_modelComponents ["LeftFrontRollerWheel"], getConstraints());
hinge = Utilities::getHingeOnBody(m_modelComponents ["RightRearRollerWheel"], getConstraints());
hinge = Utilities::getHingeOnBody(m_modelComponents ["RightFrontRollerWheel"], getConstraints());
}
: m_bulldozer(bulldozer), m_targetRPM(0) {
m_pidController->setGains(p, i, d);
m_pidController->setIntegralWindupClamping(0, 1);
m_pidController->setSetPoint(m_targetRPM);
}
void ThrottleController::setTargetRPM(
agx::Real targetRPM) {
m_targetRPM = targetRPM;
}
agx::Real ThrottleController::getCurrentRPM()
const {
return m_bulldozer->getEngine()->getRPM();
}
void ThrottleController::update(
agx::Real t) {
m_pidController->setSetPoint(m_targetRPM);
m_pidController->update(t, getCurrentRPM());
m_bulldozer->getEngine()->setThrottle(m_pidController->getManipulatedVariable());
}
ThrottleController::~ThrottleController() {
delete m_pidController;
}
{
solver->setNumPPGSRestingIterations(10);
osg::Group*
root =
new osg::Group();
auto foundation = Utilities::Foundation(simulation, application, root);
Utilities::setupCamera(foundation);
agxJson::Value settings;
Utilities::readSettings("data/models/bulldozer_d10t.json", settings);
auto terrain = Utilities::createTerrain(foundation, settings["terrain_settings"]);
simulation->
add(terrain);
auto renderer = Utilities::createTerrainVoxelRenderer(terrain, root, settings["terrain_settings"]);
simulation->
add(renderer);
BulldozerRef bulldozer = new Bulldozer(foundation, "data/models/bulldozer_d10t.agx", settings);
simulation->
add(bulldozer);
bulldozer->getTrack(0)->createVisual(root);
bulldozer->getTrack(1)->createVisual(root);
bulldozer->setPosition(-2, 0, 1.6);
Utilities::createAndAddContactMaterial(simulation, settings["contact_material_settings"]["track_bogie"], bulldozer->getChassisBody());
Utilities::createAndAddContactMaterial(simulation, settings["contact_material_settings"]["track_wheel"], bulldozer->getChassisBody());
Utilities::createAndAddContactMaterial(simulation, settings["contact_material_settings"]["track_terrain"], bulldozer->getChassisBody());
Utilities::createAndAddParticleContactMaterial(simulation, settings["contact_material_settings"]["shovel_particles"], terrain);
simulation->
add(manager);
if (!manager->valid())
{
auto keyboardListener = new KeyboardListener(foundation, bulldozer, settings["keyboard_control"]);
simulation->
add(keyboardListener);
}
else
{
auto gamepad_settings = settings["gamepad_controls"];
manager->add(gamepadListener);
}
parkBrakeString += bulldozer->getParkingBrake() ? "ON" : "OFF";
sd->setText(2, parkBrakeString);
sd->setText(6,
agx::String::format(
"Mass in shovel: %5.2f kg", bulldozer->getShovel()->getDynamicMass()));
sd->setText(7,
agx::String::format(
"Shovel Deadload: %5.2f %%", bulldozer->getShovel()->getDeadLoadFraction() * 100.0));
}, simulation);
#if AGX_USE_WEBPLOT()
auto engineRpmSeries =
new agxPlot::DataSeries(
new EngineRpmDataGenerator(bulldozer->getEngine()),
"RPM");
auto engineTargetRpmSeries =
new agxPlot::DataSeries(
new EngineTargetRpmDataGenerator(bulldozer),
"RPM");
auto enginePowerSeries =
new agxPlot::DataSeries(
new EnginePowerDataGenerator(bulldozer->getEngine()),
"Power");
engineRpmSeries->setUnit("rpm");
engineTargetRpmSeries->setUnit("RPM");
enginePowerSeries->setUnit("kW");
auto engineRpmCurve =
new agxPlot::Curve(timeData, engineRpmSeries,
"Engine rpm" );
auto engineTargetRpmCurve =
new agxPlot::Curve(timeData, engineTargetRpmSeries,
"Target rpm");
auto enginePowerCurve =
new agxPlot::Curve(timeData, enginePowerSeries,
"Engine power");
auto plotWindow = plot->getOrCreateWindow("Engine rpm");
plotWindow->add(engineRpmCurve);
plotWindow->add(engineTargetRpmCurve);
plotWindow = plot->getOrCreateWindow("Engine Power");
plotWindow->add(enginePowerCurve);
if (!g_isUnitTest)
{
auto webplot = new agxPlot::WebPlot(true);
plot->add(webplot);
}
#endif
}
int main(int argc, char** argv)
{
std::cout <<
"\tTutorial bulldozer_with_terrain \n" <<
"\t--------------------------------\n\n" << std::endl;
g_isUnitTest = argc >= 2;
#ifdef _MSC_VER
if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)sighandler, TRUE) == FALSE)
{
return 1;
}
#else
signal(SIGTERM, &sighandler);
signal(SIGINT, &sighandler);
#endif
int result = 1;
try {
if (application->init(arguments)) {
application->addScene(buildBulldozerScene, '1');
result = application->run();
}
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
}
return result;
}
#else
int main(int, char**) {
std::cout << "This tutorial requires agxTerrain and agxSensor" << std::endl;
return 0;
}
#endif
#define AGX_DECLARE_POINTER_TYPES(type)
void setFraction(agx::Real fraction)
The model of the combustion engine consists of two subsystems: namely, the intake manifold dynamics a...
static bool readLibraryProperties(const agx::String &name, CombustionEngineParameters ¶meters)
Load the named parameters and copy data into parameters.
A differential for distributing the torque evenly between an arbitrary number of output shafts.
virtual bool connect(agxPowerLine::Side mySide, agxPowerLine::Side unitSide, agxPowerLine::Unit *unit) override
Single disk plate clutch with dry friction.
void setFraction(agx::Real fraction)
static agx::Real getPower(const agxDriveTrain::ElectricMotor *motor)
Get the power for an electric motor.
static void add(const agxDriveTrain::ElectricMotor *motor, const agxSDK::Simulation *simulation=nullptr)
Adds an electric motor to the EnergyManager.
The gear box can have an arbitrary number of gears.
A Shaft is the most basic PowerLine Unit.
A torque converter is a connector, which transfers rotating power with the help of a fluid coupling.
A small argument parser class.
agxIO::FilePathContainer & getFilePath(Type t=RESOURCE_PATH)
@ RESOURCE_PATH
Specifies paths for where to search for resource files such as scripts, images, models etc.
static Environment * instance(void)
agx::String find(const agx::String &filename) const
First it will try to locate 'filename' in one of the specified directories.
Class that can be used to listen to events generated by a GamePad or Joystick.
virtual bool axisUpdate(const agxIO::JoystickState &state, int axis)
Called when the manager has a new joystick state.
virtual bool buttonChanged(const agxIO::JoystickState &state, int button, bool down)
Called when button button changed state.
virtual void removeNotification()
Called when this listener has been removed from a JoystickManager.
virtual bool sliderMoved(const agxIO::JoystickState &state, int slider)
Called when slider slider has changed state.
virtual bool povMoved(const agxIO::JoystickState &state, int pov)
Called when POV button pov has changed state.
virtual void addNotification()
Called when this listener has been added to a JoystickManager.
JoystickManager * getManager()
A Joystick/GamePad manager that initializes the connected devices and allows for adding listeners.
agx::Real normalize(int axisValue) const
Normalizes axis value [-1, 1].
Store the current state of a Joystick/GamePad.
agx::Int32Vector pov
Represents the value of POV button. Maximum of 4.
agx::Int32Vector axes
Contains the values for all axes.
A PID controller with the classic gain coefficients for proportional, integral, and derivative gain.
void clearText()
Reset all text to empty strings.
void setRenderVoxelFluidMass(bool enable)
Enable/disable rendering of fluid mass.
void setRenderVoxelBoundingBox(bool enable)
Enable/disable rendering of voxel bounding boxes.
void setVelocityFieldLineColor(const agx::Vec4 &lineColor)
Assign line color of velocity fields in the voxel renderer.
void setRenderCompaction(bool enable, agx::RangeReal compactionRange=agx::RangeReal(0.75, 1.5))
Enable/disable rendering of compaction.
void setRenderVoxelSolidMass(bool enable)
Enable/disable rendering of solid mass.
void setRenderVelocityField(bool enable)
Enable/disable rendering of the mass velocity field.
void setRenderSoilParticles(bool enable)
Enable/disable rendering of solid particles.
void setRenderSoilParticlesMesh(bool enable)
Set enable render particles as triangle meshes.
Abstract base class for connecting a agxPowerLine::Unit to an ordinary constraint such as a Hinge or ...
PowerLine is the base class for a system of power transfer integrated into the rigid body simulation.
Class that will connect a agxPowerLine::Unit to a rotational ordinary constraint such as a agx::Hinge...
virtual bool connect(agxPowerLine::Connector *connector)
Connect the unit to a connector.
const agx::ObserverFrame * getObserverFrame(const agx::Name &name, bool recursive=true) const
Find (linear search) and return an ObserverFrame matching the given uuid.
const agx::Material * getMaterial(const agx::Name &materialName) const
agx::Real getTimeStep() const
agx::Solver * getSolver()
agx::Real getTimeStamp() const
void setAngleOfReposeCompactionRate(agx::Real angleOfReposeCompactionRate)
Set how the compaction should increase the angle of repose.
CompactionProperties * getCompactionProperties() const
void setMaximumParticleActivationVolume(agx::Real volume)
Sets the maximum volume (m3) of active zone wedges that should wake particles.
void setAvalancheDecayFraction(agx::Real decayFraction)
Set the fraction of the height difference that violates the angle of repose condition that will be tr...
void setPenetrationForceVelocityScaling(agx::Real penetrationForceVelocityScaling)
Set the penetration force velocity scaling constant.
void setDeleteSoilParticlesOutsideBounds(bool enable)
Set if terrain should delete soil particles outside of terrain bounds.
void setAvalancheMaxHeightGrowth(agx::Real maxHeightGrowth)
Set the maximum allowed height (m) transfer per time step due to avalanching.
bool loadLibraryMaterial(const agx::String &materialName)
Loads a TerrainMaterial preset in the TerrainMaterialLibrary that contains calibrated bulk and contac...
agx::Real getLastExcavatedVolume() const
TerrainMaterial * getTerrainMaterial() const
Returns the default terrain material which is used to derive material and contact material parameters...
TerrainProperties * getProperties()
agx::Material * getMaterial(Terrain::MaterialType type=Terrain::MaterialType::TERRAIN) const
Returns one of the internal materials in agxTerrain: MaterialType::TERRAIN - The internal material us...
TerrainGridControl * getTerrainGridControl() const
Get control interface of the terrain grid data.
agx::Vec2 getSize() const
Returns the total surface size of he terrain in X Y coordinate system.
size_t getResolutionY() const
size_t getResolutionX() const
void setMaterial(agx::Material *material, Terrain::MaterialType type=Terrain::MaterialType::TERRAIN)
Assign new material to this terrain.
static void pre(FuncT callback, agxSDK::Simulation *simulation, Args &&... args)
Register a pre step event callback.
@ AGGRESSIVE
Contact reduction enabled with bin resolution = 1.
Wheel used in tracked vehicles.
@ MOVE_NODES_TO_ROTATION_PLANE
If enabled - when a node is merged to the wheel, move the node into the plane defined by the wheel ce...
@ MOVE_NODES_TO_WHEEL
Similar to MOVE_NODE_TO_ROTATION_PLANE but this property will also make sure the node is moved to the...
@ SPROCKET
Sprocket - geared driving wheel with default property MERGE_NODES.
@ IDLER
Idler - geared non-powered wheel with default property MERGE_NODES.
Assembly object representing a continuous track with a given number of shoes (nodes).
agx::RigidBody * getRigidBody() const
Oriented box friction model that uses the same normal force magnitude for all contact points associat...
Specialization for constraints that have only one degree of freedom such as Hinge and Prismatic.
agx::Real getAngle() const
This method return the current angle for the 1D constraint.
agx::Lock1D * getLock1D()
@ DIRECT
Solved only in the DIRECT solver.
virtual agx::Bool getEnable() const
agx::Vec3 transformPointToLocal(const agx::Vec3 &pointWorld) const
Transform point from the world coordinate frame to this frame.
agx::Vec3 transformVectorToLocal(const agx::Vec3 &worldVector) const
Transform vector from the world coordinate frame to this frame.
Base class for all friction models with interface and some utility/necessary methods implemented.
@ SPLIT
First Normal equation is calculated in the DIRECT solver, then in a second pass the normal and the fr...
@ DIRECT
Normal and friction equation calculated only in the DIRECT solver.
@ ITERATIVE
Normal and friction equation calculated only in the ITERATIVE solver.
@ DIRECT_AND_ITERATIVE
Normal and friction equation calculated both in the ITERATIVE and the DIRECT solver.
@ TRANSLATIONAL_2
Select DOF for the second translational axis.
@ ROTATIONAL_2
Select DOF corresponding to the second rotational axis.
@ TRANSLATIONAL_1
Select DOF for the first translational axis.
@ ROTATIONAL_1
Select DOF corresponding to the first rotational axis.
@ TRANSLATIONAL_3
Select DOF for the third translational axis.
A specific exception thrown when a signal is caught, as the current simulation step is interrupted.
Iterative Projected Cone Friction (IPC friction) model.
agx::Vec3 transformVectorToWorld(const agx::Vec3 &vectorLocal) const
agx::Vec3 getPosition() const
Current model frame position, given in world coordinate frame.
const agx::RigidBody * getRigidBody() const
Scale box friction model with oriented friction box.
void setInertiaTensor(const agx::SPDMatrix3x3 &inertiaTensor, const agx::AffineMatrix4x4 &cmLocalTransform=agx::AffineMatrix4x4(), bool autogenerateCmOffset=false, bool autogenerateInertia=false)
Convenience method for setting the inertia tensor from extern calculations given in local center of m...
const agx::Name & getName() const
void setAngularVelocityDamping(float damping)
Set angular velocity damping for the body in all directions x, y and z.
void resize(size_t size)
Resize the vector, which then enables direct addressing using the bracket '[]' operator.
iterator insert(iterator position, const T &value)
void push_back(const T &value)
void push_back(const T2 &value)
void AGXCORE_EXPORT transform(void *target, const Format *targetFormat, const void *source, const Format *sourceFormat, size_t numElements)
Transform data between two buffers.
AGXOSG_EXPORT osg::Texture2D * createTexture(const std::string &filename)
Create a Texture2D object that can be assigned to nodes.
AGXOSG_EXPORT bool setTexture(const agxCollide::Geometry *geometry, osg::Group *rootNode, const std::string &imagePath, bool repeat=false, TextureMode mode=DIFFUSE_TEXTURE)
Read an image from disk and apply to the specified geometry as a 2D Texture in OVERRIDE|PROTECTED mod...
AGXOSG_EXPORT bool readFile(const std::string &filename, agxSDK::Simulation *simulation, osg::Group *root, agxSDK::Assembly *parent=nullptr, agx::Real detailRatio=0.35, bool createAxis=false, agx::UInt selection=agxSDK::Simulation::READ_DEFAULT, agxOSG::RigidBodyRenderCache *cache=nullptr)
Utility function for reading various files:
This namespace contains classes related to vehicles dynamics.
const Node * getNode(const IteratorCompatibleContainer &container, agx::Real distanceFromStart)
Finds the wire node given current distance from the start of the wire (wire defined to be iterator co...
T1 clamp(T1 v, T2 minimum, T3 maximum)
std::pair< Real, Real > RealPair
Parameters that specifies the performance of a combustion engine.
tutorial_cable.cpp
#include <tuple>
static bool g_isUnitTest = false;
namespace
{
std::tuple<agx::RigidBodyRef, agxCollide::GeometryRef> createBody(
{
return std::make_tuple(body,geometry);
}
}
{
osg::Group*
root =
new osg::Group();
std::cout << "Tutorial 1 - Basics of creating a cable." << std::endl;
if (!g_isUnitTest) {
}
}
namespace tutorial2_helpers
{
std::tuple<agx::RigidBodyRef, agx::RigidBodyRef, agx::RigidBodyRef>
{
std::tie(ground, groundGeom) = createBody(
simulation, root,
std::tie(wall, wallGeom) = createBody(
simulation, root,
std::tie(base, baseGeom) = createBody(
simulation, root,
std::tie(arm, armGeom) = createBody(
simulation, root,
simulation->
add(constraint1);
simulation->
add(constraint2);
{
{
public:
: m_hinge(hinge)
, m_minAngle(minAngle)
, m_maxAngle(maxAngle)
, m_speed(speed)
, m_ignoreUntil(2.0)
{
}
{
if (time > m_ignoreUntil)
{
if (m_hinge->getMotor1D()->getSpeed() >=
agx::Real(0.0))
{
if (m_hinge->getAngle() > m_maxAngle)
{
m_hinge->getMotor1D()->setSpeed(-m_speed);
}
else
{
m_hinge->getMotor1D()->setSpeed(m_speed);
}
}
else if (m_hinge->getMotor1D()->getSpeed() <
agx::Real(0.0))
{
if (m_hinge->getAngle() < m_minAngle)
{
m_hinge->getMotor1D()->setSpeed(m_speed);
}
else
{
m_hinge->getMotor1D()->setSpeed(-m_speed);
}
}
}
}
protected:
virtual ~HingeOperator() {}
private:
};
simulation->
add(
new HingeOperator(hinge, minAngle, maxAngle, speed));
};
return std::make_tuple(ground, base, arm);
}
}
{
std::cout << "Tutorial 2 - Attaching the cable to rigid bodies." << std::endl;
osg::Group*
root =
new osg::Group();
using namespace tutorial2_helpers;
std::tie(ground, base, arm) = createScene(simulation, root, baseLength, armLength, baseRadius, armRadius, machineHeight);
}
{
std::cout << "Tutorial 3 - Cable properties." << std::endl;
osg::Group*
root =
new osg::Group();
auto createBendTest = [simulation,
root, baseHe, &worldOffset](
agx::Real youngsModulus)
{
};
createBendTest(1e10);
createBendTest(2e10);
createBendTest(5e10);
createBendTest(1e11);
createBendTest(2e11);
createBendTest(5e11);
{
return cable;
};
cable2->setCableProperties(sharedProperties);
}
{
std::cout << "Tutorial 4 - Iterating over a cable." << std::endl;
osg::Group*
root =
new osg::Group();
unsigned counter(0);
while (!iterator.
isEnd()) {
agx::Vec3 beginPosition = iterator->getBeginPosition();
agx::Vec3 endPosition = iterator->getEndPosition();
++counter;
std::cout <<
"Segment " << counter <<
" extends from " << beginPosition.
x() <<
" to " << endPosition.
x() <<
"." << std::endl;
++iterator;
}
{
public:
: m_cable(cable)
{
}
{
for (auto segment : *m_cable)
{
agx::Real tension = segment->getStretchTension();
++segment;
}
}
private:
osg::Group* m_root;
};
simulation->
add(
new TensionRenderer(cable, root));
simulation->
add(longerCable);
for (auto segment : *longerCable)
{
}
{
public:
: m_cable(cable)
{
}
{
for (auto segment : *m_cable)
{
agx::Real tension = segment->getBendTension();
}
}
private:
osg::Group* m_root;
};
simulation->
add(
new BendTensionRenderer(longerCable, root));
}
{
for (auto segment : *cable)
{
}
}
{
std::cout << "Tutorial 5 - Routing parameters." << std::endl;
osg::Group*
root =
new osg::Group();
auto x = {0.0, 1.0, 1.1, 1.25, 2.0};
for (auto xPos : x) {
}
for (auto xPos : x) {
}
std::cout << "Cable created with " << cable1->getNumSegments() << " segments and resolution " << cable1->getResolution() << "." << std::endl;
for (auto xPos : x) {
}
std::cout <<
"\nFirst initialization attempt resulted in " << report.
getNumSegments() <<
". Resolution is " << cable2->getResolution() << std::endl;
report = cable2->tryInitialize(
agx::Real(0.051));
std::cout <<
"\nSecond initialization attempt resulted in " << report.
getNumSegments() <<
". Resolution is " << cable2->getResolution() << std::endl;
if (!g_isUnitTest) {
}
}
{
std::cout << "Tutorial 6 - Detecting collisions." << std::endl;
osg::Group*
root =
new osg::Group();
leftCable->setName("LeftCable");
simulation->
add(leftCable);
createCableRenderer(leftCable, red, root);
rightCable->setName("RightCable");
simulation->
add(rightCable);
createCableRenderer(rightCable, green, root);
{
public:
{
if (cable1 == nullptr && cable2 == nullptr)
std::cout << "Collision between body " << bodyName1 << " and body " << bodyName2 << '.' << std::endl;
else if (cable1 != nullptr && cable2 == nullptr)
std::cout <<
"Collision between cable " << cable1->
getName() <<
" and body " << bodyName2 <<
'.' << std::endl;
else if (cable1 == nullptr && cable2 != nullptr)
std::cout <<
"Collision between cable " << cable2->
getName() <<
" and body " << bodyName1 <<
'.' << std::endl;
else if (cable1 != nullptr && cable2 != nullptr)
std::cout <<
"Collision between cable " << cable1->
getName() <<
" and cable " << cable2->
getName() <<
'.' << std::endl;
}
};
simulation->
add(
new CableContactFinder());
}
{
std::cout << "Tutorial 7 - Attaching objects along a cable after it has been initialized." << std::endl;
osg::Group*
root =
new osg::Group();
createCableRenderer(cable,
agx::Vec3f(1, 0, 0), root);
agx::Vec3 objectAttachPoint(objectHe.x(), -objectHe.y(), -objectHe.z());
cable->attach(segment, object, objectAttachPoint);
simulation->
add(rigidObject);
while (rigidSegment->getEndPosition().x() < rigidObject->
getPosition().
x())
++rigidSegment;
cable->attach(rigidSegment, rigidObject, transform);
}
{
std::cout << "Tutorial 8 - Using rebind to create a spiral." << std::endl;
osg::Group*
root =
new osg::Group();
cable->setPosition(0, 0, spiralHeight / 2);
agx::Real angleStep = 360.0 / angleResolution;
agx::Real heightStep = 0.2*radius + spiralHeight / (angleResolution * heightResolution);
while (z < zEnd)
{
for (auto a = 0.0; a < 360 - angleStep; a += angleStep)
{
z = z + heightStep;
}
}
cable->rebind();
createCableRenderer(cable,
agx::Vec3f(1, 0, 0), root);
}
{
std::cout << "Tutorial 9 - Control the precise initial location and orientation of segments." << std::endl;
osg::Group*
root =
new osg::Group();
struct Placement {
};
{
};
for (auto& placement : placements) {
node->getRigidBody()->setRotation(placement.rot);
cable->add(node);
}
cable->add(node);
std::cout <<
"\nInitialization resulted in " << report.
getNumSegments() <<
". Resolution is " << cable->getResolution() << std::endl;
}
{
};
for (auto& placement : placements) {
node->getRigidBody()->setRotation(placement.rot);
cable->add(node);
}
for (auto segment : cable->getSegments()) {
}
}
{
};
for (auto& placement : placements) {
node->getRigidBody()->setPosition(placement.pos);
}
else {
}
node->getRigidBody()->setRotation(placement.rot);
cable->add(node);
}
}
}
int main( int argc, char** argv )
{
std::cout <<
"\tTutorial Cable\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
application->addScene(buildTutorial2, '2');
application->addScene(buildTutorial3, '3');
application->addScene(buildTutorial4, '4');
application->addScene(buildTutorial5, '5');
application->addScene(buildTutorial6, '6');
application->addScene(buildTutorial7, '7');
application->addScene(buildTutorial8, '8');
application->addScene(buildTutorial9, '9');
if ( application->init( argc, argv ) )
{
if (args->
read(
"--isFileSanityUnitTest") || args->
read(
"--agxOnly"))
{
g_isUnitTest = true;
}
return application->run();
}
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
A cable node that is attached to a RigidBody.
Class controlling material properties of one or more cables.
void setYoungsModulus(agx::Real youngsModulus, agxCable::Direction direction)
Set Young's modulus of the cable along the given direction.
A model of a cable using a lumped elements model.
static agxCable::Cable * getCableForGeometry(agxCollide::Geometry *geometry)
A cable not that is simply a point in space.
A route that creates segments directly from the routing nodes.
size_t getNumSegments() const
agx::Real getActualError() const
agx::RigidBody * getRigidBody()
void setEnableCollisions(const Geometry *g1, const Geometry *g2, bool flag)
Explicitly disable/enable a geometry pair.
bool read(const std::string &key, size_t startPosition=0, bool findAlsoHandled=true)
Looks for an argument, and returns true if it finds it.
void setAutoStepping(bool flag, bool resetTimer=true)
Should the application step automatically? False for 'pause', true for 'play'.
void setEnableOSGRenderer(bool flag)
A node that can be associated with a collision geometry so that the transformation of this node is up...
agx::Name getName() const
Optionally unbounded linked structure segment iterator object to iterate segments in a SegmentStructu...
AffineMatrix4x4T< T > & setRotate(const QuatT< T > &q)
Set the rotational part of the matrix using the specified quaternion leaving the translational part u...
static AffineMatrix4x4T< Real > rotate(const Vec3T< Real > &from, const Vec3T< Real > &to)
Return a matrix which rotate a vector from from to to.
AffineMatrix4x4T< T > & setTranslate(const Vec3T< T > &t)
Set the translational part of the matrix using the vector t.
static agx::Bool calculateFramesFromWorld(agx::Vec3 worldPoint, agx::Vec3 worldAxis, const agx::RigidBody *rb1, agx::Frame *rb1Frame, const agx::RigidBody *rb2, agx::Frame *rb2Frame)
Given a point and an axis in world, this function calculates each local attachment frame for one or t...
Representation of a name string.
MotionControl
The MotionControl enumeration indicates what makes a RigidBody move.
@ DYNAMICS
This body moves from the influence of forces.
agx::Vec3 getPosition() const
Current model frame position, given in world coordinate frame.
void setSpeed(agx::Real speed)
Assign target speed for this controller.
AGXOSG_EXPORT GeometryNode * findGeometryNode(const agxCollide::Geometry *geometry, osg::Group *rootNode)
Finds the geometry node associated to a geometry.
AGXOSG_EXPORT osg::MatrixTransform * createAxes(float scale=1, float width=3)
Create a x, y and z arrow with the size 1.
const Vec3 Y_AXIS(0.0, 1.0, 0.0)
const Vec3 Z_AXIS(0.0, 0.0, 1.0)
const Vec3 X_AXIS(1.0, 0.0, 0.0)
AGXPHYSICS_EXPORT agx::Bool equivalent(const agx::AddedMassInteraction::Matrix6x6 &lhs, const agx::AddedMassInteraction::Matrix6x6 &rhs, agx::Real eps=agx::RealEpsilon)
tutorial_cableWindAndWater.cpp
#include "tutorialUtils.h"
namespace
{
{
return node;
}
}
{
osg::Group*
root =
new osg::Group( );
waterGeometry->setPosition( 0, 0, -5 );
waterGeometry->setMaterial( waterMaterial );
::createWaterVisual( waterGeometry, root );
controller->addWater( waterGeometry );
simulation->
add( controller );
simulation->
add( waterGeometry );
simulation->
add( cable1 );
simulation->
add( cable2 );
simulation->
add( cable3 );
size_t numSegments = cable3->getNumSegments();
int counter( 0 );
while ( !iterator.
isEnd() && counter <
static_cast<agx::Real>(numSegments) * 0.5 )
{
++counter;
iterator++;
}
}
{
osg::Group*
root =
new osg::Group( );
simulation->
add( controller );
simulation->
add( cable1 );
simulation->
add( cable2 );
controller->setEnableAerodynamics( cable2, true );
simulation->
add( cable3 );
size_t numSegments = cable3->getNumSegments();
int counter( 0 );
while ( !iterator.
isEnd() && counter <
static_cast<agx::Real>(numSegments) * 0.5 )
{
controller->setEnableAerodynamics( rb, true );
++counter;
iterator++;
}
}
int main( int argc, char** argv )
{
"\tTutorial cable - wind and water\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
Controller that collects relevant data and executes algorithms (probably mostly aero- and hydrodynami...
static agxRender::Color Orange()
static agxRender::Color DeepSkyBlue()
static agxRender::Color Green()
static agxRender::Color DarkBlue()
AGXOSG_EXPORT void setAlpha(osg::Node *node, float alpha)
Set the alpha part of the material for a node.
AGXOSG_EXPORT void setAmbientColor(osg::Node *node, const agx::Vec4f &color)
Set the ambient part of a material for a node.
AGXOSG_EXPORT void setShininess(osg::Node *node, float shininess)
Set the shininess exponent for the Phong specular model.
AGXOSG_EXPORT void setSpecularColor(osg::Node *node, const agx::Vec4f &color)
Set the specular part of a material for a node.
tutorial_cable_damage.cpp
{
std::cout << "Tutorial 1 - Basic usage.\n";
osg::Group* root = new osg::Group();
simulation->
add(obstacle);
cable->addComponent(damage);
damage->setStretchDeformationWeight(
agx::Real(100.0));
damage->setBendDeformationWeight(
agx::Real(1.0));
damage->setTwistDeformationWeight(
agx::Real(30.0));
damage->setStretchRateWeight(
agx::Real(100.0));
damage->setStretchTensionWeight(
agx::Real(1e-3));
damage->setBendTensionWeight(
agx::Real(0.02));
damage->setTwistTensionWeight(
agx::Real(0.04));
damage->setNormalForceWeight(
agx::Real(2e-4));
damage->setFrictionForceWeight(
agx::Real(2.0));
std::cout <<
"Total damage: " << segmentDamage.
total() <<
'\n';
std::cout <<
"Stretch tension: " << segmentDamage.
stretchTension() <<
'\n';
std::cout <<
"Stretch rate: " << segmentDamage.
stretchRate() <<
'\n';
std::cout <<
"Bend tension: " << segmentDamage.
bendTension() <<
'\n';
std::cout <<
"Bend rate: " << segmentDamage.
bendRate() <<
'\n';
std::cout <<
"Twist tension: " << segmentDamage.
twistTension() <<
'\n';
std::cout <<
"Twist rate: " << segmentDamage.
twistRate() <<
'\n';
std::cout << '\n';
std::cout <<
"Total damage: " << accumulatedSegmentDamage.
total() <<
'\n';
std::cout <<
"Stretch tension: " << accumulatedSegmentDamage.
stretchTension() <<
'\n';
std::cout <<
"Stretch rate: " << accumulatedSegmentDamage.
stretchRate() <<
'\n';
std::cout <<
"Bend tension: " << accumulatedSegmentDamage.
bendTension() <<
'\n';
std::cout <<
"Bend rate: " << accumulatedSegmentDamage.
bendRate() <<
'\n';
std::cout <<
"Twist tension: " << accumulatedSegmentDamage.
twistTension() <<
'\n';
std::cout <<
"Twist rate: " << accumulatedSegmentDamage.
twistRate() <<
'\n';
std::cout << '\n';
simulation->
add(renderer);
for (auto& d: damages) {
largest[i] = std::max(largest[i], d[i]);
}
}
int i(0);
i++;
}, simulation);
}
int main(int argc, char** argv)
{
<< "\tTutorial cable damage\n" << "\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
if (application->init(argc, argv)) {
return application->run();
}
}
catch (std::exception& e) {
std::cerr << "\nMain caught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
The CableDamage class is used to estimate damage caused to a cable during a simulation.
A SegmentDamage instance records damages estimates computed for a single cable segment.
agx::Real stretchRate() const
agx::Real stretchDeformation() const
agx::Real bendDeformation() const
agx::Real contactFrictionForce() const
agx::Real bendTension() const
agx::Real twistRate() const
agx::Real bendRate() const
agx::Real stretchTension() const
agx::Real twistTension() const
agx::Real twistDeformation() const
agx::Real contactNormalForce() const
Class that creates OSG rendering nodes for each segment of a cable and colors them with a color gradi...
void stepForward()
Take one step forward in the simulation.
agx::TimeStamp stepTo(agx::TimeStamp t)
Step the simulation forward 1 or more time steps in time until we get to the time t.
void AGXPHYSICS_EXPORT init()
Initialize AGX Dynamics API including thread resources and must be executed before using the AGX API.
tutorial_cable_tunneling_guard.cpp
namespace tutorial_helpers
{
{
fixedCable->setName("Fixed cable");
assembly->
add(fixedCable);
swingingCable->setName("Swinging cable");
assembly->
add(swingingCable);
return assembly;
}
{
fixedCable->setName("Fixed cable");
assembly->
add(fixedCable);
fallingCable->setName("Falling cable");
assembly->
add(fallingCable);
return assembly;
}
{
sd->
setText(0,
"Press e to pause/resume the simulation.");
sd->
setText(1,
"Press r to step the simulation when paused.");
sd->
setText(2,
"Press g to enable/disable rendering of the tunneling guard hulls.");
}
}
{
using tutorial_helpers::create_swinging_cable_assembly;
std::cout << "Tutorial 1 - Basic usage\n";
osg::Group *
root =
new osg::Group();
4));
}
{
using tutorial_helpers::create_swinging_cable_assembly;
std::cout << "Tutorial 2 - Hull Scale\n";
osg::Group *
root =
new osg::Group();
simulation->
add(smallScale);
simulation->
add(largeScale);
simulation->
add(twoSmallScales);
}
{
std::cout << "Tutorial 3 - Angle Threshold.\n";
osg::Group *
root =
new osg::Group();
simulation->
add(fixedCable);
guard->setAngleThreshold(0);
verticallyFallingCable->addComponent(guard);
simulation->
add(verticallyFallingCable);
verticallyFallingCableDefaultAngleThreshold->addComponent(
guard);
simulation->
add(verticallyFallingCableDefaultAngleThreshold);
guard->setAngleThreshold(0.5);
nearMissCableLowAngle->addComponent(guard);
simulation->
add(nearMissCableLowAngle);
}
{
using tutorial_helpers::create_falling_cable_assembly;
std::cout << "Tutorial 4 - Leniency\n";
osg::Group *
root =
new osg::Group();
guard->setLeniency(1);
simulation->
add(softCables);
simulation->
add(softCablesWithLeniency);
}
{
using tutorial_helpers::create_falling_cable_assembly;
std::cout << "Tutorial 5 - Debounce Steps.\n";
osg::Group *
root =
new osg::Group();
guard->setDebounceSteps(3);
simulation->
add(softCables);
simulation->
add(softCablesWithDebounce);
}
int main(int argc, char **argv)
{
<< "\tTutorial cable tunneling guard\n"
<< "\t--------------------------------\n\n"
<< std::endl;
try
{
application->addScene(buildTutorial1, '1');
application->addScene(buildTutorial2, '2');
application->addScene(buildTutorial3, '3');
application->addScene(buildTutorial4, '4');
application->addScene(buildTutorial5, '5');
if (application->init(argc, argv))
{
return application->run();
}
}
catch (std::exception &e)
{
std::cerr << "\nMain caught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
A linked structure componenent which uses additional sensor geometries placed around each segment to ...
static agxCable::Cable * asCable(agxSDK::Assembly *assembly)
agxCable::CableProperties * getCableProperties() const
Return the cable properties for this cable.
const AssemblyRefSetVector & getAssemblies() const
virtual agx::Bool addComponent(agxSDK::LinkedStructureComponent *component)
Add new component to this linked structure.
tutorial_car.cpp
#include "tutorialUtils.h"
{
public:
enum GeometryType { CYLINDER = 0, SPHERE, DOUBLE_SPHERE };
CarWheel(
RigidBody* carBody,
const agx::String& bodyAttachmentName,
Real radius,
const Vec3& suspensionAxis,
const Vec3& drivingAxis, osg::Group* root, GeometryType geometryType = SPHERE )
: m_body( carBody ), m_bodyAttachmentName( bodyAttachmentName ), m_radius( radius ), m_suspensionAxis( suspensionAxis ),
m_drivingAxis( drivingAxis ), m_root( root ), m_geometryType( geometryType )
{
}
{
}
RigidBody* getWheelSteeringAttachment() {
return m_wheelSteeringAttachment; }
const RigidBody* getBody()
const {
return m_body; }
Hinge* getDrivingHinge() {
return m_drivingHinge; }
Hinge* getSteeringHinge() {
return m_steeringHinge; }
{
getSuspension()->getRange1D( Constraint2DOF::FIRST )->setEnable( true );
getSuspension()->getRange1D( Constraint2DOF::FIRST )->setRange( range );
getSuspension()->getLock1D( Constraint2DOF::FIRST )->setEnable( true );
getSuspension()->getLock1D( Constraint2DOF::FIRST )->setPosition( position );
getSuspension()->getLock1D( Constraint2DOF::FIRST )->getRegularizationParameters()->setCompliance( lockCompliance );
getSuspension()->getLock1D( Constraint2DOF::FIRST )->getRegularizationParameters()->setDamping( lockDamping );
}
private:
{
if ( m_geometryType == CYLINDER ) {
}
else if ( m_geometryType == SPHERE ) {
}
else if ( m_geometryType == DOUBLE_SPHERE ){
}
m_wheelSteeringAttachment->getMassProperties()->setMass( 45 );
add( m_wheelSteeringAttachment );
m_wheelSteeringAttachment->addAttachment( suspensionFrame, "suspension" );
m_wheelSteeringAttachment->addAttachment( suspensionFrame, "steering" );
m_wheelSteeringAttachment->addAttachment( drivingFrame, "driving" );
Real distFromAttachment = 0.05;
wheelFrame->
setLocalMatrix( AffineMatrix4x4::rotate(
Vec3( 0, 0, 1 ), m_drivingAxis ) * AffineMatrix4x4::translate( -m_drivingAxis * distFromAttachment ) );
m_wheel->addAttachment( wheelFrame, "driving" );
m_wheelSteeringAttachment->setPosition(
Vec3( 0, 0, 0 ) );
m_wheel->setPosition( m_drivingAxis * distFromAttachment );
m_steeringHinge =
new Hinge( m_body, m_body->getAttachment( m_bodyAttachmentName ), m_wheelSteeringAttachment, m_wheelSteeringAttachment->getAttachment(
"steering" ) );
m_steeringHinge->setCompliance( 1E2 );
m_steeringHinge->getMotor1D()->getRegularizationParameters()->setCompliance( 1E-10 );
m_steeringHinge->getMotor1D()->setForceRange(
RangeReal( 2E3 ) );
m_steeringAndSuspension =
new CylindricalJoint( m_body, m_body->getAttachment( m_bodyAttachmentName ), m_wheelSteeringAttachment, m_wheelSteeringAttachment->getAttachment(
"suspension" ) );
add( m_steeringAndSuspension );
m_drivingHinge =
new Hinge( m_wheel, m_wheel->getAttachment(
"driving" ), m_wheelSteeringAttachment, m_wheelSteeringAttachment->getAttachment(
"driving" ) );
}
using Assembly::addNotification;
using Assembly::removeNotification;
private:
osg::observer_ptr< osg::Group > m_root;
GeometryType m_geometryType;
};
{
public:
CarController( CarWheel* backRight, CarWheel* backLeft, CarWheel* frontRight, CarWheel* frontLeft,
Real maxTorque,
int keyAccelerate,
int keyBrake,
int keyGearUp,
int keyGearDown,
int keySteerLeft,
int keySteerRight )
: m_backRight( backRight ), m_backLeft( backLeft ), m_frontRight( frontRight ), m_frontLeft( frontLeft ), m_maxTorque( maxTorque ),
m_gearRatio( 8,
Real( 0 ) ), m_differentialRatio( 4.1 ), m_gasPedalInput( 0 ), m_brakePedalInput( 0 ), m_steeringInput( 0 )
{
m_input = new CarControllerInput( m_gasPedalInput, m_brakePedalInput, m_steeringInput, int(m_gearRatio.size()), keyAccelerate, keyBrake, keyGearUp, keyGearDown, keySteerLeft, keySteerRight );
m_backRight->getDrivingHinge()->getMotor1D()->setEnable( true );
m_backLeft->getDrivingHinge()->getMotor1D()->setEnable( true );
m_backRight->getDrivingHinge()->getMotor1D()->setSpeed( -100 );
m_backLeft->getDrivingHinge()->getMotor1D()->setSpeed( 100 );
m_gearRatio[ 0 ] = 0;
m_gearRatio[ 1 ] = 3.15;
m_gearRatio[ 2 ] = 2.18;
m_gearRatio[ 3 ] = 1.57;
m_gearRatio[ 4 ] = 1.19;
m_gearRatio[ 5 ] = 0.94;
m_gearRatio[ 6 ] = 0.76;
m_gearRatio[ 7 ] = 4.1;
}
{
}
{
}
{
computeAndSetTorque( m_backRight->getDrivingHinge(), m_backLeft->getDrivingHinge() );
computeAndSetBrake( m_frontRight->getDrivingHinge(), m_frontLeft->getDrivingHinge() );
updateSteering( m_frontRight->getSteeringHinge(), m_frontLeft->getSteeringHinge() );
m_input->update( 0, 0 );
std::stringstream ss;
ss << "Speed: " << int( m_frontLeft->getBody()->getVelocity().length() * 3.6 ) << " km/h";
}
private:
void computeAndSetTorque(
Hinge* h1,
Hinge* h2 )
const
{
agxAssert( m_input->getGear() < m_gearRatio.size() && m_gearRatio[ m_input->getGear() ] >= 0 && m_differentialRatio > 0 );
Real gearRatio = m_gearRatio[ m_input->getGear() ];
m_backRight->getDrivingHinge()->getMotor1D()->setSpeed( -getMaxWheelRPM( m_input->getGear() ) );
m_backLeft->getDrivingHinge()->getMotor1D()->setSpeed( getMaxWheelRPM( m_input->getGear() ) );
return;
}
Real omega0 = 0.5 * omegaTot / ( gearRatio * m_differentialRatio );
Real taue = m_gasPedalInput * getTorque( omega0 );
Real taud = gearRatio * m_differentialRatio * taue;
}
void computeAndSetBrake(
Hinge* h1,
Hinge* h2 )
const
{
Real maxBrakeTorque = 2E3;
Real brakeTorque = maxBrakeTorque * m_brakePedalInput;
}
{
Real maxSteeringSpeed = 2;
return;
}
Real bodySpeed = m_frontLeft->getBody()->getVelocity().length();
}
{
return m_maxTorque;
}
Real getMaxWheelRPM(
size_t gear )
const
{
int sgn = int(2 * ( gear != m_gearRatio.size() - 1 ) - 1);
return m_gearRatio[ gear ] > 0 ? sgn * 200 / m_gearRatio[ gear ] : 0;
}
private:
{
public:
CarControllerInput(
Real& gasPedalInput,
Real& brakePedalInput,
Real& steeringInput,
int numGearsIncReverse,
int keyAccelerate,
int keyBrake,
int keyGearUp,
int keyGearDown,
int keySteerLeft,
int keySteerRight )
: m_gasPedalInput( gasPedalInput ), m_brakePedalInput( brakePedalInput ), m_steeringInput( steeringInput ), m_numGears( numGearsIncReverse ),
m_keyAccelerate( keyAccelerate ), m_keyBrake( keyBrake ), m_keyGearUp( keyGearUp ), m_keyGearDown( keyGearDown ),
m_keySteerLeft( keySteerLeft ), m_keySteerRight( keySteerRight ), m_gear( 0 ), m_steeringReleased( true )
{
setMask( ActivationMask(KEYBOARD | UPDATE) );
}
virtual bool keyboard( int key, unsigned int , float , float , bool keydown )
{
bool handled = false;
if ( key == m_keyAccelerate ) {
m_gasPedalInput = (
Real)keydown;
handled = true;
}
else if ( key == m_keyBrake ) {
m_brakePedalInput = (
Real)keydown;
handled = true;
}
else if ( keydown && key == m_keyGearUp ) {
m_gear = std::min( m_gear + 1, m_numGears - 2 );
handled = true;
}
else if ( keydown && key == m_keyGearDown ) {
m_gear = std::max( m_gear - 1, -1 );
handled = true;
}
else if ( key == m_keySteerLeft ) {
m_steeringReleased = !keydown;
m_steeringInput = -
Real( keydown );
handled = true;
}
else if ( key == m_keySteerRight ) {
m_steeringReleased = !keydown;
m_steeringInput =
Real( keydown );
handled = true;
}
return handled;
}
virtual void update( float , float )
{
std::stringstream gTxt;
gTxt << "Gear: " << ( m_gear == 0 ? "N" : m_gear < 0 ? "R" : "" );
if ( m_gear > 0 )
gTxt << m_gear;
}
size_t getGear() const
{
if ( m_gear < 0 )
return m_numGears - 1;
return m_gear;
}
bool getSteeringReleased() const
{
return m_steeringReleased;
}
protected:
virtual ~CarControllerInput() {}
private:
CarControllerInput& operator=(const CarControllerInput&) {return *this;}
private:
int m_numGears;
int m_keyAccelerate;
int m_keyBrake;
int m_keyGearUp;
int m_keyGearDown;
int m_keySteerLeft;
int m_keySteerRight;
int m_gear;
bool m_steeringReleased;
};
private:
CarWheelRef m_backRight;
CarWheelRef m_backLeft;
CarWheelRef m_frontRight;
CarWheelRef m_frontLeft;
Real m_differentialRatio;
CarControllerInputRef m_input;
};
{
osg::Group *
root =
new osg::Group;
simulation->
add( ground );
enum eWheel { RIGHT_BACK = 0, LEFT_BACK, RIGHT_FRONT, LEFT_FRONT };
Vec3 carBodyDim =
Vec3( 2.2, 0.75, 0.45 );
CarWheel::GeometryType geometryType = CarWheel::SPHERE;
simulation->
add( carBody );
carBody->
addAttachment( rightFrontWheelAttachment,
"rightFront" );
CarWheelRef wheels[4];
wheels[ RIGHT_BACK ] =
new CarWheel( carBody,
"rightBack", wheelRadius,
Vec3( 0, 0, 1 ),
Vec3( 0, -1, 0 ), root, geometryType );
wheels[ RIGHT_BACK ]->setPosition( rightBackWheelAttachment->
getTranslate() );
wheels[ LEFT_BACK ] =
new CarWheel( carBody,
"leftBack", wheelRadius,
Vec3( 0, 0, 1 ),
Vec3( 0, 1, 0 ), root, geometryType );
wheels[ LEFT_BACK ]->setPosition( leftBackWheelAttachment->
getTranslate() );
wheels[ RIGHT_FRONT ] =
new CarWheel( carBody,
"rightFront", wheelRadius,
Vec3( 0, 0, 1 ),
Vec3( 0, -1, 0 ), root, geometryType );
wheels[ RIGHT_FRONT ]->setPosition( rightFrontWheelAttachment->
getTranslate() );
wheels[ LEFT_FRONT ] =
new CarWheel( carBody,
"leftFront", wheelRadius,
Vec3( 0, 0, 1 ),
Vec3( 0, 1, 0 ), root, geometryType );
wheels[ LEFT_FRONT ]->setPosition( leftFrontWheelAttachment->
getTranslate() );
for ( int i = 0; i < 4; ++i ) {
simulation->
add( wheels[ i ] );
}
Real suspensionCompliance = 9.9E-6;
Real suspensionDamping = 6.0 / 60;
for ( int i = 0; i < 4; ++i ) {
wheels[ i ]->setSuspension( suspensionRange, suspensionCompliance, suspensionDamping );
}
wheels[ RIGHT_BACK ]->getSteeringHinge()->getLock1D()->setEnable( true );
wheels[ LEFT_BACK ]->getSteeringHinge()->getLock1D()->setEnable( true );
wheels[ RIGHT_FRONT ]->getSteeringHinge()->getRange1D()->setEnable( true );
wheels[ RIGHT_FRONT ]->getSteeringHinge()->getRange1D()->setRange( steeringRange );
wheels[ LEFT_FRONT ]->getSteeringHinge()->getRange1D()->setEnable( true );
wheels[ LEFT_FRONT ]->getSteeringHinge()->getRange1D()->setRange( steeringRange );
wheels[ RIGHT_FRONT ]->getSteeringHinge()->getMotor1D()->setEnable( true );
wheels[ LEFT_FRONT ]->getSteeringHinge()->getMotor1D()->setEnable( true );
wheels[ RIGHT_FRONT ]->getSteeringHinge()->getMotor1D()->setForceRange(
RangeReal( 1E3 ) );
wheels[ LEFT_FRONT ]->getSteeringHinge()->getMotor1D()->setForceRange(
RangeReal( 1E3 ) );
simulation->
add( groundMaterial );
simulation->
add( backWheelMaterial );
simulation->
add( frontWheelMaterial );
for ( int i = 0; i < 2; ++i ) {
}
simulation->
add( backWheelGroundContact );
simulation->
add( frontWheelGroundContact );
for ( int i = 0; i < 4; ++i )
wheels[ i ]->getWheel()->getMassProperties()->setMass( 90 );
simulation->
add(
new CarController( wheels[ RIGHT_BACK ], wheels[ LEFT_BACK ], wheels[ RIGHT_FRONT ], wheels[ LEFT_FRONT ], maxTorque,
std::cerr << "\n\n";
std::cerr << "Key binding for controlling the car: \n\n";
std::cerr << " \'a\': forward (remember to switch gears!)\n";
std::cerr << " \'z\': brake\n";
std::cerr << " \'s\': gear up\n";
std::cerr << " \'x\': gear down\n";
std::cerr << " Left: turn left\n";
std::cerr << " Right: turn right\n" << std::endl;
}
int main( int argc, char** argv )
{
std::cout <<
"\tCar tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorialCar, '1' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
TextProxy * acquireText(const agx::String &text, const agx::Vec3 &pos, float size=1)
Create and return a new text sphere from the Proxy factory.
Abstract base class which encapsulated information for rendering a specific shape.
virtual void setColor(const agx::Vec3 &color)
Set the color of this proxy.
virtual void addNotification(agxSDK::Simulation *)
Called when this assembly is added to a simulation (given that it not already is in the simulation).
static agxStream::Serializable * create(agxStream::InputArchive &)
virtual void addNotification()
Called when this listener is added to the simulation.
virtual void removeNotification()
Called when this listener is removed from the simulation.
bool remove(agx::ContactMaterial *material)
Remove an explicit contact material from the set of existing ContactMaterials.
agxRender::RenderManager * getRenderManager()
virtual void pre(const agx::TimeStamp &time)
Called before a step is taken in the simulation Implement this method in the derived class to get cal...
virtual void setMask(int mask) override
Specifies a bitmask which determines which event types will activate this listener.
agx::Real getCurrentSpeed() const
This methods return the current speed for the 1D constraint.
A cylindrical joint is similar to a prismatic joint but with an extra degree of freedom free (rotatio...
void setForceRange(agx::RangeReal forceRange, const agx::UInt row=0)
Assign force range to a given row.
void setLocalMatrix(const agx::AffineMatrix4x4 &matrix)
Assign the local transformation matrix for this frame ignoring any eventual parent transformation.
agx::Vec3 getTranslate() const
void setLocalRotate(const agx::Quat &rotation)
Assign the parent relative rotation of this frame.
void setPosition(agx::Real position)
Assign new target position.
bool addAttachment(agx::Frame *frame, const agx::String &name)
Add a named attachment frame to this rigid body.
void setName(const agx::Name &name)
Assign new name to this rigid body.
AGXPHYSICS_EXPORT agx::Bool equalsZero(const agx::AddedMassInteraction::Matrix6x6 &matrix, agx::Real eps=agx::RealEpsilon)
tutorial_carSteering.cpp
{
public:
: m_backRight(wheelJoints[3]), m_backLeft(wheelJoints[2]), m_frontRight(wheelJoints[1]), m_frontLeft(wheelJoints[0]), m_steering(steering),
m_senceDecorator(senceDecorator)
{
m_backLeft->getLock1D(WheelJoint::STEERING)->setEnable(true);
m_backRight->getLock1D(WheelJoint::STEERING)->setEnable(true);
for (int i = 0; i < 4; ++i)
{
wheelJoints[i]->getMotor1D(WheelJoint::WHEEL)->setEnable(true);
wheelJoints[i]->getMotor1D(WheelJoint::WHEEL)->setSpeed(5.0);
}
m_senceDecorator->setText(0,
agx::String::format(
"Steering: %s", m_steering->getName().c_str()));
}
{
const Real maxSteeringAngle = 1.0;
if (t < 1.0)
{
m_steering->setSteeringAngle(0.0);
}
else if (t < 2.0)
{
m_steering->setSteeringAngle(maxSteeringAngle * (t - 1.0));
}
else
{
m_steering->setSteeringAngle(maxSteeringAngle);
}
}
private:
};
WheelRefVector& wheels, WheelJointRefVector& wheelJoints)
{
enum eWheel { RIGHT_BACK = 0, LEFT_BACK, RIGHT_FRONT, LEFT_FRONT };
ground->setName("ground");
Vec3 carHalfLengths =
Vec3(2.3, 0.8, 0.5);
simulation->
add(chassisBody);
rightBackWheelAttachment->
setLocalTranslate(
Vec3(-carHalfLengths.
x() + wheelRadius, -carHalfLengths.
y() - clearance, -carHalfLengths.
z()));
leftBackWheelAttachment->
setLocalTranslate(
Vec3(-carHalfLengths.
x() + wheelRadius, carHalfLengths.
y() + clearance, -carHalfLengths.
z()));
rightFrontWheelAttachment->
setLocalTranslate(
Vec3(carHalfLengths.
x() - wheelRadius, -carHalfLengths.
y() - clearance, -carHalfLengths.
z()));
leftFrontWheelAttachment->
setLocalTranslate(
Vec3(carHalfLengths.
x() - wheelRadius, carHalfLengths.
y() + clearance, -carHalfLengths.
z()));
chassisBody->
addAttachment(rightBackWheelAttachment,
"rightBack");
chassisBody->
addAttachment(rightFrontWheelAttachment,
"rightFront");
chassisBody->
addAttachment(leftFrontWheelAttachment,
"leftFront");
wheels[RIGHT_BACK]->setPosition(rightBackWheelAttachment->
getTranslate());
wheels[LEFT_BACK]->setPosition(leftBackWheelAttachment->
getTranslate());
wheels[RIGHT_FRONT]->setPosition(rightFrontWheelAttachment->
getTranslate());
wheels[LEFT_FRONT]->setPosition(leftFrontWheelAttachment->
getTranslate());
for ( int i = 0; i < 4; ++i )
{
simulation->
add(wheels[i]);
}
Vec3 rightBackPointWorld = wheels[RIGHT_BACK]->getPosition();
Vec3 leftBackPointWorld = wheels[LEFT_BACK]->getPosition();
Vec3 leftFrontPointWorld = wheels[LEFT_FRONT]->getPosition();
Vec3 rightFrontPointWorld = wheels[RIGHT_FRONT]->getPosition();
simulation->
add(rightBackWheelJoint);
simulation->
add(leftBackWheelJoint);
simulation->
add(rightFrontWheelJoint);
simulation->
add(leftFrontWheelJoint);
wheelJoints[0] = leftFrontWheelJoint;
wheelJoints[1] = rightFrontWheelJoint;
wheelJoints[2] = leftBackWheelJoint;
wheelJoints[3] = rightBackWheelJoint;
ground->setMaterial( groundMaterial );
simulation->
add( groundMaterial );
simulation->
add( backWheelMaterial );
simulation->
add( frontWheelMaterial );
for ( int i = 0; i < 2; ++i )
{
}
simulation->
add(backWheelGroundContact);
simulation->
add(frontWheelGroundContact);
for (int i = 0; i < 4; ++i)
wheels[i]->getMassProperties()->setMass(90);
}
{
osg::Group*
root =
new osg::Group;
WheelJointRefVector wheelJoints;
wheelJoints.resize(4);
WheelRefVector wheels;
wheels.resize(4);
buildTutorialCar(simulation, application, root, wheels, wheelJoints);
ackermann->setName("Ackermann");
simulation->
add(ackermann);
simulation->
add(
new SteeringController(wheelJoints, ackermann, sd));
}
{
osg::Group*
root =
new osg::Group;
WheelJointRefVector wheelJoints;
wheelJoints.resize(4);
WheelRefVector wheels;
wheels.resize(4);
buildTutorialCar(simulation, application, root, wheels, wheelJoints);
rackPinion->setName("RackPinion");
simulation->
add(rackPinion);
simulation->
add(
new SteeringController(wheelJoints, rackPinion, sd));
}
{
osg::Group*
root =
new osg::Group;
WheelJointRefVector wheelJoints;
wheelJoints.resize(4);
WheelRefVector wheels;
wheels.resize(4);
buildTutorialCar(simulation, application, root, wheels, wheelJoints);
bellCrank->setName("BellCrank");
simulation->
add(bellCrank);
simulation->
add(
new SteeringController(wheelJoints, bellCrank, sd));
}
{
osg::Group*
root =
new osg::Group;
WheelJointRefVector wheelJoints;
wheelJoints.resize(4);
WheelRefVector wheels;
wheels.resize(4);
buildTutorialCar(simulation, application, root, wheels, wheelJoints);
davis->setName("Davis");
simulation->
add(
new SteeringController(wheelJoints, davis, sd));
}
int main( int argc, char** argv )
{
std::cout <<
"\tTutorial: car steering \n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildAckermannSteer, '1');
application->addScene(buildBellCrankSteer, '2');
application->addScene(buildRackPinionSteer, '3');
application->addScene(buildDavisSteer, '4');
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
static agxRender::Color Wheat()
static agxRender::Color DarkGoldenrod()
Helper class to define reference frames for WheelJoint.
The wheel constraint is designed to attach two bodies such that one of them is free to rotate about a...
agx::Vec3 transformVectorToWorld(const agx::Vec3 &vectorLocal) const
Transform vector from this frame to the world frame.
constexpr Real radiansToDegrees(Real angle)
tutorial_centerOfBuoyancy.cpp
#include "tutorialUtils.h"
namespace
{
{
float alpha(0.3f);
return node;
}
}
{
public:
WaveWaterWrapper()
{
return worldPoint.
z() - surfaceLevel;
}
};
{
public:
CenterOfBuoyancyListener(
: m_refGeom(refGeom), m_visualGeom(visualGeom), m_waterController(waterController)
{
}
{
m_waterController->getCenterOfBuoyancy(m_refGeom, cob);
m_visualGeom->setPosition(cob);
}
private:
};
{
osg::Group*
root =
new osg::Group();
waterGeometry->setPosition(position);
createWaterVisual(waterGeometry, root);
controller->addWater(waterGeometry);
controller->setWaterWrapper(waterGeometry, new WaveWaterWrapper());
simulation->
add(controller);
simulation->
add(waterGeometry);
controller->setEnableCenterOfBuoyancy(sphereGeometry, true);
controller->getCenterOfBuoyancy(sphereGeometry, centerOfBuoyancy);
simulation->
add(cobGeometry);
cobGeometry->setEnableCollisions(false);
simulation->
add(
new CenterOfBuoyancyListener(sphereGeometry, cobGeometry, controller));
}
{
osg::Group*
root =
new osg::Group();
waterGeometry->setPosition(position);
::createWaterVisual(waterGeometry, root);
controller->addWater(waterGeometry);
controller->setWaterWrapper(waterGeometry, new WaveWaterWrapper());
simulation->
add(controller);
simulation->
add(waterGeometry);
controller->setEnableCenterOfBuoyancy(shipGeometry, true);
simulation->
add(cobGeometry);
cobGeometry->setEnableCollisions(false);
simulation->
add(
new CenterOfBuoyancyListener(shipGeometry, cobGeometry, controller));
}
int main(int argc, char** argv)
{
"\tTutorial hydrodynamics\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
application->addScene(buildTutorial2, '2');
if (application->init(argc, argv))
return application->run();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
@ REMOVE_DUPLICATE_VERTICES
Removes exactly identical duplicates in the vertices, as well as (resulting or previously existing) t...
Water wrapper that can be inherited to create custom made wrappers.
virtual agx::Real findHeightFromSurface(const agx::Vec3 &worldPoint, const agx::Vec3 &upVector, const agx::TimeStamp &t) const
Finds height from surface given point in world.
Contain classes for higher level modeling primitives, such as Tree, Terrain etc.
AGXPHYSICS_EXPORT agxCollide::Trimesh * createTrimesh(const agx::String &filename, uint32_t trimeshOptions=agxCollide::Trimesh::REMOVE_DUPLICATE_VERTICES, const agx::Matrix3x3 &vertexRotateAndScale=agx::Matrix3x3(), const agx::Vec3 &vertexTranslate=agx::Vec3())
Create a Trimesh shape from a supported mesh file format (See agxIO::MeshReader::FileType) containing...
Matrix3x3T< Real > Matrix3x3
tutorial_collisionDetection.cpp
#include <iomanip>
{
public:
agxSDK::StepEventListener(
agxSDK::StepEventListener::POST_STEP),
m_app(app), m_textStartRow(textStartRow)
{
}
{
if (m_app && m_app->getSceneDecorator()) {
int textRowCount = 0;
const int w = 7;
for (size_t i = 0; i < m_bodies.size(); ++i) {
if (m_bodies[i].isValid()) {
agx::Vec3 omega = m_bodies[i]->getAngularVelocity();
std::stringstream ss;
ss.precision(3);
ss << std::fixed << "Body " << std::setw(2) << i << ": " << m_bodies[i]->getName()
<<
": Velocity: (" << std::setw(w) << v.
x() << std::setw(w) << v.
y() << std::setw(w) << v.
z() <<
") m/s"
<<
"; Angular velocity : (" << std::setw(w) << omega.
x() << std::setw(w) << omega.
y() << std::setw(w) << omega.
z() <<
") rad/s";
m_app->getSceneDecorator()->setText(textRowCount + m_textStartRow, ss.str());
textRowCount++;
}
}
}
}
{
m_bodies.push_back(body);
}
protected:
virtual ~VelocityPrinter() {}
protected:
int m_textStartRow;
};
{
osg::Group*
root =
new osg::Group();
assert(gearMesh);
gearGeometry->setMaterial(material);
assert(bowlMesh);
bowlGeometry->setMaterial(material);
app->
getSceneDecorator()->
setText(0,
"Gear resting in bowl. Press 'g' to toggle debug rendering, 'G' to toggle OSG rendering.");
VelocityPrinterRef velocityPrinter = new VelocityPrinter(app, 2);
sim->
add(velocityPrinter);
velocityPrinter->addBody(gear);
velocityPrinter->addBody(bowl);
velocityPrinter->post(0);
agx::Vec3 eye =
agx::Vec3(-3.0832610934950855E-001, -1.5247185050586647E+000, 1.0936281422567065E+000);
agx::Vec3 center =
agx::Vec3(3.1770658032579266E-002, -8.2853946902834041E-002, 1.7500546587327595E-001);
agx::Vec3 up =
agx::Vec3(1.0626159313065037E-001, 5.1633116377248522E-001, 8.4977091215379164E-001);
}
{
osg::Group*
root =
new osg::Group();
app->
getSceneDecorator()->
setText(1,
"Note the difference in angular velocity due to non-perfect gear arrangement - this kind ");
VelocityPrinterRef velocityPrinter = new VelocityPrinter(app, 5);
sim->
add(velocityPrinter);
velocityPrinter->addBody(rb1);
velocityPrinter->addBody(rb2);
velocityPrinter->post(0);
agx::Vec3 eye =
agx::Vec3(2.7230274677276611E-001, -2.3059253137839035E+000, 1.7681157588958740E-001);
agx::Vec3 center =
agx::Vec3(2.7230274677276611E-001, -7.3146820068359375E-003, 1.7681157588958740E-001);
agx::Vec3 up =
agx::Vec3(0.0000000000000000E+000, 0.0000000000000000E+000, 1.0000000000000000E+000);
}
int main(int argc, char** argv)
{
std::cout <<
"\tCollision detection tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
application->addScene(buildTutorial2, '2');
if (application->init(argc, argv))
return application->run();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
agxSDK::Simulation * getSimulation()
void disableFlags(unsigned int flags)
Disable all rendering flags specified in flags.
@ RENDER_BATCH_WIRES
Wire content batch rendered.
@ RENDER_BATCH_BODIES
Batch render bodies.
@ RENDER_BATCH_CONTACTS
Render contacts as one batch call for all contacts.
The agxSDK namespace contain classes to bridge the collision detection system and the dynamical simul...
tutorial_constraints.cpp
#include "tutorialUtils.h"
#ifdef __clang__
# pragma clang diagnostic ignored "-Winvalid-source-encoding"
#endif
typedef std::pair< agx::RigidBody*, agx::RigidBody* > RigidBodyPtrPair;
{
if ( createGroundBox ) {
agx::Vec3 boxGeometryHalfExtent( 15, 15, 0.5 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
boxGeometry->setName( "boxGeometry" );
}
return std::make_pair( rb1, rb2 );
}
{
osg::Group*
root =
new osg::Group();
RigidBodyPtrPair rbPair = createDefaultScene(
agx::Vec3( -4, 2, 7 ),
agx::Vec3( 0, 2, 7 ), simulation, root );
rbPair = createDefaultScene(
agx::Vec3( -4, -2, 7 ),
agx::Vec3( 0, -2, 7 ), simulation, root,
false );
}
{
osg::Group*
root =
new osg::Group();
RigidBodyPtrPair rbPair = createDefaultScene(
agx::Vec3( -4, 0, 7 ),
agx::Vec3( 0, 0, 7 ), simulation, root );
}
{
public:
: m_constraint( constraint ), m_speed( speed ), m_keyPositive( keyPositive ), m_keyNegative( keyNegative )
{
if ( m_constraint.isValid() )
m_constraint->getMotor1D()->setEnable( true );
}
virtual bool keyboard(
int key,
unsigned int ,
float ,
float ,
bool keydown )
{
bool handled = false;
if ( !m_constraint.isValid() )
return false;
if ( key == m_keyPositive ) {
m_constraint->getMotor1D()->setSpeed( m_speed * int( keydown ) );
handled = true;
}
else if ( key == m_keyNegative ) {
m_constraint->getMotor1D()->setSpeed( -m_speed * int( keydown ) );
handled = true;
}
return handled;
}
private:
int m_keyPositive;
int m_keyNegative;
};
{
osg::Group*
root =
new osg::Group();
RigidBodyPtrPair rbPair = createDefaultScene(
agx::Vec3( -4, -2, 7 ),
agx::Vec3( 0, -2, 7 ), simulation, root );
l->setCompliance( 1.0E-10 );
l->setCompliance( 2.0E-7, 3 );
rbPair = createDefaultScene(
agx::Vec3( -4, 2, 7 ),
agx::Vec3( 0, 2, 7 ), simulation, root );
l2->setCompliance( 1.0E-10 );
l2->setCompliance( 2.0E-7, 0 );
std::cout << "Press left/right to operate prismatic constraint\n";
std::cout << "Press up/down to operate hinge constraint\n" << std::endl;
}
{
public:
: m_firstBody( firstBody )
{
}
virtual bool keyboard(
int key,
unsigned int ,
float ,
float ,
bool )
override
{
bool handled = false;
if ( !m_firstBody.isValid() )
return false;
m_firstBody->setVelocity(m_firstBody->getVelocity() +
agx::Vec3(0, 0, 1));
handled = true;
}
m_firstBody->setVelocity(m_firstBody->getVelocity() +
agx::Vec3(0, 0, -1));
handled = true;
}
m_firstBody->setVelocity(m_firstBody->getVelocity() +
agx::Vec3(1, 0, 0));
handled = true;
}
m_firstBody->setVelocity(m_firstBody->getVelocity() +
agx::Vec3(-1, 0, 0));
handled = true;
}
return handled;
}
private:
};
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 15, 15, 0.5 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
boxGeometry->setName( "boxGeometry" );
const size_t numSegments = 30;
for( size_t i = 0; i < numSegments; ++i ) {
dynamicRbGeometry->add( dynamicRbCylinder );
dynamicRb->
add( dynamicRbGeometry );
simulation->
add( dynamicRb );
if ( prevRb != 0L ) {
ballJoint->setCompliance(
agx::Real( 1E-7 ) );
simulation->
add( ballJoint );
}
prevRb = dynamicRb;
}
simulation->
add(
new CableMoveKeyboardHandler( body ) );
}
{
public:
: m_rb1( rb1 ), m_rb2( rb2 )
{
}
private:
};
{
std::cout << "Time: " << t << std::endl;
return KEEP_CONTACT;
m_rb2->setVelocity(
agx::Vec3( 0, 0, 0.2 ) );
std::cout << "Lifting" << std::endl;
return KEEP_CONTACT;
}
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 3, 3, 0.5 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
boxGeometry->setName( "boxGeometry" );
simulation->
add( grip1 );
simulation->
add( grip2 );
}
{
osg::Group*
root =
new osg::Group();
RigidBodyPtrPair rbPair = createDefaultScene(
agx::Vec3( -4, 0, 15 ),
agx::Vec3( 4, 0, 15 ), simulation, root,
false );
{
#if 1
#else
#endif
}
#if 0
{
h1->setCompliance( 1E-11 );
}
#endif
#if 0
{
}
#endif
#if 0
{
}
#endif
#if 0
{
}
#endif
}
{
public:
{
create( dimension, numSegments, mass, compliance, root );
}
{
}
private:
{
for ( size_t i = 0; i < numSegments; ++i ) {
if ( prev == 0L ) {
m_first = rb;
}
else {
lock->setCompliance( compliance );
lock->getRegularizationParameters( j )->setDamping( damping );
}
prev = rb;
position.x() += dl;
}
m_last = prev;
++ms_index;
}
static size_t ms_index;
};
{
public:
{
create( radius, thickness, numSegments, mass, compliance, damping, root );
}
{
}
private:
{
for ( size_t i = 0; i < numSegments; ++i ) {
if ( first == 0L ) {
first = rb;
}
else {
lock->setCompliance( compliance );
lock->getRegularizationParameters( j )->setDamping( damping );
}
prev = rb;
}
lock->setCompliance( compliance );
lock->getRegularizationParameters( j )->setDamping( damping );
++ms_index;
}
static size_t ms_index;
};
size_t Beam::ms_index = 1;
size_t Wheel::ms_index = 123456;
{
osg::Group*
root =
new osg::Group();
size_t numSegments = 100;
agx::ref_ptr<Beam> beam1 =
new Beam( dimension, numSegments, totalMass, compliance, root );
simulation->
add( beam1 );
attLock->setCompliance( 1E-12 );
simulation->
add( attLock );
attLock->setCompliance( 1E-12 );
simulation->
add( attLock );
compliance = 1E-9;
totalMass = 12E3;
agx::ref_ptr<Beam> beam2 =
new Beam( dimension, numSegments, totalMass, compliance, root );
simulation->
add( beam2 );
beam2->setPosition(
agx::Vec3( 0, 0.5, 5 ) );
}
{
public:
: m_h1( h1 ), m_h2( h2 ), m_motorForce( motorForce ), m_motorSpeed( motorSpeed )
{
}
bool keyboard(
int key,
unsigned int ,
float ,
float ,
bool keydown )
{
bool handled = false;
m_h1->getMotor1D()->setSpeed( m_motorSpeed * keydown );
m_h2->getMotor1D()->setSpeed(-m_motorSpeed * keydown );
m_h1->getMotor1D()->setForceRange(
agx::RangeReal( m_motorForce * keydown ) );
m_h2->getMotor1D()->setForceRange(
agx::RangeReal( m_motorForce * keydown ) );
handled = true;
}
m_h1->getMotor1D()->setSpeed(-m_motorSpeed * keydown );
m_h2->getMotor1D()->setSpeed( m_motorSpeed * keydown );
m_h1->getMotor1D()->setForceRange(
agx::RangeReal( m_motorForce * keydown ) );
m_h2->getMotor1D()->setForceRange(
agx::RangeReal( m_motorForce * keydown ) );
handled = true;
}
return handled;
}
private:
};
{
osg::Group*
root =
new osg::Group();
size_t numSegments = 500;
agx::ref_ptr<Beam> beam1 =
new Beam( dimension, numSegments, totalMass, compliance, root );
simulation->
add( beam1 );
#if 0
l->setCompliance( 1E-10 );
l->setCompliance( 1E-10 );
#else
simulation->
add(
new Tutorial58KeyboardController( att1, att2, motorForce, motorSpeed ) );
simulation->
add(
new agxOSG::TextEventListener(
"Rotate beam with LEFT/RIGHT keys",
agx::Vec2(0.1, 0.1),
agx::Vec3(1,0,0)));
#endif
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( wheel1 );
simulation->
add( wheel2 );
simulation->
add( plane );
}
{
osg::Group*
root =
new osg::Group();
body1->
add( boxGeometry1 );
simulation->
add( body1 );
simulation->
add( lock1 );
body2->
add( boxGeometry2 );
simulation->
add( body2 );
simulation->
add( lock2 );
simulation->
add(
new agxOSG::TextEventListener(
"Pick bodies with CTRL+left mouse",
agx::Vec2( 0.1, 0.1 ),
agx::Vec3( 1, 0, 0 ) ) );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( prismatic );
std::cout <<
"Initial angle: " << prismatic->
getAngle() << std::endl;
}
{
public:
:
agxSDK::StepEventListener(
agxSDK::StepEventListener::POST_STEP ), m_ec( ec ), m_sd( sd ), m_index( index )
{
update();
}
protected:
{
update();
}
private:
void update()
{
if ( m_ec == 0L || m_sd == 0L )
return;
m_sd->setText( (
int)m_index,
agx::String::format(
"Applied force by %s: %3.3f Nm", m_ec->getName().c_str(), m_ec->getCurrentForce() ) );
}
private:
osg::observer_ptr< agxOSG::SceneDecorator > m_sd;
size_t m_index;
};
{
osg::Group*
root =
new osg::Group();
simulation->
add( hinge );
frictionController->setEnable( true );
frictionController->setSpeed(
agx::Real( 0 ) );
rangeController->setEnable( true );
rangeController->setCompliance(
agx::Real( 1E-10 ) );
simulation->
add(
new ElementaryConstraintCurrentForceReader( frictionController, application->
getSceneDecorator(), 1 ) );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( cylindrical );
cylindrical->getScrew1D()->setEnable( true );
cylindrical->getScrew1D()->setLead(
agx::Real( 0.01 ) );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( universal );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( universal );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( universal );
simulation->
add(meshGeom);
}
int main( int argc, char** argv )
{
std::cout <<
"\tConstraints tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
application->addScene( buildTutorial4, '4' );
application->addScene( buildTutorial5, '5' );
application->addScene( buildTutorial6, '6' );
application->addScene( buildTutorial7, '7' );
application->addScene( buildTutorial8, '8' );
application->addScene( buildTutorial9, '9' );
application->addScene( buildTutorial10, '0' );
application->addScene( buildTutorial11, '!' );
application->addScene( buildTutorial12, '"' );
application->addScene( buildTutorial13, '#' );
application->addScene( buildTutorial14, '%' );
application->addScene( buildTutorial15, '&' );
application->addScene( buildTutorial16, '/' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
Implements a Capsule shape for geometric intersection tests.
void setPosition(const agx::Vec3 &p)
Set the position of the frame in world coordinates.
Templated version of ExecuteFilter, for objects which contain geometries and inherit from agx::Refere...
virtual void setMask(int f)
Specifies a bitmask which determines which event types that will activate this listener.
bool setUniformGravity(const agx::Vec3 &g)
Set the uniform gravity.
Abstraction of a wheel of arbitrary geometry.
agx::Angle::Type getType() const
@ ROTATIONAL
Axis is about U, V or N.
agx::Angle::Axis getAxis() const
agx::Angle * getAngle(agx::UInt index) const
Access constraint angle given index.
agx::Range1D * getRange1D()
Basic data holder class for "angle based" (secondary) constraints.
agx::Bool addSecondaryConstraint(const agx::Name &name, agx::ElementaryConstraint *secondaryConstraint)
Add secondary constraint (like motor, range and/or lock etc) given name.
agx::AttachmentPair * getAttachmentPair() const
virtual void setCompliance(agx::Real compliance, agx::Int dof)
Set the compliance of this constraint for the i:th DOF.
Elementary constraint base class with interface and global constraint functionality.
void setRotate(const agx::Quat &rotation)
Assign the final (world) rotation of this frame.
void setCenter(const Vec3 ¢er)
Sets the center of the hinge in world coordinates.
void setAxis(const Vec3 &axis)
Sets the axis of the hinge in world coordinates.
@ TRANSLATIONAL_2
Select DOF for the second translational axis.
@ ROTATIONAL_2
Select DOF corresponding to the second rotational axis.
@ TRANSLATIONAL_1
Select DOF for the first translational axis.
@ ROTATIONAL_1
Select DOF corresponding to the first rotational axis.
@ NUM_DOF
Number of DOF available for this constraint.
@ ROTATIONAL_3
Select DOF for rotation around Z-axis.
@ TRANSLATIONAL_3
Select DOF for the third translational axis.
SurfaceMaterial * getSurfaceMaterial()
A Prismatic Universal Joint has two translational dof's constrained.
@ TRANSLATIONAL_CONTROLLER_1
Select second controller of the prismatic universal joint, control angle.
A prismatic joint keeps a fixed relative orientation between the attached bodies but allows them to s...
Elementary secondary constraint to keep constraint angle within two given values.
void setRange(agx::RangeReal range)
Assign range for the constraint angle to between.
void setTransform(const agx::AffineMatrix4x4 &matrix)
Set the transform of the body.
Implementation of constraint angle ABOUT an axis.
void setRoughness(Real roughness)
Set the roughness parameter.
Elementary secondary constraint to drive something given target speed (translational or rotational).
A Universal Joint has one fixed relative point, the 3 translational dof's, and one constrained axis (...
@ ROTATIONAL_CONTROLLER_2
Select first controller of the prismatic universal joint, control angle.
@ ROTATIONAL_CONTROLLER_1
tutorial_contactForces.cpp
{
osg::Group* root = new osg::Group;
simulation->
add( ground );
{
sceneDecorator->setText( 0, "Tutorial contact forces: Normal force." );
if ( geometryContacts.empty() ) {
sceneDecorator->setText( 1, " - The box is currently not in contact with the ground." );
}
else {
auto geometryContact = geometryContacts.
front();
agx::Real expectedRestingNormalForce = geometryContact->rigidBody( 0 )->getMassProperties()->getMass() *
for ( const auto& p : geometryContact->points() )
currentNormalForce += p.getNormalForceMagnitude();
sceneDecorator->setText( 1,
agx::String::format(
" - Number of contact points: %d", geometryContact->points().size() ) );
sceneDecorator->setText( 2,
agx::String::format(
" - Contains impacts: %s", geometryContact->hasImpactingPoints() ?
"true" :
"false" ) );
sceneDecorator->setText( 3,
agx::String::format(
" - Current normal force: %.10f N", currentNormalForce ) );
sceneDecorator->setText( 4,
agx::String::format(
" - Expected resting normal force: %.10f N", expectedRestingNormalForce ) );
}
};
displayNormalForce( simulation );
}
{
osg::Group*
root =
new osg::Group;
simulation->
add( ground );
auto displayFrictionForce = [application, contactMaterial](
agxSDK::Simulation* simulation )
{
sceneDecorator->setText( 0, "Tutorial contact forces: Friction force." );
if ( geometryContacts.empty() ) {
sceneDecorator->setText( 1, " - The box is currently not in contact with the ground." );
}
else {
auto geometryContact = geometryContacts.
front();
agx::Real expectedRestingNormalForce = geometryContact->rigidBody( 0 )->getMassProperties()->getMass() *
for ( const auto& p : geometryContact->points() )
currentFrictionForce += p.getTangentialForceMagnitude();
agx::Real maxFrictionForce = contactMaterial->getFrictionCoefficient() *
expectedRestingNormalForce;
currentFrictionForce,
1.0E-4 ) ?
"true" :
"false" ) );
sceneDecorator->setText( 2,
agx::String::format(
" - Current friction force: %.5f N", currentFrictionForce ) );
sceneDecorator->setText( 3,
agx::String::format(
" - Max resting friction force: %.5f N", maxFrictionForce ) );
}
};
}
{
osg::Group*
root =
new osg::Group;
simulation->
add( ground );
contactMaterial->setAdhesion( 0.0, 5.0E-3 );
{
sceneDecorator->setText( 0, "Tutorial contact forces: Friction force." );
if ( geometryContacts.empty() ) {
sceneDecorator->setText( 1, " - The box is currently not in contact with the ground." );
}
else {
auto geometryContact = geometryContacts.
front();
sceneDecorator->setText( 1, " - Geometry contact state:" );
sceneDecorator->setText( 2,
agx::String::format(
" * Contains impacting contact points: %s", geometryContact->hasImpactingPoints() ?
"true" :
"false" ) );
sceneDecorator->setText( 3,
agx::String::format(
" * Interacting bodies: %s <-> %s", geometryContact->rigidBody( 0 )->getName().c_str(),
geometryContact->rigidBody( 1 )->getName().c_str() ) );
sceneDecorator->setText( 4,
agx::String::format(
" * Number of contact points: %d", geometryContact->points().size() ) );
for (
agx::UInt i = 0u; i < geometryContact->points().size(); ++i ) {
const auto& p = geometryContact->points()[ i ];
sceneDecorator->setText( 4 + (
int)i + 1,
agx::String::format(
" o Point %d: Is impacting [%s], Normal force [%4.2f N], Friction force [%4.2f N]",
(int)i + 1,
p.getNormalForceMagnitude(),
p.getTangentialForceMagnitude() ),
}
}
};
displayContactPointState( simulation );
}
{
osg::Group*
root =
new osg::Group;
contactMaterial->setAdhesion(0.0, 5.0E-3);
{
sceneDecorator->setText(0, "Tutorial contact forces: ContactForceReader Friction force.");
agx::Vec3 normalForce = contactForceReader->getNormalContactForces(ground, box);
agx::Vec3 frictionForce = contactForceReader->getTangentialContactForces(ground, box);
};
displayContactForces(simulation, contactForceReader, ground, box);
}
int main( int argc, char** argv )
{
std::cout <<
"\tContact forces tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene(buildTutorial3, '3');
application->addScene( buildTutorial4, '4' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
const GeometryContactPtrVector & getGeometryContacts() const
Access to the vector containing all narrow phase contacts.
static agxRender::Color MediumVioletRed()
static agxRender::Color LightGreen()
agx::Vec3 getUniformGravity() const
If the current gravity field is not a uniform one (for example PointGravityField),...
Real length() const
Length of the vector = sqrt( vec .
Namespace containing classes for handling debug rendering of collision geometries,...
tutorial_contactMeasurement.cpp
#include <iomanip>
{
osg::Group* root = new osg::Group;
{
sceneDecorator->setText( 0, "Tutorial contact measurement: Maximum impact energy." );
sceneDecorator->setText( 2,
agx::String::format(
" - Kinetic energy : %.10f J", (boxMass * boxVelocity.length2()) / 2));
};
displayMaxImpactEnergy( simulation );
return root;
}
int main(int argc, char** argv)
{
std::cout <<
"\tContact measurement tutorial\n" <<
"\t-------------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
if (application->init(argc, argv))
return application->run();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
agx::RigidBody * getRigidBody(const agx::Name &name)
Find (linear search) and return named RigidBody.
static void setRigidBodyIsEmitted(agx::RigidBody *body, bool isEmitted)
Manually override setting that specifies if a RigidBody was created from an Emitter.
tutorial_convex.cpp
#include "tutorialUtils.h"
#include <ostream>
{
}
{
osg::Group* root = new osg::Group();
createPyramid( 0.4, 0.8, vertices, indices );
convexRigidBody->
setName(
"fromAConvexMesh" );
simulation->
add( convexRigidBody );
srand(5);
for(size_t i=0; i < 30; i++)
{
}
convexRigidBody1->
setName(
"createConvexFromRandomPoints");
simulation->
add( convexRigidBody1 );
convexRigidBody2->
setName(
"CreatedFromMeshFile");
simulation->
add(convexRigidBody2);
staticRB->
add( staticBodyGeometry );
simulation->
add(staticRB);
}
{
osg::Group*
root =
new osg::Group();
std::cerr << "Creating Convex Decompositions" << std::endl;
for (size_t i = 0; i < 5; i++)
{
size_t resolution = 5+i*10;
timer.stop();
std::cout << "\tResolution " << resolution << " Time: " << timer.getTime() / 1000 << " Seconds." << std::endl;
for (auto c : result)
{
convexRigidBody->
add(geometry);
}
convexRigidBody->
setName(
"ConvexDecomposition");
simulation->
add(convexRigidBody);
}
staticRB->
add(staticBodyGeometry);
simulation->
add(staticRB);
}
namespace {
struct Settings {
int resolution = 0;
double alpha = 0;
double beta = 0;
double concavity = 0;
int numVertices = 0;
Settings(int res, double a, double b, double c, int n) :
resolution(res),
alpha(a),
beta(b),
concavity(c),
numVertices(n)
{
}
Settings(const Settings& s) :
resolution(s.resolution),
alpha(s.alpha),
beta(s.beta),
concavity(s.concavity),
numVertices(s.numVertices)
{
}
};
std::ostream&
operator <<(std::ostream& os,
const Settings& s)
{
os <<
agx::String::format(
"Resolution=%d, alpha=%f, beta=%f, concavity=%f, maxVertices=%d", s.resolution, s.alpha, s.beta, s.concavity, s.numVertices);
return os;
}
}
{
public:
{
}
double getTime() const
{
return m_time;
}
Settings getSettings() const
{
return m_settings;
}
{
if (!m_factory)
return m_factory->getConvexShapes();
}
{
parameters->resolution = m_settings.resolution;
parameters->alpha = m_settings.alpha;
parameters->beta = m_settings.beta;
parameters->concavity = m_settings.concavity;
parameters->maxNumVerticesPerConvex = m_settings.numVertices;
timer.stop();
m_time = timer.getTime() / 1000;
}
private:
const Settings& m_settings;
double m_time;
};
{
osg::Group*
root =
new osg::Group();
if (!meshReader->readFile("models/bunny.obj"))
std::vector<Settings> settings = {
Settings({5,0,0,0.1,10}),
Settings({20,0.01,0.01,0.05,1000}),
Settings({30,0.001,0.01,0.01,1000})
};
std::cerr << "Creating Convex Decompositions..." << std::endl;
for (const auto& s : settings)
{
auto cooker = new ConvexCooker(meshReader, s);
cooker->start();
}
std::for_each(cookers.
begin(), cookers.
end(), [](ConvexCooker* c) { c->join(); } );
int i = 0;
for (auto t : cookers)
{
std::cerr << t->getSettings() << ": " << t->getTime() << std::endl;
for (auto c : t->getConvexShapes())
{
convexRigidBody->
add(geometry);
}
convexRigidBody->
setName(
"ConvexDecomposition");
std::stringstream str;
str << t->getSettings() << std::ends;
simulation->
add(convexRigidBody);
i++;
}
cookers.clear();
staticRB->
add(staticBodyGeometry);
simulation->
add(staticRB);
}
int main( int argc, char** argv )
{
std::cout <<
"\tTutorial Convex\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
std::ostream & operator<<(std::ostream &o, const agx::Vec6 &v)
Class for creating convex meshes from a set of vertices and indices.
A convex class for geometric intersection tests.
Class for reading a mesh object from file and extracting vertices, normals and indices for collision ...
Basic wrapper class aroud std::thread.
virtual void run()
This method is invoked by start.
Base class providing referencing counted objects.
static Thread * registerAsAgxThread()
Register the current thread as an AGX thread.
static void unregisterAsAgxThread()
Remove agx attributes from current thread.
The Timer class permits timing execution speed with the same refinement as the built in hardware cloc...
void clear(ClearPolicy policy=SHRINK_BUFFER_AVERAGED)
Remove all elements, optionally with maintained buffer allocation.
agx::Vector< agxCollide::ConvexRef > ConvexRefVector
AGXOSG_EXPORT bool addText(const agxCollide::Geometry *geometry, osg::Group *root, const std::string &text, const agx::Vec3 &relPosition=agx::Vec3(0, 0, 0), const agx::Vec4f &color=agxRender::Color::Yellow(), agx::Real size=0.2)
Add text relative to an geometry.
AGXPHYSICS_EXPORT bool createConvexDecomposition(const agx::String &filename, agxCollide::ConvexRefVector &results, size_t elementResolutionPerAxis=50, const agx::Matrix3x3 &transformation=agx::Matrix3x3(), const agx::Vec3 &translation=agx::Vec3())
Creates a set of convex shapes that approximates the triangle mesh defined by filename using V-HACD w...
AGXPHYSICS_EXPORT agxCollide::Convex * createConvex(const agx::Vec3Vector &vertices, const agx::Matrix3x3 &transformation=agx::Matrix3x3(), const agx::Vec3 &translation=agx::Vec3())
Creates a convex shape from vertices only, building their convex hull.
tutorial_conveyorBelts.cpp
{
public:
ConveyorBelt(
const agx::Vec3Vector& points) : Geometry(), m_points(points), m_speed(0)
{
m_unitDirs.reserve(points.
size());
for (
size_t i = 0; i < points.
size() - 1; ++i) {
agx::Vec3 unitDir = m_points[i + 1] - m_points[i];
m_unitDirs.push_back(unitDir);
}
{
agx::Vec3 unitDir = m_points.front() - m_points.back();
m_unitDirs.push_back(unitDir);
}
}
}
{
if (m_points.size() > 2 && m_speed !=
agx::Real(0)) {
size_t closestIndex = 0;
for (size_t i = 0; i < m_points.size(); ++i) {
if (dist2 < closestDist2) {
closestDist2 = dist2;
closestIndex = i;
}
}
const size_t indexBefore = (closestIndex == 0) ? m_points.size() - 1 : closestIndex - 1;
const agx::Vec3& oldDir = m_unitDirs[indexBefore];
const agx::Vec3& newDir = m_unitDirs[closestIndex];
const agx::Real oldDist2 = (p - (m_points[closestIndex] - oldDir * eps)).
length2();
const agx::Real newDist2 = (p - (m_points[closestIndex] + newDir * eps)).length2();
const agx::Vec3& dir = (oldDist2 < newDist2) ? oldDir : newDir;
return v;
}
else {
}
}
{
return m_points;
}
{
return m_unitDirs;
}
{
m_speed = speed;
}
{
return m_speed;
}
private:
virtual ~ConveyorBelt()
{
}
};
ConveyorBeltRef createConveyorBelt()
{
ConveyorBeltRef conveyorBelt = new ConveyorBelt(points);
conveyorBelt->setSpeed(1);
return conveyorBelt;
}
{
return mat;
}
{
osg::Group*
root =
new osg::Group();
ConveyorBeltRef conveyorBelt = createConveyorBelt();
groundBody->
add(conveyorBelt);
simulation->
add(groundBody);
conveyorBelt->setMaterial(mat);
{
geom->setMaterial(mat);
}
{
geom->setMaterial(mat);
}
}
{
osg::Group*
root =
new osg::Group();
ConveyorBeltRef conveyorBelt = createConveyorBelt();
groundBody->
add( conveyorBelt );
simulation->
add(groundBody);
conveyorBelt->setMaterial(mat);
sphere->setPosition(0, 0, 0.5);
emitter->setGeometry(sphere);
emitter->setRate(10);
emitter->setLife(1000);
simulation->
add(emitter);
}
{
public:
CircularConveyorBelt()
{}
{
if (speed >= 0.8)
speed = 0;
direction[2] = 0;
direction.
set(direction[1], -direction[0], 0);
}
};
{
osg::Group*
root =
new osg::Group();
groundBody->
add(conveyorBelt);
simulation->
add(groundBody);
conveyorBelt->setMaterial(mat);
sphere->setPosition(0, 0, 0.5);
emitter->setGeometry(sphere);
emitter->setRate(10);
emitter->setLife(1000);
simulation->
add(emitter);
}
{
public:
CurveConveyorBelt()
{}
{
double speed = (point.
point() - localOrigin).length();
if (speed >= 1.6)
speed = 0;
direction[2] = 0;
direction.
set(direction[1], -direction[0], 0);
}
};
{
osg::Group*
root =
new osg::Group();
convGeo->setSurfaceVelocity(
agx::Vec3f(0.0, -1.0, 0.0));
simulation->
add(convBody);
convGeo->setMaterial(mat);
conveyorBelt->setSurfaceVelocity(
agx::Vec3f(1, 1, 1));
conveyorBelt->add(curveMesh);
groundBody->
add(conveyorBelt);
simulation->
add(groundBody);
conveyorBelt->setMaterial(mat);
canGeo->setMaterial(mat);
simulation->
add(canBody);
sphere->setPosition(2.2, 0.4, 0.4);
emitter->setGeometry(sphere);
emitter->setRate(10);
emitter->setLife(1000);
simulation->
add(emitter);
}
int main( int argc, char** argv )
{
"\tTutorial conveyorBelts\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(ConveyorBeltWithSurfaceVelocity, '1');
application->addScene(ConveyorBeltWithSurfaceVelocityAndParticles, '2');
application->addScene(CircularConveyorBeltWithSurfaceVelocityAndParticles, '3');
application->addScene(CurvedConveyorBeltWithSurfaceVelocityAndParticles, '4');
if (application->init( argc, argv ))
return application->run();
} catch (std::exception& e)
{
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
agx::Vec3 getPosition() const
virtual agx::Vec3f calculateSurfaceVelocity(const agxCollide::LocalContactPoint &point, size_t index) const
Implement this method to get access to different surface velocities on the geometry:s surface.
void setSurfaceVelocity(const agx::Vec3f &surfaceVelocity)
Set the velocity of this geometry's surface in the geometry's local coordinate frame.
void addStatic(const Point &point)
Add static point (sphere).
static RenderSingleton * instance()
Spawns new particles inside a given volume given to the Emitter.
virtual void setParticleMass(Real mass, bool updateExistingParticles=false)
Set particle mass.
void setMaterial(Material *material, bool updateExistingParticles=false)
Set the default particle material.
void setParticleRadius(Real radius, bool updateExistingParticles=false)
Set the default particle radius of created particles, with option of updating existing particles.
Real distance2(const Vec3T &v2) const
Squared distance to another vector.
Real length2() const
Length squared of the vector = vec .
AGXCORE_EXPORT const Real RealMax
tutorial_create_oriented_bounding_volume.cpp
void createBoundingVolume(
osg::Group *root,
{
auto boundingVolumeShape = createShape(radiusHeight[0], radiusHeight[1]);
}
{
std::cout << "Creating oriented bounding volumes from mesh shapes\n";
osg::Group* root = new osg::Group();
std::string filename = "models/bunny.obj";
double scale = 4;
filename,
ground->setPosition(0, 0, 0);
simulation->
add(mesh_geom);
simulation->
add(box_geometry);
root->addChild(axes_node);
{
};
{
};
createCylinder = [](double radius,
{
};
createCapsule = [](double radius,
{
};
createBoundingVolume(simulation, root, mesh_data,
agx::Vec3(1, 0, 0), computeOrientedCylinder, createCylinder);
createBoundingVolume(simulation, root, mesh_data,
agx::Vec3(3, 0, 0), computeOrientedCapsule, createCapsule);
}
int main(int argc, char** argv)
{
<< "\tTutorial oriented bounding volumes\n" << "\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
if (application->init(argc, argv)) {
return application->run();
}
}
catch (std::exception& e) {
std::cerr << "\nMain caught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
Class for data sharing only to be used by Mesh, its internal classes and children.
agx::Vec3Vector & getVertices()
agx::UInt32Vector & getIndices()
const CollisionMeshData * getMeshData() const
Triangle mesh for geometric intersection tests.
@ NO_WARNINGS
Deactivates warnings about flaws in the mesh topology which will lead to bad simulation results.
void setEnableShadows(bool flag)
Enable/disable shadows for the sub-graph.
void setShadowMethod(ShadowMethod m)
Specify which method is used for generating shadows,.
static agxRender::Color White()
AGXOSG_EXPORT bool setEnableWireFrameMode(osg::Node *node, bool flag)
AGXOSG_EXPORT bool forceWireFrameModeOn(osg::Node *node)
AGXPHYSICS_EXPORT bool computeOrientedCylinder(const agx::Vec3Vector &vertices, agx::Vec2 &radiusHeight, agx::AffineMatrix4x4 &localRotation, agxUtil::ShapeOrientation::Orientation orientation=agxUtil::ShapeOrientation::MINIMIZE_VOLUME)
Compute the radius, height and rotation of a cylinder that encapsulates a specified box specified by ...
AGXPHYSICS_EXPORT bool computeOrientedBox(const agx::Vec3Vector &vertices, agx::Vec3 &halfExtents, agx::AffineMatrix4x4 &transform)
Computes an oriented bounding box around the specified vertices.
AGXPHYSICS_EXPORT bool computeOrientedCapsule(const agx::Vec3Vector &vertices, agx::Vec2 &radiusHeight, agx::AffineMatrix4x4 &localRotation, agxUtil::ShapeOrientation::Orientation orientation=agxUtil::ShapeOrientation::MINIMIZE_VOLUME)
Compute the radius, height and rotation of a capsule that encapsulates a specified box specified by h...
Vec2T< Real > Vec2
The object holding 2 dimensional vectors and providing basic arithmetic.
AffineMatrix4x4T< Real > AffineMatrix4x4
@ X_AXIS
Choose the principal axis along the X-axis of the bounding box.
@ MINIMIZE_VOLUME
Automatically choose the direction which leads to the smallest volume.
@ Z_AXIS
Choose the principal axis along the Z-axis of the bounding box.
tutorial_customConstraints.cpp
#include "tutorialUtils.h"
{
public:
{
if ( rb1 == 0L || rb1 == rb2 )
return;
const Vec3 worldHingeAxis( 0, 1, 0 );
Constraint::calculateFramesFromWorld( relAnchorPosition, worldHingeAxis, rb1, frame1, rb2, frame2 );
bool constructSuccess = construct( rb1, frame1, rb2, frame2, createFunc );
if ( !constructSuccess ) {
}
}
{
return createElementaryConstraints() && createSecondaryConstraints();
}
Bool createElementaryConstraints()
{
std::cout << "Constructing elementary primary constraints." << std::endl;
addElementaryConstraint(
"D1_VN",
new Dot1(
Dot1Data( ap, Attachment::V, Attachment::N ) ) );
addElementaryConstraint(
"D1_UN",
new Dot1(
Dot1Data( ap, Attachment::U, Attachment::N ) ) );
return true;
}
Bool createSecondaryConstraints()
{
return true;
}
{
if ( !isValid() )
return;
if ( line1 != 0L )
if ( line2 != 0L )
if ( line1 != 0L )
if ( line2 != 0L )
att1->
setTransform( AffineMatrix4x4::translate( a1->
get( Attachment::ANCHOR_POS ) ) );
att2->
setTransform( AffineMatrix4x4::translate( a2->
get( Attachment::ANCHOR_POS ) ) );
a1->
get( Attachment::ANCHOR_POS ) + a1->
get( Attachment::N ) );
}
virtual ~MyHingeImplementation() {}
};
{
return dynamic_cast< MyHingeImplementation* >( implementation )->create();
}
{
public:
{
m_implementation = new MyHingeImplementation( rb1, rb2 );
}
virtual int getNumDOF() const override { return 5; }
protected:
{
if ( m_implementation != 0L )
static_cast< const MyHingeImplementation* >( m_implementation )->render( mgr, scale );
}
virtual ~MyHinge()
{
if ( m_implementation != 0L )
{
delete m_implementation;
m_implementation = 0L;
}
}
};
{
osg::Group *
root =
new osg::Group;
simulation->
add( ground );
simulation->
add( myHinge );
}
{
public:
MyElementaryDistanceData(
const Vec3& point1,
const Vec3& point2 )
: p1( point1 ), p2( point2 ) {}
MyElementaryDistanceData( const MyElementaryDistanceData& other )
: p1( other.p1 ), p2( other.p2 ) {}
virtual ~MyElementaryDistanceData() {}
private:
MyElementaryDistanceData& operator=(const MyElementaryDistanceData&) {return *this;}
};
{
public:
MyElementaryDistance( const MyElementaryDistanceData& data )
{
m_restLength = ( m_data.p1 - m_data.p2 ).length();
m_currentLength = m_restLength;
}
virtual ~MyElementaryDistance() {}
{
Vec3 k = m_data.p1 - m_data.p2;
if ( writeState == GWriteState::NORMAL ) {
}
else if ( writeState == GWriteState::IGNORE_FIRST ) {
}
else {
agxAssert( writeState == GWriteState::WRITE_SECOND_FIRST );
}
return numBlocks;
}
{
g[ row ] = m_currentLength - m_restLength;
return 1;
}
protected:
};
{
public:
: m_distance( 0L )
{
if ( rb1 == 0L || rb2 == 0L )
return;
createData( rb1, rb2 );
m_distance = new MyElementaryDistance( MyElementaryDistanceData( m_p1, m_p2 ) );
m_ec.push_back( m_distance );
}
virtual ~MyDistanceJointImplementation() {}
virtual void prepare() override
{
ConstraintImplementation::prepare();
m_p1 = m_bodies.front()->getPosition();
m_p2 = m_bodies.back()->getPosition();
}
{
if ( !isValid() )
return;
}
protected:
MyDistanceJointImplementation() : m_distance( 0L ) {}
{
m_bodies.push_back( rb1 );
m_bodies.push_back( rb2 );
prepare();
}
protected:
MyElementaryDistance* m_distance;
};
{
public:
{
m_implementation = new MyDistanceJointImplementation( rb1, rb2 );
}
protected:
MyDistanceJoint() {}
virtual ~MyDistanceJoint()
{
if ( m_implementation )
{
delete m_implementation;
m_implementation = 0;
}
}
virtual int getNumDOF() const override { return 1; }
{
static_cast< const MyDistanceJointImplementation* >( m_implementation )->render( mgr, scale );
}
};
{
osg::Group *
root =
new osg::Group;
simulation->
add( ground );
simulation->
add( distanceJoint );
}
class MyAdvancedElementaryDistance : public MyElementaryDistance
{
public:
MyAdvancedElementaryDistance(
const Vec3& p1,
const Vec3& p2 )
: MyElementaryDistance( MyElementaryDistanceData( p1, p2 ) ), m_desiredSpeed( 0 ) {}
virtual ~MyAdvancedElementaryDistance() {}
{
if ( getRegularizationParameters()->isHolonomic() )
return MyElementaryDistance::getViolation( g, row );
else
g[ row ] = 0;
return 1;
}
virtual UInt getVelocity(
Real* v,
UInt row )
const override
{
if ( getRegularizationParameters()->isHolonomic() )
v[ row ] = 0;
else
v[ row ] = m_desiredSpeed;
return 1;
}
{
if ( !getRegularizationParameters()->isHolonomic() && !getForceRange().isInfinite() )
bounds[ row + 0 ] = getForceRange() * dt;
else
}
void setDesiredSpeed(
Real desiredSpeed )
{
m_desiredSpeed = desiredSpeed;
}
Real getDesiredSpeed()
const {
return m_desiredSpeed; }
void updateRestLength()
{
m_restLength = (m_data.p1 - m_data.p2).length();
}
protected:
};
class MyAdvancedDistanceJointImplementation : public MyDistanceJointImplementation
{
public:
: m_advancedDistance( 0L )
{
if ( rb1 == 0L || rb2 == 0L )
return;
createData( rb1, rb2 );
m_distance = m_advancedDistance = new MyAdvancedElementaryDistance( m_p1, m_p2 );
m_ec.push_back( m_distance );
}
void setMotorEnable( bool enable )
{
if ( enable ) {
m_advancedDistance->getRegularizationParameters( 0 )->setType( RegularizationParameters::NONHOLONOMIC );
}
else {
m_advancedDistance->getRegularizationParameters( 0 )->setType( RegularizationParameters::HOLONOMIC );
m_advancedDistance->updateRestLength();
}
}
void setDesiredSpeed(
Real desiredSpeed )
{
m_advancedDistance->setDesiredSpeed( desiredSpeed );
}
void setForceRange(
Real lower,
Real upper )
{
m_advancedDistance->setForceRange( lower, upper );
}
protected:
MyAdvancedElementaryDistance* m_advancedDistance;
};
class MyAdvancedDistanceJoint : public MyDistanceJoint
{
public:
{
m_implementation = new MyAdvancedDistanceJointImplementation( rb1, rb2 );
}
void setMotorEnable( bool enable )
{
static_cast< MyAdvancedDistanceJointImplementation* >( m_implementation )->setMotorEnable( enable );
}
void setDesiredSpeed(
Real desiredSpeed )
{
static_cast< MyAdvancedDistanceJointImplementation* >( m_implementation )->setDesiredSpeed( desiredSpeed );
}
void setForceRange(
Real lower,
Real upper )
{
static_cast< MyAdvancedDistanceJointImplementation* >( m_implementation )->setForceRange( lower, upper );
}
virtual int getNumDOF() const override { return 1; }
protected:
virtual ~MyAdvancedDistanceJoint()
{
if (m_implementation)
{
delete m_implementation;
m_implementation = 0;
}
}
};
{
public:
MyDistanceJointKeyboardController( MyAdvancedDistanceJoint* distanceJoint,
Real speed,
int keyIn,
int keyOut )
: m_distanceJoint( distanceJoint ), m_speed( speed ), m_keyIn( keyIn ), m_keyOut( keyOut )
{
}
bool keyboard(
int key,
unsigned int ,
float ,
float ,
bool keydown )
{
bool handled = key == m_keyIn || key == m_keyOut;
if ( !handled || !m_distanceJoint.isValid() )
return false;
m_distanceJoint->setMotorEnable( keydown );
m_distanceJoint->setDesiredSpeed( (key == m_keyOut) * m_speed - (key == m_keyIn) * m_speed );
return true;
}
private:
int m_keyIn;
int m_keyOut;
};
{
osg::Group *
root =
new osg::Group;
simulation->
add( ground );
simulation->
add( distanceJoint );
distanceJoint->setForceRange(
Real( -1E5 ),
Real( 1E5 ) );
LockController* lock = LockController::safeCast( hinge->getSecondaryConstraintGivenName(
"LR" ) );
if ( lock != 0L )
simulation->
add( hinge );
simulation->
add(
new MyDistanceJointKeyboardController( distanceJoint, speed, keyIn, keyOut ) );
}
{
return true;
}
{
public:
{
Constraint::calculateFramesFromBody( rb1LocalPosition,
rb1LocalAxis,
rb1,
f1,
rb2,
f2 );
auto success = construct( rb1,
f1,
rb2,
f2,
createMyLock );
if ( !success )
for (
size_t i = 0u; i <
sizeof( m_offDiagonalParameters ) /
sizeof(
Real ); ++i )
m_offDiagonalParameters[ i ] = 0.0;
setSupportsComplianceMatrix( true );
}
{
return m_offDiagonalParameters[ calculateIndexAndValidate( row, col ) ];
}
void setOffDiagonalParameter(
UInt row,
UInt col,
Real value )
{
if ( value < 0.0 )
m_offDiagonalParameters[ calculateIndexAndValidate( row, col ) ] = value;
}
public:
{
for (
UInt32 row = 1u; row < 6u; ++row ) {
for (
UInt32 col = 0u; col < row && col < 5u; ++col ) {
matrix[ row ][ col ] = -m_offDiagonalParameters[ calculateIndex( row, col ) ];
}
}
}
private:
template<typename T>
T calculateIndex( T row, T col ) const
{
return (T)( row * ( row - 1 ) / 2 + col );
}
template<typename T>
T calculateIndexAndValidate( T row, T col ) const
{
if ( row >= 6 || col >= 6 )
if ( row == col )
if ( col > row )
return calculateIndex( row, col );
}
private:
Real m_offDiagonalParameters[ (6 * 6 - 6) / 2 ];
};
{
public:
MyBeam(
Vec3 halfExtents,
UInt numSegments )
{
const auto segmentHalfExtents =
Vec3( halfExtents.
x() /
static_cast<agx::Real>(numSegments),
auto position =
Vec3( segmentHalfExtents.x(), 0, 0 );
for (
UInt i = 0u; i < numSegments; ++i ) {
position.x() += 2.0 * segmentHalfExtents.x();
RigidBody* prevSegment = m_segments.empty() ?
nullptr :
m_segments.back();
if ( prevSegment == nullptr ) {
m_worldLock =
new LockJoint( segment,
Vec3( -segmentHalfExtents.x(), 0, 0 ) );
}
else {
MyLockJointImplementation* lock = new MyLockJointImplementation( prevSegment,
Vec3( segmentHalfExtents.x(), 0, 0 ),
Vec3::X_AXIS(),
segment );
getRep()->getSubConstraints().push_back( lock );
}
m_segments.push_back( segment );
}
}
{
for (
UInt i = 0u; i < m_segments.size(); ++i )
root ),
}
void setComplianceMatrixValue(
Real compliance,
int dof = -1 )
{
compliance = std::max( compliance, 0.0 );
visit( [dof, compliance]( MyLockJointImplementation* lock )
{
lock->userAPIsetCompliance( compliance, dof );
} );
}
void setComplianceMatrixValue(
UInt row,
UInt col,
Real value )
{
if ( row == col ) {
setComplianceMatrixValue( value, (int)row );
return;
}
visit( [row, col, value]( MyLockJointImplementation* lock )
{
lock->setOffDiagonalParameter( row, col, value );
} );
}
public:
virtual void addNotification() override
{
getRep()->getSimulationProxy()->add( m_worldLock );
for ( auto segment : m_segments )
getRep()->getSimulationProxy()->add( segment );
}
virtual void removeNotification() override
{
getRep()->getSimulationProxy()->remove( m_worldLock );
for ( auto rb : m_segments )
getRep()->getSimulationProxy()->remove( rb );
}
{
visit( [mgr, scale]( MyLockJointImplementation* lock )
{
} );
}
virtual int getNumDOF() const override
{
return 0;
}
protected:
virtual ~MyBeam()
{
while ( !getRep()->getSubConstraints().empty() ) {
delete getRep()->getSubConstraints().back();
getRep()->getSubConstraints().pop_back();
}
delete getRep();
setRep( nullptr );
}
private:
void visit( std::function<void( MyLockJointImplementation* )> visitor ) const
{
for (
UInt i = 0u; i < getRep()->getSubConstraints().size(); ++i )
visitor( static_cast<MyLockJointImplementation*>( getRep()->getSubConstraints()[ i ] ) );
}
private:
};
{
osg::Group*
root =
new osg::Group;
MyBeamRef beam =
new MyBeam(
Vec3( 3, 0.25, 0.25 ), 10 );
beam->createVisual( root );
beam->setComplianceMatrixValue( 1.0E-6, LockJoint::ROTATIONAL_1 );
beam->setComplianceMatrixValue( 1.0E-6, LockJoint::ROTATIONAL_2 );
beam->setComplianceMatrixValue( 5.0E-6, LockJoint::ROTATIONAL_3 );
beam->setComplianceMatrixValue( (
UInt)LockJoint::ROTATIONAL_3,
(
UInt)LockJoint::ROTATIONAL_2,
1.0E-3 );
}
int main( int argc, char** argv )
{
std::cout <<
"\tTutorial custom constraints\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorialMyHinge1, '1' );
application->addScene( buildTutorialMyDistanceJoint1, '2' );
application->addScene( buildTutorialMyDistanceJoint2, '3' );
application->addScene( buildTutorialMyLockJointComplianceMatrix, '4' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
Class for managing the rendering of geometries, shapes, rigid bodies, constraints etc.
LineProxy * acquireLine(const agx::Vec3 &p1, const agx::Vec3 &p2)
Create and return a new proxy Line from the Proxy factory.
SphereProxy * acquireSphere(float radius)
Create and return a new proxy sphere from the Proxy factory.
virtual agx::Vec3 getColor() const
virtual void setTransform(const agx::AffineMatrix4x4 &transform)
Set the transform of this RenderProxy, if overridden, set the m_transform and call onUpdate(TRANSFORM...
Constraint attachment pair class.
const agx::Vec3 & get(agx::Attachment::Transformed entry) const
virtual bool addElementaryConstraint(const agx::String &name, agx::ElementaryConstraint *elementaryConstraint)
Add elementary constraint (like Spherical, Dot1, Dot2 etc) given name.
The base class for a constraint.
Data holder class for elementary constraint Dot1.
Dot1 elementary constraint: Two orthogonal vectors.
Class to hold data for elementary constraints.
virtual agx::AttachmentPair * getAttachmentPair() const override
void set(const T &v1, const T &v2)
Assignment.
Interface to the lower triangle of the compliance matrix block in the system matrix.
Elementary secondary constraint to keep constraint angle at a given target position.
Data holder class for elementary constraint QuatLock.
Locks the three rotational degrees of freedom (relative coordinates).
Constraint attachment class for agx::RigidBody.
Spherical elementary constraint: Two points in world coincide.
void swap(agx::Name &lhs, agx::Name &rhs)
tutorial_customMergeSplitAlgorithm.cpp
namespace tutorial1
{
{
public:
BoxContactMergeSplitAlgorithm();
protected:
virtual ~BoxContactMergeSplitAlgorithm();
};
BoxContactMergeSplitAlgorithm::BoxContactMergeSplitAlgorithm()
: MergeSplitAlgorithm()
{
setEnableSerialization( false );
}
BoxContactMergeSplitAlgorithm::~BoxContactMergeSplitAlgorithm()
{
}
{
const auto& geometryContacts = getMergeSplitHandler()->getSimulation()->getSpace()->getGeometryContacts();
for ( const auto geometryContact : geometryContacts ) {
if ( !geometryContact->isValid() || !geometryContact->enabled() )
continue;
agxSDK::MergedState mergedState( geometryContact->geometry( 0 ), geometryContact->geometry( 1 ), getMergeSplitHandler() );
continue;
actions.
split( mergedState );
}
}
{
continue;
const auto mergedState = postSolveData.
createMergedState( i, *getMergeSplitHandler() );
continue;
if ( !geometryContact.body1() || !geometryContact.body2() )
continue;
if ( geometryContact.points().size() != 4 )
continue;
{
};
for ( auto contactPoint : geometryContact.points() ) {
if ( !contactPoint.enabled() )
continue;
maxSpeedInContact = std::max(
agx::absolute( calculateSpeed( contactPoint.point(), contactPoint.normal() ) ), maxSpeedInContact );
maxSpeedInContact = std::max(
agx::absolute( calculateSpeed( contactPoint.point(), contactPoint.tangentU() ) ), maxSpeedInContact );
maxSpeedInContact = std::max(
agx::absolute( calculateSpeed( contactPoint.point(), contactPoint.tangentV() ) ), maxSpeedInContact );
}
if ( maxSpeedInContact < 0.3 )
}
}
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( ground );
srand(5);
{
return;
}, simulation, firstSpawnTime );
for ( auto algorithm : algorithms )
}
namespace tutorial2
{
{
public:
InclinationTestMergeSplitAlgorithm();
protected:
virtual ~InclinationTestMergeSplitAlgorithm();
};
InclinationTestMergeSplitAlgorithm::InclinationTestMergeSplitAlgorithm()
: MergeSplitAlgorithm()
{
setEnableSerialization( false );
}
InclinationTestMergeSplitAlgorithm::~InclinationTestMergeSplitAlgorithm()
{
}
{
const auto& constraints = getMergeSplitHandler()->getSimulation()->getConstraints();
for ( const auto& constraint : constraints ) {
continue;
if ( mergedBody == nullptr )
return;
{
agxAssert( edge->isTagged( agx::MergedBody::EdgeInteraction::CONTACT ) );
};
mergedBody->
traverse( ground, edgeVisitor );
for ( auto edge : edges ) {
agx::RigidBody* otherRb = edge->getRigidBody1() == ground ? edge->getRigidBody2() : edge->getRigidBody1();
if ( otherRb == nullptr || !edge->isTagged( agx::MergedBody::EdgeInteraction::CONTACT ) )
continue;
agxSDK::MergedState state( edge->getRigidBody1(), edge->getRigidBody2(), getMergeSplitHandler() );
const agx::Vec3 gravity = getMergeSplitHandler()->getSimulation()->getGravityField()->calculateGravity( otherRb->
getPosition() );
getMergeSplitHandler() );
otherRb,
ground,
gravityForce,
gravity,
*getMergeSplitHandler() );
if ( result.shouldSplit )
actions.
split( state, edge );
}
}
}
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( ground );
simulation->
add( groundHinge );
{
};
agx::Vec3 boxPosition( 10, -0.5 * 2.0 * numBoxes + 1.0, 1.5 );
createMaterial( box, i );
{
else
}, simulation, box, node );
}
{
}, simulation );
}
int main( int argc, char** argv )
{
"\tTutorial custom merge-split algorithms\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
void setFontSize(const float &fontSize)
Sets the font Size.
static agxRender::Color LightBlue()
Object containing merge-split actions to be processed by the agxSDK::MergeSplitHandler.
agx::Bool merge(const agxSDK::MergedState &mergedState, agx::MergedBody::EdgeInteraction *edge)
Merge given merged state and an edge.
agx::Bool split(const agxSDK::MergedState &mergedState)
Split given allowed actions in the merged state.
Stateless base class for merge and split of interactions.
static agxSDK::MergeSplitProperties * getOrCreateProperties(agx::RigidBody *rb)
Creates (or returns already present) merge-split parameters given a rigid body.
void setEnable(agx::Bool enable)
Enable/disable merge split.
agx::Bool remove(agxSDK::MergeSplitAlgorithm *mergeSplitAlgorithm)
Remove split algorithm.
MergeSplitAlgorithmContainer getAlgorithms() const
agx::Bool add(agxSDK::MergeSplitAlgorithm *mergeSplitAlgorithm)
Add new merge-split algorithm.
void setEnableMerge(agx::Bool enable)
Enable/disable merge for this object.
void setEnableMergeSplit(agx::Bool enable)
Enable/disable both merge and split for the object.
Object holding merged bodies and state given a pair of rigid bodies.
agxSDK::MergeSplitHandler * getMergeSplitHandler() const
Merge split handler is an object that enables performance boosts by merging rigid bodies together whi...
static void preCollide(FuncT callback, agxSDK::Simulation *simulation, Args &&... args)
Register a pre-collide step event callback.
agx::RigidBody * getBodyAt(agx::UInt i)
agx::UInt getNumBodies() const
returns the number of bodies involved
void setCompliance(agx::Real compliance, int row)
Assign compliance for a given row, or all rows if row = -1 (default).
Structure holding several "normal" rigid bodies.
std::function< void(EdgeInteraction *) > EdgeInteractionVisitor
static const agx::MergedBody * getActive(const agx::RigidBody *rb)
This method returns an agx::MergedBody that is active and in a simulation.
void traverse(RigidBodyVisitor visitor, agx::Bool includeDisabled=false) const
Traverse the internal interaction graph and receive callback for all rigid bodies (unique).
EdgeInteraction(agx::RigidBody *rb1, agx::RigidBody *rb2, InteractionTag tag, agx::Bool valid=true)
Construct given two rigid bodies and a valid flag.
GeometryContactEdgeInteraction()
Default constructor used by serialization.
const char * c_str() const
agx::Vec3 getAngularVelocity() const
Angular velocity in world coordinate frame.
agx::Vec3 getCmPosition() const
agx::Real getSpeed() const
AGXPHYSICS_EXPORT ExternalForceSplitResult shouldSplitGivenExternalForce(agxSDK::MergedBodySolverData::ForceFrame edgeStrength, const agx::RigidBody *rb, const agx::RigidBody *otherRb, const agx::Vec3 &externalForces, const agx::Vec3 &externalTorques, const agx::Vec3 &gravity, const agxSDK::MergeSplitHandler &handler)
Checks if two bodies should split given edge strength force frame and external forces.
AGXPHYSICS_EXPORT agxSDK::MergedBodySolverData::ForceFrame calculateContactForceFrame(const agx::RigidBody *refRb, const agx::MergedBody::GeometryContactEdgeInteraction *contactEdge, const MergeSplitHandler *handler=nullptr)
Calculates force frame (edge strength) given reference rigid body, contact edge and optionally an mer...
agx::Real calculateImpactSpeed(const agx::Vec3 &point, const agx::Vec3f &normal, const agx::Vec3 &cmPos, const agx::Vec3 &linVel, const agx::Vec3 &angVel)
Calculates impact speed of one body given contact point and normal.
T absolute(T v)
return the absolute value.
static constexpr Real DEG_TO_RAD
agx::GraphNode::Type getType(agx::UInt index) const
agx::Physics::GraphNodeData & graphNode
agx::Physics::GeometryContactPtr asGeometryContact(agx::UInt index) const
MergedState createMergedState(agx::UInt index, const agxSDK::MergeSplitHandler &handler) const
tutorial_driveTrain.cpp
#if AGX_USE_WEBPLOT()
#endif
#include <chrono>
#include <thread>
namespace
{
{
public:
: m_shaft( shaft )
{
}
{
if ( m_shaft == nullptr )
{
return result;
}
result.
value = m_shaft->getRPM( );
return result;
}
private:
};
{
public:
: m_connector(connector)
{
}
{
if (m_connector == nullptr)
{
return result;
}
if (m_connector->getConstraint()->getEnable())
result.
value = m_connector->getConstraint()->getCurrentForce();
else
result.
value = m_connector->getOutputConnections().front()->getDimension()->getOutputLoad();
return result;
}
private:
};
{
public:
: m_rotationalActuator(rotationalActuator)
{
}
{
if (m_rotationalActuator == nullptr)
{
return result;
}
return result;
}
private:
};
{
public:
: m_engine( engine )
{
}
{
if ( m_engine == nullptr )
{
return result;
}
return result;
}
private:
};
{
public:
: m_engine(engine)
{
}
{
if (m_engine == nullptr)
{
return result;
}
result.
value = m_engine->getPowerGenerator()->getPowerTimeIntegralLookupTable()->lookupCurrent() * m_engine->getThrottle();
return result;
}
private:
};
}
void runTutorial1(bool )
{
std::cout << "\nTutorial 1: Demonstrating basic drive train component creation and assembly." << std::endl;
simulation->add(powerLine);
shaft1->connect(gearA);
gearA->connect(shaft2);
powerLine->add(shaft1);
powerLine->add(shaft3);
shaft1->getRotationalDimension()->setGradient(initialAngularVelocity);
shaft3->getRotationalDimension()->setGradient(initialAngularVelocity);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(0.5));
std::cout << "Angular velocities shaft 1, 2, 3 & 4:" << std::endl;
std::cout << " Shaft1 : " << shaft1->getRotationalDimension()->getGradient() << " rad/s." << std::endl;
std::cout << " Shaft2 : " << shaft2->getRotationalDimension()->getGradient() << " rad/s." << std::endl;
std::cout << " Shaft3 : " << shaft3->getRotationalDimension()->getGradient() << " rad/s." << std::endl;
std::cout << " Shaft4 : " << shaft4->getRotationalDimension()->getGradient() << " rad/s." << std::endl;
}
void runTutorial2(bool showPlots)
{
std::cout << "\nTutorial 2: Demonstrating creation and configuration of an Engine." << std::endl;
simulation->add(powerLine);
engine->setRPMTorqueTable(rpm_torque);
engine->getPowerGenerator()->getPowerTimeIntegralLookupTable()->setDefaultValue(defaultTorque);
engine->ignition(true);
heavilyDampedShaft->getRotationalDimension()->setVelocityDamping(
agx::Real(1000.0) );
heavilyDampedShaft->setInertia(
agx::Real( 100 ) );
engine->connect(gear100);
gear100->connect(heavilyDampedShaft);
powerLine->add(engine);
if (showPlots) {
rpmSeries->setUnit("RPM");
torqueSeries->setUnit("Nm");
plotWindow->
add(rpmCurve);
plotWindow->
add(gearTorqueCurve);
#if AGX_USE_WEBPLOT()
plotSystem->
add(
new agxPlot::WebPlot(
true));
#endif
}
std::cout << " Engine RPM after 0.5 seconds : " << engine->getRPM( ) << " RPM." << std::endl;
std::cout << " Gear torque exposure after 0.5 seconds : " << gear100->getConstraint()->getCurrentForce() << " RPM." << std::endl;
std::cout << " Engine RPM after 5.0 seconds : " << engine->getRPM( ) << " RPM." << std::endl;
std::cout << " Gear torque exposure after 5.0 seconds : " << gear100->getConstraint( )->getCurrentForce( ) << " RPM." << std::endl;
std::cout << " Engine RPM after 10.0 seconds : " << engine->getRPM( ) << " RPM." << std::endl;
std::cout << " Gear torque exposure after 10.0 seconds : " << gear100->getConstraint( )->getCurrentForce( ) << " RPM." << std::endl;
std::cout << "See web plot or 'EngineDrivingHeavyShaft.dat' for full picture of engine RPM and gear torque" << std::endl;
}
void runTutorial3(bool showPlots)
{
std::cout << "\nTutorial 3: Demonstrating creation and configuration of an RotationalActuator." << std::endl;
simulation->add(powerLine);
engine->getPowerGenerator( )->getPowerTimeIntegralLookupTable( )->setDefaultValue( defaultTorque );
engine->ignition(true);
simulation->add(ground);
simulation->add(propeller);
simulation->add(hinge);
simulation->setUniformGravity(
agx::Vec3(0, 0, 0));
engine->connect(gear);
gear->connect(rotationalActuator);
powerLine->add(engine);
if (showPlots) {
rpmSeries->setUnit("RPM");
torqueSeries->setUnit("Nm");
relRpmSeries->setUnit("RPM");
plotWindow->
add(rpmCurve);
plotWindow->
add(gearTorqueCurve);
plotWindow->
add(relRpmCurve);
#if AGX_USE_WEBPLOT()
plotSystem->
add(
new agxPlot::WebPlot(
true));
#endif
}
std::cout << " Engine RPM after 0.5 seconds : " << engine->getRPM() << " RPM." << std::endl;
std::cout << " Gear torque exposure after 0.5 seconds : " << gear->getConstraint()->getCurrentForce() << " Nm." << std::endl;
std::cout << " Engine RPM after 5 seconds : " << engine->getRPM() << " RPM." << std::endl;
std::cout << " Gear torque exposure after 5 seconds : " << gear->getConstraint()->getCurrentForce() << " Nm." << std::endl;
std::cout << " Engine RPM after 10 seconds : " << engine->getRPM() << " RPM." << std::endl;
std::cout << " Gear torque exposure after 10 seconds : " << gear->getConstraint()->getCurrentForce() << " Nm." << std::endl;
std::cout << "See web plot or 'EngineDrivingRotationalActuator.dat' for full picture of RPM and torque" << std::endl;
}
namespace
{
{
engine->setRPMTorqueTable(rpm_torque);
engine->ignition(true);
return engine;
}
{
simulation->
add(propeller);
return rotationalActuator;
}
}
void runTutorial4(bool showPlots)
{
std::cout << "\nTutorial 4: Demonstrating RpmController for Engine." << std::endl;
simulation->add( powerLine );
engine->connect(gear);
gear->connect(rotationalActuator);
simulation->setUniformGravity(
agx::Vec3( 0, 0, 0 ) );
powerLine->add( engine );
engine->setThrottleCalculator( rpmController );
if (showPlots) {
rpmSeries->setUnit( "RPM" );
torqueSeries->setUnit( "Nm" );
throttleSeries->setUnit( "%" );
plotWindow->
add( rpmCurve );
plotWindow->
add( gearTorqueCurve );
plotWindow->
add( throttleCurve );
#if AGX_USE_WEBPLOT()
plotSystem->
add(
new agxPlot::WebPlot(
true));
#endif
}
std::cout << " Engine RPM after 0.5 seconds : " << engine->getRPM( ) << " RPM." << std::endl;
std::cout << " Gear torque exposure after 0.5 seconds : " << gear->getConstraint( )->getCurrentForce( ) << " Nm." << std::endl;
std::cout <<
" Engine throttle 0.5 seconds : " << engine->getThrottle()*
agx::Real(100) <<
"%" << std::endl;
std::cout << " Engine RPM after 5 seconds : " << engine->getRPM( ) << " RPM." << std::endl;
std::cout << " Gear torque exposure after 5 seconds : " << gear->getConstraint( )->getCurrentForce( ) << " Nm." << std::endl;
std::cout <<
" Engine throttle 5 seconds : " << engine->getThrottle( )*
agx::Real( 100 ) <<
"%" << std::endl;
std::cout << " Engine RPM after 10 seconds : " << engine->getRPM( ) << " RPM." << std::endl;
std::cout << " Gear torque exposure after 10 seconds : " << gear->getConstraint( )->getCurrentForce( ) << " Nm." << std::endl;
std::cout <<
" Engine throttle 10 seconds : " << engine->getThrottle( )*
agx::Real( 100 ) <<
"%" << std::endl;
std::cout << " Engine RPM after 20 seconds : " << engine->getRPM() << " RPM." << std::endl;
std::cout << " Gear torque exposure after 20 seconds : " << gear->getConstraint()->getCurrentForce() << " Nm." << std::endl;
std::cout <<
" Engine throttle 20 seconds : " << engine->getThrottle()*
agx::Real(100) <<
"%" << std::endl;
std::cout << "See web plot or 'EngineThrottle.dat' for full picture of RPM, torque and engine throttle." << std::endl;
}
namespace
{
{
}
}
void runTutorial5(bool showPlots)
{
std::cout << "\nTutorial 5: Demonstrating TorqueConverter functionality." << std::endl;
simulation->add(powerLine);
simulation->setUniformGravity(
agx::Vec3(0, 0, 0));
createRpmControllerAndInternalRotationalActuatorResistance(engine, rotationalActuator, constantFrictionForce);
torqueConverter->setEfficiencyTable(velRatio_efficiency);
engine->connect(torqueConverter);
torqueConverter->connect(rotationalActuator);
powerLine->add(engine);
torqueConverter->enableLockUp(true);
torqueConverter->enableLockUp(false);
if (showPlots) {
rpmSeries->setUnit("RPM");
torqueSeries->setUnit("Nm");
relRpmSeries->setUnit("RPM");
engineTorqueSeries->setUnit("Nm");
plotWindow->
add(rpmCurve);
plotWindow->
add(gearTorqueCurve);
plotWindow->
add(relRpmCurve);
plotWindow->
add(engineTorqueCurve);
#if AGX_USE_WEBPLOT()
plotSystem->
add(
new agxPlot::WebPlot(
true));
#endif
}
std::cout << " Engine RPM after 0.5 seconds : " << engine->getRPM() << " RPM." << std::endl;
std::cout << " Converter torque after 0.5 seconds : " << torqueConverter->getTurbineTorque() << " Nm." << std::endl;
std::cout << " Engine RPM after 2 seconds : " << engine->getRPM() << " RPM." << std::endl;
std::cout << " Converter torque after 2 seconds : " << torqueConverter-> getTurbineTorque() << " Nm." << std::endl;
std::cout << " Engine RPM after 4 seconds : " << engine->getRPM() << " RPM." << std::endl;
std::cout << " Converter torque after 4 seconds : " << torqueConverter->getTurbineTorque() << " Nm." << std::endl;
std::cout << " Engine RPM after 6 seconds : " << engine->getRPM() << " RPM." << std::endl;
std::cout << " Converter torque after 6 seconds : " << torqueConverter->getTurbineTorque() << " Nm." << std::endl;
std::cout << "See web plot or 'TorqueConverter.dat' for full picture of RPM and torque." << std::endl;
}
void runTutorial6(bool showPlots)
{
std::cout << "\nTutorial 6: Demonstrating Clutch functionality." << std::endl;
simulation->add(powerLine);
simulation->setUniformGravity(
agx::Vec3(0, 0, 0));
createRpmControllerAndInternalRotationalActuatorResistance(engine, rotationalActuator, constantFrictionForce);
engine->connect(clutch);
clutch->connect(rotationalActuator);
powerLine->add(engine);
if (showPlots) {
rpmSeries->setUnit("RPM");
torqueSeries->setUnit("Nm");
relRpmSeries->setUnit("RPM");
engineTorqueSeries->setUnit("Nm");
plotWindow->
add(rpmCurve);
plotWindow->
add(gearTorqueCurve);
plotWindow->
add(relRpmCurve);
plotWindow->
add(engineTorqueCurve);
#if AGX_USE_WEBPLOT()
plotSystem->
add(
new agxPlot::WebPlot(
true));
#endif
}
for (size_t i = 0; i < 600; ++i)
{
if (i % 100 == 0)
{
std::cout <<
" Engine RPM after" <<
agx::Real(i) /
agx::Real(60) <<
" seconds : " << engine->getRPM() <<
" RPM." << std::endl;
std::cout <<
" Clutch after" <<
agx::Real(i) /
agx::Real(60) <<
" seconds : " << clutch->getConstraint()->getCurrentForce() <<
" Nm." << std::endl;
std::cout <<
" Engine torque applied after" <<
agx::Real(i) /
agx::Real(60) <<
" seconds : " << engine->getPowerGenerator()->getPowerTimeIntegralLookupTable()->lookupCurrent() <<
" RPM." << std::endl;
}
simulation->stepForward();
}
}
void runTutorial7(bool showPlots)
{
std::cout << "\nTutorial 7: Demonstrating GearBox functionality." << std::endl;
simulation->add(powerLine);
simulation->setUniformGravity(
agx::Vec3(0, 0, 0));
createRpmControllerAndInternalRotationalActuatorResistance(engine, rotationalActuator, constantFrictionForce);
gearBox->setGearRatios(gearRatios);
engine->connect(gearBox);
gearBox->connect(rotationalActuator);
powerLine->add(engine);
if (showPlots) {
rpmSeries->setUnit("RPM");
torqueSeries->setUnit("Nm");
relRpmSeries->setUnit("RPM");
plotWindow->
add(rpmCurve);
plotWindow->
add(gearTorqueCurve);
plotWindow->
add(relRpmCurve);
#if AGX_USE_WEBPLOT()
plotSystem->
add(
new agxPlot::WebPlot(
true));
#endif
}
gearBox->setGear(1);
for (size_t i = 1; i < 600; ++i)
{
if (i % 100 == 0)
{
int gear = static_cast<int>(i) / 100;
std::cout << "using gear [" << i << "] with gear ratio " << gearBox->getGearRatio(gear) << std::endl;
gearBox->setGear(gear);
std::cout <<
" Engine RPM after" <<
agx::Real(i) /
agx::Real(60) <<
" seconds : " << engine->getRPM() <<
" RPM." << std::endl;
std::cout <<
" GearBox after" <<
agx::Real(i) /
agx::Real(60) <<
" seconds : " << gearBox->getConstraint()->getCurrentForce() <<
" Nm." << std::endl;
}
simulation->stepForward();
}
}
void runTutorial8(bool showPlots)
{
std::cout << "\nTutorial 8: Demonstrating Differential functionality." << std::endl;
simulation->add(powerLine);
simulation->setUniformGravity(
agx::Vec3(0, 0, 0));
createRpmControllerAndInternalRotationalActuatorResistance(engine, rotationalActuator, constantFrictionForce);
powerLine->add(engine);
if (showPlots) {
rpmSeries->setUnit("RPM");
relRpmSeries->setUnit("RPM");
relRpm2Series->setUnit("RPM");
plotWindow->
add(rpmCurve);
plotWindow->
add(relRpmCurve);
plotWindow->
add(relRpm2Curve);
#if AGX_USE_WEBPLOT()
plotSystem->
add(
new agxPlot::WebPlot(
true));
#endif
}
for (size_t i = 0; i < 600; ++i)
{
if (i % 100 == 0)
{
std::cout <<
" Engine RPM after" <<
agx::Real(i) /
agx::Real(60) <<
" seconds : " << engine->getRPM() <<
" RPM." << std::endl;
}
simulation->stepForward();
}
}
void runTutorial9(bool showPlots)
{
std::cout << "\nTutorial 9: Demonstrating Dry Clutch functionality." << std::endl;
simulation->add(powerLine);
simulation->setUniformGravity(
agx::Vec3(0, 0, 0));
createRpmControllerAndInternalRotationalActuatorResistance(engine, rotationalActuator, constantFrictionForce);
dryClutch->setManualMode(true);
dryClutch->setTorqueCapacity(1000);
dryClutch->setAutoLock(true);
engine->connect(dryClutch);
dryClutch->connect(rotationalActuator);
powerLine->add(engine);
if (showPlots) {
rpmSeries->setUnit("RPM");
relRpmSeries->setUnit("RPM");
engineTorqueSeries->setUnit("Nm");
torqueSeries->setUnit("Nm");
plotWindow->
add(rpmCurve);
plotWindow->
add(relRpmCurve);
plotWindow->
add(engineTorqueCurve);
plotWindow->
add(gearTorqueCurve);
#if AGX_USE_WEBPLOT()
plotSystem->
add(
new agxPlot::WebPlot(
true));
#endif
}
for (size_t i = 0; i < 600; ++i)
{
if (i % 100 == 0)
{
std::cout <<
" Engine RPM after" <<
agx::Real(i) /
agx::Real(60) <<
" seconds : " << engine->getRPM() <<
" RPM." << std::endl;
std::cout <<
" Dry clutch torque after" <<
agx::Real(i) /
agx::Real(60) <<
" seconds : " << dryClutch->getTorque() <<
" Nm." << std::endl;
std::cout <<
" Engine torque applied after" <<
agx::Real(i) /
agx::Real(60) <<
" seconds : " << engine->getPowerGenerator()->getPowerTimeIntegralLookupTable()->lookupCurrent() <<
" RPM." << std::endl;
}
simulation->stepForward();
}
}
void runTutorial10(bool showPlots)
{
std::cout << "\nTutorial 10: Demonstrating how to connect a WheelJoint to a drive-train." << std::endl;
simulation->add(powerLine);
simulation->add(chassis);
simulation->add(wheel);
simulation->add(wheelJoint);
powerLine->add(engine);
powerLine->add(actuator);
engine->connect(actuator);
if (showPlots)
{
new DriveTrainShaftRPMListener(engine), "Engine RPM");
new DriveTrainRotationalActuatorRelativeRPMListener(actuator), "Wheel RPM");
engineRpmSeries->setUnit("RPM");
wheelRpmSeries->setUnit("RPM");
plotWindow->
add(engineRpmCurve);
plotWindow->
add(wheelRpmCurve);
#if AGX_USE_WEBPLOT()
plotSystem->
add(
new agxPlot::WebPlot(
true));
#endif
}
std::cout << " Engine RPM after 0.1 seconds : " << engine->getRPM() << " RPM." << std::endl;
std::cout << " Engine RPM after 0.2 seconds : " << engine->getRPM() << " RPM." << std::endl;
std::cout << " Engine RPM after 1 second : " << engine->getRPM() << " RPM." << std::endl;
if (showPlots)
{
using namespace std::chrono_literals;
std::this_thread::sleep_for(1s);
simulation->stepForward();
}
std::cout << "See web plot or 'RotationalActuatorDrivingWheel.dat' for full picture of RPM and torque" << std::endl;
}
int main(int argc, char** )
{
std::cout <<
"\tTutorial Hydraulics\n" <<
"\t--------------------------------\n\n" << std::endl;
bool showPlots = argc <= 1;
runTutorial1(showPlots);
runTutorial2(showPlots);
runTutorial3(showPlots);
runTutorial4(showPlots);
runTutorial5(showPlots);
runTutorial6(showPlots);
runTutorial7(showPlots);
runTutorial8(showPlots);
runTutorial9(showPlots);
runTutorial10(showPlots);
return 0;
}
Defines a viscous coupling between two rotational dimensions.
An engine that tries to hold a fixed velocity.
A gear is a connector between two rotational units.
Implementation of a combustion engine.
void setThrottleCalculator(ThrottleCalculator *controller)
Set a throttle controller that will be asked for a throttle every time step while the engine is turne...
ThrottleCalculator that strives to keep the engine at some given RPM.
Event listener that will generate plotting data.
virtual DataGenerator::Result getValue()
Output definition that will write plot data to a file.
System that manages the plotting system.
void add(agxPlot::Output *output)
Add an output to listen to the System.
agxPlot::Window * getOrCreateWindow(const agx::String &windowId="")
Get the default plot associated to the system.
bool add(Curve *curve)
Add the specified curve to the plot.
const agx::Constraint * getConstraint() const
Links two connections together.
static agx::Real convertAngularVelocityToRPM(const agx::Real angVel)
Convert between m/s and rpm.
Description of constraint geometry for one of the free degrees of freedom of a wheel joint.
@ WHEEL
For control of wheel rotation.
tutorial_driveTrain_ElectricMotor.cpp
void runTutorial1()
{
std::cout << "\nTutorial 1: Demonstrating basic electric motor creation.\n\n";
simulation->add(powerLine);
inductance);
motor->setVoltage(voltage);
powerLine->add(motor);
std::cout << "Time(s)\tAngular velocity(rad/s)" << std::endl;
std::cout << "0 \t" << motor->getAngularVelocity() << std::endl;
std::cout << 5.0 << " \t" << motor->getAngularVelocity() << std::endl;
for (int i = 15; i <= 60; i += 15) {
std::cout << i << " \t" << motor->getAngularVelocity() << std::endl;
}
}
void runTutorial2()
{
std::cout << "\nTutorial 2: Demonstrating using electric motor with other drive train components.\n\n";
simulation->add(powerLine);
inductance);
motor->setVoltage(voltage);
motor->connect(gear);
gear->connect(shaft);
powerLine->add(motor);
std::cout << " Engine RPM after 0.5 seconds : " << motor->getRPM() << " RPM." << std::endl;
std::cout << " Shaft RPM after 0.5 seconds : " << shaft->getRPM() << " RPM." << std::endl;
std::cout << " Gear torque exposure after 0.5 seconds : " << gear->getConstraint()->getCurrentForce() << " Nm." << std::endl;
std::cout << " Engine RPM after 5.0 seconds : " << motor->getRPM() << " RPM." << std::endl;
std::cout << " Shaft RPM after 5.0 seconds : " << shaft->getRPM() << " RPM." << std::endl;
std::cout << " Gear torque exposure after 5.0 seconds : " << gear->getConstraint()->getCurrentForce() << " Nm." << std::endl;
std::cout << " Engine RPM after 10.0 seconds : " << motor->getRPM() << " RPM." << std::endl;
std::cout << " Shaft RPM after 10.0 seconds : " << shaft->getRPM() << " RPM." << std::endl;
std::cout << " Gear torque exposure after 10.0 seconds : " << gear->getConstraint()->getCurrentForce() << " Nm." << std::endl;
}
int main(int , char** )
{
std::cout <<
"\tTutorial Electric Motor\n" <<
"\t--------------------------------\n\n" << std::endl;
runTutorial1();
runTutorial2();
return 0;
}
An electric motor is a roational unit that is a source of power in a drive train.
tutorial_driveTrain_combustionEngine.cpp
#include "tutorialUtils.h"
#if AGX_USE_WEBPLOT()
#endif
struct CarConfig
{
{
} wheel;
struct Chassis {
} chassis;
struct Engine {
} engine;
struct DryClutch
{
struct Duration
{
} duration;
} dryClutch;
struct Differential
{
} differential;
{
} material;
struct Park
{
} park;
struct Idle
{
} idle;
struct Reverse
{
struct ThrottleAdjust
{
} throttleAdjust;
} reverse;
struct Accelerate
{
struct ThrottleAdjust
{
} throttleAdjust;
} accelerate;
struct Brake
{
} brake;
} carConfig;
struct Car
{
};
struct Action
{
std::function<void()> func;
};
{
public:
m_carBody(car.body), m_engine(car.engine), m_clutch(car.clutch), m_brake(car.brake), m_gearBox(car.gearBox), m_sceneDecorator(sd)
{
m_actions = {
{"Parked", std::bind(&VehicleControl::park, this)},
{"Idling", std::bind(&VehicleControl::idle, this)},
{"Accelerating", std::bind(&VehicleControl::accelerate, this)},
{"Braking", std::bind(&VehicleControl::brake, this)},
{"Reversing", std::bind(&VehicleControl::reverse, this)},
};
m_sceneDecorator->setFontSize(float(0.028));
}
{
logVehicleData(t);
updateStatus();
addDisplayStates();
}
{
m_t = t;
m_carSpeed = m_carBody->getVelocity().x() * ms_to_kph;
m_engineRPM = m_engine->getRPM();
m_engineTorque = m_engine->getIndicatedTorque();
m_clutchEngaged = m_clutch->isEngaged();
}
void addDisplayStates()
{
}
void updateStatus()
{
auto action = std::find_if(m_actions.begin(), m_actions.end(),
[&](const Action& a) { return a.name == m_currentStatus; });
if (action != m_actions.end())
{
action->func();
}
else
{
std::cerr << "Error: unknown status '" << m_currentStatus << "'" << std::endl;
}
};
void park()
{
if (!m_isParking)
{
m_isParking = true;
m_currentGear = 0;
setGear();
releaseThrottle();
m_engine->setEnable(false);
m_startTime = m_t;
m_actionDuration = carConfig.park.duration;
}
if ((m_t - m_startTime) > m_actionDuration)
{
m_currentStatus = "Idling";
m_isParking = false;
}
};
void idle()
{
if (!m_isIdling)
{
m_isIdling = true;
m_engine->setEnable(true);
m_clutch->setEngage(false);
m_currentGear = 1;
setGear();
m_startTime = m_t;
m_actionDuration = carConfig.idle.duration;
}
if (m_t - m_startTime > m_actionDuration)
{
m_currentStatus = "Accelerating";
m_isIdling = false;
m_startTime = m_t;
}
};
void accelerate()
{
if (m_carSpeed > carConfig.accelerate.targetSpeed* ms_to_kph)
{
m_currentStatus = "Braking";
}
if (m_carSpeed > m_maxSpeedsPerGear[m_currentGear - 1])
{
shiftUp();
}
else
{
agx::Bool isGearShifting = (m_t - m_gearShiftStartTime < m_gearShiftDuration);
if (isGearShifting && m_gearShiftStartTime > 0)
{
releaseThrottle();
disengageClutch();
}
else
{
if (!m_clutch->isEngaged())
{
engageClutch();
m_targetPos = carConfig.accelerate.throttleAdjust.targetPos;
m_adjustDuration = carConfig.accelerate.throttleAdjust.duration;
}
applyThrottle(currentPos);
}
}
};
void brake()
{
if (!m_isBraking)
{
m_isBraking = true;
releaseThrottle();
disengageClutch();
}
applyBrake();
try {
if (abs(m_carSpeed) < m_minSpeedsPerGear[m_currentGear - 1] && m_currentGear <= (int) m_minSpeedsPerGear.size())
{
shiftDown();
}
}
catch (std::out_of_range& ) {
}
if (abs(m_carSpeed) < carConfig.brake.targetSpeed)
{
releaseBrake();
m_isBraking = false;
if (m_currentGear == 7)
m_currentStatus = "Parked";
else
m_currentStatus = "Reversing";
return;
}
};
void reverse()
{
if (!m_isReversing)
{
m_isReversing = true;
m_currentGear = 7;
setGear();
if (!m_clutch->isEngaged())
engageClutch();
m_startTime = m_t;
m_actionDuration = carConfig.reverse.duration;
m_adjustDuration = carConfig.reverse.throttleAdjust.duration;
m_targetPos = carConfig.reverse.throttleAdjust.targetPos;
}
applyThrottle(currentPos);
if ((m_t - m_startTime) > m_actionDuration)
{
m_currentStatus = "Braking";
m_isReversing = false;
return;
}
};
{
return m_gearRatios[m_currentGear];
}
void setGear()
{
m_gearBox->setGearRatio(gearRatio);
m_gearBox->rebind();
}
void shiftUp()
{
releaseThrottle();
disengageClutch();
m_currentGear = agx::clamp<int>((int)m_currentGear + 1, 0, 6);
setGear();
m_gearShiftStartTime = m_t;
m_startTime = m_t + m_gearShiftDuration;
}
void shiftDown()
{
m_currentGear = agx::clamp<int>((int)m_currentGear - 1, 1, (int)m_currentGear - 1);
setGear();
}
void engageClutch()
{
m_clutch->setEngage(true);
m_clutch->setEngageTimeConstant(m_engageDuration);
}
void disengageClutch()
{
m_clutch->setEngage(false);
m_clutch->setDisengageTimeConstant(m_disengageDuration);
}
{
m_engine->setThrottle(targetPos);
}
void releaseThrottle()
{
m_engine->setThrottle(0.0);
}
void applyBrake()
{
m_brake->setEngage(true);
}
void releaseBrake()
{
m_brake->setEngage(false);
}
{
return agx::clamp(targetPos * elapsedTime / duration, 0.0, targetPos);
}
private :
agx::Real m_gearShiftDuration = carConfig.gearShiftDuration;
agx::Real m_engageDuration = carConfig.dryClutch.duration.engage;
agx::Real m_disengageDuration = carConfig.dryClutch.duration.disengage;
};
{
public:
: m_body(body)
{
}
{
if (m_body == nullptr)
{
return result;
}
result.
value = m_body->getVelocity().x() * ms_to_kph;
return result;
}
private:
};
{
tireGeom->setMaterial(material);
sim->add(tire);
sim->add(hinge);
return hinge;
}
{
Car car;
agx::Real halfLength = carConfig.length / 2.0;
agx::Real halfHeight = carConfig.height / 2.0;
agx::Real wheelPosX = halfLength - carConfig.wheel.radius;
agx::Real wheelPosY = halfWidth + carConfig.wheel.width;
agx::Real wheelPosZ = carConfig.wheel.radius;
agx::Real frictionCoef = carConfig.material.friction;
agx::Real restitution = carConfig.material.restitution;
agx::Real youngsModulus = carConfig.material.youngsModulus;
sim->add(contactMaterial);
friction->setSolveType(solverType);
floorGeom->setMaterial(groundMaterial);
sim->add(floor);
sim->add(carBody);
car.body = carBody;
agx::HingeRef back_left = createWheel(
agx::Vec3(-wheelPosX, wheelPosY, wheelPosZ), carBody, tireMaterial, sim, root);
agx::HingeRef back_right = createWheel(
agx::Vec3(-wheelPosX, -wheelPosY, wheelPosZ), carBody, tireMaterial, sim, root);
agx::HingeRef front_left = createWheel(
agx::Vec3(wheelPosX, wheelPosY, wheelPosZ), carBody, tireMaterial, sim, root);
agx::HingeRef front_right = createWheel(
agx::Vec3(wheelPosX, -wheelPosY, wheelPosZ), carBody, tireMaterial, sim, root);
car.back_left = back_left;
car.back_right = back_right;
car.front_left = front_left;
car.front_right = front_right;
return car;
}
{
osg::Group*
root =
new osg::Group();
std::cout << "\nTutorial 1: Demonstrating basic combustion engine creation.\n\n";
powerLine->add(engine);
engine->setEnable(true);
std::cout << "Time(s)\tVelocity(RPM)\tThrottle" << std::endl;
std::cout << "0 \t" << engine->getRPM() << "\t\t" << engine->getThrottle() << std::endl;
std::cout << "1.0 \t" << engine->getRPM() << "\t\t" << engine->getThrottle() << std::endl;
std::cout << "10.0 \t" << engine->getRPM() << "\t\t" << engine->getThrottle() << std::endl;
}
{
osg::Group*
root =
new osg::Group();
std::cout << "\nTutorial 2: Demonstrating connecting combustion engine to other drive train components.\n\n";
powerLine->add(engine);
engine->connect(gear);
gear->connect(shaft);
engine->setEnable(true);
std::cout << "Time(s)\tVelocity(RPM)\tThrottle" << std::endl;
std::cout << 3.0 << "\t" << shaft->getRPM() << "\t\t" << engine->getThrottle() << std::endl;
std::cout << 30.0 << "\t" << shaft->getRPM() << "\t\t" << engine->getThrottle() << std::endl;
}
{
osg::Group*
root =
new osg::Group();
std::cout << "\nTutorial 3: Drive a car with the combustion engine.\n\n";
agx::Real crankShaftInertia = carConfig.engine.crankShaftInertia;
agx::Real clutchTorqueCapacity = carConfig.dryClutch.torqueCapacity;
Car car = createCar(sim, root);
if (!engine->loadLibraryProperties("Saab-B234i"))
powerLine->add(engine);
engine->setEnable(true);
engine->setInertia(crankShaftInertia);
dryClutch->setTorqueCapacity(clutchTorqueCapacity);
dryClutch->setAutoLock(true);
powerLine->add(dryClutch);
inputShaft->getRotationalDimension()->setName("inputShaft");
powerLine->add(inputShaft);
powerLine->add(gearBox);
outputShaft->getRotationalDimension()->setName("outputShaft");
powerLine->add(outputShaft);
powerLine->add(differential);
differential->setGearRatio(carConfig.differential.finalDriveRatio);
powerLine->add(brake);
powerLine->add(leftWheel);
powerLine->add(rightWheel);
engine->connect(dryClutch);
dryClutch->connect(inputShaft);
inputShaft->connect(gearBox);
gearBox->connect(outputShaft);
outputShaft->connect(differential);
differential->connect(brake);
brake->connect(leftWheel);
leftWheel->connect(rightWheel);
car.engine = engine;
car.clutch = dryClutch;
car.inputShaft = inputShaft;
car.gearBox = gearBox;
car.outputShaft = outputShaft;
car.differential = differential;
car.brake = brake;
car.leftWheel = leftWheel;
car.rightWheel = rightWheel;
VehicleControl* vehicleControl = new VehicleControl(car, sd);
sim->
add(vehicleControl);
if (showPlot)
{
plotWindow->
add(carSpeedCurve);
#if AGX_USE_WEBPLOT()
plotSystem->
add(
new agxPlot::WebPlot(
true));
#endif
}
}
int main(int argc, char** argv)
{
std::cout <<
"\tTutorial Combustion Engine\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(runTutorial1);
application->addScene(runTutorial2);
application->addScene(runTutorial3);
if (application->init(argc, argv))
return application->run();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
A Gear that uses a holonomic constraint instead of the default nonholonomic contstraint.
static agxRender::Color Cyan()
bool setTimeStep(agx::Real timeStep)
Sets the time step determined by the TimeGovernor in the DynamicsSystem.
agx::Real maxTorque
The maximum rated torque (Nm) which is the highest brake torque that an engine is allowed to deliver.
agx::Real idleRPM
The crankshaft speed (rpm) of the engine that is idling.
agx::Real maxPowerRPM
The rated power speed (rpm) (i.e.
agx::Real maxTorqueRPM
The rated torque speed (rpm) (i.e.
agx::Real displacementVolume
The total displacement volume (m^3) of the engine, which is the sum of the volumes of the cylinders.
tutorial_energyManager.cpp
#include "tutorialUtils.h"
{
osg::Group* root = new osg::Group();
simulation->
add( sphere );
simulation->
add( hinge );
energyManager->add( hinge );
energyManager->add( sphere );
rbKineticEnergy ) );
auto rbKineticEnergyChange = energyManager->getKineticEnergyChange( sphere );
rbKineticEnergyChange ) );
auto rbPotentialChange = energyManager->getPotentialChange( sphere );
rbPotentialChange ) );
auto rbDissipation = energyManager->getDissipation( sphere );
rbDissipation ) );
auto hingePotentialChange = energyManager->getPotentialChange( hinge );
hingePotentialChange ) );
auto hingeDissipation = energyManager->getDissipation( hinge );
hingeDissipation ) );
};
printEnergy( simulation );
return root;
}
{
osg::Group*
root =
new osg::Group();
motor->setEnable( true );
motor->setSpeed( 0.1 );
range->setEnable( true );
range->setRange( 0, 0.5 );
simulation->
add( prismatic );
energyManager->add( prismatic );
auto motorPower = energyManager->getPower( motor );
auto motorDissipation = energyManager->getDissipation( motor );
};
printEnergy( simulation );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( powerLine );
simulation->
add( sphere );
simulation->
add( hinge );
inductance );
motor->connect( rotationalActuator );
powerLine->add( motor );
motor->setVoltage( 2.0 );
auto angularVelocity = motor->getAngularVelocity();
angularVelocity ) );
};
printEnergy( simulation );
}
int main( int argc, char** argv )
{
std::cout <<
"\tEnergyManager tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
if ( application->init( argc, argv ) )
return application->run();
} catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
static agx::Real getWorkDone(const agxDriveTrain::ElectricMotor *motor)
Get the work done by an electric motor.
static agxRender::Color Violet()
static agxRender::Color DarkCyan()
static agxRender::Color ForestGreen()
static agx::Real getKineticEnergy(const agx::RigidBody *rb)
Get the kinetic energy for a rigid body.
agxSDK::EnergyManager * getEnergyManager() const
return the energy manager of the system.
tutorial_fracture.cpp
{
osg::Group* root = new osg::Group;
simulation->
add(matIter);
geomPlane->setPosition(
Vec3(0, 0, -.5));
simulation->
add(geomPlane);
geomPlane->setMaterial(matIter);
geom->setMaterial(matIter);
simulation->
add(staticWireHolder);
wire->setMaterial(wireMaterial);
simulation->
add(wireRenderer);
wireRenderer->setColor(
agx::Vec4f(.75f, .75f, 0, 1));
fracture->setFractureMinimumSize(.1);
fracture->setNumFractureFragmentsInterval(50, 100);
simulation->
add(fracture);
size_t numWalls = 3;
for (size_t i = 0; i < numWalls; ++i)
{
wallGeom->setPosition(
Vec3(-4 +
Real(i), 0, wallWec.
z()));
simulation->
add(wallGeom);
wallGeom->setMaterial(matIter);
fracture->addBreakable(wallGeom);
}
fracture->addBreaker(geom);
{
};
fracture->setNewBodyCallback(callback);
}
{
osg::Group*
root =
new osg::Group;
simulation->
add(matIter);
geomPlane->setPosition(
Vec3(0, 0, -.5));
simulation->
add(geomPlane);
geomPlane->setMaterial(matIter);
fracture->setFractureMinimumSize(.1);
fracture->setNumFractureFragmentsInterval(10, 20);
simulation->
add(fracture);
fracture->addBreaker(geomPlane);
fracture->setAddFractureFragmentsToGenerator(false);
std::vector<std::string> rocks = {
"data\\models\\convex_stones\\convex_rock2.obj",
"data\\models\\convex_stones\\convex_rock3.obj",
"data\\models\\convex_stones\\convex_rock4.obj",
"data\\models\\convex_stones\\convex_rock5.obj",
"data\\models\\convex_stones\\convex_rock6.obj"
};
for (size_t i = 0; i < 10; ++i)
{
std::string sRock = rocks[agx::random<int>(0, static_cast<int>(rocks.size() - 1))];
rockRef->setMaterial(matIter);
Vec3 spawnVec = floorVec; spawnVec.
z() = 0;
rbBody->
setPosition(Vec3::random(-spawnVec / 2, spawnVec / 2) +
Vec3(0, 0, height));
fracture->addBreakable(rockRef);
}
{
};
fracture->setNewBodyCallback(callback);
}
{
osg::Group*
root =
new osg::Group;
simulation->
add(matIter);
geomPlane->setPosition(
Vec3(0, 0, -.5));
simulation->
add(geomPlane);
geomPlane->setMaterial(matIter);
fracture->setFractureMinimumSize(.5);
fracture->setNumFractureFragmentsInterval(2, 5);
simulation->
add(fracture);
fracture->addBreaker(geomPlane);
fracture->setAddFractureFragmentsToGenerator(true);
agx::String rock =
"data\\models\\convex_stones\\convex_rock2.obj";
rockRef->setMaterial(matIter);
fracture->addBreakable(rockRef);
{
};
fracture->setNewBodyCallback(callback);
}
{
osg::Group*
root =
new osg::Group;
simulation->
add(matIter);
geomPlane->setPosition(
Vec3(0, 0, -.5));
simulation->
add(geomPlane);
geomPlane->setMaterial(matIter);
fracture->setFractureMinimumSize(.1);
fracture->setNumFractureFragmentsInterval(10, 20);
simulation->
add(fracture);
fracture->addBreaker(geomPlane);
fracture->setAddFractureFragmentsToGenerator(true);
rockRef->setMaterial(matIter);
fracture->addBreakable(rockRef);
{
};
fracture->setNewBodyCallback(callback);
}
{
osg::Group*
root =
new osg::Group;
simulation->
add(matIter);
geomPlane->setPosition(
Vec3(0, 0, -.5));
simulation->
add(geomPlane);
geomPlane->setMaterial(matIter);
generator->setResolution(0.75);
auto fractureShape = [generator, simulation, matIter,
root](
const Vec3& posForFragments,
const Quat& rotForFragments)
{
std::cout <<
"3D Voronoi diagram of shape type '"<< shapeToFracture->getTypeName() <<
" took " << timer.
getTime() <<
" ms to compute."<< std::endl;
std::cout << "# Voronoi cells: " << data.size() << std::endl;
for (auto& cell : data)
{
volume += convex->getVolume();
geom->setMaterial(matIter);
simulation->add(rb);
}
Real oldVolume = shapeToFracture->getVolume();
Real relDiff = ((oldVolume - volume) / oldVolume) *
Real(100.0);
std::cout << "Relative volume difference: " << relDiff << " %" << std::endl;
};
}
int main(int argc, char** argv)
{
int status = 0;
"\tRigidBody demonstrator\n" <<
"\t--------------------------------\n\n" <<
LOGGER_END();
try {
application->addScene(buildTutorial1, '1');
application->addScene(buildTutorial2, '2');
application->addScene(buildTutorial3, '3');
application->addScene(buildTutorial4, '4');
application->addScene(buildTutorial5, '5');
if (application->init(argc, argv)) {
status = application->run();
}
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return status;
}
std::function< void(FractureGenerator *, agxCollide::Geometry *, agx::RigidBody *)> NewBodyCallback
static agxRender::Color Brown()
static agxRender::Color Burlywood()
static agxRender::Color SlateGray()
static EulerAngles random(const Vec3 &min=Vec3(Real(0.0)), const Vec3 &max=Vec3(2 *agx::PI), EulerConvention::Convention c=EulerConvention::DEFAULT_CONVENTION)
Generate an EulerAngles object with random angles in every axis [ 0 - 2PI ].
The HighAccurayTimer class can replace the regular Timer class in high level applications where the n...
Real64 getTime() const
Report total elapsed time since start in milliseconds, excluding intervals when the timer was suspend...
void start()
Starts the HighAccuracyTimer.
void stop()
Stops the HighAccuracyTimer.
WireMaterial * getWireMaterial()
static Vec4T random(T min, T max)
void setYoungsModulusStretch(Real youngsStretch)
Set the Young's modulus controlling the stretchiness of a wire.
AGXPHYSICS_EXPORT agxCollide::Convex * createFromConvex(const agx::Vec3Vector &vertices, const agx::UInt32Vector &indices, const agx::String &sourceName="convexFromReaderWriter", const agx::Matrix3x3 &transformation=agx::Matrix3x3(), const agx::Vec3 &translation=agx::Vec3())
Creates a convex from vertices which are known to be convex.
tutorial_frictionModels.cpp
{
osg::Group *root = new osg::Group;
simulation->
add( ground );
enum FrictionModelType { BOX, SCALE_BOX, ITERATIVE_PROJECTED };
FrictionModelType modelType = ITERATIVE_PROJECTED;
if ( modelType == BOX )
else if ( modelType == SCALE_BOX )
else if ( modelType == ITERATIVE_PROJECTED )
ipcFriction->setSolveType( FrictionModel::DIRECT );
return root;
}
{
public:
GivenNormalForceBoxFrictionModel()
void setNormalForce(
Real normalForce )
{
m_normalForce = normalForce;
}
Real getNormalForce()
const {
return m_normalForce; }
{
FrictionModel::calculateTangentPlane( geometry1, geometry2, point, normal, depth, ret_u, ret_v );
renderDirection( point, ret_u, ret_v );
}
{
return getNormalForce() /
Real( numPoints );
}
protected:
virtual ~GivenNormalForceBoxFrictionModel() {}
void renderDirection(
const Vec3& point,
const Vec3& u,
const Vec3& v )
const
{
}
protected:
};
class OrientedBoxGivenNormalForceBoxFrictionModel : public GivenNormalForceBoxFrictionModel
{
public:
OrientedBoxGivenNormalForceBoxFrictionModel(
RigidBody* rb,
const Vec3& primaryDirection )
: m_rb( rb ), m_primaryDirection( primaryDirection ) {}
protected:
OrientedBoxGivenNormalForceBoxFrictionModel() : m_rb( 0L ), m_primaryDirection() {}
virtual ~OrientedBoxGivenNormalForceBoxFrictionModel() {}
{
ret_u = m_rb->getFrame()->transformVectorToWorld( m_primaryDirection );
ret_u -= normal * ( normal * ret_u );
FrictionModel::calculateTangentPlane( geometry1, geometry2, point, normal, depth, ret_u, ret_v );
return;
}
ret_v = normal ^ ret_u;
renderDirection( point, ret_u, ret_v );
}
protected:
};
void GivenNormalForceBoxFrictionModel::store(
OutputArchive& out )
const
{
BoxFrictionModel::store( out );
}
void GivenNormalForceBoxFrictionModel::restore(
InputArchive& in )
{
BoxFrictionModel::restore( in );
}
void OrientedBoxGivenNormalForceBoxFrictionModel::store(
OutputArchive& out )
const
{
GivenNormalForceBoxFrictionModel::store( out );
}
void OrientedBoxGivenNormalForceBoxFrictionModel::restore(
InputArchive& in )
{
GivenNormalForceBoxFrictionModel::restore( in );
m_rb = rb;
}
{
osg::Group *
root =
new osg::Group;
simulation->
add( ground );
simulation->
add( groundBox1ContactMaterial );
simulation->
add( groundBox2ContactMaterial );
GivenNormalForceBoxFrictionModel* box1FrictionModel = new GivenNormalForceBoxFrictionModel();
OrientedBoxGivenNormalForceBoxFrictionModel* box2FrictionModel =
new OrientedBoxGivenNormalForceBoxFrictionModel( box2,
Vec3( 1, 0, 0 ) );
}
int main( int argc, char** argv )
{
std::cout <<
"\tFriction model tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
#define AGXSTREAM_INSTANTIATE_STORAGE(C)
Use this macro to instantiate a storage for a Serializable class. If you have '::' in the class name,...
#define AGXSTREAM_DECLARE_SERIALIZABLE(T)
Use this in a Serializable class to add the required methods Important: Use full namespace in the dec...
void add(const Point &point)
Add temporary point (sphere).
Class for writing serialized data in binary format to a stream.
void setSolveType(SolveType solveType)
Solve type for a friction model can be either DIRECT (all equations to the direct solver),...
This namespace contain classes for streaming classes into archives, ASCII, binary for storage (serial...
InputRef< T > in(const char *name, T &obj)
Create an object with a name and a reference to the object that should be restored (usually a pointer...
OutputRef< Serializable > out(const char *name, const Serializable *obj)
Return an object that contain the name and the object that should be serialized to an archive.
tutorial_gamePad.cpp
#if AGX_USE_OIS()
struct XYAxis {
{
m_axes[0] = x;
m_axes[1] = y;
for (int i = 0; i < 2; i++)
{
m_text[i]->setCharacterSize(0.2f);
m_text[i]->setPosition(
agx::Vec3(0, 0, 0.4 - 0.2 * i));
root->addChild(m_text[i]);
}
}
void setValue(int index, double value)
{
if (index < 2 && index >= 0)
{
m_axes[index]->getLock1D()->setPosition(value);
}
}
~XYAxis()
{
m_axes[0] = nullptr;
m_axes[1] = nullptr;
}
osg::ref_ptr<agxOSG::Text> m_text[2];
};
struct Button
{
{
setPressed(false);
m_text->setCharacterSize(0.2f);
m_text->setPosition(
agx::Vec3(0, -0.11, 0.08));
m_node->addChild(m_text);
}
void setPressed(bool flag)
{
if (flag)
else
}
osg::ref_ptr<agxOSG::GeometryNode> m_node;
osg::ref_ptr<agxOSG::Text> m_text;
};
public:
SimulationJoystickListener(AxisVector& axes, XYAxis& pov, ButtonVector& buttons) :
JoystickListener(),
m_axes(axes),
m_pov(pov),
m_buttons(buttons)
{
std::map<int, agx::Vec2> direction = {
};
m_direction = std::move(direction);
}
virtual ~SimulationJoystickListener() {}
{
}
{
}
m_buttons[button].setPressed(down);
return false;
}
m_axes[axis / 2].setValue(axis % 2, val);
return false;
}
auto dir = m_direction[val];
m_pov.setValue(0, -dir[1]);
m_pov.setValue(1, -dir[0]);
return false;
}
std::cout << "sliderMoved: index = " << index << std::endl;
return false;
}
protected:
AxisVector m_axes;
XYAxis m_pov;
std::map<int, agx::Vec2> m_direction;
ButtonVector m_buttons;
};
{
cylinder->setPosition(position);
cylinder->setEnableCollisions(false);
simulation->
add(cylinder);
return Button(node, i);
}
{
cylinder->setPosition(position);
cylinder->setEnableCollisions(false);
simulation->
add(cylinder);
double size = 0.1;
simulation->
add(centerBody);
centerBody->setPosition(position);
osg::MatrixTransform* textRoot = new osg::MatrixTransform();
textRoot->setMatrix(osg::Matrix::translate(position.
x(), position.
y(), position.
z() + 1.1));
root->addChild(textRoot);
return XYAxis(x, y, textRoot);
}
{
osg::Group*
root =
new osg::Group();
background->setPosition(0, 0.4, 0);
simulation->
add(background);
simulation->
add(manager);
if (!manager->valid())
AxisVector axes;
double delta = 2;
{
}
ButtonVector buttons;
const int numButtons = 13;
delta = std::abs(leftPosition.x()) * 2 / numButtons;
for (int i = 0; i < numButtons; i++)
{
auto position = leftPosition +
agx::Vec3(delta * i, 0, 3);
auto button = createButton(i, position, simulation, root);
buttons.push_back(button);
}
manager->add(listener);
auto cam_eye =
agx::Vec3(-2.155E-01, -1.487E+01, -3.7E+00);
auto cam_center =
agx::Vec3(-4.90E-01, 5.5E-01, -6.3E-01);
auto up =
agx::Vec3(0.0082, -0.1952, 0.9807);
agx::Vec3 center(-0.2719, -14.7023, -4.98773);
agx::Vec3 direction = (eye.asVec3() - center).normal();
}
int main(int argc, char** argv)
{
std::cout <<
"\tGamePad Tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
if (application->init(argc, argv))
return application->run();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
#else
#include <iostream>
int main(int, char**)
{
std::cout <<
"\tGamePad Tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
std::cout << "Build without agxSensor support" << std::endl;
}
#endif
static int const NUM_AXES
static const int NUM_AXES
void setDirection(const agx::Vec3 &direction)
Set the direction of the light.
void setPosition(const agx::Vec4 &position)
Set the position of the light.
void setEnableCalculateLightPositions(Lights l, bool f)
agxOSG::LightSource getLightSource(Lights l)
Get a wrapper for a LightSource with only AGX types.
tutorial_granularBodies.cpp
#include "tutorialUtils.h"
#include <json/json.h>
namespace
{
{
public:
: m_prismatic(prismatic),
m_speed(speed),
m_interval(interval),
m_last(0)
{
setMask(MotorStepListener::PRE_STEP);
}
{
if (t - m_last >= m_interval)
{
m_last = t;
m_speed = -m_speed;
m_prismatic->getMotor1D()->setSpeed(m_speed);
}
}
};
}
{
std::cout << "Tutorial 1: Basic example of GranularBodySystem." << std::endl;
osg::Group*
root =
new osg::Group;
simulation->
add(granularBodySystem);
granularBodySystem->setParticleRadius(0.08);
granularBodySystem->setMaterial(pelletmat);
floorGeometry->setMaterial(floormat);
granularBodySystem->spawnParticlesInBound(containerBound,
granularBodySystem->getParticleRadius(),
agx::Vec3(2.0 * granularBodySystem->getParticleRadius()),
0.3 * granularBodySystem->getParticleRadius());
auto particles = granularBodySystem->getParticles();
for (size_t i = 0; i < particles.size(); ++i)
{
gPtr->setMaterial(pelletmat->getEntity());
gPtr->setRadius(0.08);
gPtr.
state().setEnabled(
true);
}
std::cout << "Particle system now has " << granularBodySystem->getNumParticles() << " particles" << std::endl;
w1->setPosition(funnelPos.
x(), funnelPos.
y(), funnelPos.
z());
containerAssembly->add(w1);
w2->setPosition(-funnelPos.
y(), funnelPos.
x(), funnelPos.
z());
containerAssembly->add(w2);
w3->setPosition(funnelPos.
y(), -funnelPos.
x(), funnelPos.
z());
containerAssembly->add(w3);
w4->setPosition(funnelPos.
x(), -funnelPos.
y(), funnelPos.
z());
containerAssembly->add(w4);
simulation->
add(containerAssembly);
osg::Group* node = nullptr;
conveyorSize[1],
conveyorSize[2]));
conveyor->addGroup("conveyor");
conveyor->setPosition(0, 0, 0);
conveyor->setSurfaceVelocity(
agx::Vec3f(1, 0, 0));
assembly->add(conveyor);
conveyorSize[2],
conveyorSize[2] * 3));
conveyor2->addGroup("conveyor");
conveyor2->setPosition(-scraperSize[0], conveyorSize[1] - conveyorSize[2], conveyorSize[2] * 4);
assembly->add(conveyor2);
conveyorSize[2],
conveyorSize[2] * 3));
conveyor3->addGroup("conveyor");
conveyor3->setPosition(-scraperSize[0], -conveyorSize[1] + conveyorSize[2], conveyorSize[2] * 4);
assembly->add(conveyor3);
conveyorSize[1],
conveyorSize[2] * 4));
conveyor4->addGroup("conveyor");
conveyor4->setPosition(conveyorSize[0] + conveyorSize[2], 0, conveyorSize[2] * 4);
assembly->add(conveyor4);
scraperSize[1],
scraperSize[2]));
scraperGeometry->addGroup("scraper");
assembly->add(scraper);
scraper->
setPosition(conveyorSize[0] - scraperSize[0], 0, scraperSize[2] + conveyorSize[2]);
assembly->setPosition(conveyorSize[0] / 2, 0, 0);
prismatic->
getRange1D()->
setRange(-conveyorSize[1] + scraperSize[1], conveyorSize[1] - scraperSize[1]);
assembly->add(prismatic);
simulation->
add(listener);
simulation->
add(assembly);
}
namespace
{
{
public:
: m_emitter(emitter), m_emitterGeom(geom), m_speed(speed)
{
setMask(ParticleEmitterMover::PRE_STEP);
}
{
if (m_emitter == nullptr || m_emitterGeom == nullptr) {
return;
}
m_emitter->setVelocity(m_emitterGeom->getTransform().transform3x3(
agx::Vec3(0, -1, 0)) * m_speed);
}
};
}
{
std::cout << "Tutorial 2: Particle emitter with granular <-> rigid body interaction" << std::endl;
osg::Group*
root =
new osg::Group;
floorGeometry->setMaterial(bodymat);
simulation->
add(floorGeometry);
simulation->
add(granularBodySystem);
granularBodySystem->setParticleRadius(0.04);
spawnGeom->setPosition(0, 2, 1);
simulation->
add(spawnGeom);
granularBodySystem->setEnableCollisions(spawnGeom, false);
emitter->setSeed(0);
emitter->setGeometry(spawnGeom);
emitter->setDistributionTable(distribution);
simulation->
add(emitter);
emitter->setRate(250);
emitter->setLife(3.0);
simulation->
add(
new ParticleEmitterMover(emitter, spawnGeom, 4.0));
int numX = 8, numY = 4;
agx::Real startX = -numX / 2 * spacing + boxSixe[0] * 2.5;
for (int xi = 0; xi < numX; xi++)
{
for (int yi = 0; yi < numY; yi++)
{
startY + yi * spacing,
z);
geometry->setMaterial(bodymat);
}
}
}
{
std::cout << "Tutorial 3: GranularBody contact data extraction." << std::endl;
osg::Group*
root =
new osg::Group();
simulation->
add(granularBodySystem);
auto createParticleRow = [granularBodySystem](
agx::Real radius,
int numParticles)
{
agx::Vec3 startPos =
agx::Vec3(increment * radius * (numParticles / 2) - radius, 0, height);
for (int i = 0; i < numParticles; i++)
{
auto p = granularBodySystem->createParticle();
p->setPosition(
agx::Vec3(i * increment * radius, 0, 0) - startPos);
p->setRadius(radius);
}
};
int numParticles = 6;
createParticleRow(radius, 0.5, increment, numParticles);
createParticleRow(radius, 5.0, increment, numParticles);
{
auto particleParticleContacts = s->getSpace()->getParticleParticleContacts();
for (auto& c : particleParticleContacts)
{
if (rel21.length() > 1E-1)
{
std::cout <<
agx::String::format(
"t=%5.2f particle: %d <-> particle: %d relative velocity: %5.2f m/s, force: %5.2f N",
t, particle1ID, particle2ID, rel21.length(), f.
length()) << std::endl;
}
}
};
{
auto particleGeometryContacts = s->getSpace()->getParticleGeometryContacts();
for (auto& c : particleGeometryContacts)
{
if (rel21.length() > 1E-1)
{
std::cout <<
agx::String::format(
"t=%5.2f particle: %d <-> geometry: %d relative velocity: %5.2f m/s, force: %5.2f N",
t, particleID, geometryID, rel21.length(), f.
length()) << std::endl;
}
}
};
}
{
public:
private:
};
{
}
{
auto dt = getSimulation()->getTimeStep() * 2;
for (size_t i = 0; i < particleStateArray.size(); i++)
{
if (particleLifeArray[i] < dt)
{
velocityArray[i].set(0, 0, 0);
angularVelocityArray[i].set(0, 0, 0);
}
}
}
{
agxJson::Reader reader;
agxJson::Value points;
bool result = reader.parse(points_json_data, points);
if (!result) {
return nullptr;
}
for (agxJson::ArrayIndex i = 0; i < points.size(); ++i)
{
agxJson::Value& point = points[i];
for (auto idx = 0; idx < 3; idx++)
p[idx] = point[idx].asDouble();
}
return spline;
}
{
const auto& first_pos = points[0];
motor->setEnable(true);
motor->setSpeed(0.05);
motor->setForceRange(-100, 100);
range->setEnable(true);
range->setForceRange(-1000, 1000);
range->setRange(-0.2, 0.2);
lock->setEnable(true);
return joint;
}
static const struct GranularMaterialSpecification
{
const double density = 2000;
} g_granularMaterialSpecification;
const double g_timeStep = 1.0 / 2100;
static const struct GranularMaterialInteractionSpecification
{
const double YM = 2.1e+11;
const double frictionCoefficient = 1;
const double rollingResistance = 1;
const double restitution = 2.0;
const double damping = 4.5 * (1.0 / g_timeStep);
const double adhesiveForce = 10;
const double adhesiveOverlap = 1e-4;
} g_materialInteractionSpecification;
static const struct EmitterSpecification {
const double radius = 0.15e-3;
const double rate = 0.0009 * 1E3 / (60 * 60);
const double lifeTime = 0.065;
} g_emitterSpecification;
{
granular_material->getBulkMaterial()->setDensity(g_granularMaterialSpecification.density);
c->
setDamping(g_materialInteractionSpecification.damping);
c->
setAdhesion(g_materialInteractionSpecification.adhesiveForce, g_materialInteractionSpecification.adhesiveOverlap);
c2->
setDamping(g_materialInteractionSpecification.damping);
c2->
setAdhesion(g_materialInteractionSpecification.adhesiveForce, g_materialInteractionSpecification.adhesiveOverlap);
return std::make_pair(granular_material, ground_material);
}
{
granularBodySystem->setMaterial(granular_material);
simulation->
add(granularBodySystem);
emitter->setLife(g_emitterSpecification.lifeTime);
emitter->setDistributionTable(table);
emitter->setDistributionTable(table);
emitter->setRate(g_emitterSpecification.rate);
emitter->setMaximumEmittedQuantity(g_emitterSpecification.maximumQuantity);
emitter->setVelocity(g_emitterSpecification.velocity);
simulation->
add(emitter);
return granularBodySystem;
}
static const std::string s_points_json_data = "[[-0.5383610006568054, 0.07257386344015329, 0.2612060291523281], [-0.5418047474216708, 0.11293615062691789, 0.26370108581085105], [-0.5361500496970344, 0.14519659816770528, 0.2652300372674672], [-0.4775519268170531, 0.2080014742821952, 0.26620497184289876], [-0.3964428295845093, 0.27186709252238345, 0.26621986020922855], [-0.3176976805866512, 0.3171006043823843, 0.2661171216674311], [-0.27655114555090565, 0.3241443742601609, 0.2701447747685833], [-0.2274695051932803, 0.31130002896729714, 0.2668435478351895], [-0.21083479463568902, 0.3024535683707251, 0.26533277559175883], [-0.20934248531798608, 0.2888885696911075, 0.263967521222618], [-0.22866360309227196, 0.2540214836548071, 0.26167469092540563], [-0.3008557485211213, 0.1496838681729756, 0.2507133475332961], [-0.32996967457399284, 0.09763065730373077, 0.24863848352199786], [-0.3631376428639147, 0.05981029822581489, 0.25300237978599127], [-0.3924501514037914, 0.047172225366720985, 0.24857640003099735], [-0.4268284901932429, 0.047616282428914934, 0.25091825113762367], [-0.4654995140423077, 0.0468375557720938, 0.25370482194536853], [-0.5162173618987876, 0.04642251652096434, 0.2576855869280641], [-0.5368906411728297, 0.06920317649666592, 0.26626008087213454]]";
{
std::cout << "Tutorial 4: Make particles kinematic along spline joint." << std::endl;
osg::Group*
root =
new osg::Group();
std::pair<agx::Material*, agx::Material*> materials = createMaterials(simulation);
floor->setMaterial(ground_material);
nozzle_geom->setEnableCollisions(false);
nozzle->add(nozzle_geom);
simulation->
add(createSplineJoint(s_points_json_data, nozzle));
auto granular_system = createGranularBodySystem(simulation, root, nozzle, granular_material);
simulation->
add(
new AgeKinematic(granular_system));
solver->setNumPPGSRestingIterations(5);
}
int main(int argc, char** argv)
{
std::cout <<
"\tTutorial particle system\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
application->addScene(buildTutorial2, '2');
application->addScene(buildTutorial3, '3');
application->addScene(buildTutorial4, '4');
if (application->init(argc, argv))
return application->run();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
Type-specific Array used for fast access into the data held by a Buffer.
Abstract representation of a data buffer.
agxData::Array< T > & getArray()
Convenience method to get an direct data array.
static agxRender::Color DarkOrange()
A Spline constructed of piecewise third-order polynomials which pass through a set of control points.
PointVector & getPoints()
void add(const agx::Vec3 &point, agx::Real tension=Point::DEFAULT_TENSION, agx::Real stretch=Point::DEFAULT_STRETCH, agx::Real curvature=Point::DEFAULT_CURVATURE)
Add point to the control points given tension (1 max, 0 min) and stretch.
@ DIRECT_AND_ITERATIVE
Solved both in the ITERATIVE and the DIRECT solver.
void setSolveType(agx::Constraint::SolveType solveType)
Specify the solve type for this constraint.
agx::Solver * getSolver()
friend class DistributionTable
agx::Object * getResource(const agx::Path &path, agx::Model *model=nullptr)
A basic particle system that contains all the storages and buffers required for basic operation and a...
Pointer to a entity instance of type Physics.GranularBody.
@ DYNAMICS
This Granular Body moves from the influence of forces.
AGXPHYSICS_EXPORT agx::ParticleState & state()
A SplineJoint is a constraint with a motor/range/lock for translation/rotation that will restrict a b...
tutorial_gravityFields.cpp
#include "tutorialUtils.h"
{
agx::Vec3 currentPos(-boxLength * numBoxesAtBottomRow, 0.0, boxHeight);
for (int i = numBoxesAtBottomRow; i > 0; --i) {
for (int j = i; j > 0; --j) {
if (boxMass > 0)
currentPos.x() += (
agx::Real)2.0 * boxLength + eps;
}
currentPos.x() = boxLength * (-i + 1);
currentPos.z() += (
agx::Real)2.0 * boxHeight + eps;
}
return parent;
}
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 3, 3, 0.5 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
simulation->
add( parent );
gravityField->setGravity(
agx::Vec3(0, 0, -9.81));
}
{
osg::Group*
root =
new osg::Group();
gravityField->setGravity(9.81);
}
{
public:
MyGravityField() {}
{
return position*-1;
}
protected:
~MyGravityField() {}
};
{
osg::Group*
root =
new osg::Group();
}
int main( int argc, char** argv )
{
std::cout <<
"\nGravityModel tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3', false );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
void setGravityField(agx::GravityField *gravityField)
Set the GravityField model used in gravity calculations.
The custom gravity field allow a user to derive from this class and implement their own gravity funct...
virtual agx::Vec3 calculateGravity(const agx::Vec3 &position) const override
Given position, a gravity acceleration will be calculated and returned.
The class PointGravityField calculates a gravity which is uniform in magnitude over the entire space ...
tutorial_hydraulics.cpp
void runTutorial1()
{
std::cout << "\nTutorial 1: Demonstrating basic hydraulic component creation and assembly." << std::endl;
simulation->add(powerLine);
shortMediumCoupling->connect(INPUT, OUTPUT, shortPipe);
shortMediumCoupling->connect(OUTPUT, INPUT, mediumPipe);
mediumPipe->connect(longPipe);
mediumLongCoupling = longPipe->getInputFlowConnector();
powerLine->setSource(shortPipe);
shortPipe->getFlowDimension()->setGradient(initialFlowRate);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(0.5));
std::cout << "Flow rates throug the three pipes:" << std::endl;
std::cout << " Short pipe flow rate: " << shortPipe->getFlowRate() << " m^3/s." << std::endl;
std::cout << " Medium pipe flow rate: " << mediumPipe->getFlowRate() << " m^3/s." << std::endl;
std::cout << " Long pipe flow rate: " << longPipe->getFlowRate() << " m^3/s." << std::endl;
mediumLongCoupling->connect(OUTPUT, INPUT, secondLongPipe);
shortPipe->getFlowDimension()->setGradient(initialFlowRate);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(0.5));
std::cout << "Flow rates after connecting the second long pipe:" << std::endl;
std::cout << " Short pipe flow rate: " << shortPipe->getFlowRate() << " m^3/s." << std::endl;
std::cout << " Medium pipe flow rate: " << mediumPipe->getFlowRate() << " m^3/s." << std::endl;
std::cout << " Long pipe flow rate: " << longPipe->getFlowRate() << " m^3/s." << std::endl;
std::cout << " Second long pipe flow rate: " << secondLongPipe->getFlowRate() << " m^3/s." << std::endl;
std::cout << " Sum flow in long pipes: " << longPipe->getFlowRate()+secondLongPipe->getFlowRate() <<" m^3/s." << std::endl;
}
void runTutorial2()
{
std::cout << "\nTutorial 2: Demonstrating pressure generation using an engine and a pump." << std::endl;
simulation->add(powerLine);
powerLine->add(engine);
pump->connect(INPUT, OUTPUT, engine);
pump->connect(OUTPUT, INPUT, shortPipe);
shortMediumConnector->connect(INPUT, OUTPUT, shortPipe);
shortMediumConnector->connect(OUTPUT, INPUT, mediumPipe);
mediumLongConnector->connect(INPUT, OUTPUT, mediumPipe);
mediumLongConnector->connect(OUTPUT, INPUT, longPipe);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(20));
std::cout << "Connector pressures with pump at start:" << std::endl;
std::cout << " Pump pressure: " << pump->getPressure() << std::endl;
std::cout << " Short-to-medium: " << shortMediumConnector->getPressure() << std::endl;
std::cout << " Medium-to-long: " << mediumLongConnector->getPressure() << std::endl;
std::cout << " Flow rate: " << longPipe->getFlowRate() << std::endl;
tankShortConnector->connect(INPUT, OUTPUT, tankPipe);
tankShortConnector->connect(OUTPUT, INPUT, shortPipe);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(10));
std::cout << "Connector pressures with tank pipe at start:" << std::endl;
std::cout << " Pump inlet pressure: " << tankShortConnector->getPressure() << std::endl;
std::cout << " Pump pressure: " << pump->getPressure() << std::endl;
std::cout << " Pump outlet pressure: " << shortMediumConnector->getPressure() << std::endl;
std::cout << " Medium-to-long: " << mediumLongConnector->getPressure() << std::endl;
std::cout << " Flow rate: " << longPipe->getFlowRate() << std::endl;
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(10));
std::cout << "Connector pressures with tank pipe at start and tank pressure:" << std::endl;
std::cout << " Pump inlet pressure: " << tankShortConnector->getPressure() << std::endl;
std::cout << " Pump pressure: " << pump->getPressure() << std::endl;
std::cout << " Pump outlet pressure: " << shortMediumConnector->getPressure() << std::endl;
std::cout << " Medium-to-long: " << mediumLongConnector->getPressure() << std::endl;
std::cout << " Flow rate: " << longPipe->getFlowRate() << std::endl;
std::cout << "Loads:" << std::endl;
std::cout << " TankPipe in: " << tankPipe->getFlowDimension()->getInputLoad() << std::endl;
std::cout << " TankPipe out: " << tankPipe->getFlowDimension()->getOutputLoad() << std::endl;
std::cout << " ShortPipe in: " << shortPipe->getFlowDimension()->getInputLoad() << std::endl;
std::cout << " ShortPipe out: " << shortPipe->getFlowDimension()->getOutputLoad() << std::endl;
std::cout << " MediumPipe in: " << mediumPipe->getFlowDimension()->getInputLoad() << std::endl;
std::cout << " MediumPipe out: " << mediumPipe->getFlowDimension()->getOutputLoad() << std::endl;
std::cout << " LongPipe in: " << longPipe->getFlowDimension()->getInputLoad() << std::endl;
std::cout << " LongPipe out: " << longPipe->getFlowDimension()->getOutputLoad() << std::endl;
}
namespace Tutorial_3
{
class Mechanics
{
public:
{
this->createFloor(simulation);
this->createCylinderBodies(simulation);
this->createPrismatic(simulation);
}
{
return m_base;
}
{
return m_head;
}
{
return m_prismatic;
}
private:
{
m_floor->setPosition(pos);
simulation->
add(m_floor);
}
{
m_base->setPosition(basePos);
m_head->setPosition(headPos);
}
{
simulation->
add(m_prismatic);
}
private:
};
}
void runTutorial3()
{
std::cout << "\nTutorial 3: Connection to mechanical system and more hydraulic components." << std::endl;
using namespace Tutorial_3;
Mechanics mechanics(simulation);
simulation->add(powerLine);
powerLine->add(inputPipe);
cylinderRange.
lower() = mechanics.getHead()->getPosition().z() - mechanics.getBase()->getPosition().z();
mechanics.getJoint()->getRange1D()->setRange(cylinderRange);
mechanics.getJoint()->getRange1D()->setEnable(true);
inputPipe->connect(cylinder);
cylinder->connect(outputPipe);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(1));
std::cout << "At rest:" << std::endl;
std::cout << " Input pressure: " << inputPipe->getOutputFlowConnector()->getPressure() << std::endl;
std::cout << " Output pressure: " << outputPipe->getInputFlowConnector()->getPressure() << std::endl;
std::cout << " Input flow rate: " << inputPipe->getFlowRate() << std::endl;
std::cout << " Output flow rate: " << outputPipe->getFlowRate() << std::endl;
controlOutput->connect(INPUT, OUTPUT, flowRateControl);
controlOutput->connect(OUTPUT, INPUT, inputPipe);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(1));
std::cout << "With passive constant flow valve:" << std::endl;
std::cout << " Input pressure: " << inputPipe->getOutputFlowConnector()->getPressure() << std::endl;
std::cout << " Output pressure: " << outputPipe->getInputFlowConnector()->getPressure() << std::endl;
std::cout << " Input flow rate: " << inputPipe->getFlowRate() << std::endl;
std::cout << " Output flow rate: " << outputPipe->getFlowRate() << std::endl;
flowRateControl->setAllowPumping(true);
simulation->stepForward();
std::cout << "One time step of active constant flow valve:" << std::endl;
std::cout << " Input pressure: " << inputPipe->getOutputFlowConnector()->getPressure() << std::endl;
std::cout << " Output pressure: " << outputPipe->getInputFlowConnector()->getPressure() << std::endl;
std::cout << " Input flow rate: " << inputPipe->getFlowRate() << std::endl;
std::cout << " Output flow rate: " << outputPipe->getFlowRate() << std::endl;
flowRateControl->setEnable(false);
flowRateControl->getFlowDimension()->setGradient(
agx::Real(0));
inputPipe->getFlowDimension()->setGradient(
agx::Real(0));
cylinder->getFrontChamber()->getFlowDimension()->setGradient(
agx::Real(0));
cylinder->getRearChamber()->getFlowDimension()->setGradient(
agx::Real(0));
outputPipe->getFlowDimension()->setGradient(
agx::Real(0));
mechanics.getBase()->setVelocity(
agx::Vec3());
mechanics.getHead()->setVelocity(
agx::Vec3());
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(1));
controlOutput->disconnect(inputPipe);
controlOutput->connect(OUTPUT, INPUT, reliefValve);
inpipeInput->connect(INPUT, OUTPUT, reliefValve);
inpipeInput->connect(OUTPUT, INPUT, inputPipe);
flowRateControl->setEnable(true);
simulation->stepForward();
std::cout << "One time step of active constant flow valve with relief valve:" << std::endl;
std::cout << " Input pressure: " << inputPipe->getOutputFlowConnector()->getPressure() << std::endl;
std::cout << " Output pressure: " << outputPipe->getInputFlowConnector()->getPressure() << std::endl;
std::cout << " Input flow rate: " << inputPipe->getFlowRate() << std::endl;
std::cout << " Output flow rate: " << outputPipe->getFlowRate() << std::endl;
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(1));
std::cout << "With relief valve:" << std::endl;
std::cout << " Input pressure: " << inputPipe->getOutputFlowConnector()->getPressure() << std::endl;
std::cout << " Output pressure: " << outputPipe->getInputFlowConnector()->getPressure() << std::endl;
std::cout << " Input flow rate: " << inputPipe->getFlowRate() << std::endl;
std::cout << " Output flow rate: " << outputPipe->getFlowRate() << std::endl;
}
void runTutorial4()
{
std::cout << "\nTutorial 4: Flow control using spool valve." << std::endl;
simulation->add(powerLine);
pump->getFlowDimension()->setName("PressureSource");
pump->setAllowPumping(true);
powerLine->add(pump);
pump->connect(relief);
pumpPipe->getFlowDimension()->setName("Pump");
forwardPipe->getFlowDimension()->setName("Forward");
backwardPipe->getFlowDimension()->setName("Backward");
tankPipe->getFlowDimension()->setName("Tank");
relief->connect(pumpPipe);
spool->connect(INPUT, OUTPUT, pumpPipe);
spool->connect(OUTPUT, INPUT, forwardPipe);
spool->connect(OUTPUT, INPUT, backwardPipe);
spool->connect(INPUT, INPUT, tankPipe);
coupling->connect(INPUT, OUTPUT, forwardPipe);
coupling->connect(OUTPUT, OUTPUT, backwardPipe);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(1));
std::cout << "= Flow is blocked at spool valve." << std::endl;
std::cout << " Pump flow rate: " << pumpPipe->getFlowRate() << std::endl;
std::cout << " Forward flow rate: " << forwardPipe->getFlowRate() << std::endl;
std::cout << " Backward flow rate: " << backwardPipe->getFlowRate() << std::endl;
std::cout << " Tank flow rate: " << tankPipe->getFlowRate() << std::endl;
spool->link(pumpPipe, forwardPipe);
spool->link(tankPipe, backwardPipe);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(3));
std::cout << "= Forward flow." << std::endl;
std::cout << " Pump flow rate: " << pumpPipe->getFlowRate() << std::endl;
std::cout << " Forward flow rate: " << forwardPipe->getFlowRate() << std::endl;
std::cout << " Backward flow rate:" << backwardPipe->getFlowRate() << std::endl;
std::cout << " Tank flow rate: " << tankPipe->getFlowRate() << std::endl;
spool->unlink(pumpPipe);
spool->unlink(tankPipe);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(3));
std::cout << "= Flow is blocked at spool valve." << std::endl;
std::cout << " Pump flow rate: " << pumpPipe->getFlowRate() << std::endl;
std::cout << " Forward flow rate: " << forwardPipe->getFlowRate() << std::endl;
std::cout << " Backward flow rate: " << backwardPipe->getFlowRate() << std::endl;
std::cout << " Tank flow rate: " << tankPipe->getFlowRate() << std::endl;
spool->link(pumpPipe, backwardPipe);
spool->link(tankPipe, forwardPipe);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(3));
std::cout << "= Backward flow." << std::endl;
std::cout << " Pump flow rate: " << pumpPipe->getFlowRate() << std::endl;
std::cout << " Forward flow rate: " << forwardPipe->getFlowRate() << std::endl;
std::cout << " Backward flow rate: " << backwardPipe->getFlowRate() << std::endl;
std::cout << " Tank flow rate: " << tankPipe->getFlowRate() << std::endl;
spool->unlink(forwardPipe);
spool->link(tankPipe, backwardPipe);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(3));
std::cout << "= Flow to tank." << std::endl;
std::cout << " Pump flow rate: " << pumpPipe->getFlowRate() << std::endl;
std::cout << " Forward flow rate: " << forwardPipe->getFlowRate() << std::endl;
std::cout << " Backward flow rate: " << backwardPipe->getFlowRate() << std::endl;
std::cout << " Tank flow rate: " << tankPipe->getFlowRate() << std::endl;
}
namespace tutorial_5
{
class FourWayThreePositionSpoolValve
{
public:
FourWayThreePositionSpoolValve(
m_in1(in1), m_in2(in2),
m_out1(out1), m_out2(out2),
{
bool connected = true;
this->linkNone();
}
void linkNone()
{
m_spool->unlink(m_in1);
m_spool->unlink(m_in2);
agxVerify(m_spool->getNumLinks() ==
size_t(0));
}
void linkParallel()
{
this->linkNone();
m_spool->link(m_in1, m_out1);
m_spool->link(m_in2, m_out2);
agxVerify(m_spool->getNumLinks() ==
size_t(2));
}
void linkCross()
{
this->linkNone();
m_spool->link(m_in1, m_out2);
m_spool->link(m_in2, m_out1);
agxVerify(m_spool->getNumLinks() ==
size_t(2));
}
private:
};
}
void runTutorial5()
{
std::cout << "\nTutorial 5: Flow control using wrapped spool valve." << std::endl;
simulation->add(powerLine);
pump->getFlowDimension()->setName("PressureSource");
pump->setAllowPumping(true);
powerLine->add(pump);
pump->connect(relief);
pumpPipe->getFlowDimension()->setName("Pump");
forwardPipe->getFlowDimension()->setName("Forward");
backwardPipe->getFlowDimension()->setName("Backward");
tankPipe->getFlowDimension()->setName("Tank");
relief->connect(pumpPipe);
tutorial_5::FourWayThreePositionSpoolValve spool(
pumpPipe, OUTPUT,
tankPipe, INPUT,
forwardPipe, INPUT,
backwardPipe, INPUT);
coupling->connect(INPUT, OUTPUT, forwardPipe);
coupling->connect(OUTPUT, OUTPUT, backwardPipe);
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(3));
std::cout << "= Flow blocked at spool valve." << std::endl;
std::cout << " Pump flow rate: " << pumpPipe->getFlowRate() << std::endl;
std::cout << " Forward flow rate: " << forwardPipe->getFlowRate() << std::endl;
std::cout << " Backward flow rate: " << backwardPipe->getFlowRate() << std::endl;
std::cout << " Tank flow rate: " << tankPipe->getFlowRate() << std::endl;
spool.linkParallel();
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(3));
std::cout << "= Forward flow." << std::endl;
std::cout << " Pump flow rate: " << pumpPipe->getFlowRate() << std::endl;
std::cout << " Forward flow rate: " << forwardPipe->getFlowRate() << std::endl;
std::cout << " Backward flow rate: " << backwardPipe->getFlowRate() << std::endl;
std::cout << " Tank flow rate: " << tankPipe->getFlowRate() << std::endl;
spool.linkNone();
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(3));
std::cout << "= Flow blocked at spool valve." << std::endl;
std::cout << " Pump flow rate: " << pumpPipe->getFlowRate() << std::endl;
std::cout << " Forward flow rate: " << forwardPipe->getFlowRate() << std::endl;
std::cout << " Backward flow rate: " << backwardPipe->getFlowRate() << std::endl;
std::cout << " Tank flow rate: " << tankPipe->getFlowRate() << std::endl;
spool.linkCross();
simulation->stepTo(simulation->getTimeStamp() +
agx::Real(3));
std::cout << "= Backward flow." << std::endl;
std::cout << " Pump flow rate: " << pumpPipe->getFlowRate() << std::endl;
std::cout << " Forward flow rate: " << forwardPipe->getFlowRate() << std::endl;
std::cout << " Backward flow rate: " << backwardPipe->getFlowRate() << std::endl;
std::cout << " Tank flow rate: " << tankPipe->getFlowRate() << std::endl;
}
int main(int , char** )
{
std::cout <<
"\tTutorial Hydraulics\n" <<
"\t--------------------------------\n\n" << std::endl;
runTutorial1();
runTutorial2();
runTutorial3();
runTutorial4();
runTutorial5();
return 0;
}
An engine that is driven by an EnginePowerGenerator.
A constant flow valve is a valve that acts to limit the flow rate through a pipe to some maximum valu...
A FlowConnector is a junction of a set of FlowUnits.
FlowUnit is the base class for Units that contains a FlowDimension.
A pipe carries fluid flow through the hydraulics system.
A PistonActuator is a model of a hydraulic cylinder.
A PowerLine Connector that connects to a rotational input and a flow output.
The relief valve is a hydraulic component that limits the pressure at some point in the system.
Spool valves are used to dynamically redirect flow during a simulation.
Will generate power by adding a load to a physical dimension.
agxPowerLine::PowerTimeIntegralLookupTable * getPowerTimeIntegralLookupTable() const
A lookup table used for power time integral (torque for rotational power, force for translational pow...
virtual void insertValue(const agx::Real x, const agx::Real y)
insert a value to the lookup table.
AGXHYDRAULICS_EXPORT void setTankPressure(agx::Real tankPressure)
Set the tank pressure of the global implicit tank.
AGXHYDRAULICS_EXPORT agx::Real diameterToArea(agx::Real diameter)
The agxHydraulics namespace contains classes for modeling hydraulic circuits within the power line fr...
tutorial_hydraulics_coupling.cpp
#include "HydraulicUtils.h"
class RotationalUnitInterface
{
public:
protected:
private:
};
class EngineInterface : public RotationalUnitInterface
{
public:
};
class PropellerInterface : public RotationalUnitInterface
{
public:
};
class ClutchInterface
{
public:
private:
};
class GearBoxInterface
{
public:
private:
};
{
public:
EngineInterface engine, PropellerInterface propeller,
ClutchInterface pumpClutch, GearBoxInterface pumpGearBox,
ClutchInterface propellerClutch, GearBoxInterface propellerGearBox);
protected:
virtual void updateEngineAgxState();
virtual void collectEngineAgxState();
virtual void updatePropellerAgxState();
virtual agx::Real calculatePropellerTorque();
virtual agx::Real calculatePropellerInertia();
virtual void collectPropellerAgxState();
private:
EngineInterface m_engine;
PropellerInterface m_propeller;
ClutchInterface m_pumpClutch;
GearBoxInterface m_pumpGearBox;
ClutchInterface m_propellerClutch;
GearBoxInterface m_propellerGearBox;
};
struct Foundation
{
StatsPrinter* printer;
bool writeToDisk;
};
class Scene
{
public:
Scene();
void construct(const Foundation& foundation);
public:
private:
void constructMechanics(const Foundation& foundation);
void constructPowerLine(const Foundation& foundation);
};
class DistanceScene
{
public:
DistanceScene(const Foundation& foundation);
private:
Scene m_scene;
};
class PrismaticScene
{
public:
PrismaticScene(const Foundation& foundation);
private:
Scene m_scene;
};
{
foundation.simulation->add(body);
return body;
}
{
}
{
if (engine != nullptr) {
} else {
}
}
agx::Real RotationalUnitInterface::getAngularVelocity()
const
{
return m_unit->getRotationalDimension()->getGradient();
}
RotationalUnitInterface(engine)
{
}
RotationalUnitInterface(propeller)
{
}
{
}
void ClutchInterface::configure(
agx::Real engagement)
{
}
{
return m_clutch;
}
{
}
void GearBoxInterface::configure(
agx::Real gear)
{
m_gearBox->setGear((int)gear);
}
{
return m_gearBox;
}
void createClutchEvent(
ClutchInterface* clutch)
{
agxControl::Callback1ActionImplementation<agx::Real>* configureClutch =
new agxControl::Callback1ActionImplementation<agx::Real>(
time, &ClutchInterface::configure, clutch, fraction);
simulation->
add(
new agxControl::CallbackAction(configureClutch));
}
void createGearChangeEvent(
GearBoxInterface* gearBox)
{
agxControl::Callback1ActionImplementation<agx::Real>* setGear =
new agxControl::Callback1ActionImplementation<agx::Real>(
time, &GearBoxInterface::configure, gearBox, gear);
simulation->
add(
new agxControl::CallbackAction(setGear));
}
ClutchInterface* pumpClutch, ClutchInterface* propellerClutch,
GearBoxInterface* , GearBoxInterface* )
{
createClutchEvent(simulation, time,
agx::Real(1), pumpClutch);
createClutchEvent(simulation, time,
agx::Real(1), propellerClutch);
}
EngineInterface engine, PropellerInterface propeller,
ClutchInterface pumpClutch, GearBoxInterface pumpGearBox,
ClutchInterface propellerClutch, GearBoxInterface propellerGearBox)
:
agxSDK::StepEventListener(PRE_STEP | POST_STEP),
m_engine(engine), m_propeller(propeller),
m_pumpClutch(pumpClutch), m_pumpGearBox(pumpGearBox),
m_propellerClutch(propellerClutch), m_propellerGearBox(propellerGearBox),
m_lastEngineVelocity(0), m_lastPropellerVelocity(0)
{
buildEventList(simulation, &m_pumpClutch, &m_propellerClutch, &m_pumpGearBox, &m_propellerGearBox);
}
{
this->updateEngineAgxState();
this->updatePropellerAgxState();
}
{
this->collectEngineAgxState();
this->collectPropellerAgxState();
}
void External::updateEngineAgxState()
{
agx::Real newTorque = this->calculateEngineTorque();
agx::Real newInertia = this->calculateEngineInertia();
m_engine.configure(newTorque, newInertia);
}
{
if (m_lastEngineVelocity < cutoffVelocity)
return maxTorque;
agx::Real cutoffOvershoot = m_lastEngineVelocity - cutoffVelocity;
agx::Real cutoffRange = maxVelocity - cutoffVelocity;
return maxTorque - progress*maxTorque;
}
{
}
void External::collectEngineAgxState()
{
m_lastEngineVelocity = m_engine.getAngularVelocity();
}
void External::updatePropellerAgxState()
{
agx::Real newTorque = this->calculatePropellerTorque();
agx::Real newInertia = this->calculatePropellerInertia();
m_propeller.configure(newTorque, newInertia);
}
agx::Real External::calculatePropellerTorque()
{
return -scale * m_lastPropellerVelocity * m_lastPropellerVelocity;
}
agx::Real External::calculatePropellerInertia()
{
return baseInertia + scale*m_lastPropellerVelocity;
}
void External::collectPropellerAgxState()
{
m_lastPropellerVelocity = m_propeller.getAngularVelocity();
}
Scene::Scene() :
pipeLength(1),
fluidDensity(800),
baseHe(0.5),
baseAttachOffset(
agx::
Real(0.8)*baseHe),
armHalfLength(2.0),
armHalfWidth(0.1),
armHe(armHalfLength, armHalfWidth, armHalfWidth)
{
}
void Scene::construct(const Foundation& foundation)
{
this->constructMechanics(foundation);
this->constructPowerLine(foundation);
External* external = new External(foundation.simulation,
EngineInterface(engine), PropellerInterface(propeller),
ClutchInterface(pumpClutch), GearBoxInterface(pumpGearBox),
ClutchInterface(propellerClutch), GearBoxInterface(propellerGearBox));
foundation.simulation->add(external);
}
void Scene::constructMechanics(const Foundation& foundation)
{
lowerArm = createBox(armHe,
agx::Vec3(baseAttachOffset,
agx::Real(0), baseAttachOffset+armHalfLength),
"Lower arm", foundation);
agx::Vec3 circleDirection(std::cos(-upperArmAngle),
agx::Real(0), std::sin(-upperArmAngle));
agx::Vec3 upperArmPos = circleCenter + armHalfLength*circleDirection;
upperArm = createBox(armHe, upperArmPos, "Upper arm", foundation);
foundation.simulation->getSpace()->setEnableCollisions(base->getGeometries()[0], lowerArm->getGeometries()[0], false);
foundation.simulation->getSpace()->setEnableCollisions(lowerArm->getGeometries()[0], upperArm->getGeometries()[0], false);
lowerArm, lowerArmJointFrame,
upperArm, upperArmJointFrame);
armJoint =
new agx::Hinge(lowerArm, lowerArmJointFrame, upperArm, upperArmJointFrame);
foundation.simulation->add(armJoint);
base, baseJointFrame,
lowerArm, baseArmFrame);
baseJoint =
new agx::Hinge(base, baseJointFrame, lowerArm, baseArmFrame);
baseJoint->getLock1D()->setEnable(true);
foundation.simulation->add(baseJoint);
}
void Scene::constructPowerLine(const Foundation& foundation)
{
foundation.simulation->add(powerLine);
engine->ignition(true);
powerLine->setSource(engine);
pumpClutch->getRotationalDimension()->setName("PumpClutch");
pumpGearBox->setGearRatios(pumpGears);
pumpGearBox->setGear(0);
cutoffPressure, deadheadPressure, internalLeakage, flowRatio, compensatorArea);
pumpPipe->getFlowDimension()->setName("Pump pipe");
relief->getInputChamber()->getFlowDimension()->setName("Relief valve");
tankPipe->getFlowDimension()->setName("Tank pipe");
propellerClutch->setEfficiency(
agx::Real(1e100));
propellerClutch->getRotationalDimension()->setName("PropellerClutch");
propellerGearBox->setGearRatios(propellerGears);
propellerGearBox->setGear(0);
agxVerify(propellerClutch->connect(propellerGearBox));
agxVerify(propellerGearBox->connect(propeller));
foundation.printer->addPressureGauge(pump, "Pump");
foundation.printer->addFlowRateGauge(pumpPipe, "Pump");
foundation.printer->addCustomGauge(std::bind(&agxPowerLine::TranslationalDimension::getValue, pump->getPoppetDimension()), "PumpActivation", "");
if (foundation.writeToDisk)
{
foundation.simulation->add(dotWriter);
}
}
DistanceScene::DistanceScene(const Foundation& foundation)
{
m_scene.construct(foundation);
m_distanceJoint->getRange1D()->setEnable(true);
m_distanceJoint->getLock1D()->setEnable(false);
foundation.simulation->add(m_distanceJoint);
m_distanceJoint, m_scene.barrelArea, m_scene.pistonArea, m_scene.fluidDensity);
bool success = true;
m_scene.pumpPipe->connect(m_scene.piston);
agxVerify(success);
m_scene.piston->connect(m_scene.tankPipe);
agxVerify(success);
foundation.printer->addFlowRateGauge(m_scene.piston->getRearChamber(), "RearChamber");
foundation.printer->addFlowRateGauge(m_scene.piston->getFrontChamber(), "FrontChamber");
}
PrismaticScene::PrismaticScene(const Foundation& foundation)
{
m_scene.construct(foundation);
}
{
const bool isUnitTest = arguments->
find(
"--isFileSanityUnitTest") != -1;
const bool writeToDisk = !isUnitTest;
osg::Group*
root =
new osg::Group();
StatsPrinter* printer = new StatsPrinter(simulation, application, writeToDisk);
Foundation foundation = {simulation, application,
root, printer, writeToDisk};
DistanceScene scene(foundation);
}
int main(int argc, char** argv)
{
" External coupling demo application.\n" <<
" -----------------------------------------------------\n\n" <<
LOGGER_END();
{
try {
application->addScene( buildDistanceScene, '1' );
if (application->init(argc, argv)) {
return application->run();
}
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
}
return 0;
}
void setThrottle(agx::Real throttle)
Set the throttle input.
agxPowerLine::PowerGenerator * getPowerGenerator() const
agx::Real getTorque() const
The gear box can have an arbitrary number of gears.
The gear box can have an arbitrary number of gears.
static std::string getStaticTypeName()
agx::Real getPistonPosition() const
The VariableDisplacementPump is a pressure regulated pump that automatically alters its own displacem...
int find(const std::string &key, size_t startPosition=0, bool findAlsoHandled=true)
Finds the position of an argument.
agxIO::ArgumentParser * getArguments()
Return a value of y given any value of x.
virtual void addLoad(agx::Real load)
Add a load to the dimension.
void setName(const agx::Name &name)
virtual agx::Real getGradient() const
The rotational has one rotational degree of freedom.
agx::Real getInputTorque() const
virtual agx::Real getOutputLoad() const override
virtual bool setInertia(agx::Real value)
set the inertia of the rotational dimension.
A rotational unit has one degree of freedom.
agxPowerLine::RotationalDimension * getRotationalDimension() const
returns a pointer to the rotational dimension
A StepEventListener that writes a power line.dot graph in the LAST event.
static agx::Bool calculateFramesFromBody(agx::Vec3 bodyPoint, agx::Vec3 bodyAxis, const agx::RigidBody *body, agx::Frame *bodyFrame, const agx::RigidBody *otherBody, agx::Frame *otherFrame)
Calculates the constraint attachment frames given point and axis in body coordinates of body.
virtual void clear()
clear all data
AGXHYDRAULICS_EXPORT agx::Real cubicMetersPerSecondToGallonsPerMinute(agx::Real cubicMetersPerSecond)
AGXHYDRAULICS_EXPORT agx::Real pascalToPsi(agx::Real pascals)
AGXHYDRAULICS_EXPORT agx::Real cubicMetersPerSecondToLitersPerMinute(agx::Real cubicMetersPerSecond)
AGXHYDRAULICS_EXPORT agx::Real pascalToBar(agx::Real pascals)
AGXHYDRAULICS_EXPORT agx::Real radiansPerSecondToRevolutionsPerMinute(agx::Real radiansPerSecond)
AGXCORE_EXPORT const Real RealEpsilon
tutorial_hydrodynamics.cpp
#include "tutorialUtils.h"
namespace
{
{
float alpha( 0.3f );
return node;
}
{
for (
agx::UInt o = numSpheresBase; o > 0; --o ) {
assembly->add( sphere );
}
pos.
z() += std::sqrt(
agx::Real( 3 ) ) * sphereRadius + eps;
}
}
pos.
x() = -sphereRadius *
static_cast<agx::Real>(numSpheresBase);
}
return assembly;
}
{
for (
agx::UInt i = 0, numVertices = vertices.
size(); i < numVertices; ++i )
}
{
public:
protected:
{
m_timer.reset( true );
}
{
return;
m_timer.stop();
if ( m_app != nullptr ) {
m_app->getSceneDecorator()->setText( 0,
agx::String::format(
"Time to step forward: %6.2f ms (%6.1f fps)",
time,
getSimulation()->getDynamicsSystem()->getRigidBodies().size() ) );
}
}
protected:
};
}
{
osg::Group*
root =
new osg::Group();
waterGeometry->setPosition( 0, 0, -15 );
waterGeometry->setMaterial( waterMaterial );
::createWaterVisual( waterGeometry, root );
controller->addWater( waterGeometry );
simulation->
add( controller );
simulation->
add( waterGeometry );
pyramid->setPosition( 0, 0, 4 );
simulation->
add( pyramid );
simulation->
add(
new Printer( application ) );
}
{
osg::Group*
root =
new osg::Group();
waterGeometry->setPosition( 0, 0, -15 );
waterGeometry->setMaterial( waterMaterial );
::createWaterVisual( waterGeometry, root );
controller->addWater( waterGeometry );
simulation->
add( controller );
simulation->
add( waterGeometry );
std::srand( 4 );
for (
agx::UInt layer = 0; layer < numLayers; ++layer ) {
for (
agx::UInt i = 0; i < numPerLayer; ++i ) {
simulation->
add( torus );
}
}
simulation->
add(
new Printer( application ) );
}
namespace
{
{
public:
protected:
virtual ~WaveGenerator();
protected:
osg::ref_ptr<agxOSG::GeometryNode> m_hfNode;
};
:
agxSDK::StepEventListener(
agxSDK::StepEventListener::PRE_COLLIDE ), m_hf( hf )
{
m_heights.resize( m_hf->getResolutionX() * m_hf->getResolutionY() );
}
WaveGenerator::~WaveGenerator()
{
}
{
if ( m_hf == nullptr ) {
getSimulation()->remove( this );
return;
}
for ( unsigned int i = 0; i < m_hf->getResolutionY(); ++i ) {
for ( unsigned int j = 0; j < m_hf->getResolutionX(); ++j ) {
agx::UInt index = i * m_hf->getResolutionX() + j;
}
}
m_hf->setHeights( m_heights );
}
{
public:
private:
};
:
agxSDK::StepEventListener(
agxSDK::StepEventListener::PRE_COLLIDE ), m_ship( ship ), m_localPosition( localPosition ),
m_localForce( localForce )
{
}
{
if ( m_ship == nullptr ) {
getSimulation()->remove( this );
return;
}
0.3,
m_ship->getFrame()->transformPointToWorld( m_localPosition ) +
m_ship->getFrame()->transformVectorToWorld( m_localForce.normal() ),
0.2,
m_ship->addForceAtPosition( m_ship->getFrame()->transformVectorToWorld( m_localForce ),
m_ship->getFrame()->transformPointToWorld( m_localPosition ) );
}
{
meshReader->readFile( "models/ship.obj" );
::scaleMesh( meshReader->getVertices(),
agx::Vec3( 0.01 ) );
&meshReader->getIndices(),
"ship" ) ) );
return ship;
}
}
{
osg::Group*
root =
new osg::Group();
waterGeometry->setPosition( 0, 0, 0 );
waterGeometry->setMaterial( waterMaterial );
controller->addWater( waterGeometry );
simulation->
add( controller );
simulation->
add( waterGeometry );
bool useAeroDynamics = false;
if (useAeroDynamics) {
controller->setEnableAerodynamics(ship, true);
}
pyramid->setPosition( 0, -10, 4 );
simulation->
add( pyramid );
::createWaterVisual(waterGeometry, root);
WaveGenerator* waveGenerator = new ::WaveGenerator( heightField );
waveGenerator->preCollide( 0 );
simulation->
add( waveGenerator );
simulation->
add(
new Printer( application ) );
}
{
osg::Group*
root =
new osg::Group();
waterGeometry->setPosition( 0, 0, -15 );
waterGeometry->setMaterial( waterMaterial );
::createWaterVisual( waterGeometry, root );
controller->addWater( waterGeometry );
simulation->
add( controller );
simulation->
add( waterGeometry );
pyramid->setPosition( 0, 0, 4 );
simulation->
add( pyramid );
}
namespace
{
{
public:
private:
private:
WaveGeneratorRef m_waveGenerator;
};
WaveGenerator* waveGenerator,
osg::Group* root )
:
agxSDK::StepEventListener(
agxSDK::StepEventListener::PRE_COLLIDE )
, m_waterGeometry( waterGeometry )
, m_waveGenerator( waveGenerator )
, m_hydrodynamicsModule( new
agxModel::WindAndWaterController() )
, m_shipInAgX( ship )
, m_time( 0 )
{
if ( root != nullptr )
}
{
return m_shipInAgX != nullptr &&
!m_shipInAgX->getGeometries().empty() ?
m_shipInAgX->getGeometries()[ 0 ]->getShapes()[ 0 ] :
nullptr;
}
{
if ( m_shipInAgX == nullptr ) {
getSimulation()->remove( this );
return;
}
if ( m_waveGenerator != nullptr )
m_waveGenerator->preCollide( m_time );
if ( m_hydrodynamicsModule != nullptr ) {
m_shipInAgX,
getSimulation()->getUniformGravity(),
nullptr,
true );
m_shipInAgX->getMassProperties()->setMassCoefficients( addedMassCoeffs );
m_shipInAgX->getMassProperties()->setInertiaTensorCoefficients( addedInertiaCoeffs );
}
m_time += getSimulation()->getTimeStep();
}
}
{
osg::Group*
root =
new osg::Group();
waterGeometry->setPosition( 0, 0, 0 );
waterGeometry->setMaterial( waterMaterial );
::createWaterVisual(waterGeometry, root);
::WaveGenerator* waveGenerator = new ::WaveGenerator( heightField );
waveGenerator->preCollide( 0 );
simulation->
add(
new MyHydroSimulation( waterGeometry, waveGenerator, ship, root ) );
}
namespace
{
{
public:
WaveGenerator* waveGenerator,
osg::Group* root );
private:
private:
WaveGeneratorRef m_waveGenerator;
};
WaveGenerator* waveGenerator,
osg::Group* root )
:
agxSDK::StepEventListener(
agxSDK::StepEventListener::PRE_COLLIDE ), m_waterGeometry( waterGeometry ),
m_waveGenerator( waveGenerator ), m_hydrodynamicsModule( new
agxModel::WindAndWaterController() ), m_shipInAgXDontTouch( ship ),
m_kinematicBody( kinematicBody ), m_lock( lock ), m_time( 0 ), m_gravity( 0, 0, -
agx::GRAVITY_ACCELERATION )
{
if ( m_lock != nullptr )
m_lock->setEnableComputeForces( true );
if ( root != nullptr )
}
{
return m_shipInAgXDontTouch != nullptr && !m_shipInAgXDontTouch->getGeometries().empty() ?
m_shipInAgXDontTouch->getGeometries()[ 0 ]->getShapes()[ 0 ] :
nullptr;
}
{
if ( m_shipInAgXDontTouch == nullptr || m_kinematicBody == nullptr ) {
getSimulation()->remove( this );
return;
}
if ( m_waveGenerator != nullptr )
m_waveGenerator->preCollide( m_time );
const agx::Real timeStep = getSimulation()->getTimeStep();
totalForce += m_shipInAgXDontTouch->getMassProperties()->getMass() * m_gravity;
if ( m_hydrodynamicsModule != nullptr ) {
m_shipInAgXDontTouch,
m_gravity,
nullptr,
true );
}
if ( m_lock != nullptr ) {
m_lock->getLastForce( m_kinematicBody, forceFromAgX, torqueFromAgX );
totalForce += forceFromAgX;
totalTorque += torqueFromAgX;
}
agx::Matrix3x3 shipWorldInertiaMatrix = shipRT *
agx::Matrix3x3( m_shipInAgXDontTouch->getMassProperties()->getPrincipalInertiae() ) * shipR;
agx::Vec3 linearVelocity = m_kinematicBody->getVelocity() + shipWorldMassMatrix.
inverse() * totalForce * timeStep;
agx::Vec3 angularVelocity = m_kinematicBody->getAngularVelocity() + shipWorldInertiaMatrix.
inverse() * totalTorque * timeStep;
agx::Vec3 position = m_kinematicBody->getPosition() + linearVelocity * timeStep;
m_kinematicBody->moveTo( position, orientation, timeStep );
m_time += timeStep;
}
}
{
osg::Group*
root =
new osg::Group();
waterGeometry->setPosition( 0, 0, 0 );
waterGeometry->setMaterial( waterMaterial );
::createWaterVisual(waterGeometry, root);
::WaveGenerator* waveGenerator = new ::WaveGenerator( heightField );
waveGenerator->preCollide( 0 );
simulation->
add( kinematicShipAttachmentBody );
simulation->
add(
new MyHydroSimulationAndStepper( waterGeometry, ship, kinematicShipAttachmentBody, lock, waveGenerator, root ) );
}
{
public:
: m_app(app)
, m_body(body)
, m_left(left)
, m_right(right)
, m_leftRear(leftRear)
, m_rightRear(rightRear)
, m_rotator(rotator)
, m_wire(wire)
{
m_app->getSceneDecorator()->setText(6,
agx::String::format(
"up/down - Increase/decrease speed"));
}
bool keyboard(
int key,
unsigned int,
float,
float,
bool down)
{
if (down && key == 'z') {
m_left->setSpeed(-0.5);
m_leftRear->setSpeed(-0.5);
return true;
}
if (!down && key == 'z') {
m_left->setSpeed(0);
m_leftRear->setSpeed(0);
return true;
}
if (down && key == 'a') {
m_left->setSpeed(0.5);
m_leftRear->setSpeed(0.5);
return true;
}
if (!down && key == 'a') {
m_left->setSpeed(0);
m_leftRear->setSpeed(0);
return true;
}
if (down && key == 'x') {
m_right->setSpeed(-0.5);
m_rightRear->setSpeed(-0.5);
return true;
}
if (!down && key == 'x') {
m_right->setSpeed(0);
m_rightRear->setSpeed(0);
return true;
}
if (down && key == 's') {
m_right->setSpeed(0.5);
m_rightRear->setSpeed(0.5);
return true;
}
if (!down && key == 's') {
m_right->setSpeed(0);
m_rightRear->setSpeed(0);
return true;
}
{
m_rotator->setSpeed(s);
std::cerr << "Speed: " << m_rotator->getSpeed() << std::endl;
return true;
}
{
agx::Real s = std::min(0.7, m_rotator->getSpeed()*1.01);
m_rotator->setSpeed(s);
std::cerr << "Speed: " << m_rotator->getSpeed() << std::endl;
return true;
}
return false;
}
{
agx::Real tension = m_wire->getTension(2).smoothed;
m_app->getSceneDecorator()->setText(2,
agx::String::format(
"Tension: %6.2f kN", tension / 1000));
}
};
{
public:
: m_controller(controller)
{
}
{
for (
size_t i = 0; i<shapes.
size(); i++)
{
}
}
private:
osg::Group* m_root;
};
{
osg::Group*
root =
new osg::Group();
waterGeometry->setPosition(0, 0, -30);
waterGeometry->setMaterial(waterMaterial);
::createWaterVisual(waterGeometry, root);
controller->addWater(waterGeometry);
simulation->
add(controller);
simulation->
add(waterGeometry);
simulation->
add(bottomGeometry);
{
}
if (!leftWing)
if (!rightWing)
if (!body)
if (!leftWingHinge)
if (!rightWingHinge)
if (!wireConnection)
if (!rightWingHingeRear)
if (!leftWingHingeRear)
if (!bodyGeometry)
submarine->setPosition(-45, -45, 0);
shipGeom->setEnableCollisions(false);
simulation->
add(wireRenderer);
simulation->
add(
new GuiListener(application, body, leftWingHinge->
getMotor1D(), rightWingHinge->getMotor1D(),
leftWingHingeRear->getMotor1D(), rightWingHingeRear->getMotor1D(), hinge->
getMotor1D(), wire));
PressureVisitor visitor(controller, root);
submarine->traverse(&visitor);
}
namespace
{
{
public:
: m_flowCoefficient( flowCoefficient ), m_center( center )
{
}
{
agx::Vec3 positionFromCenter = worldPoint - m_center;
agx::Real xVelocity = positionFromCenter.
x() - 2 * positionFromCenter.
y();
agx::Real yVelocity = 2 * positionFromCenter.
x() + positionFromCenter.
y();
return m_flowCoefficient *
agx::Vec3( xVelocity, yVelocity, 0 );
}
protected:
CustomWaterFlowGenerator() {}
private:
};
{
}
{
}
}
{
osg::Group*
root =
new osg::Group();
waterGeometry1->setPosition( position1 );
::createWaterVisual( waterGeometry1, root );
waterGeometry2->setPosition( position2 );
::createWaterVisual( waterGeometry2, root );
controller->addWater( waterGeometry1 );
controller->addWater( waterGeometry2 );
controller->setWaterFlowGenerator( waterGeometry2, new ::CustomWaterFlowGenerator(
agx::Real( 0.3 ), position2 ) );
simulation->
add( controller );
simulation->
add( waterGeometry1 );
simulation->
add( waterGeometry2 );
for ( int i = 0; i < 2; i++ ) {
for ( int j = 0; j < 2; j++ ) {
simulation->
add( sphere1 );
simulation->
add( sphere2 );
}
}
}
{
public:
WaveWaterWrapper()
{
return worldPoint.
z() - surfaceLevel;
}
};
{
osg::Group*
root =
new osg::Group();
waterGeometry->setPosition( position );
::createWaterVisual( waterGeometry, root );
controller->addWater( waterGeometry );
controller->setWaterWrapper( waterGeometry, new WaveWaterWrapper() );
simulation->
add( controller );
simulation->
add( waterGeometry );
for( int i = 0; i < 10; i++ ) {
simulation->
add( sphere );
}
}
int main( int argc, char** argv )
{
"\tTutorial hydrodynamics\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
application->addScene( buildTutorial4, '4' );
application->addScene( buildTutorial5, '5' );
application->addScene( buildTutorial6, '6' );
application->addScene( buildTutorial7, '7' );
application->addScene( buildTutorial8, '8' );
application->addScene( buildTutorial9, '9' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
void setEnableCollisions(const Geometry *otherGeometry, bool flag)
Specify whether otherGeometry is allowed to collide with this geometry.
const ShapeRefVector & getShapes() const
agx::Vec3 getLocalPosition() const
Class to generate constant water flow in a scene.
Interface for generating water flow.
virtual agx::Vec3 getVelocity(const agx::Vec3 &worldPoint) const
Calculate and return wind at a given position in world.
void setCoefficient(Coefficient type, agx::Real coefficient)
Set pressure drag, viscous drag, lift or buoyancy coefficient.
static void setHydrodynamicCoefficient(WindAndWaterController *controller, agx::RigidBody *rb, Coefficient type, agx::Real coefficient)
Set a hydrodynamic coefficient for a rigid body.
static agxRender::Color IndianRed()
Class for visiting all elements in a tree of Assemblies.
virtual void visit(agx::Constraint *)
virtual void update(float x, float y)
Called once per simulation frame.
const agx::Constraint * getConstraint(const agx::Uuid &uuid) const
Find (linear search) and return a pointer to a Constraint with the given uuid.
void setPreIntegratePositions(bool flag)
Set true to integrate positions at the start of the timestep rather than at the end.
const agxCollide::Geometry * getGeometry(const agx::Name &name) const
Find (linear search) and return named collision Geometry.
virtual void preCollide(const agx::TimeStamp &time)
Called before collision detection is performed in the simulation Implement this method in the derived...
virtual void last(const agx::TimeStamp &time)
Called after a step is taken in the simulation Implement this method in the derived class to get call...
Matrix3x3T< T > transpose() const
Calculate the transpose.
Matrix3x3T< T > inverse() const
Calculate the inverse.
static Vec3T mul(const Vec3T &lhs, const Vec3T &rhs)
Element-wise-multiplication.
Data for rigid body with aero/hydro static/dynamic forces and added mass and inertia matrices.
void getAddedMassAndInertiaDiagonalCoefficients(const agx::RigidBody *rb, agx::Vec3 &addedMassDiagonalCoefficients, agx::Vec3 &addedInertiaDiagonalCoefficients) const
Calculate the diagonal coefficients given the added mass/inertia matrices have been calculated given ...
tutorial_imu.cpp
#include <tuple>
static bool g_isUnitTest = false;
{
auto root = new osg::Group();
simulation->
add(imuBody);
environment->
add(imuBody);
}
);
}
);
new agxSensor::IMUModelAccelerometerAttachment(agx::AffineMatrix4x4(), accelerometerModel),
new agxSensor::IMUModelGyroscopeAttachment(agx::AffineMatrix4x4(), gyroscopeModel),
new agxSensor::IMUModelMagnetometerAttachment(agx::AffineMatrix4x4(), magnetometerModel)
}
));
struct NineDoF
{
struct {
} accelerometer;
struct {
} gyroscope;
struct {
} magnetometer;
};
size_t outputId = 0u;
std::tie(outputId, std::ignore) = imu->getOutputHandler()
->add<NineDoF,
.value_or(std::make_pair(0u, nullptr));
if (!g_isUnitTest) {
size_t outputId)
{
->view<NineDoF>(outputId);
for (const NineDoF& v : output) {
}.length();
"Inertial Measurement Unit");
}.length();
}.length();
}
},
simulation,
imu,
outputId);
}
}
{
auto root =
new osg::Group();
simulation->
add(accelerometerBody);
environment->
add(accelerometerBody);
environment->
add(accelerometer);
struct XYZ
{
};
size_t outputId = 0u;
std::tie(outputId, std::ignore) = accelerometer->getOutputHandler()
->add<XYZ,
.value_or(std::make_pair(0u, nullptr));
if (!g_isUnitTest) {
size_t outputId)
{
->view<XYZ>(outputId);
for (const XYZ& v : output) {
}
},
simulation,
accelerometer,
outputId);
}
}
{
auto root =
new osg::Group();
simulation->
add(gyroscopeBody);
environment->
add(gyroscopeBody);
environment->
add(gyroscope);
struct XYZ
{
};
size_t outputId = 0u;
std::tie(outputId, std::ignore) = gyroscope->getOutputHandler()
->add<XYZ,
.value_or(std::make_pair(0u, nullptr));
if (!g_isUnitTest) {
size_t outputId)
{
->view<XYZ>(outputId);
for (const XYZ& v : output) {
}
},
simulation,
gyroscope,
outputId);
}
}
{
auto root =
new osg::Group();
simulation->
add(magnetometerBody);
environment->
add(magnetometerBody);
environment->
add(magnetometer);
struct XYZ
{
};
size_t outputId = 0u;
std::tie(outputId, std::ignore) = magnetometer->getOutputHandler()
->add<XYZ,
.value_or(std::make_pair(0u, nullptr));
if (!g_isUnitTest) {
size_t outputId)
{
->view<XYZ>(outputId);
for (const XYZ& v : output) {
}
},
simulation,
magnetometer,
outputId);
}
}
int main(int argc, char** argv)
{
std::cout <<
"\tLidar tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
g_isUnitTest = argc >= 2;
try {
application->addScene(buildTutorial1, '1');
application->addScene(buildTutorial2, '2');
application->addScene(buildTutorial3, '3');
application->addScene(buildTutorial4, '4');
if (application->init(argc, argv))
return application->run();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
static void add(agx::Real yValue, const agx::String &curve, const agx::String &window="")
Add y-value to a curve, named curve, in a window named window, where the x-values are integers,...
Base accelerometer model describing the fundamental parameters, such as measurement range and zero bi...
Accelerometer instance class defined by a frame/transform and a model.
AccelerometerOutputHandler * getOutputHandler() const
View for reading sections of a binary packed buffer of the specified template parameter type.
Sensor environment implementation where different sensors collects data given a set of added objects,...
void setMagneticField(MagneticField *magneticField)
Set environment magnetic field.
bool add(agxCollide::Shape *shape)
Add shape to be visible to sensors in this sensor environment.
static Environment * getOrCreate(agxSDK::Simulation *simulation)
Returns an existing or creates and initializes a new instance of a sensor environment given a simulat...
Applies an offset to the zero rate bias depending on the linear acceleration that the gyroscope is ex...
Base gyroscope model describing the fundamental parameters, such as measurement range and zero bias,...
Gyroscope instance class defined by a frame/transform and a model.
GyroscopeOutputHandler * getOutputHandler() const
IMU model describing which sensors are attached on this certain kind of IMU.
@ ACCELEROMETER_Z_F64
1x 64-bit floating point accelerometer z-axis output.
@ GYROSCOPE_Y_F64
1x 64-bit floating point gyroscope y-axis output.
@ MAGNETOMETER_Y_F64
1x 64-bit floating point magnetometer y-axis output.
@ ACCELEROMETER_X_F64
1x 64-bit floating point accelerometer x-axis output.
@ ACCELEROMETER_Y_F64
1x 64-bit floating point accelerometer y-axis output.
@ MAGNETOMETER_Z_F64
1x 64-bit floating point magnetometer z-axis output.
@ GYROSCOPE_X_F64
1x 64-bit floating point gyroscope x-axis output.
@ MAGNETOMETER_X_F64
1x 64-bit floating point magnetometer x-axis output.
@ GYROSCOPE_Z_F64
1x 64-bit floating point gyroscope z-axis output.
Inertial measurement unit (IMU) instance class defined by a frame/transform and a model.
IMUOutputHandler * getOutputHandler() const
Base magnetometer model describing the fundamental parameters, such as measurement range and zero flu...
Magnetometer instance class defined by a frame/transform and a model.
MagnetometerOutputHandler * getOutputHandler() const
Specification of cross sensitivity between tree axis.
Applies a total Gaussian noise to the triaxial signal based on a specified noise RMS.
@ Y_VALUE_F64
1x 64-bit floating point y-axis output.
@ Z_VALUE_F64
1x 64-bit floating point z-axis output.
@ X_VALUE_F64
1x 64-bit floating point x-axis output.
Range specification along three axis.
Applies Gaussian noise to the triaxial signal based on a specified noise spectral density and the sam...
tutorial_io.cpp
#include "tutorialUtils.h"
{
osg::Group* root = new osg::Group();
if (!meshReader->readFile( fileName ))
for(size_t n=0; n < 10; n++ )
{
if (!num_convex)
{
}
for(; shapeIt != convexShapes.
end(); ++shapeIt)
{
dynamicRBGeometry->add( shape );
dynamicRB->
add( dynamicRBGeometry );
dynamicRBGeometry->setRotation( euler );
}
simulation->
add( dynamicRB );
}
simulation->
add( staticRB );
}
{
osg::Group*
root =
new osg::Group;
if (image)
{
hf = hfg->createHeightFieldFromImage(image, realSizeX, realSizeY, lowestHeight, highestHeight);
heightFieldGeometry->add(hf);
heightFieldBody->
add( heightFieldGeometry );
simulation->
add( heightFieldBody );
}
else
size_t nX=10;
size_t nY=10;
for ( size_t i = 0; i < nX; ++i )
for ( size_t j = 0; j < nY; ++j ) {
sphereBody->
add(geometry);
1.0 ));
simulation->
add( sphereBody );
}
}
{
osg::Group*
root =
new osg::Group;
simulation->
add( kinematicBody );
simulation->
add( cylinder );
simulation->
add( boxBody );
simulation->
add( staticRB );
}
{
public:
m_root(
root), m_geomUuid(geomUuid), m_geomName(geomName)
{
}
osg::ref_ptr<osg::Group> m_root;
};
{
if (className == "agxCollide::Geometry")
{
if (geometry)
{
std::cerr <<
"Creating visual for Geometry: " << geometry->
getName() << std::endl;
if (geometry->
getName() == m_geomName)
{
if (geometry->
getUuid() == m_geomUuid)
std::cerr << "Uuid for restored geometry matches!!: " << m_geomUuid << std::endl;
else
std::cerr << "ERROR: Uuid for restored geometry does not match!!" << std::endl;
}
}
}
}
{
osg::Group*
root =
new osg::Group;
const agx::String cylinderGeometryName =
"CylinderGeometry";
cylGeom->setName(cylinderGeometryName);
agx::Uuid cylinderGeometryUuid = cylGeom->getUuid();
simulation->
add(cylinder);
boxGeom->setName("BoxGeometry");
simulation->
add(boxBody);
staticBoxGeom->setName("StaticBoxGeometry");
staticRB->
add( staticBoxGeom );
simulation->
add(staticRB);
#if 1
root->removeChild(0,
root->getNumChildren());
std::stringstream os(std::ios_base::out | std::ios_base::binary);
bool binaryFormat = true;
simulation->
write(os, binaryFormat);
os.seekg(0);
std::stringstream is(std::ios_base::in | std::ios_base::binary);
is.str(os.str());
simulation->
addRestoreListener(
new MyRestoreListener(root, cylinderGeometryUuid, cylinderGeometryName));
simulation->
read(is, binaryFormat);
#endif
}
int main( int argc, char** argv )
{
std::cout <<
"\tIO tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial4, '4' );
application->addScene( buildTutorial5, '5' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
const agx::Name & getName() const
Get the name.
This class contains all Geometries and performs Broad Phase and Narrow Phase collision detection to c...
ImageReader for png-files.
size_t read(const agx::String &filename, agxSDK::Assembly *parent=nullptr, agx::UInt selection=READ_DEFAULT)
Read serialized simulation from the specified file and append to this simulation.
void setSpace(agxCollide::Space *space)
Replace the current Collision space with space.
CleanupSelectionMask
Specification of what should be cleaned up from a simulation during a call to the cleanup() method.
@ ASSEMBLIES
Remove all assemblies.
@ SYSTEM
Remove the dynamic system.
@ MATERIALS
Remove all materials.
@ SPACE
Remove the collision space.
void cleanup(agx::UInt selection=CLEANUP_ALL, bool fast=false)
This method will remove/cleanup selected parts of the simulation.
void setDynamicsSystem(agx::DynamicsSystem *system)
Replace the current DynamicsSystem with system.
size_t write(const agx::String &filename) const
Serialize the simulation to the specified file.
bool addRestoreListener(agxStream::RestoreListener *listener)
Add a restore listener to the Simulation.
Class for listening to the restore of Serializable objects.
virtual void restore(Serializable *obj, const agx::String &className)=0
Virtual method called for each restored object during the de-serialization process.
This class is an abstract base class for all classes that can be stored and retrieved from an Archive...
agx::Uuid getUuid() const
A UUID, or Universally unique identifier, is intended to uniquely identify information in a distribut...
AGXPHYSICS_EXPORT bool writeFile(FileType type, std::ostream &outStream, agxSDK::Simulation *simulation, agx::Vec3 from=agx::Vec3(), agx::Vec3 at=agx::Vec3(), agx::Vec3 up=agx::Vec3(), double fovy=0, double aspectRatio=0)
Utility function for write a simulation to a stream.
static AGXCORE_EXPORT agx::String getTmpFilename()
tutorial_lidar.cpp
namespace tutorial_helpers
{
{
};
}
return bodies;
}
{
public:
range )
{
}
virtual ~CustomSingleRayLidarModel() = default;
};
}
{
using namespace tutorial_helpers;
auto root =
new osg::Group();
}
simulation->
add(lidarRb);
root->addChild(renderer);
struct IsHitOutput
{
int32_t isHit;
};
lidar->getOutputHandler()->add<IsHitOutput,
{
lidar->getOutputHandler()->view<IsHitOutput>().size() ) );
},
simulation,
lidar,
app );
}
{
auto root =
new osg::Group();
environment->
add( lidar );
auto bodies = tutorial_helpers::createPrimitiveShapeCircle( 25.0, 3.0 );
for ( const auto& rb : bodies ) {
}
{
{
}
float intensity() const
{
}
};
lidar->getOutputHandler()->add<HitPointAndIntensity,
{
auto hitPointAndIntensityView = lidar->getOutputHandler()->view<HitPointAndIntensity>();
for ( const auto& hitPointAndIntensity : hitPointAndIntensityView ) {
hitPointAndIntensity.position(),
0.003,
const float intensityScaled = std::min( 1.5E2f * hitPointAndIntensity.intensity(), 1.0f );
0.012,
intensityScaled,
0.3f } );
}
},
simulation,
lidar );
struct HitPointAndNormal
{
};
lidar->getOutputHandler()->add<HitPointAndNormal,
{
auto hitPointAndNormalView = lidar->getOutputHandler()->view<HitPointAndNormal>();
for ( const auto& hitPointAndNormal : hitPointAndNormalView ) {
agx::Vec3{ hitPointAndNormal.position + 0.25f * hitPointAndNormal.
normal },
0.006,
}
},
simulation,
lidar );
}
namespace myComponents
{
{
static constexpr float DefaultValue = 1.0f;
using agxRender::Color::operator=;
};
struct NameComponent : std::string
{
static constexpr char DefaultValue{};
using std::string::string;
using std::string::operator=;
};
struct MyComponentsInterface
{
template<typename T>
static MyComponentsInterface get( T& instance )
{
return { agxSensor::manageRtEntityId( instance ) };
}
MyComponentsInterface() = default;
: m_entity( entity )
{
}
std::string getName() const
{
return agxSensor::RtRegistry::storage<NameComponent>().get( m_entity );
}
void setName( std::string name )
{
agxSensor::RtRegistry::getOrCreateStorage<NameComponent>()[ m_entity ] = name;
}
{
return agxSensor::RtRegistry::storage<ColorComponent>().get( m_entity );
}
{
agxSensor::RtRegistry::getOrCreateStorage<ColorComponent>()[ m_entity ] = color;
}
private:
};
}
{
auto root =
new osg::Group();
auto bodies = tutorial_helpers::createPrimitiveShapeCircle( 25.0, 3.0 );
for ( const auto& rb : bodies ) {
}
using namespace myComponents;
for ( size_t i = 0u; i < bodies.size(); ++i ) {
const auto& rb = bodies[ i ];
auto components = MyComponentsInterface::get( rb );
components.setName( rb->
getGeometries()[ 0 ]->getShapes()[ 0 ]->getClassName() );
components.setColor( colors[ i ] );
const auto rbId = agxSensor::getRtEntityId( rb );
const auto geometryId = agxSensor::getRtEntityId( rb->
getGeometries()[ 0 ] );
const auto shapeId = agxSensor::getRtEntityId( rb->
getGeometries()[ 0 ]->getShapes()[ 0 ] );
std::cout << "ERROR: Unexpected rigid body entity id is agxSensor::NullRtEntityId.\n";
else if ( rbId != geometryId )
std::cout << "ERROR: Unexpected geometry entity id is not the same as its rigid body.\n";
else if ( geometryId != shapeId )
std::cout << "ERROR: Unexpected shape entity id is not the same as its geometry.\n";
}
simulation->
add( lidarRb );
new tutorial_helpers::CustomSingleRayLidarModel() );
environment->
add( lidar );
struct HitEntityId
{
int32_t entityId;
};
lidar->getOutputHandler()->add<HitEntityId,
lidarRenderer->setHitPointScale( 4.0f );
root->addChild( lidarRenderer );
{
auto entityIdView = lidar->getOutputHandler()->view<HitEntityId>();
if ( entityIdView.empty() ) {
sd->
setText( 0,
"No Hit", noColorColor );
sd->
setText( 1,
"", noColorColor );
sd->
setText( 2,
"", noColorColor );
}
else {
const auto hitEntityId = entityIdView[ 0 ].entityId;
sd->
setText( 0,
"Hit:", noColorColor );
}
},
simulation,
app,
lidar );
}
{
auto root =
new osg::Group();
const auto assureIdenticalSurfaceMaterial = []( const auto instance1, const auto instance2 )
{
if ( !sm1.isValid() ) {
std::cout << "ERROR: Surface material for instance \""
<< instance1->template asSafe<agxStream::Serializable>()->getClassName()
<< "\" is invalid.\n";
return;
}
if ( !sm2.isValid() ) {
std::cout << "ERROR: Surface material for instance \""
<< instance2->template asSafe<agxStream::Serializable>()->getClassName()
<< "\" is invalid.\n";
return;
}
if ( sm1 != sm2 ) {
std::cout << "ERROR: The surface material between instance \""
<< instance1->template asSafe<agxStream::Serializable>()->getClassName()
<< "\" and \""
<< instance2->template asSafe<agxStream::Serializable>()->getClassName()
<< "\" isn't identical.\n";
}
};
const auto assureIsOwner = []( const auto instance )
{
if ( !agxSensor::isRtMaterialOwner( instance ) )
std::cout << "ERROR: " << "Instance type "
<< instance->template asSafe<agxStream::Serializable>()->getClassName()
<< " isn't owner of the surface material.\n";
};
assureIdenticalSurfaceMaterial( rb, geometry );
for (
const auto& shape : geometry->
getShapes() ) {
assureIdenticalSurfaceMaterial( geometry, shape );
assureIdenticalSurfaceMaterial( rb, shape );
}
}
for ( const auto& g2Shape : g2->getShapes() )
assureIdenticalSurfaceMaterial( g2, g2Shape );
assureIsOwner( rb );
assureIdenticalSurfaceMaterial( rb, g1 );
assureIdenticalSurfaceMaterial( rb, g1->getShapes()[ 0 ] );
assureIsOwner( g2 );
assureIdenticalSurfaceMaterial( g2, g2->getShapes()[ 0 ] );
assureIsOwner( g2->getShapes()[ 1 ] );
assureIsOwner( g3 );
assureIdenticalSurfaceMaterial( g3, g3->getShapes()[ 0 ] );
assureIsOwner( g3->getShapes()[ 1 ] );
assureIsOwner( g3->getShapes()[ 2 ] );
environment->
add( lidar );
renderer->setIntensityScale( 10.0f );
root->addChild( renderer );
}
int main(int argc, char** argv)
{
std::cout <<
"\tLidar tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
return 0;
application->addScene(buildTutorial1, '1');
application->addScene(buildTutorial2, '2');
application->addScene(buildTutorial3, '3');
application->addScene(buildTutorial4, '4');
if (application->init(argc, argv))
return application->run();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
A right circular cone shape for geometric intersection tests.
A right circular hollow cone shape for geometric intersection tests.
A hollow cylinder shape for geometric intersection tests.
Internal class used by agxWire::Wire.
Lidar point cloud renderer.
static agxRender::Color LightGray()
Color()
Default constructor. Set color to 0,0,0,1.
void setEnable(bool flag)
Enable/disable the whole rendering system.
A simple 360 degree sweeping lidar model of a vertical field of view of +/- 50 degrees and a resoluti...
Specialized class for the Ouster OS1 lidar model.
Base lidar model describing the fundamentals, such as ray pattern, range and beam properties,...
Ray pattern with only one single ray along the local z-axis of the Lidar using it.
Lidar instance class defined by a frame/transform and a model.
static bool verifyRaytraceSupported()
Checks if raytracing is supported on this device/platform.
static RtLambertianOpaqueMaterial create()
Create a new lambertian opaque surface material in the raytrace environment.
RtLambertianOpaqueMaterial setReflectivity(float reflectivity)
Assign new surface reflectivity of this material.
static RtLambertianOpaqueMaterial get(const agxCollide::Shape *shape)
@ PADDING_32
32-bit padding
@ XYZ_VEC3_F32
3x 32-bit floating point position of the hit.
@ IS_HIT_I32
1x 32-bit flag (0 or 1) indicating if the hit is a hit or miss.
@ NORMAL_VEC3_F32
3x 32-bit float normal vector of the entity surface where the hit-point is located.
@ INTENSITY_F32
1x 32-bit floating point intensity of the hit.
@ ENTITY_ID_I32
1x 32-bit int index of the entity being hit.
RtSurfaceMaterial assignTo(agxCollide::Shape *shape) const
Assign/associate a surface material to the given shape.
bool setParent(agx::Frame *frame)
Set the parent of this Frame.
Vec3T< T > asVec3() const
constexpr RtEntityId NullRtEntityId
Null entity id.
RtEntityId
Entity id > 0 to access user data given raytrace results including ENTITY_ID_I32.
constexpr RtEntityIdIndex MaxRtEntityIdIndex
Index of the maximum enetity id.
tutorial_listeners.cpp
#include "tutorialUtils.h"
{
public:
: m_rb( rb )
{
}
virtual bool keyboard(
int key,
unsigned int modKeyMask,
float ,
float ,
bool keydown )
{
bool handled = false;
if ( !m_rb.isValid() )
return false;
switch ( key )
{
if ( leftCtrlDown )
m_rb->setAngularVelocity( m_rb->getFrame()->transformVectorToWorld(
agx::Vec3( 0, -angularSpeed*
int( keydown ), 0 ) ) );
else
m_rb->setAngularVelocity( m_rb->getFrame()->transformVectorToWorld(
agx::Vec3( 0, 0, -angularSpeed*
int( keydown ) ) ) );
handled = true;
break;
if ( leftCtrlDown )
m_rb->setAngularVelocity( m_rb->getFrame()->transformVectorToWorld(
agx::Vec3( 0, angularSpeed*
int( keydown ), 0 ) ) );
else
m_rb->setAngularVelocity( m_rb->getFrame()->transformVectorToWorld(
agx::Vec3( 0, 0, angularSpeed*
int( keydown ) ) ) );
handled = true;
break;
m_rb->setAngularVelocity( m_rb->getFrame()->transformVectorToWorld(
agx::Vec3( -angularSpeed*
int( keydown ), 0, 0 ) ) );
handled = true;
break;
m_rb->setAngularVelocity( m_rb->getFrame()->transformVectorToWorld(
agx::Vec3( angularSpeed*
int( keydown ), 0, 0 ) ) );
handled = true;
break;
case 'j':
if ( keydown ) {
handled = true;
}
break;
}
return handled;
}
virtual bool mouseDragged( MouseButtonMask ,
float ,
float ) {
return false; }
virtual bool mouseMoved(
float ,
float ) {
return false; }
virtual bool mouse( MouseButtonMask , MouseState ,
float ,
float ) {
return false; }
virtual void update(
float ,
float ) {}
private:
};
{
osg::Group*
root =
new osg::Group();
simulation->
add( kinematicCylinder );
simulation->
addEventListener(
new SimpleKeyboardListener( kinematicCylinder ) );
}
{
public:
MyContactListener()
: m_impactCounter( 0 )
{
setMask( IMPACT | CONTACT | SEPARATION );
}
{
++m_impactCounter;
std::cout << "Number of impacts: " << m_impactCounter;
if ( m_impactCounter > 2 ) {
std::cout << " is larger than 2, REMOVING CONTACT IMMEDIATELY!" << std::endl;
}
std::cout << std::endl;
}
{
if ( m_impactCounter > 2 )
}
{
if ( m_impactCounter > 2 )
return;
std::cout << "Separation: " << cd.first->getName() << " <-> " << cd.second->getName() << std::endl;
}
private:
size_t m_impactCounter;
};
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 15, 15, 0.5 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
boxGeometry->setName( "boxGeometry" );
sphereGeometry->setName( "sphereGeometry" );
sphere->
add( sphereGeometry );
simulation->
add( sphere );
MyContactListener *listener = new MyContactListener();
}
{
public:
: m_rb( rb ), m_targetPosition( targetPosition )
{
setMask( PRE_COLLIDE | POST_STEP );
if ( m_rb.isValid() )
m_rb->setPosition( m_targetPosition );
}
{
if ( !m_rb.isValid() ) {
return;
}
if (
agx::equalsZero( (m_targetPosition - m_rb->getPosition()).length2() ) ) {
m_targetPosition = m_targetPosition ^
agx::Vec3( 0, 1, 0 );
m_rb->moveTo( targetTransform, 1.0 );
}
}
{
std::cout << "Current position: " << m_rb->getPosition() << std::endl;
std::cout << "Target position: " << m_targetPosition << std::endl;
std::cout << "Distance left: " << ( m_rb->getPosition() - m_targetPosition ).length() << std::endl << std::endl;
}
private:
};
{
osg::Group*
root =
new osg::Group();
simulation->
add( kinematicBody );
simulation->
add(
new MyStepEventListener( kinematicBody,
agx::Vec3( -3, 0, -3 ) ) );
}
{
public:
OnSensorVelocityScaler(
agx::Real speedScale )
: m_speedScale( speedScale )
{
}
{
}
}
}
{
if ( cd.first->isSensor() && cd.second->getRigidBody() )
cd.second->getRigidBody()->setVelocity( m_incidentVelocity );
else if ( cd.second->isSensor() && cd.first->getRigidBody() )
cd.first->getRigidBody()->setVelocity( m_incidentVelocity );
}
private:
};
{
osg::Group*
root =
new osg::Group();
simulation->
add( kinematicBody );
simulation->
add(
new MyStepEventListener( kinematicBody, initialPathPosition ) );
simulation->
add( sensor1 );
simulation->
add( sensor2 );
sensor1->setSensor( true );
sensor2->setSensor( true );
sensor1->setPosition(
agx::Vec3( 0, 0, initialPathPosition.z() ) );
sensor2->setPosition(
agx::Vec3( 0, 0,-initialPathPosition.z() ) );
OnSensorVelocityScaler *listener = new OnSensorVelocityScaler( 0.5 );
listener = new OnSensorVelocityScaler( 2.0 );
}
{
public:
public:
:
agxSDK::ContactEventListener(
agxSDK::ContactEventListener::IMPACT |
agxSDK::ContactEventListener::CONTACT )
{
}
{
return storeContact( gc );
}
{
return storeContact( gc );
}
GeometryContactContainer& getContacts() { return m_contacts; }
const GeometryContactContainer& getContacts() const { return m_contacts; }
protected:
virtual ~MyContactEventListener() {}
{
m_contacts.push_back( *gc );
}
protected:
GeometryContactContainer m_contacts;
};
{
public:
ContactStepListener( MyContactEventListener* contactEvent )
:
agxSDK::StepEventListener(
agxSDK::StepEventListener::POST_STEP ),
m_contactEvent( contactEvent )
{
}
{
if ( m_contactEvent && m_contactEvent->getSimulation() == 0L )
}
{
}
{
MyContactEventListener::GeometryContactContainer& contacts = m_contactEvent->getContacts();
for ( size_t i = 0; i < contacts.size(); ++i ) {
continue;
for (
size_t j = 0; j < gc.
points().size(); ++j ) {
else
std::cout << "Contact point " << j << ":" << std::endl;
}
std::cout <<
"Normal force sum: " << normalForceSum <<
" N. ((m1 + m2)g = " << (1 + 1) *
agx::GRAVITY_ACCELERATION <<
" N.)" << std::endl;
}
std::cout << std::endl;
contacts.clear();
}
protected:
virtual ~ContactStepListener() {}
protected:
};
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 15, 15, 0.5 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
boxGeometry->setName( "boxGeometry" );
box1->
add( box1Geometry );
box2->
add( box2Geometry );
simulation->
add(
new ContactStepListener(
new MyContactEventListener(
new agxSDK::GeometryFilter( boxGeometry, box1Geometry ) ) ) );
}
int main( int argc, char** argv )
{
std::cout <<
"\tListener tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
application->addScene( buildTutorial4, '4' );
application->addScene( buildTutorial5, '5' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
bool isSensor() const
Return true if geometry is a sensor.
Abstract base class that implements a filter that selects which events should trigger a Listener.
virtual bool mouseDragged(MouseButtonMask buttonMask, float x, float y)
Called when one or more mouse buttons are pressed and moved.
virtual bool mouseMoved(float x, float y)
virtual bool mouse(MouseButtonMask button, MouseState state, float x, float y)
bool addEventListener(agxSDK::EventListener *listener, int priority=EventManager::DEFAULT_PRIORITY)
Add an event listener The listener will get a call to its addNotification method if adding was succes...
static void createAttachmentBase(const agx::Vec3d &N, agx::Vec3d &U, agx::Vec3d &V)
Given vector N, create orthonormal base.
virtual void calculateTangentPlane(const agxCollide::Geometry *geometry1, const agxCollide::Geometry *geometry2, const agx::Vec3 &point, const agx::Vec3 &normal, const agx::Real depth, agx::Vec3 &ret_u, agx::Vec3 &ret_v) const
Calculates friction plane given normal.
static constexpr Real GRAVITY_ACCELERATION
AGXCORE_EXPORT const Real REAL_SQRT_EPSILON
tutorial_machine_reconfiguration.cpp
#include <initializer_list>
struct RandomPos {
std::vector<std::string> jointNames;
: min(_min), max(_max), jointNames(names)
{}
};
{
public:
: m_col(collection), m_refBody(refBody)
{
}
virtual bool keyboard(
int key,
unsigned int ,
float ,
float ,
bool keydown)
override
{
bool handled = false;
if (key == 'A' and keydown) {
for (const auto& item : m_items) {
for (const auto& name : item.jointNames) {
auto joint = m_col->getConstraint(name);
if (joint) {
}
else {
}
}
}
bool status = m_rr.computeTransforms(m_col, m_refBody, cpv, results);
if (!status) {
}
else {
m_rr.applyTransforms(m_col, cpv, results, true);
}
handled = true;
}
return handled;
}
void addRandomization(struct RandomPos rp) {
m_items.emplace_back(rp);
}
private:
std::vector<struct RandomPos> m_items;
};
{
auto root =
new osg::Group();
auto filename = fpc.
find(modelName);
if (filename == "") {
}
simulation->
read(filename, wheel_loader);
col->add(wheel_loader);
auto rp = new RandomizePose(col, refBody);
rp->addRandomization(RandomPos(-0.4, 0.4, {"WaistHinge"} ));
rp->addRandomization(RandomPos(-0.8, 0.015, {"LeftLowerPrismatic", "RightLowerPrismatic"}));
rp->addRandomization(RandomPos(-0.2, 0.2, {"CenterPrismatic"}));
}
int main(int argc, char** argv)
{
"\tTutorial Machine Reconfiguration (agxModel::ReconfigureRequest)\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
if ( application->init(argc, argv))
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
A Collection is a collection of basic simulation objects, such as rigid bodies, constraints,...
size_t find(const AgXString &sub, int start=0, int end=MAX_32BIT_INT) const
static AGXCORE_EXPORT agx::String convertToNativeFilePath(const agx::String &filePath)
Struct that specifies a position for a constraint angle.
tutorial_material.cpp
#include "tutorialUtils.h"
{
osg::Group* root = new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 15, 15, 0.5 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
rb->
add( sphereGeometry );
simulation->
add( sphereMaterial );
sphereGeometry->setMaterial( sphereMaterial );
boxGeometry->setMaterial( sphereMaterial );
return root;
}
{
osg::Group* root = new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 15, 8, 0.5 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
for ( size_t i = 0; i < rbs.size(); ++i ) {
rbs[ i ]->setRotation( boxGeometry->getRotation() );
simulation->
add( rbs[ i ] );
}
rbs[ 0 ]->setPosition( boxGeometry->getFrame()->transformPointToWorld(
agx::Vec3( -boxGeometryHalfExtent.x() + boxHalfExtent.x(),
-5,
boxGeometryHalfExtent.z() + boxHalfExtent.z() ) ) );
rbs[ 1 ]->setPosition( boxGeometry->getFrame()->transformPointToWorld(
agx::Vec3( -boxGeometryHalfExtent.x() + boxHalfExtent.x(),
0,
boxGeometryHalfExtent.z() + boxHalfExtent.z() ) ) );
rbs[ 2 ]->setPosition( boxGeometry->getFrame()->transformPointToWorld(
agx::Vec3( -boxGeometryHalfExtent.x() + boxHalfExtent.x(),
5,
boxGeometryHalfExtent.z() + boxHalfExtent.z() ) ) );
rbs[ 0 ]->getGeometries().front()->setMaterial( m1 );
rbs[ 1 ]->getGeometries().front()->setMaterial( m2 );
rbs[ 2 ]->getGeometries().front()->setMaterial( m3 );
}
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 15, 8, 0.5 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
sphere1->
add( sphere1Geometry );
simulation->
add( sphere1 );
sphere2->
add( sphere2Geometry );
simulation->
add( sphere2 );
boxGeometry->setMaterial( boxGeometryMat );
simulation->
add( boxGeometryMat );
sphere1Geometry->setMaterial( sphere1Mat );
simulation->
add( sphere1Mat );
sphere2Geometry->setMaterial( sphere2Mat );
simulation->
add( sphere2Mat );
simulation->
add( boxSphere1Contact );
simulation->
add( boxSphere2Contact );
}
{
public:
{
}
{
if ( !m_rb.isValid() )
return;
std::cout << "Name: " << m_rb->getName() << ": Speed: " << m_rb->getVelocity().length() << ": Velocity: " << m_rb->getVelocity() << std::endl;
}
private:
};
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 15, 8, 0.5 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
boxGeometry->setSurfaceVelocity( (
agx::Vec3f)surfaceVelocity );
simulation->
add( rbBox1 );
rbBox2->
add( rbBox2Geometry );
simulation->
add( rbBox2 );
rbBox2Geometry->setSurfaceVelocity(
agx::Vec3f( 5, (
float)-surfaceVelocity.
y(), 0 ) );
simulation->
add(
new PrintRBVelocityAndSpeed( rbBox1 ) );
}
{
public:
: m_position( position ), m_halfExtent( halfExtent ), m_time( time ), m_currentTime( 0 ), m_root(
root )
{
}
{
createAndAdd();
}
{
if ( m_currentTime > m_time ) {
createAndAdd();
m_currentTime = 0;
return;
}
}
private:
void createAndAdd()
{
std::stringstream ss;
ss <<
"Material" << material->
getIndex();
}
private:
osg::observer_ptr< osg::Group > m_root;
};
{
public:
: m_position( position ), m_radius( radius ), m_height( height ), m_time( time ), m_currentTime( 0 ), m_root(
root )
{
}
{
createAndAdd();
}
{
if ( m_currentTime > m_time ) {
createAndAdd();
m_currentTime = 0;
return;
}
}
private:
void createAndAdd()
{
std::stringstream ss;
ss <<
"Material" << material->
getIndex();
}
private:
osg::observer_ptr< osg::Group > m_root;
};
{
public:
{
for (
size_t i = 0; i < bodies.
size(); ++i ) {
if ( bodies[ i ]->getPosition().z() < m_z )
}
while ( !remove.
empty() ) {
}
}
private:
};
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 15, 10, 0.5 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
convSec1RB->
add( convSec1Geom );
simulation->
add( convSec1RB );
convSec2RB->
add( convSec2Geom );
simulation->
add( convSec2RB );
convSec1Geom->setSurfaceVelocity(
agx::Vec3f(3,0,0) );
convSec2Geom->setSurfaceVelocity(
agx::Vec3f(3,0,0) );
simulation->
add(
new KillerObject( -100 ) );
}
{
osg::Group*
root =
new osg::Group();
agx::Vec3 boxGeometryHalfExtent( 15, 10, 0.5 );
boxGeometry->setPosition( boxGeometry->getPosition() -
agx::Vec3( 0, 0, boxGeometryHalfExtent.z() ) );
simulation->
add( boxGeometry );
convSec1RB->
add( convSec1Geom );
simulation->
add( convSec1RB );
convSec2RB->
add( convSec2Geom );
simulation->
add( convSec2RB );
convSec1Geom->setSurfaceVelocity(
agx::Vec3f( 3, 0, 0 ) );
convSec2Geom->setSurfaceVelocity(
agx::Vec3f( 3, 0, 0 ) );
simulation->
add(
new SimpleCylinderSpawner( convSec1RB->
getPosition() +
agx::Vec3( -5, 0, 0.5f + 1.0f ), 1, 1, 1.5f, root ) );
simulation->
add(
new KillerObject( -100 ) );
}
{
public:
{
if (!m_textureImage->read( texture ))
}
{
}
{
if (!image)
return 0.5;
unsigned int iy = (width-1)-agx::clamp<unsigned int>( (
unsigned int)(width*point.
y()/10), (
unsigned int)0, (width-1));
unsigned int ix = agx::clamp<unsigned int>((
unsigned int)(height*point.
x())/10, (
unsigned int)0,height-1);
agx::Real v = m_textureImage->getValueR(ix,iy);
return v;
}
{
for(size_t i=0; i < 5; i++)
m_decorator->setText((int)i, "" );
for(
size_t i=0; i < gc->
points().size(); i++)
{
if (i < 5)
m_decorator->setText((int)i, str );
}
}
};
{
osg::Group*
root =
new osg::Group();
agx::String surfaceTexture =
"data/textures/frictionmap.png";
agx::Vec3 boxGeometryHalfExtent( 5, 5, 0.5 );
boxGeometry->setPosition( boxGeometryHalfExtent );
simulation->
add( boxGeometry );
smallBox->
add(smallBoxGeometry);
simulation->
add( smallBox );
}
int main( int argc, char** argv )
{
std::cout <<
"\tMaterial tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
application->addScene( buildTutorial4, '4' );
application->addScene( buildTutorial5, '5' );
application->addScene( buildTutorial6, '6' );
application->addScene( buildTutorial7, '7' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
Class to represent Images.
unsigned int getHeight() const
unsigned int getWidth() const
agx::UInt32 getIndex() const
This index is given at creation of this object.
void setYoungsModulus(Real value)
Set the Young's modulus of the material, same as spring coefficient k.
agx::TimeGovernor * getTimeGovernor()
void setName(const agx::Name &name)
Set the name of the material.
virtual Real getTimeStep() const
tutorial_mergeSplitHandler.cpp
namespace
{
{
return rb;
}
{
agx::Vec3 currentPos( -boxLength *
static_cast<agx::Real>(numBoxesAtBottomRow), 0.0, boxHeight );
for ( int i = int( numBoxesAtBottomRow ); i > 0; --i ) {
auto& bodies = result.
back();
for ( int j = i; j > 0; --j ) {
currentPos.x() += (
agx::Real)2.0 * boxLength + eps;
}
currentPos.x() = boxLength * ( - i + 1 );
currentPos.z() += (
agx::Real)2.0 * boxHeight + eps;
}
return result;
}
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( ground );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( ground );
groundWorldLock->setCompliance( 1.0E-11 );
simulation->
add( groundWorldLock );
{
if ( isMerged )
else
};
{
}, simulation );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( ground );
auto rows = ::createPyramid(
agx::Vec3( 0, 0, 0 ),
agx::Vec3( 0.5, 0.5, 0.5 ), 8, simulation, root );
for ( auto firstRowRb : rows[ 0 ] )
for (
agx::UInt row = 0; row < rows.size() - 1; ++row ) {
const auto& thisRow = rows[ row ];
const auto& aboveRow = rows[ row + 1 ];
for (
agx::UInt aboveRbIndex = 0; aboveRbIndex < aboveRow.size(); ++aboveRbIndex ) {
auto rb = aboveRow[ aboveRbIndex ];
for (
agx::UInt thisRowRbIndex = aboveRbIndex; thisRowRbIndex <= aboveRbIndex + 1; ++thisRowRbIndex )
}
}
simulation->
add( mergedBody );
for ( const auto& row : rows )
for ( auto rb : row )
auto sendBullet = [ simulation,
root ]()
{
};
sendBullet();
{
application->
getSceneDecorator()->setText( 1,
"2. Merged body registered to the handler. Automatic split is active." );
}
sendBullet();
application->
getSceneDecorator()->
setText( 2,
"3. Sending second bullet - it's up to the handle to split the merged objects!" );
}
}, simulation );
}
namespace tutorial4
{
{
public:
protected:
virtual void separation(
const agx::TimeStamp& t, agxCollide::GeometryPair& geometryPair )
override;
private:
private:
};
:
agxSDK::ContactEventListener(
agxSDK::ContactEventListener::IMPACT |
agxSDK::ContactEventListener::SEPARATION,
m_sensor( sensor )
{
}
{
handleGeometry( findOtherGeometry( geometryContact->
geometry( 0 ), geometryContact->
geometry( 1 ) ),
true );
}
void ExplicitSplitSensorListener::separation(
const agx::TimeStamp& , agxCollide::GeometryPair& geometryPair )
{
handleGeometry( findOtherGeometry( geometryPair.first, geometryPair.second ), false );
}
{
return geometry2 == m_sensor ? geometry1 : geometry2;
}
{
agxAssert( geometry !=
nullptr && geometry != m_sensor );
return;
if ( isImpact ) {
}
else
}
}
{
osg::Group*
root =
new osg::Group();
srand(5);
simulation->
add( ground );
for (
agx::Real x = -18.0; x < 19.0; x += 2.0 ) {
for (
agx::Real y = -18.0; y < 19.0; y += 2.0 ) {
{
else
}, simulation, box, node );
}
}
sensor->setPosition( 12, 0, 0 );
sensor->setSensor( true );
simulation->
add( sensor );
simulation->
add(
new tutorial4::ExplicitSplitSensorListener( sensor ) );
{
const agx::Vec3 currentPosition = sensor->getPosition();
if ( !agxCollide::pointInBox( currentPosition,
agx::Vec3( 20, 20, 0.5 ) ) ) {
normal[ indexMax ] = -currentPosition[ indexMax ] / currentPosition[ indexMax ];
velocity -= 2.0 * velocity * normal * normal;
}
sensor->setPosition( sensor->getPosition() + simulation->
getTimeStep() * velocity );
}, simulation, velocity );
}
{
osg::Group*
root =
new osg::Group();
agx::RigidBodyRef floor = createBox(floorHe, floorPos, floorColor, simulation, root);
floorProperties->
addGroup(filterGroup1);
barPos.x() = -barPos.x();
rightBarProperties->
addGroup(filterGroup2);
frontBarProperties->
addGroup(filterGroup2);
}
int main( int argc, char** argv )
{
"\tTutorial Merge Split Handler (Adaptive Model Order Reduction)\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
application->addScene( buildTutorial4, '4' );
application->addScene( buildTutorial5, '5' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
static agxRender::Color Gray()
static agxRender::Color DarkGray()
Base class for a merge split algorithm handling a set of merged bodies.
void setEnableMergePair(const agx::Name &group1, const agx::Name &group2, agx::Bool enable)
If enable is false, from now on reject merges between bodies in the two groups even when the MergeSpl...
static const agxSDK::MergeSplitProperties * getProperties(const agx::RigidBody *rb)
void registerMergedBody(agx::MergedBody *mergedBody)
Register merged body to this merge-split handler.
static agx::Bool split(agx::RigidBody *rb)
Split a rigid body that has been merged by a merge split handler.
void addGroup(const agx::Name &group)
Add all rigid bodies with this MergeSplitProperty to the given merge ignore group.
agx::Bool getEnableSplit() const
agx::Bool add(EdgeInteraction *edge)
Add an edge interaction defining a base interaction between one or two rigid bodies that will be cons...
ContactGeneratorEdgeInteraction()
Default constructor used by serialization.
ExecuteFilterT< agxCollide::Geometry, agxCollide::Geometry > GeometryFilter
tutorial_mergedBody.cpp
namespace
{
{
if ( numBoxes < 1 )
return nullptr;
assembly->add( box );
}
return assembly;
}
{
public:
{
{
if ( mb == nullptr ) return;
};
}
public:
:
agxSDK::ContactEventListener(
agxSDK::ContactEventListener::IMPACT, filter ),
m_callback( callback )
{
}
{
if ( m_callback )
m_callback( gc );
}
private:
};
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( ground );
boxRow1->setPosition( 0, 1, 3 );
simulation->
add( boxRow1 );
boxRow2->setPosition( 0, -1, 3 );
simulation->
add( boxRow2 );
for (
agx::UInt i = 1; i < boxRow2->getRigidBodies().size(); ++i ) {
}
simulation->
add( mergedBody );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( ground );
simulation->
add( splitBox );
boxRow->setPosition( 0, 1, 3 );
simulation->
add( boxRow );
for (
agx::UInt i = 1; i < boxRow->getRigidBodies().size(); ++i ) {
}
simulation->
add( mergedBody );
{
agxAssert( mb1 ==
nullptr || mb2 ==
nullptr );
if ( mb == nullptr )
return;
};
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( ground );
simulation->
add( mergedBody );
{
return rb;
};
::OnImpactCallback::createSplitOnImpact( rbLeft, simulation );
::OnImpactCallback::createSplitOnImpact( rbRight, simulation );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( ground );
simulation->
add( rb1Hinge );
simulation->
add( rb1Rb2Hinge );
simulation->
add( mergedBody );
{
std::cout << "Hinge edge interaction successfully removed? " << ( success ? "Yes!" : "No." ) << std::endl;
simulation->
remove( mergedBody );
}
std::cout << "Hinge edge interaction successfully added? " << ( success ? "Yes!" : "No." ) << std::endl;
if ( success )
simulation->
add( mergedBody );
}
}, simulation );
}
int main( int argc, char** argv )
{
"\tTutorial Merged Bodies\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
application->addScene( buildTutorial4, '4' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
Generalized callback, using std::function.
agx::Bool remove(EdgeInteraction *edge)
Remove an edge interaction from this merged body.
static const agx::MergedBody * get(const agx::RigidBody *rb)
This method returns an agx::MergedBody if rb has been added to one.
BinaryConstraintEdgeInteraction()
Default constructor used by serialization.
static BinaryConstraintEdgeInteraction * getEdge(const agx::Constraint *constraint)
EmptyEdgeInteraction()
Default constructor used by serialization.
agx::Bool isEmpty() const
tutorial_particleSystem.cpp
#include "tutorialUtils.h"
{
std::cout << "\nBuilding Scene 1 - Single pillar of rigid particles" << std::endl;
std::cout << "Demonstrates how to create and initialize particles one by one." << std::endl;
osg::Group *root = new osg::Group;
simulation->
add(floorGeometry);
simulation->
add(particleSystem);
for(size_t i=1; i < 10; i++) {
particle->setRadius(0.1);
}
return root;
}
{
public:
{
setMask( ParticleEmitterMover::PRE_STEP );
}
{
if (!m_emitter) {
return;
}
m_emitter->setVelocity( m_emitterGeom->getTransform().transform3x3(
agx::Vec3(0,-1,0) )*m_speed );
}
};
{
std::cout << "Demonstrates how to use a particle emitter" << std::endl;
osg::Group *
root =
new osg::Group;
simulation->
add(floorGeometry);
simulation->
add(particleSystem);
spawnGeom->setPosition(0,2,1);
simulation->
add(spawnGeom);
emitter->setSeed(0);
emitter->setGeometry( spawnGeom );
simulation->
add(emitter);
emitter->setRate( 150 );
emitter->setLife( 3.0 );
simulation->
add(
new ParticleEmitterMover(emitter, spawnGeom) );
for(size_t i=0; i < 4; i++)
{
for(size_t j=0; j < 4; j++)
{
}
}
}
{
public:
protected:
};
{
if (!m_ps->getMaterial())
setMask(SmokeEffect::PRE_STEP);
}
{
if (!m_ps)
{
getSimulation()->remove(this);
return;
}
agx::Real radius = m_ps->getParticleRadius();
agx::Vec3 G = getSimulation()->getUniformGravity();
agx::Real volume = area*radius*four_over_three;
agx::Vec3 accel_force = G * (mass - density*volume);
size_t n = forces.size();
for (size_t i=0; i<n; ++i)
{
agx::Vec3 relative_wind = velocities[i]-m_wind;
agx::Vec3 wind_force = - relative_wind * area * (viscosityCoefficient + densityCoefficient*relative_wind.
length());
agx::Vec3 force = (wind_force+accel_force+boyancy);
forces[i] += force;
}
}
{
std::cout << "Demonstrates how to access and add forces to particles by simulating a very simple smoke." << std::endl;
osg::Group *
root =
new osg::Group;
simulation->
add(particleSystem);
simulation->
add(
new SmokeEffect(particleSystem));
spawnGeom->setPosition(0,0,0);
simulation->
add(spawnGeom);
emitter->setGeometry( spawnGeom );
emitter->setSeed(0);
simulation->
add(emitter);
emitter->setRate( 600 );
emitter->setLife( 10.0 );
int n = 15;
agx::Real xStart = 0.1+-n*(delta+radius*2)/2;
for (int i=0; i < n; i++)
{
g->setPosition(xStart+(delta+radius)*i, yStart,0.3);
}
}
int main( int argc, char** argv )
{
std::cout <<
"\tTutorial particle system\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
void setEnableCollisions(const agxCollide::Geometry *geometry, bool flag)
Specify whether otherGeometry is allowed to collide with this geometry.
Physics::ParticlePtr createParticle()
Create a new particle in the particle system.
Pointer to a entity instance of type Physics.Particle.
A specialization of the particle system that simulates the particles as a set of hard core spheres.
tutorial_pidController.cpp
{
public:
{
}
{
agx::Real winchBaseVelocity = 0.5 * std::cos(1 / 10.0 * time);
m_winch->getRigidBody()->setVelocity(
agx::Vec3(0, 0, winchBaseVelocity));
return m_pendulumWeight->getPosition().z();
}
{
m_winch->setSpeed(-manipulatedVariable);
}
private:
};
{
public:
m_ship(ship), m_pid(pid), m_application(application)
{
}
{
m_ship->setVelocity(
agx::Vec3(0, 0, 1.0 * sin(0.2 * 3.14 * time)));
if (m_application) {
m_application->getSceneDecorator()->setText(1, "Last 10 seconds cost function value " + std::to_string(m_pid->calculateCostFunctionValue(10)));
m_application->getSceneDecorator()->setText(2, "Current error " + std::to_string(m_pid->getError()) + " meters.");
if (time > 8)
m_application->getSceneDecorator()->setText(0, "PID is active");
else
m_application->getSceneDecorator()->setText(0, "PID deactivated");
}
}
private:
};
{
loadReferenceCenterGeometry->setEnableCollisions(false);
simulation->
add(loadReferenceRb);
return loadReferenceRb;
}
std::tuple<agxWire::WinchRef, agx::RigidBodyRef> createWirePendulumWithWinch(
agxSDK::Simulation* simulation, osg::Group* root,
const agx::Vec3 &initialPosition)
{
simulation->
add(winchBase);
simulation->
add(pendulumWeight);
wire->add(winch, pulledInLength);
simulation->
add(renderer);
return std::make_tuple(winch, pendulumWeight);
}
{
osg::Group *
root =
new osg::Group;
const agx::Vec3 initialPosition(-0.5, 0.5, -10);
std::tie(winch, pendulumWeight) = createWirePendulumWithWinch(simulation, root, initialPosition);
pidController->setGains(proportionalGain, integralGain, derivativeGain);
pidController->setSetPoint(setPoint);
pidController->setManipulatedVariableLimit(-5, 5);
simulation->
add(controller_handler);
}
std::tuple<agx::PrismaticRef, agx::RigidBodyRef, agx::RigidBodyRef> createGangwayWithPrismatic(
agxSDK::Simulation* simulation, osg::Group* root)
{
pillar->setEnableCollisions(false);
simulation->
add(gangway);
simulation->
add(fixPoint);
return std::make_tuple(slider, gangway, ship);
}
{
osg::Group*
root =
new osg::Group;
std::tie(slider, gangway, ship) = createGangwayWithPrismatic(simulation, root);
{
},
{
{
}
}
);
pidController->setGains(proportionalGain, integralGain, derivativeGain);
pidController->setSetPoint(setPoint);
pidController->setManipulatedVariableLimit(-1, 1);
pidController->setManipulatedVariableAccelerationLimit(-2, 2);
simulation->
add(controller_handler);
simulation->
add(
new ShipPositionController(ship, pidController, application));
}
int main(int argc, char** argv )
{
int status = 0;
std::cout <<
"\tPID controller\n" <<
"\tPress 1 to control a winch\n" <<
"\tPress 2 to control a gangway with a prismatic\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( winchPidControlled, '1' );
application->addScene( prismaticPidControlled, '2');
if ( application->init( argc, argv ) ) {
status = application->run();
}
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return status;
}
The Plant (aka the Physical System) is added to the ControllHandler in the Control System.
virtual agx::Real getProcessVariable(const agx::TimeStamp &time)
virtual void setManipulatedVariable(agx::Real manipulatedVariable)
Set the Manipulated Variable(MV) controlling the Plant, also known as Control Variable.
This is a PID handler that collects the Plant measured Process Variable (PV) and execute PID update()...
static agxRender::Color DarkSlateBlue()
static agxRender::Color DarkSlateGray()
Free node class used to route wires.
Winch object which works like agxWire::WireWinchController but enables the features of having stored,...
Helper function to define the reference frame used by the prismatic constraint.
Real distance(const Vec3T &v2) const
Distance to another vector.
tutorial_pointcloud_over_ros2.cpp
namespace helpers
{
osg::Group* root,
{
};
environment->add(rb);
}
}
{
simulation->
add(lidarRb);
root->addChild(renderer);
return lidar;
}
}
{
auto root =
new osg::Group();
}
helpers::addPrimitiveShapeCircleToSensorEnvironement(simulation, root, 70, 10);
auto lidar = helpers::createHorizontalSweepLidar(simulation, root);
lidarOutput->build<
>();
lidar->getOutputHandler()->add(42, lidarOutput);
std::string frameId = "map";
bool isDense = lidar->getOutputHandler()->getEnableRemoveRayMisses();
"ROS2 message created with %d bytes of data from %d points with fields:",
);
int i = 1;
for (
auto& field : ros2Message.
fields)
}
{
auto root =
new osg::Group();
}
helpers::addPrimitiveShapeCircleToSensorEnvironement(simulation, root, 70, 10);
auto lidar = helpers::createHorizontalSweepLidar(simulation, root);
{
public:
:
agxSDK::StepEventListener(
agxSDK::StepEventListener::POST_STEP)
, m_topic(rosTopic)
, m_frame(frame)
, m_lidarOutputHandler(lidarOutputHandler)
, m_publisher(rosTopic)
{
m_output->build<
>();
m_lidarOutputHandler->add(42, m_output);
}
{
if (!m_output->hasUnreadData())
return;
bool isDense = m_lidarOutputHandler->getEnableRemoveRayMisses();
m_publisher.sendMessage(message);
}
private:
std::string m_topic;
std::string m_frame;
};
auto lidarStepPublisher = new LidarStepPublisher("tutorial2", "map", lidar->getOutputHandler());
simulation->
add(lidarStepPublisher);
}
int main(int argc, char** argv)
{
std::cout <<
"\tagxSensor + ROS2 tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
return 0;
application->addScene(buildTutorial1, '1');
application->addScene(buildTutorial2, '2');
if (application->init(argc, argv))
return application->run();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
Publisher class that enables sending ROS2 messages.
Raytrace output data where RtOutput::Field defines the data available.
@ DISTANCE_F32
1x 32-bit floating point distance of hit from emission origin.
AGXROS2_EXPORT sensorMsgs::PointCloud2 convertLidarOutput(agxSensor::RtOutput *lidarOutput, double timestamp, std::string frameId, bool isDense, uint32_t height=1)
Uses an output from agxSensor's lidar module to create an equivalent ROS2 PointCloud2 message.
The agxSensor namespace contains components to model real-time sensors connected to the physics of AG...
std::vector< uint8_t > data
std::vector< PointField > fields
tutorial_python_excavator_controller.cpp
#include <signal.h>
#include <iostream>
#if AGX_USE_PYTHON() && AGX_USE_AGXTERRAIN()
#include <agxCFG/utils.h>
namespace {
#ifdef _MSC_VER
BOOL WINAPI sighandler(DWORD )
{
std::cout << "Caught: CTRL-C or closing window. Shutting down agx." << std::endl;
}
#else
void sighandler(int )
{
std::cout << "Caught: CTRL-C or closing window. Shutting down agx." << std::endl;
}
#endif
}
{
public:
{
}
{
return m_constraint1dof->getAngle();
}
{
m_constraint1dof->getMotor1D()->setSpeed(manipulatedVariable);
}
private:
};
{
public:
enum Constraints
{
BucketPrismatic,
RightSprocketHinge,
LeftSprocketHinge,
ArmPrismatic2,
ArmPrismatic1,
StickPrismatic,
CabinHinge
};
enum ControllerMode
{
TARGET_SPEED,
TARGET_POSITION
};
bool registerConstraint(const std::string& name, Constraints constraint);
bool setSpeed(Constraints constraint, double speed);
double getSpeed(Constraints constraint) const;
double getPosition(Constraints constraint) const;
bool setPosition(Constraints constraint, double position);
double getForce(Constraints constraint) const;
protected:
std::string constraintToName(Constraints constraint) const;
void setupConstraints();
void update(Constraints constraint, double time);
void updatePositionControllers(double time);
private:
ConstraintMap m_constraints;
ControllerMode m_controllerMode;
ConstraintControllerMap m_controllers;
};
m_application(application), m_controllerMode(mode)
{
setMask(StepEventListener::PRE_STEP | StepEventListener::POST_STEP);
setupConstraints();
}
void ExcavatorController::setupConstraints()
{
for (auto c : m_constraints)
{
auto constraint = c.second.second;
pidController->setGains(proportionalGain, integralGain, derivativeGain);
pidController->setSetPoint(0);
pidController->setManipulatedVariableLimit(-0.5, 0.5);
m_controllers.insert(c.first, controllerHandler);
}
}
void ExcavatorController::setText(
int row,
const std::string& text,
agxRender::Color color)
{
m_application->getSceneDecorator()->setText(row, text, color);
}
void ExcavatorController::addNotification()
{
registerConstraint("BucketPrismatic", BucketPrismatic);
registerConstraint("RightSprocketHinge", RightSprocketHinge);
registerConstraint("LeftSprocketHinge", LeftSprocketHinge);
registerConstraint("ArmPrismatic2", ArmPrismatic2);
registerConstraint("ArmPrismatic1", ArmPrismatic1);
registerConstraint("StickPrismatic", StickPrismatic);
registerConstraint("CabinHinge", CabinHinge);
setupConstraints();
}
bool ExcavatorController::registerConstraint(const std::string& name, Constraints constraint)
{
auto dof1d_constraint = getSimulation()->getConstraint1DOF(name);
if (!dof1d_constraint)
{
return false;
}
else
std::cout << "Registered constraint '" << name << "'" << std::endl;
m_constraints.insert(constraint, std::make_pair(name, dof1d_constraint));
return true;
}
{
auto c = m_constraints.get_or_return_default(constraint);
return nullptr;
}
return c.second;
}
{
auto c = m_constraints.get_or_return_default(constraint);
return nullptr;
}
return c.second;
}
bool ExcavatorController::setSpeed(Constraints constraint, double speed)
{
auto c = getConstraint(constraint);
if (!c)
return false;
c->getMotor1D()->setSpeed(speed);
return true;
}
double ExcavatorController::getSpeed(Constraints constraint) const
{
auto c = getConstraint(constraint);
if (!c)
return 0;
return c->getMotor1D()->getSpeed();
}
double ExcavatorController::getPosition(Constraints constraint) const
{
auto c = getConstraint(constraint);
if (!c)
return 0;
return c->getAngle();
}
bool ExcavatorController::setPosition(Constraints constraint, double pos)
{
if (constraint == LeftSprocketHinge || constraint == RightSprocketHinge)
{
return 0;
}
auto c = getConstraint(constraint);
if (!c)
return 0;
auto controller = m_controllers.get_or_return_default(constraint);
if (!controller)
return false;
controller->getController()->setSetPoint(pos);
return true;
}
void ExcavatorController::updatePositionControllers(double time)
{
for (auto c : m_constraints)
{
if (c.first != LeftSprocketHinge && c.first != RightSprocketHinge)
update((Constraints)c.first, time);
}
}
void ExcavatorController::update(Constraints constraint, double time)
{
auto controller = m_controllers.get_or_return_default(constraint);
controller->post(time);
}
double ExcavatorController::getForce(Constraints constraint) const
{
auto c = getConstraint(constraint);
if (!c)
return 0;
return c->getMotor1D()->getCurrentForce();
}
std::string ExcavatorController::constraintToName(Constraints constraint) const
{
auto it = m_constraints.find(constraint);
if (it == m_constraints.end()) {
return "";
}
return it->second.first;
}
{
int start = 1;
for (auto c : m_constraints)
{
auto pos = getPosition(c.first);
bool isHinge = c.second.second->as<
agx::Hinge>() !=
nullptr;
const char* title = isHinge ? "angle" : "position";
const char* unit = isHinge ? "rad" : "m";
}
if (m_controllerMode == TARGET_SPEED) {
}
else
{
updatePositionControllers(time);
}
}
{
int start = (int)m_constraints.size() + 3;
for (auto c : m_constraints)
{
auto force = getForce(c.first);
bool isHinge = c.second.second->as<
agx::Hinge>() !=
nullptr;
const char* title = isHinge ? "Torque" : "Force";
const char* unit = isHinge ? "Nm" : "N";
}
}
{
public:
EventTrigger() {}
{
std::vector<Events::iterator> removeEvents;
for (Events::iterator it=events.begin(); it != events.end(); it++)
{
bool remove = (*it)(time);
if (remove)
}
if (!removeEvents.empty())
for (auto it : removeEvents)
events.erase(it);
}
typedef std::vector<std::function<bool(double)>> Events;
Events events;
};
bool inTime(double currentTime, double time)
{
return std::abs(currentTime - time) <= 1.0 / 60;
}
{
bool success;
osg::Group* parent = application->
executePythonScript(
"data/python/agxTerrain/excavator_365_terrain.agxPy",
"buildScene1", success);
if (success) {
simulation->
add(excavatorController);
trigger->events.push_back([excavatorController](double time) { excavatorController->setPosition(ExcavatorController::BucketPrismatic, std::sin(time) * 0.5); return false; });
trigger->events.push_back([excavatorController](double time) { excavatorController->setPosition(ExcavatorController::ArmPrismatic1, std::sin(time) * 0.5); return false; });
trigger->events.push_back([excavatorController](double time) { excavatorController->setPosition(ExcavatorController::ArmPrismatic2, std::sin(time) * 0.5); return false; });
trigger->events.push_back([excavatorController](double time) { excavatorController->setPosition(ExcavatorController::CabinHinge, std::sin(time) * 2); return false; });
trigger->events.push_back([excavatorController](double time) { excavatorController->setPosition(ExcavatorController::StickPrismatic, std::sin(time) * 0.3); return false; });
auto setSpeed = [excavatorController](double speed, double when)
{
return [excavatorController, when, speed](double time)
{
if (inTime(time, when)) {
excavatorController->setSpeed(ExcavatorController::LeftSprocketHinge, speed);
excavatorController->setSpeed(ExcavatorController::RightSprocketHinge, speed);
return true;
}
return false;
};
};
trigger->events.push_back(setSpeed(1, 3));
trigger->events.push_back(setSpeed(-1, 10));
trigger->events.push_back(setSpeed(0, 15));
simulation->
add(trigger);
}
return parent;
}
int main(int argc, char** argv)
{
#ifdef _MSC_VER
if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)sighandler, TRUE) == FALSE)
{
return 1;
}
#else
signal(SIGTERM, &sighandler);
signal(SIGINT, &sighandler);
#endif
int result = 1;
try {
if (application->init(arguments)) {
application->addScene(buildExcavatorPythonScene, '1');
result = application->run();
}
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
}
return result;
}
#else
int main(int, char**)
{
std::cout << "This tutorial needs agxPython and agxTerrain to function as expected" << std::endl;
return 0;
}
#endif
osg::Group * executePythonScript(const agx::String &file, const agx::String &function, bool &success)
Execute a python script file with a named function.
Inheritance with partial specialization due to bug with ref_ptr containers.
void setLockedAtZeroSpeed(agx::Bool lockedAtZeroSpeed)
If this state is set and the speed is set to zero, the behavior is identical to 'setLocked( true )'.
tutorial_rigidBodyEmitter.cpp
#include "tutorialUtils.h"
{
osg::Group* root = new osg::Group();
floor_geometry->setEnableCollisions( false );
floor_geometry->setMaterial(floorMat);
emitter->setRate(30.0);
emitter->setMaximumEmittedQuantity(200);
emitter->addCollisionGroup( "Particles" );
emitter->setDistributionTable(distributionTable);
geometryTemplate1->setMaterial(rbMat);
bodyTemplate1->
add(geometryTemplate1);
geometryTemplate2->setMaterial(rbMat);
bodyTemplate2->
add(geometryTemplate2);
emitGeometry->setPosition(0.0, 0.0, 4.0);
emitGeometry->addGroup( "Emitter" );
emitter->setGeometry(emitGeometry);
simulation->
add(emitGeometry);
simulation->
add(emitter);
return root;
}
int main(int argc, char** argv)
{
std::cout <<
"\tTutorial RigidBodyEmitter\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
if (application->init(argc, argv))
return application->run();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
This is a sensor class that can be attached to an geometry in the simulation.
Spawns new Rigid Bodies inside a volume given to the Emitter.
DistributionModel()
Default constructor.
AGXPHYSICS_EXPORT agxControl::EventSensor * createSinkOnGeometry(agxCollide::Geometry *geometry, bool onlyIncludeEmittedBodies)
Utillity function for creating a "sink" on a geometry that will remove rigid bodies and particles tha...
tutorial_robot_pick_and_place.cpp
#include <stdexcept>
{
public:
:
agxSDK::StepEventListener(
agxSDK::StepEventListener::PRE_STEP), m_chain(chain), m_panda(panda), m_sd(sd)
{
auto nj = m_chain->getNumJoints();
auto nl = m_chain->getNumLinks() - 1;
m_desiredQ.resize(nj);
m_desiredQd.resize(nj);
m_desiredQdd.resize(nj);
m_actualQ.resize(nj);
m_actualQd.resize(nj);
m_extForce.resize(nl * 6);
}
{
auto endEffector = m_chain->getLinks().back();
auto nl = m_chain->getNumLinks() - 1;
m_sensor->getLastForce(
agx::UInt(0u), lf1, lf2,
true);
auto estimated = std::max(lf1[2] / g.length() - m_toolMass, 0.0);
for (auto i = 0u; i < 3; ++i) {
m_extForce[(nl - 1) * 6 + i] = -g[i] * estimated;
}
if (m_sd) {
}
for (auto i = 0u, idx = 0u; i < m_chain->getJoints().size(); ++i) {
if (!joint)
continue;
m_actualQd[idx] = joint->getCurrentSpeed();
m_desiredQd[idx] = (m_desiredQ[idx] - m_actualQ[idx]) / h;
m_desiredQdd[idx] = (m_desiredQd[idx] - m_actualQd[idx]) / h;
idx++;
}
auto status = m_chain->computeInverseDynamics(m_desiredQdd, m_extForce, m_torque);
throw std::runtime_error("Inverse dynamics computation failed");
}
for (auto i = 0u, idx=0u; i < m_chain->getJoints().size(); ++i) {
if (joint) {
auto motor = joint->getMotor1D();
motor->setEnable(true);
motor->setForceRange(m_torque[idx], m_torque[idx]);
idx++;
}
}
}
{
if (q.
size() != m_chain->getNumJoints()) {
return false;
}
m_desiredQ = q;
return true;
}
private:
double m_toolMass;
};
enum TaskCommand { TASK_MOVE, TASK_OPEN, TASK_CLOSE };
struct TaskItem {
TaskCommand cmd;
double startTime;
double endTime;
TaskItem(TaskCommand c, double t0, double t1)
: cmd(c), startTime(t0), endTime(t1)
{}
: cmd(c), startTime(t0), endTime(t1), from(m0), to(m1)
{}
};
{
public:
:
agxSDK::StepEventListener(
agxSDK::StepEventListener::PRE_COLLIDE), m_chain(chain), m_panda(panda), m_llc(llc)
{
}
{
for (auto taskIndex=0u; taskIndex < m_tasks.size(); ++taskIndex) {
const auto& task = m_tasks[taskIndex];
if (task.startTime > t || task.endTime < t) {
continue;
}
double localT = t - task.startTime;
double taskDuration = task.endTime - task.startTime;
if (task.cmd == TASK_OPEN || task.cmd == TASK_CLOSE) {
auto joint = m_panda->getConstraint(
"panda_finger_joint1")->as<
agx::Prismatic>();
joint->getMotor1D()->setEnable(true);
joint->getMotor1D()->setSpeed(0.05 * (task.cmd==TASK_OPEN ? 1.0 : -1.0));
}
else if (task.cmd == TASK_MOVE) {
agx::Vec3 posT = pos0 + (pos1 - pos0) * (localT / taskDuration);
agx::Vec3 aaT = aa0 + (aa1 - aa0) * (localT / taskDuration);
}
else {
axis = aaT / angle;
}
currentAngles.
resize(m_chain->getNumJoints());
for (auto i = 0u, idx=0u; i < m_chain->getJoints().size(); ++i) {
if (joint)
currentAngles[idx++] = joint->getAngle();
}
auto status = m_chain->computeInverseKinematics(targetPose, currentAngles, targetAngles);
throw std::runtime_error("Inverse Kinematics computation failed");
}
m_llc->setDesiredQ(targetAngles);
}
}
}
void addTask(TaskItem ti) {
m_tasks.push_back(ti);
}
private:
};
{
osg::Group*
root =
new osg::Group();
auto boxbox = mm->getOrCreateContactMaterial(boxMaterial, boxMaterial);
boxbox->setFrictionCoefficient(0.5);
auto boxgripper = mm->getOrCreateContactMaterial(boxMaterial, gripperMaterial);
boxgripper->setFrictionCoefficient(0.9);
auto boxStack1Pos = table->getPosition() +
agx::Vec3(-0.5 * tableSize[0], -0.5 * tableSize[1], tableSize[2] + boxSize[2]);
auto boxStack2Pos = table->getPosition() +
agx::Vec3(0.3 * tableSize[0], 0.4 * tableSize[1], tableSize[2] + boxSize[2]);
agx::Vec3 boxPositions[3] = {boxStack1Pos, boxStack1Pos +
agx::Vec3(0, 0, 0.05999), boxStack2Pos};
for (auto idx=0u; idx < 3; ++idx) {
}
auto pandaFile = env->getFilePath().find("models/URDF/panda/panda.urdf");
throw std::runtime_error("Kinematic Chain describing robot is not valid.");
}
setRobotPose(chain, panda, readyPose);
auto up =
agx::Vec3(-0.0251, 0.1424, 0.9895);
auto tc = new TaskController(chain, panda, llc);
llc->setDesiredQ(readyPose);
createTaskPlan(tc, observer->getFrame()->getMatrix(), boxSize, boxStack1Pos, boxStack2Pos);
}
{
auto finger2Rel = finger2->getFrame()->getLocalMatrix() * eeInverse;
throw std::runtime_error("Forward computation failed");
}
for (
size_t idx = 0; idx < bodies.
size(); ++idx) {
bodies[idx]->getFrame()->setLocalMatrix(transforms[idx]);
}
}
{
auto pickupOffset = 0.05;
auto dropOffset = 0.08;
auto q2 =
agx::Quat(
agx::PI,
agx::Vec3(1, 0, 0),
agx::PI * 0.5,
agx::Vec3(0, 0, 1), 0,
agx::Vec3(0, 0, 1));
auto q3 =
agx::Quat(
agx::PI,
agx::Vec3(1, 0, 0), -
agx::PI * 0.5,
agx::Vec3(0, 1, 0),
agx::PI,
agx::Vec3(1, 0, 0));
auto boxZ = boxSize[2] * 2;
auto yzDiff = boxSize[2] - boxSize[1];
auto p0 = startTransform;
tc->addTask(TaskItem(TASK_OPEN, 0.0, 1.0));
tc->addTask(TaskItem(TASK_MOVE, 0.0, 1.0, p0, p1));
tc->addTask(TaskItem(TASK_MOVE, 1.0, 2.0, p1, p2));
tc->addTask(TaskItem(TASK_CLOSE, 2.0, 3.0));
tc->addTask(TaskItem(TASK_MOVE, 3.0, 4.0, p2, p1));
tc->addTask(TaskItem(TASK_MOVE, 4.0, 6.0, p1, p3));
tc->addTask(TaskItem(TASK_MOVE, 6.0, 7.0, p3, p4));
tc->addTask(TaskItem(TASK_OPEN, 7.0, 7.5));
tc->addTask(TaskItem(TASK_MOVE, 7.5, 9.0, p4, p3));
tc->addTask(TaskItem(TASK_MOVE, 9.0, 11.0, p3, p5));
tc->addTask(TaskItem(TASK_MOVE, 11.0, 12.0, p5, p6));
tc->addTask(TaskItem(TASK_CLOSE, 12.0, 13.0));
tc->addTask(TaskItem(TASK_MOVE, 13.0, 14.0, p6, p5));
tc->addTask(TaskItem(TASK_MOVE, 14.0, 16.0, p5, p7));
tc->addTask(TaskItem(TASK_MOVE, 16.0, 17.0, p7, p8));
tc->addTask(TaskItem(TASK_OPEN, 17.0, 18.0));
tc->addTask(TaskItem(TASK_MOVE, 18.0, 19.0, p8, p7));
tc->addTask(TaskItem(TASK_MOVE, 19.0, 20.0, p7, p0));
}
int main( int argc, char** argv )
{
std::cout <<
"\tRobot - pick and place\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1' );
if (application->init(argc, argv))
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
@ SUCCESS
Return value indicating success.
A Serial Kinematic Chain is a KinematicChain where there are no loops or branches.
KinematicChainStatus computeForwardKinematicsAll(const agx::RealVector &jointPositions, agx::AffineMatrix4x4Vector &outputTransforms, bool clampToJointLimits=false)
Compute the transform for all links in the chain relative the base given provided joint values.
const agx::RigidBodyRefVector & getLinks() const
Get the links that are in the chain.
static agxSDK::AssemblyRef read(const agx::String &filePath, const agx::String &packagePath="", const agx::RealVector *joints=nullptr, const Settings &settings=Settings())
Reads a URDF file and returns an agxSDK::Assembly representation of it.
agx::RigidBody * getRigidBody(const agx::Name &name, bool recursive=true)
Find (linear search) and return named RigidBody.
const agx::Constraint * getConstraint(const agx::Uuid &uuid, bool recursive=true) const
Find (linear search) and return a pointer to a Constraint with the given uuid.
agx::GravityField * getGravityField()
AffineMatrix4x4T< T > inverse() const
Quick inverse, transpose rotation part, and change sign of translation part.
void setEnableComputeForces(agx::Bool enable)
Enable (or disable) computation of the forces applied to the dynamic bodies in this constraint.
const agx::AffineMatrix4x4 & getLocalMatrix() const
virtual agx::Vec3 calculateGravity(const agx::Vec3 &position) const =0
Given position, a gravity acceleration will be calculated and returned.
With this class you can attach an ObserverFrame object relative to a RigidBody.
agx::Real getAngle() const
Get the rotation angle represented by the quaternion.
void getRotate(T &angle, T &x, T &y, T &z) const
Get the angle and vector components represented by the quaternion.
agx::Vec3T< T > getUnitVector() const
Get the rotation unit vector represented by the quaternion.
static AGXCORE_EXPORT agx::String getDirectory(const agx::String &filePath)
Settings for controlling details regarding the URDF reader.
tutorial_ros2.cpp
#include <cstdint>
#include <sstream>
#include <vector>
namespace tutorial_ros2_helpers
{
class ScreenLogger
{
public:
{
if (application == nullptr)
return;
application->getSceneDecorator()->setText(row++, text, color);
}
private:
int row = 0;
};
}
{
using namespace tutorial_ros2_helpers;
ScreenLogger logger(application);
static const agx::Vec4f green(0.0, 1.0, 0.0, 1.0);
static const agx::Vec4f yellow(0.0, 1.0, 1.0, 1.0);
logger.write("Basic Publisher and Subscriber demo");
logger.write("");
pub.sendMessage(msgSend);
if (sub.receiveMessage(msgReceive))
{
}
else
{
logger.write(
agx::String(
"[Subscriber] Something went wrong, did not receive any message."), red);
}
return new osg::Group();
}
{
using namespace tutorial_ros2_helpers;
ScreenLogger logger(application);
static const agx::Vec4f green(0.0, 1.0, 0.0, 1.0);
static const agx::Vec4f yellow(0.0, 1.0, 1.0, 1.0);
logger.write("Quality of Service (QOS) demo");
logger.write("");
for (int i = 1; i <= 10; i++)
{
pub_early_sender.sendMessage({i});
}
"[Late Subscriber] Expecing to get %d messages...", qos.
historyDepth), green);
while (sub_late_joiner.receiveMessage(msgReceive))
{
"[Late Subscriber] Got message: %d", msgReceive.
data), green);
}
return new osg::Group();
}
namespace tutorial_ros2_helpers
{
struct MyStruct
{
int32_t myInt {0};
std::vector<std::string> myStrings;
bool myBool {false};
};
{
std::stringstream ss;
ss << v.myInt << " " << "{ ";
for (const auto& s : v.myStrings)
ss << s << " ";
auto boolToStr = [](bool b) { return b ? "true" : "false"; };
ss << "} " << boolToStr(v.myBool);
return ss.str();
}
}
{
using namespace tutorial_ros2_helpers;
ScreenLogger logger(application);
static const agx::Vec4f green(0.0, 1.0, 0.0, 1.0);
static const agx::Vec4f yellow(0.0, 1.0, 1.0, 1.0);
logger.write("AnyMessageBuilder and AnyMessageParser demo");
logger.write("");
MyStruct sendStruct;
sendStruct.myInt = 42;
sendStruct.myStrings = { "apple", "toast", "quanta" };
sendStruct.myBool = true;
builder
"[Publisher] Sending custom struct: %s",
toString(sendStruct).c_str()), yellow);
if (!sub.receiveMessage(receiveMsg))
{
logger.write(
agx::String(
"[Subscriber] Something went wrong, did not receive any message."), red);
return new osg::Group();
}
MyStruct receiveStruct;
receiveStruct.myInt = parser.
readInt32(receiveMsg);
receiveStruct.myBool = parser.
readBool(receiveMsg);
"[Subscriber] Received custom struct: %s",
toString(receiveStruct).c_str()), green);
return new osg::Group();
}
int main(int argc, char** argv)
{
std::cout <<
"\tROS2 tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
application->addScene(buildTutorial2, '2');
application->addScene(buildTutorial3, '3');
if (application->init(argc, argv))
return application->run();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
AnyMessageBuilder is a helper class for serializing custom data structures to an agxMsgs::Any message...
const agxROS2::agxMsgs::Any & getMessage() const
The lifetime of the returnvalue is bound to this AnyMessageBuilder.
AnyMessageBuilder & writeStringSequence(const std::vector< std::string > &d)
AnyMessageBuilder & beginMessage()
AnyMessageBuilder & writeBool(bool d)
AnyMessageBuilder & writeInt32(int32_t d)
AnyMessageParser is a helper class for deserializing agxMsgs::Any back to the original custom data ty...
void beginParse()
Must be called once prior to parsing a single complete message.
int32_t readInt32(const agxROS2::agxMsgs::Any &message)
std::vector< std::string > readStringSequence(const agxROS2::agxMsgs::Any &message)
bool readBool(const agxROS2::agxMsgs::Any &message)
Subscriber class that enables receiving ROS2 messages.
const char * toString(Side side)
Struct containing Quality of Service (QOS) settings.
QOS_RELIABILITY reliability
QOS_DURABILITY durability
tutorial_statistics.cpp
{
dynamicRBGeometry->add(dynamicRBBox);
dynamicRB->
add(dynamicRBGeometry);
simulation->
add(dynamicRB);
simulation->
add(staticRB);
}
void printStatistics()
{
entryNames.
push_back(std::make_pair(
"Simulation",
"Step forward time"));
entryNames.
push_back(std::make_pair(
"Simulation",
"Dynamics-system time"));
entryNames.
push_back(std::make_pair(
"Simulation",
"Collision-detection time"));
entryNames.
push_back(std::make_pair(
"Simulation",
"Pre-collide event time"));
entryNames.
push_back(std::make_pair(
"Simulation",
"Pre-step event time"));
entryNames.
push_back(std::make_pair(
"Simulation",
"Post-step event time"));
entryNames.
push_back(std::make_pair(
"Simulation",
"Triggering contact events"));
entryNames.
push_back(std::make_pair(
"Simulation",
"Culling contacts"));
entryNames.
push_back(std::make_pair(
"Simulation",
"Triggering contact separation events"));
entryNames.
push_back(std::make_pair(
"Simulation",
"Contact reduction"));
entryNames.
push_back(std::make_pair(
"Simulation",
"Committing removed contacts"));
entryNames.
push_back(std::make_pair(
"Space",
"Broad-phase time"));
entryNames.
push_back(std::make_pair(
"Space",
"Narrow-phase time"));
entryNames.
push_back(std::make_pair(
"Space",
"Sync bounds time"));
entryNames.
push_back(std::make_pair(
"Space",
"Sync transforms time"));
entryNames.
push_back(std::make_pair(
"Space",
"Num geometries"));
entryNames.
push_back(std::make_pair(
"Space",
"Num geometry-geometry contact points"));
entryNames.
push_back(std::make_pair(
"Space",
"Num geometry-geometry contacts"));
entryNames.
push_back(std::make_pair(
"Space",
"Num narrow phase tests"));
entryNames.
push_back(std::make_pair(
"DynamicsSystem",
"Update time"));
entryNames.
push_back(std::make_pair(
"DynamicsSystem",
"Solver time"));
entryNames.
push_back(std::make_pair(
"DynamicsSystem",
"Sabre: Factoring"));
entryNames.
push_back(std::make_pair(
"DynamicsSystem",
"Sabre: Solve"));
entryNames.
push_back(std::make_pair(
"DynamicsSystem",
"Num multi-body constraints"));
entryNames.
push_back(std::make_pair(
"DynamicsSystem",
"Num solve islands"));
entryNames.
push_back(std::make_pair(
"DynamicsSystem",
"Num enabled rigid bodies"));
entryNames.
push_back(std::make_pair(
"DynamicsSystem",
"Num binary constraints"));
entryNames.
push_back(std::make_pair(
"DynamicsSystem",
"Num contact constraints"));
for (auto entry : entryNames) {
std::cout << entry.first <<
":" << entry.second <<
" " << info.
current <<
" (" << info.
accumulated <<
")" << std::endl;
}
}
void buildTutorial1()
{
createScene(simulation);
while (simulation->getTimeStamp() < 2)
{
simulation->stepForward();
printStatistics();
}
}
int main()
{
std::cout <<
"\tStatistics tutorial\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
buildTutorial1();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
void setEnable(bool)
Enables or disables gathering of statistics data.
static Statistics * instance()
TimingInfo getTimingInfo(const String &providerName, const String &dataName, size_t providerIndex=0)
Will find a provider by name, and data by name, and return the stored statistics data.
tutorial_suctionGripper.cpp
#include "SuctionGripperTutorialHelpers.h"
const size_t& sealResolution = 4u)
{
cupGeometry->setMaterial(material);
cupBody->add(cupGeometry);
auto lineSensorVector = -lineSensorReach * localCupForceDir;
return cup;
}
)
{
}
{
holderBody->add(holderGeometry);
holderBody->getFrame()->setLocalTranslate(
agx::Vec3(0, 0, holderOffsetZ));
return holderBody;
}
osg::Group* root,
{
auto holderBody = createHolderBody(holderOffsetZ);
auto holderBodyOffset =
agx::Vec3(0, 0, -holderOffsetZ);
auto cup = createSuctionCup(cupMaterial);
attachSuctionCup(gripper, cup, holderBodyOffset,
agx::Vec3(0, 0, cupOffsetZ));
addDeformableMesh(simulation, root, gripper->
getHolderRigidBody(), cup, holderBodyOffset);
simulation->
add(gripper);
return gripper;
}
osg::Group* root,
{
holderBody->add(holderGeometry);
holderBody->getFrame()->setLocalTranslate(
agx::Vec3(0, 0, holderOffsetZ));
auto holderBodyOffset =
agx::Vec3(0, 0, -holderOffsetZ);
auto cup1 = createSuctionCup(cupMaterial, lipRadius, lipHeight);
attachSuctionCup(gripper, cup1, holderBodyOffset,
agx::Vec3(cupOffsetX, cupOffsetY, cupOffsetZ));
auto cup2 = createSuctionCup(cupMaterial, lipRadius, lipHeight);
attachSuctionCup(gripper, cup2, holderBodyOffset,
agx::Vec3(cupOffsetX, -cupOffsetY, cupOffsetZ));
auto cup3 = createSuctionCup(cupMaterial, lipRadius, lipHeight);
attachSuctionCup(gripper, cup3, holderBodyOffset,
agx::Vec3(0, cupOffsetY, cupOffsetZ));
auto cup4 = createSuctionCup(cupMaterial, lipRadius, lipHeight);
attachSuctionCup(gripper, cup4, holderBodyOffset,
agx::Vec3(0, -cupOffsetY, cupOffsetZ));
auto cup5 = createSuctionCup(cupMaterial, lipRadius, lipHeight);
attachSuctionCup(gripper, cup5, holderBodyOffset,
agx::Vec3(-cupOffsetX, cupOffsetY, cupOffsetZ));
auto cup6 = createSuctionCup(cupMaterial, lipRadius, lipHeight);
attachSuctionCup(gripper, cup6, holderBodyOffset,
agx::Vec3(-cupOffsetX, -cupOffsetY, cupOffsetZ));
simulation->
add(gripper);
return gripper;
}
{
setupCamera(application);
osg::Group*
root =
new osg::Group();
auto cupMaterial = prepareGroundBoxAndMaterials(simulation, root);
auto sealResolution = size_t(0);
auto geometry = gripper->getTrimeshGeometry();
auto deformer = gripper->getTrimeshDeformer();
simulation->
add(deformer);
simulation->
add(gripper);
simulation->
add(updater);
setupGripperKeyboardController(simulation, gripper);
setupVaccumLevelPrinter(application, simulation, gripper);
}
{
setupCamera(application);
osg::Group*
root =
new osg::Group();
auto cupMaterial = prepareGroundBoxAndMaterials(simulation, root);
auto holderBody = createHolderBody(holderOffsetZ);
auto holderBodyOffset =
agx::Vec3(0, 0, -holderOffsetZ);
auto cupLocalPosition =
agx::Vec3(0, 0, -0.05);
attachSuctionCup(gripper, cup, holderBodyOffset, cupLocalPosition);
simulation->
add(gripper);
setupGripperKeyboardController(simulation, gripper);
setupVaccumLevelPrinter(application, simulation, gripper);
}
{
setupCamera(application);
osg::Group*
root =
new osg::Group();
auto cupMaterial = prepareGroundBoxAndMaterials(simulation, root);
auto gripper = createSuctionGripperOneCup(simulation, root, cupMaterial);
setupGripperKeyboardController(simulation, gripper);
setupVaccumLevelPrinter(application, simulation, gripper);
}
{
setupCamera(application);
osg::Group*
root =
new osg::Group();
auto cupMaterial = prepareGroundBoxAndMaterials(simulation, root, halfSide, halfHeight);
auto gripper = createMultiCupSuctionGripper(simulation, root, cupMaterial);
setupGripperKeyboardController(simulation, gripper);
setupVaccumLevelPrinter(application, simulation, gripper);
}
{
setupCamera(application);
osg::Group*
root =
new osg::Group();
createGround(simulation, root);
auto cupMaterial = prepareGroundDeformableAndMaterials(simulation, root, 0.3);
auto gripper = createMultiCupSuctionGripper(simulation, root, cupMaterial);
setupGripperKeyboardController(simulation, gripper);
setupVaccumLevelPrinter(application, simulation, gripper);
}
int main( int argc, char** argv )
{
std::cout <<
"\tTutorial SuctionGripper\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorialSingleCupSuctionGripper, '1');
application->addScene(buildTutorialSuctionGripperDefaultCup, '2');
application->addScene(buildTutorialSuctionGripperSingleCup, '3' );
application->addScene(buildTutorialSuctionGripperMultiCup, '4' );
application->addScene(buildTutorialSuctionGripperDeformableObject, '5');
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
const agx::RigidBody * getRigidBody() const
This should be considered a template class used as a base for Suction Gripper simulation models.
agx::RigidBody * getHolderRigidBody()
void addSuctionCup(SuctionCup *cup)
Add one suction cup to the gripper.
static agx::LockJoint * positionAndLockSuctionCup(agxModel::SuctionGripper *gripper, agxModel::SuctionCup *cup, agx::Vec3 holderLocalTranslate, agx::Vec3 cupLocalTranslate)
const VacuumSystem * getVacuumSystem() const
void setDesiredVacuum(agx::Real vacuumLevel)
Set the current vacuum level (0-1) for the system If set outside the valid range, the value is clampe...
AGXOSG_EXPORT agxSDK::EventListener * createVisualUpdater(agxSDK::EventListener *listener, osg::Group *root)
Given some specific classes inheriting from EventListener, this function can handle custom visual rep...
tutorial_terrainPager_tileEvents.cpp
#if AGX_USE_AGXTERRAIN()
#ifdef _MSC_VER
#include <process.h>
#define getpid _getpid
#else
#include <unistd.h>
#endif
{
public:
~DemoListener();
private:
size_t m_lastCount;
};
StepEventListener(
agxSDK::StepEventListener::LAST_STEP ), m_pager( pager )
{
m_lastCount = 0;
if ( m_pager ) {
m_pager->tileLoadEvent.addCallback( &m_loadCallback1 );
m_pager->tileUnloadEvent.addCallback( &m_unloadCallback1 );
m_pager->tileLoadEvent.addCallback( &m_loadCallback2 );
m_pager->tileUnloadEvent.addCallback( &m_unloadCallback2 );
}
else {
exit(1);
}
}
DemoListener::~DemoListener()
{
if ( m_pager ) {
m_pager->tileLoadEvent.removeCallback( &m_loadCallback1 );
m_pager->tileUnloadEvent.removeCallback( &m_unloadCallback1 );
m_pager->tileLoadEvent.removeCallback( &m_loadCallback2 );
m_pager->tileUnloadEvent.removeCallback( &m_unloadCallback2 );
}
}
assert( m_activeTiles.contains( t ) == false );
m_activeTiles[t] = id;
printf("\t+ Tile [%i,%i] was loaded\n", id[0], id[1] );
}
assert( m_activeTiles.contains( t ) == true );
m_activeTiles.erase( t );
printf("\t- Tile [%i,%i] was unloaded\n", id[0], id[1] );
}
{
printf("\t%+2i, tile id: [%i,%i]\n", num, id[0], id[1] );
}
if ( m_activeTiles.size() != m_lastCount ) {
m_lastCount = m_activeTiles.size();
printf("At T=%5.2f, the DemoListener knows about %i Terrain instances\n",
(float) time, (int) m_activeTiles.size() );
}
}
{
osg::Group*
root =
new osg::Group();
tds->addSourceGeometry( geom );
size_t tileResolution = 51;
size_t tileOverlap = 5;
tp->setName("TerrainPager");
tp->setTerrainDataSource( tds );
if ( tmpDir.length() == 0 ) {
printf("Failed to created temp directory for terrain data");
exit(2);
}
else {
printf("Using %s for temp storage of terrain data\n", tmpDir.c_str() );
}
tp->setFileCacheDirectory( tmpDir );
tp->add( body, 1, 6 );
auto dl = new DemoListener( tp );
}
int main( int argc, char** argv )
{
std::cout <<
"\tTerrainPager and TileCallbacks\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
#else
int main( int, char** )
{
"\tTerrainPager and TileCallbacks\n" <<
"\t--------------------------------\n" <<
LOGGER_ENDL() <<
"AGX Dynamics built without support for agxTerrain" <<
LOGGER_ENDL();
return 0;
}
#endif
This class performs raycasting against the source geometry to get height data for tiles.
Templated callback with two arguments.
static AGXCORE_EXPORT agx::String mkTmpDirectory(const agx::String &prefix="")
Create a temporary directory.
tutorial_threadAffinity.cpp
int tutorial_setAffinityAGXThreads()
{
const unsigned int NUM_THREADS = 4;
bool values[NUM_THREADS];
bool ok;
for (auto i = 1u; i < NUM_THREADS; ++i)
{
ok &= values[i];
}
for (auto i = 0u; i < NUM_THREADS; ++i)
{
std::cerr <<
agx::String::format(
"Status for setting affinity for thread %i: %i\n", i, values[i]) << std::endl;
}
return (ok ? 0 : 1);
}
{
public:
MyThread(unsigned int id ) : m_sum(0), m_id(id)
{
}
virtual ~MyThread() {}
double m_sum;
unsigned int m_id;
};
void MyThread::run()
{
while ( m_sum < 1000000000)
{
m_sum += std::exp2(2) * std::cos(0.4) * std::exp2(2);
}
}
void tutorial_createThreads()
{
const unsigned int NUM_THREADS = 6;
for (auto i = 0u; i < NUM_THREADS; i++)
{
}
t->setThreadAffinity(0xffff);
std::cerr << "Starting threads" << std::endl;
for (auto thread : threads)
thread->start();
std::cerr << "Waiting for threads" << std::endl;
for (auto thread : threads)
{
thread->join();
}
std::cerr << "Shutting down" << std::endl;
threads.clear();
}
int main( int, char** )
{
tutorial_createThreads();
return tutorial_setAffinityAGXThreads();
}
bool setThreadAffinity(agx::UInt64 cpumask)
Thread Affinity can be used to influence on which logical cores threads are scheduled and allowed to ...
agx::Thread is a representation of an OS specific implementation of a computational thread.
static Thread * getCurrentThread()
static Thread * getThread(size_t id)
tutorial_threads.cpp
{
public:
MyListener()
{}
{
if (!m_threads.contains(thread))
{
std::cerr << "In a pre-callback. Thread: " << thread << std::endl;
m_threads.insert(thread);
}
}
};
{
public:
{
m_block.reset();
}
void release()
{
m_block.release();
}
virtual ~MyThread() {}
};
void MyThread::run()
{
std::cerr << "Thread: " << thread << std::endl;
m_block.block();
m_block.reset();
while (sim->getTimeStamp() - t < 0.1)
{
sim->add(body);
sim->stepForward();
sim->remove(body);
}
}
void threadTutorial1()
{
std::cerr << "Running " << __FUNCTION__ << std::endl << std::endl;
sim->add(new MyListener);
std::cerr << "Main thread: " << thread << std::endl;
for (size_t i = 0; i < 4; i++)
{
MyThread *t = new MyThread(sim);
t->start();
}
for (
size_t i = 0; i < threads.
size(); i++)
{
MyThread *t = threads[i];
t->release();
t->join();
}
for (
size_t i = 0; i < threads.
size(); i++)
{
MyThread *t = threads[i];
delete t;
}
}
{
numVertices = trimeshShape->getNumVertices();
if (!trimeshShape)
{
return nullptr;
}
dynamicRBGeometry->setEnableCollisions(false);
dynamicRBGeometry->setMaterial(material);
dynamicRBGeometry->add(trimeshShape);
dynamicRB->
add(dynamicRBGeometry);
return dynamicRB;
}
{
public:
MyLoadingThread(int numObjects, const std::string& filename) : m_numObjects(numObjects), m_filename(filename)
{
}
virtual ~MyLoadingThread() {}
int m_numObjects;
std::string m_filename;
size_t m_numVertices;
};
void MyLoadingThread::run()
{
for (auto i = 0; i < m_numObjects; i++)
{
m_bodies.push_back(body);
}
}
void threadTutorial2()
{
std::cerr << std::endl << "Running " << __FUNCTION__ << std::endl << std::endl;
MyLoadingThread myThread(20, "models/torusRound.obj");
myThread.start();
std::cerr << "Simulating while creating";
while (simTimer.getTime() < 2000)
{
int i = (int)simTimer.getTime();
simulation->stepForward();
if ((i % 700) == 0)
std::cerr << ".";
}
std::cerr << std::endl << "Done Simulating " << std::endl;
myThread.join();
std::cerr << "Creating " << myThread.m_bodies.size() << " objects (each with " << myThread.m_numVertices << " num vertices) took " << myThread.m_time << "ms" << std::endl;
for (auto b : myThread.m_bodies)
simulation->add(b);
std::cerr <<
"Adding " << myThread.m_bodies.size() <<
" bodies to simulation took " << timer.
getTime() <<
"ms" << std::endl;
std::cerr << "Continue to simulate with the added bodies" << std::endl;
simTimer.reset(true);
while (simTimer.getTime() < 0.5)
{
int i = (int)simTimer.getTime();
simulation->stepForward();
if ((i % 700) == 0)
std::cerr << ".";
}
std::cerr << std::endl << "Done Simulating " << std::endl;
}
int main(int , char** )
{
std::cout <<
"\tTutorial Threads\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
threadTutorial1();
threadTutorial2();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
Block synchronization primitive.
Inheritance with partial specialization due to bug with ref_ptr containers.
tutorial_tire.cpp
{
osg::Group* root = new osg::Group();
simulation->
add(tireOnGround);
simulation->
add(groundStraight);
groundStraight->setMaterial(groundMaterial);
groundStraight->setPosition(groundHalfExtent, 0, -0.5);
simulation->
add(groundRotated);
groundRotated->setMaterial(groundMaterial);
const agx::Vec3 chassisHalfExtents(1.2, 0.8, 0.2);
simulation->
add(chassis);
car->add(chassis);
for (int i = 0; i < 4; ++i) {
agx::Vec3 pos(i < 2 ? -1 : 1, (i % 2 == 1) ? -1 : 1, 0);
car->add(wheel);
car->add(tire);
tire->getRigidBody());
}
return root;
}
{
osg::Group*
root =
new osg::Group();
simulation->
add(tireOnGround);
simulation->
add(groundStraight);
groundStraight->setMaterial(groundMaterial);
groundStraight->setPosition(groundHalfExtent, 0, -0.5);
simulation->
add(groundRotated);
groundRotated->setMaterial(groundMaterial);
const agx::Vec3 chassisHalfExtents(1.2, 0.8, 0.2);
simulation->
add(chassis);
car->add(chassis);
for (int i = 0; i < 4; ++i) {
agx::Vec3 pos(i < 2 ? -1 : 1, (i % 2 == 1) ? -1 : 1, 0);
simulation->
add(hubRigidBody);
car->add(hubRigidBody);
simulation->
add(tireRigidBody);
car->add(tireRigidBody);
car->add(tire);
tire->getHubRigidBody());
}
}
int main( int argc, char** argv )
{
std::cout <<
"\tTutorial Tire\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorialOneBodyTire, '1' );
application->addScene( buildTutorialTwoBodyTire, '2' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
tutorial_trackedVehicle.cpp
namespace utils
{
{
public:
TrackedVehicle();
private:
};
TrackedVehicle::TrackedVehicle()
{
}
{
if ( m_chassis != nullptr ) {
m_chassis = nullptr;
}
m_chassis = chassis;
}
{
return m_chassis;
}
{
if ( m_rightTrack != nullptr ) {
m_rightTrack = nullptr;
}
m_rightTrack = rightTrack;
}
{
return m_rightTrack;
}
{
if ( m_leftTrack != nullptr ) {
m_leftTrack = nullptr;
}
m_leftTrack = leftTrack;
}
{
return m_leftTrack;
}
{
public:
enum Action
{
FORWARD,
REVERSE,
RIGHT,
LEFT
};
public:
TrackedVehicleKeyboardController( TrackedVehicle* trackedVehicle );
void bind( Action action,
Key key,
public:
virtual bool keyboard( int key, unsigned int modKey, float x, float y, bool down ) override;
virtual void update( float x, float y ) override;
protected:
~TrackedVehicleKeyboardController();
private:
struct KeyData
{
Action action;
};
private:
TrackedVehicleObserver m_trackedVehicle;
KeyDataTable m_keyDataTable;
};
TrackedVehicleKeyboardController::TrackedVehicleKeyboardController( TrackedVehicle* trackedVehicle )
: m_trackedVehicle( trackedVehicle )
{
auto hr = m_trackedVehicle->getRightTrack()->findReferenceWheel()->getConstraint()->as<
agx::Hinge>();
auto hl = m_trackedVehicle->getLeftTrack()->findReferenceWheel()->getConstraint()->
as<
agx::Hinge>();
hr->getMotor1D()->setLockedAtZeroSpeed( true );
}
TrackedVehicleKeyboardController::~TrackedVehicleKeyboardController()
{
}
void TrackedVehicleKeyboardController::bind( TrackedVehicleKeyboardController::Action action,
Key key,
{
m_keyDataTable.insert( key, KeyData
{
targetSpeed,
accelerationTime,
deaccelerationTime,
action,
false
} );
}
bool TrackedVehicleKeyboardController::keyboard( int key, unsigned int, float, float, bool down )
{
if ( m_trackedVehicle == nullptr )
return false;
auto dataIt = m_keyDataTable.find( key );
if ( dataIt == m_keyDataTable.end() )
return false;
auto& data = dataIt->second;
if ( down && !data.isDown ) {
data.eventStart = getSimulation()->getTimeStamp();
}
else if ( !down ) {
data.eventStart = getSimulation()->getTimeStamp();
}
data.isDown = down;
return true;
}
void TrackedVehicleKeyboardController::update( float, float )
{
if ( m_trackedVehicle == nullptr ) {
getSimulation()->remove( this );
return;
}
for ( auto& kv : m_keyDataTable ) {
auto& data = kv.second;
data.eventTime =
agx::clamp( getSimulation()->getTimeStamp() - data.eventStart,
agx::Real( 0 ), data.accelerationTime );
if ( data.isDown )
else
speed =
agx::lerp( data.target,
agx::Real( 0 ), data.eventTime / data.deaccelerationTime );
if ( data.action == Action::FORWARD ) {
speedRight += speed;
speedLeft += speed;
}
else if ( data.action == Action::REVERSE ) {
speedRight -= speed;
speedLeft -= speed;
}
else if ( data.action == Action::LEFT ) {
speedRight += speed;
speedLeft -= speed;
}
else if ( data.action == Action::RIGHT ) {
speedRight -= speed;
speedLeft += speed;
}
}
m_trackedVehicle->getRightTrack()->findReferenceWheel()->getConstraint()->as<
agx::Hinge>()->getMotor1D()->setSpeed( speedRight );
m_trackedVehicle->getLeftTrack()->findReferenceWheel()->getConstraint()->
as<
agx::Hinge>()->getMotor1D()->setSpeed( speedLeft );
}
}
{
auto root =
new osg::Group();
const auto heightFieldImage = "textures/terrain_test/terrain_height.png";
const auto heightFieldTexture = "textures/terrain_test/terrain_detail.png";
const auto heightFieldSize =
agx::Vec2( 60.0, 60.0 );
const auto heightFieldMinMax =
agx::Vec2( -2.0, 2.0 );
heightFieldSize.x(),
heightFieldSize.y(),
heightFieldMinMax.x(),
heightFieldMinMax.y() ) );
simulation->
add( ground );
utils::TrackedVehicleRef trackedVehicle = new utils::TrackedVehicle();
trackedVehicle->setChassis( chassis );
const agx::Real nodeDistanceTension = 5.0E-3;
rollerRadius;
{
return rb;
};
{
radius,
createWheelRigidBody( radius, height, position ),
};
{
sprocketRadius,
sprocketHeight,
agx::Vec3( chassisHalfExtents.
x() - 6.5 * sprocketRadius,
0,
1.5 * sprocketRadius ) ) );
idlerRadius,
idlerHeight,
agx::Vec3( chassisHalfExtents.
x() - 0.9 * idlerRadius,
0,
-0.5 * chassisHalfExtents.
z() ) ) );
agx::Vec3 rollerPosition = track->getWheels().back()->getRigidBody()->getLocalPosition() -
agx::Vec3( idlerRadius + rollerRadius + rollerOffset,
0,
2.0 * rollerRadius );
for (
agx::UInt i = 0; i < numRollers; ++i ) {
rollerRadius,
rollerHeight,
rollerPosition );
if ( i == 0 || i == numRollers - 1 )
track->add( roller );
rollerPosition.
x() -= ( 2.0 * rollerRadius + rollerOffset );
}
idlerRadius,
idlerHeight,
rollerPosition +
agx::Vec3( rollerRadius - idlerRadius,
0,
2.0 * rollerRadius ) ) );
return track;
};
trackedVehicle->setRightTrack( createTrack() );
trackedVehicle->setLeftTrack( createTrack() );
trackedVehicle->getRightTrack()->setPosition( 0, -chassisHalfExtents.
y() - 0.6 * nodeWidth, 0 );
trackedVehicle->getLeftTrack()->setPosition( 0, chassisHalfExtents.
y() + 0.6 * nodeWidth, 0 );
trackedVehicle->getRightTrack()->setEnableCollisions( trackedVehicle->getChassis(), false );
trackedVehicle->getLeftTrack()->setEnableCollisions( trackedVehicle->getChassis(), false );
{
auto hinge = wheel->attachConstraint<
agx::Hinge>(
"hinge", chassis, hingeWheelOffset );
const auto rollerHingeCompliance = 5.0E-7;
const auto rollerHingeDamping = 0.15;
}
}
};
createWheelHinges( trackedVehicle->getRightTrack(), trackedVehicle->getChassis(), 0.6 * nodeWidth );
createWheelHinges( trackedVehicle->getLeftTrack(), trackedVehicle->getChassis(), -0.6 * nodeWidth );
{
};
setTrackProperties( trackedVehicle->getRightTrack() );
setTrackProperties( trackedVehicle->getLeftTrack() );
const agx::Real trackWheelFrictionCoefficient = 1.0;
const agx::Real trackWheelSurfaceViscosity = 1.0E-5;
ground->setMaterial( groundMaterial );
trackedVehicle->getRightTrack()->setMaterial( trackMaterial );
for ( const auto& wheel : trackedVehicle->getRightTrack()->getWheels() )
trackedVehicle->getLeftTrack()->setMaterial( trackMaterial );
for ( const auto& wheel : trackedVehicle->getLeftTrack()->getWheels() )
trackedVehicle->getChassis()->getFrame(),
trackWheelContactMaterial->setFrictionModel( trackWheelFrictionModel );
trackWheelContactMaterial->setFrictionCoefficient( trackWheelFrictionCoefficient );
trackWheelContactMaterial->setSurfaceViscosity( trackWheelSurfaceViscosity );
trackWheelContactMaterial->setRestitution( 0.0 );
trackedVehicle->getChassis()->getFrame(),
true );
trackGroundContactMaterial->setFrictionModel( trackGroundFrictionModel );
trackGroundContactMaterial->setRestitution( 0.0 );
trackedVehicle->setPosition( 0, 0, 3 );
simulation->
add( trackedVehicle );
{
for (
const auto& wheel : track->
getWheels() )
for (
const auto& node : track->
nodes() )
};
utils::TrackedVehicleKeyboardControllerRef keyControl = new utils::TrackedVehicleKeyboardController( trackedVehicle );
keyControl->bind( utils::TrackedVehicleKeyboardController::Action::FORWARD, utils::Key::KEY_Up, 4.0, 0.5, 0.25 );
keyControl->bind( utils::TrackedVehicleKeyboardController::Action::REVERSE, utils::Key::KEY_Down, 4.0, 0.5, 0.25 );
keyControl->bind( utils::TrackedVehicleKeyboardController::Action::RIGHT, utils::Key::KEY_Right, 2.5, 0.25, 0.15 );
keyControl->bind( utils::TrackedVehicleKeyboardController::Action::LEFT, utils::Key::KEY_Left, 2.5, 0.25, 0.15 );
simulation->
add( keyControl );
}
{
auto root =
new osg::Group();
{
return rb;
};
{
radius,
createWheelRigidBody( radius, height, position ),
};
{
if ( ( counter++ % 4 ) == 0 ) {
thickness = thickerThickness;
heightOffset = -0.5 * ( thickerThickness - defaultThickness );
}
0.25,
} );
for ( auto wheel : track->getWheels() ) {
wheel->attachHinge( "hinge", nullptr, 0.0 );
}
for ( auto node : track->nodes() )
track->findReferenceWheel()->getConstraint()->as<
agx::Hinge>()->getMotor1D()->
setEnable(
true );
track->findReferenceWheel()->getConstraint()->as<
agx::Hinge>()->getMotor1D()->setSpeed( 1.5 );
simulation->
add( track );
}
{
auto root =
new osg::Group();
const auto wheelRadius = 0.45;
const auto wheelHeight = 0.35;
wheelRadius,
wheelRb );
auto hinge = wheel->attachHinge( "hinge", nullptr, 0.0 );
simulation->
add( wheelRb );
simulation->
add( hinge );
{
};
debuRenderWheel( simulation, wheel );
}
namespace utils
{
{
public:
public:
virtual bool keyboard( int key, unsigned int modKey, float x, float y, bool down ) override;
virtual void update( float x, float y ) override;
protected:
virtual ~TrackPropertyKeyboardHandler();
private:
private:
void traverse( TrackCallback callback ) const;
private:
TracksContainer m_tracks;
};
: Base( Base::ActivationMask( Base::KEYBOARD |
Base::UPDATE ) ),
m_tracks( tracks ),
m_application( application ),
m_stepForwardTime( 0.1, 0.0 )
{
}
TrackPropertyKeyboardHandler::~TrackPropertyKeyboardHandler()
{
}
bool TrackPropertyKeyboardHandler::keyboard( int key, unsigned int, float, float, bool down )
{
if ( m_tracks.empty() || m_tracks.contains( nullptr ) ) {
getSimulation()->remove( this );
return false;
}
const auto keyNumNodesPerSegment = key >= Base::KEY_F1 && key <= Base::KEY_F9;
const auto handled = keyNumNodesPerSegment;
if ( keyNumNodesPerSegment ) {
if ( !down ) {
{
} );
}
}
return handled;
}
void TrackPropertyKeyboardHandler::update( float, float )
{
if ( m_application == nullptr || m_tracks.empty() || m_tracks.contains( nullptr ) )
return;
if ( stepForwardTime > 0.0 )
m_stepForwardTime.update( stepForwardTime );
const auto refTrack = m_tracks.front();
const auto mergeEnabled = refTrack->getInternalMergeProperties()->getEnableMerge();
const auto numMergedNodes = refTrack->getInternalMergeProperties()->getNumNodesPerMergeSegment();
m_application->getSceneDecorator()->setText( 0,
m_stepForwardTime.get() ) );
m_application->getSceneDecorator()->setText( 1,
mergeEnabled ? "true" : "false" ),
m_application->getSceneDecorator()->setText( 2,
numMergedNodes ) );
}
void TrackPropertyKeyboardHandler::traverse( TrackPropertyKeyboardHandler::TrackCallback callback ) const
{
for ( auto track : m_tracks ) {
if ( refWheel == nullptr || refWheel->getConstraint() == nullptr )
continue;
if ( hinge != nullptr )
callback( track, hinge, index++ );
}
}
}
{
auto root =
new osg::Group();
35,
35,
-1,
5 ) );
ground->setPosition( 4, 7, -3 );
simulation->
add( ground );
const auto createConveyor = [](
agx::Real rollerRadius,
{
0,
0 );
for (
agx::UInt i = 0; i < numRollers; ++i ) {
width + rollerRadius ) ) );
i == numRollers - 1 ?
rollerRadius,
rollerRb );
conveyor->add( roller );
}
return conveyor;
};
const auto createConveyorVisual = [
root,
{
for ( auto wheel : conveyor.getWheels() )
for ( auto node : conveyor.nodes() )
};
{
auto hinge = wheel->attachHinge( "hinge", parent, 0.0 );
}
};
properties->setStabilizingHingeFrictionParameter( 4.0E-3 );
properties->setMinStabilizingHingeNormalForce( 50.0 );
{
};
{
continue;
auto hinge = wheel->getConstraint()->as<
agx::Hinge>();
}
};
auto conveyor1 = createConveyor( 0.1, 2.0, 0.01, 50, 260, 1.0E-3 );
setConveyorProperties( *conveyor1 );
conveyor1->setMaterial( conveyorMaterial );
simulation->
add( conveyor1 );
attachConveryor( *conveyor1, nullptr );
setSpeed( *conveyor1, 6.0 );
auto conveyor2 = createConveyor( 0.1, 2.5, 0.01, 25, 160, 1.0E-3 );
setConveyorProperties( *conveyor2 );
conveyor2->setMaterial( conveyorMaterial );
simulation->
add( conveyor2 );
attachConveryor( *conveyor2, nullptr );
setSpeed( *conveyor2, 6.5 );
boxConveyorCm->setRestitution( 0 );
boxConveyorCm->setFrictionCoefficient( 1.0 );
boxConveyorCm->setSurfaceViscosity( 1.0E-4 );
const auto spawnTimeEvery = 2.8;
const auto spawnPosition =
agx::Vec3( -4, 0, 0.62 );
spawnTimeEvery,
spawnPosition,
{
if ( spawnTimer < spawnTimeEvery ) {
return;
}
spawnTimer = 0.0;
simulation->
add(
new utils::TrackPropertyKeyboardHandler( utils::TrackPropertyKeyboardHandler::TracksContainer
{
conveyor1.get(),
}, application ) );
}
int main( int argc, char** argv )
{
"\tTutorial Tracked Vehicle (agxVehicle::Track)\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
application->addScene( buildTutorial4, '4' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
static agxCollide::HeightField * createFromFile(const agx::String &filename, agx::Real sizeX, agx::Real sizeY, agx::Real low, agx::Real high, agx::Real bottomMargin=agx::Real(1))
osg::Group * getNonShadowedScene() const
static agxRender::Color DarkRed()
static agxRender::Color LightSeaGreen()
bool remove(agx::Constraint *constraint)
Remove a constraint from this assembly (does not recurse down in tree).
const agx::Vec3 & getHalfExtents() const
agx::RigidBody * getRigidBody() const
agx::Real getLength() const
Exponential moving average statistic.
static agxCollide::HeightField * createHeightFieldFromFile(const agx::String &filename, agx::Real sizeX, agx::Real sizeY, agx::Real low, agx::Real high, agx::Real bottomMargin=agx::Real(1))
void setNumNodesPerMergeSegment(agx::UInt numNodesPerMergeSegment)
Assign number of nodes in a row that may merge together.
void setLockToReachMergeConditionCompliance(agx::Real compliance)
Assign compliance of the hinge lock used to reach merge condition.
void setEnableMerge(agx::Bool enable)
Enable/disable merge of nodes to segments in the track.
void setContactReduction(ContactReduction contactReductionLevel)
Set contact reduction level of merged nodes against other objects.
Node/segment/shoe object in agxVehicle::Track.
Object containing properties of an agxVehicle::Track.
void setHingeCompliance(agx::Real compliance)
Assign compliance (for all hinge degrees of freedom) used in the hinges between track nodes.
void setStabilizingHingeFrictionParameter(agx::Real frictionParameter)
Friction parameter of the internal friction in the node hinges.
@ SPLIT_SEGMENTS
Wheels with this property will split segments, i.e., intermediate nodes that are merged.
@ ROLLER
Roller - track return or road wheel.
agxVehicle::TrackInternalMergeProperties * getInternalMergeProperties() const
agxVehicle::TrackProperties * getProperties() const
void setProperties(agxVehicle::TrackProperties *properties)
Assign properties to this track.
agxVehicle::TrackWheel * findReferenceWheel() const
Reference wheel's rotation axis is defining the plane in which this track will be created.
const agxVehicle::Track::TrackWheelContainer & getWheels() const
virtual void setEnable(agx::Bool enable)
Enable/disable a constraint.
T * as()
Try to do dynamic_cast on the constraint and return a pointer to the casted constraint.
virtual void setDamping(agx::Real damping, agx::Int dof)
Set the damping of this constraint for the i:th DOF.
This class is a combined container which has the find complexity of a HashTable, deterministic iterat...
AGXPHYSICS_EXPORT void debugRenderFrame(const agx::AffineMatrix4x4 &transform, float scale, const agx::Vec4f &colorIdentifier, agxRender::RenderManager *mgr=nullptr)
T1 const lerp(T1 const &a, T1 const &b, T2 t)
Linearly interpolate from a to b using t = {0..1}.
tutorial_tree.cpp
{
std::cout << "\nTutorial 1: Basic Tree." << std::endl;
treeRoot->addBranch(branch1);
AffineMatrix4x4 transform = AffineMatrix4x4::rotate(Vec3::Y_AXIS(), Vec3::Z_AXIS()) *
AffineMatrix4x4::translate(0, 0, length1 / 2);
branch1->getRigidBody()->add(capsule1, transform);
osg::Group* root = new osg::Group();
branch1->addBranch(branch2);
transform = AffineMatrix4x4::rotate(Vec3::Y_AXIS(), Vec3::Z_AXIS()) *
AffineMatrix4x4::translate(0, 0, length2/2);
branch2->getRigidBody()->add(capsule2, transform);
Real length3 = (
Vec3(-0.5, 0, 2) -
Vec3(-r1 + r3, 0, 1)).length();
branch1->addBranch(branch3);
transform = AffineMatrix4x4::rotate(Vec3::Y_AXIS(), Vec3::Z_AXIS()) *
AffineMatrix4x4::translate(0, 0, length3 / 2);
tree->attach(
nullptr,
new Frame());
tree = tree->cut(branch_geom, branch_geom->getPosition());
return root;
}
Vec3 startPos,
Vec3 endPos,
int numSegments)
{
branch->addBranch(newBranch);
Real segLength = (endPos - startPos).length() / numSegments;
Real posZ = segLength / 2;
if (numSegments > 1) {
dr = (endRadius - startRadius) / (numSegments - 1);
}
for (int i = 0; i < numSegments; i++) {
AffineMatrix4x4::translate(0, 0, posZ);
r += dr;
posZ += segLength;
}
return newBranch;
}
{
std::cout << "\nTutorial 2: Bigger Tree." << std::endl;
osg::Group*
root =
new osg::Group();
int numSegments = 4;
std::list<agxModel::Tree::BranchRef> branches = {branch};
agx::Real dr = (endRadius - startRadius) / (numSegments - 1);
while (endRadius > 0.05) {
startRadius = endRadius + dr;
endRadius = startRadius - 0.02;
startPos = endPos;
endPos = startPos +
Vec3(random<Real>(-0.05, 0.05), random<Real>(-0.05, 0.05), 1);
branch = createBranch(branch, startRadius, endRadius, startPos, endPos, numSegments);
branches.push_back(branch);
}
startRadius = 0.04;
endRadius = 0.01;
numSegments = 5;
int nrBranches = random<int>(3,7);
for (int i = 0; i<nrBranches; i++) {
startPos = b->getRigidBody()->getFrame()->transformPointToWorld(0, 0, random<Real>(0,1));
endPos = startPos + new_dir * (length + random<Real>(-0.1,0.1));
branch = createBranch(b, startRadius, endRadius, startPos, endPos, numSegments);
}
length = length - 0.3;
}
tree->attach(
nullptr,
new Frame());
tree->addGroupID(123);
}
const Vec3& treeDir,
Real totalLength,
int numSegments,
Real startRadius,
Real endRadius,
int numSubBranches = 2, bool isBranch = false)
{
if (numSegments == 0 || totalLength < 1E-3 || startRadius < 1E-3 || endRadius < 1E-3)
return;
Real segmentLength = totalLength / numSegments;
Real dr = (startRadius - endRadius) / numSegments;
Real currEndRadius = startRadius;
int numInnerSegments = 3;
if (isBranch)
branchBend = 0.2;
for (int i = 0; i < numSegments; i++) {
random<Real>(-branchBend, branchBend),
random<Real>(-branchBend, branchBend))) * treeDir;
Vec3 endPos = startPos + newDir * segmentLength;
Real currStartRadius = currEndRadius - dr / numInnerSegments;
currEndRadius -= dr;
startPos, endPos, numInnerSegments);
if (i > 0 && i < numSegments - 1) {
random<Real>(-PI, PI))) *
Vec3(0, 0, 1);
Vec3 tempDir = newBranch->getRigidBody()->getFrame()->transformVectorToWorld(localDir);
Vec3 segmentPosition = startPos + newDir * random<Real>(0, 0.95 * totalLength / numSegments) +
endRadius * tempDir;
Real newLength = 0.4 * (numSegments - i) * totalLength / numSegments;
int newNumSegments = std::min(numSegments - i, numSubBranches + 1);
Real newSegmentsStartRadius = std::max(0.4 * currEndRadius, 0.02);
Real newSegmentsEndRadius = std::max(0.4 * endRadius, 0.01);
randomTreeGenerator(newBranch,
segmentPosition,
tempDir,
newLength,
newNumSegments,
newSegmentsStartRadius,
newSegmentsEndRadius,
numSubBranches,
true);
}
startPos += (newDir * segmentLength);
currBranch = newBranch;
}
}
if (branchConstraint != nullptr) {
Real translationalCompliance = 1 / (1500 * 1E6 * PI * r * r);
Real rotationalCompliance = translationalCompliance * 10;
branchConstraint->
setCompliance(translationalCompliance, LockJoint::TRANSLATIONAL_1);
branchConstraint->
setCompliance(translationalCompliance, LockJoint::TRANSLATIONAL_2);
branchConstraint->
setCompliance(translationalCompliance, LockJoint::TRANSLATIONAL_3);
branchConstraint->
setCompliance(rotationalCompliance, LockJoint::ROTATIONAL_1);
branchConstraint->
setCompliance(rotationalCompliance, LockJoint::ROTATIONAL_2);
branchConstraint->
setCompliance(rotationalCompliance, LockJoint::ROTATIONAL_3);
}
}
{
public:
: m_simulation(simulation), m_graphicsRoot(graphicsRoot) {}
{
if (c != nullptr)
Real tensileStrength = 100 * 1E6 * PI * r * r;
Real bendingStrength = 20 * 1E6 * PI * r * r;
branch->
setMaxLoad(tensileStrength, bendingStrength);
setConstraintParameters(branch, r);
}
{
}
virtual BranchListener*
clone()
{
return new BranchListener(m_simulation, m_graphicsRoot.get());
}
protected:
virtual ~BranchListener() {}
osg::observer_ptr<osg::Group> m_graphicsRoot;
};
{
std::cout << "\nTutorial 3: BranchEventListener." << std::endl;
randomTreeGenerator(tree->getRoot(),
Vec3(0, 0, 0), Vec3::Z_AXIS(), 35, 40, 0.5, 0.05);
osg::Group*
root =
new osg::Group();
BranchListener* bl = new BranchListener(simulation, root);
tree->setBranchEventListener(bl);
tree->addGroupID(123);
AffineMatrix4x4::translate(0, 0, -0.7));
tree->attach(ground,
new Frame());
tree->cut(branch);
}
{
std::cout << "\nTutorial 4: Wake up tree." << std::endl;
randomTreeGenerator(tree->getRoot(),
Vec3(0, 0, 0), Vec3::Z_AXIS(), 35, 40, 0.5, 0.05);
osg::Group*
root =
new osg::Group();
tree->setBranchEventListener(new BranchListener(simulation, root));
tree->addGroupID(123);
tree->attach(
nullptr,
new Frame());
tree->makeStatic(true);
AffineMatrix4x4::translate(1, 1, 42));
}
{
private:
int m_groupId = 0;
public:
CustomTreeExecuteFilter(int groupId)
m_groupId(groupId)
{
}
return collisionWithTree && collisionWithGroup;
}
bool match(
const agxCollide::GeometryPair& pair)
const {
bool collisionWithTree = pair.first->hasGroup(treeId) || pair.second->hasGroup(treeId);
bool collisionWithGroup = pair.first->hasGroup(m_groupId) || pair.second->hasGroup(m_groupId);
return collisionWithTree && collisionWithGroup;
}
return new CustomTreeExecuteFilter(m_groupId);
}
};
{
std::cout << "\nTutorial 5: Custom TreeExecuteFilter." << std::endl;
randomTreeGenerator(tree->getRoot(),
Vec3(0, 0, 0), Vec3::Z_AXIS(), 35, 40, 0.5, 0.05);
osg::Group*
root =
new osg::Group();
tree->setBranchEventListener(new BranchListener(simulation, root));
tree->addGroupID(123);
tree->attach(
nullptr,
new Frame());
int collisionGroupId = 321;
tree->makeStatic(true, new CustomTreeExecuteFilter(collisionGroupId));
AffineMatrix4x4::translate(1, 1, 55));
AffineMatrix4x4::translate(1, 1, 37));
simulation->
add(sphere2);
}
int main(int argc, char** argv)
{
std::cout <<
"\tTutorial Tree\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene(buildTutorial1, '1');
application->addScene(buildTutorial2, '2');
application->addScene(buildTutorial3, '3');
application->addScene(buildTutorial4, '4');
application->addScene(buildTutorial5, '5');
if (application->init(argc, argv))
return application->run();
}
catch (std::exception& e) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
agx::Real getRadius() const
bool hasGroup(agx::UInt32 id) const
This is performing a linear search among the collision group id for this Geometry.
BranchEventListener handles certain events happening to a Tree.
virtual void onHighLoad(agxModel::Tree::Branch *branch)
Event when branch branch connection is under high load.
virtual agxModel::Tree::BranchEventListener * clone()
Clone method used by cut.
virtual void onCreate(agxModel::Tree::Branch *branch)
Event when branch branch has been configured with position, orientation and constraint to parent (if ...
Branch is a part of the Tree, with a body and a constraint to its parent.
agx::RigidBody * getRigidBody() const
void setMaxLoad(agx::Real translational, agx::Real rotational)
Assign max load for this branch (connection at start).
TreeExecuteFilter allows to catch contacts with the Tree.
agx::UInt32 getTreeID() const
virtual agxModel::Tree::TreeExecuteFilter * clone()
Clone method of the TreeExecuteFilter.
virtual bool match(const agxCollide::GeometryContact &contact) const override
Find contacts where one geometry is the Tree.
Tree is a structure of Branch objects in a mathematical Tree structure where a Branch can have severa...
virtual Tree * cut(agxCollide::Geometry *geometry, const agx::Vec3 &point, agx::Real minShapeHeight=agx::Real(1E-2))
Cut geometry (only one shape per geometry supported) given point in world and minimum shape height,...
virtual bool match(const agxCollide::GeometryContact &gc) const
Called when narrow phase tests have determined overlap (when we have detailed intersection between th...
AGXPHYSICS_EXPORT void addGroup(const agx::RigidBody *body, unsigned id)
For the specified body, add the collision group ID to its geometries.
tutorial_trimesh.cpp
#include "tutorialUtils.h"
{
osg::Group* root = new osg::Group();
triangleMeshRigidBody->
setName(
"handmade" );
simulation->
add( triangleMeshRigidBody );
return root;
}
{
osg::Group* root = new osg::Group();
const char* collisionModel = "models/torusRoundForCollision.obj";
const char* visualModel = "models/torusRound.obj";
if ( !trimeshShape )
{
return root;
}
dynamicRBGeometry->setMaterial( material );
dynamicRBGeometry->add( trimeshShape);
dynamicRB->
add( dynamicRBGeometry );
simulation->
add( dynamicRB );
if ( visualTrimesh )
geometryNode->addChild( visualTrimesh );
else
root->addChild( geometryNode );
staticRB->
add( staticBodyGeometry );
staticBodyGeometry->setMaterial( material );
simulation->
add( staticRB );
simulation->
add( staticRB );
}
{
osg::Group*
root =
new osg::Group();
const char* collisionModel = "models/torusRoundForCollision.obj";
size_t numMeshesPerDim = 3;
for (size_t i = 0; i < numMeshesPerDim; ++i) {
for (size_t j = 0; j < numMeshesPerDim; ++j) {
triangleMeshRigidBody->
setName(
"MeshCopy" );
geom->setMaterial( material );
triangleMeshRigidBody->
add( geom );
objectDistance);
simulation->
add( triangleMeshRigidBody );
}
}
staticGeometry->setMaterial( material );
staticRB->
add( staticGeometry );
simulation->
add( staticRB );
simulation->
add( staticRB );
}
{
osg::Group*
root =
new osg::Group;
geo->add( trimesh );
bool hasHalfEdge = trimesh->hasHalfEdge();
std::cout << "The trimesh has " << (hasHalfEdge? "a " : "no ") << "half edge structure.\n";
bool isClosed = trimesh->isValidAndClosed();
std::cout << "The trimesh is " << (isClosed? "" : "not ") << "closed.\n";
bool triangleValid = triangle.
isValid();
std::cout << "The triangle is " << (triangleValid? "" : "not ") << "valid.\n";
std::cout << "Triangle 0 has the neighboring triangle with triangle index " <<
neighborTriangleIndex << " at edge 1.\n"
<< "This triangle is " << (neighborTriangle.isValid() ? "" : "not ") << "valid\n";
size_t neighborEdgeIndex = triangle.getHalfEdgePartnerLocalEdgeIndex(1);
std::cout << "Edge 1 from triangle 0 has the index " << neighborEdgeIndex <<
" in the triangle with index " << neighborTriangleIndex << "\n";
}
namespace {
{
public:
IteratorMover(
agxCollide::Geometry* geometry,
unsigned int startTriangle, uint_fast8_t startVertex )
: m_geometry(geometry)
{
if (m_geometry) {
if (m_geometry->getShapes().size()) {
if (mesh) {
m_trimesh = mesh;
}
}
}
setMask( GuiEventListener::KEYBOARD );
}
virtual bool keyboard(
int key,
unsigned ,
float ,
float ,
bool keydown ) {
if ( keydown && ( key == GuiEventListener::KEY_Right ) ) {
std::cout << "pressed right" << std::endl;
m_circulator++;
RenderTriangle();
return true;
}
return false;
}
private:
void RenderTriangle()
{
if (m_trimesh && m_circulator.isValid()) {
return;
triangle.
getVertex(1) * m_geometry->getTransform(),
triangle.
getVertex(2) * m_geometry->getTransform() };
int index = m_circulator.getLocalEdgeIndex();
agx::Vec3 normalStart = (verts[index] + verts[(index + 1) % 3]) * 0.5;
}
}
};
}
namespace {
{
public:
agxSDK::StepEventListener(
agxSDK::StepEventListener::POST_STEP),
m_trimeshGeo( trimeshGeo), m_lineGeo( lineGeo ), m_color(color), m_maxIt(maxIt)
{
updateValid();
}
private:
void updateValid()
{
if (!m_valid)
LOGGER_WARNING() <<
"TriangleTraverser should have one geometry with exactly one line and one with exactly one trimesh. Ignoring.\n" <<
LOGGER_END();
}
{
updateValid();
if (!m_valid)
return;
unsigned int triangleIndex;
trimeshToWorld = m_trimeshGeo->getShapes()[0]->getLocalTransform() * trimeshToWorld;
lineToWorld = m_lineGeo->getShapes()[0]->getLocalTransform() * lineToWorld;
if (!line) {
return;
}
if (!trimesh) {
return;
}
agx::Vec3 lineStartInTrimesh = lineStartInWorld * worldToTrimesh;
agx::Vec3 lineEndInTrimesh = lineEndInWorld * worldToTrimesh;
bool foundPoint = findIntersectionLineSegmentMesh( lineStartInTrimesh, lineEndInTrimesh, trimesh, lineSegmentT, triangleIndex );
if (!foundPoint)
return;
agx::Vec3 hitPointInWorld = lineStartInWorld * (1-lineSegmentT) + lineEndInWorld * lineSegmentT;
agx::Vec3 dirInTrimesh = lineEndInTrimesh - lineStartInTrimesh;
agx::Vec3 dirProjToTrimeshXY( dirInTrimesh[0], dirInTrimesh[1], 0 );
agx::Vec3 pointInTrimesh = hitPointInWorld * worldToTrimesh;
size_t numTriangles = 0;
size_t edgeIndex = 3;
bool atEndLength = false;
const agx::Real segmentRestLength = (lineEndInTrimesh - pointInTrimesh).length();
while (numTriangles < m_maxIt && triangle.
isValid() && !atEndLength) {
uint_fast8_t newEdgeIndex = 3;
for (uint_fast8_t i = 0; i < 3; ++i) {
if (i == edgeIndex)
continue;
if (agxCollide::intersectLineSegmentHyperPlane(pointInTrimesh, lineEndInTrimesh,
if (tmpT >= 0 && tmpT < newT) {
newT = tmpT;
newEdgeIndex = i;
newPointInTrimesh = tmpPoint;
}
}
}
if (newEdgeIndex == 3) {
atEndLength = true;
}
else {
if ((newPointInTrimesh - lineEndInTrimesh) * dirProjToTrimeshXY > 0) {
atEndLength = true;
}
}
if (atEndLength) {
newPointInTrimesh = lineEndInTrimesh;
}
else {
LOGGER_WARNING() <<
"An error occurred while intersecting the ray with triangles:\n"
<<
"tested triangle normal is orthogonal to terrain normal.\n" <<
LOGGER_END();
}
traversedLength += (newPointInTrimesh - pointInTrimesh).length();
if ( traversedLength > segmentRestLength) {
atEndLength = true;
agx::Vec3 dir = newPointInTrimesh - pointInTrimesh;
newPointInTrimesh -= dir * (traversedLength - segmentRestLength);
}
pointInTrimesh = newPointInTrimesh;
if (!atEndLength){
}
numTriangles++;
}
}
private:
size_t m_maxIt;
bool m_valid;
};
}
{
std::cout << "Trimesh traversal along a line.\nPress 'g' to turn on debug rendering.\n" <<
"Observe how the lines attached to the sphere are projected to the trimesh, beginning from the first intersection.\n";
osg::Group*
root =
new osg::Group();
if ( trimesh ) {
trimeshGeo->add( trimesh );
}
else
if ( trimeshGeo ) {
trimeshBody->
add( trimeshGeo );
}
else
simulation->
add( trimeshBody );
sphereBody->
add(sphereGeo);
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 2; ++j) {
lineGeo->setEnableCollisions(trimeshGeo, false);
lineGeo->setSensor(true);
simulation->
add(
new TriangleTraverser(trimeshGeo, lineGeo,
agx::Vec4(dirs[i], 1)));
sphereBody->
add( lineGeo );
}
}
simulation->
add(sphereBody);
}
{
std::cout << "Trimesh traversal along a line.\nPress 'g' to turn on debug rendering.\n" <<
"Observe how the lines attached to the sphere are projected to the trimesh, beginning from the first intersection.\n";
osg::Group*
root =
new osg::Group();
if ( trimesh ) {
trimeshGeo->add( trimesh );
}
else
if ( trimeshGeo ) {
trimeshBody->
add( trimeshGeo );
}
else
simulation->
add( trimeshBody );
sphereBody->
add(sphereGeo);
for (int i = 0; i < 3; ++i) {
{
lineGeo->setEnableCollisions(trimeshGeo, false);
lineGeo->setSensor(true);
simulation->
add(
new TriangleTraverser(trimeshGeo, lineGeo,
agx::Vec4(dirs[i], 1)));
sphereBody->
add( lineGeo );
}
{
lineGeo->setEnableCollisions(trimeshGeo, false);
lineGeo->setSensor(true);
simulation->
add(
new TriangleTraverser(trimeshGeo, lineGeo,
agx::Vec4(dirs[i], 1)));
sphereBody->
add( lineGeo );
}
}
simulation->
add(sphereBody);
}
namespace {
struct Settings {
double reductionRatio = 0;
double aggressiveness = 0;
Settings(double reduction, double agg) :
reductionRatio(reduction),
aggressiveness(agg)
{
}
Settings(const Settings& s) :
reductionRatio(s.reductionRatio),
aggressiveness(s.aggressiveness)
{
}
};
std::ostream&
operator <<(std::ostream& os,
const Settings& s)
{
os <<
agx::String::format(
"ReductionRatio=%f, aggressiveness=%f", s.reductionRatio, s.aggressiveness);
return os;
}
{
public:
{
}
double getTime() const
{
return m_time;
}
Settings getSettings() const
{
return m_settings;
}
{
return m_trimesh;
}
{
agxUtil::reduceMesh(m_meshData->getVertices(), m_meshData->getIndices(), outVertices, outIndices, m_settings.reductionRatio, m_settings.aggressiveness);
}
private:
const Settings& m_settings;
double m_time;
};
}
{
osg::Group*
root =
new osg::Group();
"models/bunny.obj",
rotateAndScale);
if (!originalTrimesh)
std::vector<Settings> settings = {
Settings({0.99999,7}),
Settings({0.9,7}),
Settings({0.6,7}),
Settings({0.3,7}),
Settings({0.1,7}),
Settings({0.05,100}),
Settings({0.2,10}),
Settings({0.4,200}),
Settings({0.8,200}),
};
std::cerr << "Creating Mesh reducers..." << std::endl;
for (const auto& s : settings)
{
auto reducer = new MeshReducer(originalTrimesh->getMeshData(), s);
}
std::for_each(meshReducers.
begin(), meshReducers.
end(), [](MeshReducer* c) { c->start(); });
std::for_each(meshReducers.
begin(), meshReducers.
end(), [](MeshReducer* c) { c->join(); });
int i = 0;
for (auto t : meshReducers)
{
std::cerr << t->getSettings() << ": " << t->getTime() << " ms." << std::endl;
{
meshRigidBody->
add(geometry);
}
meshRigidBody->
setName(
"MeshReduction");
std::stringstream str;
str << t->getSettings() << ": " << t->getTrimesh()->getMeshData()->getIndices().size()/3 << " triangles" << std::ends;
simulation->
add(meshRigidBody);
i++;
}
meshReducers.clear();
staticRB->
add(staticBodyGeometry);
simulation->
add(staticRB);
}
int main( int argc, char** argv )
{
std::cout <<
"\tTutorial Trimesh\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
application->addScene( traversalTutorial1, '5' );
application->addScene( traversalTutorial2, '6' );
application->addScene(traversalTutorial3, '7');
application->addScene(reduceMeshTutorial, '8');
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
A line shape for intersection tests, mostly for depth sensing and picking.
agx::Vec3 getFirstPoint() const
Returns the first point, in local coordinates.
agx::Vec3 getSecondPoint() const
Returns the second point, in local coordinates.
Stores triangle mesh data as triangle lists, i.e.
Class for more intuitive access to the Mesh's mesh data.
agx::Vec3 getVertex(uint_fast8_t localVertexIndex) const
agx::Vec3 getEdgeEndVertex(uint_fast8_t localEdgeIndex) const
agx::Vec3 getEdgeStartVertex(uint_fast8_t localEdgeIndex) const
agx::Vec3 getNormal() const
This function returns the triangle normal (as stored in the collision mesh data).
size_t getTriangleIndex() const
Implementations.
size_t getHalfEdgePartnerLocalEdgeIndex(uint_fast8_t localEdgeIndex) const
const Triangle getHalfEdgePartner(uint_fast8_t localEdgeIndex) const
Class in order to circulate over the edges connected to a Triangle's vertex.
VertexEdgeCirculator createVertexEdgeCirculator(size_t triangleIndex, uint_fast8_t localVertexIndex) const
Creates an VertexEdgeCirculator pointing to this Triangle and a Voronoi region.
const Triangle getTriangle(size_t triangleIndex) const
void clearAll()
Clear everything.
void setContactReductionBinResolution(agx::UInt8 binResolution)
Specify the bin resolution used when evaluating contacts for reduction between body-body contacts.
AGXOSG_EXPORT osg::Node * readNodeFile(const std::string &filename, bool searchForIve=false)
Read a specified filename from disk and return it as a OpenSceneGraph node.
AGXPHYSICS_EXPORT agxCollide::Trimesh * createTrimeshTerrain(const agx::String &filename, agx::Real bottomMargin, uint32_t trimeshOptions=agxCollide::Trimesh::REMOVE_DUPLICATE_VERTICES, const agx::Matrix3x3 &vertexRotateAndScale=agx::Matrix3x3(), const agx::Vec3 &vertexTranslate=agx::Vec3())
Create a Terrain shape from a supported mesh model file (See agxIO::MeshReader::FileType).
AGXPHYSICS_EXPORT bool reduceMesh(const agx::Vec3Vector &vertices, const agx::UInt32Vector &indices, agx::Vec3Vector &outVertices, agx::UInt32Vector &outIndices, double reductionRatio=0.5, double aggressiveness=7.0)
Perform mesh reduction using Fast-Quadric-Mesh.
tutorial_wind.cpp
#include "tutorialUtils.h"
namespace
{
{
}
{
}
{
return getShape( getGeometry( rb ) );
}
{
public:
: m_windGenerator( windGenerator ), m_app( app ) {
setMask( KEYBOARD | UPDATE ); }
virtual bool keyboard(
int key,
unsigned int ,
float ,
float ,
bool down )
override
{
m_windGenerator->setVelocity( m_windGenerator->getVelocity() +
agx::Real( 0.1 ) * m_windGenerator->getVelocity().normal() );
m_windGenerator->setVelocity( m_windGenerator->getVelocity() -
agx::Real( 0.1 ) * m_windGenerator->getVelocity().normal() );
m_windGenerator->setVelocity( dq * m_windGenerator->getVelocity() );
m_windGenerator->setVelocity( dq.conj() * m_windGenerator->getVelocity() );
}
virtual void update(
float ,
float )
override
{
if ( m_app == 0L )
return;
m_app->getSceneDecorator()->setText( 0, "Change wind speed: Key up and key down." );
m_app->getSceneDecorator()->setText( 1, "Change wind direction (in xy plane): Key left and key right." );
m_app->getSceneDecorator()->setText( 2,
agx::String::format(
"Wind velocity: [%2.3f %2.3f %2.3f] m/s", m_windGenerator->getVelocity().x(), m_windGenerator->getVelocity().y(), m_windGenerator->getVelocity().z() ),
agxRender::Color::Red() );
}
private:
};
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( controller );
simulation->
add( ground );
simulation->
add( sphere );
simulation->
add( boxWire );
controller->setEnableAerodynamics( sphere, true );
controller->setEnableAerodynamics( box, true );
controller->setWindGenerator( windGenerator );
simulation->
add( new ::Tutorial1WindControl( windGenerator, application ) );
}
namespace
{
{
public:
: m_shadowingGeometry( shadowingGeometry ), m_halfExtents( halfExtents ), m_windVelocity( windVelocity )
{
}
{
m_cachedFarPlanePos[ 0 ] = m_shadowingGeometry->getPosition() + m_shadowingGeometry->getFrame()->transformVectorToWorld(
agx::Vec3( -m_halfExtents.x(), 0, 0 ) );
m_cachedFarPlanePos[ 1 ] = m_shadowingGeometry->getPosition() + m_shadowingGeometry->getFrame()->transformVectorToWorld(
agx::Vec3( 0, m_halfExtents.y(), 0 ) );
m_cachedFarPlanePos[ 2 ] = m_shadowingGeometry->getPosition() + m_shadowingGeometry->getFrame()->transformVectorToWorld(
agx::Vec3( 0, -m_halfExtents.y(), 0 ) );
m_cachedFarPlanePos[ 3 ] = m_shadowingGeometry->getPosition() + m_shadowingGeometry->getFrame()->transformVectorToWorld(
agx::Vec3( 0, 0, m_halfExtents.z() ) );
}
{
return isShadowed( worldPoint ) ?
agx::Vec3() : m_windVelocity;
}
protected:
CustomWindGenerator() {}
private:
{
return worldPoint.
x() < m_cachedFarPlanePos[ 0 ].x() &&
worldPoint.
y() < m_cachedFarPlanePos[ 1 ].y() &&
worldPoint.
y() > m_cachedFarPlanePos[ 2 ].y() &&
worldPoint.
z() < m_cachedFarPlanePos[ 3 ].z();
}
private:
};
{
}
{
m_shadowingGeometry = shadowingGeometry;
}
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( controller );
simulation->
add( ground );
simulation->
add( shadowingBox );
simulation->
add( sphere1 );
simulation->
add( sphere2 );
simulation->
add( sphere3 );
simulation->
add( sphere4 );
controller->setEnableAerodynamics( sphere1, true );
controller->setEnableAerodynamics( sphere2, true );
controller->setEnableAerodynamics( sphere3, true );
controller->setEnableAerodynamics( sphere4, true );
controller->setWindGenerator( new ::CustomWindGenerator( shadowingBox, shadowingBoxHalfExtents, windVelocity ) );
}
int main( int argc, char** argv )
{
"\tTutorial hydrodynamics\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
Class to generate constant wind in a scene.
Interface for generating wind effects.
virtual void prepare(const agxCollide::Geometry *geometry)
Called before update of geometry.
tutorial_wire1.cpp
#include <osg/ShapeDrawable>
#include "tutorialUtils.h"
template < typename T >
{
public:
: m_wire( wire ), m_spline( new T() )
{
osg::Cylinder* cylinder = new osg::Cylinder();
cylinder->set( osg::Vec3(), (float)m_wire->getRadius(), 1 );
cylinder->setRotation( osg::Quat( -
agx::PI_2, osg::Vec3( 1, 0, 0 ) ) );
m_osgCylinder = new osg::Geode;
m_osgCylinder->setCullingActive( false );
osg::TessellationHints* th = new osg::TessellationHints();
th->setDetailRatio( 0.4f );
osg::ShapeDrawable* drawable = new osg::ShapeDrawable( cylinder, th );
m_osgCylinder->getOrCreateStateSet();
m_osgCylinder->addDrawable( drawable );
m_group = new osg::Group;
m_group->setDataVariance( osg::Object::STATIC );
root->addChild( m_group.get() );
}
protected:
virtual ~RenderWireListener() {}
{
if( m_wire.isValid() )
render();
}
{
if ( !m_wire.isValid() ) {
return;
}
render();
}
void render()
{
m_spline->clear();
size_t objCounter = 0;
if ( !m_wire->getRenderListEmpty() ) {
while ( renderIt != renderEndIt ) {
m_spline->add( node->
getWorldPosition(), agx::clamp< agx::Real >( m_wire->getTension( node ).prev.raw / refMaxTension, 0, 1 ) );
}
else
++renderIt;
}
m_spline->updateTangents();
const size_t numControlPoints = m_spline->getNumPoints() - 1;
const size_t numSegments = 40;
for ( size_t i = 0; i < numControlPoints; ++i ) {
bool valid = addCylinder( prevPoint, point, objCounter );
objCounter += valid;
prevPoint = point;
}
start = 0;
}
}
if ( objCounter < m_group->getNumChildren() ) {
m_group->removeChildren( (unsigned int)objCounter, m_group->getNumChildren() - (unsigned int)objCounter );
}
}
{
if ( length < 1E-3 )
return false;
bool reused = index < m_group->getNumChildren();
osg::MatrixTransform* worldTransform = reused ? dynamic_cast< osg::MatrixTransform* >( m_group->getChild( (unsigned int)index ) ) : new osg::MatrixTransform;
assert( worldTransform );
assert( !reused || worldTransform->getNumChildren() == 1 );
osg::MatrixTransform* scaleTransform = reused ? dynamic_cast< osg::MatrixTransform* >( worldTransform->getChild( 0 ) ) : new osg::MatrixTransform;
assert( scaleTransform );
scaleTransform->setMatrix( osg::Matrix::scale( 1, length, 1 ) );
if ( !reused ) {
scaleTransform->addChild( m_osgCylinder.get() );
worldTransform->addChild( scaleTransform );
worldTransform->setDataVariance( osg::Object::STATIC );
worldTransform->computeBound();
m_group->addChild( worldTransform );
}
return true;
}
protected:
osg::ref_ptr< osg::Geode > m_osgCylinder;
osg::ref_ptr< osg::Group > m_group;
};
{
osg::Group*
root =
new osg::Group();
agx::Real wireResolutionPerUnitLength( 0.5 );
bool collisionsEnabled = true;
}
{
osg::Group*
root =
new osg::Group();
agx::Real wireResolutionPerUnitLength( 0.5 );
bool collisionsEnabled = true;
{
wire->add( beginNode );
wire->add( endNode );
simulation->
add(
new RenderWireListener< agxUtil::CardinalSpline >( wire, root ) );
}
{
simulation->
add( rbBox );
wire->add( beginNode );
wire->add( endNode );
simulation->
add(
new RenderWireListener< agxUtil::CardinalSpline >( wire, root ) );
}
{
simulation->
add( rbBox );
wire->add( beginNode );
wire->add( boxNode1 );
wire->add( boxNode2 );
wire->add( endNode );
simulation->
add(
new RenderWireListener< agxUtil::CardinalSpline >( wire, root ) );
}
{
simulation->
add( beginBox );
simulation->
add( endBox );
simulation->
add( slidingBox1 );
simulation->
add( slidingBox2 );
wire->add( beginNode );
wire->add( eyeBox1 );
wire->add( eyeBox2 );
wire->add( endNode );
simulation->
add(
new RenderWireListener< agxUtil::CardinalSpline >( wire, root ) );
}
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( rbBox1 );
simulation->
add( rbBox2 );
simulation->
add( rbBox3 );
{
winch->setForceRange( maxWinchForce );
winch->setSpeed( desiredWinchSpeed );
winch->setBrakeForceRange( brakeForce );
winch->setAutoFeed( true );
wire->add( winch, wireLength );
std::cout << " * Wire 1 *" << std::endl;
std::cout << " - Rest length after initialize: " << wire->getRestLength() << " m." << std::endl;
std::cout << " - Winch pulled in length after initialize: " << wire->getWinchController( 0 )->getPulledInWireLength( ) << " m." << std::endl << std::endl;
}
{
winch->setAutoFeed( false );
wire->add( winch, wireLengthInsideWinch );
std::cout << " * Wire 2 *" << std::endl;
std::cout << " - Rest length after initialize: " << wire->getRestLength() << " m." << std::endl;
std::cout << " - Winch pulled in length after initialize: " << wire->getWinchController( 0 )->getPulledInWireLength( ) << " m." << std::endl << std::endl;
}
{
agx::Real wireResolutionPerUnitLength( 0.333 );
winch1->setAutoFeed( true );
winch2->setAutoFeed( true );
wire->add( winch1, initialLengthInWinch1 );
wire->add( winch2, initialLengthInWinch2 );
std::cout << " * Wire 3 *" << std::endl;
std::cout << " - Rest length after initialize: " << wire->getRestLength() << " m." << std::endl;
std::cout << " - Winch 1 pulled in length after initialize: " << wire->getWinchController( 0 )->getPulledInWireLength( ) << " m." << std::endl;
std::cout << " - Winch 2 pulled in length after initialize: " << wire->getWinchController( 1 )->getPulledInWireLength( ) << " m." << std::endl << std::endl;
}
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( assembly );
rbBox->
add( rbBoxGeometry );
rbBoxGeometry->setMaterial( rbBoxMaterial );
assembly->add( rbBox );
for ( size_t i = 0; i < 2; ++i ) {
assembly->add( rb1 );
assembly->add( rb2 );
simulation->
add( wires[ i ] );
}
wires[ 0 ]->setMaterial( wire1Material );
wires[ 1 ]->getMaterial()->getBulkMaterial()->setYoungsModulus( 2E8 );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( rbBox1 );
simulation->
add( slidingBox );
wire->add( eye );
eye->getMaterial()->setFrictionCoefficient(
agx::Real( 1.5 ) );
wire->setEnableCollisions( slidingBox, false );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( rbBox1 );
simulation->
add( rbBox2 );
simulation->
add( dynBox1 );
simulation->
add( dynBox2 );
wire->getMaterial()->getWireMaterial()->setYoungsModulusStretch( 1E9 );
wire->getMaterial()->getWireMaterial()->setYoungsModulusBend( 1 );
wire->getMaterial()->getBulkMaterial()->setDensity( 1000 );
wire->getMaterial()->getSurfaceMaterial()->setRoughness( 0 );
}
{
planeGeometry->setName("Plane1");
planeGeometry->setMaterial(planeMaterial);
simulation->
add(rbBoxPlane);
structure->
add(rbBoxPlane);
planeGeometry2->setName("Plane2");
planeGeometry2->setMaterial(planeMaterial);
simulation->
add(rbBoxPlane2);
structure->
add(rbBoxPlane2);
simulation->
add(rbBoxWinch);
structure->
add(rbBoxWinch);
pipeGeom->setName("Pipe");
pipeGeom->setMaterial(pipeMaterial);
simulation->
add(rbPipeMesh);
structure->
add(rbPipeMesh);
wire->setMaterial(wireMaterial);
winch->setForceRange(maxWinchForce);
winch->setSpeed(desiredWinchSpeed);
winch->setBrakeForceRange(brakeForce);
winch->setAutoFeed(true);
wire->add(winch, wireLength);
simulation->
add(structure);
return structure;
}
{
osg::Group*
root =
new osg::Group();
agxSDK::AssemblyRef s1 = createScene7Structure(simulation, application, root, !useDynamicContacts, -translate);
agxSDK::AssemblyRef s2 = createScene7Structure(simulation, application, root, useDynamicContacts, translate);
}
{
for(
agx::Int i = 0; i < numPoints; i++ ) {
convexPoints.
push_back(
agx::Vec3( radius * std::cos( angle ) * scaling.
x(), 0, radius * std::sin( angle ) * scaling.
z() ) + center );
angle += step;
}
return convexPoints;
}
{
for(
agx::UInt i = 0, numVertices = vertices.
size(); i < numVertices; ++i )
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( rbBox );
{
simulation->
add( eyeNodeAreaBody );
simulation->
add( weight );
simulation->
add( eyeBody );
wire->add( eyeNode );
simulation->
add( wireRenderer );
simulation->
add( eyeNodeArea );
}
{
meshReader->readFile( "models/torusForCollision.obj" );
::scaleMesh( meshReader->getVertices(), scaling );
simulation->
add( eyeNodeAreaBody );
simulation->
add( weight );
simulation->
add( eyeBody );
wire->add( eyeNode );
simulation->
add( wireRenderer );
auto convexPoints = createConvexPoints( 10, 0.8, eyeNodeAreaBody->
getCmLocalTranslate(), scaling );
simulation->
add( eyeNodeArea );
}
}
int main( int argc, char** argv )
{
"\tTutorial wire\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
application->addScene( buildTutorial4, '4' );
application->addScene( buildTutorial5, '5' );
application->addScene( buildTutorial6, '6');
application->addScene( buildTutorial7, '7' );
application->addScene( buildTutorial8, '8' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
#define AGX_MAT4X4_TO_OSG(X)
Point class for storing 3D point, tension, stretch and curvature.
Class for defining a circular area in which the eye node is allowed to exist.
Class for defining a convex area in which the eye node is allowed to exist.
Constraint that allows an eye node to move in a 2D area relative a rigid body.
Eye node is a type of node that can slide along a wire with or without friction.
Class defining a node that is part of a wire.
@ BODY_FIXED
Body fixed node has a fixed position relative a parent (body or world).
const agx::Vec3 & getWorldPosition() const
Node::Type getType() const
Proxy class for wire iterators.
agx::Bool setEnableDynamicWireContacts(agxCollide::Geometry *geometry, agx::Bool enable)
Enable, or disable, the use of dynamic wire contact model against this geometry.
static WireController * instance()
Winch class for handling in and out hauling of wires.
agx::Vec3 getCmLocalTranslate() const
AGXPHYSICS_EXPORT agx::AffineMatrix4x4 calculateCylinderTransform(const agx::Vec3 &startPoint, const agx::Vec3 &endPoint)
Calculates transform that can be used for cylinders/capsules like geometries.
tutorial_wireLink.cpp
namespace
{
{
}
{
if ( root != 0L ) {
wireRenderer->setColor( color );
simulation->
add( wireRenderer );
}
}
}
{
osg::Group*
root =
new osg::Group();
link->getRigidBody()->setPosition( 0, 0, 2 );
wire1->add( link );
wire2->add( link );
simulation->
add( wire1 );
simulation->
add( wire2 );
}
{
osg::Group*
root =
new osg::Group();
beginLink->getRigidBody()->setPosition( -15, 0, 3 );
link12->getRigidBody()->setPosition( -5, 0, 0 );
link23->getRigidBody()->setPosition( 5, 0, 5 );
endLink->getRigidBody()->setPosition( 12, 0, -3 );
wire1->add( beginLink );
wire1->add( link12 );
wire2->add( link12 );
wire2->add( link23 );
wire3->add( link23 );
wire3->add( endLink );
simulation->
add( wire1 );
simulation->
add( wire2 );
simulation->
add( wire3 );
}
{
osg::Group*
root =
new osg::Group();
link->getRigidBody()->setPosition( 0, 0, 2 );
simulation->
add( winchRb );
wire1->add( winch );
wire1->add( link );
wire2->add( link );
simulation->
add( wire1 );
simulation->
add( wire2 );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( ground );
winch->add( pulledInWire1, 150 );
winch->add( pulledInWire2, 80 );
winch->add( pulledInWire3, 250 );
activeLink12->getRigidBody()->setPosition( 8, 0, 1 );
activeWire1->add( winch, 5 );
activeWire1->add( activeLink12 );
activeWire2->add( activeLink12 );
simulation->
add( activeWire1 );
simulation->
add( activeWire2 );
}
{
osg::Group*
root =
new osg::Group();
link1->getRigidBody()->setPosition( 0, -yOffset, 0 );
link2->getRigidBody()->setPosition( 0, 0, 0 );
link3->getRigidBody()->setPosition( 0, yOffset, 0 );
}
{
osg::Group*
root =
new osg::Group();
link1->getDefaultWireConnectionProperties()->setBendStiffness( connectionStiffness );
link2->getDefaultWireConnectionProperties()->setBendStiffness( connectionStiffness );
link3->getDefaultWireConnectionProperties()->setBendStiffness( connectionStiffness );
link1->getRigidBody()->setPosition( 0, -yOffset, 0 );
link2->getRigidBody()->setPosition( 0, 0, 0 );
link3->getRigidBody()->setPosition( 0, yOffset, 0 );
}
{
osg::Group*
root =
new osg::Group();
simulation->
add( ground );
link1->getRigidBody()->setPosition( -0.3, 0, 1 );
link2->getRigidBody()->setPosition( 0.3, 0, 1 );
wire1->add( link1,
agx::Vec3( -0.3, 0, 0 ) );
link1->connect( link2, agx::Constraint::createFromBody< agx::Hinge >(
agx::Vec3( 0.3, 0, 0 ),
agx::Vec3::X_AXIS(), link1->getRigidBody(), link2->getRigidBody() ) );
if ( link2ToLink1Connection != link1ToLink2Connection )
std::cout << "Expected: The nodes are not the same." << std::endl;
else
std::cout << "THE NODES ARE SAME?!" << std::endl;
if ( thisIsLink2ToLink1Connection == link2ToLink1Connection )
std::cout << "Expected: link2ToLink1Connection FOUND!" << std::endl;
else
std::cout << "link2ToLink1Connection LOST?!" << std::endl;
if ( h1 == h2 )
std::cout << "Expected: Access to the constraint from both links works!" << std::endl;
else
std::cout << "Where are the constraints?!" << std::endl;
link1->getRigidBody()->setAngularVelocity( 2.5, 0, 0 );
}
int main( int argc, char** argv )
{
application->addScene( buildScene1, '1' );
application->addScene( buildScene2, '2' );
application->addScene( buildScene3, '3' );
application->addScene( buildScene4, '4' );
application->addScene( buildScene5, '5' );
application->addScene( buildScene6, '6' );
application->addScene( buildScene7, '7' );
try {
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
void setBendStiffness(agx::Real bendStiffness)
Assign bend stiffness of the link to wire connection.
Interface class for nodes connected to links.
virtual agxWire::ILinkNode * getLinkConnection() const =0
virtual agxWire::ILinkNode::ConnectionProperties * getConnectionProperties() const =0
Object that defines the relation between different types of wires (agxWire::Wire).
@ WIRE_END
Link attached at end of wire.
@ WIRE_BEGIN
Link attached at begin of wire.
static agx::Bool connect(agxWire::Wire *wire1, const agx::Vec3 &relativeTranslate1, agxWire::Link *link, agxWire::Wire *wire2, const agx::Vec3 &relativeTranslate2)
Utility function to connect two wires with a link.
tutorial_wireWindAndWater.cpp
#include "tutorialUtils.h"
namespace
{
{
return node;
}
}
{
osg::Group*
root =
new osg::Group( );
waterGeometry->setPosition( 0, 0, -10 );
waterGeometry->setMaterial( waterMaterial );
::createWaterVisual( waterGeometry, root );
controller->addWater( waterGeometry );
simulation->
add( controller );
simulation->
add( waterGeometry );
simulation->
add( wire1 );
wire1->setEnableCollisions( waterGeometry, false );
controller->setEnableAerodynamics( wire1, false );
simulation->
add( wireRenderer1 );
simulation->
add( wire2 );
simulation->
add( wireRenderer2 );
simulation->
add( wire3 );
simulation->
add( wireRenderer3 );
}
{
osg::Group*
root =
new osg::Group( );
simulation->
add( controller );
simulation->
add( wire1 );
simulation->
add( wireRenderer1 );
simulation->
add( wire2 );
controller->setEnableAerodynamics( wire2, true );
simulation->
add( wireRenderer2 );
simulation->
add( wire3 );
controller->setEnableAerodynamics( wire3, true );
simulation->
add( wireRenderer3 );
}
{
osg::Group*
root =
new osg::Group( );
simulation->
add( controller );
controller->setWindGenerator( windGenerator );
simulation->
add( wire1 );
simulation->
add( wireRenderer1 );
simulation->
add( wire2 );
controller->setEnableAerodynamics( wire2, true );
simulation->
add( wireRenderer2 );
simulation->
add( wire3 );
controller->setEnableAerodynamics( wire3, true );
simulation->
add( wireRenderer3 );
}
int main( int argc, char** argv )
{
"\tTutorial wire - wind and water\n" <<
"\t--------------------------------\n\n" << std::endl;
try {
application->addScene( buildTutorial1, '1' );
application->addScene( buildTutorial2, '2' );
application->addScene( buildTutorial3, '3' );
if ( application->init( argc, argv ) )
return application->run();
}
catch ( std::exception& e ) {
std::cerr << "\nCaught exception: " << e.what() << std::endl;
return 1;
}
return 0;
}