/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/

#ifndef OSG_CAMERA
#define OSG_CAMERA 1

#include <osg/Transform>
#include <osg/Viewport>
#include <osg/ColorMask>
#include <osg/CullSettings>
#include <osg/Texture>
#include <osg/Image>
#include <osg/GraphicsContext>
#include <osg/Stats>

#include <OpenThreads/Mutex>


#if defined(OSG_GLES1_AVAILABLE) || defined(OSG_GLES2_AVAILABLE) || defined(OSG_GLES3_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
    #define GL_FRONT_LEFT       0x0400
    #define GL_FRONT_RIGHT      0x0401
    #define GL_BACK_LEFT        0x0402
    #define GL_BACK_RIGHT       0x0403
#endif

namespace osg {

// forward declare View to allow Camera to point back to the View that its within
class View;
class RenderInfo;

/** Camera - is a subclass of Transform which represents encapsulates the settings of a Camera.
*/
class OSG_EXPORT Camera : public Transform, public CullSettings
{
    public :


        Camera();

        /** Copy constructor using CopyOp to manage deep vs shallow copy.*/
        Camera(const Camera&,const CopyOp& copyop=CopyOp::SHALLOW_COPY);

        META_Node(osg, Camera);

        virtual Camera* asCamera() { return this; }
        virtual const Camera* asCamera() const { return this; }

        /** Set the View that this Camera is part of. */
        void setView(View* view) { _view = view; }

        /** Get the View that this Camera is part of. */
        View* getView() { return _view; }

        /** Get the const View that this Camera is part of. */
        const View* getView() const { return _view; }


        /** Set the Stats object used to collect various frame related
          * timing and scene graph stats. */
        void setStats(osg::Stats* stats) { _stats = stats; }

        /** Get the Stats object.*/
        osg::Stats* getStats() { return _stats.get(); }

        /** Get the const Stats object.*/
        const osg::Stats* getStats() const { return _stats.get(); }


        /** Set whether this camera allows events to be generated by the
          * associated graphics window to be associated with this camera. */
        void setAllowEventFocus(bool focus) { _allowEventFocus = focus; }

        /** Get whether this camera allows events to be generated by the
          * associated graphics window to be associated with this camera. */
        bool getAllowEventFocus() const { return _allowEventFocus; }


        /** Set the DisplaySettings object associated with this view.*/
        void setDisplaySettings(osg::DisplaySettings* ds) { _displaySettings = ds; }

        /** Get the DisplaySettings object associated with this view.*/
        osg::DisplaySettings* getDisplaySettings() { return _displaySettings.get(); }

        /** Get the const DisplaySettings object associated with this view.*/
        const osg::DisplaySettings* getDisplaySettings() const { return _displaySettings.get(); }


        /** Set the clear mask used in glClear().
          * Defaults to GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT. */
        inline void setClearMask(GLbitfield mask) { _clearMask = mask; applyMaskAction(CLEAR_MASK); }

        /** Get the clear mask.*/
        inline GLbitfield getClearMask() const { return _clearMask; }

        /** Set the clear color used in glClearColor().
          * glClearColor is only called if mask & GL_COLOR_BUFFER_BIT is true*/
        void setClearColor(const osg::Vec4& color) { _clearColor=color; applyMaskAction(CLEAR_COLOR); }

        /** Get the clear color.*/
        const osg::Vec4& getClearColor() const { return _clearColor; }

        /** Set the clear accum used in glClearAccum().
          * glClearAcumm is only called if mask & GL_ACCUM_BUFFER_BIT is true. */
        void setClearAccum(const osg::Vec4& color) { _clearAccum=color; }

        /** Get the clear accum value.*/
        const osg::Vec4& getClearAccum() const { return _clearAccum; }

        /** Set the clear depth used in glClearDepth(). Defaults to 1.0
          * glClearDepth is only called if mask & GL_DEPTH_BUFFER_BIT is true. */
        void setClearDepth(double depth) { _clearDepth=depth; }

        /** Get the clear depth value.*/
        double getClearDepth() const { return _clearDepth; }

        /** Set the clear stencil value used in glClearStencil(). Defaults to 0;
          * glClearStencil is only called if mask & GL_STENCIL_BUFFER_BIT is true*/
        void setClearStencil(int stencil) { _clearStencil=stencil; }

        /** Get the clear stencil value.*/
        int getClearStencil() const { return _clearStencil; }


        /** Set the color mask of the camera to use specified osg::ColorMask. */
        void setColorMask(osg::ColorMask* colorMask);


        /** Set the color mask of the camera to specified values. */
        void setColorMask(bool red, bool green, bool blue, bool alpha);

        /** Get the const ColorMask. */
        const ColorMask* getColorMask() const { return _colorMask.get(); }

        /** Get the ColorMask. */
        ColorMask* getColorMask() { return _colorMask.get(); }


        /** Set the viewport of the camera to use specified osg::Viewport. */
        void setViewport(osg::Viewport* viewport);

        /** Set the viewport of the camera to specified dimensions. */
        void setViewport(int x,int y,int width,int height);

        /** Get the const viewport. */
        const Viewport* getViewport() const { return _viewport.get(); }

        /** Get the viewport. */
        Viewport* getViewport() { return _viewport.get(); }



        enum TransformOrder
        {
            PRE_MULTIPLY,
            POST_MULTIPLY
        };

        /** Set the transformation order for world-to-local and local-to-world transformation.*/
        void setTransformOrder(TransformOrder order) { _transformOrder = order; }

        /** Get the transformation order.*/
        TransformOrder getTransformOrder() const { return _transformOrder; }

        enum ProjectionResizePolicy
        {
            FIXED, /**< Keep the projection matrix fixed, despite window resizes.*/
            HORIZONTAL, /**< Adjust the HORIZONTAL field of view on window resizes.*/
            VERTICAL /**< Adjust the VERTICAL field of view on window resizes.*/

        };

        /** Set the policy used to determine if and how the projection matrix should be adjusted on window resizes. */
        inline void setProjectionResizePolicy(ProjectionResizePolicy policy) { _projectionResizePolicy = policy; }

        /** Get the policy used to determine if and how the projection matrix should be adjusted on window resizes. */
        inline ProjectionResizePolicy getProjectionResizePolicy() const { return _projectionResizePolicy; }


        /** Set the projection matrix. Can be thought of as setting the lens of a camera. */
        inline void setProjectionMatrix(const osg::Matrixf& matrix) { _projectionMatrix.set(matrix); }

        /** Set the projection matrix. Can be thought of as setting the lens of a camera. */
        inline void setProjectionMatrix(const osg::Matrixd& matrix) { _projectionMatrix.set(matrix); }

        /** Set to an orthographic projection. See OpenGL glOrtho for documentation further details.*/
        void setProjectionMatrixAsOrtho(double left, double right,
                                        double bottom, double top,
                                        double zNear, double zFar);

        /** Set to a 2D orthographic projection. See OpenGL glOrtho2D documentation for further details.*/
        void setProjectionMatrixAsOrtho2D(double left, double right,
                                          double bottom, double top);

        /** Set to a perspective projection. See OpenGL glFrustum documentation for further details.*/
        void setProjectionMatrixAsFrustum(double left, double right,
                                          double bottom, double top,
                                          double zNear, double zFar);

        /** Create a symmetrical perspective projection, See OpenGL gluPerspective documentation for further details.
          * Aspect ratio is defined as width/height.*/
        void setProjectionMatrixAsPerspective(double fovy,double aspectRatio,
                                              double zNear, double zFar);

        /** Get the projection matrix.*/
        osg::Matrixd& getProjectionMatrix() { return _projectionMatrix; }

        /** Get the const projection matrix.*/
        const osg::Matrixd& getProjectionMatrix() const { return _projectionMatrix; }

        /** Get the orthographic settings of the orthographic projection matrix.
          * Returns false if matrix is not an orthographic matrix, where parameter values are undefined.*/
        bool getProjectionMatrixAsOrtho(double& left, double& right,
                                        double& bottom, double& top,
                                        double& zNear, double& zFar) const;

        /** Get the frustum setting of a perspective projection matrix.
          * Returns false if matrix is not a perspective matrix, where parameter values are undefined.*/
        bool getProjectionMatrixAsFrustum(double& left, double& right,
                                          double& bottom, double& top,
                                          double& zNear, double& zFar) const;

        /** Get the frustum setting of a symmetric perspective projection matrix.
          * Returns false if matrix is not a perspective matrix, where parameter values are undefined.
          * Note, if matrix is not a symmetric perspective matrix then the shear will be lost.
          * Asymmetric matrices occur when stereo, power walls, caves and reality center display are used.
          * In these configurations one should use the 'getProjectionMatrixAsFrustum' method instead.*/
        bool getProjectionMatrixAsPerspective(double& fovy,double& aspectRatio,
                                              double& zNear, double& zFar) const;



        /** Set the view matrix. Can be thought of as setting the position of the world relative to the camera in camera coordinates. */
        inline void setViewMatrix(const osg::Matrixf& matrix) { _viewMatrix.set(matrix);  dirtyBound();}

        /** Set the view matrix. Can be thought of as setting the position of the world relative to the camera in camera coordinates. */
        inline void setViewMatrix(const osg::Matrixd& matrix) { _viewMatrix.set(matrix);  dirtyBound();}

        /** Get the view matrix. */
        osg::Matrixd& getViewMatrix() { return _viewMatrix; }

        /** Get the const view matrix. */
        const osg::Matrixd& getViewMatrix() const { return _viewMatrix; }

        /** Set to the position and orientation of view matrix, using the same convention as gluLookAt. */
        void setViewMatrixAsLookAt(const osg::Vec3d& eye,const osg::Vec3d& center,const osg::Vec3d& up);

        /** Get to the position and orientation of a modelview matrix, using the same convention as gluLookAt. */
        void getViewMatrixAsLookAt(osg::Vec3d& eye,osg::Vec3d& center,osg::Vec3d& up,double lookDistance=1.0) const;

        /** Get to the position and orientation of a modelview matrix, using the same convention as gluLookAt. */
        void getViewMatrixAsLookAt(osg::Vec3f& eye,osg::Vec3f& center,osg::Vec3f& up,float lookDistance=1.0f) const;

        /** Get the inverse view matrix.*/
        Matrixd getInverseViewMatrix() const;


        enum RenderOrder
        {
            PRE_RENDER,
            NESTED_RENDER,
            POST_RENDER
        };

        /** Set the rendering order of this camera's subgraph relative to any camera that this subgraph is nested within.
          * For rendering to a texture, one typically uses PRE_RENDER.
          * For Head Up Displays, one would typically use POST_RENDER.*/
        void setRenderOrder(RenderOrder order, int orderNum = 0) { _renderOrder = order; _renderOrderNum = orderNum; }

        /** Get the rendering order of this camera's subgraph relative to any camera that this subgraph is nested within.*/
        RenderOrder getRenderOrder() const { return _renderOrder; }

        /** Get the rendering order number of this camera relative to any sibling cameras in this subgraph.*/
        int getRenderOrderNum() const { return _renderOrderNum; }

        /** Return true if this Camera is set up as a render to texture camera, i.e. it has textures assigned to it.*/
        bool isRenderToTextureCamera() const;

        enum RenderTargetImplementation
        {
            FRAME_BUFFER_OBJECT,
            PIXEL_BUFFER_RTT,
            PIXEL_BUFFER,
            FRAME_BUFFER,
            SEPARATE_WINDOW
        };

        /** Set the render target.*/
        void setRenderTargetImplementation(RenderTargetImplementation impl);

        /** Set the render target and fall-back that's used if the former isn't available.*/
        void setRenderTargetImplementation(RenderTargetImplementation impl, RenderTargetImplementation fallback);

        /** Get the render target.*/
        RenderTargetImplementation getRenderTargetImplementation() const { return _renderTargetImplementation; }

        /** Get the render target fallback.*/
        RenderTargetImplementation getRenderTargetFallback() const { return _renderTargetFallback; }


        /** Set the draw buffer used at the start of each frame draw.
          * Note, a buffer value of GL_NONE is used to specify that the rendering back-end should choose the most appropriate buffer.*/
        void setDrawBuffer(GLenum buffer) { _drawBuffer = buffer; applyMaskAction( DRAW_BUFFER ); }

        /** Get the draw buffer used at the start of each frame draw. */
        GLenum getDrawBuffer() const { return _drawBuffer; }

        /** Set the read buffer for any required copy operations to use.
          * Note, a buffer value of GL_NONE is used to specify that the rendering back-end should choose the most appropriate buffer.*/
        void setReadBuffer(GLenum buffer) { _readBuffer = buffer; applyMaskAction( READ_BUFFER ); }

        /** Get the read buffer for any required copy operations to use. */
        GLenum getReadBuffer() const { return _readBuffer; }

        enum BufferComponent
        {
            DEPTH_BUFFER,
            STENCIL_BUFFER,
            PACKED_DEPTH_STENCIL_BUFFER,
            COLOR_BUFFER,
            COLOR_BUFFER0,
            COLOR_BUFFER1 = COLOR_BUFFER0+1,
            COLOR_BUFFER2 = COLOR_BUFFER0+2,
            COLOR_BUFFER3 = COLOR_BUFFER0+3,
            COLOR_BUFFER4 = COLOR_BUFFER0+4,
            COLOR_BUFFER5 = COLOR_BUFFER0+5,
            COLOR_BUFFER6 = COLOR_BUFFER0+6,
            COLOR_BUFFER7 = COLOR_BUFFER0+7,
            COLOR_BUFFER8 = COLOR_BUFFER0+8,
            COLOR_BUFFER9 = COLOR_BUFFER0+9,
            COLOR_BUFFER10 = COLOR_BUFFER0+10,
            COLOR_BUFFER11 = COLOR_BUFFER0+11,
            COLOR_BUFFER12 = COLOR_BUFFER0+12,
            COLOR_BUFFER13 = COLOR_BUFFER0+13,
            COLOR_BUFFER14 = COLOR_BUFFER0+14,
            COLOR_BUFFER15 = COLOR_BUFFER0+15
        };

        static const unsigned int FACE_CONTROLLED_BY_GEOMETRY_SHADER;

        /** Attach a buffer with specified OpenGL internal format.*/
        void attach(BufferComponent buffer, GLenum internalFormat);

        /** Attach a Texture to specified buffer component.
          * The level parameter controls the mip map level of the texture that is attached.
          * The face parameter controls the face of texture cube map or z level of 3d texture.
          * The mipMapGeneration flag controls whether mipmap generation should be done for texture.*/
        void attach(BufferComponent buffer, osg::Texture* texture, unsigned int level = 0, unsigned int face=0, bool mipMapGeneration=false,
            unsigned int multisampleSamples = 0,
            unsigned int multisampleColorSamples = 0);

        /** Attach a Image to specified buffer component.*/
        void attach(BufferComponent buffer, osg::Image* image,
            unsigned int multisampleSamples = 0,
            unsigned int multisampleColorSamples = 0);

        /** Detach specified buffer component.*/
        void detach(BufferComponent buffer);

        struct Attachment
        {
            Attachment():
                _internalFormat(GL_NONE),
                _level(0),
                _face(0),
                _mipMapGeneration(false),
                _multisampleSamples(0),
                _multisampleColorSamples(0) {}

            int width() const
            {
                if (_texture.valid()) return _texture->getTextureWidth();
                if (_image.valid()) return _image->s();
                return 0;
            };

            int height() const
            {
                if (_texture.valid()) return _texture->getTextureHeight();
                if (_image.valid()) return _image->t();
                return 0;
            };

            int depth() const
            {
                if (_texture.valid()) return _texture->getTextureDepth();
                if (_image.valid()) return _image->r();
                return 0;
            };

            GLenum              _internalFormat;
            ref_ptr<Image>      _image;
            ref_ptr<Texture>    _texture;
            unsigned int        _level;
            unsigned int        _face;
            bool                _mipMapGeneration;
            unsigned int        _multisampleSamples;
            unsigned int        _multisampleColorSamples;
        };

        typedef std::map< BufferComponent, Attachment> BufferAttachmentMap;

        /** Get the BufferAttachmentMap, used to configure frame buffer objects, pbuffers and texture reads.*/
        BufferAttachmentMap& getBufferAttachmentMap() { return _bufferAttachmentMap; }

        /** Get the const BufferAttachmentMap, used to configure frame buffer objects, pbuffers and texture reads.*/
        const BufferAttachmentMap& getBufferAttachmentMap() const { return _bufferAttachmentMap; }


        /** Increment the _attachementMapModifiedCount so that the rendering backend will know that it needs to be updated to handle any new settings (such as format change/resizes.).*/
        void dirtyAttachmentMap() { ++_attachmentMapModifiedCount; }

        /** Set the AttachmentMapModifiedCount to a specific value.  Note, normal usage you would simply call dirtyAttachmentMap(). */
        void setAttachmentMapModifiedCount(unsigned int v) { _attachmentMapModifiedCount = v; }

        /** Get the AttachmentMapModifiedCount. */
        unsigned int getAttachmentMapModifiedCount() const { return _attachmentMapModifiedCount; }

        /** Resize the image and textures in the AttachementMap.*/
        void resizeAttachments(int width, int height);


        enum ResizeMask
        {
            RESIZE_VIEWPORT=1,
            RESIZE_ATTACHMENTS=2,
            RESIZE_PROJECTIONMATRIX=4,
            RESIZE_DEFAULT=RESIZE_VIEWPORT|RESIZE_ATTACHMENTS
        };

        /** Resize, to the specified width and height, the viewport, attachments and projection matrix according to the resizeMask provided.
          * Note, the adjustment of the projection matrix is done if the RESIZE_PROJECTIONMATRIX mask to set and according to the rules specified in the ProjectionResizePolicy. */
        void resize(int width, int height, int resizeMask=RESIZE_DEFAULT);


        /** Explicit control over implicit allocation of buffers when using FBO.
         Implicit buffers are automatically substituted when user have not attached such buffer.

         Camera may set up two FBOs: primary Render FBO and secondary Resolve FBO for multisample usage.
         So in practice we have two masks defined for the Camera:
            implicitBufferAttachmentRenderMask
            implicitBufferAttachmentResolveMask
         They can be set together by setImplicitBufferAttachmentMask method, or separately
         by setImplicitBufferAttachmentRenderMask and setImplicitBufferAttachmentResolveMask.

         Camera defaults are USE_DISPLAY_SETTINGS_MASK which means that by default
         Camera chooses to substitute buffer attachments as defined by DisplaySettings.

         Usually DisplaySettings implicit buffer attachment selection defaults to: DEPTH and COLOR
         for both primary (Render) FBO and seconday Multisample (Resolve) FBO
         ie: IMPLICIT_DEPTH_BUFFER_ATTACHMENT | IMPLICIT_COLOR_BUFFER_ATTACHMENT

         If these masks are not changed and user did not attach depth buffer and/or color buffer
         to Camera, then OSG implicitly substitutes these buffers.
         By default it does not implicitly allocate a stencil buffer.
         Use implicit buffer attachment  masks to override default behavior:
         to turn off DEPTH or COLOR buffer substitution or to enforce STENCIL buffer substitution.

         Note that both values are ignored if not using FBO.
         Note that the second mask value is ignored if not using MSFBO.
        */
        enum ImplicitBufferAttachment
        {
            IMPLICIT_DEPTH_BUFFER_ATTACHMENT = DisplaySettings::IMPLICIT_DEPTH_BUFFER_ATTACHMENT,
            IMPLICIT_STENCIL_BUFFER_ATTACHMENT = DisplaySettings::IMPLICIT_STENCIL_BUFFER_ATTACHMENT,
            IMPLICIT_COLOR_BUFFER_ATTACHMENT = DisplaySettings::IMPLICIT_COLOR_BUFFER_ATTACHMENT,
            USE_DISPLAY_SETTINGS_MASK = (~0)
        };

        typedef int ImplicitBufferAttachmentMask;

        void setImplicitBufferAttachmentMask(ImplicitBufferAttachmentMask renderMask = DisplaySettings::DEFAULT_IMPLICIT_BUFFER_ATTACHMENT, ImplicitBufferAttachmentMask resolveMask = DisplaySettings::DEFAULT_IMPLICIT_BUFFER_ATTACHMENT)
        {
            _implicitBufferAttachmentRenderMask = renderMask;
            _implicitBufferAttachmentResolveMask = resolveMask;
        }

        void setImplicitBufferAttachmentRenderMask(ImplicitBufferAttachmentMask implicitBufferAttachmentRenderMask)
        {
            _implicitBufferAttachmentRenderMask = implicitBufferAttachmentRenderMask;
        }

        void setImplicitBufferAttachmentResolveMask(ImplicitBufferAttachmentMask implicitBufferAttachmentResolveMask)
        {
            _implicitBufferAttachmentResolveMask = implicitBufferAttachmentResolveMask;
        }

        ImplicitBufferAttachmentMask getImplicitBufferAttachmentRenderMask() const
        {
            return _implicitBufferAttachmentRenderMask;
        }

        /**
        Get mask selecting implicit buffer attachments for Camera primary FBO
        if effectiveMask parameter is set, method follows USE_DISPLAY_SETTINGS_MASK dependence and returns effective mask
        if effectiveMask parameter is reset, method returns nominal mask set by the Camera
        */
        ImplicitBufferAttachmentMask getImplicitBufferAttachmentRenderMask(bool effectiveMask) const
        {
            if( effectiveMask && _implicitBufferAttachmentRenderMask == USE_DISPLAY_SETTINGS_MASK )
            {
                const DisplaySettings * ds = _displaySettings.valid() ? _displaySettings.get() : DisplaySettings::instance().get();
                return ds->getImplicitBufferAttachmentRenderMask();
            }
            else
            {
                return _implicitBufferAttachmentRenderMask;
            }
        }

        ImplicitBufferAttachmentMask getImplicitBufferAttachmentResolveMask() const
        {
            return _implicitBufferAttachmentResolveMask;
        }

        /**
        Get mask selecting implicit buffer attachments for Camera secondary MULTISAMPLE FBO
        if effectiveMask parameter is set, method follows USE_DISPLAY_SETTINGS_MASK dependence and returns effective mask
        if effectiveMask parameter is reset, method returns nominal mask set by the Camera
        */
        ImplicitBufferAttachmentMask getImplicitBufferAttachmentResolveMask(bool effectiveMask) const
        {
            if( effectiveMask && _implicitBufferAttachmentResolveMask == USE_DISPLAY_SETTINGS_MASK )
            {
                const DisplaySettings * ds = _displaySettings.valid() ? _displaySettings.get() : DisplaySettings::instance().get();
                return ds->getImplicitBufferAttachmentResolveMask();
            }
            else
            {
                return _implicitBufferAttachmentResolveMask;
            }
        }

        /** Set the process affinity hint for any Camera Threads that are/will be assigned to this Camera.*/
        void setProcessorAffinity(const OpenThreads::Affinity& affinity);
        OpenThreads::Affinity& getProcessorAffinity() { return _affinity; }
        const OpenThreads::Affinity& getProcessorAffinity() const { return _affinity; }

        /** Create a operation thread for this camera.*/
        void createCameraThread();

        /** Assign a operation thread to the camera.*/
        void setCameraThread(OperationThread* gt);

        /** Get the operation thread assigned to this camera.*/
        OperationThread* getCameraThread() { return _cameraThread.get(); }

        /** Get the const operation thread assigned to this camera.*/
        const OperationThread* getCameraThread() const { return _cameraThread.get(); }



        /** Set the GraphicsContext that provides the mechansim for managing the OpenGL graphics context associated with this camera.*/
        void setGraphicsContext(GraphicsContext* context);

        /** Get the GraphicsContext.*/
        GraphicsContext* getGraphicsContext() { return _graphicsContext.get(); }

        /** Get the const GraphicsContext.*/
        const GraphicsContext* getGraphicsContext() const { return _graphicsContext.get(); }


        /** Set the Rendering object that is used to implement rendering of the subgraph.*/
        void setRenderer(osg::GraphicsOperation* rc) { _renderer = rc; }

        /** Get the Rendering object that is used to implement rendering of the subgraph.*/
        osg::GraphicsOperation* getRenderer() { return _renderer.get(); }

        /** Get the const Rendering object that is used to implement rendering of the subgraph.*/
        const osg::GraphicsOperation* getRenderer() const { return _renderer.get(); }


        /** Set the Rendering cache that is used for cached objects associated with rendering of subgraphs.*/
        void setRenderingCache(osg::Object* rc) { _renderingCache = rc; }

        /** Get the Rendering cache that is used for cached objects associated with rendering of subgraphs.*/
        osg::Object* getRenderingCache() { return _renderingCache.get(); }

        /** Get the const Rendering cache that is used for cached objects associated with rendering of subgraphs.*/
        const osg::Object* getRenderingCache() const { return _renderingCache.get(); }


        /** Draw callback for custom operations.*/
        struct OSG_EXPORT DrawCallback : Callback
        {
            DrawCallback() {}

            DrawCallback(const DrawCallback& org,const CopyOp& copyop):
                Callback(org, copyop) {}

            META_Object(osg, DrawCallback);

            /** Functor method called by rendering thread to recursively launch operator() on _nestedcallback **/
            inline void run(osg::RenderInfo& renderInfo) const
            {
                operator () (renderInfo);

                if (_nestedCallback.valid())
                    ((const DrawCallback*)_nestedCallback.get())->run(renderInfo);
            }

            /** Users will typically override this method to carry tasks such as screen capture or bufferobject readback. **/
            virtual void operator () (osg::RenderInfo& renderInfo) const;

            /** Functor method, provided for backwards compatibility, called by operator() (osg::RenderInfo& renderInfo) method.**/
            virtual void operator () (const osg::Camera& /*camera*/) const {}

            /** Resize any per context GLObject buffers to specified size. */
            virtual void resizeGLObjectBuffers(unsigned int maxSize)
            {
                if (_nestedCallback.valid())
                    _nestedCallback->resizeGLObjectBuffers(maxSize);
            }

            /** If State is non-zero, this function releases any associated OpenGL objects for
            * the specified graphics context. Otherwise, releases OpenGL objexts
            * for all graphics contexts. */
            virtual void releaseGLObjects(osg::State* state = 0) const
            {
                if (_nestedCallback.valid())
                    _nestedCallback->releaseGLObjects(state);
            }

        };

        /** Set the initial draw callback for custom operations to be done before the drawing of the camera's subgraph and pre render stages.*/
        void setInitialDrawCallback(DrawCallback* cb) { _initialDrawCallback = cb; }

        /** Get the initial draw callback.*/
        DrawCallback* getInitialDrawCallback() { return _initialDrawCallback.get(); }

        /** Get the const initial draw callback.*/
        const DrawCallback* getInitialDrawCallback() const { return _initialDrawCallback.get(); }

        /** Convenience method that sets DrawCallback Initial callback of the node if it doesn't exist, or nest it into the existing one. */
        inline void addInitialDrawCallback(DrawCallback* nc)
        {
            if (nc != NULL)
            {
                if (_initialDrawCallback.valid()) _initialDrawCallback->addNestedCallback(nc);
                else setInitialDrawCallback(nc);
            }
        }

        template<class T> void addInitialDrawCallback(const ref_ptr<T>& nc) { addInitialDrawCallback(nc.get()); }

        /** Convenience method that removes a given callback from a node, even if that callback is nested. There is no error return in case the given callback is not found. */
        inline void removeInitialDrawCallback(DrawCallback* nc)
        {
            if (nc != NULL && _initialDrawCallback.valid())
            {
                if (_initialDrawCallback == nc)
                {
                    ref_ptr< DrawCallback> new_nested_callback = (DrawCallback*) nc->getNestedCallback();
                    nc->setNestedCallback(0);
                    setInitialDrawCallback(new_nested_callback.get());
                }
                else _initialDrawCallback->removeNestedCallback(nc);
            }
        }

        template<class T> void removeInitialDrawCallback(const ref_ptr<T>& nc) { removeInitialDrawCallback(nc.get()); }

        /** Set the pre draw callback for custom operations to be done before the drawing of the camera's subgraph but after any pre render stages have been completed.*/
        void setPreDrawCallback(DrawCallback* cb) { _preDrawCallback = cb; }

        /** Get the pre draw callback.*/
        DrawCallback* getPreDrawCallback() { return _preDrawCallback.get(); }

        /** Get the const pre draw callback.*/
        const DrawCallback* getPreDrawCallback() const { return _preDrawCallback.get(); }

        /** Convenience method that sets DrawCallback Initial callback of the node if it doesn't exist, or nest it into the existing one. */
        inline void addPreDrawCallback(DrawCallback* nc)
        {
            if (nc != NULL)
            {
                if (_preDrawCallback.valid()) _preDrawCallback->addNestedCallback(nc);
                else setPreDrawCallback(nc);
            }
        }

        template<class T> void addPreDrawCallback(const ref_ptr<T>& nc) { addPreDrawCallback(nc.get()); }

        /** Convenience method that removes a given callback from a node, even if that callback is nested. There is no error return in case the given callback is not found. */
        inline void removePreDrawCallback(DrawCallback* nc)
        {
            if (nc != NULL && _preDrawCallback.valid())
            {
                if (_preDrawCallback == nc)
                {
                    ref_ptr< DrawCallback> new_nested_callback = (DrawCallback*) nc->getNestedCallback();
                    nc->setNestedCallback(0);
                    setPreDrawCallback(new_nested_callback.get());
                }
                else _preDrawCallback->removeNestedCallback(nc);
            }
        }

        template<class T> void removePreDrawCallback(const ref_ptr<T>& nc) { removePreDrawCallback(nc.get()); }

        /** Set the post draw callback for custom operations to be done after the drawing of the camera's subgraph but before the any post render stages have been completed.*/
        void setPostDrawCallback(DrawCallback* cb) { _postDrawCallback = cb; }

        /** Get the post draw callback.*/
        DrawCallback* getPostDrawCallback() { return _postDrawCallback.get(); }

        /** Get the const post draw callback.*/
        const DrawCallback* getPostDrawCallback() const { return _postDrawCallback.get(); }

        /** Convenience method that sets DrawCallback Initial callback of the node if it doesn't exist, or nest it into the existing one. */
        inline void addPostDrawCallback(DrawCallback* nc)
        {
            if (nc != NULL)
            {
                if (_postDrawCallback.valid()) _postDrawCallback->addNestedCallback(nc);
                else setPostDrawCallback(nc);
            }
        }

        template<class T> void addPostDrawCallback(const ref_ptr<T>& nc) { addPostDrawCallback(nc.get()); }

        /** Convenience method that removes a given callback from a node, even if that callback is nested. There is no error return in case the given callback is not found. */
        inline void removePostDrawCallback(DrawCallback* nc)
        {
            if (nc != NULL && _postDrawCallback.valid())
            {
                if (_postDrawCallback == nc)
                {
                    ref_ptr< DrawCallback> new_nested_callback = (DrawCallback*) nc->getNestedCallback();
                    nc->setNestedCallback(0);
                    setPostDrawCallback(new_nested_callback.get());
                }
                else _postDrawCallback->removeNestedCallback(nc);
            }
        }

        template<class T> void removePostDrawCallback(const ref_ptr<T>& nc) { removePostDrawCallback(nc.get()); }

        /** Set the final draw callback for custom operations to be done after the drawing of the camera's subgraph and all of the post render stages has been completed.*/
        void setFinalDrawCallback(DrawCallback* cb) { _finalDrawCallback = cb; }

        /** Get the final draw callback.*/
        DrawCallback* getFinalDrawCallback() { return _finalDrawCallback.get(); }

        /** Get the const final draw callback.*/
        const DrawCallback* getFinalDrawCallback() const { return _finalDrawCallback.get(); }

        /** Convenience method that sets DrawCallback Initial callback of the node if it doesn't exist, or nest it into the existing one. */
        inline void addFinalDrawCallback(DrawCallback* nc)
        {
            if (nc != NULL)
            {
                if (_finalDrawCallback.valid()) _finalDrawCallback->addNestedCallback(nc);
                else setFinalDrawCallback(nc);
            }
        }

        template<class T> void addFinalDrawCallback(const ref_ptr<T>& nc) { addFinalDrawCallback(nc.get()); }

        /** Convenience method that removes a given callback from a node, even if that callback is nested. There is no error return in case the given callback is not found. */
        inline void removeFinalDrawCallback(DrawCallback* nc)
        {
            if (nc != NULL && _finalDrawCallback.valid())
            {
                if (_finalDrawCallback == nc)
                {
                    ref_ptr< DrawCallback> new_nested_callback = (DrawCallback*) nc->getNestedCallback();
                    nc->setNestedCallback(0);
                    setFinalDrawCallback(new_nested_callback.get());
                }
                else _finalDrawCallback->removeNestedCallback(nc);
            }
        }

        template<class T> void removeFinalDrawCallback(const ref_ptr<T>& nc) { removeFinalDrawCallback(nc.get()); }

        OpenThreads::Mutex* getDataChangeMutex() const { return &_dataChangeMutex; }

        /** Resize any per context GLObject buffers to specified size. */
        virtual void resizeGLObjectBuffers(unsigned int maxSize);

        /** If State is non-zero, this function releases any associated OpenGL objects for
           * the specified graphics context. Otherwise, releases OpenGL objexts
           * for all graphics contexts. */
        virtual void releaseGLObjects(osg::State* = 0) const;

    public:

        /** Transform method that must be defined to provide generic interface for scene graph traversals.*/
        virtual bool computeLocalToWorldMatrix(Matrix& matrix,NodeVisitor*) const;

        /** Transform method that must be defined to provide generic interface for scene graph traversals.*/
        virtual bool computeWorldToLocalMatrix(Matrix& matrix,NodeVisitor*) const;

        /** Inherit the local cull settings variable from specified CullSettings object, according to the inheritance mask.*/
        virtual void inheritCullSettings(const CullSettings& settings, unsigned int inheritanceMask);

    protected :

        virtual ~Camera();

        mutable OpenThreads::Mutex          _dataChangeMutex;


        View*                               _view;

        osg::ref_ptr<osg::Stats>            _stats;

        bool                                _allowEventFocus;

        osg::ref_ptr<osg::DisplaySettings>  _displaySettings;

        GLbitfield                          _clearMask;
        osg::Vec4                           _clearColor;
        osg::Vec4                           _clearAccum;
        double                              _clearDepth;
        int                                 _clearStencil;

        ref_ptr<ColorMask>                  _colorMask;
        ref_ptr<Viewport>                   _viewport;

        TransformOrder                      _transformOrder;
        ProjectionResizePolicy              _projectionResizePolicy;

        Matrixd                             _projectionMatrix;
        Matrixd                             _viewMatrix;

        RenderOrder                         _renderOrder;
        int                                 _renderOrderNum;

        GLenum                              _drawBuffer;
        GLenum                              _readBuffer;

        RenderTargetImplementation          _renderTargetImplementation;
        RenderTargetImplementation          _renderTargetFallback;
        BufferAttachmentMap                 _bufferAttachmentMap;
        ImplicitBufferAttachmentMask        _implicitBufferAttachmentRenderMask;
        ImplicitBufferAttachmentMask        _implicitBufferAttachmentResolveMask;

        unsigned int                        _attachmentMapModifiedCount;

        OpenThreads::Affinity               _affinity;
        ref_ptr<OperationThread>            _cameraThread;

        ref_ptr<GraphicsContext>            _graphicsContext;

        ref_ptr<GraphicsOperation>          _renderer;
        ref_ptr<Object>                     _renderingCache;

        ref_ptr<DrawCallback>               _initialDrawCallback;
        ref_ptr<DrawCallback>               _preDrawCallback;
        ref_ptr<DrawCallback>               _postDrawCallback;
        ref_ptr<DrawCallback>               _finalDrawCallback;
};


/** Functor to assist with ordering cameras in the order they should be rendered in.*/
struct CameraRenderOrderSortOp
{
    inline bool operator() (const Camera* lhs,const Camera* rhs) const
    {
        if (lhs->getRenderOrder()<rhs->getRenderOrder()) return true;
        if (rhs->getRenderOrder()<lhs->getRenderOrder()) return false;
        return lhs->getRenderOrderNum()<rhs->getRenderOrderNum();
    }
};


}

#endif
