The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1242 lines
46KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #include "../JuceDemoHeader.h"
  20. #if JUCE_OPENGL
  21. #include "WavefrontObjParser.h"
  22. //==============================================================================
  23. struct OpenGLDemoClasses
  24. {
  25. /** Vertex data to be passed to the shaders.
  26. For the purposes of this demo, each vertex will have a 3D position, a colour and a
  27. 2D texture co-ordinate. Of course you can ignore these or manipulate them in the
  28. shader programs but are some useful defaults to work from.
  29. */
  30. struct Vertex
  31. {
  32. float position[3];
  33. float normal[3];
  34. float colour[4];
  35. float texCoord[2];
  36. };
  37. //==============================================================================
  38. // This class just manages the attributes that the demo shaders use.
  39. struct Attributes
  40. {
  41. Attributes (OpenGLContext& openGLContext, OpenGLShaderProgram& shader)
  42. {
  43. position = createAttribute (openGLContext, shader, "position");
  44. normal = createAttribute (openGLContext, shader, "normal");
  45. sourceColour = createAttribute (openGLContext, shader, "sourceColour");
  46. textureCoordIn = createAttribute (openGLContext, shader, "textureCoordIn");
  47. }
  48. void enable (OpenGLContext& openGLContext)
  49. {
  50. if (position != nullptr)
  51. {
  52. openGLContext.extensions.glVertexAttribPointer (position->attributeID, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex), 0);
  53. openGLContext.extensions.glEnableVertexAttribArray (position->attributeID);
  54. }
  55. if (normal != nullptr)
  56. {
  57. openGLContext.extensions.glVertexAttribPointer (normal->attributeID, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 3));
  58. openGLContext.extensions.glEnableVertexAttribArray (normal->attributeID);
  59. }
  60. if (sourceColour != nullptr)
  61. {
  62. openGLContext.extensions.glVertexAttribPointer (sourceColour->attributeID, 4, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 6));
  63. openGLContext.extensions.glEnableVertexAttribArray (sourceColour->attributeID);
  64. }
  65. if (textureCoordIn != nullptr)
  66. {
  67. openGLContext.extensions.glVertexAttribPointer (textureCoordIn->attributeID, 2, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 10));
  68. openGLContext.extensions.glEnableVertexAttribArray (textureCoordIn->attributeID);
  69. }
  70. }
  71. void disable (OpenGLContext& openGLContext)
  72. {
  73. if (position != nullptr) openGLContext.extensions.glDisableVertexAttribArray (position->attributeID);
  74. if (normal != nullptr) openGLContext.extensions.glDisableVertexAttribArray (normal->attributeID);
  75. if (sourceColour != nullptr) openGLContext.extensions.glDisableVertexAttribArray (sourceColour->attributeID);
  76. if (textureCoordIn != nullptr) openGLContext.extensions.glDisableVertexAttribArray (textureCoordIn->attributeID);
  77. }
  78. ScopedPointer<OpenGLShaderProgram::Attribute> position, normal, sourceColour, textureCoordIn;
  79. private:
  80. static OpenGLShaderProgram::Attribute* createAttribute (OpenGLContext& openGLContext,
  81. OpenGLShaderProgram& shader,
  82. const char* attributeName)
  83. {
  84. if (openGLContext.extensions.glGetAttribLocation (shader.getProgramID(), attributeName) < 0)
  85. return nullptr;
  86. return new OpenGLShaderProgram::Attribute (shader, attributeName);
  87. }
  88. };
  89. //==============================================================================
  90. // This class just manages the uniform values that the demo shaders use.
  91. struct Uniforms
  92. {
  93. Uniforms (OpenGLContext& openGLContext, OpenGLShaderProgram& shader)
  94. {
  95. projectionMatrix = createUniform (openGLContext, shader, "projectionMatrix");
  96. viewMatrix = createUniform (openGLContext, shader, "viewMatrix");
  97. texture = createUniform (openGLContext, shader, "demoTexture");
  98. lightPosition = createUniform (openGLContext, shader, "lightPosition");
  99. bouncingNumber = createUniform (openGLContext, shader, "bouncingNumber");
  100. }
  101. ScopedPointer<OpenGLShaderProgram::Uniform> projectionMatrix, viewMatrix, texture, lightPosition, bouncingNumber;
  102. private:
  103. static OpenGLShaderProgram::Uniform* createUniform (OpenGLContext& openGLContext,
  104. OpenGLShaderProgram& shader,
  105. const char* uniformName)
  106. {
  107. if (openGLContext.extensions.glGetUniformLocation (shader.getProgramID(), uniformName) < 0)
  108. return nullptr;
  109. return new OpenGLShaderProgram::Uniform (shader, uniformName);
  110. }
  111. };
  112. //==============================================================================
  113. /** This loads a 3D model from an OBJ file and converts it into some vertex buffers
  114. that we can draw.
  115. */
  116. struct Shape
  117. {
  118. Shape (OpenGLContext& openGLContext)
  119. {
  120. if (shapeFile.load (BinaryData::teapot_obj).wasOk())
  121. for (int i = 0; i < shapeFile.shapes.size(); ++i)
  122. vertexBuffers.add (new VertexBuffer (openGLContext, *shapeFile.shapes.getUnchecked(i)));
  123. }
  124. void draw (OpenGLContext& openGLContext, Attributes& attributes)
  125. {
  126. for (int i = 0; i < vertexBuffers.size(); ++i)
  127. {
  128. VertexBuffer& vertexBuffer = *vertexBuffers.getUnchecked (i);
  129. vertexBuffer.bind();
  130. attributes.enable (openGLContext);
  131. glDrawElements (GL_TRIANGLES, vertexBuffer.numIndices, GL_UNSIGNED_INT, 0);
  132. attributes.disable (openGLContext);
  133. }
  134. }
  135. private:
  136. struct VertexBuffer
  137. {
  138. VertexBuffer (OpenGLContext& context, WavefrontObjFile::Shape& shape) : openGLContext (context)
  139. {
  140. numIndices = shape.mesh.indices.size();
  141. openGLContext.extensions.glGenBuffers (1, &vertexBuffer);
  142. openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
  143. Array<Vertex> vertices;
  144. createVertexListFromMesh (shape.mesh, vertices, Colours::green);
  145. openGLContext.extensions.glBufferData (GL_ARRAY_BUFFER, vertices.size() * (int) sizeof (Vertex),
  146. vertices.getRawDataPointer(), GL_STATIC_DRAW);
  147. openGLContext.extensions.glGenBuffers (1, &indexBuffer);
  148. openGLContext.extensions.glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
  149. openGLContext.extensions.glBufferData (GL_ELEMENT_ARRAY_BUFFER, numIndices * (int) sizeof (juce::uint32),
  150. shape.mesh.indices.getRawDataPointer(), GL_STATIC_DRAW);
  151. }
  152. ~VertexBuffer()
  153. {
  154. openGLContext.extensions.glDeleteBuffers (1, &vertexBuffer);
  155. openGLContext.extensions.glDeleteBuffers (1, &indexBuffer);
  156. }
  157. void bind()
  158. {
  159. openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
  160. openGLContext.extensions.glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
  161. }
  162. GLuint vertexBuffer, indexBuffer;
  163. int numIndices;
  164. OpenGLContext& openGLContext;
  165. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VertexBuffer)
  166. };
  167. WavefrontObjFile shapeFile;
  168. OwnedArray<VertexBuffer> vertexBuffers;
  169. static void createVertexListFromMesh (const WavefrontObjFile::Mesh& mesh, Array<Vertex>& list, Colour colour)
  170. {
  171. const float scale = 0.2f;
  172. WavefrontObjFile::TextureCoord defaultTexCoord = { 0.5f, 0.5f };
  173. WavefrontObjFile::Vertex defaultNormal = { 0.5f, 0.5f, 0.5f };
  174. for (int i = 0; i < mesh.vertices.size(); ++i)
  175. {
  176. const WavefrontObjFile::Vertex& v = mesh.vertices.getReference (i);
  177. const WavefrontObjFile::Vertex& n
  178. = i < mesh.normals.size() ? mesh.normals.getReference (i) : defaultNormal;
  179. const WavefrontObjFile::TextureCoord& tc
  180. = i < mesh.textureCoords.size() ? mesh.textureCoords.getReference (i) : defaultTexCoord;
  181. Vertex vert =
  182. {
  183. { scale * v.x, scale * v.y, scale * v.z, },
  184. { scale * n.x, scale * n.y, scale * n.z, },
  185. { colour.getFloatRed(), colour.getFloatGreen(), colour.getFloatBlue(), colour.getFloatAlpha() },
  186. { tc.x, tc.y }
  187. };
  188. list.add (vert);
  189. }
  190. }
  191. };
  192. //==============================================================================
  193. // These classes are used to load textures from the various sources that the demo uses..
  194. struct DemoTexture
  195. {
  196. virtual ~DemoTexture() {}
  197. virtual bool applyTo (OpenGLTexture&) = 0;
  198. String name;
  199. };
  200. struct DynamicTexture : public DemoTexture
  201. {
  202. DynamicTexture() { name = "Dynamically-generated texture"; }
  203. Image image;
  204. BouncingNumber x, y;
  205. bool applyTo (OpenGLTexture& texture) override
  206. {
  207. const int size = 128;
  208. if (! image.isValid())
  209. image = Image (Image::ARGB, size, size, true);
  210. {
  211. Graphics g (image);
  212. g.fillAll (Colours::lightcyan);
  213. g.setColour (Colours::darkred);
  214. g.drawRect (0, 0, size, size, 2);
  215. g.setColour (Colours::green);
  216. g.fillEllipse (x.getValue() * size * 0.9f, y.getValue() * size * 0.9f, size * 0.1f, size * 0.1f);
  217. g.setColour (Colours::black);
  218. g.setFont (40);
  219. g.drawFittedText (String (Time::getCurrentTime().getMilliseconds()), image.getBounds(), Justification::centred, 1);
  220. }
  221. texture.loadImage (image);
  222. return true;
  223. }
  224. };
  225. struct BuiltInTexture : public DemoTexture
  226. {
  227. BuiltInTexture (const char* nm, const void* imageData, size_t imageSize)
  228. : image (resizeImageToPowerOfTwo (ImageFileFormat::loadFrom (imageData, imageSize)))
  229. {
  230. name = nm;
  231. }
  232. Image image;
  233. bool applyTo (OpenGLTexture& texture) override
  234. {
  235. texture.loadImage (image);
  236. return false;
  237. }
  238. };
  239. struct TextureFromFile : public DemoTexture
  240. {
  241. TextureFromFile (const File& file)
  242. {
  243. name = file.getFileName();
  244. image = resizeImageToPowerOfTwo (ImageFileFormat::loadFrom (file));
  245. }
  246. Image image;
  247. bool applyTo (OpenGLTexture& texture) override
  248. {
  249. texture.loadImage (image);
  250. return false;
  251. }
  252. };
  253. static Image resizeImageToPowerOfTwo (Image image)
  254. {
  255. if (! (isPowerOfTwo (image.getWidth()) && isPowerOfTwo (image.getHeight())))
  256. return image.rescaled (jmin (1024, nextPowerOfTwo (image.getWidth())),
  257. jmin (1024, nextPowerOfTwo (image.getHeight())));
  258. return image;
  259. }
  260. class OpenGLDemo;
  261. //==============================================================================
  262. /**
  263. This component sits on top of the main GL demo, and contains all the sliders
  264. and widgets that control things.
  265. */
  266. class DemoControlsOverlay : public Component,
  267. private CodeDocument::Listener,
  268. private ComboBox::Listener,
  269. private Slider::Listener,
  270. private Button::Listener,
  271. private Timer
  272. {
  273. public:
  274. DemoControlsOverlay (OpenGLDemo& d)
  275. : demo (d),
  276. vertexEditorComp (vertexDocument, nullptr),
  277. fragmentEditorComp (fragmentDocument, nullptr)
  278. {
  279. addAndMakeVisible (statusLabel);
  280. statusLabel.setJustificationType (Justification::topLeft);
  281. statusLabel.setFont (Font (14.0f));
  282. addAndMakeVisible (sizeSlider);
  283. sizeSlider.setRange (0.0, 1.0, 0.001);
  284. sizeSlider.addListener (this);
  285. addAndMakeVisible (zoomLabel);
  286. zoomLabel.setText ("Zoom:", dontSendNotification);
  287. zoomLabel.attachToComponent (&sizeSlider, true);
  288. addAndMakeVisible (speedSlider);
  289. speedSlider.setRange (0.0, 0.5, 0.001);
  290. speedSlider.addListener (this);
  291. speedSlider.setSkewFactor (0.5f);
  292. addAndMakeVisible (speedLabel);
  293. speedLabel.setText ("Speed:", dontSendNotification);
  294. speedLabel.attachToComponent (&speedSlider, true);
  295. addAndMakeVisible (showBackgroundToggle);
  296. showBackgroundToggle.addListener (this);
  297. addAndMakeVisible (tabbedComp);
  298. tabbedComp.setTabBarDepth (25);
  299. tabbedComp.setColour (TabbedButtonBar::tabTextColourId, Colours::grey);
  300. tabbedComp.addTab ("Vertex", Colours::transparentBlack, &vertexEditorComp, false);
  301. tabbedComp.addTab ("Fragment", Colours::transparentBlack, &fragmentEditorComp, false);
  302. vertexDocument.addListener (this);
  303. fragmentDocument.addListener (this);
  304. textures.add (new BuiltInTexture ("Portmeirion", BinaryData::portmeirion_jpg, BinaryData::portmeirion_jpgSize));
  305. textures.add (new BuiltInTexture ("Tiled Background", BinaryData::tile_background_png, BinaryData::tile_background_pngSize));
  306. textures.add (new BuiltInTexture ("JUCE logo", BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize));
  307. textures.add (new DynamicTexture());
  308. addAndMakeVisible (textureBox);
  309. textureBox.addListener (this);
  310. updateTexturesList();
  311. addAndMakeVisible (presetBox);
  312. presetBox.addListener (this);
  313. Array<ShaderPreset> presets (getPresets());
  314. StringArray presetNames;
  315. for (int i = 0; i < presets.size(); ++i)
  316. presetBox.addItem (presets[i].name, i + 1);
  317. addAndMakeVisible (presetLabel);
  318. presetLabel.setText ("Shader Preset:", dontSendNotification);
  319. presetLabel.attachToComponent (&presetBox, true);
  320. addAndMakeVisible (textureLabel);
  321. textureLabel.setText ("Texture:", dontSendNotification);
  322. textureLabel.attachToComponent (&textureBox, true);
  323. lookAndFeelChanged();
  324. }
  325. void initialise()
  326. {
  327. showBackgroundToggle.setToggleState (false, sendNotification);
  328. textureBox.setSelectedItemIndex (0);
  329. presetBox.setSelectedItemIndex (0);
  330. speedSlider.setValue (0.01);
  331. sizeSlider.setValue (0.5);
  332. }
  333. void resized() override
  334. {
  335. Rectangle<int> area (getLocalBounds().reduced (4));
  336. Rectangle<int> top (area.removeFromTop (75));
  337. Rectangle<int> sliders (top.removeFromRight (area.getWidth() / 2));
  338. showBackgroundToggle.setBounds (sliders.removeFromBottom (25));
  339. speedSlider.setBounds (sliders.removeFromBottom (25));
  340. sizeSlider.setBounds (sliders.removeFromBottom (25));
  341. top.removeFromRight (70);
  342. statusLabel.setBounds (top);
  343. Rectangle<int> shaderArea (area.removeFromBottom (area.getHeight() / 2));
  344. Rectangle<int> presets (shaderArea.removeFromTop (25));
  345. presets.removeFromLeft (100);
  346. presetBox.setBounds (presets.removeFromLeft (150));
  347. presets.removeFromLeft (100);
  348. textureBox.setBounds (presets);
  349. shaderArea.removeFromTop (4);
  350. tabbedComp.setBounds (shaderArea);
  351. }
  352. void mouseDown (const MouseEvent& e) override
  353. {
  354. demo.draggableOrientation.mouseDown (e.getPosition());
  355. }
  356. void mouseDrag (const MouseEvent& e) override
  357. {
  358. demo.draggableOrientation.mouseDrag (e.getPosition());
  359. }
  360. void mouseWheelMove (const MouseEvent&, const MouseWheelDetails& d) override
  361. {
  362. sizeSlider.setValue (sizeSlider.getValue() + d.deltaY);
  363. }
  364. void mouseMagnify (const MouseEvent&, float magnifyAmmount) override
  365. {
  366. sizeSlider.setValue (sizeSlider.getValue() + magnifyAmmount - 1.0f);
  367. }
  368. void selectPreset (int preset)
  369. {
  370. const ShaderPreset& p = getPresets()[preset];
  371. vertexDocument.replaceAllContent (p.vertexShader);
  372. fragmentDocument.replaceAllContent (p.fragmentShader);
  373. startTimer (1);
  374. }
  375. void selectTexture (int itemID)
  376. {
  377. #if JUCE_MODAL_LOOPS_PERMITTED
  378. if (itemID == 1000)
  379. {
  380. static File lastLocation = File::getSpecialLocation (File::userPicturesDirectory);
  381. FileChooser fc ("Choose an image to open...", lastLocation, "*.jpg;*.jpeg;*.png;*.gif");
  382. if (fc.browseForFileToOpen())
  383. {
  384. lastLocation = fc.getResult();
  385. textures.add (new TextureFromFile (fc.getResult()));
  386. updateTexturesList();
  387. textureBox.setSelectedId (textures.size());
  388. }
  389. }
  390. else
  391. #endif
  392. {
  393. if (DemoTexture* t = textures [itemID - 1])
  394. demo.setTexture (t);
  395. }
  396. }
  397. void updateTexturesList()
  398. {
  399. textureBox.clear();
  400. for (int i = 0; i < textures.size(); ++i)
  401. textureBox.addItem (textures.getUnchecked(i)->name, i + 1);
  402. #if JUCE_MODAL_LOOPS_PERMITTED
  403. textureBox.addSeparator();
  404. textureBox.addItem ("Load from a file...", 1000);
  405. #endif
  406. }
  407. void updateShader()
  408. {
  409. startTimer (10);
  410. }
  411. Label statusLabel;
  412. private:
  413. void sliderValueChanged (Slider*) override
  414. {
  415. demo.scale = (float) sizeSlider.getValue();
  416. demo.rotationSpeed = (float) speedSlider.getValue();
  417. }
  418. void buttonClicked (Button*) override
  419. {
  420. demo.doBackgroundDrawing = showBackgroundToggle.getToggleState();
  421. }
  422. enum { shaderLinkDelay = 500 };
  423. void codeDocumentTextInserted (const String& /*newText*/, int /*insertIndex*/) override
  424. {
  425. startTimer (shaderLinkDelay);
  426. }
  427. void codeDocumentTextDeleted (int /*startIndex*/, int /*endIndex*/) override
  428. {
  429. startTimer (shaderLinkDelay);
  430. }
  431. void timerCallback() override
  432. {
  433. stopTimer();
  434. demo.setShaderProgram (vertexDocument.getAllContent(),
  435. fragmentDocument.getAllContent());
  436. }
  437. void comboBoxChanged (ComboBox* box) override
  438. {
  439. if (box == &presetBox)
  440. selectPreset (presetBox.getSelectedItemIndex());
  441. else if (box == &textureBox)
  442. selectTexture (textureBox.getSelectedId());
  443. }
  444. void lookAndFeelChanged() override
  445. {
  446. auto editorBackground = getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
  447. Colours::white);
  448. for (int i = tabbedComp.getNumTabs(); i >= 0; --i)
  449. tabbedComp.setTabBackgroundColour (i, editorBackground);
  450. vertexEditorComp.setColour (CodeEditorComponent::backgroundColourId, editorBackground);
  451. fragmentEditorComp.setColour (CodeEditorComponent::backgroundColourId, editorBackground);
  452. }
  453. OpenGLDemo& demo;
  454. Label speedLabel, zoomLabel;
  455. CodeDocument vertexDocument, fragmentDocument;
  456. CodeEditorComponent vertexEditorComp, fragmentEditorComp;
  457. TabbedComponent tabbedComp { TabbedButtonBar::TabsAtLeft };
  458. ComboBox presetBox, textureBox;
  459. Label presetLabel, textureLabel;
  460. Slider speedSlider, sizeSlider;
  461. ToggleButton showBackgroundToggle { "Draw 2D graphics in background" };
  462. OwnedArray<DemoTexture> textures;
  463. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoControlsOverlay)
  464. };
  465. //==============================================================================
  466. /** This is the main demo component - the GL context gets attached to it, and
  467. it implements the OpenGLRenderer callback so that it can do real GL work.
  468. */
  469. class OpenGLDemo : public Component,
  470. private OpenGLRenderer,
  471. private AsyncUpdater
  472. {
  473. public:
  474. OpenGLDemo()
  475. {
  476. if (MainAppWindow* mw = MainAppWindow::getMainAppWindow())
  477. mw->setRenderingEngine (0);
  478. setOpaque (true);
  479. addAndMakeVisible (controlsOverlay = new DemoControlsOverlay (*this));
  480. openGLContext.setRenderer (this);
  481. openGLContext.attachTo (*this);
  482. openGLContext.setContinuousRepainting (true);
  483. controlsOverlay->initialise();
  484. }
  485. ~OpenGLDemo()
  486. {
  487. openGLContext.detach();
  488. }
  489. void newOpenGLContextCreated() override
  490. {
  491. // nothing to do in this case - we'll initialise our shaders + textures
  492. // on demand, during the render callback.
  493. freeAllContextObjects();
  494. if (controlsOverlay != nullptr)
  495. controlsOverlay->updateShader();
  496. }
  497. void openGLContextClosing() override
  498. {
  499. // When the context is about to close, you must use this callback to delete
  500. // any GPU resources while the context is still current.
  501. freeAllContextObjects();
  502. if (lastTexture != nullptr)
  503. setTexture (lastTexture);
  504. }
  505. void freeAllContextObjects()
  506. {
  507. shape.reset();
  508. shader.reset();
  509. attributes.reset();
  510. uniforms.reset();
  511. texture.release();
  512. }
  513. // This is a virtual method in OpenGLRenderer, and is called when it's time
  514. // to do your GL rendering.
  515. void renderOpenGL() override
  516. {
  517. jassert (OpenGLHelpers::isContextActive());
  518. auto desktopScale = (float) openGLContext.getRenderingScale();
  519. OpenGLHelpers::clear (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
  520. Colours::lightblue));
  521. if (textureToUse != nullptr)
  522. if (! textureToUse->applyTo (texture))
  523. textureToUse = nullptr;
  524. // First draw our background graphics to demonstrate the OpenGLGraphicsContext class
  525. if (doBackgroundDrawing)
  526. drawBackground2DStuff (desktopScale);
  527. updateShader(); // Check whether we need to compile a new shader
  528. if (shader == nullptr)
  529. return;
  530. // Having used the juce 2D renderer, it will have messed-up a whole load of GL state, so
  531. // we need to initialise some important settings before doing our normal GL 3D drawing..
  532. glEnable (GL_DEPTH_TEST);
  533. glDepthFunc (GL_LESS);
  534. glEnable (GL_BLEND);
  535. glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  536. openGLContext.extensions.glActiveTexture (GL_TEXTURE0);
  537. glEnable (GL_TEXTURE_2D);
  538. glViewport (0, 0, roundToInt (desktopScale * getWidth()), roundToInt (desktopScale * getHeight()));
  539. texture.bind();
  540. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  541. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  542. shader->use();
  543. if (uniforms->projectionMatrix != nullptr)
  544. uniforms->projectionMatrix->setMatrix4 (getProjectionMatrix().mat, 1, false);
  545. if (uniforms->viewMatrix != nullptr)
  546. uniforms->viewMatrix->setMatrix4 (getViewMatrix().mat, 1, false);
  547. if (uniforms->texture != nullptr)
  548. uniforms->texture->set ((GLint) 0);
  549. if (uniforms->lightPosition != nullptr)
  550. uniforms->lightPosition->set (-15.0f, 10.0f, 15.0f, 0.0f);
  551. if (uniforms->bouncingNumber != nullptr)
  552. uniforms->bouncingNumber->set (bouncingNumber.getValue());
  553. shape->draw (openGLContext, *attributes);
  554. // Reset the element buffers so child Components draw correctly
  555. openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, 0);
  556. openGLContext.extensions.glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
  557. if (! controlsOverlay->isMouseButtonDown())
  558. rotation += (float) rotationSpeed;
  559. }
  560. Matrix3D<float> getProjectionMatrix() const
  561. {
  562. auto w = 1.0f / (scale + 0.1f);
  563. auto h = w * getLocalBounds().toFloat().getAspectRatio (false);
  564. return Matrix3D<float>::fromFrustum (-w, w, -h, h, 4.0f, 30.0f);
  565. }
  566. Matrix3D<float> getViewMatrix() const
  567. {
  568. auto viewMatrix = draggableOrientation.getRotationMatrix()
  569. * Vector3D<float> (0.0f, 1.0f, -10.0f);
  570. auto rotationMatrix = Matrix3D<float>::rotation ({ rotation, rotation, -0.3f });
  571. return rotationMatrix * viewMatrix;
  572. }
  573. void setTexture (DemoTexture* t)
  574. {
  575. lastTexture = textureToUse = t;
  576. }
  577. void setShaderProgram (const String& vertexShader, const String& fragmentShader)
  578. {
  579. newVertexShader = vertexShader;
  580. newFragmentShader = fragmentShader;
  581. }
  582. void paint (Graphics&) override {}
  583. void resized() override
  584. {
  585. controlsOverlay->setBounds (getLocalBounds());
  586. draggableOrientation.setViewport (getLocalBounds());
  587. }
  588. Draggable3DOrientation draggableOrientation;
  589. bool doBackgroundDrawing = false;
  590. float scale = 0.5f, rotationSpeed = 0;
  591. BouncingNumber bouncingNumber;
  592. private:
  593. void handleAsyncUpdate() override
  594. {
  595. controlsOverlay->statusLabel.setText (statusText, dontSendNotification);
  596. }
  597. void drawBackground2DStuff (float desktopScale)
  598. {
  599. // Create an OpenGLGraphicsContext that will draw into this GL window..
  600. ScopedPointer<LowLevelGraphicsContext> glRenderer (createOpenGLGraphicsContext (openGLContext,
  601. roundToInt (desktopScale * getWidth()),
  602. roundToInt (desktopScale * getHeight())));
  603. if (glRenderer != nullptr)
  604. {
  605. Graphics g (*glRenderer);
  606. g.addTransform (AffineTransform::scale (desktopScale));
  607. for (int i = 0; i < numElementsInArray (stars); ++i)
  608. {
  609. float size = 0.25f;
  610. // This stuff just creates a spinning star shape and fills it..
  611. Path p;
  612. p.addStar (Point<float> (getWidth() * stars[i].x.getValue(),
  613. getHeight() * stars[i].y.getValue()), 7,
  614. getHeight() * size * 0.5f,
  615. getHeight() * size,
  616. stars[i].angle.getValue());
  617. float hue = stars[i].hue.getValue();
  618. g.setGradientFill (ColourGradient (Colours::green.withRotatedHue (hue).withAlpha (0.8f),
  619. 0, 0,
  620. Colours::red.withRotatedHue (hue).withAlpha (0.5f),
  621. 0, (float) getHeight(), false));
  622. g.fillPath (p);
  623. }
  624. }
  625. }
  626. OpenGLContext openGLContext;
  627. ScopedPointer<DemoControlsOverlay> controlsOverlay;
  628. float rotation = 0;
  629. ScopedPointer<OpenGLShaderProgram> shader;
  630. ScopedPointer<Shape> shape;
  631. ScopedPointer<Attributes> attributes;
  632. ScopedPointer<Uniforms> uniforms;
  633. OpenGLTexture texture;
  634. DemoTexture* textureToUse = nullptr;
  635. DemoTexture* lastTexture = nullptr;
  636. String newVertexShader, newFragmentShader, statusText;
  637. struct BackgroundStar
  638. {
  639. SlowerBouncingNumber x, y, hue, angle;
  640. };
  641. BackgroundStar stars[3];
  642. //==============================================================================
  643. void updateShader()
  644. {
  645. if (newVertexShader.isNotEmpty() || newFragmentShader.isNotEmpty())
  646. {
  647. ScopedPointer<OpenGLShaderProgram> newShader (new OpenGLShaderProgram (openGLContext));
  648. if (newShader->addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (newVertexShader))
  649. && newShader->addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (newFragmentShader))
  650. && newShader->link())
  651. {
  652. shape.reset();
  653. attributes.reset();
  654. uniforms.reset();
  655. shader = newShader;
  656. shader->use();
  657. shape = new Shape (openGLContext);
  658. attributes = new Attributes (openGLContext, *shader);
  659. uniforms = new Uniforms (openGLContext, *shader);
  660. statusText = "GLSL: v" + String (OpenGLShaderProgram::getLanguageVersion(), 2);
  661. }
  662. else
  663. {
  664. statusText = newShader->getLastError();
  665. }
  666. triggerAsyncUpdate();
  667. newVertexShader = {};
  668. newFragmentShader = {};
  669. }
  670. }
  671. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLDemo)
  672. };
  673. //==============================================================================
  674. struct ShaderPreset
  675. {
  676. const char* name;
  677. const char* vertexShader;
  678. const char* fragmentShader;
  679. };
  680. static Array<ShaderPreset> getPresets()
  681. {
  682. #define SHADER_DEMO_HEADER \
  683. "/* This is a live OpenGL Shader demo.\n" \
  684. " Edit the shader program below and it will be \n" \
  685. " compiled and applied to the model above!\n" \
  686. "*/\n\n"
  687. ShaderPreset presets[] =
  688. {
  689. {
  690. "Texture + Lighting",
  691. SHADER_DEMO_HEADER
  692. "attribute vec4 position;\n"
  693. "attribute vec4 normal;\n"
  694. "attribute vec4 sourceColour;\n"
  695. "attribute vec2 textureCoordIn;\n"
  696. "\n"
  697. "uniform mat4 projectionMatrix;\n"
  698. "uniform mat4 viewMatrix;\n"
  699. "uniform vec4 lightPosition;\n"
  700. "\n"
  701. "varying vec4 destinationColour;\n"
  702. "varying vec2 textureCoordOut;\n"
  703. "varying float lightIntensity;\n"
  704. "\n"
  705. "void main()\n"
  706. "{\n"
  707. " destinationColour = sourceColour;\n"
  708. " textureCoordOut = textureCoordIn;\n"
  709. "\n"
  710. " vec4 light = viewMatrix * lightPosition;\n"
  711. " lightIntensity = dot (light, normal);\n"
  712. "\n"
  713. " gl_Position = projectionMatrix * viewMatrix * position;\n"
  714. "}\n",
  715. SHADER_DEMO_HEADER
  716. #if JUCE_OPENGL_ES
  717. "varying lowp vec4 destinationColour;\n"
  718. "varying lowp vec2 textureCoordOut;\n"
  719. "varying highp float lightIntensity;\n"
  720. #else
  721. "varying vec4 destinationColour;\n"
  722. "varying vec2 textureCoordOut;\n"
  723. "varying float lightIntensity;\n"
  724. #endif
  725. "\n"
  726. "uniform sampler2D demoTexture;\n"
  727. "\n"
  728. "void main()\n"
  729. "{\n"
  730. #if JUCE_OPENGL_ES
  731. " highp float l = max (0.3, lightIntensity * 0.3);\n"
  732. " highp vec4 colour = vec4 (l, l, l, 1.0);\n"
  733. #else
  734. " float l = max (0.3, lightIntensity * 0.3);\n"
  735. " vec4 colour = vec4 (l, l, l, 1.0);\n"
  736. #endif
  737. " gl_FragColor = colour * texture2D (demoTexture, textureCoordOut);\n"
  738. "}\n"
  739. },
  740. {
  741. "Textured",
  742. SHADER_DEMO_HEADER
  743. "attribute vec4 position;\n"
  744. "attribute vec4 sourceColour;\n"
  745. "attribute vec2 textureCoordIn;\n"
  746. "\n"
  747. "uniform mat4 projectionMatrix;\n"
  748. "uniform mat4 viewMatrix;\n"
  749. "\n"
  750. "varying vec4 destinationColour;\n"
  751. "varying vec2 textureCoordOut;\n"
  752. "\n"
  753. "void main()\n"
  754. "{\n"
  755. " destinationColour = sourceColour;\n"
  756. " textureCoordOut = textureCoordIn;\n"
  757. " gl_Position = projectionMatrix * viewMatrix * position;\n"
  758. "}\n",
  759. SHADER_DEMO_HEADER
  760. #if JUCE_OPENGL_ES
  761. "varying lowp vec4 destinationColour;\n"
  762. "varying lowp vec2 textureCoordOut;\n"
  763. #else
  764. "varying vec4 destinationColour;\n"
  765. "varying vec2 textureCoordOut;\n"
  766. #endif
  767. "\n"
  768. "uniform sampler2D demoTexture;\n"
  769. "\n"
  770. "void main()\n"
  771. "{\n"
  772. " gl_FragColor = texture2D (demoTexture, textureCoordOut);\n"
  773. "}\n"
  774. },
  775. {
  776. "Flat Colour",
  777. SHADER_DEMO_HEADER
  778. "attribute vec4 position;\n"
  779. "attribute vec4 sourceColour;\n"
  780. "attribute vec2 textureCoordIn;\n"
  781. "\n"
  782. "uniform mat4 projectionMatrix;\n"
  783. "uniform mat4 viewMatrix;\n"
  784. "\n"
  785. "varying vec4 destinationColour;\n"
  786. "varying vec2 textureCoordOut;\n"
  787. "\n"
  788. "void main()\n"
  789. "{\n"
  790. " destinationColour = sourceColour;\n"
  791. " textureCoordOut = textureCoordIn;\n"
  792. " gl_Position = projectionMatrix * viewMatrix * position;\n"
  793. "}\n",
  794. SHADER_DEMO_HEADER
  795. #if JUCE_OPENGL_ES
  796. "varying lowp vec4 destinationColour;\n"
  797. "varying lowp vec2 textureCoordOut;\n"
  798. #else
  799. "varying vec4 destinationColour;\n"
  800. "varying vec2 textureCoordOut;\n"
  801. #endif
  802. "\n"
  803. "void main()\n"
  804. "{\n"
  805. " gl_FragColor = destinationColour;\n"
  806. "}\n"
  807. },
  808. {
  809. "Rainbow",
  810. SHADER_DEMO_HEADER
  811. "attribute vec4 position;\n"
  812. "attribute vec4 sourceColour;\n"
  813. "attribute vec2 textureCoordIn;\n"
  814. "\n"
  815. "uniform mat4 projectionMatrix;\n"
  816. "uniform mat4 viewMatrix;\n"
  817. "\n"
  818. "varying vec4 destinationColour;\n"
  819. "varying vec2 textureCoordOut;\n"
  820. "\n"
  821. "varying float xPos;\n"
  822. "varying float yPos;\n"
  823. "varying float zPos;\n"
  824. "\n"
  825. "void main()\n"
  826. "{\n"
  827. " vec4 v = vec4 (position);\n"
  828. " xPos = clamp (v.x, 0.0, 1.0);\n"
  829. " yPos = clamp (v.y, 0.0, 1.0);\n"
  830. " zPos = clamp (v.z, 0.0, 1.0);\n"
  831. " gl_Position = projectionMatrix * viewMatrix * position;\n"
  832. "}",
  833. SHADER_DEMO_HEADER
  834. #if JUCE_OPENGL_ES
  835. "varying lowp vec4 destinationColour;\n"
  836. "varying lowp vec2 textureCoordOut;\n"
  837. "varying lowp float xPos;\n"
  838. "varying lowp float yPos;\n"
  839. "varying lowp float zPos;\n"
  840. #else
  841. "varying vec4 destinationColour;\n"
  842. "varying vec2 textureCoordOut;\n"
  843. "varying float xPos;\n"
  844. "varying float yPos;\n"
  845. "varying float zPos;\n"
  846. #endif
  847. "\n"
  848. "void main()\n"
  849. "{\n"
  850. " gl_FragColor = vec4 (xPos, yPos, zPos, 1.0);\n"
  851. "}"
  852. },
  853. {
  854. "Changing Colour",
  855. SHADER_DEMO_HEADER
  856. "attribute vec4 position;\n"
  857. "attribute vec2 textureCoordIn;\n"
  858. "\n"
  859. "uniform mat4 projectionMatrix;\n"
  860. "uniform mat4 viewMatrix;\n"
  861. "\n"
  862. "varying vec2 textureCoordOut;\n"
  863. "\n"
  864. "void main()\n"
  865. "{\n"
  866. " textureCoordOut = textureCoordIn;\n"
  867. " gl_Position = projectionMatrix * viewMatrix * position;\n"
  868. "}\n",
  869. SHADER_DEMO_HEADER
  870. "#define PI 3.1415926535897932384626433832795\n"
  871. "\n"
  872. #if JUCE_OPENGL_ES
  873. "precision mediump float;\n"
  874. "varying lowp vec2 textureCoordOut;\n"
  875. #else
  876. "varying vec2 textureCoordOut;\n"
  877. #endif
  878. "uniform float bouncingNumber;\n"
  879. "\n"
  880. "void main()\n"
  881. "{\n"
  882. " float b = bouncingNumber;\n"
  883. " float n = b * PI * 2.0;\n"
  884. " float sn = (sin (n * textureCoordOut.x) * 0.5) + 0.5;\n"
  885. " float cn = (sin (n * textureCoordOut.y) * 0.5) + 0.5;\n"
  886. "\n"
  887. " vec4 col = vec4 (b, sn, cn, 1.0);\n"
  888. " gl_FragColor = col;\n"
  889. "}\n"
  890. },
  891. {
  892. "Simple Light",
  893. SHADER_DEMO_HEADER
  894. "attribute vec4 position;\n"
  895. "attribute vec4 normal;\n"
  896. "\n"
  897. "uniform mat4 projectionMatrix;\n"
  898. "uniform mat4 viewMatrix;\n"
  899. "uniform vec4 lightPosition;\n"
  900. "\n"
  901. "varying float lightIntensity;\n"
  902. "\n"
  903. "void main()\n"
  904. "{\n"
  905. " vec4 light = viewMatrix * lightPosition;\n"
  906. " lightIntensity = dot (light, normal);\n"
  907. "\n"
  908. " gl_Position = projectionMatrix * viewMatrix * position;\n"
  909. "}\n",
  910. SHADER_DEMO_HEADER
  911. #if JUCE_OPENGL_ES
  912. "varying highp float lightIntensity;\n"
  913. #else
  914. "varying float lightIntensity;\n"
  915. #endif
  916. "\n"
  917. "void main()\n"
  918. "{\n"
  919. #if JUCE_OPENGL_ES
  920. " highp float l = lightIntensity * 0.25;\n"
  921. " highp vec4 colour = vec4 (l, l, l, 1.0);\n"
  922. #else
  923. " float l = lightIntensity * 0.25;\n"
  924. " vec4 colour = vec4 (l, l, l, 1.0);\n"
  925. #endif
  926. "\n"
  927. " gl_FragColor = colour;\n"
  928. "}\n"
  929. },
  930. {
  931. "Flattened",
  932. SHADER_DEMO_HEADER
  933. "attribute vec4 position;\n"
  934. "attribute vec4 normal;\n"
  935. "\n"
  936. "uniform mat4 projectionMatrix;\n"
  937. "uniform mat4 viewMatrix;\n"
  938. "uniform vec4 lightPosition;\n"
  939. "\n"
  940. "varying float lightIntensity;\n"
  941. "\n"
  942. "void main()\n"
  943. "{\n"
  944. " vec4 light = viewMatrix * lightPosition;\n"
  945. " lightIntensity = dot (light, normal);\n"
  946. "\n"
  947. " vec4 v = vec4 (position);\n"
  948. " v.z = v.z * 0.1;\n"
  949. "\n"
  950. " gl_Position = projectionMatrix * viewMatrix * v;\n"
  951. "}\n",
  952. SHADER_DEMO_HEADER
  953. #if JUCE_OPENGL_ES
  954. "varying highp float lightIntensity;\n"
  955. #else
  956. "varying float lightIntensity;\n"
  957. #endif
  958. "\n"
  959. "void main()\n"
  960. "{\n"
  961. #if JUCE_OPENGL_ES
  962. " highp float l = lightIntensity * 0.25;\n"
  963. " highp vec4 colour = vec4 (l, l, l, 1.0);\n"
  964. #else
  965. " float l = lightIntensity * 0.25;\n"
  966. " vec4 colour = vec4 (l, l, l, 1.0);\n"
  967. #endif
  968. "\n"
  969. " gl_FragColor = colour;\n"
  970. "}\n"
  971. },
  972. {
  973. "Toon Shader",
  974. SHADER_DEMO_HEADER
  975. "attribute vec4 position;\n"
  976. "attribute vec4 normal;\n"
  977. "\n"
  978. "uniform mat4 projectionMatrix;\n"
  979. "uniform mat4 viewMatrix;\n"
  980. "uniform vec4 lightPosition;\n"
  981. "\n"
  982. "varying float lightIntensity;\n"
  983. "\n"
  984. "void main()\n"
  985. "{\n"
  986. " vec4 light = viewMatrix * lightPosition;\n"
  987. " lightIntensity = dot (light, normal);\n"
  988. "\n"
  989. " gl_Position = projectionMatrix * viewMatrix * position;\n"
  990. "}\n",
  991. SHADER_DEMO_HEADER
  992. #if JUCE_OPENGL_ES
  993. "varying highp float lightIntensity;\n"
  994. #else
  995. "varying float lightIntensity;\n"
  996. #endif
  997. "\n"
  998. "void main()\n"
  999. "{\n"
  1000. #if JUCE_OPENGL_ES
  1001. " highp float intensity = lightIntensity * 0.5;\n"
  1002. " highp vec4 colour;\n"
  1003. #else
  1004. " float intensity = lightIntensity * 0.5;\n"
  1005. " vec4 colour;\n"
  1006. #endif
  1007. "\n"
  1008. " if (intensity > 0.95)\n"
  1009. " colour = vec4 (1.0, 0.5, 0.5, 1.0);\n"
  1010. " else if (intensity > 0.5)\n"
  1011. " colour = vec4 (0.6, 0.3, 0.3, 1.0);\n"
  1012. " else if (intensity > 0.25)\n"
  1013. " colour = vec4 (0.4, 0.2, 0.2, 1.0);\n"
  1014. " else\n"
  1015. " colour = vec4 (0.2, 0.1, 0.1, 1.0);\n"
  1016. "\n"
  1017. " gl_FragColor = colour;\n"
  1018. "}\n"
  1019. }
  1020. };
  1021. return Array<ShaderPreset> (presets, numElementsInArray (presets));
  1022. }
  1023. };
  1024. // This static object will register this demo type in a global list of demos..
  1025. static JuceDemoType<OpenGLDemoClasses::OpenGLDemo> demo ("20 Graphics: OpenGL");
  1026. #endif