1 /* 2 ============================================================================== 3 4 This file is part of the JUCE examples. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 The code included in this file is provided under the terms of the ISC license 8 http://www.isc.org/downloads/software-support-policy/isc-license. Permission 9 To use, copy, modify, and/or distribute this software for any purpose with or 10 without fee is hereby granted provided that the above copyright notice and 11 this permission notice appear in all copies. 12 13 THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, 14 WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR 15 PURPOSE, ARE DISCLAIMED. 16 17 ============================================================================== 18 */ 19 20 /******************************************************************************* 21 The block below describes the properties of this PIP. A PIP is a short snippet 22 of code that can be read by the Projucer and used to generate a JUCE project. 23 24 BEGIN_JUCE_PIP_METADATA 25 26 name: OpenGLDemo2D 27 version: 1.0.0 28 vendor: JUCE 29 website: http://juce.com 30 description: Simple 2D OpenGL application. 31 32 dependencies: juce_core, juce_data_structures, juce_events, juce_graphics, 33 juce_gui_basics, juce_gui_extra, juce_opengl 34 exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone 35 36 moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1 37 38 type: Component 39 mainClass: OpenGLDemo2D 40 41 useLocalCopy: 1 42 43 END_JUCE_PIP_METADATA 44 45 *******************************************************************************/ 46 47 #pragma once 48 49 #include "../Assets/DemoUtilities.h" 50 51 //============================================================================== 52 class OpenGLDemo2D : public Component, 53 private CodeDocument::Listener, 54 private Timer 55 { 56 public: OpenGLDemo2D()57 OpenGLDemo2D() 58 { 59 setOpaque (true); 60 61 if (auto* peer = getPeer()) 62 peer->setCurrentRenderingEngine (0); 63 64 openGLContext.attachTo (*getTopLevelComponent()); 65 66 addAndMakeVisible (statusLabel); 67 statusLabel.setJustificationType (Justification::topLeft); 68 statusLabel.setFont (Font (14.0f)); 69 70 auto presets = getPresets(); 71 72 for (int i = 0; i < presets.size(); ++i) 73 presetBox.addItem (presets[i].name, i + 1); 74 75 addAndMakeVisible (presetLabel); 76 presetLabel.attachToComponent (&presetBox, true); 77 78 addAndMakeVisible (presetBox); 79 presetBox.onChange = [this] { selectPreset (presetBox.getSelectedItemIndex()); }; 80 81 fragmentEditorComp.setOpaque (false); 82 fragmentDocument.addListener (this); 83 addAndMakeVisible (fragmentEditorComp); 84 85 presetBox.setSelectedItemIndex (0); 86 87 setSize (500, 500); 88 } 89 ~OpenGLDemo2D()90 ~OpenGLDemo2D() override 91 { 92 openGLContext.detach(); 93 shader.reset(); 94 } 95 paint(Graphics & g)96 void paint (Graphics& g) override 97 { 98 g.fillCheckerBoard (getLocalBounds().toFloat(), 48.0f, 48.0f, Colours::lightgrey, Colours::white); 99 100 if (shader.get() == nullptr || shader->getFragmentShaderCode() != fragmentCode) 101 { 102 shader.reset(); 103 104 if (fragmentCode.isNotEmpty()) 105 { 106 shader.reset (new OpenGLGraphicsContextCustomShader (fragmentCode)); 107 108 auto result = shader->checkCompilation (g.getInternalContext()); 109 110 if (result.failed()) 111 { 112 statusLabel.setText (result.getErrorMessage(), dontSendNotification); 113 shader.reset(); 114 } 115 } 116 } 117 118 if (shader.get() != nullptr) 119 { 120 statusLabel.setText ({}, dontSendNotification); 121 122 shader->fillRect (g.getInternalContext(), getLocalBounds()); 123 } 124 } 125 resized()126 void resized() override 127 { 128 auto area = getLocalBounds().reduced (4); 129 130 statusLabel.setBounds (area.removeFromTop (75)); 131 132 area.removeFromTop (area.getHeight() / 2); 133 134 auto presets = area.removeFromTop (25); 135 presets.removeFromLeft (100); 136 presetBox.setBounds (presets.removeFromLeft (150)); 137 138 area.removeFromTop (4); 139 fragmentEditorComp.setBounds (area); 140 } 141 selectPreset(int preset)142 void selectPreset (int preset) 143 { 144 fragmentDocument.replaceAllContent (getPresets()[preset].fragmentShader); 145 startTimer (1); 146 } 147 148 std::unique_ptr<OpenGLGraphicsContextCustomShader> shader; 149 150 Label statusLabel, presetLabel { {}, "Shader Preset:" }; 151 ComboBox presetBox; 152 CodeDocument fragmentDocument; 153 CodeEditorComponent fragmentEditorComp { fragmentDocument, nullptr }; 154 String fragmentCode; 155 156 private: 157 OpenGLContext openGLContext; 158 159 enum { shaderLinkDelay = 500 }; 160 codeDocumentTextInserted(const String &,int)161 void codeDocumentTextInserted (const String& /*newText*/, int /*insertIndex*/) override 162 { 163 startTimer (shaderLinkDelay); 164 } 165 codeDocumentTextDeleted(int,int)166 void codeDocumentTextDeleted (int /*startIndex*/, int /*endIndex*/) override 167 { 168 startTimer (shaderLinkDelay); 169 } 170 timerCallback()171 void timerCallback() override 172 { 173 stopTimer(); 174 fragmentCode = fragmentDocument.getAllContent(); 175 repaint(); 176 } 177 178 struct ShaderPreset 179 { 180 const char* name; 181 const char* fragmentShader; 182 }; 183 getPresets()184 static Array<ShaderPreset> getPresets() 185 { 186 #define SHADER_2DDEMO_HEADER \ 187 "/* This demo shows the use of the OpenGLGraphicsContextCustomShader,\n" \ 188 " which allows a 2D area to be filled using a GL shader program.\n" \ 189 "\n" \ 190 " Edit the shader program below and it will be \n" \ 191 " recompiled in real-time!\n" \ 192 "*/\n\n" 193 194 ShaderPreset presets[] = 195 { 196 { 197 "Simple Gradient", 198 199 SHADER_2DDEMO_HEADER 200 "void main()\n" 201 "{\n" 202 " " JUCE_MEDIUMP " vec4 colour1 = vec4 (1.0, 0.4, 0.6, 1.0);\n" 203 " " JUCE_MEDIUMP " vec4 colour2 = vec4 (0.0, 0.8, 0.6, 1.0);\n" 204 " " JUCE_MEDIUMP " float alpha = pixelPos.x / 1000.0;\n" 205 " gl_FragColor = pixelAlpha * mix (colour1, colour2, alpha);\n" 206 "}\n" 207 }, 208 209 { 210 "Circular Gradient", 211 212 SHADER_2DDEMO_HEADER 213 "void main()\n" 214 "{\n" 215 " " JUCE_MEDIUMP " vec4 colour1 = vec4 (1.0, 0.4, 0.6, 1.0);\n" 216 " " JUCE_MEDIUMP " vec4 colour2 = vec4 (0.3, 0.4, 0.4, 1.0);\n" 217 " " JUCE_MEDIUMP " float alpha = distance (pixelPos, vec2 (600.0, 500.0)) / 400.0;\n" 218 " gl_FragColor = pixelAlpha * mix (colour1, colour2, alpha);\n" 219 "}\n" 220 }, 221 222 { 223 "Circle", 224 225 SHADER_2DDEMO_HEADER 226 "void main()\n" 227 "{\n" 228 " " JUCE_MEDIUMP " vec4 colour1 = vec4 (0.1, 0.1, 0.9, 1.0);\n" 229 " " JUCE_MEDIUMP " vec4 colour2 = vec4 (0.0, 0.8, 0.6, 1.0);\n" 230 " " JUCE_MEDIUMP " float distance = distance (pixelPos, vec2 (600.0, 500.0));\n" 231 "\n" 232 " " JUCE_MEDIUMP " float innerRadius = 200.0;\n" 233 " " JUCE_MEDIUMP " float outerRadius = 210.0;\n" 234 "\n" 235 " if (distance < innerRadius)\n" 236 " gl_FragColor = colour1;\n" 237 " else if (distance > outerRadius)\n" 238 " gl_FragColor = colour2;\n" 239 " else\n" 240 " gl_FragColor = mix (colour1, colour2, (distance - innerRadius) / (outerRadius - innerRadius));\n" 241 "\n" 242 " gl_FragColor *= pixelAlpha;\n" 243 "}\n" 244 }, 245 246 { 247 "Solid Colour", 248 249 SHADER_2DDEMO_HEADER 250 "void main()\n" 251 "{\n" 252 " gl_FragColor = vec4 (1.0, 0.6, 0.1, pixelAlpha);\n" 253 "}\n" 254 } 255 }; 256 257 return Array<ShaderPreset> (presets, numElementsInArray (presets)); 258 } 259 260 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLDemo2D) 261 }; 262