| #include "opengl/opengl_renderer.hpp" |
| #include "opengl/opengl_render_path.hpp" |
| #include "opengl/opengl_render_paint.hpp" |
| #include "opengl_shaders.cpp" |
| #include <cassert> |
| |
| using namespace rive; |
| |
| GLuint createAndCompileShader(GLuint type, const char* source); |
| |
| OpenGLRenderer::OpenGLRenderer() {} |
| OpenGLRenderer::~OpenGLRenderer() |
| { |
| glDeleteProgram(m_Program); |
| glDeleteShader(m_VertexShader); |
| glDeleteShader(m_FragmentShader); |
| glDeleteBuffers(1, &m_IndexBuffer); |
| glDeleteVertexArrays(1, &m_VertexArray); |
| } |
| |
| bool OpenGLRenderer::initialize(void* data) |
| { |
| assert(m_VertexShader == 0 && m_FragmentShader == 0 && m_Program == 0); |
| |
| m_VertexShader = |
| createAndCompileShader(GL_VERTEX_SHADER, vertexShaderSource); |
| if (m_VertexShader == 0) |
| { |
| return false; |
| } |
| |
| m_FragmentShader = |
| createAndCompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource); |
| if (m_FragmentShader == 0) |
| { |
| return false; |
| } |
| |
| m_Program = glCreateProgram(); |
| glAttachShader(m_Program, m_VertexShader); |
| glAttachShader(m_Program, m_FragmentShader); |
| glLinkProgram(m_Program); |
| GLint isLinked = 0; |
| glGetProgramiv(m_Program, GL_LINK_STATUS, (int*)&isLinked); |
| if (isLinked == GL_FALSE) |
| { |
| GLint maxLength = 0; |
| glGetProgramiv(m_Program, GL_INFO_LOG_LENGTH, &maxLength); |
| |
| std::vector<GLchar> infoLog(maxLength); |
| glGetProgramInfoLog(m_Program, maxLength, &maxLength, &infoLog[0]); |
| fprintf(stderr, "Failed to link program %s\n", &infoLog[0]); |
| return false; |
| } |
| |
| // Create index buffer which we'll grow and populate as necessary. |
| glGenBuffers(1, &m_IndexBuffer); |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBuffer); |
| |
| // Two triangles for bounds. |
| m_Indices.emplace_back(0); |
| m_Indices.emplace_back(1); |
| m_Indices.emplace_back(2); |
| m_Indices.emplace_back(2); |
| m_Indices.emplace_back(3); |
| m_Indices.emplace_back(0); |
| |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, |
| m_Indices.size() * sizeof(unsigned short), |
| &m_Indices[0], |
| GL_STATIC_DRAW); |
| |
| glGenVertexArrays(1, &m_VertexArray); |
| glBindVertexArray(m_VertexArray); |
| |
| glUseProgram(m_Program); |
| |
| m_ProjectionUniformIndex = glGetUniformLocation(m_Program, "projection"); |
| m_TransformUniformIndex = glGetUniformLocation(m_Program, "transform"); |
| |
| m_FillTypeUniformIndex = glGetUniformLocation(m_Program, "fillType"); |
| m_StopCountUniformIndex = glGetUniformLocation(m_Program, "count"); |
| m_StopColorsUniformIndex = glGetUniformLocation(m_Program, "colors"); |
| m_StopsUniformIndex = glGetUniformLocation(m_Program, "stops"); |
| m_ColorUniformIndex = glGetUniformLocation(m_Program, "color"); |
| m_GradientPositionUniformIndex = |
| glGetUniformLocation(m_Program, "position"); |
| m_ShapeTransformUniformIndex = |
| glGetUniformLocation(m_Program, "localTransform"); |
| |
| float projection[16] = {0.0f}; |
| orthographicProjection(projection, 0.0f, 800, 800, 0.0f, 0.0f, 1.0f); |
| modelViewProjection(projection); |
| |
| return true; |
| } |
| |
| void OpenGLRenderer::drawPath(RenderPath* path, RenderPaint* paint) |
| { |
| auto glPaint = static_cast<OpenGLRenderPaint*>(paint); |
| if (glPaint->style() == RenderPaintStyle::stroke || !glPaint->doesDraw()) |
| { |
| return; |
| } |
| |
| auto glPath = static_cast<OpenGLRenderPath*>(path); |
| |
| // Set up stencil buffer. |
| glStencilMask(0xFF); |
| glStencilFunc(GL_ALWAYS, 0x0, 0xFF); |
| glColorMask(false, false, false, false); |
| glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); |
| glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); |
| |
| glPath->stencil(this, transform()); |
| |
| glColorMask(true, true, true, true); |
| glStencilFunc(GL_NOTEQUAL, 0, 0xFF); |
| glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); |
| |
| glPaint->draw(this, transform(), glPath); |
| // glPath->cover(this, transform()); |
| } |
| |
| void OpenGLRenderer::clipPath(RenderPath* path) {} |
| |
| void OpenGLRenderer::startFrame() |
| { |
| glUseProgram(m_Program); |
| glEnableVertexAttribArray(0); |
| glUniformMatrix4fv( |
| m_ProjectionUniformIndex, 1, GL_FALSE, m_ModelViewProjection); |
| glEnable(GL_STENCIL_TEST); |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
| } |
| |
| void OpenGLRenderer::endFrame() {} |
| |
| RenderPaint* OpenGLRenderer::makeRenderPaint() |
| { |
| return new OpenGLRenderPaint(); |
| } |
| RenderPath* OpenGLRenderer::makeRenderPath() { return new OpenGLRenderPath(); } |
| |
| void OpenGLRenderer::updateIndexBuffer(std::size_t contourLength) |
| { |
| if (contourLength < 2) |
| { |
| return; |
| } |
| auto edgeCount = (m_Indices.size() - 6) / 3; |
| auto targetEdgeCount = contourLength - 2; |
| if (edgeCount < targetEdgeCount) |
| { |
| while (edgeCount < targetEdgeCount) |
| { |
| m_Indices.push_back(3); |
| m_Indices.push_back(edgeCount + 4); |
| m_Indices.push_back(edgeCount + 5); |
| edgeCount++; |
| } |
| |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBuffer); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, |
| m_Indices.size() * sizeof(unsigned short), |
| &m_Indices[0], |
| GL_STATIC_DRAW); |
| } |
| } |
| |
| GLuint createAndCompileShader(GLuint type, const char* source) |
| { |
| GLuint shader = glCreateShader(type); |
| glShaderSource(shader, 1, &source, nullptr); |
| glCompileShader(shader); |
| GLint isCompiled = 0; |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled); |
| if (isCompiled == GL_FALSE) |
| { |
| GLint maxLength = 0; |
| glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); |
| |
| std::vector<GLchar> infoLog(maxLength); |
| glGetShaderInfoLog(shader, maxLength, &maxLength, &infoLog[0]); |
| fprintf(stderr, "Failed to compile shader %s\n", &infoLog[0]); |
| glDeleteShader(shader); |
| |
| return 0; |
| } |
| |
| return shader; |
| } |