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.

1304 lines
46KB

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