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.

438 lines
15KB

  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: OpenGLAppDemo
  20. version: 1.0.0
  21. vendor: JUCE
  22. website: http://juce.com
  23. description: Simple 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, xcode_iphone
  27. moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
  28. type: Component
  29. mainClass: OpenGLAppDemo
  30. useLocalCopy: 1
  31. END_JUCE_PIP_METADATA
  32. *******************************************************************************/
  33. #pragma once
  34. #include "../Assets/DemoUtilities.h"
  35. #include "../Assets/WavefrontObjParser.h"
  36. //==============================================================================
  37. /*
  38. This component lives inside our window, and this is where you should put all
  39. your controls and content.
  40. */
  41. class OpenGLAppDemo final : public OpenGLAppComponent
  42. {
  43. public:
  44. //==============================================================================
  45. OpenGLAppDemo()
  46. {
  47. setSize (800, 600);
  48. }
  49. ~OpenGLAppDemo() override
  50. {
  51. shutdownOpenGL();
  52. }
  53. void initialise() override
  54. {
  55. createShaders();
  56. }
  57. void shutdown() override
  58. {
  59. shader .reset();
  60. shape .reset();
  61. attributes.reset();
  62. uniforms .reset();
  63. }
  64. Matrix3D<float> getProjectionMatrix() const
  65. {
  66. const ScopedLock lock (mutex);
  67. auto w = 1.0f / (0.5f + 0.1f);
  68. auto h = w * bounds.toFloat().getAspectRatio (false);
  69. return Matrix3D<float>::fromFrustum (-w, w, -h, h, 4.0f, 30.0f);
  70. }
  71. Matrix3D<float> getViewMatrix() const
  72. {
  73. auto viewMatrix = Matrix3D<float>::fromTranslation ({ 0.0f, 0.0f, -10.0f });
  74. auto rotationMatrix = viewMatrix.rotation ({ -0.3f, 5.0f * std::sin ((float) getFrameCounter() * 0.01f), 0.0f });
  75. return viewMatrix * rotationMatrix;
  76. }
  77. void render() override
  78. {
  79. using namespace ::juce::gl;
  80. jassert (OpenGLHelpers::isContextActive());
  81. auto desktopScale = (float) openGLContext.getRenderingScale();
  82. OpenGLHelpers::clear (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
  83. glEnable (GL_BLEND);
  84. glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  85. {
  86. const ScopedLock lock (mutex);
  87. glViewport (0, 0,
  88. roundToInt (desktopScale * (float) bounds.getWidth()),
  89. roundToInt (desktopScale * (float) bounds.getHeight()));
  90. }
  91. shader->use();
  92. if (uniforms->projectionMatrix != nullptr)
  93. uniforms->projectionMatrix->setMatrix4 (getProjectionMatrix().mat, 1, false);
  94. if (uniforms->viewMatrix != nullptr)
  95. uniforms->viewMatrix->setMatrix4 (getViewMatrix().mat, 1, false);
  96. shape->draw (*attributes);
  97. // Reset the element buffers so child Components draw correctly
  98. glBindBuffer (GL_ARRAY_BUFFER, 0);
  99. glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
  100. }
  101. void paint (Graphics& g) override
  102. {
  103. // You can add your component specific drawing code here!
  104. // This will draw over the top of the openGL background.
  105. g.setColour (getLookAndFeel().findColour (Label::textColourId));
  106. g.setFont (20);
  107. g.drawText ("OpenGL Example", 25, 20, 300, 30, Justification::left);
  108. g.drawLine (20, 20, 170, 20);
  109. g.drawLine (20, 50, 170, 50);
  110. }
  111. void resized() override
  112. {
  113. // This is called when this component is resized.
  114. // If you add any child components, this is where you should
  115. // update their positions.
  116. const ScopedLock lock (mutex);
  117. bounds = getLocalBounds();
  118. }
  119. void createShaders()
  120. {
  121. vertexShader =
  122. "attribute vec4 position;\n"
  123. "attribute vec4 sourceColour;\n"
  124. "attribute vec2 textureCoordIn;\n"
  125. "\n"
  126. "uniform mat4 projectionMatrix;\n"
  127. "uniform mat4 viewMatrix;\n"
  128. "\n"
  129. "varying vec4 destinationColour;\n"
  130. "varying vec2 textureCoordOut;\n"
  131. "\n"
  132. "void main()\n"
  133. "{\n"
  134. " destinationColour = sourceColour;\n"
  135. " textureCoordOut = textureCoordIn;\n"
  136. " gl_Position = projectionMatrix * viewMatrix * position;\n"
  137. "}\n";
  138. fragmentShader =
  139. #if JUCE_OPENGL_ES
  140. "varying lowp vec4 destinationColour;\n"
  141. "varying lowp vec2 textureCoordOut;\n"
  142. #else
  143. "varying vec4 destinationColour;\n"
  144. "varying vec2 textureCoordOut;\n"
  145. #endif
  146. "\n"
  147. "void main()\n"
  148. "{\n"
  149. #if JUCE_OPENGL_ES
  150. " lowp vec4 colour = vec4(0.95, 0.57, 0.03, 0.7);\n"
  151. #else
  152. " vec4 colour = vec4(0.95, 0.57, 0.03, 0.7);\n"
  153. #endif
  154. " gl_FragColor = colour;\n"
  155. "}\n";
  156. std::unique_ptr<OpenGLShaderProgram> newShader (new OpenGLShaderProgram (openGLContext));
  157. String statusText;
  158. if (newShader->addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (vertexShader))
  159. && newShader->addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (fragmentShader))
  160. && newShader->link())
  161. {
  162. shape .reset();
  163. attributes.reset();
  164. uniforms .reset();
  165. shader = std::move (newShader);
  166. shader->use();
  167. shape .reset (new Shape());
  168. attributes.reset (new Attributes (*shader));
  169. uniforms .reset (new Uniforms (*shader));
  170. statusText = "GLSL: v" + String (OpenGLShaderProgram::getLanguageVersion(), 2);
  171. }
  172. else
  173. {
  174. statusText = newShader->getLastError();
  175. }
  176. }
  177. private:
  178. //==============================================================================
  179. struct Vertex
  180. {
  181. float position[3];
  182. float normal[3];
  183. float colour[4];
  184. float texCoord[2];
  185. };
  186. //==============================================================================
  187. // This class just manages the attributes that the shaders use.
  188. struct Attributes
  189. {
  190. explicit Attributes (OpenGLShaderProgram& shaderProgram)
  191. {
  192. position .reset (createAttribute (shaderProgram, "position"));
  193. normal .reset (createAttribute (shaderProgram, "normal"));
  194. sourceColour .reset (createAttribute (shaderProgram, "sourceColour"));
  195. textureCoordIn.reset (createAttribute (shaderProgram, "textureCoordIn"));
  196. }
  197. void enable()
  198. {
  199. using namespace ::juce::gl;
  200. if (position.get() != nullptr)
  201. {
  202. glVertexAttribPointer (position->attributeID, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex), nullptr);
  203. glEnableVertexAttribArray (position->attributeID);
  204. }
  205. if (normal.get() != nullptr)
  206. {
  207. glVertexAttribPointer (normal->attributeID, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 3));
  208. glEnableVertexAttribArray (normal->attributeID);
  209. }
  210. if (sourceColour.get() != nullptr)
  211. {
  212. glVertexAttribPointer (sourceColour->attributeID, 4, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 6));
  213. glEnableVertexAttribArray (sourceColour->attributeID);
  214. }
  215. if (textureCoordIn.get() != nullptr)
  216. {
  217. glVertexAttribPointer (textureCoordIn->attributeID, 2, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 10));
  218. glEnableVertexAttribArray (textureCoordIn->attributeID);
  219. }
  220. }
  221. void disable()
  222. {
  223. using namespace ::juce::gl;
  224. if (position != nullptr) glDisableVertexAttribArray (position->attributeID);
  225. if (normal != nullptr) glDisableVertexAttribArray (normal->attributeID);
  226. if (sourceColour != nullptr) glDisableVertexAttribArray (sourceColour->attributeID);
  227. if (textureCoordIn != nullptr) glDisableVertexAttribArray (textureCoordIn->attributeID);
  228. }
  229. std::unique_ptr<OpenGLShaderProgram::Attribute> position, normal, sourceColour, textureCoordIn;
  230. private:
  231. static OpenGLShaderProgram::Attribute* createAttribute (OpenGLShaderProgram& shader,
  232. const char* attributeName)
  233. {
  234. using namespace ::juce::gl;
  235. if (glGetAttribLocation (shader.getProgramID(), attributeName) < 0)
  236. return nullptr;
  237. return new OpenGLShaderProgram::Attribute (shader, attributeName);
  238. }
  239. };
  240. //==============================================================================
  241. // This class just manages the uniform values that the demo shaders use.
  242. struct Uniforms
  243. {
  244. explicit Uniforms (OpenGLShaderProgram& shaderProgram)
  245. {
  246. projectionMatrix.reset (createUniform (shaderProgram, "projectionMatrix"));
  247. viewMatrix .reset (createUniform (shaderProgram, "viewMatrix"));
  248. }
  249. std::unique_ptr<OpenGLShaderProgram::Uniform> projectionMatrix, viewMatrix;
  250. private:
  251. static OpenGLShaderProgram::Uniform* createUniform (OpenGLShaderProgram& shaderProgram,
  252. const char* uniformName)
  253. {
  254. using namespace ::juce::gl;
  255. if (glGetUniformLocation (shaderProgram.getProgramID(), uniformName) < 0)
  256. return nullptr;
  257. return new OpenGLShaderProgram::Uniform (shaderProgram, uniformName);
  258. }
  259. };
  260. //==============================================================================
  261. /** This loads a 3D model from an OBJ file and converts it into some vertex buffers
  262. that we can draw.
  263. */
  264. struct Shape
  265. {
  266. Shape()
  267. {
  268. if (shapeFile.load (loadEntireAssetIntoString ("teapot.obj")).wasOk())
  269. for (auto* shapeVertices : shapeFile.shapes)
  270. vertexBuffers.add (new VertexBuffer (*shapeVertices));
  271. }
  272. void draw (Attributes& glAttributes)
  273. {
  274. using namespace ::juce::gl;
  275. for (auto* vertexBuffer : vertexBuffers)
  276. {
  277. vertexBuffer->bind();
  278. glAttributes.enable();
  279. glDrawElements (GL_TRIANGLES, vertexBuffer->numIndices, GL_UNSIGNED_INT, nullptr);
  280. glAttributes.disable();
  281. }
  282. }
  283. private:
  284. struct VertexBuffer
  285. {
  286. explicit VertexBuffer (WavefrontObjFile::Shape& aShape)
  287. {
  288. using namespace ::juce::gl;
  289. numIndices = aShape.mesh.indices.size();
  290. glGenBuffers (1, &vertexBuffer);
  291. glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
  292. Array<Vertex> vertices;
  293. createVertexListFromMesh (aShape.mesh, vertices, Colours::green);
  294. glBufferData (GL_ARRAY_BUFFER,
  295. static_cast<GLsizeiptr> (static_cast<size_t> (vertices.size()) * sizeof (Vertex)),
  296. vertices.getRawDataPointer(), GL_STATIC_DRAW);
  297. glGenBuffers (1, &indexBuffer);
  298. glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
  299. glBufferData (GL_ELEMENT_ARRAY_BUFFER,
  300. static_cast<GLsizeiptr> (static_cast<size_t> (numIndices) * sizeof (juce::uint32)),
  301. aShape.mesh.indices.getRawDataPointer(), GL_STATIC_DRAW);
  302. }
  303. ~VertexBuffer()
  304. {
  305. using namespace ::juce::gl;
  306. glDeleteBuffers (1, &vertexBuffer);
  307. glDeleteBuffers (1, &indexBuffer);
  308. }
  309. void bind()
  310. {
  311. using namespace ::juce::gl;
  312. glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
  313. glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
  314. }
  315. GLuint vertexBuffer, indexBuffer;
  316. int numIndices;
  317. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VertexBuffer)
  318. };
  319. WavefrontObjFile shapeFile;
  320. OwnedArray<VertexBuffer> vertexBuffers;
  321. static void createVertexListFromMesh (const WavefrontObjFile::Mesh& mesh, Array<Vertex>& list, Colour colour)
  322. {
  323. auto scale = 0.2f;
  324. WavefrontObjFile::TextureCoord defaultTexCoord { 0.5f, 0.5f };
  325. WavefrontObjFile::Vertex defaultNormal { 0.5f, 0.5f, 0.5f };
  326. for (auto i = 0; i < mesh.vertices.size(); ++i)
  327. {
  328. const auto& v = mesh.vertices.getReference (i);
  329. const auto& n = i < mesh.normals.size() ? mesh.normals.getReference (i) : defaultNormal;
  330. const auto& tc = i < mesh.textureCoords.size() ? mesh.textureCoords.getReference (i) : defaultTexCoord;
  331. list.add ({ { scale * v.x, scale * v.y, scale * v.z, },
  332. { scale * n.x, scale * n.y, scale * n.z, },
  333. { colour.getFloatRed(), colour.getFloatGreen(), colour.getFloatBlue(), colour.getFloatAlpha() },
  334. { tc.x, tc.y } });
  335. }
  336. }
  337. };
  338. const char* vertexShader;
  339. const char* fragmentShader;
  340. std::unique_ptr<OpenGLShaderProgram> shader;
  341. std::unique_ptr<Shape> shape;
  342. std::unique_ptr<Attributes> attributes;
  343. std::unique_ptr<Uniforms> uniforms;
  344. String newVertexShader, newFragmentShader;
  345. Rectangle<int> bounds;
  346. CriticalSection mutex;
  347. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLAppDemo)
  348. };