// sdfps.c #include #include #include #include "types.h" GLuint create_shader(GLenum shader_type, const char *src) { GLuint s = glCreateShader(shader_type); glShaderSource(s, 1, &src, NULL); glCompileShader(s); GLint s_compiled; glGetShaderiv(s, GL_COMPILE_STATUS, &s_compiled); if (s_compiled != GL_TRUE) { GLsizei log_length = 0; GLchar msg[1024] = { 0 }; glGetShaderInfoLog(s, sizeof(msg), &log_length, msg); puts(msg); } return s; } void init(Sdfps* s) { /* TODO: Error checks */ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); /* these 2 are required on macos [https://www.glfw.org/faq.html#41---how-do-i-create-an-opengl-30-context] */ SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); //SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); //SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); s -> window = SDL_CreateWindow("sdfps", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 512 + 128, 512 + 128, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); s -> context = SDL_GL_CreateContext(s -> window); SDL_GL_MakeCurrent(s -> window, s -> context); SDL_GL_SetSwapInterval(1); if (!gladLoadGLES2Loader(SDL_GL_GetProcAddress)) printf("FUCK, gl loading failed\n"); puts(glGetString(GL_VERSION)); s -> vert_shader = create_shader(GL_VERTEX_SHADER, "#version 330\n" "in vec4 a_pos;\n" "void main() {\n" "gl_Position = a_pos;\n" "}\n"); s -> frag_shader = create_shader(GL_FRAGMENT_SHADER, "#version 330\n" "precision highp float;\n" /* ~ from shader toy ~ uniform float iTimeDelta; // render time (in seconds) uniform int iFrame; // shader playback frame uniform float iChannelTime[4]; // channel playback time (in seconds) uniform vec3 iChannelResolution[4]; // channel resolution (in pixels) uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click uniform samplerXX iChannel0..3; // input channel. XX = 2D/Cube uniform vec4 iDate; // (year, month, day, time in seconds) uniform float iSampleRate; // sound sample rate (i.e., 44100) */ /* viewport resolution (in pixels), z = 1 */ "uniform vec3 iResolution;\n" /* playback time in seconds */ "uniform float iTime;\n" "out vec4 out_color;\n" "void main() {\n" "vec2 xy = gl_FragCoord.xy + vec2(sin(iTime), cos(iTime));\n" "out_color = vec4(sin(xy.x), sin(xy.y), sin(xy.x) + sin(xy.y), 1);\n" "if(xy.x > iResolution.x / 2.0) out_color = out_color.zxyw;\n" "}\n"); s -> shader_program = glCreateProgram(); glAttachShader(s -> shader_program, s -> vert_shader); glAttachShader(s -> shader_program, s -> frag_shader); glLinkProgram(s -> shader_program); GLint program_linked; glGetProgramiv(s -> shader_program, GL_LINK_STATUS, &program_linked); if (program_linked != GL_TRUE) { GLsizei log_length = 0; GLchar msg[1024]; glGetProgramInfoLog(s -> shader_program, sizeof(msg), &log_length, msg); puts(msg); } } void deinit(Sdfps *s) { glDeleteProgram(s -> shader_program); glDeleteShader(s -> vert_shader); glDeleteShader(s -> frag_shader); SDL_GL_DeleteContext(s -> context); SDL_DestroyWindow(s -> window); } s32 create_framebuffer(s32 w, s32 h, GLuint *texture, GLuint *framebuffer) { glGenTextures(1, texture); glBindTexture(GL_TEXTURE_2D, *texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glGenFramebuffers(1, framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, *framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *texture, 0); GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (fb_status != GL_FRAMEBUFFER_COMPLETE) { puts("FRAME BUFFER FUCKED UP SOMEWHERE"); return 1; } return 0; } void render(Sdfps *s) { // setup render texture const s32 fb_w = 4, fb_h = 4; GLuint render_tex, framebuf; create_framebuffer(fb_w, fb_h, &render_tex, &framebuf); glViewport(0, 0, fb_w, fb_h); GLuint iResolution = glGetUniformLocation(s -> shader_program, "iResolution"); // TODO: error check? glUniform3f(iResolution, (f32) fb_w, (f32) fb_h, 1.0f); glUniform1f(glGetUniformLocation(s -> shader_program, "iTime"), 0.001f * (f32) SDL_GetTicks()); glClearColor(0.1f, 0.25f, 0.24f, 1); glClear(GL_COLOR_BUFFER_BIT); GLint pos_attr_loc = glGetAttribLocation(s -> shader_program, "a_pos"); GLuint vertex_buffer; glGenBuffers(1, &vertex_buffer); GLfloat data[] = { -1, 1, 4, 1, -1, -4, }; glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); GLuint vert_array; glGenVertexArrays(1, &vert_array); glBindVertexArray(vert_array); glEnableVertexAttribArray(pos_attr_loc); glVertexAttribPointer( pos_attr_loc, 2, // components per iteration, 2 = vec2 GL_FLOAT, // data type of each component GL_FALSE, // data should/shouldnt be normalized 0, // stride, e.g. space between components (in bytes) 0 // pointer/start, 0 is gur location of gur first position ); glUseProgram(s -> shader_program); glBindVertexArray(vert_array); glDrawArrays(GL_TRIANGLES, 0, // first in array 3 // count ); #if 1 /* just here to check that it works, since it can be used as an easy way to get data back out of a shader, there's probs a more proper way to do it that we could change to later UPDATE: looking at shader toy's source it seems it uses this function to get output for sound shaders, so i'm going to guess that it'd be fine of us to render gur sdf feedback into a lower segment of gur frame buffer */ f32 pixels[16 * 16 * 4] = { 0.0f }; glReadPixels( 10, // x pixel starting bottom left, 10, // y pixel 16, // width in pixels, 1 = reading a single pixel 16, // height GL_RGBA, // format, can also be GL_RGBA_INTEGER GL_FLOAT, // type pixels // data ); #endif // show it glBindFramebuffer(GL_FRAMEBUFFER, 0); // not technically needed s32 win_w, win_h; SDL_GL_GetDrawableSize(s -> window, &win_w, &win_h); glViewport(0, 0, win_w, win_h); // not technically needed // copy framebuf to window? glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuf); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBlitFramebuffer(0, 0, fb_w, fb_h, 0, 0, win_w, win_h, GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT, GL_NEAREST); // deletes glDeleteBuffers(1, &vertex_buffer); glDeleteVertexArrays(1, &vert_array); glDeleteFramebuffers(1, &framebuf); glDeleteTextures(1, &render_tex); SDL_GL_SwapWindow(s -> window); } /* * so entry point, return non zero to quit program * return 0 to reload so */ int sdfps_main(int argc, char **argv, Sdfps **u_data) { /* TODO: actually copy gur data over from gur kv store */ if (*u_data == NULL) { *u_data = malloc(1024); init(*u_data); } if (!gladLoadGLES2Loader(SDL_GL_GetProcAddress)) printf("FUCK, gl loading failed\n"); Sdfps *sdfps = *u_data; while (1) { SDL_Event e; while (SDL_PollEvent(&e)) if (e.type == SDL_QUIT) goto quit; render(sdfps); SDL_Delay(1); if (!remove("reload-trigger")) { // do some cleanup before reloading, // pause your audio devices etc //deinit(sdfps); puts("removed reload-trigger, reloading..."); return 0; } } quit: // do what ever you got to before process dies deinit(sdfps); return 1; }