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.

1258 lines
46KB

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