1296 lines
46 KiB
C
1296 lines
46 KiB
C
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
This file is part of the JUCE examples.
|
||
|
Copyright (c) 2020 - Raw Material Software Limited
|
||
|
|
||
|
The code included in this file is provided under the terms of the ISC license
|
||
|
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||
|
To use, copy, modify, and/or distribute this software for any purpose with or
|
||
|
without fee is hereby granted provided that the above copyright notice and
|
||
|
this permission notice appear in all copies.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
|
||
|
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
|
||
|
PURPOSE, ARE DISCLAIMED.
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
/*******************************************************************************
|
||
|
The block below describes the properties of this PIP. A PIP is a short snippet
|
||
|
of code that can be read by the Projucer and used to generate a JUCE project.
|
||
|
|
||
|
BEGIN_JUCE_PIP_METADATA
|
||
|
|
||
|
name: OpenGLDemo
|
||
|
version: 1.0.0
|
||
|
vendor: JUCE
|
||
|
website: http://juce.com
|
||
|
description: Simple 3D OpenGL application.
|
||
|
|
||
|
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
|
||
|
juce_gui_basics, juce_gui_extra, juce_opengl
|
||
|
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
|
||
|
|
||
|
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
|
||
|
|
||
|
type: Component
|
||
|
mainClass: OpenGLDemo
|
||
|
|
||
|
useLocalCopy: 1
|
||
|
|
||
|
END_JUCE_PIP_METADATA
|
||
|
|
||
|
*******************************************************************************/
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include "../Assets/DemoUtilities.h"
|
||
|
#include "../Assets/WavefrontObjParser.h"
|
||
|
|
||
|
struct OpenGLUtils
|
||
|
{
|
||
|
//==============================================================================
|
||
|
/** Vertex data to be passed to the shaders.
|
||
|
For the purposes of this demo, each vertex will have a 3D position, a colour and a
|
||
|
2D texture co-ordinate. Of course you can ignore these or manipulate them in the
|
||
|
shader programs but are some useful defaults to work from.
|
||
|
*/
|
||
|
struct Vertex
|
||
|
{
|
||
|
float position[3];
|
||
|
float normal[3];
|
||
|
float colour[4];
|
||
|
float texCoord[2];
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
// This class just manages the attributes that the demo shaders use.
|
||
|
struct Attributes
|
||
|
{
|
||
|
explicit Attributes (OpenGLShaderProgram& shader)
|
||
|
{
|
||
|
position .reset (createAttribute (shader, "position"));
|
||
|
normal .reset (createAttribute (shader, "normal"));
|
||
|
sourceColour .reset (createAttribute (shader, "sourceColour"));
|
||
|
textureCoordIn.reset (createAttribute (shader, "textureCoordIn"));
|
||
|
}
|
||
|
|
||
|
void enable()
|
||
|
{
|
||
|
using namespace ::juce::gl;
|
||
|
|
||
|
if (position.get() != nullptr)
|
||
|
{
|
||
|
glVertexAttribPointer (position->attributeID, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex), nullptr);
|
||
|
glEnableVertexAttribArray (position->attributeID);
|
||
|
}
|
||
|
|
||
|
if (normal.get() != nullptr)
|
||
|
{
|
||
|
glVertexAttribPointer (normal->attributeID, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 3));
|
||
|
glEnableVertexAttribArray (normal->attributeID);
|
||
|
}
|
||
|
|
||
|
if (sourceColour.get() != nullptr)
|
||
|
{
|
||
|
glVertexAttribPointer (sourceColour->attributeID, 4, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 6));
|
||
|
glEnableVertexAttribArray (sourceColour->attributeID);
|
||
|
}
|
||
|
|
||
|
if (textureCoordIn.get() != nullptr)
|
||
|
{
|
||
|
glVertexAttribPointer (textureCoordIn->attributeID, 2, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 10));
|
||
|
glEnableVertexAttribArray (textureCoordIn->attributeID);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void disable()
|
||
|
{
|
||
|
using namespace ::juce::gl;
|
||
|
|
||
|
if (position.get() != nullptr) glDisableVertexAttribArray (position->attributeID);
|
||
|
if (normal.get() != nullptr) glDisableVertexAttribArray (normal->attributeID);
|
||
|
if (sourceColour.get() != nullptr) glDisableVertexAttribArray (sourceColour->attributeID);
|
||
|
if (textureCoordIn.get() != nullptr) glDisableVertexAttribArray (textureCoordIn->attributeID);
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<OpenGLShaderProgram::Attribute> position, normal, sourceColour, textureCoordIn;
|
||
|
|
||
|
private:
|
||
|
static OpenGLShaderProgram::Attribute* createAttribute (OpenGLShaderProgram& shader,
|
||
|
const char* attributeName)
|
||
|
{
|
||
|
using namespace ::juce::gl;
|
||
|
|
||
|
if (glGetAttribLocation (shader.getProgramID(), attributeName) < 0)
|
||
|
return nullptr;
|
||
|
|
||
|
return new OpenGLShaderProgram::Attribute (shader, attributeName);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
// This class just manages the uniform values that the demo shaders use.
|
||
|
struct Uniforms
|
||
|
{
|
||
|
explicit Uniforms (OpenGLShaderProgram& shader)
|
||
|
{
|
||
|
projectionMatrix.reset (createUniform (shader, "projectionMatrix"));
|
||
|
viewMatrix .reset (createUniform (shader, "viewMatrix"));
|
||
|
texture .reset (createUniform (shader, "demoTexture"));
|
||
|
lightPosition .reset (createUniform (shader, "lightPosition"));
|
||
|
bouncingNumber .reset (createUniform (shader, "bouncingNumber"));
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<OpenGLShaderProgram::Uniform> projectionMatrix, viewMatrix, texture, lightPosition, bouncingNumber;
|
||
|
|
||
|
private:
|
||
|
static OpenGLShaderProgram::Uniform* createUniform (OpenGLShaderProgram& shader,
|
||
|
const char* uniformName)
|
||
|
{
|
||
|
using namespace ::juce::gl;
|
||
|
|
||
|
if (glGetUniformLocation (shader.getProgramID(), uniformName) < 0)
|
||
|
return nullptr;
|
||
|
|
||
|
return new OpenGLShaderProgram::Uniform (shader, uniformName);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
/** This loads a 3D model from an OBJ file and converts it into some vertex buffers
|
||
|
that we can draw.
|
||
|
*/
|
||
|
struct Shape
|
||
|
{
|
||
|
Shape()
|
||
|
{
|
||
|
if (shapeFile.load (loadEntireAssetIntoString ("teapot.obj")).wasOk())
|
||
|
for (auto* s : shapeFile.shapes)
|
||
|
vertexBuffers.add (new VertexBuffer (*s));
|
||
|
}
|
||
|
|
||
|
void draw (Attributes& attributes)
|
||
|
{
|
||
|
using namespace ::juce::gl;
|
||
|
|
||
|
for (auto* vertexBuffer : vertexBuffers)
|
||
|
{
|
||
|
vertexBuffer->bind();
|
||
|
|
||
|
attributes.enable();
|
||
|
glDrawElements (GL_TRIANGLES, vertexBuffer->numIndices, GL_UNSIGNED_INT, nullptr);
|
||
|
attributes.disable();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
struct VertexBuffer
|
||
|
{
|
||
|
explicit VertexBuffer (WavefrontObjFile::Shape& shape)
|
||
|
{
|
||
|
using namespace ::juce::gl;
|
||
|
|
||
|
numIndices = shape.mesh.indices.size();
|
||
|
|
||
|
glGenBuffers (1, &vertexBuffer);
|
||
|
glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
|
||
|
|
||
|
Array<Vertex> vertices;
|
||
|
createVertexListFromMesh (shape.mesh, vertices, Colours::green);
|
||
|
|
||
|
glBufferData (GL_ARRAY_BUFFER, vertices.size() * (int) sizeof (Vertex),
|
||
|
vertices.getRawDataPointer(), GL_STATIC_DRAW);
|
||
|
|
||
|
glGenBuffers (1, &indexBuffer);
|
||
|
glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||
|
glBufferData (GL_ELEMENT_ARRAY_BUFFER, numIndices * (int) sizeof (juce::uint32),
|
||
|
shape.mesh.indices.getRawDataPointer(), GL_STATIC_DRAW);
|
||
|
}
|
||
|
|
||
|
~VertexBuffer()
|
||
|
{
|
||
|
using namespace ::juce::gl;
|
||
|
|
||
|
glDeleteBuffers (1, &vertexBuffer);
|
||
|
glDeleteBuffers (1, &indexBuffer);
|
||
|
}
|
||
|
|
||
|
void bind()
|
||
|
{
|
||
|
using namespace ::juce::gl;
|
||
|
|
||
|
glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
|
||
|
glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||
|
}
|
||
|
|
||
|
GLuint vertexBuffer, indexBuffer;
|
||
|
int numIndices;
|
||
|
|
||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VertexBuffer)
|
||
|
};
|
||
|
|
||
|
WavefrontObjFile shapeFile;
|
||
|
OwnedArray<VertexBuffer> vertexBuffers;
|
||
|
|
||
|
static void createVertexListFromMesh (const WavefrontObjFile::Mesh& mesh, Array<Vertex>& list, Colour colour)
|
||
|
{
|
||
|
auto scale = 0.2f;
|
||
|
WavefrontObjFile::TextureCoord defaultTexCoord = { 0.5f, 0.5f };
|
||
|
WavefrontObjFile::Vertex defaultNormal = { 0.5f, 0.5f, 0.5f };
|
||
|
|
||
|
for (int i = 0; i < mesh.vertices.size(); ++i)
|
||
|
{
|
||
|
auto& v = mesh.vertices.getReference (i);
|
||
|
|
||
|
auto& n = (i < mesh.normals.size() ? mesh.normals.getReference (i)
|
||
|
: defaultNormal);
|
||
|
|
||
|
auto& tc = (i < mesh.textureCoords.size() ? mesh.textureCoords.getReference (i)
|
||
|
: defaultTexCoord);
|
||
|
|
||
|
list.add ({ { scale * v.x, scale * v.y, scale * v.z, },
|
||
|
{ scale * n.x, scale * n.y, scale * n.z, },
|
||
|
{ colour.getFloatRed(), colour.getFloatGreen(), colour.getFloatBlue(), colour.getFloatAlpha() },
|
||
|
{ tc.x, tc.y } });
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
struct ShaderPreset
|
||
|
{
|
||
|
const char* name;
|
||
|
const char* vertexShader;
|
||
|
const char* fragmentShader;
|
||
|
};
|
||
|
|
||
|
static Array<ShaderPreset> getPresets()
|
||
|
{
|
||
|
#define SHADER_DEMO_HEADER \
|
||
|
"/* This is a live OpenGL Shader demo.\n" \
|
||
|
" Edit the shader program below and it will be \n" \
|
||
|
" compiled and applied to the model above!\n" \
|
||
|
"*/\n\n"
|
||
|
|
||
|
ShaderPreset presets[] =
|
||
|
{
|
||
|
{
|
||
|
"Texture + Lighting",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
"attribute vec4 position;\n"
|
||
|
"attribute vec4 normal;\n"
|
||
|
"attribute vec4 sourceColour;\n"
|
||
|
"attribute vec2 textureCoordIn;\n"
|
||
|
"\n"
|
||
|
"uniform mat4 projectionMatrix;\n"
|
||
|
"uniform mat4 viewMatrix;\n"
|
||
|
"uniform vec4 lightPosition;\n"
|
||
|
"\n"
|
||
|
"varying vec4 destinationColour;\n"
|
||
|
"varying vec2 textureCoordOut;\n"
|
||
|
"varying float lightIntensity;\n"
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
" destinationColour = sourceColour;\n"
|
||
|
" textureCoordOut = textureCoordIn;\n"
|
||
|
"\n"
|
||
|
" vec4 light = viewMatrix * lightPosition;\n"
|
||
|
" lightIntensity = dot (light, normal);\n"
|
||
|
"\n"
|
||
|
" gl_Position = projectionMatrix * viewMatrix * position;\n"
|
||
|
"}\n",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
#if JUCE_OPENGL_ES
|
||
|
"varying lowp vec4 destinationColour;\n"
|
||
|
"varying lowp vec2 textureCoordOut;\n"
|
||
|
"varying highp float lightIntensity;\n"
|
||
|
#else
|
||
|
"varying vec4 destinationColour;\n"
|
||
|
"varying vec2 textureCoordOut;\n"
|
||
|
"varying float lightIntensity;\n"
|
||
|
#endif
|
||
|
"\n"
|
||
|
"uniform sampler2D demoTexture;\n"
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
#if JUCE_OPENGL_ES
|
||
|
" highp float l = max (0.3, lightIntensity * 0.3);\n"
|
||
|
" highp vec4 colour = vec4 (l, l, l, 1.0);\n"
|
||
|
#else
|
||
|
" float l = max (0.3, lightIntensity * 0.3);\n"
|
||
|
" vec4 colour = vec4 (l, l, l, 1.0);\n"
|
||
|
#endif
|
||
|
" gl_FragColor = colour * texture2D (demoTexture, textureCoordOut);\n"
|
||
|
"}\n"
|
||
|
},
|
||
|
|
||
|
{
|
||
|
"Textured",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
"attribute vec4 position;\n"
|
||
|
"attribute vec4 sourceColour;\n"
|
||
|
"attribute vec2 textureCoordIn;\n"
|
||
|
"\n"
|
||
|
"uniform mat4 projectionMatrix;\n"
|
||
|
"uniform mat4 viewMatrix;\n"
|
||
|
"\n"
|
||
|
"varying vec4 destinationColour;\n"
|
||
|
"varying vec2 textureCoordOut;\n"
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
" destinationColour = sourceColour;\n"
|
||
|
" textureCoordOut = textureCoordIn;\n"
|
||
|
" gl_Position = projectionMatrix * viewMatrix * position;\n"
|
||
|
"}\n",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
#if JUCE_OPENGL_ES
|
||
|
"varying lowp vec4 destinationColour;\n"
|
||
|
"varying lowp vec2 textureCoordOut;\n"
|
||
|
#else
|
||
|
"varying vec4 destinationColour;\n"
|
||
|
"varying vec2 textureCoordOut;\n"
|
||
|
#endif
|
||
|
"\n"
|
||
|
"uniform sampler2D demoTexture;\n"
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
" gl_FragColor = texture2D (demoTexture, textureCoordOut);\n"
|
||
|
"}\n"
|
||
|
},
|
||
|
|
||
|
{
|
||
|
"Flat Colour",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
"attribute vec4 position;\n"
|
||
|
"attribute vec4 sourceColour;\n"
|
||
|
"attribute vec2 textureCoordIn;\n"
|
||
|
"\n"
|
||
|
"uniform mat4 projectionMatrix;\n"
|
||
|
"uniform mat4 viewMatrix;\n"
|
||
|
"\n"
|
||
|
"varying vec4 destinationColour;\n"
|
||
|
"varying vec2 textureCoordOut;\n"
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
" destinationColour = sourceColour;\n"
|
||
|
" textureCoordOut = textureCoordIn;\n"
|
||
|
" gl_Position = projectionMatrix * viewMatrix * position;\n"
|
||
|
"}\n",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
#if JUCE_OPENGL_ES
|
||
|
"varying lowp vec4 destinationColour;\n"
|
||
|
"varying lowp vec2 textureCoordOut;\n"
|
||
|
#else
|
||
|
"varying vec4 destinationColour;\n"
|
||
|
"varying vec2 textureCoordOut;\n"
|
||
|
#endif
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
" gl_FragColor = destinationColour;\n"
|
||
|
"}\n"
|
||
|
},
|
||
|
|
||
|
{
|
||
|
"Rainbow",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
"attribute vec4 position;\n"
|
||
|
"attribute vec4 sourceColour;\n"
|
||
|
"attribute vec2 textureCoordIn;\n"
|
||
|
"\n"
|
||
|
"uniform mat4 projectionMatrix;\n"
|
||
|
"uniform mat4 viewMatrix;\n"
|
||
|
"\n"
|
||
|
"varying vec4 destinationColour;\n"
|
||
|
"varying vec2 textureCoordOut;\n"
|
||
|
"\n"
|
||
|
"varying float xPos;\n"
|
||
|
"varying float yPos;\n"
|
||
|
"varying float zPos;\n"
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
" vec4 v = vec4 (position);\n"
|
||
|
" xPos = clamp (v.x, 0.0, 1.0);\n"
|
||
|
" yPos = clamp (v.y, 0.0, 1.0);\n"
|
||
|
" zPos = clamp (v.z, 0.0, 1.0);\n"
|
||
|
" gl_Position = projectionMatrix * viewMatrix * position;\n"
|
||
|
"}",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
#if JUCE_OPENGL_ES
|
||
|
"varying lowp vec4 destinationColour;\n"
|
||
|
"varying lowp vec2 textureCoordOut;\n"
|
||
|
"varying lowp float xPos;\n"
|
||
|
"varying lowp float yPos;\n"
|
||
|
"varying lowp float zPos;\n"
|
||
|
#else
|
||
|
"varying vec4 destinationColour;\n"
|
||
|
"varying vec2 textureCoordOut;\n"
|
||
|
"varying float xPos;\n"
|
||
|
"varying float yPos;\n"
|
||
|
"varying float zPos;\n"
|
||
|
#endif
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
" gl_FragColor = vec4 (xPos, yPos, zPos, 1.0);\n"
|
||
|
"}"
|
||
|
},
|
||
|
|
||
|
{
|
||
|
"Changing Colour",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
"attribute vec4 position;\n"
|
||
|
"attribute vec2 textureCoordIn;\n"
|
||
|
"\n"
|
||
|
"uniform mat4 projectionMatrix;\n"
|
||
|
"uniform mat4 viewMatrix;\n"
|
||
|
"\n"
|
||
|
"varying vec2 textureCoordOut;\n"
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
" textureCoordOut = textureCoordIn;\n"
|
||
|
" gl_Position = projectionMatrix * viewMatrix * position;\n"
|
||
|
"}\n",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
"#define PI 3.1415926535897932384626433832795\n"
|
||
|
"\n"
|
||
|
#if JUCE_OPENGL_ES
|
||
|
"precision mediump float;\n"
|
||
|
"varying lowp vec2 textureCoordOut;\n"
|
||
|
#else
|
||
|
"varying vec2 textureCoordOut;\n"
|
||
|
#endif
|
||
|
"uniform float bouncingNumber;\n"
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
" float b = bouncingNumber;\n"
|
||
|
" float n = b * PI * 2.0;\n"
|
||
|
" float sn = (sin (n * textureCoordOut.x) * 0.5) + 0.5;\n"
|
||
|
" float cn = (sin (n * textureCoordOut.y) * 0.5) + 0.5;\n"
|
||
|
"\n"
|
||
|
" vec4 col = vec4 (b, sn, cn, 1.0);\n"
|
||
|
" gl_FragColor = col;\n"
|
||
|
"}\n"
|
||
|
},
|
||
|
|
||
|
{
|
||
|
"Simple Light",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
"attribute vec4 position;\n"
|
||
|
"attribute vec4 normal;\n"
|
||
|
"\n"
|
||
|
"uniform mat4 projectionMatrix;\n"
|
||
|
"uniform mat4 viewMatrix;\n"
|
||
|
"uniform vec4 lightPosition;\n"
|
||
|
"\n"
|
||
|
"varying float lightIntensity;\n"
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
" vec4 light = viewMatrix * lightPosition;\n"
|
||
|
" lightIntensity = dot (light, normal);\n"
|
||
|
"\n"
|
||
|
" gl_Position = projectionMatrix * viewMatrix * position;\n"
|
||
|
"}\n",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
#if JUCE_OPENGL_ES
|
||
|
"varying highp float lightIntensity;\n"
|
||
|
#else
|
||
|
"varying float lightIntensity;\n"
|
||
|
#endif
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
#if JUCE_OPENGL_ES
|
||
|
" highp float l = lightIntensity * 0.25;\n"
|
||
|
" highp vec4 colour = vec4 (l, l, l, 1.0);\n"
|
||
|
#else
|
||
|
" float l = lightIntensity * 0.25;\n"
|
||
|
" vec4 colour = vec4 (l, l, l, 1.0);\n"
|
||
|
#endif
|
||
|
"\n"
|
||
|
" gl_FragColor = colour;\n"
|
||
|
"}\n"
|
||
|
},
|
||
|
|
||
|
{
|
||
|
"Flattened",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
"attribute vec4 position;\n"
|
||
|
"attribute vec4 normal;\n"
|
||
|
"\n"
|
||
|
"uniform mat4 projectionMatrix;\n"
|
||
|
"uniform mat4 viewMatrix;\n"
|
||
|
"uniform vec4 lightPosition;\n"
|
||
|
"\n"
|
||
|
"varying float lightIntensity;\n"
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
" vec4 light = viewMatrix * lightPosition;\n"
|
||
|
" lightIntensity = dot (light, normal);\n"
|
||
|
"\n"
|
||
|
" vec4 v = vec4 (position);\n"
|
||
|
" v.z = v.z * 0.1;\n"
|
||
|
"\n"
|
||
|
" gl_Position = projectionMatrix * viewMatrix * v;\n"
|
||
|
"}\n",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
#if JUCE_OPENGL_ES
|
||
|
"varying highp float lightIntensity;\n"
|
||
|
#else
|
||
|
"varying float lightIntensity;\n"
|
||
|
#endif
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
#if JUCE_OPENGL_ES
|
||
|
" highp float l = lightIntensity * 0.25;\n"
|
||
|
" highp vec4 colour = vec4 (l, l, l, 1.0);\n"
|
||
|
#else
|
||
|
" float l = lightIntensity * 0.25;\n"
|
||
|
" vec4 colour = vec4 (l, l, l, 1.0);\n"
|
||
|
#endif
|
||
|
"\n"
|
||
|
" gl_FragColor = colour;\n"
|
||
|
"}\n"
|
||
|
},
|
||
|
|
||
|
{
|
||
|
"Toon Shader",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
"attribute vec4 position;\n"
|
||
|
"attribute vec4 normal;\n"
|
||
|
"\n"
|
||
|
"uniform mat4 projectionMatrix;\n"
|
||
|
"uniform mat4 viewMatrix;\n"
|
||
|
"uniform vec4 lightPosition;\n"
|
||
|
"\n"
|
||
|
"varying float lightIntensity;\n"
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
" vec4 light = viewMatrix * lightPosition;\n"
|
||
|
" lightIntensity = dot (light, normal);\n"
|
||
|
"\n"
|
||
|
" gl_Position = projectionMatrix * viewMatrix * position;\n"
|
||
|
"}\n",
|
||
|
|
||
|
SHADER_DEMO_HEADER
|
||
|
#if JUCE_OPENGL_ES
|
||
|
"varying highp float lightIntensity;\n"
|
||
|
#else
|
||
|
"varying float lightIntensity;\n"
|
||
|
#endif
|
||
|
"\n"
|
||
|
"void main()\n"
|
||
|
"{\n"
|
||
|
#if JUCE_OPENGL_ES
|
||
|
" highp float intensity = lightIntensity * 0.5;\n"
|
||
|
" highp vec4 colour;\n"
|
||
|
#else
|
||
|
" float intensity = lightIntensity * 0.5;\n"
|
||
|
" vec4 colour;\n"
|
||
|
#endif
|
||
|
"\n"
|
||
|
" if (intensity > 0.95)\n"
|
||
|
" colour = vec4 (1.0, 0.5, 0.5, 1.0);\n"
|
||
|
" else if (intensity > 0.5)\n"
|
||
|
" colour = vec4 (0.6, 0.3, 0.3, 1.0);\n"
|
||
|
" else if (intensity > 0.25)\n"
|
||
|
" colour = vec4 (0.4, 0.2, 0.2, 1.0);\n"
|
||
|
" else\n"
|
||
|
" colour = vec4 (0.2, 0.1, 0.1, 1.0);\n"
|
||
|
"\n"
|
||
|
" gl_FragColor = colour;\n"
|
||
|
"}\n"
|
||
|
}
|
||
|
};
|
||
|
|
||
|
return Array<ShaderPreset> (presets, numElementsInArray (presets));
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
// These classes are used to load textures from the various sources that the demo uses..
|
||
|
struct DemoTexture
|
||
|
{
|
||
|
virtual ~DemoTexture() {}
|
||
|
virtual bool applyTo (OpenGLTexture&) = 0;
|
||
|
|
||
|
String name;
|
||
|
};
|
||
|
|
||
|
struct DynamicTexture : public DemoTexture
|
||
|
{
|
||
|
DynamicTexture() { name = "Dynamically-generated texture"; }
|
||
|
|
||
|
Image image;
|
||
|
BouncingNumber x, y;
|
||
|
|
||
|
bool applyTo (OpenGLTexture& texture) override
|
||
|
{
|
||
|
int size = 128;
|
||
|
|
||
|
if (! image.isValid())
|
||
|
image = Image (Image::ARGB, size, size, true);
|
||
|
|
||
|
{
|
||
|
Graphics g (image);
|
||
|
g.fillAll (Colours::lightcyan);
|
||
|
|
||
|
g.setColour (Colours::darkred);
|
||
|
g.drawRect (0, 0, size, size, 2);
|
||
|
|
||
|
g.setColour (Colours::green);
|
||
|
g.fillEllipse (x.getValue() * (float) size * 0.9f,
|
||
|
y.getValue() * (float) size * 0.9f,
|
||
|
(float) size * 0.1f,
|
||
|
(float) size * 0.1f);
|
||
|
|
||
|
g.setColour (Colours::black);
|
||
|
g.setFont (40);
|
||
|
|
||
|
g.drawFittedText (String (Time::getCurrentTime().getMilliseconds()), image.getBounds(), Justification::centred, 1);
|
||
|
}
|
||
|
|
||
|
texture.loadImage (image);
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static Image resizeImageToPowerOfTwo (Image image)
|
||
|
{
|
||
|
if (! (isPowerOfTwo (image.getWidth()) && isPowerOfTwo (image.getHeight())))
|
||
|
return image.rescaled (jmin (1024, nextPowerOfTwo (image.getWidth())),
|
||
|
jmin (1024, nextPowerOfTwo (image.getHeight())));
|
||
|
|
||
|
return image;
|
||
|
}
|
||
|
|
||
|
struct BuiltInTexture : public DemoTexture
|
||
|
{
|
||
|
BuiltInTexture (const char* nm, const void* imageData, size_t imageSize)
|
||
|
: image (resizeImageToPowerOfTwo (ImageFileFormat::loadFrom (imageData, imageSize)))
|
||
|
{
|
||
|
name = nm;
|
||
|
}
|
||
|
|
||
|
Image image;
|
||
|
|
||
|
bool applyTo (OpenGLTexture& texture) override
|
||
|
{
|
||
|
texture.loadImage (image);
|
||
|
return false;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct TextureFromFile : public DemoTexture
|
||
|
{
|
||
|
TextureFromFile (const File& file)
|
||
|
{
|
||
|
name = file.getFileName();
|
||
|
image = resizeImageToPowerOfTwo (ImageFileFormat::loadFrom (file));
|
||
|
}
|
||
|
|
||
|
Image image;
|
||
|
|
||
|
bool applyTo (OpenGLTexture& texture) override
|
||
|
{
|
||
|
texture.loadImage (image);
|
||
|
return false;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct TextureFromAsset : public DemoTexture
|
||
|
{
|
||
|
TextureFromAsset (const char* assetName)
|
||
|
{
|
||
|
name = assetName;
|
||
|
image = resizeImageToPowerOfTwo (getImageFromAssets (assetName));
|
||
|
}
|
||
|
|
||
|
Image image;
|
||
|
|
||
|
bool applyTo (OpenGLTexture& texture) override
|
||
|
{
|
||
|
texture.loadImage (image);
|
||
|
return false;
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
/** This is the main demo component - the GL context gets attached to it, and
|
||
|
it implements the OpenGLRenderer callback so that it can do real GL work.
|
||
|
*/
|
||
|
class OpenGLDemo : public Component,
|
||
|
private OpenGLRenderer,
|
||
|
private AsyncUpdater
|
||
|
{
|
||
|
public:
|
||
|
OpenGLDemo()
|
||
|
{
|
||
|
if (auto* peer = getPeer())
|
||
|
peer->setCurrentRenderingEngine (0);
|
||
|
|
||
|
setOpaque (true);
|
||
|
controlsOverlay.reset (new DemoControlsOverlay (*this));
|
||
|
addAndMakeVisible (controlsOverlay.get());
|
||
|
|
||
|
openGLContext.setRenderer (this);
|
||
|
openGLContext.attachTo (*this);
|
||
|
openGLContext.setContinuousRepainting (true);
|
||
|
|
||
|
controlsOverlay->initialise();
|
||
|
|
||
|
setSize (500, 500);
|
||
|
}
|
||
|
|
||
|
~OpenGLDemo() override
|
||
|
{
|
||
|
openGLContext.detach();
|
||
|
}
|
||
|
|
||
|
void newOpenGLContextCreated() override
|
||
|
{
|
||
|
// nothing to do in this case - we'll initialise our shaders + textures
|
||
|
// on demand, during the render callback.
|
||
|
freeAllContextObjects();
|
||
|
|
||
|
if (controlsOverlay.get() != nullptr)
|
||
|
controlsOverlay->updateShader();
|
||
|
}
|
||
|
|
||
|
void openGLContextClosing() override
|
||
|
{
|
||
|
// When the context is about to close, you must use this callback to delete
|
||
|
// any GPU resources while the context is still current.
|
||
|
freeAllContextObjects();
|
||
|
|
||
|
if (lastTexture != nullptr)
|
||
|
setTexture (lastTexture);
|
||
|
}
|
||
|
|
||
|
void freeAllContextObjects()
|
||
|
{
|
||
|
shape .reset();
|
||
|
shader .reset();
|
||
|
attributes.reset();
|
||
|
uniforms .reset();
|
||
|
texture .release();
|
||
|
}
|
||
|
|
||
|
// This is a virtual method in OpenGLRenderer, and is called when it's time
|
||
|
// to do your GL rendering.
|
||
|
void renderOpenGL() override
|
||
|
{
|
||
|
using namespace ::juce::gl;
|
||
|
|
||
|
const ScopedLock lock (mutex);
|
||
|
|
||
|
jassert (OpenGLHelpers::isContextActive());
|
||
|
|
||
|
auto desktopScale = (float) openGLContext.getRenderingScale();
|
||
|
|
||
|
OpenGLHelpers::clear (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
|
||
|
Colours::lightblue));
|
||
|
|
||
|
if (textureToUse != nullptr)
|
||
|
if (! textureToUse->applyTo (texture))
|
||
|
textureToUse = nullptr;
|
||
|
|
||
|
// First draw our background graphics to demonstrate the OpenGLGraphicsContext class
|
||
|
if (doBackgroundDrawing)
|
||
|
drawBackground2DStuff (desktopScale);
|
||
|
|
||
|
updateShader(); // Check whether we need to compile a new shader
|
||
|
|
||
|
if (shader.get() == nullptr)
|
||
|
return;
|
||
|
|
||
|
// Having used the juce 2D renderer, it will have messed-up a whole load of GL state, so
|
||
|
// we need to initialise some important settings before doing our normal GL 3D drawing..
|
||
|
glEnable (GL_DEPTH_TEST);
|
||
|
glDepthFunc (GL_LESS);
|
||
|
glEnable (GL_BLEND);
|
||
|
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||
|
glActiveTexture (GL_TEXTURE0);
|
||
|
glEnable (GL_TEXTURE_2D);
|
||
|
|
||
|
glViewport (0, 0,
|
||
|
roundToInt (desktopScale * (float) bounds.getWidth()),
|
||
|
roundToInt (desktopScale * (float) bounds.getHeight()));
|
||
|
|
||
|
texture.bind();
|
||
|
|
||
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||
|
|
||
|
shader->use();
|
||
|
|
||
|
if (uniforms->projectionMatrix.get() != nullptr)
|
||
|
uniforms->projectionMatrix->setMatrix4 (getProjectionMatrix().mat, 1, false);
|
||
|
|
||
|
if (uniforms->viewMatrix.get() != nullptr)
|
||
|
uniforms->viewMatrix->setMatrix4 (getViewMatrix().mat, 1, false);
|
||
|
|
||
|
if (uniforms->texture.get() != nullptr)
|
||
|
uniforms->texture->set ((GLint) 0);
|
||
|
|
||
|
if (uniforms->lightPosition.get() != nullptr)
|
||
|
uniforms->lightPosition->set (-15.0f, 10.0f, 15.0f, 0.0f);
|
||
|
|
||
|
if (uniforms->bouncingNumber.get() != nullptr)
|
||
|
uniforms->bouncingNumber->set (bouncingNumber.getValue());
|
||
|
|
||
|
shape->draw (*attributes);
|
||
|
|
||
|
// Reset the element buffers so child Components draw correctly
|
||
|
glBindBuffer (GL_ARRAY_BUFFER, 0);
|
||
|
glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
|
||
|
|
||
|
if (! controlsOverlay->isMouseButtonDownThreadsafe())
|
||
|
rotation += (float) rotationSpeed;
|
||
|
}
|
||
|
|
||
|
Matrix3D<float> getProjectionMatrix() const
|
||
|
{
|
||
|
const ScopedLock lock (mutex);
|
||
|
|
||
|
auto w = 1.0f / (scale + 0.1f);
|
||
|
auto h = w * bounds.toFloat().getAspectRatio (false);
|
||
|
|
||
|
return Matrix3D<float>::fromFrustum (-w, w, -h, h, 4.0f, 30.0f);
|
||
|
}
|
||
|
|
||
|
Matrix3D<float> getViewMatrix() const
|
||
|
{
|
||
|
const ScopedLock lock (mutex);
|
||
|
|
||
|
auto viewMatrix = draggableOrientation.getRotationMatrix() * Vector3D<float> (0.0f, 1.0f, -10.0f);
|
||
|
auto rotationMatrix = Matrix3D<float>::rotation ({ rotation, rotation, -0.3f });
|
||
|
|
||
|
return rotationMatrix * viewMatrix;
|
||
|
}
|
||
|
|
||
|
void setTexture (OpenGLUtils::DemoTexture* t)
|
||
|
{
|
||
|
lastTexture = textureToUse = t;
|
||
|
}
|
||
|
|
||
|
void setShaderProgram (const String& vertexShader, const String& fragmentShader)
|
||
|
{
|
||
|
newVertexShader = vertexShader;
|
||
|
newFragmentShader = fragmentShader;
|
||
|
}
|
||
|
|
||
|
void paint (Graphics&) override {}
|
||
|
|
||
|
void resized() override
|
||
|
{
|
||
|
const ScopedLock lock (mutex);
|
||
|
|
||
|
bounds = getLocalBounds();
|
||
|
controlsOverlay->setBounds (bounds);
|
||
|
draggableOrientation.setViewport (bounds);
|
||
|
}
|
||
|
|
||
|
Rectangle<int> bounds;
|
||
|
Draggable3DOrientation draggableOrientation;
|
||
|
bool doBackgroundDrawing = false;
|
||
|
float scale = 0.5f, rotationSpeed = 0.0f;
|
||
|
BouncingNumber bouncingNumber;
|
||
|
CriticalSection mutex;
|
||
|
|
||
|
private:
|
||
|
void handleAsyncUpdate() override
|
||
|
{
|
||
|
controlsOverlay->statusLabel.setText (statusText, dontSendNotification);
|
||
|
}
|
||
|
|
||
|
void drawBackground2DStuff (float desktopScale)
|
||
|
{
|
||
|
// Create an OpenGLGraphicsContext that will draw into this GL window..
|
||
|
std::unique_ptr<LowLevelGraphicsContext> glRenderer (createOpenGLGraphicsContext (openGLContext,
|
||
|
roundToInt (desktopScale * (float) bounds.getWidth()),
|
||
|
roundToInt (desktopScale * (float) bounds.getHeight())));
|
||
|
|
||
|
if (glRenderer.get() != nullptr)
|
||
|
{
|
||
|
Graphics g (*glRenderer);
|
||
|
g.addTransform (AffineTransform::scale (desktopScale));
|
||
|
|
||
|
for (auto s : stars)
|
||
|
{
|
||
|
auto size = 0.25f;
|
||
|
|
||
|
// This stuff just creates a spinning star shape and fills it..
|
||
|
Path p;
|
||
|
p.addStar ({ (float) bounds.getWidth() * s.x.getValue(),
|
||
|
(float) bounds.getHeight() * s.y.getValue() },
|
||
|
7,
|
||
|
(float) bounds.getHeight() * size * 0.5f,
|
||
|
(float) bounds.getHeight() * size,
|
||
|
s.angle.getValue());
|
||
|
|
||
|
auto hue = s.hue.getValue();
|
||
|
|
||
|
g.setGradientFill (ColourGradient (Colours::green.withRotatedHue (hue).withAlpha (0.8f),
|
||
|
0, 0,
|
||
|
Colours::red.withRotatedHue (hue).withAlpha (0.5f),
|
||
|
0, (float) bounds.getHeight(), false));
|
||
|
g.fillPath (p);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
OpenGLContext openGLContext;
|
||
|
|
||
|
//==============================================================================
|
||
|
/**
|
||
|
This component sits on top of the main GL demo, and contains all the sliders
|
||
|
and widgets that control things.
|
||
|
*/
|
||
|
class DemoControlsOverlay : public Component,
|
||
|
private CodeDocument::Listener,
|
||
|
private Slider::Listener,
|
||
|
private Timer
|
||
|
{
|
||
|
public:
|
||
|
DemoControlsOverlay (OpenGLDemo& d)
|
||
|
: demo (d)
|
||
|
{
|
||
|
addAndMakeVisible (statusLabel);
|
||
|
statusLabel.setJustificationType (Justification::topLeft);
|
||
|
statusLabel.setFont (Font (14.0f));
|
||
|
|
||
|
addAndMakeVisible (sizeSlider);
|
||
|
sizeSlider.setRange (0.0, 1.0, 0.001);
|
||
|
sizeSlider.addListener (this);
|
||
|
|
||
|
addAndMakeVisible (zoomLabel);
|
||
|
zoomLabel.attachToComponent (&sizeSlider, true);
|
||
|
|
||
|
addAndMakeVisible (speedSlider);
|
||
|
speedSlider.setRange (0.0, 0.5, 0.001);
|
||
|
speedSlider.addListener (this);
|
||
|
speedSlider.setSkewFactor (0.5f);
|
||
|
|
||
|
addAndMakeVisible (speedLabel);
|
||
|
speedLabel.attachToComponent (&speedSlider, true);
|
||
|
|
||
|
addAndMakeVisible (showBackgroundToggle);
|
||
|
showBackgroundToggle.onClick = [this] { demo.doBackgroundDrawing = showBackgroundToggle.getToggleState(); };
|
||
|
|
||
|
addAndMakeVisible (tabbedComp);
|
||
|
tabbedComp.setTabBarDepth (25);
|
||
|
tabbedComp.setColour (TabbedButtonBar::tabTextColourId, Colours::grey);
|
||
|
tabbedComp.addTab ("Vertex", Colours::transparentBlack, &vertexEditorComp, false);
|
||
|
tabbedComp.addTab ("Fragment", Colours::transparentBlack, &fragmentEditorComp, false);
|
||
|
|
||
|
vertexDocument.addListener (this);
|
||
|
fragmentDocument.addListener (this);
|
||
|
|
||
|
textures.add (new OpenGLUtils::TextureFromAsset ("portmeirion.jpg"));
|
||
|
textures.add (new OpenGLUtils::TextureFromAsset ("tile_background.png"));
|
||
|
textures.add (new OpenGLUtils::TextureFromAsset ("juce_icon.png"));
|
||
|
textures.add (new OpenGLUtils::DynamicTexture());
|
||
|
|
||
|
addAndMakeVisible (textureBox);
|
||
|
textureBox.onChange = [this] { selectTexture (textureBox.getSelectedId()); };
|
||
|
updateTexturesList();
|
||
|
|
||
|
addAndMakeVisible (presetBox);
|
||
|
presetBox.onChange = [this] { selectPreset (presetBox.getSelectedItemIndex()); };
|
||
|
|
||
|
auto presets = OpenGLUtils::getPresets();
|
||
|
|
||
|
for (int i = 0; i < presets.size(); ++i)
|
||
|
presetBox.addItem (presets[i].name, i + 1);
|
||
|
|
||
|
addAndMakeVisible (presetLabel);
|
||
|
presetLabel.attachToComponent (&presetBox, true);
|
||
|
|
||
|
addAndMakeVisible (textureLabel);
|
||
|
textureLabel.attachToComponent (&textureBox, true);
|
||
|
|
||
|
lookAndFeelChanged();
|
||
|
}
|
||
|
|
||
|
void initialise()
|
||
|
{
|
||
|
showBackgroundToggle.setToggleState (false, sendNotification);
|
||
|
textureBox.setSelectedItemIndex (0);
|
||
|
presetBox .setSelectedItemIndex (0);
|
||
|
speedSlider.setValue (0.01);
|
||
|
sizeSlider .setValue (0.5);
|
||
|
}
|
||
|
|
||
|
void resized() override
|
||
|
{
|
||
|
auto area = getLocalBounds().reduced (4);
|
||
|
|
||
|
auto top = area.removeFromTop (75);
|
||
|
|
||
|
auto sliders = top.removeFromRight (area.getWidth() / 2);
|
||
|
showBackgroundToggle.setBounds (sliders.removeFromBottom (25));
|
||
|
speedSlider .setBounds (sliders.removeFromBottom (25));
|
||
|
sizeSlider .setBounds (sliders.removeFromBottom (25));
|
||
|
|
||
|
top.removeFromRight (70);
|
||
|
statusLabel.setBounds (top);
|
||
|
|
||
|
auto shaderArea = area.removeFromBottom (area.getHeight() / 2);
|
||
|
|
||
|
auto presets = shaderArea.removeFromTop (25);
|
||
|
presets.removeFromLeft (100);
|
||
|
presetBox.setBounds (presets.removeFromLeft (150));
|
||
|
presets.removeFromLeft (100);
|
||
|
textureBox.setBounds (presets);
|
||
|
|
||
|
shaderArea.removeFromTop (4);
|
||
|
tabbedComp.setBounds (shaderArea);
|
||
|
}
|
||
|
|
||
|
bool isMouseButtonDownThreadsafe() const { return buttonDown; }
|
||
|
|
||
|
void mouseDown (const MouseEvent& e) override
|
||
|
{
|
||
|
const ScopedLock lock (demo.mutex);
|
||
|
demo.draggableOrientation.mouseDown (e.getPosition());
|
||
|
|
||
|
buttonDown = true;
|
||
|
}
|
||
|
|
||
|
void mouseDrag (const MouseEvent& e) override
|
||
|
{
|
||
|
const ScopedLock lock (demo.mutex);
|
||
|
demo.draggableOrientation.mouseDrag (e.getPosition());
|
||
|
}
|
||
|
|
||
|
void mouseUp (const MouseEvent&) override
|
||
|
{
|
||
|
buttonDown = false;
|
||
|
}
|
||
|
|
||
|
void mouseWheelMove (const MouseEvent&, const MouseWheelDetails& d) override
|
||
|
{
|
||
|
sizeSlider.setValue (sizeSlider.getValue() + d.deltaY);
|
||
|
}
|
||
|
|
||
|
void mouseMagnify (const MouseEvent&, float magnifyAmmount) override
|
||
|
{
|
||
|
sizeSlider.setValue (sizeSlider.getValue() + magnifyAmmount - 1.0f);
|
||
|
}
|
||
|
|
||
|
void selectPreset (int preset)
|
||
|
{
|
||
|
const auto& p = OpenGLUtils::getPresets()[preset];
|
||
|
|
||
|
vertexDocument .replaceAllContent (p.vertexShader);
|
||
|
fragmentDocument.replaceAllContent (p.fragmentShader);
|
||
|
|
||
|
startTimer (1);
|
||
|
}
|
||
|
|
||
|
void selectTexture (int itemID)
|
||
|
{
|
||
|
if (itemID == 1000)
|
||
|
{
|
||
|
textureFileChooser = std::make_unique<FileChooser> ("Choose an image to open...",
|
||
|
File::getSpecialLocation (File::userPicturesDirectory),
|
||
|
"*.jpg;*.jpeg;*.png;*.gif");
|
||
|
auto chooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles;
|
||
|
|
||
|
textureFileChooser->launchAsync (chooserFlags, [this] (const FileChooser& fc)
|
||
|
{
|
||
|
if (fc.getResult() == File{})
|
||
|
return;
|
||
|
|
||
|
textures.add (new OpenGLUtils::TextureFromFile (fc.getResult()));
|
||
|
updateTexturesList();
|
||
|
|
||
|
textureBox.setSelectedId (textures.size());
|
||
|
});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (auto* t = textures[itemID - 1])
|
||
|
demo.setTexture (t);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void updateTexturesList()
|
||
|
{
|
||
|
textureBox.clear();
|
||
|
|
||
|
for (int i = 0; i < textures.size(); ++i)
|
||
|
textureBox.addItem (textures.getUnchecked (i)->name, i + 1);
|
||
|
|
||
|
textureBox.addSeparator();
|
||
|
textureBox.addItem ("Load from a file...", 1000);
|
||
|
}
|
||
|
|
||
|
void updateShader()
|
||
|
{
|
||
|
startTimer (10);
|
||
|
}
|
||
|
|
||
|
Label statusLabel;
|
||
|
|
||
|
private:
|
||
|
void sliderValueChanged (Slider*) override
|
||
|
{
|
||
|
const ScopedLock lock (demo.mutex);
|
||
|
|
||
|
demo.scale = (float) sizeSlider .getValue();
|
||
|
demo.rotationSpeed = (float) speedSlider.getValue();
|
||
|
}
|
||
|
|
||
|
enum { shaderLinkDelay = 500 };
|
||
|
|
||
|
void codeDocumentTextInserted (const String& /*newText*/, int /*insertIndex*/) override
|
||
|
{
|
||
|
startTimer (shaderLinkDelay);
|
||
|
}
|
||
|
|
||
|
void codeDocumentTextDeleted (int /*startIndex*/, int /*endIndex*/) override
|
||
|
{
|
||
|
startTimer (shaderLinkDelay);
|
||
|
}
|
||
|
|
||
|
void timerCallback() override
|
||
|
{
|
||
|
stopTimer();
|
||
|
demo.setShaderProgram (vertexDocument .getAllContent(),
|
||
|
fragmentDocument.getAllContent());
|
||
|
}
|
||
|
|
||
|
void lookAndFeelChanged() override
|
||
|
{
|
||
|
auto editorBackground = getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
|
||
|
Colours::white);
|
||
|
|
||
|
for (int i = tabbedComp.getNumTabs(); i >= 0; --i)
|
||
|
tabbedComp.setTabBackgroundColour (i, editorBackground);
|
||
|
|
||
|
vertexEditorComp .setColour (CodeEditorComponent::backgroundColourId, editorBackground);
|
||
|
fragmentEditorComp.setColour (CodeEditorComponent::backgroundColourId, editorBackground);
|
||
|
}
|
||
|
|
||
|
OpenGLDemo& demo;
|
||
|
|
||
|
Label speedLabel { {}, "Speed:" },
|
||
|
zoomLabel { {}, "Zoom:" };
|
||
|
|
||
|
CodeDocument vertexDocument, fragmentDocument;
|
||
|
CodeEditorComponent vertexEditorComp { vertexDocument, nullptr },
|
||
|
fragmentEditorComp { fragmentDocument, nullptr };
|
||
|
|
||
|
TabbedComponent tabbedComp { TabbedButtonBar::TabsAtLeft };
|
||
|
|
||
|
ComboBox presetBox, textureBox;
|
||
|
|
||
|
Label presetLabel { {}, "Shader Preset:" },
|
||
|
textureLabel { {}, "Texture:" };
|
||
|
|
||
|
Slider speedSlider, sizeSlider;
|
||
|
|
||
|
ToggleButton showBackgroundToggle { "Draw 2D graphics in background" };
|
||
|
|
||
|
OwnedArray<OpenGLUtils::DemoTexture> textures;
|
||
|
|
||
|
std::unique_ptr<FileChooser> textureFileChooser;
|
||
|
|
||
|
std::atomic<bool> buttonDown { false };
|
||
|
|
||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoControlsOverlay)
|
||
|
};
|
||
|
|
||
|
std::unique_ptr<DemoControlsOverlay> controlsOverlay;
|
||
|
|
||
|
float rotation = 0.0f;
|
||
|
|
||
|
std::unique_ptr<OpenGLShaderProgram> shader;
|
||
|
std::unique_ptr<OpenGLUtils::Shape> shape;
|
||
|
std::unique_ptr<OpenGLUtils::Attributes> attributes;
|
||
|
std::unique_ptr<OpenGLUtils::Uniforms> uniforms;
|
||
|
|
||
|
OpenGLTexture texture;
|
||
|
OpenGLUtils::DemoTexture* textureToUse = nullptr;
|
||
|
OpenGLUtils::DemoTexture* lastTexture = nullptr;
|
||
|
|
||
|
String newVertexShader, newFragmentShader, statusText;
|
||
|
|
||
|
struct BackgroundStar
|
||
|
{
|
||
|
SlowerBouncingNumber x, y, hue, angle;
|
||
|
};
|
||
|
|
||
|
BackgroundStar stars[3];
|
||
|
|
||
|
//==============================================================================
|
||
|
void updateShader()
|
||
|
{
|
||
|
if (newVertexShader.isNotEmpty() || newFragmentShader.isNotEmpty())
|
||
|
{
|
||
|
std::unique_ptr<OpenGLShaderProgram> newShader (new OpenGLShaderProgram (openGLContext));
|
||
|
|
||
|
if (newShader->addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (newVertexShader))
|
||
|
&& newShader->addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (newFragmentShader))
|
||
|
&& newShader->link())
|
||
|
{
|
||
|
shape .reset();
|
||
|
attributes.reset();
|
||
|
uniforms .reset();
|
||
|
|
||
|
shader.reset (newShader.release());
|
||
|
shader->use();
|
||
|
|
||
|
shape .reset (new OpenGLUtils::Shape ());
|
||
|
attributes.reset (new OpenGLUtils::Attributes (*shader));
|
||
|
uniforms .reset (new OpenGLUtils::Uniforms (*shader));
|
||
|
|
||
|
statusText = "GLSL: v" + String (OpenGLShaderProgram::getLanguageVersion(), 2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statusText = newShader->getLastError();
|
||
|
}
|
||
|
|
||
|
triggerAsyncUpdate();
|
||
|
|
||
|
newVertexShader = {};
|
||
|
newFragmentShader = {};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLDemo)
|
||
|
};
|