207 lines
6.2 KiB
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,
|
|
};
|
|
}
|