Bloom on sculpture

This commit is contained in:
Peter Slattery 2022-04-17 07:24:22 -07:00
parent d7520367e2
commit 1b7a9c6841
13 changed files with 246 additions and 69 deletions

View File

@ -1,3 +1,3 @@
SCRIPT_REL_DIR=$(dirname "${BASH_SOURCE[0]}")
$SCRIPT_REL_DIR/build_.sh prod osx arm64
$SCRIPT_REL_DIR/build_.sh debug osx arm64
# $SCRIPT_REL_DIR/build_.sh debug wasm intel

Binary file not shown.

Binary file not shown.

View File

@ -42,12 +42,18 @@ struct Geometry_Buffer
u32 indices_len;
};
typedef struct Texture_Desc Texture_Desc;
struct Texture_Desc
{
u32 w, h, s;
u32 mag_filter, min_filter, fmt_internal, fmt_data;
};
typedef struct Texture Texture;
struct Texture
{
u32 id;
u32 w, h, s;
Texture_Desc desc;
};
typedef struct Graphics_Frame_Desc Graphics_Frame_Desc;
@ -66,6 +72,8 @@ Geometry_Buffer geometry_buffer_create(r32* vertices, u32 vertices_len, u32* ind
Shader shader_create(String code_vert, String code_frag, String* attribs, u32 attribs_len, String* uniforms, u32 uniforms_len);
void geometry_buffer_update(Geometry_Buffer* buffer, r32* verts, u32 verts_offset, u32 verts_len, u32* indices, u32 indices_offset, u32 indices_len);
Geometry_Buffer unit_quad_create();
// Shaders
void geometry_bind(Geometry_Buffer geo);
void shader_bind(Shader shader);
@ -75,7 +83,7 @@ void vertex_attrib_pointer(Geometry_Buffer geo, Shader shader, u32 count, u32 at
void set_uniform(Shader shader, u32 index, m44 u);
// Textures
Texture texture_create(u8* pixels, u32 width, u32 height, u32 stride);
Texture texture_create(Texture_Desc desc, u8* pixels);
void texture_bind(Texture tex);
void texture_update(Texture tex, u8* new_pixels, u32 width, u32 height, u32 stride);
@ -170,6 +178,22 @@ geometry_buffer_update(
os_gl_no_error();
}
Geometry_Buffer
unit_quad_create()
{
r32 z = 0;
r32 r = 1;
r32 verts[] = {
// pos uv
-r, -r, z, 0, 0,
r, -r, z, 1, 0,
r, r, z, 1, 1,
-1, r, z, 0, 1,
};
u32 indices[] = { 0, 1, 2, 0, 2, 3 };
return geometry_buffer_create(verts, sizeof(verts) / sizeof(r32), indices, 6);
}
Shader
shader_create(String code_vert, String code_frag, String* attrs, u32 attrs_len, String* uniforms, u32 uniforms_len){
Shader result = {};
@ -300,8 +324,22 @@ void vertex_attrib_pointer(Geometry_Buffer geo, Shader shader, GLuint count, GLu
os_gl_no_error();
}
Texture_Desc
texture_desc_default(u32 width, u32 height)
{
return (Texture_Desc){
.w = width,
.h = height,
.s = width,
.min_filter = GL_NEAREST,
.mag_filter = GL_LINEAR,
.fmt_internal = GL_RGBA,
.fmt_data = GL_RGBA
};
}
Texture
texture_create(u8* pixels, u32 width, u32 height, u32 stride)
texture_create(Texture_Desc desc, u8* pixels)
{
Texture result = {};
glGenTextures(1, &result.id);
@ -312,27 +350,26 @@ texture_create(u8* pixels, u32 width, u32 height, u32 stride)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, desc.min_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, desc.mag_filter);
os_gl_no_error();
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
desc.fmt_internal,
desc.w,
desc.h,
0,
GL_RGBA,
desc.fmt_data,
GL_UNSIGNED_BYTE,
pixels
);
os_gl_no_error();
result.w = width;
result.h = height;
result.s = stride;
result.desc = desc;
glBindTexture(GL_TEXTURE_2D, 0);
return result;
}
@ -342,7 +379,7 @@ texture_update(Texture tex, u8* new_pixels, u32 width, u32 height, u32 stride)
// NOTE(PS): this function simply replaces the entire image
// we can write a more granular version if we need it
assert(tex.w == width && tex.h == height && tex.s == stride);
assert(tex.desc.w == width && tex.desc.h == height && tex.desc.s == stride);
texture_bind(tex);
glTexSubImage2D(
GL_TEXTURE_2D,
@ -370,8 +407,8 @@ frame_begin(Graphics_Frame_Desc desc)
glClearColor(cc.r, cc.g, cc.b, cc.a);
v2 vmin = desc.viewport_min;
v2 vmax = desc.viewport_max;
glViewport((GLsizei)vmin.x, (GLsizei)vmin.y, (GLint)vmax.x, (GLint)vmax.y);
v2 vdim = HMM_SubtractVec2(desc.viewport_max, desc.viewport_min);
glViewport((GLsizei)vmin.x, (GLsizei)vmin.y, (GLint)vdim.x, (GLint)vdim.y);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

View File

@ -11,7 +11,7 @@ ed_load_font_cb(File_Async_Job_Args result, u8* user_data)
invalid_code_path;
}
r32 scale = stbtt_ScaleForPixelHeight(&font, 18.0f);
r32 scale = stbtt_ScaleForPixelHeight(&font, 16.0f);
s32 ascent, descent, line_gap;
stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap);
ui->font_ascent = (r32)ascent * scale;
@ -19,6 +19,8 @@ ed_load_font_cb(File_Async_Job_Args result, u8* user_data)
ui->font_line_gap = (r32)line_gap * scale;
if (ui->font_line_gap == 0) ui->font_line_gap = 5;
ui->font_texture_scale = 2;
scale *= ui->font_texture_scale;
String c = lit_str("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-=_+[]{}\\|;:'\",<.>/?`~");
for (u64 i = 0; i < c.len; i++)
{
@ -28,7 +30,7 @@ ed_load_font_cb(File_Async_Job_Args result, u8* user_data)
s32 x0, y0, x1, y1;
stbtt_GetCodepointBitmapBoxSubpixel(&font, (char)c.str[i], scale, scale, 0, 0, &x0, &y0, &x1, &y1);
v2 offset = (v2){ 0, (r32)y0 };
v2 offset = (v2){ 0, (r32)y0 / ui->font_texture_scale };
texture_atlas_register(&state->editor->ui.atlas, bp, (u32)w, (u32)h, id, offset, TextureAtlasRegistration_PixelFormat_Alpha);
stbtt_FreeBitmap(bp, 0);
}
@ -103,14 +105,15 @@ Shader shd;
Texture tex;
internal void
ed_init(App_State* state)
ed_init(App_State* state, Editor_Desc* desc)
{
lumenarium_env_validate();
Editor* editor = allocator_alloc_struct(permanent, Editor);
zero_struct(*editor);
state->editor = editor;
editor->content_scale = (v2){1,1};
editor->window_dim = desc->init_window_dim;
editor->content_scale = desc->content_scale;
editor->ui = ui_create(4096, 4096, state->input_state, permanent);
editor->ui.draw_panel_cb = ed_draw_panel;
editor->ui.draw_panel_cb_data = (u8*)state;
@ -158,7 +161,7 @@ ed_init(App_State* state)
vertex_attrib_pointer(b, shd, 2, shd.attrs[1], 5, 3);
u32 tex_pix[] = { 0xFFFFFFFF, 0xFF00FFFF, 0xFFFFFF00, 0xFFFF00FF };
tex = texture_create((u8*)tex_pix, 2, 2, 2);
tex = texture_create(texture_desc_default(2, 2), (u8*)tex_pix);
ed_sculpture_visualizer_init(state);
lumenarium_env_validate();
@ -270,27 +273,20 @@ ed_sculpture_updated(App_State* state, r32 scale, r32 led_size)
geo.buffer_index.values,
geo.buffer_index.len
);
for (u32 i = 0; i < 6; i++)
{
u32 index = geo.buffer_index.values[1008 + i];
r32* values = geo.buffer_vertex.values + (index * 5);
printf("%d -> %f %f %f, %f %f\n", index, values[0], values[1], values[2], values[3], values[4]);
}
vertex_attrib_pointer(ed->sculpture_geo, ed->sculpture_shd, 3, ed->sculpture_shd.attrs[0], 5, 0);
vertex_attrib_pointer(ed->sculpture_geo, ed->sculpture_shd, 2, ed->sculpture_shd.attrs[1], 5, 3);
// TODO(PS): make this have enough pixels for the sculpture
// TODO(PS): map leds to pixels
if (ed->sculpture_tex.w != 0)
if (ed->sculpture_tex.desc.w != 0)
{
invalid_code_path;
// TODO(PS): destroy the old texture
}
u8* pixels = ed_leds_to_texture(state, &scratch, pixels_dim);
ed->sculpture_tex = texture_create(pixels, pixels_dim, pixels_dim, pixels_dim);
ed->sculpture_tex = texture_create(texture_desc_default(pixels_dim, pixels_dim), pixels);
scratch_release(scratch);
lumenarium_env_validate();
@ -313,7 +309,7 @@ ed_frame(App_State* state)
{
scratch_get(scratch);
u32 w = ed->sculpture_tex.w;
u32 w = ed->sculpture_tex.desc.w;
u8* pixels = ed_leds_to_texture(state, &scratch, w);
texture_update(ed->sculpture_tex, pixels, w, w, w);
scratch_release(scratch);

View File

@ -3,6 +3,13 @@
#ifndef LUMENARIUM_EDITOR_H
#define LUMENARIUM_EDITOR_H
typedef struct Editor_Desc Editor_Desc;
struct Editor_Desc
{
v2 content_scale;
v2 init_window_dim;
};
typedef struct Editor Editor;
struct Editor
{

View File

@ -1,5 +1,11 @@
#include "lumenarium_editor_sculpture_visualizer_shaders.h"
u32 fbo;
Texture fbo_tex_c;
u32 fbo_rbo;
Geometry_Buffer fs_quad;
Shader fs_shd;
internal void
ed_sculpture_visualizer_init(App_State* state)
{
@ -11,6 +17,64 @@ ed_sculpture_visualizer_init(App_State* state)
String attrs[] = { lit_str("a_pos"), lit_str("a_uv") };
String uniforms[] = { lit_str("proj") };
editor->sculpture_shd = shader_create(vert, frag, attrs, 2, uniforms, 1);
{
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
v2 wds = HMM_MultiplyVec2(editor->window_dim, editor->content_scale);
s32 w = (s32)wds.x;
s32 h = (s32)wds.y;
fbo_tex_c = texture_create(
(Texture_Desc){
.w = w, .h = h, .s = w,
.min_filter = GL_LINEAR,
.mag_filter = GL_LINEAR,
.fmt_internal = GL_RGBA,
.fmt_data = GL_RGBA,
},
0
);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_tex_c.id, 0);
glGenRenderbuffers(1, &fbo_rbo);
glBindRenderbuffer(GL_RENDERBUFFER, fbo_rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo_rbo);
u32 status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
u32 complete = GL_FRAMEBUFFER_COMPLETE;
if (status != complete)
{
#define GL_ENUM_ERROR_CASE(e, msg) case e: { printf("Error: %s - %s\n", #e, msg); } break
switch (status) {
GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_UNDEFINED, "");
GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, "");
GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT, "");
GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER, "");
GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER, "");
GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_UNSUPPORTED, "");
GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE, "");
//GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS, "");
default: {
os_gl_no_error();
} break;
}
printf("Error: unable to complete framebuffer\n");
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
String vert = xplatform_shader_program_get_vert(sculpture_comp_shd);
String frag = xplatform_shader_program_get_frag(sculpture_comp_shd);
String shd_a[] = { lit_str("a_pos"), lit_str("a_uv") };
String shd_u[] = { lit_str("proj") };
fs_shd = shader_create(vert, frag, shd_a, 2, shd_u, 1);
fs_quad = unit_quad_create();
vertex_attrib_pointer(fs_quad, fs_shd, 3, fs_shd.attrs[0], 5, 0);
vertex_attrib_pointer(fs_quad, fs_shd, 2, fs_shd.attrs[1], 5, 3);
}
}
r32 cam_theta = 0;
@ -21,24 +85,11 @@ ed_sculpture_visualizer(App_State* state)
{
Editor* ed = state->editor;
Input_State* in = state->input_state;
u32 delta = 1;
if (input_key_is_down(in, KeyCode_LeftShift) || input_key_is_down(in, KeyCode_RightShift))
{
delta = 100;
}
if (input_key_went_down(in, KeyCode_UpArrow))
{
offset += delta;
printf("%d\n", offset);
}
if (input_key_went_down(in, KeyCode_DownArrow))
{
offset -= delta;
printf("%d\n", offset);
}
offset = clamp(0, offset, ed->sculpture_geo.indices_len);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
os_gl_no_error();
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
// Set the viewport to the current layout's region so that the sculpture
// never overlaps any other ui elements
@ -75,6 +126,19 @@ ed_sculpture_visualizer(App_State* state)
geometry_drawi(ed->sculpture_geo, k, 0);
// reset the viewport for all other rendering
glBindFramebuffer(GL_FRAMEBUFFER, 0);
v2 wds = HMM_MultiplyVec2(ed->window_dim, ed->content_scale);
glViewport(0, 0, (s32)wds.x, (s32)wds.y);
m44 ortho = HMM_Orthographic(0, ed->window_dim.x, ed->window_dim.y, 0, 0.01f, 200.0f);
m44 scale = HMM_Scale((v3){ed->window_dim.x / 2, ed->window_dim.y / 2, 100});
m44 pos = HMM_Translate((v3){ed->window_dim.x / 2, ed->window_dim.y / 2, -99});
m44 model = HMM_MultiplyMat4(pos, scale);
m44 mvp = HMM_MultiplyMat4(ortho, model);
shader_bind(fs_shd);
set_uniform(fs_shd, 0, mvp);
texture_bind(fbo_tex_c);
geometry_bind(fs_quad);
geometry_drawi(fs_quad, 6, 0);
}

View File

@ -62,4 +62,75 @@ global XPlatform_Shader_Program_Src sculpture_shd = {
" gl_FragColor = vec4(uv.x,1,uv.y,1);\n"
"}"
),
};
};
global XPlatform_Shader_Program_Src sculpture_comp_shd = {
.win32_vert = lit_str(""
),
.win32_frag = lit_str(
""
),
.osx_vert = lit_str(
"#version 330 core\n"
"layout (location = 0) in vec3 a_pos;\n"
"layout (location = 1) in vec2 a_uv;\n"
"out vec2 uv;\n"
"uniform mat4 proj;\n"
"void main(void) {\n"
" gl_Position = proj * vec4(a_pos, 1.0);\n"
" uv = a_uv;\n"
"}"
),
.osx_frag = lit_str(
"#version 330 core\n"
"in vec2 uv;\n"
"out vec4 FragColor;\n"
"uniform sampler2D tex;\n"
"float normpdf(in float x, in float sigma) { return 0.39894*exp(-0.5*x*x/(sigma*sigma))/sigma; }\n"
"void main(void) {\n"
" vec4 orig_p = texture(tex, uv);\n"
" vec2 tex_size = textureSize(tex, 0);\n"
" vec2 tex_offset = 1.0 / tex_size;\n"
" "
" const int m_size = 15;\n // must be odd\n"
" const int k_size = (m_size - 1) / 2;\n"
" float kernel[m_size];\n"
" float sigma = 7.0;\n"
" float z = 0;\n"
" for (int i = 0; i <= k_size; i++) {\n"
" float v = normpdf(float(i), sigma);\n"
" kernel[k_size + i] = v; kernel[k_size - i] = v;\n"
" }\n"
" for (int i = 0; i < m_size; i++) {\n"
" z += kernel[i];\n"
" }\n"
" vec3 bloom_acc = vec3(0);\n"
" for (int i = -k_size; i <= k_size; i++) {\n"
" for (int j = -k_size; j <= k_size; j++) {\n"
" vec2 uvp = uv + (vec2(float(i), float(j)) / tex_size);\n"
" bloom_acc += kernel[k_size + j] * kernel[k_size + i] * texture(tex, uvp).xyz;\n"
" }\n"
" }\n"
" vec3 bloom_color = bloom_acc / (z * z);\n"
" vec3 final_color = orig_p.xyz + bloom_color;\n"
// tone mapping
" float exposure = 1.0;\n"
" float gamma = 2.2;\n"
" vec3 result = vec3(1.0) - exp(-final_color * exposure);\n"
// also gamma correct while we're at it
" result = pow(result, vec3(1.0 / gamma));\n"
" FragColor = vec4(result, 1.0);\n"
"}"
),
.wasm_vert = lit_str(
""
),
.wasm_frag = lit_str(
""
),
};

View File

@ -47,7 +47,7 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a)
// Texture Atlas
result.atlas = texture_atlas_create(1024, 1024, 512, permanent);
result.atlas_texture = texture_create(result.atlas.pixels, 1024, 1024, 1024);
result.atlas_texture = texture_create(texture_desc_default(1024, 1024), result.atlas.pixels);
u32 white_sprite[] = {
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
@ -117,8 +117,8 @@ ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint)
result.uv = texture_atlas_sprite_get_uvs(&ui->atlas, sprite);
v3 dim = (v3){
(r32)(sprite.max_x - sprite.min_x),
(r32)(sprite.max_y - sprite.min_y),
(r32)(sprite.max_x - sprite.min_x) / ui->font_texture_scale,
(r32)(sprite.max_y - sprite.min_y) / ui->font_texture_scale,
0,
};
result.pmin = at;
@ -126,7 +126,7 @@ ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint)
result.pmin.XY = v2_floor(result.pmin.XY);
result.pmax = HMM_AddVec3(result.pmin, dim);
result.baseline_after = (v3){ result.pmax.x, at.y, at.z };
result.baseline_after = (v3){ result.pmax.x + 1.5f, at.y, at.z };
return result;
}
@ -193,12 +193,10 @@ ui_draw_panel(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data)
c = PINK_V4;
}
#if 0
ui_sprite_push_color(ui, l0p0, l0p1, sid, c);
ui_sprite_push_color(ui, l1p0, l1p1, sid, c);
ui_sprite_push_color(ui, l2p0, l2p1, sid, c);
ui_sprite_push_color(ui, l3p0, l3p1, sid, c);
#endif
}
internal void

View File

@ -187,6 +187,7 @@ struct UI
Texture_Atlas atlas;
r32 font_ascent, font_descent, font_line_gap, font_space_width;
r32 font_texture_scale;
UI_Widget_Pool widgets;
UI_Style_Sheet* style_sheet;

View File

@ -72,4 +72,5 @@ global XPlatform_Shader_Program_Src ui_shader = {
" gl_FragColor = texture2D(tex, uv) * color;\n"
"}"
),
};
};

View File

@ -2,7 +2,7 @@
#include "user_space/user_space_incenter.cpp"
internal App_State*
lumenarium_init()
lumenarium_init(Editor_Desc* ed_desc)
{
App_State* state = 0;
@ -48,7 +48,7 @@ lumenarium_init()
en_init(state, desc);
if (has_flag(state->flags, AppState_RunEditor)) ed_init(state);
if (has_flag(state->flags, AppState_RunEditor)) ed_init(state, ed_desc);
if (has_flag(state->flags, AppState_RunUserSpace)) incenter_init(state);
scratch_release(scratch);
return state;

View File

@ -289,12 +289,15 @@ int main (int arg_count, char** args)
}
glfwSetErrorCallback(glfw_error_callback);
s32 init_window_width = 1400;
s32 init_window_height = 700;
glfwWindowHint(GLFW_DOUBLEBUFFER, true);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, 1);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow* window = glfwCreateWindow(1400, 700, "Lumenarium", NULL, NULL);
GLFWwindow* window = glfwCreateWindow(init_window_width, init_window_width, "Lumenarium", NULL, NULL);
if (!window)
{
printf("Error: Unable to create a glfw window\n");
@ -310,15 +313,14 @@ int main (int arg_count, char** args)
glfwSetCursorPosCallback(window, cursor_position_callback);
glfwSetScrollCallback(window, scroll_callback);
App_State* state = lumenarium_init();
app_state_data = (u8*)state;
Editor_Desc ed_desc = {};
float xscale, yscale;
glfwGetWindowContentScale(window, &xscale, &yscale);
ed_desc.content_scale = (v2){ xscale, yscale };
ed_desc.init_window_dim = (v2){init_window_width, init_window_height};
if (has_flag(state->flags, AppState_RunEditor))
{
float xscale, yscale;
glfwGetWindowContentScale(window, &xscale, &yscale);
state->editor->content_scale = (v2){ xscale, yscale };
}
App_State* state = lumenarium_init(&ed_desc);
app_state_data = (u8*)state;
bool running = true;
r64 target_seconds_per_frame = 1.0 / 30.0f;