|  | /*
  ==============================================================================
   This file is part of the JUCE library.
   Copyright (c) 2013 - Raw Material Software Ltd.
   Permission is granted to use this software under the terms of either:
   a) the GPL v2 (or any later version)
   b) the Affero GPL v3
   Details of these licenses can be found at: www.gnu.org/licenses
   JUCE 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 GNU General Public License for more details.
   ------------------------------------------------------------------------------
   To release a closed-source product which uses JUCE, commercial licenses are
   available: visit www.juce.com for more information.
  ==============================================================================
*/
#include "../jucedemo_headers.h"
#if JUCE_OPENGL
//==============================================================================
class DemoOpenGLCanvas  : public Component,
                          public OpenGLRenderer,
                          public Timer
{
public:
    DemoOpenGLCanvas()
        : rotation (0.0f),
          textScrollPos (200)
    {
        infoLabel.setText ("These sliders demonstrate how components and 2D graphics can be rendered "
                           "using OpenGL by using the OpenGLContext class.", dontSendNotification);
        infoLabel.setInterceptsMouseClicks (false, false);
        addAndMakeVisible (&infoLabel);
        infoLabel.setBounds ("parent.width * 0.05, bottom - 150, parent.width * 0.4, parent.height - 60");
        speedSlider.setRange (-10.0, 10.0, 0.1);
        speedSlider.setPopupMenuEnabled (true);
        speedSlider.setValue (Random::getSystemRandom().nextDouble() * 3.0, dontSendNotification);
        speedSlider.setSliderStyle (Slider::LinearHorizontal);
        speedSlider.setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
        addAndMakeVisible (&speedSlider);
        speedSlider.setBounds ("parent.width * 0.05, parent.height - 65, parent.width * 0.6, top + 24");
        sizeSlider.setRange (0.2, 2.0, 0.01);
        sizeSlider.setPopupMenuEnabled (true);
        sizeSlider.setValue (Random::getSystemRandom().nextDouble() + 0.5, dontSendNotification);
        sizeSlider.setSliderStyle (Slider::LinearHorizontal);
        sizeSlider.setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
        addAndMakeVisible (&sizeSlider);
        sizeSlider.setBounds ("parent.width * 0.05, parent.height - 35, parent.width * 0.6, top + 24");
        openGLContext.setRenderer (this);
        openGLContext.setComponentPaintingEnabled (true);
        openGLContext.attachTo (*this);
        startTimer (1000 / 30);
    }
    ~DemoOpenGLCanvas()
    {
        openGLContext.detach();
    }
    // when the component creates a new internal context, this is called, and
    // we'll use the opportunity to create some images to use as textures.
    void newOpenGLContextCreated()
    {
        logoImage = createLogoImage();
        dynamicTextureImage = Image (Image::ARGB, 128, 128, true, OpenGLImageType());
    }
    void openGLContextClosing()
    {
        // We have to make sure we release any openGL images before the
        // GL context gets closed..
        logoImage = Image::null;
        dynamicTextureImage = Image::null;
    }
    void mouseDown (const MouseEvent& e)
    {
        draggableOrientation.mouseDown (e.getPosition());
    }
    void mouseDrag (const MouseEvent& e)
    {
        draggableOrientation.mouseDrag (e.getPosition());
        openGLContext.triggerRepaint();
    }
    void resized()
    {
        draggableOrientation.setViewport (getLocalBounds());
    }
    void paint (Graphics&) {}
    void renderOpenGL()
    {
        OpenGLHelpers::clear (Colours::darkgrey.withAlpha (1.0f));
        updateTextureImage();  // this will update our dynamically-changing texture image.
        drawBackground2DStuff(); // draws some 2D content to demonstrate the OpenGLGraphicsContext class
        // Having used the juce 2D renderer, it will have messed-up a whole load of GL state, so
        // we'll put back any important settings before doing our normal GL 3D drawing..
        glEnable (GL_DEPTH_TEST);
        glDepthFunc (GL_LESS);
        glEnable (GL_BLEND);
        glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glEnable (GL_TEXTURE_2D);
       #if JUCE_USE_OPENGL_FIXED_FUNCTION
        OpenGLHelpers::prepareFor2D (getContextWidth(), getContextHeight());
        OpenGLHelpers::setPerspective (45.0, getContextWidth() / (double) getContextHeight(), 0.1, 100.0);
        glTranslatef (0.0f, 0.0f, -5.0f);
        draggableOrientation.applyToOpenGLMatrix();
        // logoImage and dynamicTextureImage are actually OpenGL images, so we can use this utility function to
        // extract the frame buffer which is their backing store, and use it directly.
        OpenGLFrameBuffer* tex1 = OpenGLImageType::getFrameBufferFrom (logoImage);
        OpenGLFrameBuffer* tex2 = OpenGLImageType::getFrameBufferFrom (dynamicTextureImage);
        if (tex1 != nullptr && tex2 != nullptr)
        {
            // This draws the sides of our spinning cube.
            // I've used some of the juce helper functions, but you can also just use normal GL calls here too.
            tex1->draw3D (-1.0f,  1.0f,  1.0f, -1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  1.0f,  1.0f,  1.0f, Colours::white);
            tex1->draw3D (-1.0f,  1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, Colours::white);
            tex1->draw3D (-1.0f, -1.0f, -1.0f,  1.0f, -1.0f, -1.0f,  1.0f, -1.0f,  1.0f, -1.0f, -1.0f,  1.0f, Colours::white);
            tex2->draw3D (-1.0f,  1.0f, -1.0f, -1.0f,  1.0f,  1.0f,  1.0f,  1.0f,  1.0f,  1.0f,  1.0f, -1.0f, Colours::white);
            tex2->draw3D ( 1.0f,  1.0f, -1.0f,  1.0f,  1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  1.0f, -1.0f, -1.0f, Colours::white);
            tex2->draw3D (-1.0f,  1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,  1.0f, -1.0f,  1.0f,  1.0f, Colours::white);
        }
       #endif
    }
    void updateTextureImage()
    {
        // This image is a special framebuffer-backed image, so when we draw to it, the context
        // will render directly into its framebuffer
        if (dynamicTextureImage.isValid())
        {
            dynamicTextureImage.clear (dynamicTextureImage.getBounds(),
                                       Colours::red.withRotatedHue (fabsf (::sinf (rotation / 300.0f))).withAlpha (0.7f));
            Graphics g (dynamicTextureImage);
            g.setFont (dynamicTextureImage.getHeight() / 3.0f);
            g.setColour (Colours::black);
            drawScrollingMessage (g, dynamicTextureImage.getHeight() / 2);
        }
    }
    void drawBackground2DStuff()
    {
        // Create an OpenGLGraphicsContext that will draw into this GL window..
        ScopedPointer<LowLevelGraphicsContext> glRenderer (createOpenGLGraphicsContext (openGLContext,
                                                                                        getContextWidth(),
                                                                                        getContextHeight()));
        if (glRenderer != nullptr)
        {
            Graphics g (glRenderer);
            g.addTransform (AffineTransform::scale ((float) getScale()));
            // This stuff just creates a spinning star shape and fills it..
            Path p;
            const float scale = getHeight() * 0.4f;
            p.addStar (Point<float> (getWidth() * 0.7f, getHeight() * 0.4f), 7,
                       scale * (float) sizeSlider.getValue(), scale,
                       rotation / 50.0f);
            g.setGradientFill (ColourGradient (Colours::green.withRotatedHue (fabsf (::sinf (rotation / 300.0f))),
                                               0, 0,
                                               Colours::green.withRotatedHue (fabsf (::cosf (rotation / -431.0f))),
                                               0, (float) getHeight(), false));
            g.fillPath (p);
        }
    }
    double getScale() const         { return Desktop::getInstance().getDisplays().getDisplayContaining (getScreenBounds().getCentre()).scale; }
    int getContextWidth() const     { return roundToInt (getScale() * getWidth()); }
    int getContextHeight() const    { return roundToInt (getScale() * getHeight()); }
    void timerCallback()
    {
        rotation += (float) speedSlider.getValue();
        textScrollPos += 1.4f;
        openGLContext.triggerRepaint();
    }
private:
    OpenGLContext openGLContext;
    Image logoImage, dynamicTextureImage;
    float rotation, textScrollPos;
    Draggable3DOrientation draggableOrientation;
    Slider speedSlider, sizeSlider;
    Label infoLabel;
    // Functions to create a couple of images to use as textures..
    static Image createLogoImage()
    {
        Image image (Image::ARGB, 256, 256, true, OpenGLImageType());
        if (image.isValid())
        {
            Graphics g (image);
            g.fillAll (Colours::lightgrey.withAlpha (0.8f));
            g.drawImageWithin (ImageFileFormat::loadFrom (BinaryData::juce_png, BinaryData::juce_pngSize),
                               0, 0, image.getWidth(), image.getHeight(), RectanglePlacement::stretchToFit);
            drawRandomStars (g, image.getWidth(), image.getHeight());
        }
        return image;
    }
    static void drawRandomStars (Graphics& g, int w, int h)
    {
        Random r;
        for (int i = 10; --i >= 0;)
        {
            Path pp;
            pp.addStar (Point<float> (r.nextFloat() * w, r.nextFloat() * h), r.nextInt (8) + 3, 10.0f, 20.0f, 0.0f);
            g.setColour (Colours::pink.withAlpha (0.4f));
            g.fillPath (pp);
        }
    }
    void drawScrollingMessage (Graphics& g, int y) const
    {
        g.drawSingleLineText ("The background, foreground and texture are all being drawn using the OpenGLGraphicsContext class, which "
                              "lets you use a standard JUCE 2D graphics context to render directly onto an OpenGL window or framebuffer...  ",
                              (int) -std::fmod (textScrollPos, 2500.0f), y);
    }
};
//==============================================================================
class OpenGLDemo  : public Component
{
public:
    OpenGLDemo()
        : Component ("OpenGL")
    {
        addAndMakeVisible (&canvas);
    }
    void resized()
    {
        canvas.setBounds (10, 10, getWidth() - 20, getHeight() - 50);
    }
private:
    DemoOpenGLCanvas canvas;
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLDemo)
};
//==============================================================================
Component* createOpenGLDemo()
{
    return new OpenGLDemo();
}
#endif
 |