#ifndef RENDER_H
#define RENDER_H
-struct framebuffer
-{
- GLuint fb, colour, rb;
- int div;
- GLuint format;
+typedef struct framebuffer framebuffer;
- int allocated;
-};
/*
* All standard buffers used in rendering
*/
VG_STATIC struct pipeline
{
-#if 0
- float fov;
-#endif
glmesh fsquad;
- GLuint fb_background,
- rgb_background,
- mv_background,
- rb_background;
+ framebuffer *fb_main,
+ *fb_heightmap,
+ *fb_water_reflection,
+ *fb_water_beneath;
/* STD140 */
struct ub_world_lighting
widgets[3];
float shadow_spread, shadow_length;
-
- GLuint fb_depthmap, rgb_depthmap;
GLuint ubo_world_lighting,
ubo_world;
}
};
+struct framebuffer
+{
+ const char *display_name;
+ int resolution_div,
+ fixed_w,
+ fixed_h;
+
+ struct framebuffer_attachment
+ {
+ const char *display_name;
+
+ enum framebuffer_attachment_type
+ {
+ k_framebuffer_attachment_type_none,
+ k_framebuffer_attachment_type_colour,
+ k_framebuffer_attachment_type_renderbuffer
+ }
+ purpose;
+
+ enum framebuffer_quality_profile
+ {
+ k_framebuffer_quality_all,
+ k_framebuffer_quality_high_only
+ }
+ quality;
+
+ GLenum internalformat,
+ format,
+ type,
+ attachment;
+
+ GLuint id;
+ }
+ attachments[5];
+ GLuint fb;
+ framebuffer **link;
+}
+framebuffers[] =
+{
+ {
+ /*
+ * The primary draw target
+ */
+ "Main",
+ .link = &gpipeline.fb_main,
+ .resolution_div = 1,
+ .attachments =
+ {
+ {
+ "Colour", k_framebuffer_attachment_type_colour,
+
+ .internalformat = GL_RGB,
+ .format = GL_RGB,
+ .type = GL_UNSIGNED_BYTE,
+ .attachment = GL_COLOR_ATTACHMENT0
+ },
+ {
+ "Motion Vectors", k_framebuffer_attachment_type_colour,
+
+ .quality = k_framebuffer_quality_high_only,
+ .internalformat = GL_RG16F,
+ .format = GL_RG,
+ .type = GL_FLOAT,
+ .attachment = GL_COLOR_ATTACHMENT1
+ },
+ {
+ "Depth/Stencil", k_framebuffer_attachment_type_renderbuffer,
+
+ .internalformat = GL_DEPTH24_STENCIL8,
+ .attachment = GL_DEPTH_STENCIL_ATTACHMENT
+ }
+ }
+ },
+ {
+ /*
+ * A ortho projection of the world, used for shadows and ocean colouring.
+ * Note: it does not have a render buffer attachement because it's
+ * intended to be drawn to in a MAX blending mode
+ */
+ "Heightmap",
+ .link = &gpipeline.fb_heightmap,
+ .fixed_w = 1024,
+ .fixed_h = 1024,
+
+ .attachments =
+ {
+ {
+ "Depth", k_framebuffer_attachment_type_colour,
+
+ .internalformat = GL_R32F,
+ .format = GL_RED,
+ .type = GL_FLOAT,
+ .attachment = GL_COLOR_ATTACHMENT0
+ }
+ }
+ },
+ {
+ /*
+ * Second rendered view from the perspective of the water reflection
+ */
+ "Water reflection",
+ .link = &gpipeline.fb_water_reflection,
+ .resolution_div = 3,
+ .attachments =
+ {
+ {
+ "Colour", k_framebuffer_attachment_type_colour,
+ .internalformat = GL_RGB,
+ .format = GL_RGB,
+ .type = GL_UNSIGNED_BYTE,
+ .attachment = GL_COLOR_ATTACHMENT0
+ },
+ {
+ "Depth/Stencil", k_framebuffer_attachment_type_renderbuffer,
+
+ .internalformat = GL_DEPTH24_STENCIL8,
+ .attachment = GL_DEPTH_STENCIL_ATTACHMENT
+ }
+ }
+ },
+ {
+ /*
+ * Thid rendered view from the perspective of the camera, but just
+ * captures stuff thats under the water
+ */
+ "Water Beneath",
+ .link = &gpipeline.fb_water_beneath,
+ .resolution_div = 4,
+ .attachments =
+ {
+ {
+ "Colour", k_framebuffer_attachment_type_colour,
+ .internalformat = GL_RGBA,
+ .format = GL_RGBA,
+ .type = GL_UNSIGNED_BYTE,
+ .attachment = GL_COLOR_ATTACHMENT0
+ },
+ {
+ "Depth/Stencil", k_framebuffer_attachment_type_renderbuffer,
+
+ .internalformat = GL_DEPTH24_STENCIL8,
+ .attachment = GL_DEPTH_STENCIL_ATTACHMENT
+ }
+ }
+ }
+};
+
+/*
+ * Get the current (automatically scaled or fixed) resolution of framebuffer
+ */
+VG_STATIC void render_fb_get_current_res( struct framebuffer *fb,
+ int *x, int *y )
+{
+ if( fb->resolution_div )
+ {
+ *x = vg.window_x / fb->resolution_div;
+ *y = vg.window_y / fb->resolution_div;
+ }
+ else
+ {
+ *x = fb->fixed_w;
+ *y = fb->fixed_h;
+ }
+}
+
+/*
+ * Bind framebuffer for drawing to
+ */
+VG_STATIC void render_fb_bind( framebuffer *fb )
+{
+ int x, y;
+ render_fb_get_current_res( fb, &x, &y );
+ glBindFramebuffer( GL_FRAMEBUFFER, fb->fb );
+ glViewport( 0, 0, x, y );
+}
+
+/*
+ * Bind framebuffer attachment's texture
+ */
+VG_STATIC void render_fb_bind_texture( framebuffer *fb,
+ int attachment, int slot )
+{
+ struct framebuffer_attachment *at = &fb->attachments[attachment];
+
+ if( at->purpose != k_framebuffer_attachment_type_colour )
+ {
+ vg_fatal_exit_loop( "illegal operation: bind non-colour framebuffer"
+ " attachment to texture slot" );
+ }
+
+ glActiveTexture( GL_TEXTURE0 + slot );
+ glBindTexture( GL_TEXTURE_2D, fb->attachments[attachment].id );
+}
+
+
/*
* Shaders
*/
GLuint idx = glGetUniformBlockIndex( shader, "ub_world_lighting" );
glUniformBlockBinding( shader, idx, 0 );
- glActiveTexture( GL_TEXTURE0 + texture_id );
- glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_depthmap );
+ render_fb_bind_texture( gpipeline.fb_heightmap, 0, texture_id );
glUniform1i( glGetUniformLocation( shader, "g_world_depth" ), texture_id );
}
&gpipeline.ub_world_lighting );
}
+#define FB_FORMAT_STR( E ) { E, #E },
+
/*
- * Framebuffers
+ * Convert OpenGL attachment ID enum to string
*/
+VG_STATIC const char *render_fb_attachment_str( GLenum e )
+{
+ struct { GLenum e; const char *str; }
+ formats[] =
+ {
+ FB_FORMAT_STR(GL_COLOR_ATTACHMENT0)
+ FB_FORMAT_STR(GL_COLOR_ATTACHMENT1)
+ FB_FORMAT_STR(GL_COLOR_ATTACHMENT2)
+ FB_FORMAT_STR(GL_COLOR_ATTACHMENT3)
+ FB_FORMAT_STR(GL_COLOR_ATTACHMENT4)
+ FB_FORMAT_STR(GL_DEPTH_STENCIL_ATTACHMENT)
+ };
+
+ for( int i=0; i<vg_list_size(formats); i++ )
+ if( formats[i].e == e )
+ return formats[i].str;
+
+ return "UNDEFINED";
+}
+/*
+ * Convert OpenGL texture format enums from TexImage2D table 1,2 &
+ * RenderBufferStorage Table 1, into strings
+ */
+VG_STATIC const char *render_fb_format_str( GLenum format )
+{
+ struct { GLenum e; const char *str; }
+ formats[] =
+ {
+ /* Table 1 */
+ FB_FORMAT_STR(GL_DEPTH_COMPONENT)
+ FB_FORMAT_STR(GL_DEPTH_STENCIL)
+ FB_FORMAT_STR(GL_RED)
+ FB_FORMAT_STR(GL_RG)
+ FB_FORMAT_STR(GL_RGB)
+ FB_FORMAT_STR(GL_RGBA)
+
+ /* Render buffer formats */
+ FB_FORMAT_STR(GL_DEPTH_COMPONENT16)
+ FB_FORMAT_STR(GL_DEPTH_COMPONENT24)
+ FB_FORMAT_STR(GL_DEPTH_COMPONENT32F)
+ FB_FORMAT_STR(GL_DEPTH24_STENCIL8)
+ FB_FORMAT_STR(GL_DEPTH32F_STENCIL8)
+ FB_FORMAT_STR(GL_STENCIL_INDEX8)
+
+ /* Table 2 */
+ FB_FORMAT_STR(GL_R8)
+ FB_FORMAT_STR(GL_R8_SNORM)
+ FB_FORMAT_STR(GL_R16)
+ FB_FORMAT_STR(GL_R16_SNORM)
+ FB_FORMAT_STR(GL_RG8)
+ FB_FORMAT_STR(GL_RG8_SNORM)
+ FB_FORMAT_STR(GL_RG16)
+ FB_FORMAT_STR(GL_RG16_SNORM)
+ FB_FORMAT_STR(GL_R3_G3_B2)
+ FB_FORMAT_STR(GL_RGB4)
+ FB_FORMAT_STR(GL_RGB5)
+ FB_FORMAT_STR(GL_RGB8)
+ FB_FORMAT_STR(GL_RGB8_SNORM)
+ FB_FORMAT_STR(GL_RGB10)
+ FB_FORMAT_STR(GL_RGB12)
+ FB_FORMAT_STR(GL_RGB16_SNORM)
+ FB_FORMAT_STR(GL_RGBA2)
+ FB_FORMAT_STR(GL_RGBA4)
+ FB_FORMAT_STR(GL_RGB5_A1)
+ FB_FORMAT_STR(GL_RGBA8)
+ FB_FORMAT_STR(GL_RGBA8_SNORM)
+ FB_FORMAT_STR(GL_RGB10_A2)
+ FB_FORMAT_STR(GL_RGB10_A2UI)
+ FB_FORMAT_STR(GL_RGBA12)
+ FB_FORMAT_STR(GL_RGBA16)
+ FB_FORMAT_STR(GL_SRGB8)
+ FB_FORMAT_STR(GL_SRGB8_ALPHA8)
+ FB_FORMAT_STR(GL_R16F)
+ FB_FORMAT_STR(GL_RG16F)
+ FB_FORMAT_STR(GL_RGB16F)
+ FB_FORMAT_STR(GL_RGBA16F)
+ FB_FORMAT_STR(GL_R32F)
+ FB_FORMAT_STR(GL_RG32F)
+ FB_FORMAT_STR(GL_RGB32F)
+ FB_FORMAT_STR(GL_RGBA32F)
+ FB_FORMAT_STR(GL_R11F_G11F_B10F)
+ FB_FORMAT_STR(GL_RGB9_E5)
+ FB_FORMAT_STR(GL_R8I)
+ FB_FORMAT_STR(GL_R8UI)
+ FB_FORMAT_STR(GL_R16I)
+ FB_FORMAT_STR(GL_R16UI)
+ FB_FORMAT_STR(GL_R32I)
+ FB_FORMAT_STR(GL_R32UI)
+ FB_FORMAT_STR(GL_RG8I)
+ FB_FORMAT_STR(GL_RG8UI)
+ FB_FORMAT_STR(GL_RG16I)
+ FB_FORMAT_STR(GL_RG16UI)
+ FB_FORMAT_STR(GL_RG32I)
+ FB_FORMAT_STR(GL_RG32UI)
+ FB_FORMAT_STR(GL_RGB8I)
+ FB_FORMAT_STR(GL_RGB8UI)
+ FB_FORMAT_STR(GL_RGB16I)
+ FB_FORMAT_STR(GL_RGB16UI)
+ FB_FORMAT_STR(GL_RGB32I)
+ FB_FORMAT_STR(GL_RGB32UI)
+ FB_FORMAT_STR(GL_RGBA8I)
+ FB_FORMAT_STR(GL_RGBA8UI)
+ FB_FORMAT_STR(GL_RGBA16I)
+ FB_FORMAT_STR(GL_RGBA16UI)
+ FB_FORMAT_STR(GL_RGBA32I)
+ FB_FORMAT_STR(GL_RGBA32UI)
+ };
+
+ for( int i=0; i<vg_list_size(formats); i++ )
+ if( formats[i].e == format )
+ return formats[i].str;
+
+ return "UNDEFINED";
+}
+
+/*
+ * Bind and allocate texture for framebuffer attachment
+ */
+VG_STATIC void render_fb_allocate_texture( struct framebuffer *fb,
+ struct framebuffer_attachment *a )
+{
+ int rx, ry;
+ render_fb_get_current_res( fb, &rx, &ry );
+
+ if( a->purpose == k_framebuffer_attachment_type_renderbuffer )
+ {
+ glBindRenderbuffer( GL_RENDERBUFFER, a->id );
+ glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, rx, ry );
+ }
+ else if( a->purpose == k_framebuffer_attachment_type_colour )
+ {
+ glBindTexture( GL_TEXTURE_2D, a->id );
+ glTexImage2D( GL_TEXTURE_2D, 0, a->internalformat, rx, ry,
+ 0, a->format, a->type, NULL );
+ }
+}
+
+/*
+ * Full allocation of a framebuffer
+ */
+VG_STATIC void render_fb_allocate( struct framebuffer *fb )
+{
+ glGenFramebuffers( 1, &fb->fb );
+ glBindFramebuffer( GL_FRAMEBUFFER, fb->fb );
+
+ int rx, ry;
+ render_fb_get_current_res( fb, &rx, &ry );
+
+ vg_info( "allocate_framebuffer( %s, %dx%d )\n", fb->display_name, rx, ry );
+ vg_info( "{\n" );
+
+ GLenum colour_attachments[4];
+ u32 colour_count = 0;
+
+ for( int j=0; j<vg_list_size(fb->attachments); j++ )
+ {
+ struct framebuffer_attachment *attachment = &fb->attachments[j];
+
+ if( attachment->purpose == k_framebuffer_attachment_type_none )
+ continue;
+
+ vg_info( " %s: %s\n",
+ render_fb_attachment_str( attachment->attachment ),
+ render_fb_format_str( attachment->internalformat ) );
+
+ if( attachment->purpose == k_framebuffer_attachment_type_renderbuffer )
+ {
+ glGenRenderbuffers( 1, &attachment->id );
+ render_fb_allocate_texture( fb, attachment );
+ glFramebufferRenderbuffer( GL_FRAMEBUFFER,
+ GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, attachment->id );
+ }
+ else if( attachment->purpose == k_framebuffer_attachment_type_colour )
+ {
+ glGenTextures( 1, &attachment->id );
+ render_fb_allocate_texture( fb, attachment );
+ 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 );
+
+ glFramebufferTexture2D( GL_FRAMEBUFFER, attachment->attachment,
+ GL_TEXTURE_2D, attachment->id, 0 );
+
+ colour_attachments[ colour_count ++ ] = attachment->attachment;
+ }
+ }
+
+ glDrawBuffers( colour_count, colour_attachments );
+
+ /*
+ * Check result
+ */
+ GLenum result = glCheckFramebufferStatus( GL_FRAMEBUFFER );
+
+ if( result == GL_FRAMEBUFFER_COMPLETE )
+ {
+ /*
+ * Attatch to gpipeline
+ */
+ if( fb->link )
+ *fb->link = fb;
+
+ vg_success( " status: complete\n" );
+ vg_info( "}\n" );
+ }
+ else
+ {
+ if( result == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT )
+ vg_error( " status: Incomplete attachment" );
+ else if( result == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT )
+ vg_error( " status: Missing attachment" );
+ else if( result == GL_FRAMEBUFFER_UNSUPPORTED )
+ vg_error( " status: Unsupported framebuffer format" );
+ else
+ vg_error( " status: Generic Error" );
+
+ vg_info( "}\n" );
+ vg_fatal_exit_loop( "Incomplete framebuffer (see logs)" );
+ }
+}
+
+/*
+ * Resize/Update all framebuffers(we know about)
+ */
+VG_STATIC void render_fb_resize(void)
+{
+ if( !gpipeline.ready )
+ return;
+
+ for( int i=0; i<vg_list_size(framebuffers); i++ )
+ {
+ struct framebuffer *fb = &framebuffers[i];
+ for( int j=0; j<vg_list_size(fb->attachments); j++ )
+ {
+ struct framebuffer_attachment *attachment = &fb->attachments[j];
+ render_fb_allocate_texture( fb, attachment );
+ }
+ }
+}
+
+#if 0
VG_STATIC void fb_use( struct framebuffer *fb )
{
if( !fb )
fb->allocated = 1;
}
+VG_STATIC void _fb_glTexImage2D( GLsizei x, GLsizei y, GLint internalformat,
+ GLenum format, GLenum type, const void *data )
+{
+ glTexImage2D( GL_TEXTURE_2D, 0, internalformat, x,y, 0,
+ format, type, data );
+
+ 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 );
+}
+
+#define fb_tex2d( X,Y, INTERNAL_FORMAT, FORMAT, TYPE, DATA ) \
+ _fb_glTexImage2D( X,Y, INTERNAL_FORMAT, FORMAT, TYPE, DATA ); \
+ vg_info( "texture( %dx%d, internal: %s, format: %s, type: %s )\n", \
+ X,Y, #INTERNAL_FORMAT, #FORMAT, #TYPE );
+
VG_STATIC void fb_free( struct framebuffer *fb )
{
glDeleteTextures( 1, &fb->colour );
glBindRenderbuffer( GL_RENDERBUFFER, fb->rb );
glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, ix, iy );
}
+#endif
-VG_STATIC void render_fb_resize(void)
-{
- if( gpipeline.ready )
- {
- glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_background );
- glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, vg.window_x, vg.window_y, 0,
- GL_RGB, GL_UNSIGNED_BYTE, NULL );
-
- /* FIXME: Resizeother textures and rb */
- }
-}
-
+#if 0
VG_STATIC void render_init_temp_buffer(void)
{
vg_info( "[render] Allocate framebuffer\n" );
glGenTextures( 1, &gpipeline.rgb_background );
glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_background );
- glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, vg.window_x, vg.window_y,
- 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
- 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 );
+ fb_tex2d( vg.window_x, vg.window_y, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, NULL );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
gpipeline.rgb_background, 0 );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RG, vg.window_x, vg.window_y,
0, GL_RG, GL_FLOAT, NULL);
#endif
- glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA16F, vg.window_x, vg.window_y,
- 0, GL_RGBA, GL_FLOAT, NULL);
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RG16F, vg.window_x, vg.window_y,
+ 0, GL_RG, GL_FLOAT, NULL);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
GLuint attachments[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers( 2, attachments );
-
- GLenum result = glCheckFramebufferStatus( GL_FRAMEBUFFER );
-
- if( result != GL_FRAMEBUFFER_COMPLETE )
- {
- if( result == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT )
- vg_fatal_exit_loop( "Main RT: Incomplete attachment" );
- else if( result == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT )
- vg_fatal_exit_loop( "Main RT: Missing attachment" );
- else if( result == GL_FRAMEBUFFER_UNSUPPORTED )
- vg_fatal_exit_loop( "Main RT: Unsupported framebuffer format" );
- else
- vg_fatal_exit_loop( "Main RT: Generic Error" );
- }
+
+ render_check_framebuffer_complete();
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
VG_CHECK_GL_ERR();
GL_TEXTURE_2D,
gpipeline.rgb_depthmap, 0);
+ render_check_framebuffer_complete();
VG_CHECK_GL_ERR();
}
+#endif
VG_STATIC void render_init_fs_quad(void)
{
vg_acquire_thread_sync();
{
- render_init_temp_buffer();
- render_init_depthmap_buffer();
+ /*
+ * Complete Framebuffers
+ */
+ for( int i=0; i<vg_list_size(framebuffers); i++ )
+ {
+ struct framebuffer *fb = &framebuffers[i];
+ render_fb_allocate( fb );
+ }
+
render_init_fs_quad();
render_init_uniform_buffers();