Skip to content

Commit

Permalink
v0.01.009 - Better shaders & Audio fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
asbott committed Sep 25, 2024
1 parent 000312d commit 5d09023
Show file tree
Hide file tree
Showing 14 changed files with 617 additions and 142 deletions.
3 changes: 2 additions & 1 deletion build.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ typedef struct Context_Extra {
// #include "oogabooga/examples/input_example.c"
// #include "oogabooga/examples/sprite_animation.c"
// #include "oogabooga/examples/window_test.c"
#include "oogabooga/examples/offscreen_drawing.c"
// #include "oogabooga/examples/offscreen_drawing.c"
// #include "oogabooga/examples/threaded_drawing.c"
#include "oogabooga/examples/bloom.c"

// These examples require some extensions to be enabled. See top respective files for more info.
// #include "oogabooga/examples/particles_example.c" // Requires OOGABOOGA_EXTENSION_PARTICLES
Expand Down
2 changes: 1 addition & 1 deletion build_release.bat
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ if not exist "build\release" (
pushd build
pushd release

clang -o cgame.exe ../../build.c -Ofast -DNDEBUG -std=c11 -D_CRT_SECURE_NO_WARNINGS -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -Wno-deprecated-declarations -lkernel32 -lgdi32 -luser32 -lruntimeobject -lwinmm -ld3d11 -ldxguid -ld3dcompiler -lshlwapi -lole32 -lshcore -lavrt -lksuser -finline-functions -finline-hint-functions -ffast-math -fno-math-errno -funsafe-math-optimizations -freciprocal-math -ffinite-math-only -fassociative-math -fno-signed-zeros -fno-trapping-math -ftree-vectorize -fomit-frame-pointer -funroll-loops -fno-rtti -fno-exceptions
clang -o cgame.exe ../../build.c -Ofast -DNDEBUG -std=c11 -D_CRT_SECURE_NO_WARNINGS -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -Wno-deprecated-declarations -lkernel32 -lgdi32 -luser32 -lruntimeobject -lwinmm -ld3d11 -ldxguid -ld3dcompiler -lshlwapi -lole32 -lshcore -lavrt -lksuser -finline-functions -finline-hint-functions -ffast-math -fno-math-errno -funsafe-math-optimizations -freciprocal-math -ffinite-math-only -fassociative-math -fno-signed-zeros -fno-trapping-math -ftree-vectorize -fomit-frame-pointer -funroll-loops -fno-rtti -fno-exceptions

popd
popd
18 changes: 16 additions & 2 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## v0.01.009
## v0.01.009 - Better shaders & Audio fixes

- Audio
- Improved spacialization API
Expand All @@ -10,9 +10,23 @@
- Fixed the short fade in/out on pausing/playing without noise
- Added audio_player_transition_to_source() which makes a player fade to another source for a given amount of seconds

- Graphics
- Reworked shader extension system so you can make multiple Gfx_Shader_Extension and use them per Draw_Frame render.
- gfx_destroy_shader_extension()
- gfx_compile_shader_extension()
- Added Draw_Frame.shader_extension (which you set before gfx_render_draw_frame for it to be used)
Not setting this or setting it to {0} means default shader is used.
- Added draw_frame_bind_image_to_shader()
We can now directly bind an image to a hlsl texture slot that we can sample from
- Made examples/bloom.c example which shows how you can do post-processing effects like bloom.
- Fixed a bug where the window would maximize to fill all monitors rather than the current one
- Fixed a bug where when more than 32 images were used in the same Draw_Frame, the wrong textures would start getting drawn.

- Misc
- Added parsing functions string_to_int and string_to_float
- Fixed som bugs
- Fixed Vector(N)64's to actually use float64's instead of float32's (whoops)
- VERTEX_2D_USER_DATA_COUNT -> VERTEX_USER_DATA_COUNT
- Minor refactors, cleanup, bugfixes



Expand Down
19 changes: 18 additions & 1 deletion oogabooga/drawing.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@
#define MAX_Z ((1 << MAX_Z_BITS)/2)
#define Z_STACK_MAX 4096
#define SCISSOR_STACK_MAX 4096
#define MAX_BOUND_IMAGES 16

typedef struct Draw_Quad {
// BEWARE !! These are in ndc
Expand All @@ -181,7 +182,7 @@ typedef struct Draw_Quad {
Vector4 uv;
Vector4 scissor;

Vector4 userdata[VERTEX_2D_USER_DATA_COUNT]; // #Volatile do NOT change this to a pointer
Vector4 userdata[VERTEX_USER_DATA_COUNT]; // #Volatile do NOT change this to a pointer

} Draw_Quad;

Expand All @@ -204,6 +205,11 @@ typedef struct Draw_Frame {
s32 z_stack[Z_STACK_MAX];
bool enable_z_sorting;

Gfx_Shader_Extension shader_extension;

Gfx_Image *bound_images[MAX_BOUND_IMAGES];
int highest_bound_slot_index;

} Draw_Frame;

void draw_frame_init(Draw_Frame *frame) {
Expand Down Expand Up @@ -235,6 +241,17 @@ void draw_frame_reset(Draw_Frame *frame) {
frame->projection
= m4_make_orthographic_projection(-window.width/2, window.width/2, -window.height/2, window.height/2, -1, 10);
frame->camera_xform = m4_scalar(1.0);

frame->highest_bound_slot_index = -1;
}

void draw_frame_bind_image_to_shader(Draw_Frame *frame, Gfx_Image *image, int slot_index) {
if (slot_index >= MAX_BOUND_IMAGES) {
log_error("The highest bind image slot is %i, you tried to bind to %i", MAX_BOUND_IMAGES-1, slot_index);
return;
}
frame->bound_images[slot_index] = image;
frame->highest_bound_slot_index = max(slot_index, frame->highest_bound_slot_index);
}

// This is the global draw frame which is rendered and reset each time you call gfx_update();
Expand Down
278 changes: 278 additions & 0 deletions oogabooga/examples/bloom.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
/*
Screen-space bloom example.
How it works:
1. Draw game to an image (game_image)
2. Draw game to another image, but with a shader that only lets through color values above 1.0 (bloom_map)
3. Draw game_image to a third image (final_image), binding bloom_map to a shader which samples surrounding pixels from the bloom map to create the bloom effect.
4. Draw final_image to the window
*/



Gfx_Shader_Extension load_shader(string file, int cbuffer_size);
void draw_game(Draw_Frame *frame);
bool button(string label, Vector2 pos, Vector2 size, bool enabled);

typedef enum View_Mode {
VIEW_GAME_AFTER_POSTPROCESS,
VIEW_GAME_BEFORE_POSTPROCESS,
VIEW_BLOOM_MAP,

VIEW_MODE_MAX
} View_Mode;

string view_mode_stringify(View_Mode vm) {
switch (vm) {
case VIEW_GAME_AFTER_POSTPROCESS:
return STR("VIEW_GAME_AFTER_POSTPROCESS");
case VIEW_GAME_BEFORE_POSTPROCESS:
return STR("VIEW_GAME_BEFORE_POSTPROCESS");
case VIEW_BLOOM_MAP:
return STR("VIEW_BLOOM_MAP");
default: return STR("");
}
}

// BEWARE std140 packing:
// https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules
typedef struct Scene_Cbuffer {
Vector2 mouse_pos_screen; // We use this to make a light around the mouse cursor
Vector2 window_size; // We only use this to revert the Y in the shader because for some reason d3d11 inverts it.
} Scene_Cbuffer;

Gfx_Font *font;
u32 font_height = 28;
int entry(int argc, char **argv) {

window.title = STR("Bloom example");

font = load_font_from_disk(STR("C:/windows/fonts/arial.ttf"), get_heap_allocator());
assert(font != 0, "Failed loading arial.ttf");

// regular shader + point light which makes things extra bright
Gfx_Shader_Extension light_shader = load_shader(STR("oogabooga/examples/bloom_light.hlsl"), sizeof(Scene_Cbuffer));
// shader used to generate bloom map. Very simple: It takes the output color -1 on all channels
// so all we have left is how much bloom there should be
Gfx_Shader_Extension bloom_map_shader = load_shader(STR("oogabooga/examples/bloom_map.hlsl"), sizeof(Scene_Cbuffer));
// postprocess shader where the bloom happens. It samples from the generated bloom_map.
Gfx_Shader_Extension postprocess_bloom_shader = load_shader(STR("oogabooga/examples/bloom.hlsl"), sizeof(Scene_Cbuffer));

Gfx_Image *bloom_map = 0;
Gfx_Image *game_image = 0;
Gfx_Image *final_image = 0;

View_Mode view = VIEW_GAME_AFTER_POSTPROCESS;

Draw_Frame offscreen_draw_frame;
draw_frame_init(&offscreen_draw_frame);

Scene_Cbuffer scene_cbuffer;

// Window width and height may be 0 before first call to os_update(), and we base render target sizes of window size.
// This is an Oogabooga quirk which might get fixed at some point.
os_update();

while (!window.should_close) {
reset_temporary_storage();


///
// Create bloom map and game image when window size changes (or first time)
local_persist Os_Window last_window;
if ((last_window.width != window.width || last_window.height != window.height || !game_image) && window.width > 0 && window.height > 0) {
if (bloom_map) delete_image(bloom_map);
if (game_image) delete_image(game_image);
if (final_image) delete_image(final_image);

bloom_map = make_image_render_target(window.width, window.height, 4, 0, get_heap_allocator());
game_image = make_image_render_target(window.width, window.height, 4, 0, get_heap_allocator());
final_image = make_image_render_target(window.width, window.height, 4, 0, get_heap_allocator());
}
last_window = window;

// Set stuff in cbuffer which we need to pass to shaders
scene_cbuffer.mouse_pos_screen = v2(input_frame.mouse_x, window.height-input_frame.mouse_y);
scene_cbuffer.window_size = v2(window.width, window.height);

///
// Draw game with light shader to game_image

// Reset draw frame & clear the image with a clear color
draw_frame_reset(&offscreen_draw_frame);
gfx_clear_render_target(game_image, v4(.7, .7, .7, 1.0));

// Draw game things to offscreen Draw_Frame
draw_game(&offscreen_draw_frame);

// Set the shader & cbuffer before the render call
offscreen_draw_frame.shader_extension = light_shader;
offscreen_draw_frame.cbuffer = &scene_cbuffer;

// Render Draw_Frame to the image
///// NOTE: Drawing to one frame like this will wait for the gpu to finish the last draw call. If this becomes
// a performance bottleneck, you would have more frames "in flight" which you cycle through.
gfx_render_draw_frame(&offscreen_draw_frame, game_image);



///
// Draw game with bloom map shader to the bloom map

// Reset draw frame & clear the image
draw_frame_reset(&offscreen_draw_frame);
gfx_clear_render_target(bloom_map, COLOR_BLACK);


// Draw game things to offscreen Draw_Frame
draw_game(&offscreen_draw_frame);

// Set the shader & cbuffer before the render call
offscreen_draw_frame.shader_extension = bloom_map_shader;
offscreen_draw_frame.cbuffer = &scene_cbuffer;

// Render Draw_Frame to the image
///// NOTE: Drawing to one frame like this will wait for the gpu to finish the last draw call. If this becomes
// a performance bottleneck, you would have more frames "in flight" which you cycle through.
gfx_render_draw_frame(&offscreen_draw_frame, bloom_map);

///
// Draw game image into final image, using the bloom shader which samples from the bloom_map

draw_frame_reset(&offscreen_draw_frame);
gfx_clear_render_target(final_image, COLOR_BLACK);

// To sample from another image in the shader, we must bind it to a specific slot.
draw_frame_bind_image_to_shader(&offscreen_draw_frame, bloom_map, 0);

// Draw the game the final image, but now with the post process shader
draw_image_in_frame(game_image, v2(-window.width/2, -window.height/2), v2(window.width, window.height), COLOR_WHITE, &offscreen_draw_frame);

offscreen_draw_frame.shader_extension = postprocess_bloom_shader;
offscreen_draw_frame.cbuffer = &scene_cbuffer;

gfx_render_draw_frame(&offscreen_draw_frame, final_image);


switch (view) {
case VIEW_GAME_AFTER_POSTPROCESS:
Draw_Quad *q = draw_image(final_image, v2(-window.width/2, -window.height/2), v2(window.width, window.height), COLOR_WHITE);
// The draw image will be flipped on y, so we want to draw it "upside down"
swap(q->uv.y, q->uv.w, float);
break;
case VIEW_GAME_BEFORE_POSTPROCESS:
draw_image(game_image, v2(-window.width/2, -window.height/2), v2(window.width, window.height), COLOR_WHITE);
break;
case VIEW_BLOOM_MAP:
draw_image(bloom_map, v2(-window.width/2, -window.height/2), v2(window.width, window.height), COLOR_WHITE);
break;
default: break;
}

for (int i = 0; i < VIEW_MODE_MAX; i += 1) {
if (button(view_mode_stringify(i), v2(-window.width/2+40, window.height/2-100-i*60), v2(500, 50), i == view)) {
view = i;
}
}

os_update();
gfx_update();
}

return 0;
}

Gfx_Shader_Extension load_shader(string file_path, int cbuffer_size) {
string source;

bool ok = os_read_entire_file(file_path, &source, get_heap_allocator());
assert(ok, "Could not read %s", file_path);

Gfx_Shader_Extension shader;
ok = gfx_compile_shader_extension(source, cbuffer_size, &shader);
assert(ok, "Failed compiling shader extension");

return shader;
}

void draw_game(Draw_Frame *frame) {
// Draw a background
draw_rect_in_frame(v2(-window.width/2, -window.height/2), v2(window.width, window.height), v4(.2, .2, .2, 1), frame);

// Draw some random things, with same seed each time so it looks like persistent things
seed_for_random = 69;
for (int i = 0; i < 1000; i += 1) {

// Bias towards dark colors, so the bright bloom will stand out more
bool dark = get_random_float32_in_range(0, 1) < 0.9;

int bright_channels = 0;

if (!dark) {
if (get_random_int_in_range(0, 2)) bright_channels |= (1 << 1);
if (get_random_int_in_range(0, 2)) bright_channels |= (1 << 2);
if (get_random_int_in_range(0, 2)) bright_channels |= (1 << 3);
}

draw_rect_in_frame(v2( // Random pos
get_random_float32_in_range(-window.width/2, window.width/2),
get_random_float32_in_range(-window.height/2, window.height/2)
), v2( // Random size
get_random_float32_in_range(40, 100),
get_random_float32_in_range(40, 100)
), v4( // Random color
(bright_channels & (1 << 1)) ? get_random_float32_in_range(0.95, 1.0) : get_random_float32_in_range(0.0, 0.3),
(bright_channels & (1 << 2)) ? get_random_float32_in_range(0.95, 1.0) : get_random_float32_in_range(0.0, 0.3),
(bright_channels & (1 << 3)) ? get_random_float32_in_range(0.95, 1.0) : get_random_float32_in_range(0.0, 0.3),
1
), frame);

}
}

bool button(string label, Vector2 pos, Vector2 size, bool enabled) {

Vector4 color = v4(.45, .45, .45, 1);

float L = pos.x;
float R = L + size.x;
float B = pos.y;
float T = B + size.y;

float mx = input_frame.mouse_x - window.width/2;
float my = input_frame.mouse_y - window.height/2;

bool pressed = false;

if (mx >= L && mx < R && my >= B && my < T) {
color = v4(.15, .15, .15, 1);
if (is_key_down(MOUSE_BUTTON_LEFT)) {
color = v4(.05, .05, .05, 1);
}

pressed = is_key_just_released(MOUSE_BUTTON_LEFT);
}

if (enabled) {
color = v4_sub(color, v4(.2, .2, .2, 0));
}

draw_rect(pos, size, color);

Gfx_Text_Metrics m = measure_text(font, label, font_height, v2(1, 1));

Vector2 bottom_left = v2_sub(pos, m.functional_pos_min);
bottom_left.x += size.x/2;
bottom_left.x -= m.functional_size.x/2;

bottom_left.y += size.y/2;
bottom_left.y -= m.functional_size.y/2;

draw_text(font, label, font_height, bottom_left, v2(1, 1), COLOR_WHITE);

return pressed;
}
Loading

0 comments on commit 5d09023

Please sign in to comment.