Created
August 3, 2018 10:47
-
-
Save bhelyer/950258aa8a8297ef2bd5ac0a1b285404 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module rr.ui.gl; | |
| import core = core.exception; | |
| import io = watt.io; | |
| import file = watt.io.file; | |
| import conv = watt.conv; | |
| import amp.sdl2; | |
| import amp.gl; | |
| import amp.gl.loader; | |
| import specs = rr.specs; | |
| // This should be called with an active context. | |
| fn initialise() { | |
| gladLoadGL(SDL_GL_GetProcAddress); | |
| glGetError(); | |
| loadFragmentShader("simple.f.glsl"); | |
| gCoord2d = gShaderProgram.bindAttribute("coord2d"); | |
| gTexCoord = gShaderProgram.bindAttribute("texcoord"); | |
| gMyTexture = gShaderProgram.bindUniform("mytexture"); | |
| vertices: GLfloat[] = [ | |
| -1.0f, -1.0f, | |
| 1.0f, -1.0f, | |
| 1.0f, 1.0f, | |
| -1.0f, 1.0f, | |
| ]; | |
| texcoords: GLfloat[] = [ | |
| 0.0f, 0.0f, | |
| 1.0f, 0.0f, | |
| 1.0f, 1.0f, | |
| 0.0f, 1.0f, | |
| ]; | |
| gVertexBuffer = Buffer.createVBO(vertices); | |
| gTexCoordsBuffer = Buffer.createVBO(texcoords); | |
| gTexture = Texture.create(); | |
| glEnable(GL_BLEND); | |
| checkGlError("glEnable"); | |
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
| checkGlError("glBlendFunc"); | |
| } | |
| fn loadFragmentShader(name: string) { | |
| gFragmentShaderFilename = name; | |
| gShaderProgram.cleanup(); | |
| vshader := Shader.create(GL_VERTEX_SHADER, cast(string)file.read("res/simple.v.glsl")); | |
| fpath := new "res/${name}"; | |
| fshader := Shader.create(GL_FRAGMENT_SHADER, cast(string)file.read(fpath)); | |
| gShaderProgram = Program.create(vshader, fshader); | |
| } | |
| fn nextFragmentShader() { | |
| shaders := fragmentShaders(); | |
| assert(shaders.length > 0); | |
| currenti: size_t; | |
| foreach (i, shader; shaders) { | |
| if (shader == gFragmentShaderFilename) { | |
| currenti = i; | |
| } | |
| } | |
| currenti++; | |
| if (currenti >= shaders.length) { | |
| currenti = 0; | |
| } | |
| loadFragmentShader(shaders[currenti]); | |
| } | |
| fn cleanup() { | |
| gTexture.cleanup(); | |
| gShaderProgram.cleanup(); | |
| gVertexBuffer.cleanup(); | |
| } | |
| fn loadPixels(pixels: u8*) { | |
| gShaderProgram.use(); | |
| gTexture.bind2D(); | |
| gTexture.texImage(pixels); | |
| gVertexBuffer.enable(gCoord2d); | |
| gTexCoordsBuffer.enable(gTexCoord); | |
| glDrawArrays(GL_QUADS, 0, 4); | |
| checkGlError("glDrawArrays"); | |
| gTexCoordsBuffer.disable(gTexCoord); | |
| gVertexBuffer.disable(gCoord2d); | |
| } | |
| private: | |
| global gFragmentShaderFilename: string; | |
| global gShaderProgram: Program; | |
| global gCoord2d, gTexCoord: GLint; | |
| global gMyTexture: GLint; | |
| global gTexture: Texture; | |
| global gVertexBuffer: Buffer; | |
| global gTexCoordsBuffer: Buffer; | |
| // Return the filenames (no leading directory information etc -- just the filename). | |
| fn fragmentShaders() string[] { | |
| sourceFiles: string[]; | |
| fn addFile(s: string) file.SearchStatus { sourceFiles ~= s; return file.SearchStatus.Continue; } | |
| file.searchDir("res", "*.f.glsl", addFile); | |
| return sourceFiles; | |
| } | |
| struct Texture { | |
| id: GLuint; | |
| global fn create() Texture { | |
| tex: Texture; | |
| glGenTextures(1, &tex.id); | |
| checkGlError("glGenTextures"); | |
| glBindTexture(GL_TEXTURE_2D, tex.id); | |
| checkGlError("glBindTexture"); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| checkGlError("glTexParameteri"); | |
| return tex; | |
| } | |
| fn bind2D() { | |
| glActiveTexture(GL_TEXTURE0); | |
| checkGlError("glActiveTexture"); | |
| glUniform1i(gMyTexture, 0); | |
| checkGlError("glUniform1i"); | |
| glBindTexture(GL_TEXTURE_2D, id); | |
| checkGlError("glBindTexture"); | |
| } | |
| fn texImage(pixels: u8*) { | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, specs.ScreenWidth, specs.ScreenHeight, 0, | |
| GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, cast(GLvoid*)pixels); | |
| checkGlError("glTexImage2D"); | |
| } | |
| fn cleanup() { | |
| glDeleteTextures(1, &id); | |
| checkGlError("glDeleteTextures"); | |
| } | |
| } | |
| struct Shader { | |
| id: GLuint; | |
| global fn create(type: GLenum, source: string) Shader { | |
| s: Shader; | |
| s.id = glCreateShader(type); | |
| checkGlError("glCreateShader"); | |
| src := conv.toStringz(source); | |
| glShaderSource(s.id, 1, &src, null); | |
| checkGlError("glShaderSource"); | |
| glCompileShader(s.id); | |
| checkGlError("glCompileShader"); | |
| status: GLint; | |
| glGetShaderiv(s.id, GL_COMPILE_STATUS, &status); | |
| checkGlError("glGetShaderiv"); | |
| infoLog := s.getInfoLog(); | |
| if (infoLog !is null) { | |
| io.writeln(infoLog); | |
| } | |
| if (status != GL_TRUE) { | |
| throw new core.Exception("Error compiling shader."); | |
| } | |
| return s; | |
| } | |
| fn cleanup() { | |
| glDeleteShader(id); | |
| checkGlError("glDeleteShader"); | |
| } | |
| fn getInfoLog() string { | |
| ilength: GLint; | |
| glGetShaderiv(id, GL_INFO_LOG_LENGTH, &ilength); | |
| checkGlError("glGetShaderiv"); | |
| if (ilength <= 0) { | |
| return null; | |
| } | |
| length := cast(GLsizei)ilength; | |
| buf := new GLchar[](length); | |
| buflen: GLsizei; | |
| glGetShaderInfoLog(id, length, &buflen, buf.ptr); | |
| checkGlError("glGetShaderInfoLog"); | |
| return cast(string)buf[0 .. buflen]; | |
| } | |
| } | |
| struct Program { | |
| id: GLuint; | |
| shaders: Shader[]; | |
| global fn create(shaders: Shader[]...) Program { | |
| p: Program; | |
| p.id = glCreateProgram(); | |
| checkGlError("glCreateProgram"); | |
| p.shaders = shaders; | |
| foreach (s; shaders) { | |
| glAttachShader(p.id, s.id); | |
| checkGlError("glAttachShader"); | |
| } | |
| glLinkProgram(p.id); | |
| checkGlError("glLinkProgram"); | |
| linkOk: GLint; | |
| glGetProgramiv(p.id, GL_LINK_STATUS, &linkOk); | |
| checkGlError("glGetProgramiv"); | |
| if (linkOk != GL_TRUE) { | |
| throw new core.Exception("Error linking program."); | |
| } | |
| return p; | |
| } | |
| fn cleanup() { | |
| foreach (shader; shaders) { | |
| glDetachShader(id, shader.id); | |
| checkGlError("glDetachShader"); | |
| shader.cleanup(); | |
| } | |
| glUseProgram(0); | |
| checkGlError("glUseProgram"); | |
| glDeleteProgram(id); | |
| checkGlError("glDeleteProgram"); | |
| } | |
| fn bindAttribute(name: string) GLint { | |
| val := glGetAttribLocation(id, conv.toStringz(name)); | |
| checkGlError("glGetAttribLocation"); | |
| if (val == -1) { | |
| throw new core.Exception(new "could not bind attribute '${name}'"); | |
| } | |
| return val; | |
| } | |
| fn bindUniform(name: string) GLint { | |
| val := glGetUniformLocation(id, conv.toStringz(name)); | |
| checkGlError("glGetUniformLocation"); | |
| if (val == -1) { | |
| throw new core.Exception(new "could not bind uniform '${name}'"); | |
| } | |
| return val; | |
| } | |
| fn use() { | |
| glUseProgram(id); | |
| checkGlError("glUseProgram"); | |
| } | |
| } | |
| struct Buffer { | |
| id: GLuint; | |
| global fn createVBO(vertices: GLfloat[]) Buffer { | |
| b: Buffer; | |
| glGenBuffers(1, &b.id); | |
| checkGlError("glGenBuffers"); | |
| b.bind(); | |
| b.bufferData(vertices); | |
| return b; | |
| } | |
| fn cleanup() { | |
| glDeleteBuffers(1, &id); | |
| checkGlError("glDeleteBuffers"); | |
| } | |
| fn bind() { | |
| glBindBuffer(GL_ARRAY_BUFFER, id); | |
| checkGlError("glBindBuffer"); | |
| } | |
| fn enable(attr: GLint) { | |
| glEnableVertexAttribArray(cast(GLuint)attr); | |
| checkGlError("glEnableVertexAttribArray"); | |
| bind(); | |
| glVertexAttribPointer(cast(GLuint)attr, 2, GL_FLOAT, GL_FALSE, 0, null); | |
| checkGlError("glVertexAttribPointer"); | |
| } | |
| fn disable(attr: GLint) { | |
| glDisableVertexAttribArray(cast(GLuint)attr); | |
| checkGlError("glDisableVertexAttribArray"); | |
| } | |
| fn bufferData(vertices: GLfloat[]) { | |
| glBufferData(GL_ARRAY_BUFFER, cast(GLsizeiptr)(vertices.length * typeid(GLfloat).size), | |
| cast(void*)vertices.ptr, GL_STATIC_DRAW); | |
| checkGlError("glBufferData"); | |
| } | |
| } | |
| fn checkGlError(functionName: string = __FUNCTION__, location: string = __LOCATION__) { | |
| err := glGetError(); | |
| if (err != GL_NO_ERROR) { | |
| throw new core.Exception(new "${functionName}@${location} glError: ${errorToString(err)}"); | |
| } | |
| } | |
| fn errorToString(err: GLenum) string { | |
| switch (err) { | |
| case GL_NO_ERROR: return "GL_NO_ERROR"; | |
| case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; | |
| case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; | |
| case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; | |
| case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION"; | |
| case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; | |
| case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW"; | |
| case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW"; | |
| default: return "unknown OpenGL error"; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment