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

OSG shadows #978

Merged
merged 19 commits into from
Feb 17, 2018
Merged
Show file tree
Hide file tree
Changes from 15 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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ TAGS
.DS_Store
.cproject
.project
.vscode
*.orig
*.idb
*.pdb
Expand Down
6 changes: 4 additions & 2 deletions dart/dynamics/ShapeFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ namespace detail {

//==============================================================================
VisualAspectProperties::VisualAspectProperties(const Eigen::Vector4d& color,
const bool hidden)
const bool hidden,
const bool shadowed)
: mRGBA(color),
mHidden(hidden)
mHidden(hidden),
mShadowed(shadowed)
{
// Do nothing
}
Expand Down
9 changes: 6 additions & 3 deletions dart/dynamics/ShapeFrame.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,15 @@ class VisualAspect final :
void setRGBA(const Eigen::Vector4d& color);

DART_COMMON_GET_ASPECT_PROPERTY( Eigen::Vector4d, RGBA )
// void setRGBA(const Eigen::Vector4d& value);
// const Eigen::Vector4d& getRGBA() const;

DART_COMMON_SET_GET_ASPECT_PROPERTY( bool, Hidden )
// void setHidden(const Eigen::Vector4d& value);
// const Eigen::Vector4d& getHidden() const;
// void setHidden(const bool& value);
// const bool& getHidden() const;

DART_COMMON_SET_GET_ASPECT_PROPERTY( bool, Shadowed )
// void setShadowed(const bool& value);
// const bool& getShadowed() const;

/// Identical to setRGB(const Eigen::Vector3d&)
void setColor(const Eigen::Vector3d& color);
Expand Down
6 changes: 5 additions & 1 deletion dart/dynamics/detail/ShapeFrameAspect.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@ struct VisualAspectProperties
/// True if this shape node should be kept from rendering
bool mHidden;

/// True if this shape node should be shadowed
bool mShadowed;

/// Constructor
VisualAspectProperties(
const Eigen::Vector4d& color = Eigen::Vector4d(0.5, 0.5, 1.0, 1.0),
const bool hidden = false);
const bool hidden = false,
const bool shadowed = true);

/// Destructor
virtual ~VisualAspectProperties() = default;
Expand Down
2 changes: 1 addition & 1 deletion dart/gui/osg/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ endif()
if(DART_BUILD_GUI_OSG)

find_package(OpenSceneGraph 3.0 QUIET
COMPONENTS osg osgViewer osgManipulator osgGA osgDB)
COMPONENTS osg osgViewer osgManipulator osgGA osgDB osgShadow)

# It seems that OPENSCENEGRAPH_FOUND will inadvertently get set to true when
# OpenThreads is found, even if OpenSceneGraph is not installed. This is quite
Expand Down
4 changes: 4 additions & 0 deletions dart/gui/osg/InteractiveFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ dart::dynamics::SimpleFrame* InteractiveTool::addShapeFrame(
auto shapeFrame = mSimpleFrames.back().get();
shapeFrame->setShape(shape);
shapeFrame->createVisualAspect();
// Disable shadowing for InteractiveTool
shapeFrame->getVisualAspect(true)->setShadowed(false);

return shapeFrame;
}
Expand Down Expand Up @@ -230,6 +232,8 @@ dart::dynamics::SimpleFrame* InteractiveFrame::addShapeFrame(
auto shapeFrame = mSimpleFrames.back().get();
shapeFrame->setShape(shape);
shapeFrame->createVisualAspect();
// Disable shadowing for InteractiveFrame
shapeFrame->getVisualAspect(true)->setShadowed(false);

return shapeFrame;
}
Expand Down
12 changes: 12 additions & 0 deletions dart/gui/osg/Viewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,9 @@ void Viewer::addWorldNode(WorldNode* _newWorldNode, bool _active)
_newWorldNode->simulate(mSimulating);
_newWorldNode->mViewer = this;
_newWorldNode->setupViewer();
// set again the shadow technique to produce warning for ImGuiViewer
if(_newWorldNode->isShadowed())
_newWorldNode->setShadowTechnique(_newWorldNode->getShadowTechnique());
}

//==============================================================================
Expand Down Expand Up @@ -468,6 +471,15 @@ const ::osg::Group* Viewer::getLightGroup() const
return mLightGroup;
}

//==============================================================================
const ::osg::ref_ptr<::osg::LightSource>& Viewer::getLightSource(unsigned int index) const
{
assert(index < 2);
if(index == 0)
return mLightSource1;
return mLightSource2;
}

//==============================================================================
void Viewer::setupDefaultLights()
{
Expand Down
7 changes: 6 additions & 1 deletion dart/gui/osg/Viewer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <memory>

#include <osgViewer/Viewer>
#include <osgShadow/ShadowTechnique>

#include <Eigen/Core>

Expand Down Expand Up @@ -114,7 +115,6 @@ class ViewerAttachment : public virtual ::osg::Group
class Viewer : public osgViewer::Viewer, public dart::common::Subject
{
public:

/// Constructor for dart::gui::osg::Viewer. This will automatically create the
/// default event handler.
Viewer(const ::osg::Vec4& clearColor = ::osg::Vec4(0.9,0.9,0.9,1.0));
Expand Down Expand Up @@ -197,6 +197,11 @@ class Viewer : public osgViewer::Viewer, public dart::common::Subject
/// Get the Group node that contains the LightSources for this Viewer
const ::osg::Group* getLightGroup() const;

/// Get one of the LightSources of this Viewer
/// index either 0 or 1
/// Useful for shadowing techniques
const ::osg::ref_ptr<::osg::LightSource>& getLightSource(unsigned int index = 0) const;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Please use std::size_t for index parameters.


/// Set up the default lighting scheme
void setupDefaultLights();

Expand Down
92 changes: 88 additions & 4 deletions dart/gui/osg/WorldNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@

#include <osg/NodeCallback>

#include <osgShadow/ShadowedScene>
#include <osgShadow/ShadowMap>

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

#include "dart/simulation/World.hpp"
#include "dart/dynamics/Skeleton.hpp"
Expand All @@ -61,12 +65,33 @@ class WorldNodeCallback : public ::osg::NodeCallback
};

//==============================================================================
WorldNode::WorldNode(std::shared_ptr<dart::simulation::World> _world)
WorldNode::WorldNode(std::shared_ptr<dart::simulation::World> _world, ::osg::ref_ptr<osgShadow::ShadowTechnique> shadowTechnique)
: mWorld(_world),
mSimulating(false),
mNumStepsPerCycle(1),
mViewer(nullptr)
mViewer(nullptr),
mNormalGroup(new ::osg::Group)
{
// Flags for shadowing; maybe this needs to be global?
constexpr int ReceivesShadowTraversalMask = 0x2;
constexpr int CastsShadowTraversalMask = 0x1;

// Setup shadows
// Create a ShadowedScene
::osg::ref_ptr<osgShadow::ShadowedScene> shadowedScene = new osgShadow::ShadowedScene;
shadowedScene->setReceivesShadowTraversalMask(ReceivesShadowTraversalMask);
shadowedScene->setCastsShadowTraversalMask(CastsShadowTraversalMask);

// set the physics group
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: This is called normal group now rather than physics group.

mShadowedGroup = shadowedScene.get();
mShadowedGroup->getOrCreateStateSet();

// Add normal and shadowed groups
addChild(mNormalGroup);
addChild(mShadowedGroup);

setShadowTechnique(shadowTechnique);

setUpdateCallback(new WorldNodeCallback);
}

Expand Down Expand Up @@ -193,7 +218,11 @@ void WorldNode::clearUnusedNodes()
{
NodeMap::iterator it = mFrameToNode.find(frame);
ShapeFrameNode* node = it->second;
removeChild(node);
if(!node->getShapeFrame() || !node->getShapeFrame()->hasVisualAspect() || !node->getShapeFrame()->getVisualAspect(true)->getShadowed()) {
mNormalGroup->removeChild(node);
}
else
mShadowedGroup->removeChild(node);
mFrameToNode.erase(it);
}
}
Expand Down Expand Up @@ -260,6 +289,16 @@ void WorldNode::refreshShapeFrameNode(dart::dynamics::Frame* frame)
if(!node)
return;

// update the group that ShapeFrameNode should be
if((!node->getShapeFrame()->hasVisualAspect() || !node->getShapeFrame()->getVisualAspect(true)->getShadowed()) && node->getParent(0) != mNormalGroup) {
mShadowedGroup->removeChild(node);
mNormalGroup->addChild(node);
}
else if(node->getShapeFrame()->hasVisualAspect() && node->getShapeFrame()->getVisualAspect(true)->getShadowed() && node->getParent(0) != mShadowedGroup) {
mNormalGroup->removeChild(node);
mShadowedGroup->addChild(node);
}

node->refresh(true);
return;
}
Expand All @@ -276,7 +315,52 @@ void WorldNode::refreshShapeFrameNode(dart::dynamics::Frame* frame)
::osg::ref_ptr<ShapeFrameNode> node = new ShapeFrameNode(frame->asShapeFrame(),
this);
it->second = node;
addChild(node);
if(!node->getShapeFrame()->hasVisualAspect() || !node->getShapeFrame()->getVisualAspect(true)->getShadowed()) {
mNormalGroup->addChild(node);
}
else
mShadowedGroup->addChild(node);
}

//==============================================================================
bool WorldNode::isShadowed() const
{
return mShadowed;
}

//==============================================================================
void WorldNode::setShadowTechnique(::osg::ref_ptr<osgShadow::ShadowTechnique> shadowTechnique) {
if(!shadowTechnique) {
mShadowed = false;
static_cast<osgShadow::ShadowedScene*>(mShadowedGroup.get())->setShadowTechnique(nullptr);
}
else {
ImGuiViewer* viewer = mViewer ? dynamic_cast<ImGuiViewer*>(mViewer) : nullptr;
if(viewer)
dtwarn << "[WorldNode] You are enabling shadows inside an ImGuiViewer. "
<< "The ImGui windows may not render properly.\n";
mShadowed = true;
static_cast<osgShadow::ShadowedScene*>(mShadowedGroup.get())->setShadowTechnique(shadowTechnique.get());
}
}

//==============================================================================
::osg::ref_ptr<osgShadow::ShadowTechnique> WorldNode::getShadowTechnique() const {
if(!mShadowed)
return nullptr;
return static_cast<osgShadow::ShadowedScene*>(mShadowedGroup.get())->getShadowTechnique();
}

//==============================================================================
::osg::ref_ptr<osgShadow::ShadowTechnique> WorldNode::createDefaultShadowTechnique(const Viewer* viewer) {
::osg::ref_ptr<osgShadow::ShadowMap> sm = new osgShadow::ShadowMap;
// increase the resolution of default shadow texture for higher quality
int mapres = std::pow(2, 13);
sm->setTextureSize(::osg::Vec2s(mapres,mapres));
// we are using Light1 because this is the highest one (on up direction)
sm->setLight(viewer->getLightSource(0));

return sm;
}

} // namespace osg
Expand Down
28 changes: 27 additions & 1 deletion dart/gui/osg/WorldNode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#define DART_GUI_OSG_WORLDNODE_HPP_

#include <osg/Group>
#include <osgShadow/ShadowTechnique>
#include <unordered_map>
#include <memory>

Expand Down Expand Up @@ -67,7 +68,8 @@ class WorldNode : public ::osg::Group
friend class Viewer;

/// Default constructor
explicit WorldNode(std::shared_ptr<dart::simulation::World> _world = nullptr);
/// Shadows are disabled by default
explicit WorldNode(std::shared_ptr<dart::simulation::World> _world = nullptr, ::osg::ref_ptr<osgShadow::ShadowTechnique> shadowTechnique = nullptr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still a little bit leery about having shadowTechnique = nullptr secretly mean "Let us choose a default shading technique for you".

However, we do already have a precedent of this pattern where passing in a nullptr for ResourceRetriever means that we will use a default LocalResourceRetriever.

I might feel better about this pattern if we rename the argument from shadowTechnique to something like alternativeTechnique. That will at least tell the user that they are specifying an alternative rather than specifying the technique to use. Then passing a nullptr naturally says "I don't want to use an alternative", and so we apply the default shading technique.

Sorry if this sounds petty, but without something along these lines, a user would have to look into the source code to actually understand how these functions should be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if this sounds petty, but without something along these lines, a user would have to look into the source code to actually understand how these functions should be used.

No worries. I understand what you say. Nevertheless, in this version of the code, nullptr means no shadow (not a default shadow behavior).. The user has to specify their own ShadowTechnique. I could create a static default technique if you like..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry, I didn't notice that part of the recent change.

I actually liked providing a default shadow technique; I just wasn't comfortable with the semantics of "giving a nullptr technique means we should use a default". Sorry for not communicating that well.

I'm not clear on how a static default would work, since it seems to need information about the Viewer that's using it. I guess we could make it a factory function that takes a Viewer* argument and returns a ref_ptr<ShadowTechnique>? We wouldn't be able to use that function as the default argument for this constructor, but it could at least make it easy for users to enable shadows. We could add a comment in the documentation of this function that refers to the default ShadowTechnique factory.

Since the shadows conflict with the ImGui feature, I suppose we should have shadows turned off by default anyway. It would've been nice to have shadows on by default, but we can tackle that later once we've fixed the ImGui conflict.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not clear on how a static default would work, since it seems to need information about the Viewer that's using it. I guess we could make it a factory function that takes a Viewer* argument and returns a ref_ptr? We wouldn't be able to use that function as the default argument for this constructor, but it could at least make it easy for users to enable shadows. We could add a comment in the documentation of this function that refers to the default ShadowTechnique factory.

That's exactly what I had in mind.. A static member function that creates a default ShadowTechnique (given a Viewer*) that users can use.. I'll do that asap..

ince the shadows conflict with the ImGui feature, I suppose we should have shadows turned off by default anyway. It would've been nice to have shadows on by default, but we can tackle that later once we've fixed the ImGui conflict.

I agree on this and that's the reason I left the shadows disabled by default.. 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was also thinking of modifying the osgOperationalSpaceControl example to be able to enable/disable shadows interactively (with a simple event handler). What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds like a great place to put an example of this, thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Please remove the leading underscore from _world.


/// Set the World that this WorldNode is associated with
void setWorld(std::shared_ptr<dart::simulation::World> _newWorld);
Expand Down Expand Up @@ -129,6 +131,21 @@ class WorldNode : public ::osg::Group
/// if the simulation is not paused)
std::size_t getNumStepsPerCycle() const;

/// Get whether the WorldNode is casting shadows
bool isShadowed() const;

/// Set the ShadowTechnique
/// If you wish to disable shadows, pass a nullptr
void setShadowTechnique(::osg::ref_ptr<osgShadow::ShadowTechnique> shadowTechnique = nullptr);

/// Get the current ShadowTechnique
/// nullptr is there are no shadows
::osg::ref_ptr<osgShadow::ShadowTechnique> getShadowTechnique() const;

/// Helper function to create a default ShadowTechnique given a Viewer
/// the default ShadowTechnique is ShadowMap
static ::osg::ref_ptr<osgShadow::ShadowTechnique> createDefaultShadowTechnique(const Viewer* viewer);

protected:

/// Destructor
Expand Down Expand Up @@ -173,6 +190,15 @@ class WorldNode : public ::osg::Group
/// Viewer that this WorldNode is inside of
Viewer* mViewer;

/// OSG group for non-shadowed objects
::osg::ref_ptr<::osg::Group> mNormalGroup;

/// OSG group for shadowed objects
::osg::ref_ptr<::osg::Group> mShadowedGroup;

/// Whether the shadows are enabled
bool mShadowed;

};

} // namespace osg
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,41 @@ class ConstraintEventHandler : public ::osgGA::GUIEventHandler
dart::sub_ptr<dart::gui::osg::DragAndDrop> mDnD;
};

class ShadowEventHandler : public osgGA::GUIEventHandler
{
public:

ShadowEventHandler(OperationalSpaceControlWorld* node, dart::gui::osg::Viewer* viewer) : mNode(node), mViewer(viewer) {}

bool handle(const osgGA::GUIEventAdapter& ea,
osgGA::GUIActionAdapter&) override
{
if(ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN)
{
if(ea.getKey() == 's' || ea.getKey() == 'S')
{
if(mNode->isShadowed())
mNode->setShadowTechnique(nullptr);
else
mNode->setShadowTechnique(dart::gui::osg::WorldNode::createDefaultShadowTechnique(mViewer));
return true;
}
}

// The return value should be 'true' if the input has been fully handled
// and should not be visible to any remaining event handlers. It should be
// false if the input has not been fully handled and should be viewed by
// any remaining event handlers.
return false;
}

protected:

OperationalSpaceControlWorld* mNode;
dart::gui::osg::Viewer* mViewer;

};

int main()
{
dart::simulation::WorldPtr world(new dart::simulation::World);
Expand Down Expand Up @@ -299,6 +334,7 @@ int main()

// Add our custom event handler to the Viewer
viewer.addEventHandler(new ConstraintEventHandler(node->dnd));
viewer.addEventHandler(new ShadowEventHandler(node.get(), &viewer));

// Print out instructions
std::cout << viewer.getInstructions() << std::endl;
Expand Down