diff --git a/graphics/graphics.pro b/graphics/graphics.pro index ec3587389a..492798e8c5 100644 --- a/graphics/graphics.pro +++ b/graphics/graphics.pro @@ -23,7 +23,10 @@ SOURCES += \ opengl/vertex.cpp \ opengl/clipper.cpp \ opengl/geometry_renderer.cpp \ + opengl/shader.cpp \ + opengl/program.cpp \ opengl/defines_conv.cpp \ + opengl/program_manager.cpp \ opengl/gl_render_context.cpp \ opengl/storage.cpp \ blitter.cpp \ @@ -81,7 +84,10 @@ HEADERS += \ opengl/geometry_renderer.hpp \ opengl/data_traits.hpp \ opengl/storage.hpp \ + opengl/shader.hpp \ + opengl/program.hpp \ opengl/defines_conv.hpp \ + opengl/program_manager.hpp \ blitter.hpp \ resource_manager.hpp \ skin.hpp \ diff --git a/graphics/opengl/program.cpp b/graphics/opengl/program.cpp new file mode 100644 index 0000000000..6e33b968e8 --- /dev/null +++ b/graphics/opengl/program.cpp @@ -0,0 +1,199 @@ +#include "program.hpp" +#include "shader.hpp" +#include "buffer_object.hpp" +#include "defines_conv.hpp" +#include "../../base/thread.hpp" + +#include "../../std/bind.hpp" +#include "../vertex_decl.hpp" + +namespace graphics +{ + namespace gl + { + Program::Program(shared_ptr const & vxShader, + shared_ptr const & frgShader) + { + m_handle = glCreateProgramFn(); + OGLCHECKAFTER; + + if (!m_handle) + throw Exception("CreateProgram error", "could not create Program!"); + + OGLCHECK(glAttachShaderFn(m_handle, vxShader->id())); + OGLCHECK(glAttachShaderFn(m_handle, frgShader->id())); + + OGLCHECK(glLinkProgramFn(m_handle)); + + int linkStatus = GL_FALSE; + OGLCHECK(glGetProgramivFn(m_handle, GL_LINK_STATUS, &linkStatus)); + + if (linkStatus != GL_TRUE) + { + int bufLength = 0; + OGLCHECK(glGetProgramivFn(m_handle, GL_INFO_LOG_LENGTH, &bufLength)); + if (bufLength) + { + vector v; + v.resize(bufLength); + glGetProgramInfoLogFn(m_handle, bufLength, NULL, &v[0]); + + throw LinkException("Could not link program: ", &v[0]); + } + + throw LinkException("Could not link program: ", "Unknown link error"); + } + } + + Program::~Program() + { + OGLCHECK(glDeleteProgramFn(m_handle)); + } + + GLuint Program::getParam(char const * name) + { + GLuint res = glGetUniformLocationFn(m_handle, name); + OGLCHECKAFTER; + return res; + } + + GLuint Program::getAttribute(char const * name) + { + GLuint res = glGetAttribLocationFn(m_handle, name); + OGLCHECKAFTER; + return res; + } + + void setParamImpl(GLuint prgID, char const * name, function fn) + { + GLuint res = glGetUniformLocationFn(prgID, name); + OGLCHECKAFTER; + OGLCHECK(fn(res)); + } + + void Program::setParam(char const * name, float v0) + { + function fn = bind(&glUniform1f, _1, v0); + m_uniforms[name] = bind(&setParamImpl, m_handle, name, fn); + } + + void Program::setParam(char const * name, float v0, float v1) + { + function fn = bind(&glUniform2f, _1, v0, v1); + m_uniforms[name] = bind(&setParamImpl, m_handle, name, fn); + } + + void Program::setParam(char const * name, float v0, float v1, float v2) + { + function fn = bind(&glUniform3f, _1, v0, v1, v2); + m_uniforms[name] = bind(&setParamImpl, m_handle, name, fn); + } + + void Program::setParam(char const * name, float v0, float v1, float v2, float v3) + { + function fn = bind(&glUniform4f, _1, v0, v1, v2, v3); + m_uniforms[name] = bind(&setParamImpl, m_handle, name, fn); + } + + void Program::setParam(char const * name, int v0) + { + function fn = bind(&glUniform1i, _1, v0); + m_uniforms[name] = bind(&setParamImpl, m_handle, name, fn); + } + + void Program::setParam(char const * name, int v0, int v1) + { + function fn = bind(&glUniform2i, _1, v0, v1); + m_uniforms[name] = bind(&setParamImpl, m_handle, name, fn); + } + + void Program::setParam(char const * name, int v0, int v1, int v2) + { + function fn = bind(&glUniform3i, _1, v0, v1, v2); + m_uniforms[name] = bind(&setParamImpl, m_handle, name, fn); + } + + void Program::setParam(char const * name, int v0, int v1, int v2, int v3) + { + function fn = bind(&glUniform4i, _1, v0, v1, v2, v3); + m_uniforms[name] = bind(&setParamImpl, m_handle, name, fn); + } + + void Program::setParam(char const * name, math::Matrix const & m) + { + function fn = bind(&glUniformMatrix2fv, _1, 1, 0, &m(0, 0)); + m_uniforms[name] = bind(&setParamImpl, m_handle, name, fn); + } + + void Program::setParam(char const * name, math::Matrix const & m) + { + function fn = bind(&glUniformMatrix3fv, _1, 1, 0, &m(0, 0)); + m_uniforms[name] = bind(&setParamImpl, m_handle, name, fn); + } + + void Program::setParam(char const * name, math::Matrix const & m) + { + function fn = bind(&glUniformMatrix4fv, _1, 1, 0, &m(0, 0)); + m_uniforms[name] = bind(&setParamImpl, m_handle, name, fn); + } + + void enableAndSetVertexAttrib(GLuint id, GLint size, GLenum type, GLboolean normalized, GLsizei stride, void * ptr) + { + OGLCHECK(glEnableVertexAttribArray(id)); + OGLCHECK(glVertexAttribPointer(id, size, type, normalized, stride, ptr)); + } + + void Program::setAttribute(GLuint id, GLint size, GLenum type, GLboolean normalized, GLsizei stride, void *ptr) + { + function fn = bind(&enableAndSetVertexAttrib, id, size, type, normalized, stride, ptr); + m_attributes[id] = fn; + } + + void Program::setVertexDecl(VertexDecl const * decl) + { + for (size_t i = 0; i < decl->size(); ++i) + { + VertexAttrib const * va = decl->getAttr(i); + GLuint attrID = getAttribute(va->m_name.c_str()); + GLenum glType; + convert(va->m_elemType, glType); + setAttribute(attrID, + va->m_elemCount, + glType, + false, + va->m_stride, + (void*)((unsigned char*)m_storage.m_vertices->glPtr() + va->m_offset)); + } + } + + void Program::setStorage(Storage const & storage) + { + m_storage = storage; + } + + void Program::makeCurrent() + { + OGLCHECK(glUseProgramFn(m_handle)); + + m_storage.m_vertices->makeCurrent(); + + /// setting all attributes streams; + for (TAttributes::const_iterator it = m_attributes.begin(); + it != m_attributes.end(); + ++it) + { + it->second(); + } + + m_storage.m_indices->makeCurrent(); + + /// setting all uniforms + for (TUniforms::const_iterator it = m_uniforms.begin(); + it != m_uniforms.end(); + ++it) + it->second(); + } + } +} + + diff --git a/graphics/opengl/program.hpp b/graphics/opengl/program.hpp new file mode 100644 index 0000000000..b6da9a9519 --- /dev/null +++ b/graphics/opengl/program.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include "opengl.hpp" +#include "storage.hpp" + +#include "../../base/matrix.hpp" +#include "../../base/exception.hpp" + +#include "../../std/shared_ptr.hpp" +#include "../../std/function.hpp" +#include "../../std/string.hpp" + +namespace graphics +{ + class VertexDecl; + namespace gl + { + class Shader; + + class Program + { + private: + + GLuint m_handle; + + typedef map > TUniforms; + TUniforms m_uniforms; + + typedef map > TAttributes; + TAttributes m_attributes; + + Storage m_storage; + + public: + + DECLARE_EXCEPTION(Exception, RootException); + DECLARE_EXCEPTION(LinkException, Exception); + + Program(shared_ptr const & vxShader, + shared_ptr const & frgShader); + + ~Program(); + + unsigned getParam(char const * name); + + void setParam(char const * name, float v0); + void setParam(char const * name, float v0, float v1); + void setParam(char const * name, float v0, float v1, float v2); + void setParam(char const * name, float v0, float v1, float v2, float v3); + + void setParam(char const * name, int v0); + void setParam(char const * name, int v0, int v1); + void setParam(char const * name, int v0, int v1, int v2); + void setParam(char const * name, int v0, int v1, int v2, int v3); + + void setParam(char const * name, math::Matrix const & m); + void setParam(char const * name, math::Matrix const & m); + void setParam(char const * name, math::Matrix const & m); + + GLuint getAttribute(char const * name); + void setAttribute(GLuint id, GLint size, GLenum type, GLboolean normalized, GLsizei stride, void * ptr); + + void setVertexDecl(VertexDecl const * decl); + + void setStorage(Storage const & storage); + + void makeCurrent(); + }; + } +} diff --git a/graphics/opengl/program_manager.cpp b/graphics/opengl/program_manager.cpp new file mode 100644 index 0000000000..d3f89d1689 --- /dev/null +++ b/graphics/opengl/program_manager.cpp @@ -0,0 +1,95 @@ +#include "program_manager.hpp" +#include "opengl.hpp" + +namespace graphics +{ + namespace gl + { +#if defined(OMIM_GL_ES) + #define PRECISION "lowp" +#else + #define PRECISION "" +#endif + + ProgramManager::ProgramManager() + { + /// Vertex Shader Source + static const char vxSrc[] = + "attribute vec4 Position;\n" + "attribute vec2 Normal;\n" + "attribute vec2 TexCoordIn;\n" + "uniform mat4 ProjM;\n" + "uniform mat4 ModelViewM;\n" + "varying vec2 TexCoordOut;\n" + "void main(void) {\n" + " gl_Position = (vec4(Normal, 0.0, 0.0) + Position * ModelViewM) * ProjM;\n" + " TexCoordOut = TexCoordIn;\n" + "}\n"; + + m_vxShaders["basic"].reset(new Shader(vxSrc, EVertexShader)); + + /// Sharp Vertex Shader Source + + static const char sharpVxSrc[] = + "attribute vec4 Position;\n" + "attribute vec2 Normal;\n" + "attribute vec2 TexCoordIn;\n" + "uniform mat4 ProjM;\n" + "uniform mat4 ModelViewM;\n" + "varying vec2 TexCoordOut;\n" + "void main(void) {\n" + " gl_Position = floor(vec4(Normal, 0.0, 0.0) + Position * ModelViewM) * ProjM;\n" + " TexCoordOut = TexCoordIn;\n" + "}\n"; + + m_vxShaders["sharp"].reset(new Shader(sharpVxSrc, EVertexShader)); + + /// Fragment Shader with alphaTest + + static const char alphaTestFrgSrc [] = + "uniform sampler2D Texture;\n" + "varying " PRECISION " vec2 TexCoordOut;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(Texture, TexCoordOut);\n" + " if (gl_FragColor.a == 0.0)\n" + " discard;\n" + "}\n"; + + m_frgShaders["alphatest"].reset(new Shader(alphaTestFrgSrc, EFragmentShader)); + + /// Fragment shader without alphaTest + + static const char noAlphaTestFrgSrc[] = + "uniform sampler2D Texture;\n" + "varying " PRECISION " vec2 TexCoordOut;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(Texture, TexCoordOut);\n" + "}\n"; + m_frgShaders["noalphatest"].reset(new Shader(noAlphaTestFrgSrc, EFragmentShader)); + + getProgram("basic", "alphatest"); + getProgram("basic", "noalphatest"); + + getProgram("sharp", "alphatest"); + getProgram("sharp", "noalphatest"); + } + + shared_ptr const ProgramManager::getProgram(char const * vxName, + char const * frgName) + { + string prgName(string(vxName) + ":" + frgName); + + map >::const_iterator it = m_programs.find(prgName); + if (it != m_programs.end()) + return it->second; + + shared_ptr program(new Program(m_vxShaders[vxName], m_frgShaders[frgName])); + + m_programs[prgName] = program; + + LOG(LINFO, (this, ", ", vxName, ", ", frgName, ", ", program)); + + return program; + } + } +} diff --git a/graphics/opengl/program_manager.hpp b/graphics/opengl/program_manager.hpp new file mode 100644 index 0000000000..29ec720f3e --- /dev/null +++ b/graphics/opengl/program_manager.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "program.hpp" +#include "shader.hpp" + +#include "../../std/shared_ptr.hpp" +#include "../../std/map.hpp" +#include "../../std/string.hpp" + +namespace graphics +{ + namespace gl + { + class ProgramManager + { + private: + + map > m_vxShaders; + map > m_frgShaders; + map > m_programs; + + public: + + ProgramManager(); + + shared_ptr const getProgram(char const * vxName, + char const * frgName); + }; + } +} diff --git a/graphics/opengl/shader.cpp b/graphics/opengl/shader.cpp new file mode 100644 index 0000000000..8dbb82a957 --- /dev/null +++ b/graphics/opengl/shader.cpp @@ -0,0 +1,48 @@ +#include "shader.hpp" + +#include "defines_conv.hpp" +#include "opengl.hpp" + +namespace graphics +{ + namespace gl + { + Shader::Shader(const char *src, EShaderType type) + { + GLenum glType; + convert(type, glType); + + m_handle = glCreateShaderFn(glType); + OGLCHECKAFTER; + + if (!m_handle) + throw Exception("CreateShader error", "could not create Shader!"); + + int len = strlen(src); + + OGLCHECK(glShaderSource(m_handle, 1, &src, &len)); + + OGLCHECK(glCompileShaderFn(m_handle)); + + GLint compileRes; + OGLCHECK(glGetShaderivFn(m_handle, GL_COMPILE_STATUS, &compileRes)); + + if (compileRes == GL_FALSE) + { + GLchar msg[256]; + OGLCHECK(glGetShaderInfoLogFn(m_handle, sizeof(msg), 0, msg)); + throw CompileException("Couldn't compile shader: ", msg); + } + } + + Shader::~Shader() + { + OGLCHECK(glDeleteShaderFn(m_handle)); + } + + GLuint Shader::id() const + { + return m_handle; + } + } +} diff --git a/graphics/opengl/shader.hpp b/graphics/opengl/shader.hpp new file mode 100644 index 0000000000..6570746e22 --- /dev/null +++ b/graphics/opengl/shader.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "../../base/exception.hpp" + +#include "../defines.hpp" + +#include "opengl.hpp" + +namespace graphics +{ + namespace gl + { + class Shader + { + private: + GLuint m_handle; + public: + + DECLARE_EXCEPTION(Exception, RootException); + DECLARE_EXCEPTION(CompileException, Exception); + + /// Constructor. + Shader(char const * src, EShaderType type); + /// Destructor. + ~Shader(); + /// Handle to the program. + GLuint id() const; + }; + } +}