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.

411 lines
16KB

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