osgEarth 2.10 ImageLayerCallback Question

classic Classic list List threaded Threaded
1 message Options
Blanky Blanky
Reply | Threaded
Open this post in threaded view
|

osgEarth 2.10 ImageLayerCallback Question

This post was updated on .
Super long post, tl;dr posted at the bottom to get a rough idea of what I'm trying to do.

So going back a little bit, to this post: http://forum.osgearth.org/One-mapnode-multiple-viewers-cull-layers-td7592308.html 

I followed the instructions and found the source of the code in question:

    //! Traversal callback
    class OSGEARTH_EXPORT TraversalCallback : public osg::Callback
    {
        public:
            virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) const =0;
        protected:
            void traverse(osg::Node* node, osg::NodeVisitor* nv) const;
    };

And:

    //! Callback invoked by the terrain engine on this layer before applying
    void setCullCallback(TraversalCallback* tc);

So I subclassed the TraversalCallback for my osgEarth::ImageLayer. However, there is one issue. My goal is to have a brightness and contrast filter installed specifically on unique layers that a camera will see and be independent of each other, but for the same osgEarth::MapNode. For example if I have one osgEarth::MapNode and have 4 osgViewer::Viewer using the same osgEarth::MapNode as their scene data, each camera will be looking at the same exact scene graph and earth. I wanted to make it such that the osgEarth::MapNode had a unique set of layers being depicted by the osgEarth::MapNode for a given camera. For example, if I create the osgEarth::MapNode from a .earth file with just its image set to the default provided world .tiff image, one layer will be created when the argument parser loads.

My question is: Is it possible to set the user value of the corresponding osg::Drawable that the osgEarth::Map's RexTerrainEngineNode represents under the hood for rendering the layers? During the traversal callback, I can cull all of the layers for a camera, but I install the brightnessContrastFilter on individual layers. This doesn't seem to work individually and still affects every layer, regardless of how I wanted to split them up. So what I tried was the following:

1. Created an osgEarth::MapNode from a .earth file

2. Saved all default layers created from that osgEarth::MapNode by allocating fresh layers using said constructed osgEarth::ImageLayer's options (just one layer for .tiff image layer)

3. Removed all default layers from the osgEarth::MapNode

4. Create a new osgViewer::Viewer window with a new camera

5. Copy the default layers the same way as earlier and call setCullCallback(new customLayerCallback) on them

6. Add the copied layers back to the osgEarth::MapNode

7. Set the user value of the osgEarth::ImageLayers newly copied for the current camera's name (all cameras have unique names) and a Boolean to be false like so: osgEarth::ImageLayer* layer->setUserValue(cameraName, false);

8. In the callback I check if the node's user value for the node visitor's camera match and then check if it should be culled based on that Boolean value captured and return immediately if it is true, else traverse

So, my issue is that the user value I'm setting is not the node in the traversal, but the node in the callback traversal is the osgEarth::MapNode's osgEarth::RexTerrainEngineNode. It seems all layers get compressed into one drawable and that my task is currently not feasible the way that's set up. Will what I wish to do ever work?

This code from osgEarth::MapNode.cpp here would lead me to believe I could:

    // proxy cull callback for layers that have their own cull callback
    struct LayerCullCallbackDispatch : public osg::NodeCallback
    {
        Layer* _layer;

        LayerCullCallbackDispatch(Layer* layer) : _layer(layer) { }

        virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
        {
            if (_layer->getNode())
            {
                _layer->apply(_layer->getNode(), nv);
            }
            //if (_layer->getCullCallback() && _layer->getNode())
            //{
            //    _layer->apply(_layer->getNode(), nv);
            //}
            else
            {
                traverse(node, nv);
            }
        };
    };

However, no matter when I call layer->getNode() in my code, it always returns 0x0 no matter what. I really just want to set the user value on its underlying osg::Node counterpart, but I can't. I also don't know if this will even fix the issue with the brightnessContrastFilter because it seems to be applying to all of the layers as if they're merging under the osgEarth::RexTerrainEngineNode. I feel like I've hit a dead end here with the limitations of layers right now and would like to know if I'm close or if this is just impossible or if I need to instead try shaders instead of filters.

Also, I looked into the osgEarth::MapNode.cpp to see how layers were handled upon being added and saw this:

    void MapNode::onLayerAdded(Layer* layer, unsigned index)
    {
        if (!layer || !layer->getEnabled())
            return;
   
        // Communicate terrain resources to the layer:
        layer->setTerrainResources(getTerrainEngine()->getResources());

        // Each layer gets a callback to change the MapNode if necessary
        layer->getSceneGraphCallbacks()->add(new MapNodeObserverInstaller(this));

        // Create the layer's node, if it has one:
        osg::Node* node = layer->getNode();
        if (node)
        {
            OE_DEBUG << LC << "Adding node from layer \"" << layer->getName() << "\" to the scene     graph\n";

            // notify before adding it to the graph:
            layer->getSceneGraphCallbacks()->firePreMergeNode(node);

            // update the layer-to-node table (adds the node to the graph)
            rebuildLayerNodes(_map.get(), _layerNodes);

            // after putting it in the graph:
            layer->getSceneGraphCallbacks()->firePostMergeNode(node);
        }
    }

So I went to see what `layer->setTerrainResources(getTerrainEngine()->getResources());` did in the osgEarth::Layer.h/.cpp and found this:

        //! MapNode will call this function when terrain resources are available.
        virtual void setTerrainResources(TerrainResources*) { }


... Is the osgEarth::Layer.cpp in the GitHub source not up to date because this function is not defined in it or any of the subclasses of Layer, so where/when does the internal node representation ever get created that the osgEarth::MapNode seems to be querying everywhere because as far as I can tell, it'll always be 0x0 returned from osgEarth::Layer::getNode()?


Sorry for the long read, but any help would be greatly appreciated because I've never been this stuck before on something like this.

tl;dr One osgEarth::MapNode. Multiple viewers with the same osgEarth::MapNode as scene data. Each Viewer causes a deep copy of the base osgEarth::MapNode's layers and cull newly constructed layers from every other viewer's camera except for the one causing the new copy operation through a Layer traversal callback, such that a brightness and contrast filter will independently control the current viewer's visualization of the osgEarth::MapNode and not affect any of the other camera's visible layers.
(I want it to pretend the osgEarth::MapNode is visually unique for the given camera for performance and culling reasons)

- Blanky