diff --git a/assets/config.json b/assets/config.json new file mode 100644 index 0000000..cd04e20 --- /dev/null +++ b/assets/config.json @@ -0,0 +1,12 @@ +{ + "sounds": { + "ui_click": "assets/sounds/ui_click.ogg", + "ui_hover": "assets/sounds/ui_hover.ogg", + "blank": "assets/sounds/blank.ogg" + }, + "sprites": { + }, + "graphics": { + "smooth_textures": false + } +} diff --git a/assets/shaders.json b/assets/shaders.json new file mode 100644 index 0000000..825dbba --- /dev/null +++ b/assets/shaders.json @@ -0,0 +1,10 @@ +{ + "ui_shader": { + "file_name": "assets/shaders/ui_shader.frag", + "type": "fragment" + }, + "ERROR": { + "file_name": "assets/shaders/ui_error.frag", + "type": "fragment" + } +} diff --git a/assets/shaders/ui_error.frag b/assets/shaders/ui_error.frag new file mode 100644 index 0000000..29ccc8c --- /dev/null +++ b/assets/shaders/ui_error.frag @@ -0,0 +1,18 @@ +uniform vec2 u_resolution; +uniform vec2 u_mouse; +uniform float u_duration; +uniform float u_time; +uniform float u_time_end; +uniform sampler2D texture; +uniform bool is_shape; + +void main() { + if(is_shape) { + vec4 color = vec4(1.0, 0.0, 0.0, 1.0); + gl_FragColor = gl_Color * color; + } else { + vec4 pixel = texture2D(texture, gl_TexCoord[0].xy); + vec4 color = vec4(1.0, 0.0, 0.0, 1.0); + gl_FragColor = gl_Color * color * pixel; + } +} diff --git a/assets/shaders/ui_shader.frag b/assets/shaders/ui_shader.frag new file mode 100644 index 0000000..73b77b4 --- /dev/null +++ b/assets/shaders/ui_shader.frag @@ -0,0 +1,29 @@ +uniform vec2 u_resolution; +uniform vec2 u_mouse; +uniform float u_duration; +uniform float u_time; +uniform float u_time_end; +uniform sampler2D texture; +uniform bool is_shape; +uniform bool hover; + +vec4 blink() { + if(hover) { + return vec4(0.95, 0.95, 1.0, 1.0); + } else { + float tick = (u_time_end - u_time) / u_duration; + float blink = mix(0.5, 1.0, tick); + return vec4(blink, blink, blink, 1.0); + } +} + +void main() { + vec4 color = blink(); + + if(!is_shape) { + vec4 pixel = texture2D(texture, gl_TexCoord[0].xy); + color *= pixel; + } + + gl_FragColor = gl_Color * color; +} diff --git a/assets/shaders/ui_shape_shader.frag b/assets/shaders/ui_shape_shader.frag new file mode 100644 index 0000000..c16d6ea --- /dev/null +++ b/assets/shaders/ui_shape_shader.frag @@ -0,0 +1,12 @@ +uniform vec2 u_resolution; +uniform vec2 u_mouse; +uniform float u_duration; +uniform float u_time; +uniform float u_time_end; + +void main() { + float tick = (u_time_end - u_time) / u_duration; + float blink = smoothstep(1.0, 0.5, tick); + vec4 color = vec4(blink, blink, blink, 1.0); + gl_FragColor = gl_Color * color; +} diff --git a/assets/sounds/blank.ogg b/assets/sounds/blank.ogg new file mode 100644 index 0000000..3322d4b Binary files /dev/null and b/assets/sounds/blank.ogg differ diff --git a/assets/sounds/clicker_bark.ogg b/assets/sounds/clicker_bark.ogg new file mode 100644 index 0000000..305661f Binary files /dev/null and b/assets/sounds/clicker_bark.ogg differ diff --git a/assets/sounds/ui_click.ogg b/assets/sounds/ui_click.ogg new file mode 100644 index 0000000..7ff2e8c Binary files /dev/null and b/assets/sounds/ui_click.ogg differ diff --git a/assets/sounds/ui_hover.ogg b/assets/sounds/ui_hover.ogg new file mode 100644 index 0000000..be6e679 Binary files /dev/null and b/assets/sounds/ui_hover.ogg differ diff --git a/assets/text.otf b/assets/text.otf new file mode 100644 index 0000000..3094772 Binary files /dev/null and b/assets/text.otf differ diff --git a/meson.build b/meson.build index f5943cb..30de02c 100644 --- a/meson.build +++ b/meson.build @@ -72,13 +72,14 @@ sfml_network = subproject('sfml').get_variable('sfml_network_dep') sfml_system = subproject('sfml').get_variable('sfml_system_dep') sfml_window = subproject('sfml').get_variable('sfml_window_dep') lel_guecs = subproject('lel-guecs').get_variable('lel_guecs_dep') +lel_guecs_sfml = subproject('lel-guecs').get_variable('lel_guecs_sfml_dep') dependencies += [ fmt, freetype2, flac, ogg, vorbis, vorbisfile, vorbisenc, sfml_audio, sfml_graphics, sfml_network, sfml_system, - sfml_window, lel_guecs + sfml_window, lel_guecs, lel_guecs_sfml ] inc_dirs = include_directories('src') @@ -95,11 +96,10 @@ b8rk_dep = declare_dependency( link_with: b8rk_lib, include_directories: inc_dirs) -dependencies += [b8rk_dep] - executable('b8rk', [ 'src/main.cpp' ], cpp_args: cpp_args, link_args: link_args, + include_directories: inc_dirs, override_options: exe_defaults, - dependencies: dependencies) + dependencies: dependencies + [b8rk_dep]) diff --git a/src/code_panel.cpp b/src/code_panel.cpp new file mode 100644 index 0000000..7b25ad9 --- /dev/null +++ b/src/code_panel.cpp @@ -0,0 +1 @@ +#include "code_panel.hpp" diff --git a/src/code_panel.hpp b/src/code_panel.hpp new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/src/code_panel.hpp @@ -0,0 +1 @@ +#pragma once diff --git a/src/display.cpp b/src/display.cpp index 054ae50..d51530d 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -4,10 +4,10 @@ #include Display::Display(sf::RenderWindow& window, sf::Vector2f pos, sf::Vector2f size) : - window(window) + $window(window) { float border_width = 2.0f; - texture.setSmooth(false); + $texture.setSmooth(false); float ratio = (size.x - border_width * 2) / 64.0f; // rectangle position doesn't include the border because everyone hates humans @@ -15,20 +15,22 @@ Display::Display(sf::RenderWindow& window, sf::Vector2f pos, sf::Vector2f size) sf::Vector2f scale{ratio, ratio}; // have to move in one more time for the border we just moved in for the border - texture_sprite.setPosition({border_pos.x + border_width, border_pos.y + border_width}); - texture_sprite.setScale(scale); - - border.setPosition(border_pos); - border.setSize({64 * ratio, 32 * ratio}); - border.setOutlineColor({30,20,50,255}); - border.setOutlineThickness(border_width); - border.setFillColor({0,0,0,255}); + $texture_sprite.setPosition({border_pos.x + border_width, border_pos.y + border_width}); + $texture_sprite.setScale(scale); + + $border.setPosition(border_pos); + $border.setSize({64 * ratio, 32 * ratio}); + $border.setOutlineColor({30,20,50,255}); + $border.setOutlineThickness(border_width); + $border.setFillColor({0,0,0,255}); + + $status.init(); } void Display::handle_inputs(Chip8& vm) { - while (const auto event = window.pollEvent()) { + while (const auto event = $window.pollEvent()) { if(event->is()) { - window.close(); + $window.close(); } if(const auto* key = event->getIf()) { @@ -41,15 +43,17 @@ void Display::handle_inputs(Chip8& vm) { void Display::update(Chip8& vm) { handle_inputs(vm); - texture.update((uint8_t *)(vm.video), {64, 32}, {0,0}); + $texture.update((uint8_t *)(vm.video), {64, 32}, {0,0}); + $status.update(vm); } void Display::render() { - window.draw(border); - window.draw(texture_sprite); - window.display(); + $window.draw($border); + $window.draw($texture_sprite); + $status.render($window); + $window.display(); } bool Display::active() { - return window.isOpen(); + return $window.isOpen(); } diff --git a/src/display.hpp b/src/display.hpp index 4eed9ee..a4339b4 100644 --- a/src/display.hpp +++ b/src/display.hpp @@ -4,12 +4,14 @@ #include #include #include "chip8.hpp" +#include "status_panel.hpp" struct Display { - sf::RenderWindow& window; - sf::Texture texture{sf::Vector2u{64,32}}; - sf::Sprite texture_sprite{texture}; - sf::RectangleShape border; + sf::RenderWindow& $window; + sf::Texture $texture{sf::Vector2u{VIDEO_WIDTH, VIDEO_HEIGHT}}; + sf::Sprite $texture_sprite{$texture}; + sf::RectangleShape $border; + StatusPanel $status; Display(sf::RenderWindow& window, sf::Vector2f pos, sf::Vector2f size); diff --git a/src/main.cpp b/src/main.cpp index fe0d9b4..8d7bb06 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include "chip8.hpp" #include "stats.hpp" #include "display.hpp" +#include int main(int argc, char* argv[]) { if(argc != 4) { @@ -22,6 +23,9 @@ int main(int argc, char* argv[]) { int videoPitch = sizeof(chip8.video[0]) * VIDEO_WIDTH; + sfml::Backend backend; + guecs::init(&backend); + sf::RenderWindow window{sf::VideoMode({1280, 720}), "Chip8 Emulator"}; window.setFramerateLimit(framerate); window.setVerticalSyncEnabled(false); diff --git a/src/meson.build b/src/meson.build index 6d3db8c..f210a25 100644 --- a/src/meson.build +++ b/src/meson.build @@ -4,4 +4,6 @@ sources = files( 'chip8.cpp', 'stats.cpp', 'display.cpp', + 'status_panel.cpp', + 'code_panel.cpp', ) diff --git a/src/status_panel.cpp b/src/status_panel.cpp new file mode 100644 index 0000000..1be1028 --- /dev/null +++ b/src/status_panel.cpp @@ -0,0 +1,52 @@ +#include "status_panel.hpp" +#include + +StatusPanel::StatusPanel() { + $gui.position(1000, 0, 1280 - 1000, 720); + $gui.layout( + "[N|Reg|Stk|Key]" + "[x0|r0|s0|k0]" + "[x1|r1|s1|k1]" + "[x2|r2|s2|k2]" + "[x3|r3|s3|k3]" + "[x4|r4|s4|k4]" + "[x5|r5|s5|k5]" + "[x6|r6|s6|k6]" + "[x7|r7|s7|k7]" + "[x8|r8|s8|k8]" + "[x9|r9|s9|k9]" + "[xA|r10|s10|k10]" + "[xB|r11|s11|k11]" + "[xC|r12|s12|k12]" + "[xD|r13|s13|k13]" + "[xE|r14|s14|k14]" + "[xF|r15|s15|k15]"); +} + +void StatusPanel::init() { + for(auto& [name, cell] : $gui.cells()) { + auto id = $gui.entity(name); + $gui.set(id, {}); + $gui.set(id, {guecs::to_wstring(name)}); + } + + $gui.init(); +} + +void StatusPanel::render(sf::RenderTarget& window) { + $gui.render(window); +} + +void StatusPanel::update(Chip8& vm) { + for(size_t i = 0; i < 16; i++) { + $gui.show_text(fmt::format("r{}", i), fmt::format(L"{}", vm.registers[i])); + } + + for(size_t i = 0; i < 16; i++) { + $gui.show_text(fmt::format("s{}", i), fmt::format(L"{}", vm.stack[i])); + } + + for(size_t i = 0; i < 16; i++) { + $gui.show_text(fmt::format("k{}", i), fmt::format(L"{}", vm.keypad[i])); + } +} diff --git a/src/status_panel.hpp b/src/status_panel.hpp new file mode 100644 index 0000000..6e86a12 --- /dev/null +++ b/src/status_panel.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include "chip8.hpp" + +struct StatusPanel { + guecs::UI $gui; + + StatusPanel(); + + void init(); + void render(sf::RenderTarget& window); + void update(Chip8& vm); +};