Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a RealTimeWorldNode for displaying simulations at real-time speeds #1216

Merged
merged 14 commits into from
Jan 10, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
* GUI

* Reorganized OpenGL and GLUT files: [#1088](https://github.com/dartsim/dart/pull/1088)
* Added the RealTimeWorldNode to display simulations at real-time rates: [#1216](https://github.com/dartsim/dart/pull/1216)

* Misc

Expand Down
2 changes: 1 addition & 1 deletion dart/gui/osg/ImGuiHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ ImGuiHandler::ImGuiHandler()
mMouseWheel{0.0f},
mFontTexture{0u}
{
// Do nothing
ImGui::CreateContext();
}

//==============================================================================
Expand Down
166 changes: 166 additions & 0 deletions dart/gui/osg/RealTimeWorldNode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* Copyright (c) 2019, The DART development contributors
mxgrey marked this conversation as resolved.
Show resolved Hide resolved
* All rights reserved.
*
* The list of contributors can be found at:
* https://github.com/dartsim/dart/blob/master/LICENSE
*
* This file is provided under the following "BSD-style" License:
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

#include "dart/common/Console.hpp"
#include "dart/simulation/World.hpp"
#include "dart/gui/osg/RealTimeWorldNode.hpp"

namespace dart {
namespace gui {
namespace osg {

//==============================================================================
RealTimeWorldNode::RealTimeWorldNode(
const std::shared_ptr<dart::simulation::World>& world,
const ::osg::ref_ptr<osgShadow::ShadowTechnique>& shadower,
const double targetFrequency,
const double targetRealTimeFactor)
: WorldNode(world, shadower),
mFirstRefresh(true),
mTargetRealTimeLapse(1.0/targetFrequency),
mTargetSimTimeLapse(targetRealTimeFactor*mTargetRealTimeLapse),
mLastRealTimeFactor(0.0),
mLowestRealTimeFactor(std::numeric_limits<double>::infinity()),
mHighestRealTimeFactor(0.0)
{
// Do nothing
}

//==============================================================================
void RealTimeWorldNode::setTargetFrequency(double targetFrequency)
{
const double targetRTF = mTargetSimTimeLapse/mTargetRealTimeLapse;
mTargetRealTimeLapse = 1.0/targetFrequency;
mTargetSimTimeLapse = targetRTF*mTargetRealTimeLapse;
}

//==============================================================================
double RealTimeWorldNode::getTargetFrequency() const
{
return 1.0/mTargetRealTimeLapse;
}

//==============================================================================
void RealTimeWorldNode::setTargetRealTimeFactor(double targetRTF)
{
mTargetSimTimeLapse = targetRTF*mTargetRealTimeLapse;
}

//==============================================================================
double RealTimeWorldNode::getTargetRealTimeFactor() const
{
return mTargetSimTimeLapse/mTargetRealTimeLapse;
}

//==============================================================================
double RealTimeWorldNode::getLastRealTimeFactor() const
{
return mLastRealTimeFactor;
}

//==============================================================================
double RealTimeWorldNode::getLowestRealTimeFactor() const
{
return mLowestRealTimeFactor;
}

//==============================================================================
double RealTimeWorldNode::getHighestRealTimeFactor() const
{
return mHighestRealTimeFactor;
}

//==============================================================================
void RealTimeWorldNode::refresh()
{
customPreRefresh();
clearChildUtilizationFlags();

if (mNumStepsPerCycle != 1)
{
dtwarn << "[RealTimeWorldNode] The number of steps per cycle has been set "
<< "to [" << mNumStepsPerCycle << "], but this value is ignored by "
<< "the RealTimeWorldNode::refresh() function. Use the function "
<< "RealTimeWorldNode::setTargetRealTimeFactor(double) to change "
<< "the simulation speed\n";
mxgrey marked this conversation as resolved.
Show resolved Hide resolved
mNumStepsPerCycle = 1;
}

if (mFirstRefresh)
{
mRefreshTimer.setStartTick();
mFirstRefresh = false;
}

if (mWorld && mSimulating)
{
const double startSimTime = mWorld->getTime();
const double simTimeStep = mWorld->getTimeStep();

while(mRefreshTimer.time_s() < mTargetRealTimeLapse)
{
const double nextSimTimeLapse =
mWorld->getTime() - startSimTime + simTimeStep;

if(nextSimTimeLapse <= mTargetSimTimeLapse)
{
customPreStep();
mWorld->step();
customPostStep();
}
}

mLastRealTimeFactor =
(mWorld->getTime() - startSimTime)/mTargetRealTimeLapse;
mLowestRealTimeFactor =
std::min(mLastRealTimeFactor, mLowestRealTimeFactor);
mHighestRealTimeFactor =
std::max(mLastRealTimeFactor, mHighestRealTimeFactor);
}
else
{
mFirstRefresh = true;
}

mRefreshTimer.setStartTick();
mxgrey marked this conversation as resolved.
Show resolved Hide resolved

refreshSkeletons();
refreshSimpleFrames();

clearUnusedNodes();

customPostRefresh();
}

} // namespace osg
} // namespace gui
} // namespace dart
134 changes: 134 additions & 0 deletions dart/gui/osg/RealTimeWorldNode.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright (c) 2019, The DART development contributors
mxgrey marked this conversation as resolved.
Show resolved Hide resolved
* All rights reserved.
*
* The list of contributors can be found at:
* https://github.com/dartsim/dart/blob/master/LICENSE
*
* This file is provided under the following "BSD-style" License:
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef DART_GUI_OSG_REALTIMEWORLDNODE_HPP_
#define DART_GUI_OSG_REALTIMEWORLDNODE_HPP_

#include <osg/Timer>

#include "dart/gui/osg/WorldNode.hpp"

namespace dart {
namespace gui {
namespace osg {

class RealTimeWorldNode : public WorldNode
{
public:

/// Construct a world node that will attempt to run a simulation with close
/// to real-time playback. If a simulation is too computationally expensive,
/// the simulation might not be able to keep up with real time.
///
/// \param[in] world
/// The world to simulate
///
/// \param[in] targetFrequency
/// The expected refresh rate. The actual refresh rate may depend on your
/// monitor and your computer's display settings.
///
/// \param[in] targetRealTimeFactor
/// This factor at which the simulation should run. A value of 1.0 (default)
/// means it will try to run at real time. A value 2.0 means the simulation
/// will try to run at double real time speed (fast-forward). A value of 0.5
/// means the simulation will try to run at half of real-time speed (slowed
/// down).
///
/// \param[in] shadowTech
/// The shading technique to use when rendering this world.
RealTimeWorldNode(
const std::shared_ptr<dart::simulation::World>& world = nullptr,
const ::osg::ref_ptr<osgShadow::ShadowTechnique>& shadower = nullptr,
double targetFrequency = 60.0,
double targetRealTimeFactor = 1.0);

/// Set the target refresh rate frequency
void setTargetFrequency(double targetFrequency);

/// Get the target refresh rate frequency
double getTargetFrequency() const;

/// Set the target real time factor
void setTargetRealTimeFactor(double targetRTF);

/// Get the target real time factor
double getTargetRealTimeFactor() const;

/// Get the real time factor that was achieved in the last refresh cycle. This
/// may be smaller than target real time factor, because the simulation may
/// not have had time to reach it.
double getLastRealTimeFactor() const;

/// Get the lowest real time factor that has been hit during the simulation.
double getLowestRealTimeFactor() const;

/// Get the highest real time factor that has been hit during the simulation.
double getHighestRealTimeFactor() const;

/// Turn the text displaying the real time factor on or off
void setRealTimeFactorDisplay(bool on);

// Documentation inherited
void refresh() override;

protected:

/// Reset each time the simulation is paused
bool mFirstRefresh;

/// Keeps track of the time between refreshes
::osg::Timer mRefreshTimer;

/// The target for how much time should elapse between refreshes
double mTargetRealTimeLapse;

/// The target for how much simulation time should elapse between refreshes
double mTargetSimTimeLapse;

/// The RTF that was achieved in the last refresh cycle
double mLastRealTimeFactor;

/// The lowest RTF that has been achieved
double mLowestRealTimeFactor;

/// The highest RTF that has been achieved
double mHighestRealTimeFactor;

/// Flag to track whether we should display the real time factor
bool mDisplayRealTimeFactor;
mxgrey marked this conversation as resolved.
Show resolved Hide resolved
};

} // namespace osg
} // namespace gui
} // namespace dart

#endif // DART_GUI_OSG_REALTIMEWORLDNODE_HPP_
6 changes: 6 additions & 0 deletions dart/gui/osg/render/MeshShapeNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,12 @@ std::vector<std::string> MeshShapeNode::getTextureImagePaths(
if (index < mTextureImageArrays.size())
return mTextureImageArrays[index];

// We sometimes use this value for meshes that do not have material
// information, since assimp does not seem to have a built-in way to express
// that case.
if (index == std::numeric_limits<unsigned int>::max())
return {};

if (!mTextureImageArrays.empty())
{
dtwarn << "[MeshShapeNode::getTextureImageSet] Attempting to access "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
AtlasSimbiconWorldNode::AtlasSimbiconWorldNode(
const dart::simulation::WorldPtr& world,
const dart::dynamics::SkeletonPtr& atlas)
: dart::gui::osg::WorldNode(world),
: dart::gui::osg::RealTimeWorldNode(world),
mExternalForce(Eigen::Vector3d::Zero()),
mForceDuration(0.0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

#include "Controller.hpp"

class AtlasSimbiconWorldNode : public dart::gui::osg::WorldNode
class AtlasSimbiconWorldNode : public dart::gui::osg::RealTimeWorldNode
{
public:
/// Constructor
Expand Down
1 change: 0 additions & 1 deletion examples/osgExamples/osgAtlasSimbicon/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ int main()
// Wrap a WorldNode around it
osg::ref_ptr<AtlasSimbiconWorldNode> node
= new AtlasSimbiconWorldNode(world, atlas);
node->setNumStepsPerCycle(20);

// Create a Viewer and set it up with the WorldNode
dart::gui::osg::ImGuiViewer viewer;
Expand Down
7 changes: 2 additions & 5 deletions examples/osgExamples/osgBoxStacking/osgBoxStacking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,11 @@ dynamics::SkeletonPtr createFloor()
}

//==============================================================================
class CustomWorldNode : public dart::gui::osg::WorldNode
class CustomWorldNode : public dart::gui::osg::RealTimeWorldNode
{
public:
explicit CustomWorldNode(const dart::simulation::WorldPtr& world = nullptr)
: dart::gui::osg::WorldNode(world)
: dart::gui::osg::RealTimeWorldNode(world)
{
// Set up the customized WorldNode
}
Expand Down Expand Up @@ -417,9 +417,6 @@ int main()
// Wrap a WorldNode around it
osg::ref_ptr<CustomWorldNode> node = new CustomWorldNode(world);

// Run 20 simulation steps per one rendering frame
node->setNumStepsPerCycle(20);

// Create a Viewer and set it up with the WorldNode
dart::gui::osg::ImGuiViewer viewer;
viewer.addWorldNode(node);
Expand Down
4 changes: 2 additions & 2 deletions examples/osgExamples/osgEmpty/osgEmpty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@
#include <dart/gui/osg/osg.hpp>

//==============================================================================
class CustomWorldNode : public dart::gui::osg::WorldNode
class CustomWorldNode : public dart::gui::osg::RealTimeWorldNode
{
public:

CustomWorldNode(const dart::simulation::WorldPtr& world = nullptr)
: dart::gui::osg::WorldNode(world)
: dart::gui::osg::RealTimeWorldNode(world)
{
// Set up the customized WorldNode
}
Expand Down
Loading