| //usage: halo is an instance of GradientComponent | |
| MainContentComponent::MainContentComponent() : | |
| halo(Colour{0xff00ffff}, //inner color | |
| Colour{0x000000ff}, //outer color | |
| false, //hallow | |
| 0.75f, //xgap | |
| 0.75f, //ygap | |
| 0.3f, //gradient | |
| 0.3f) //radius | |
| { | |
| addAndMakeVisible( halo ); | |
| setSize (600, 400); | |
| } | |
| //shader setup: | |
| #define NEW_LINE "\n" | |
| const std::map<OpenGLData::OpenGLUniforms, String> OpenGLData::OpenGLUniformsMap = | |
| { | |
| { OpenGLUniforms::Bounds, "bounds" }, | |
| { OpenGLUniforms::InnerColor, "innerColor" }, | |
| { OpenGLUniforms::OuterColor, "outerColor" }, | |
| { OpenGLUniforms::Hollow, "hollow" }, | |
| { OpenGLUniforms::RoundedBoxWidth, "roundedBoxWidth" }, | |
| { OpenGLUniforms::RoundedBoxHeight, "roundedBoxHeight" }, | |
| { OpenGLUniforms::Gradient, "gradient" }, | |
| { OpenGLUniforms::Radius, "radius" } | |
| }; | |
| String GradientComponent::makeFragmentShaderHeader() | |
| { | |
| return | |
| "uniform vec4 " + OpenGLData::OpenGLUniformsMap.at(OpenGLData::OpenGLUniforms::Bounds) + ";" NEW_LINE | |
| "uniform vec4 " + OpenGLData::OpenGLUniformsMap.at(OpenGLData::OpenGLUniforms::InnerColor) + ";" NEW_LINE | |
| "uniform vec4 " + OpenGLData::OpenGLUniformsMap.at(OpenGLData::OpenGLUniforms::OuterColor) + ";" NEW_LINE | |
| "uniform bool " + OpenGLData::OpenGLUniformsMap.at(OpenGLData::OpenGLUniforms::Hollow) + ";" NEW_LINE | |
| "uniform float " + OpenGLData::OpenGLUniformsMap.at(OpenGLData::OpenGLUniforms::RoundedBoxWidth) + ";" NEW_LINE | |
| "uniform float " + OpenGLData::OpenGLUniformsMap.at(OpenGLData::OpenGLUniforms::RoundedBoxHeight) + ";" NEW_LINE | |
| "uniform float " + OpenGLData::OpenGLUniformsMap.at(OpenGLData::OpenGLUniforms::Gradient) + ";" NEW_LINE | |
| "uniform float " + OpenGLData::OpenGLUniformsMap.at(OpenGLData::OpenGLUniforms::Radius) + ";" NEW_LINE | |
| ; | |
| } | |
| void GradientComponent::newOpenGLContextCreated() | |
| { | |
| String vertexShader = | |
| "attribute vec4 position;" NEW_LINE | |
| "void main(void)" NEW_LINE | |
| "{" NEW_LINE | |
| " gl_Position = position;// * vec4(2.0, 2.0, 0.0, 1.0);" NEW_LINE | |
| "}" NEW_LINE; | |
| String fragmentShader = makeFragmentShaderHeader() + | |
| "float roundedBox(vec2 point, vec2 rectBounds, float radius)" NEW_LINE | |
| "{" NEW_LINE | |
| //this returns the distance | |
| " return length(max (abs(point) - rectBounds + radius, 0.0)) - radius;" NEW_LINE | |
| "}" NEW_LINE | |
| "float hsluv_fromLinear(float c) {" NEW_LINE | |
| " return c <= 0.0031308 ? 12.92 * c : 1.055 * pow(c, 1.0 / 2.4) - 0.055;" NEW_LINE | |
| "}" NEW_LINE | |
| "void main(void)" NEW_LINE | |
| "{" NEW_LINE | |
| // Normalized pixel coordinates (from 0 to 1) | |
| " vec2 normalizedFragCoord = gl_FragCoord.xy / bounds.xy;" NEW_LINE | |
| //" float ratio = bounds.y / bounds.x;" NEW_LINE | |
| //change normalized pixel coord range from [0,1] to [-1,1] | |
| " vec2 p = normalizedFragCoord * 2.0 - 1.0;" NEW_LINE | |
| //remove y-axis scaling from normalizing | |
| //" p.y *= ratio;" NEW_LINE | |
| " vec4 col;" NEW_LINE | |
| " float d = roundedBox(p, vec2(roundedBoxWidth, roundedBoxHeight), radius);" NEW_LINE//distance from rounded rect bounds | |
| " if( hollow == false )" NEW_LINE | |
| " {" NEW_LINE | |
| " col = sqrt(mix(innerColor*innerColor, outerColor*outerColor, smoothstep(0., gradient, d ) ));" NEW_LINE | |
| " }" NEW_LINE | |
| " else" NEW_LINE | |
| " {" NEW_LINE | |
| " if( abs(d) < gradient )" NEW_LINE | |
| " {" NEW_LINE | |
| " if( d == 0. )" NEW_LINE | |
| " {" NEW_LINE | |
| " col = innerColor; " NEW_LINE //purple | |
| " }" NEW_LINE | |
| " else" NEW_LINE | |
| " {" NEW_LINE | |
| " col = sqrt(mix(innerColor*innerColor, outerColor*outerColor, smoothstep(0., gradient, abs(d)) ));" NEW_LINE | |
| " }" NEW_LINE | |
| " }" NEW_LINE | |
| " else" NEW_LINE | |
| " {" NEW_LINE | |
| " col = outerColor; //yellow" NEW_LINE | |
| " }" NEW_LINE | |
| " }" NEW_LINE | |
| // "col.x = hsluv_fromLinear(col.x);" | |
| // "col.y = hsluv_fromLinear(col.y);" | |
| // "col.z = hsluv_fromLinear(col.z);" | |
| // "col.w = hsluv_fromLinear(col.w);" | |
| " gl_FragColor = col;" NEW_LINE | |
| "}"; | |
| shader = new OpenGLShaderProgram(openGLContext); | |
| //make sure the shader is valid and compilable | |
| if(shader->addVertexShader(vertexShader) && | |
| shader->addFragmentShader(fragmentShader) && | |
| shader->link()) | |
| { | |
| //find our attribute in the context | |
| if( openGLContext.extensions.glGetAttribLocation(shader->getProgramID(), OpenGLData::OpenGLAttributesMap.at(OpenGLData::OpenGLAttributes::Position).toRawUTF8()) < 0 ) | |
| { | |
| jassertfalse; //attribute not found | |
| } | |
| positionAttribute = new OpenGLShaderProgram::Attribute(*shader, OpenGLData::OpenGLAttributesMap.at(OpenGLData::OpenGLAttributes::Position).toRawUTF8() ); | |
| uniforms = new Uniforms(*shader); | |
| jassert( uniforms->bounds != nullptr ); | |
| jassert( uniforms->innerColor != nullptr ); | |
| jassert( uniforms->outerColor != nullptr ); | |
| jassert( uniforms->hollow != nullptr ); | |
| jassert( uniforms->roundedBoxWidth != nullptr ); | |
| jassert( uniforms->roundedBoxHeight != nullptr ); | |
| jassert( uniforms->gradient != nullptr ); | |
| openGLContext.extensions.glGenBuffers (1, &vertexBuffer); //16. generate a single buffer, store the index in vertexBuffer | |
| openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer); //17. bind the vertexBuffer object as a GL_ARRAY_BUFFER type | |
| Array<Mesh> meshes = createMeshes(); | |
| Array<float> vertices; | |
| for( auto& mesh : meshes ) | |
| { | |
| for( auto& vertex : mesh.vertices ) | |
| { | |
| vertices.add(vertex.x); | |
| vertices.add(vertex.y); | |
| } | |
| numMeshes += 1; | |
| } | |
| vertexBuffer = createVBO(vertices.getRawDataPointer(), | |
| sizeof(Vertex) * vertices.size()); | |
| } | |
| } | |
| //thanks http://www.falloutsoftware.com/tutorials/gl/gl3.htm#vertex_array !!!! | |
| GLuint GradientComponent::createVBO(GLfloat *vertices, int size) | |
| { | |
| GLuint newBuffer; | |
| glGenBuffersARB(1, &newBuffer); | |
| glBindBufferARB(GL_ARRAY_BUFFER_ARB, newBuffer); | |
| glBufferDataARB(GL_ARRAY_BUFFER_ARB, size, vertices, GL_STATIC_DRAW_ARB); | |
| return newBuffer; | |
| } | |
| //render callback: | |
| void GradientComponent::renderOpenGL() | |
| { | |
| jassert (OpenGLHelpers::isContextActive()); //can't draw without a context! | |
| OpenGLHelpers::clear(Colours::white); //white background | |
| //what do all of these do? | |
| glEnable (GL_DEPTH_TEST); | |
| glDepthFunc (GL_LESS); | |
| glEnable (GL_BLEND); | |
| glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
| glEnable (GL_TEXTURE_2D); | |
| // Draw the gradient for the background fill | |
| glMatrixMode(GL_PROJECTION); | |
| glLoadIdentity(); | |
| glMatrixMode(GL_MODELVIEW); | |
| glLoadIdentity(); | |
| shader->use(); //enable use of our shader | |
| openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer); //48. bind the vertexBuffer to the GL_ARRAY_BUFFER | |
| if (positionAttribute != nullptr) | |
| { | |
| openGLContext.extensions.glVertexAttribPointer(positionAttribute->attributeID, | |
| 2, | |
| GL_FLOAT, | |
| GL_FALSE, | |
| sizeof(Vertex), | |
| 0); | |
| openGLContext.extensions.glEnableVertexAttribArray (positionAttribute->attributeID); //52. enable reading from the attribute | |
| } | |
| //update bounds uniform | |
| updateShaderUniforms(); | |
| glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer); | |
| // Activate array-based data | |
| glEnableClientState(GL_VERTEX_ARRAY); | |
| // Stride of 2, floats | |
| glVertexPointer(2, GL_FLOAT, 0, 0); | |
| // Draw triangles | |
| glDrawArrays(GL_TRIANGLES, 0, numMeshes * 3 * sizeof(Vertex)); | |
| // Switch off vertex array data | |
| glDisableClientState(GL_VERTEX_ARRAY); | |
| // Bind with 0 to switch back to default pointer operation | |
| glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); | |
| if (positionAttribute != nullptr) | |
| openGLContext.extensions.glDisableVertexAttribArray (positionAttribute->attributeID); | |
| } | |
| void GradientComponent::updateShaderUniforms() | |
| { | |
| auto desktopScale = (float) openGLContext.getRenderingScale(); | |
| auto r = getLocalBounds().toFloat().operator*(desktopScale); //scale up window size | |
| auto& oc = outerColor; | |
| Colour ic = oc.interpolatedWith(innerColor, crossFade); | |
| if( uniforms->bounds != nullptr ) uniforms->bounds->set(r.getWidth(), r.getHeight(), 0.f, 0.f); | |
| else jassertfalse; | |
| if( uniforms->innerColor != nullptr ) uniforms->innerColor->set(ic.getFloatRed(), ic.getFloatGreen(), ic.getFloatBlue(), ic.getFloatAlpha()); | |
| else jassertfalse; | |
| if( uniforms->outerColor != nullptr ) uniforms->outerColor->set(oc.getFloatRed(), oc.getFloatGreen(), oc.getFloatBlue(), oc.getFloatAlpha()); | |
| else jassertfalse; | |
| if( uniforms->hollow != nullptr ) uniforms->hollow->set(hollow); | |
| else jassertfalse; | |
| if( uniforms->roundedBoxWidth != nullptr ) uniforms->roundedBoxWidth->set(roundedBoxWidth); | |
| else jassertfalse; | |
| if( uniforms->roundedBoxHeight != nullptr ) uniforms->roundedBoxHeight->set(roundedBoxHeight); | |
| else jassertfalse; | |
| if( uniforms->gradient != nullptr ) uniforms->gradient->set(gradient); | |
| else jassertfalse; | |
| if( uniforms->radius != nullptr) uniforms->radius->set(radius); | |
| else jassertfalse; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment