screensaver/main.c

207 lines
6.2 KiB
C

/*
* This screensaver demonstrates 2D shader effects through multiple textures.
*/
#define SGP_UNIFORM_CONTENT_SLOTS 16
#define SOKOL_IMPL
#include "sokol/sokol_gfx.h"
#include "sokol/sokol_gp.h"
#include "sokol/sokol_app.h"
#include "sokol/sokol_glue.h"
#include "sokol/sokol_log.h"
#include "sokol/sokol_time.h"
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#define STBI_NO_SIMD
#define STBI_ONLY_PNG
#include "stb/stb_image.h"
#define SOKOL_SHDC_IMPL
#include "sample-effect.glsl.h"
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <unistd.h>
static void set_macos_working_directory(void) {
CFBundleRef bundle = CFBundleGetMainBundle();
if (bundle) {
CFStringRef id = CFBundleGetIdentifier(bundle);
if (id) {
CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
if (resourcesURL) {
char path[PATH_MAX];
if (CFURLGetFileSystemRepresentation(resourcesURL, true, (UInt8*)path, PATH_MAX)) {
chdir(path);
}
CFRelease(resourcesURL);
}
}
}
}
#endif
static sg_pipeline pip;
static sg_shader shd;
static sg_image image;
static sg_sampler linear_sampler;
static sg_image perlin_image;
static void event(const sapp_event* ev) {
switch (ev->type) {
case SAPP_EVENTTYPE_KEY_DOWN:
if (ev->key_code == SAPP_KEYCODE_ESCAPE)
sapp_request_quit();
break;
default:
break;
}
}
static void frame(void) {
// begin draw commands queue
int window_width = sapp_width(), window_height = sapp_height();
sgp_begin(window_width, window_height);
float secs = sapp_frame_count() * sapp_frame_duration();
sg_image_desc image_desc = sg_query_image_desc(image);
float window_ratio = window_width / (float)window_height;
float image_ratio = image_desc.width / (float)image_desc.height;
effect_fs_uniforms_t uniforms = {0};
uniforms.iVelocity.x = 0.02f;
uniforms.iVelocity.y = 0.01f;
uniforms.iPressure = 0.3f;
uniforms.iTime = secs;
uniforms.iWarpiness = 0.2f;
uniforms.iRatio = image_ratio;
uniforms.iZoom = 0.4f;
uniforms.iLevel = 1.0f;
sgp_set_pipeline(pip);
sgp_set_pipeline(pip);
sgp_set_uniform(NULL, 0, &uniforms, sizeof(effect_fs_uniforms_t));
sgp_set_image(IMG_iTexChannel0, image);
sgp_set_image(IMG_iTexChannel1, perlin_image);
sgp_set_sampler(SMP_iSmpChannel0, linear_sampler);
sgp_set_sampler(SMP_iSmpChannel1, linear_sampler);
float width = (window_ratio >= image_ratio) ? window_width : image_ratio*window_height;
float height = (window_ratio >= image_ratio) ? window_width/image_ratio : window_height;
sgp_draw_filled_rect(0, 0, width, height);
sgp_reset_image(IMG_iTexChannel0);
sgp_reset_image(IMG_iTexChannel1);
sgp_reset_sampler(SMP_iSmpChannel0);
sgp_reset_sampler(SMP_iSmpChannel1);
sgp_reset_pipeline();
// dispatch draw commands
sg_pass pass = {.swapchain = sglue_swapchain()};
sg_begin_pass(&pass);
sgp_flush();
sgp_end();
sg_end_pass();
sg_commit();
}
static sg_image load_image(const char *filename) {
int width, height, channels;
uint8_t* data = stbi_load(filename, &width, &height, &channels, 4);
sg_image img = {SG_INVALID_ID};
if(!data)
return img;
sg_image_desc image_desc = {0};
image_desc.width = width;
image_desc.height = height;
image_desc.data.subimage[0][0].ptr = data;
image_desc.data.subimage[0][0].size = (size_t)(width * height * 4);
img = sg_make_image(&image_desc);
stbi_image_free(data);
return img;
}
static void init(void) {
#ifdef __APPLE__
set_macos_working_directory();
#endif
// Initialize Sokol GFX
sg_desc sgdesc = {
.environment = sglue_environment(),
.logger.func = slog_func
};
sg_setup(&sgdesc);
if (!sg_isvalid()) {
fprintf(stderr, "Failed to create Sokol GFX context!\n");
exit(-1);
}
// Initialize Sokol GP
sgp_desc sgpdesc = {0};
sgp_setup(&sgpdesc);
if (!sgp_is_valid()) {
fprintf(stderr, "Failed to create Sokol GP context: %s\n", sgp_get_error_message(sgp_get_last_error()));
exit(-1);
}
// Load image
image = load_image("data/images/majora.png");
perlin_image = load_image("data/images/perlin.png");
if (sg_query_image_state(image) != SG_RESOURCESTATE_VALID || sg_query_image_state(perlin_image) != SG_RESOURCESTATE_VALID) {
fprintf(stderr, "failed to load images");
exit(-1);
}
// Create linear sampler
sg_sampler_desc linear_sampler_desc = {
.min_filter = SG_FILTER_LINEAR,
.mag_filter = SG_FILTER_LINEAR,
.wrap_u = SG_WRAP_REPEAT,
.wrap_v = SG_WRAP_REPEAT,
};
linear_sampler = sg_make_sampler(&linear_sampler_desc);
if (sg_query_sampler_state(linear_sampler) != SG_RESOURCESTATE_VALID) {
fprintf(stderr, "failed to create linear sampler");
exit(-1);
}
// Initialize shader
shd = sg_make_shader(effect_program_shader_desc(sg_query_backend()));
if (sg_query_shader_state(shd) != SG_RESOURCESTATE_VALID) {
fprintf(stderr, "failed to make custom pipeline shader\n");
exit(-1);
}
sgp_pipeline_desc pip_desc = {0};
pip_desc.shader = shd;
pip_desc.has_vs_color = true;
pip = sgp_make_pipeline(&pip_desc);
if (sg_query_pipeline_state(pip) != SG_RESOURCESTATE_VALID) {
fprintf(stderr, "failed to make custom pipeline\n");
exit(-1);
}
}
static void cleanup(void) {
sg_destroy_image(image);
sg_destroy_image(perlin_image);
sg_destroy_pipeline(pip);
sgp_shutdown();
sg_shutdown();
}
sapp_desc sokol_main(int argc, char* argv[]) {
(void)argc;
(void)argv;
return (sapp_desc){
.window_title = "Majora's Screensaver",
.width = 700,
.height = 720,
.high_dpi = true,
.init_cb = init,
.event_cb = event,
.frame_cb = frame,
.cleanup_cb = cleanup,
.logger.func = slog_func,
};
}