--- /dev/null
+uniform sampler2D tex;
+
+#ifndef set_pixel
+#define set_pixel set_pixel_linear
+#endif
+
+void set_pixel_linear(vec3 c)
+{
+ gl_FragColor.rgb = c;
+ gl_FragColor.a = 1.0;
+}
+
+void set_pixel_srgb(vec3 c)
+{
+ gl_FragColor.rgb = pow(c, vec3(1.0 / 2.2));
+ gl_FragColor.a = 1.0;
+}
+
+void main()
+{
+ vec3 texel = texture2D(tex, gl_TexCoord[0].st).rgb;
+
+ set_pixel(texel);
+}
--- /dev/null
+uniform mat4 texmat;
+
+void main()
+{
+ gl_Position = gl_Vertex;
+ gl_TexCoord[0] = texmat * gl_MultiTexCoord0;
+}
#include "goatvr.h"
#include "opt.h"
#include "fs.h"
+#include "rtarg.h"
+#include "texture.h"
+#include "sdr.h"
static void draw_scene();
static float fov = 60.0;
+static RenderTarget *rtarg;
+static bool rtarg_valid;
+static unsigned int post_sdr;
+
+
bool app_init(int argc, char **argv)
{
if(!init_options(argc, argv, "vrfileman.conf")) {
glEnable(GL_FRAMEBUFFER_SRGB);
}
+ rtarg = new RenderTarget;
+ rtarg->create(GL_RGB16F);
+
if(opt.vr) {
if(goatvr_init() == -1) {
return false;
cam_height = goatvr_get_eye_height();
}
+ if(opt.srgb) {
+ add_shader_header(GL_FRAGMENT_SHADER, "#define set_pixel set_pixel_linear");
+ } else {
+ add_shader_header(GL_FRAGMENT_SHADER, "#define set_pixel set_pixel_srgb");
+ }
+ if(!(post_sdr = create_program_load("sdr/post.v.glsl", "sdr/post.p.glsl"))) {
+ return false;
+ }
+ clear_shader_header(0);
+
Mesh::use_custom_sdr_attr = false;
if(!init_backdrop()) {
if(opt.vr) {
goatvr_shutdown();
}
+ delete rtarg;
+ free_program(post_sdr);
cleanup_backdrop();
}
+static void update()
+{
+ if(!rtarg_valid) {
+ rtarg->resize(win_width, win_height);
+ }
+}
+
void app_draw()
{
+ update();
+
if(opt.vr) {
// VR mode
goatvr_draw_start();
static void draw_scene()
{
+ set_render_target(rtarg);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
draw_backdrop();
draw_fs();
+
+ set_render_target(0);
+
+ glPushAttrib(GL_ENABLE_BIT);
+ glDisable(GL_DEPTH_TEST);
+
+ glUseProgram(post_sdr);
+ set_uniform_matrix4(post_sdr, "texmat", rtarg->get_texture_matrix()[0]);
+
+ bind_texture(rtarg->get_texture());
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0, 0);
+ glVertex2f(-1, -1);
+ glTexCoord2f(1, 0);
+ glVertex2f(1, -1);
+ glTexCoord2f(1, 1);
+ glVertex2f(1, 1);
+ glTexCoord2f(0, 1);
+ glVertex2f(-1, 1);
+ glEnd();
+
+ bind_texture(0);
+ glUseProgram(0);
+
+ glPopAttrib();
}
void app_reshape(int x, int y)
{
glViewport(0, 0, x, y);
+ rtarg_valid = false;
if(opt.vr) {
goatvr_set_fb_size(x, y, 1.0);
Color color(float r, float g, float b)
{
- return opt.srgb ? Color(r, g, b) : linear_to_srgb(Color(r, g, b));
+ return Color(r, g, b);//opt.srgb ? Color(r, g, b) : linear_to_srgb(Color(r, g, b));
}
Color linear_to_srgb(const Color &c)
--- /dev/null
+#include "rtarg.h"
+#include "texture.h"
+#include "app.h"
+
+unsigned int RenderTarget::default_fbo = 0;
+
+RenderTarget::RenderTarget()
+{
+ width = height = 0;
+ fbo = 0;
+ rbuf_zstencil = 0;
+ color_tex = 0;
+ tex_face = 0;
+ tex_targ = 0;
+}
+
+RenderTarget::~RenderTarget()
+{
+ cleanup();
+}
+
+bool RenderTarget::create(unsigned int fmt)
+{
+ return create(win_width, win_height, fmt);
+}
+
+bool RenderTarget::create(int width, int height, unsigned int fmt)
+{
+ fprintf(stderr, "RenderTarget::create(%d, %d)\n", width, height);
+ cleanup();
+
+ tex_targ = GL_TEXTURE_2D;
+ this->width = width;
+ this->height = height;
+ int tex_width = next_pow2(width);
+ int tex_height = next_pow2(height);
+
+ tex_matrix.scaling((float)width / (float)tex_width, (float)height / (float)tex_height, 1.0);
+
+ color_tex = new Texture2D;
+ color_tex->create(tex_width, tex_height, fmt);
+ tex_face = 0;
+
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ glBindTexture(GL_TEXTURE_2D, color_tex->get_id());
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex->get_id(), 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glGenRenderbuffers(1, &rbuf_zstencil);
+ glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
+#ifdef GL_ES_VERSION_2_0
+ // XXX really? 16bit zbuffer attachment is the best we can do on ES2?
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, tex_width, tex_height);
+#else
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex_width, tex_height);
+#endif
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
+#ifndef GL_ES_VERSION_2_0
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
+#endif
+
+ glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo);
+ return true;
+}
+
+bool RenderTarget::create(Texture *tex, int face)
+{
+ fprintf(stderr, "RenderTarget::create(tex{%d, %d}, face:%d)\n", tex->get_size(0),
+ tex->get_size(1), face);
+
+ tex_targ = GL_TEXTURE_2D;
+ if(dynamic_cast<TextureCube*>(tex)) {
+ if(face >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) {
+ tex_targ = face;
+ } else if(face >= 0 && face < 6) {
+ tex_targ = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
+ } else {
+ fprintf(stderr, "invalid face (%d) passed to RenderTarget::create(TextureCube*, int)\n", face);
+ return false;
+ }
+ }
+
+ cleanup();
+
+ width = tex->get_size(0);
+ height = tex->get_size(1);
+
+ tex_matrix = Mat4::identity;
+
+ color_tex = tex;
+
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ glBindTexture(tex_targ, color_tex->get_id());
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0);
+ glBindTexture(tex_targ, 0);
+
+ glGenRenderbuffers(1, &rbuf_zstencil);
+ glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo);
+ return true;
+}
+
+void RenderTarget::cleanup()
+{
+ delete color_tex;
+ color_tex = 0;
+
+ if(fbo) {
+ glDeleteFramebuffers(1, &fbo);
+ }
+ if(rbuf_zstencil) {
+ glDeleteRenderbuffers(1, &rbuf_zstencil);
+ }
+
+ fbo = rbuf_zstencil = 0;
+ width = height = 0;
+ tex_face = 0;
+ tex_targ = 0;
+}
+
+bool RenderTarget::resize(int width, int height)
+{
+ if(width == this->width && height == this->height) {
+ return true;
+ }
+
+ int tex_width = next_pow2(width);
+ int tex_height = next_pow2(height);
+ this->width = width;
+ this->height = height;
+
+ if(tex_width != color_tex->get_size(0) || tex_height != color_tex->get_size(1)) {
+ printf("resizing render target (fbo %u): %dx%d [%dx%d]\n", fbo, width, height, tex_width, tex_height);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ color_tex->create(tex_width, tex_height, color_tex->get_format());
+ color_tex->bind();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0);
+ glBindTexture(tex_targ, 0);
+
+ glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
+
+ unsigned int depth_format;
+#ifndef GL_ES_VERSION_2_0
+ // desktop OpenGL, use typical depth24-stencil8 format
+ depth_format = GL_DEPTH24_STENCIL8;
+#else // OpenGL ES
+ // OpenGL ES provides combined depth/stencil formats as an extension
+#ifdef GL_OES_packed_depth_stencil
+ if(have_ext_packed_depth_stencil) {
+ depth_format = GL_DEPTH24_STENCIL8_OES;
+ } else
+#endif // GL_OES_packed_depth_stencil
+ {
+ depth_format = GL_DEPTH_COMPONENT16; // no compile-time or runtime support for packed d/s
+ }
+#endif // GL_ES_VERSION_2_0
+ glRenderbufferStorage(GL_RENDERBUFFER, depth_format, tex_width, tex_height);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo);
+ }
+
+ tex_matrix.scaling((float)width / (float)tex_width, (float)height / (float)tex_height, 1.0);
+ return true;
+}
+
+int RenderTarget::get_width() const
+{
+ return width;
+}
+
+int RenderTarget::get_height() const
+{
+ return height;
+}
+
+Texture *RenderTarget::get_texture() const
+{
+ return color_tex;
+}
+
+const Mat4 &RenderTarget::get_texture_matrix() const
+{
+ return tex_matrix;
+}
+
+
+static const char *fbstname[] = {
+ "GL_FRAMEBUFFER_COMPLETE",
+ "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT",
+ "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT",
+ "no such fbo error",
+ "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS",
+ "GL_FRAMEBUFFER_INCOMPLETE_FORMATS",
+ "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER",
+ "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER",
+ "GL_FRAMEBUFFER_UNSUPPORTED"
+};
+
+bool RenderTarget::check() const
+{
+ bool res = true;
+
+#ifndef GL_ES_VERSION_2_0
+ int prev_fb = 0;
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &prev_fb);
+#endif
+
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if(status != GL_FRAMEBUFFER_COMPLETE) {
+ fprintf(stderr, "RenderTarget::check: incomplete FBO %u: %s\n", fbo,
+ fbstname[status - GL_FRAMEBUFFER_COMPLETE]);
+ res = false;
+ goto end;
+ }
+
+end:
+#ifndef GL_ES_VERSION_2_0
+ glBindFramebuffer(GL_FRAMEBUFFER, prev_fb);
+#endif
+ return res;
+}
+
+void RenderTarget::bind() const
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ glViewport(0, 0, width, height);
+}
+
+void set_render_target(const RenderTarget *rtarg)
+{
+ if(rtarg) {
+ rtarg->bind();
+ } else {
+ glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo);
+ glViewport(0, 0, win_width, win_height);
+ }
+}
+
+int next_pow2(int x)
+{
+ x--;
+ x = (x >> 1) | x;
+ x = (x >> 2) | x;
+ x = (x >> 4) | x;
+ x = (x >> 8) | x;
+ x = (x >> 16) | x;
+ return x + 1;
+}
+
--- /dev/null
+#ifndef RTARG_H_
+#define RTARG_H_
+
+#include "gmath/gmath.h"
+#include "opengl.h"
+
+class Texture;
+
+class RenderTarget {
+private:
+ int width, height;
+
+ unsigned int fbo;
+ Texture *color_tex;
+ unsigned int tex_targ;
+ int tex_face;
+ unsigned int rbuf_zstencil;
+
+ Mat4 tex_matrix;
+
+ void bind() const;
+
+public:
+ static unsigned int default_fbo; // 0 on most platforms, GLKit fbo on iOS.
+
+ RenderTarget();
+ ~RenderTarget();
+
+ bool create(unsigned int fmt = GL_RGBA);
+ bool create(int width, int height, unsigned int fmt = GL_RGBA);
+ bool create(Texture *tex, int face = 0);
+
+ void cleanup();
+
+ bool resize(int width, int height);
+
+ int get_width() const;
+ int get_height() const;
+
+ Texture *get_texture() const;
+
+ /** calculates a texture matrix to map the full texture space
+ * onto the part of the texture occupied by the render target
+ */
+ const Mat4 &get_texture_matrix() const;
+
+ bool check() const;
+
+ friend void set_render_target(const RenderTarget *rtarg);
+};
+
+void set_render_target(const RenderTarget *rtarg);
+
+int next_pow2(int x);
+
+#endif // RTARG_H_
#include "sdr.h"
static const char *sdrtypestr(unsigned int sdrtype);
+static int sdrtypeidx(unsigned int sdrtype);
+
unsigned int create_vertex_shader(const char *src)
{
unsigned int sdr;
int success, info_len;
char *info_str = 0;
+ const char *src_str[3], *header, *footer;
+ int src_str_count = 0;
GLenum err;
+ if((header = get_shader_header(sdr_type))) {
+ src_str[src_str_count++] = header;
+ }
+ src_str[src_str_count++] = src;
+ if((footer = get_shader_footer(sdr_type))) {
+ src_str[src_str_count++] = footer;
+ }
+
sdr = glCreateShader(sdr_type);
assert(glGetError() == GL_NO_ERROR);
- glShaderSource(sdr, 1, &src, 0);
+ glShaderSource(sdr, src_str_count, src_str, 0);
err = glGetError();
assert(err == GL_NO_ERROR);
glCompileShader(sdr);
END_UNIFORM_CODE;
}
-int set_uniform_matrix4(unsigned int prog, const char *name, float *mat)
+int set_uniform_matrix4(unsigned int prog, const char *name, const float *mat)
{
BEGIN_UNIFORM_CODE {
glUniformMatrix4fv(loc, 1, GL_FALSE, mat);
END_UNIFORM_CODE;
}
-int set_uniform_matrix4_transposed(unsigned int prog, const char *name, float *mat)
+int set_uniform_matrix4_transposed(unsigned int prog, const char *name, const float *mat)
{
BEGIN_UNIFORM_CODE {
glUniformMatrix4fv(loc, 1, GL_TRUE, mat);
glVertexAttrib3f(attr_loc, x, y, z);
}
+/* ---- shader composition ---- */
+struct string {
+ char *text;
+ int len;
+};
+
+#define NUM_SHADER_TYPES 5
+static struct string header[NUM_SHADER_TYPES];
+static struct string footer[NUM_SHADER_TYPES];
+
+static void clear_string(struct string *str)
+{
+ free(str->text);
+ str->text = 0;
+ str->len = 0;
+}
+
+static void append_string(struct string *str, const char *s)
+{
+ int len, newlen;
+ char *newstr;
+
+ if(!s || !*s) return;
+
+ len = strlen(s);
+ newlen = str->len + len;
+ if(!(newstr = malloc(newlen + 2))) { /* leave space for a possible newline */
+ fprintf(stderr, "shader composition: failed to append string of size %d\n", len);
+ abort();
+ }
+
+ if(str->text) {
+ memcpy(newstr, str->text, str->len);
+ }
+ memcpy(newstr + str->len, s, len + 1);
+
+ if(s[len - 1] != '\n') {
+ newstr[newlen] = '\n';
+ newstr[newlen + 1] = 0;
+ }
+
+ free(str->text);
+ str->text = newstr;
+ str->len = newlen;
+}
+
+void clear_shader_header(unsigned int type)
+{
+ if(type) {
+ int idx = sdrtypeidx(type);
+ clear_string(&header[idx]);
+ } else {
+ int i;
+ for(i=0; i<NUM_SHADER_TYPES; i++) {
+ clear_string(&header[i]);
+ }
+ }
+}
+
+void clear_shader_footer(unsigned int type)
+{
+ if(type) {
+ int idx = sdrtypeidx(type);
+ clear_string(&footer[idx]);
+ } else {
+ int i;
+ for(i=0; i<NUM_SHADER_TYPES; i++) {
+ clear_string(&footer[i]);
+ }
+ }
+}
+
+void add_shader_header(unsigned int type, const char *s)
+{
+ if(type) {
+ int idx = sdrtypeidx(type);
+ append_string(&header[idx], s);
+ } else {
+ int i;
+ for(i=0; i<NUM_SHADER_TYPES; i++) {
+ append_string(&header[i], s);
+ }
+ }
+}
+
+void add_shader_footer(unsigned int type, const char *s)
+{
+ if(type) {
+ int idx = sdrtypeidx(type);
+ append_string(&footer[idx], s);
+ } else {
+ int i;
+ for(i=0; i<NUM_SHADER_TYPES; i++) {
+ append_string(&footer[i], s);
+ }
+ }
+}
+
+const char *get_shader_header(unsigned int type)
+{
+ int idx = sdrtypeidx(type);
+ return header[idx].text;
+}
+
+const char *get_shader_footer(unsigned int type)
+{
+ int idx = sdrtypeidx(type);
+ return footer[idx].text;
+}
+
static const char *sdrtypestr(unsigned int sdrtype)
{
switch(sdrtype) {
}
return "<unknown>";
}
+
+static int sdrtypeidx(unsigned int sdrtype)
+{
+ switch(sdrtype) {
+ case GL_VERTEX_SHADER:
+ return 0;
+ case GL_FRAGMENT_SHADER:
+ return 1;
+ case GL_TESS_CONTROL_SHADER:
+ return 2;
+ case GL_TESS_EVALUATION_SHADER:
+ return 3;
+ case GL_GEOMETRY_SHADER:
+ return 4;
+ default:
+ break;
+ }
+ return 0;
+}
int set_uniform_float2(unsigned int prog, const char *name, float x, float y);
int set_uniform_float3(unsigned int prog, const char *name, float x, float y, float z);
int set_uniform_float4(unsigned int prog, const char *name, float x, float y, float z, float w);
-int set_uniform_matrix4(unsigned int prog, const char *name, float *mat);
-int set_uniform_matrix4_transposed(unsigned int prog, const char *name, float *mat);
+int set_uniform_matrix4(unsigned int prog, const char *name, const float *mat);
+int set_uniform_matrix4_transposed(unsigned int prog, const char *name, const float *mat);
int get_attrib_loc(unsigned int prog, const char *name);
void set_attrib_float3(int attr_loc, float x, float y, float z);
+/* ---- shader composition ---- */
+
+/* clear shader header/footer text.
+ * pass the shader type to clear, or 0 to clear all types */
+void clear_shader_header(unsigned int type);
+void clear_shader_footer(unsigned int type);
+/* append text to the header/footer of a specific shader type
+ * or use type 0 to add it to all shade types */
+void add_shader_header(unsigned int type, const char *s);
+void add_shader_footer(unsigned int type, const char *s);
+/* get the current header/footer text for a specific shader type */
+const char *get_shader_header(unsigned int type);
+const char *get_shader_footer(unsigned int type);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */