mirror of
https://gitlab.com/square-game-liberation-front/F.E.I.S.git
synced 2024-09-23 19:18:28 +02:00
Chord tinting
This commit is contained in:
parent
054a608009
commit
3e863eaed7
@ -11,6 +11,7 @@
|
||||
- Claps and Beats ticks should now be perfectly synced !
|
||||
- Playfield
|
||||
- Pressing `F` displays free buttons (highlights buttons where a new note would create a collision in red)
|
||||
- Chords can now be displayed with a customizable color (Go to `Settings > Playfield`)
|
||||
- Linear View
|
||||
- notes can be selected by dragging a rectangle with the mouse
|
||||
- new settings
|
||||
|
48
assets/shaders/chord_tint.frag
Normal file
48
assets/shaders/chord_tint.frag
Normal file
@ -0,0 +1,48 @@
|
||||
uniform sampler2D texture;
|
||||
uniform vec4 tint;
|
||||
uniform float mix_amount;
|
||||
|
||||
float get_hsy_lightness(in vec3 color) {
|
||||
return 0.299*color.r + 0.587*color.g + 0.114*color.b;
|
||||
}
|
||||
|
||||
void add_hsy_lightness(inout vec3 color, float light) {
|
||||
color.r += light;
|
||||
color.g += light;
|
||||
color.b += light;
|
||||
float l = get_hsy_lightness(color);
|
||||
float n = min(color.r, min(color.g, color.b));
|
||||
float x = max(color.r, max(color.g, color.b));
|
||||
if(n < 0.0) {
|
||||
float iln = 1.0 / (l-n);
|
||||
color.r = l + ((color.r-l) * l) * iln;
|
||||
color.g = l + ((color.g-l) * l) * iln;
|
||||
color.b = l + ((color.b-l) * l) * iln;
|
||||
}
|
||||
if(x > 1.0 && (x-l) > 1e-6) {
|
||||
float il = 1.0 - l;
|
||||
float ixl = 1.0 / (x - l);
|
||||
color.r = l + ((color.r-l) * il) * ixl;
|
||||
color.g = l + ((color.g-l) * il) * ixl;
|
||||
color.b = l + ((color.b-l) * il) * ixl;
|
||||
}
|
||||
}
|
||||
|
||||
void set_hsy_lightness(inout vec3 color, float light) {
|
||||
add_hsy_lightness(color, light - get_hsy_lightness(color));
|
||||
}
|
||||
|
||||
void set_hsy_color(in vec3 src, inout vec3 dest) {
|
||||
float lum = get_hsy_lightness(dest);
|
||||
dest = src;
|
||||
set_hsy_lightness(dest, lum);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// lookup the pixel in the texture
|
||||
vec4 pixel = texture2D(texture, gl_TexCoord[0].xy);
|
||||
vec3 tinted_pixel = vec3(pixel);
|
||||
set_hsy_color(tint.rgb, tinted_pixel);
|
||||
vec3 mixed_rgb_pixel = mix(pixel.rgb, tinted_pixel, mix_amount);
|
||||
gl_FragColor = gl_Color * vec4(mixed_rgb_pixel, pixel.a);
|
||||
}
|
@ -285,6 +285,24 @@ Interval<Fraction> ChartState::visible_beats(const sf::Time& playback_position,
|
||||
void ChartState::update_visible_notes(const sf::Time& playback_position, const better::Timing& timing) {
|
||||
const auto bounds = visible_beats(playback_position, timing);
|
||||
visible_notes = chart.notes->between(bounds);
|
||||
std::map<Fraction, unsigned int> counts;
|
||||
std::for_each(
|
||||
visible_notes.cbegin(),
|
||||
visible_notes.cend(),
|
||||
[&](const auto& it){
|
||||
counts[it.second.get_time()] += 1;
|
||||
}
|
||||
);
|
||||
visible_chords.clear();
|
||||
std::for_each(
|
||||
counts.begin(),
|
||||
counts.end(),
|
||||
[&](const auto& it){
|
||||
if (it.second > 1) {
|
||||
visible_chords.insert(it.first);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
void ChartState::toggle_note(
|
||||
|
@ -58,6 +58,7 @@ struct ChartState {
|
||||
Interval<Fraction> visible_beats(const sf::Time& playback_position, const better::Timing& timing);
|
||||
void update_visible_notes(const sf::Time& playback_position, const better::Timing& timing);
|
||||
better::Notes visible_notes;
|
||||
std::set<Fraction> visible_chords;
|
||||
|
||||
void toggle_note(
|
||||
const sf::Time& playback_position,
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "config.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
|
||||
#include <SFML/Config.hpp>
|
||||
@ -8,6 +9,7 @@
|
||||
#include <toml++/toml.h>
|
||||
#include <variant>
|
||||
|
||||
#include "color.hpp"
|
||||
#include "linear_view_colors.hpp"
|
||||
#include "marker.hpp"
|
||||
#include "nowide/fstream.hpp"
|
||||
@ -145,6 +147,9 @@ void config::Windows::load_from_v1_0_0_table(const toml::table& tbl) {
|
||||
if (auto val = windows_table["show_playfield"].value<bool>()) {
|
||||
show_playfield = *val;
|
||||
}
|
||||
if (auto val = windows_table["show_playfield_settings"].value<bool>()) {
|
||||
show_playfield_settings = *val;
|
||||
}
|
||||
if (auto val = windows_table["show_file_properties"].value<bool>()) {
|
||||
show_file_properties = *val;
|
||||
}
|
||||
@ -189,6 +194,7 @@ void config::Windows::load_from_v1_0_0_table(const toml::table& tbl) {
|
||||
void config::Windows::dump_as_v1_0_0(toml::table& tbl) {
|
||||
tbl.insert_or_assign("windows", toml::table{
|
||||
{"show_playfield", show_playfield},
|
||||
{"show_playfield_settings", show_playfield_settings},
|
||||
{"show_file_properties", show_file_properties},
|
||||
{"show_status", show_status},
|
||||
{"show_playback_status", show_playback_status},
|
||||
@ -206,6 +212,25 @@ void config::Windows::dump_as_v1_0_0(toml::table& tbl) {
|
||||
}
|
||||
|
||||
|
||||
void config::Playfield::load_from_v1_0_0_table(const toml::table& tbl) {
|
||||
const auto playfield_table = tbl["playfield"];
|
||||
if (auto val = playfield_table["color_chords"].value<bool>()) {
|
||||
color_chords = *val;
|
||||
}
|
||||
load_color(playfield_table["chord_color"], chord_color);
|
||||
if (auto val = playfield_table["chord_color_mix_amount"].value<float>()) {
|
||||
chord_color_mix_amount = std::clamp(*val, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void config::Playfield::dump_as_v1_0_0(toml::table& tbl) {
|
||||
tbl.insert_or_assign("playfield", toml::table{
|
||||
{"color_chords", color_chords},
|
||||
{"chord_color", dump_color(chord_color)},
|
||||
{"chord_color_mix_amount", chord_color_mix_amount},
|
||||
});
|
||||
}
|
||||
|
||||
config::Config::Config(const std::filesystem::path& settings) :
|
||||
config_path(settings / "config.toml")
|
||||
{
|
||||
@ -245,6 +270,7 @@ toml::table config::Config::dump_as_v1_0_0() {
|
||||
editor.dump_as_v1_0_0(tbl);
|
||||
sound.dump_as_v1_0_0(tbl);
|
||||
windows.dump_as_v1_0_0(tbl);
|
||||
playfield.dump_as_v1_0_0(tbl);
|
||||
return tbl;
|
||||
}
|
||||
|
||||
@ -266,4 +292,5 @@ void config::Config::load_from_v1_0_0_table(const toml::table& tbl) {
|
||||
editor.load_from_v1_0_0_table(tbl);
|
||||
sound.load_from_v1_0_0_table(tbl);
|
||||
windows.load_from_v1_0_0_table(tbl);
|
||||
playfield.load_from_v1_0_0_table(tbl);
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <SFML/Graphics/Color.hpp>
|
||||
#include <SFML/System/Time.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
@ -58,6 +59,7 @@ namespace config {
|
||||
|
||||
struct Windows {
|
||||
bool show_playfield = true;
|
||||
bool show_playfield_settings = false;
|
||||
bool show_file_properties = false;
|
||||
bool show_status = false;
|
||||
bool show_playback_status = true;
|
||||
@ -76,6 +78,15 @@ namespace config {
|
||||
void dump_as_v1_0_0(toml::table& tbl);
|
||||
};
|
||||
|
||||
struct Playfield {
|
||||
bool color_chords = false;
|
||||
sf::Color chord_color = sf::Color{110, 200, 250, 255};
|
||||
float chord_color_mix_amount = 1.0f;
|
||||
|
||||
void load_from_v1_0_0_table(const toml::table& tbl);
|
||||
void dump_as_v1_0_0(toml::table& tbl);
|
||||
};
|
||||
|
||||
/* RAII-style class that holds settings we wish to save on disk and saves
|
||||
them upon being destroyed */
|
||||
class Config {
|
||||
@ -90,6 +101,7 @@ namespace config {
|
||||
Editor editor;
|
||||
Sound sound;
|
||||
Windows windows;
|
||||
Playfield playfield;
|
||||
|
||||
private:
|
||||
void load_from_v1_0_0_table(const toml::table& tbl);
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <SFML/System/Vector2.hpp>
|
||||
#include <tinyfiledialogs.h>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "custom_sfml_audio/synced_sound_streams.hpp"
|
||||
#include "widgets/linear_view.hpp"
|
||||
#include "better_metadata.hpp"
|
||||
@ -54,6 +55,7 @@ EditorState::EditorState(const std::filesystem::path& assets_, config::Config& c
|
||||
playfield(assets_),
|
||||
linear_view(LinearView{assets_, config_}),
|
||||
show_playfield(config_.windows.show_playfield),
|
||||
show_playfield_settings(config_.windows.show_playfield_settings),
|
||||
show_file_properties(config_.windows.show_file_properties),
|
||||
show_status(config_.windows.show_status),
|
||||
show_playback_status(config_.windows.show_playback_status),
|
||||
@ -89,6 +91,7 @@ EditorState::EditorState(
|
||||
playfield(assets_),
|
||||
linear_view(LinearView{assets_, config_}),
|
||||
show_playfield(config_.windows.show_playfield),
|
||||
show_playfield_settings(config_.windows.show_playfield_settings),
|
||||
show_file_properties(config_.windows.show_file_properties),
|
||||
show_status(config_.windows.show_status),
|
||||
show_playback_status(config_.windows.show_playback_status),
|
||||
@ -494,7 +497,6 @@ void EditorState::display_playfield(const Markers::marker_type& opt_marker, Judg
|
||||
|
||||
float squareSize = ImGui::GetWindowSize().x / 4.f;
|
||||
float TitlebarHeight = ImGui::GetWindowSize().y - ImGui::GetWindowSize().x;
|
||||
int ImGuiIndex = 0;
|
||||
|
||||
if (chart_state and opt_marker) {
|
||||
const auto& marker = **opt_marker;
|
||||
@ -514,26 +516,39 @@ void EditorState::display_playfield(const Markers::marker_type& opt_marker, Judg
|
||||
auto display = VariantVisitor {
|
||||
[&, this](const better::TapNote& tap_note){
|
||||
auto note_offset = (this->current_time() - this->time_at(tap_note.get_time()));
|
||||
const auto t = marker.at(markerEndingState, note_offset);
|
||||
auto t = marker.at(markerEndingState, note_offset);
|
||||
if (t) {
|
||||
ImGui::SetCursorPos({
|
||||
tap_note.get_position().get_x() * squareSize,
|
||||
TitlebarHeight + tap_note.get_position().get_y() * squareSize
|
||||
});
|
||||
ImGui::PushID(ImGuiIndex);
|
||||
ImGui::Image(*t, {squareSize, squareSize});
|
||||
ImGui::PopID();
|
||||
++ImGuiIndex;
|
||||
if (config.playfield.color_chords and chart_state->visible_chords.contains(tap_note.get_time())) {
|
||||
playfield.draw_chord_tap_note(
|
||||
tap_note,
|
||||
current_time(),
|
||||
*applicable_timing,
|
||||
marker,
|
||||
markerEndingState,
|
||||
config.playfield
|
||||
);
|
||||
} else {
|
||||
ImGui::SetCursorPos({
|
||||
tap_note.get_position().get_x() * squareSize,
|
||||
TitlebarHeight + tap_note.get_position().get_y() * squareSize
|
||||
});
|
||||
ImGui::Image(*t, {squareSize, squareSize});
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
[&, this](const better::LongNote& long_note){
|
||||
std::optional<config::Playfield> chord_config;
|
||||
if (config.playfield.color_chords and chart_state->visible_chords.contains(long_note.get_time())) {
|
||||
chord_config = config.playfield;
|
||||
}
|
||||
this->playfield.draw_long_note(
|
||||
long_note,
|
||||
current_time(),
|
||||
*applicable_timing,
|
||||
marker,
|
||||
markerEndingState
|
||||
markerEndingState,
|
||||
chord_config
|
||||
);
|
||||
},
|
||||
};
|
||||
@ -545,7 +560,9 @@ void EditorState::display_playfield(const Markers::marker_type& opt_marker, Judg
|
||||
ImGui::SetCursorPos({0, TitlebarHeight});
|
||||
ImGui::Image(playfield.long_note.layer);
|
||||
ImGui::SetCursorPos({0, TitlebarHeight});
|
||||
ImGui::Image(playfield.marker_layer);
|
||||
ImGui::Image(playfield.long_note_marker_layer);
|
||||
ImGui::SetCursorPos({0, TitlebarHeight});
|
||||
ImGui::Image(playfield.chord_marker_layer);
|
||||
}
|
||||
|
||||
// Display button grid
|
||||
@ -621,10 +638,7 @@ void EditorState::display_playfield(const Markers::marker_type& opt_marker, Judg
|
||||
int x = i % 4;
|
||||
int y = i / 4;
|
||||
ImGui::SetCursorPos({x * squareSize, TitlebarHeight + y * squareSize});
|
||||
ImGui::PushID(ImGuiIndex);
|
||||
ImGui::Image(playfield.note_collision, {squareSize, squareSize});
|
||||
ImGui::PopID();
|
||||
++ImGuiIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@ -635,10 +649,7 @@ void EditorState::display_playfield(const Markers::marker_type& opt_marker, Judg
|
||||
note.get_position().get_x() * squareSize,
|
||||
TitlebarHeight + note.get_position().get_y() * squareSize
|
||||
});
|
||||
ImGui::PushID(ImGuiIndex);
|
||||
ImGui::Image(playfield.note_selected, {squareSize, squareSize});
|
||||
ImGui::PopID();
|
||||
++ImGuiIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -646,9 +657,18 @@ void EditorState::display_playfield(const Markers::marker_type& opt_marker, Judg
|
||||
ImGui::End();
|
||||
};
|
||||
|
||||
/*
|
||||
Display all metadata in an editable form
|
||||
*/
|
||||
void EditorState::display_playfield_settings() {
|
||||
if (ImGui::Begin("Playfield Settings", &show_playfield_settings, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Checkbox("Color Chords", &config.playfield.color_chords);
|
||||
feis::DisabledIf(not config.playfield.color_chords, [&](){
|
||||
feis::ColorEdit4("Chord Color", config.playfield.chord_color);
|
||||
ImGui::SliderFloat("Chord Color Mix Amount", &config.playfield.chord_color_mix_amount, 0.0f, 1.0f);
|
||||
});
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Display all metadata in an editable form
|
||||
void EditorState::display_file_properties() {
|
||||
if (ImGui::Begin(
|
||||
"File Properties",
|
||||
@ -1100,16 +1120,11 @@ void EditorState::display_linear_view() {
|
||||
void EditorState::display_sound_settings() {
|
||||
if (ImGui::Begin("Sound Settings", &show_sound_settings)) {
|
||||
if (ImGui::TreeNodeEx("Music", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
const auto music_exists = music.has_value();
|
||||
if (not music_exists) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
if (ImGui::SliderInt("Volume##Music", &config.sound.music_volume, 0, 10)) {
|
||||
set_volume(config.sound.music_volume);
|
||||
}
|
||||
if (not music_exists) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
feis::DisabledIf(not music.has_value(), [&](){
|
||||
if (ImGui::SliderInt("Volume##Music", &config.sound.music_volume, 0, 10)) {
|
||||
set_volume(config.sound.music_volume);
|
||||
}
|
||||
});
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNodeEx("Beat Tick", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
|
@ -145,6 +145,9 @@ public:
|
||||
bool& show_playfield;
|
||||
void display_playfield(const Markers::marker_type& marker, Judgement markerEndingState);
|
||||
|
||||
bool& show_playfield_settings;
|
||||
void display_playfield_settings();
|
||||
|
||||
bool& show_file_properties;
|
||||
void display_file_properties();
|
||||
|
||||
|
@ -68,6 +68,17 @@ namespace feis {
|
||||
|
||||
bool SquareButton(const char* text);
|
||||
void ColorSquare(const sf::Color& color);
|
||||
|
||||
template<typename Callback>
|
||||
void DisabledIf(const bool disabled, const Callback& cb) {
|
||||
if (disabled) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
cb();
|
||||
if (disabled) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace colors {
|
||||
|
@ -452,6 +452,9 @@ int main() {
|
||||
if (editor_state->chart_state and editor_state->show_playfield) {
|
||||
editor_state->display_playfield(markers.get_chosen_marker(), markerEndingState);
|
||||
}
|
||||
if (editor_state->show_playfield_settings) {
|
||||
editor_state->display_playfield_settings();
|
||||
}
|
||||
if (editor_state->show_linear_view) {
|
||||
editor_state->display_linear_view();
|
||||
}
|
||||
@ -730,6 +733,9 @@ int main() {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Settings", editor_state.has_value())) {
|
||||
if (ImGui::MenuItem("Playfield##Settings")) {
|
||||
editor_state->show_playfield_settings = true;
|
||||
}
|
||||
if (ImGui::MenuItem("Sound")) {
|
||||
editor_state->show_sound_settings = true;
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
#include "playfield.hpp"
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <variant>
|
||||
|
||||
#include "better_note.hpp"
|
||||
#include "config.hpp"
|
||||
#include "toolbox.hpp"
|
||||
#include "utf8_strings.hpp"
|
||||
|
||||
@ -8,8 +13,19 @@ const std::string texture_file = "textures/edit_textures/game_front_edit_tex_1.t
|
||||
Playfield::Playfield(std::filesystem::path assets_folder) :
|
||||
long_note(assets_folder / "textures" / "long"),
|
||||
texture_path(assets_folder / texture_file)
|
||||
{
|
||||
if (!base_texture.load_from_path(texture_path)) {
|
||||
{
|
||||
if (sf::Shader::isAvailable()) {
|
||||
chord_tint_shader.emplace();
|
||||
const auto shader_path = assets_folder / "shaders" / "chord_tint.frag";
|
||||
if (not std::filesystem::is_regular_file(shader_path)) {
|
||||
throw std::runtime_error(fmt::format("File {} does not exist", path_to_utf8_encoded_string(shader_path)));
|
||||
}
|
||||
if (not chord_tint_shader->load_from_path(assets_folder / "shaders" / "chord_tint.frag", sf::Shader::Fragment)) {
|
||||
throw std::runtime_error(fmt::format("Could not open fragment shader {}", path_to_utf8_encoded_string(shader_path)));
|
||||
};
|
||||
chord_tint_shader->setUniform("texture", sf::Shader::CurrentTexture);
|
||||
}
|
||||
if (not base_texture.load_from_path(texture_path)) {
|
||||
std::cerr << "Unable to load texture " << texture_path;
|
||||
throw std::runtime_error("Unable to load texture " + path_to_utf8_encoded_string(texture_path));
|
||||
}
|
||||
@ -27,48 +43,36 @@ Playfield::Playfield(std::filesystem::path assets_folder) :
|
||||
note_collision.setTexture(base_texture);
|
||||
note_collision.setTextureRect({576, 0, 192, 192});
|
||||
|
||||
if (!marker_layer.create(400, 400)) {
|
||||
std::cerr << "Unable to create Playfield's markerLayer";
|
||||
throw std::runtime_error("Unable to create Playfield's markerLayer");
|
||||
if (not long_note_marker_layer.create(400, 400)) {
|
||||
throw std::runtime_error("Unable to create Playfield's long_note_marker_layer");
|
||||
}
|
||||
marker_layer.setSmooth(true);
|
||||
long_note_marker_layer.setSmooth(true);
|
||||
|
||||
if (!long_note.layer.create(400, 400)) {
|
||||
std::cerr << "Unable to create Playfield's longNoteLayer";
|
||||
throw std::runtime_error("Unable to create Playfield's longNoteLayer");
|
||||
if (not long_note.layer.create(400, 400)) {
|
||||
throw std::runtime_error("Unable to create Playfield's long_note.laye");
|
||||
}
|
||||
long_note.layer.setSmooth(true);
|
||||
|
||||
// why do we do this here ?
|
||||
long_note.backgroud.setTexture(*long_note.marker.background_at(sf::Time::Zero));
|
||||
long_note.outline.setTexture(*long_note.marker.outline_at(sf::Time::Zero));
|
||||
long_note.highlight.setTexture(*long_note.marker.highlight_at(sf::Time::Zero));
|
||||
long_note.tail.setTexture(*long_note.marker.tail_at(sf::Time::Zero));
|
||||
long_note.triangle.setTexture(*long_note.marker.triangle_at(sf::Time::Zero));
|
||||
if (not chord_marker_layer.create(400, 400)) {
|
||||
throw std::runtime_error("Unable to create Playfield's chord_marker_layer");
|
||||
}
|
||||
chord_marker_layer.setSmooth(true);
|
||||
}
|
||||
|
||||
void Playfield::resize(unsigned int width) {
|
||||
if (long_note.layer.getSize() != sf::Vector2u(width, width)) {
|
||||
if (!long_note.layer.create(width, width)) {
|
||||
std::cerr << "Unable to resize Playfield's longNoteLayer";
|
||||
throw std::runtime_error(
|
||||
"Unable to resize Playfield's longNoteLayer");
|
||||
const auto _resize = [](auto& tex, unsigned int width){
|
||||
if (tex.getSize() != sf::Vector2u(width, width)) {
|
||||
if (not tex.create(width, width)) {
|
||||
throw std::runtime_error("Unable to resize Playfield texture");
|
||||
}
|
||||
tex.setSmooth(true);
|
||||
}
|
||||
long_note.layer.setSmooth(true);
|
||||
}
|
||||
tex.clear(sf::Color::Transparent);
|
||||
};
|
||||
|
||||
long_note.layer.clear(sf::Color::Transparent);
|
||||
|
||||
if (marker_layer.getSize() != sf::Vector2u(width, width)) {
|
||||
if (!marker_layer.create(width, width)) {
|
||||
std::cerr << "Unable to resize Playfield's markerLayer";
|
||||
throw std::runtime_error(
|
||||
"Unable to resize Playfield's markerLayer");
|
||||
}
|
||||
marker_layer.setSmooth(true);
|
||||
}
|
||||
|
||||
marker_layer.clear(sf::Color::Transparent);
|
||||
_resize(long_note.layer, width);
|
||||
_resize(long_note_marker_layer, width);
|
||||
_resize(chord_marker_layer, width);
|
||||
}
|
||||
|
||||
void Playfield::draw_tail_and_receptor(
|
||||
@ -196,33 +200,31 @@ void Playfield::draw_long_note(
|
||||
const sf::Time& playback_position,
|
||||
const better::Timing& timing,
|
||||
const Marker& marker,
|
||||
const Judgement& markerEndingState
|
||||
const Judgement& marker_ending_state,
|
||||
const std::optional<config::Playfield>& chord_config
|
||||
) {
|
||||
draw_tail_and_receptor(note, playback_position, timing);
|
||||
|
||||
const float square_size = static_cast<float>(long_note.layer.getSize().x) / 4;
|
||||
const auto note_time = timing.time_at(note.get_time());
|
||||
const auto note_offset = playback_position - note_time;
|
||||
|
||||
const auto tail_end = timing.time_at(note.get_end());
|
||||
if (playback_position < tail_end) {
|
||||
// Before or During the long note
|
||||
// Display the beginning marker
|
||||
auto t = marker.at(markerEndingState, note_offset);
|
||||
if (t) {
|
||||
const float x_scale = square_size / t->getTextureRect().width;
|
||||
const float y_scale = square_size / t->getTextureRect().height;
|
||||
t->setScale(x_scale, y_scale);
|
||||
t->setPosition(
|
||||
note.get_position().get_x() * square_size,
|
||||
note.get_position().get_y() * square_size
|
||||
);
|
||||
marker_layer.draw(*t);
|
||||
const auto offset = [&](){
|
||||
if (playback_position < tail_end) {
|
||||
return playback_position - note_time;
|
||||
} else {
|
||||
return playback_position - tail_end;
|
||||
}
|
||||
|
||||
}();
|
||||
if (chord_config.has_value()) {
|
||||
draw_chord_tap_note(
|
||||
offset,
|
||||
note.get_position(),
|
||||
marker,
|
||||
marker_ending_state,
|
||||
*chord_config
|
||||
);
|
||||
} else {
|
||||
const auto tail_end_offset = playback_position - tail_end;
|
||||
auto t = marker.at(markerEndingState, tail_end_offset);
|
||||
auto t = marker.at(marker_ending_state, offset);
|
||||
if (t) {
|
||||
const float x_scale = square_size / t->getTextureRect().width;
|
||||
const float y_scale = square_size / t->getTextureRect().height;
|
||||
@ -231,7 +233,54 @@ void Playfield::draw_long_note(
|
||||
note.get_position().get_x() * square_size,
|
||||
note.get_position().get_y() * square_size
|
||||
);
|
||||
marker_layer.draw(*t);
|
||||
long_note_marker_layer.draw(*t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Playfield::draw_chord_tap_note(
|
||||
const better::TapNote& note,
|
||||
const sf::Time& playback_position,
|
||||
const better::Timing& timing,
|
||||
const Marker& marker,
|
||||
const Judgement& marker_ending_state,
|
||||
const config::Playfield& chord_config
|
||||
) {
|
||||
const auto note_time = timing.time_at(note.get_time());
|
||||
const auto note_offset = playback_position - note_time;
|
||||
draw_chord_tap_note(
|
||||
note_offset,
|
||||
note.get_position(),
|
||||
marker,
|
||||
marker_ending_state,
|
||||
chord_config
|
||||
);
|
||||
}
|
||||
|
||||
void Playfield::draw_chord_tap_note(
|
||||
const sf::Time& offset,
|
||||
const better::Position& position,
|
||||
const Marker& marker,
|
||||
const Judgement& marker_ending_state,
|
||||
const config::Playfield& chord_config
|
||||
) {
|
||||
const float square_size = static_cast<float>(chord_marker_layer.getSize().x) / 4;
|
||||
auto t = marker.at(marker_ending_state, offset);
|
||||
if (t) {
|
||||
const float x_scale = square_size / t->getTextureRect().width;
|
||||
const float y_scale = square_size / t->getTextureRect().height;
|
||||
t->setScale(x_scale, y_scale);
|
||||
t->setPosition(
|
||||
position.get_x() * square_size,
|
||||
position.get_y() * square_size
|
||||
);
|
||||
if (chord_tint_shader) {
|
||||
chord_tint_shader->setUniform("tint", sf::Glsl::Vec4(chord_config.chord_color));
|
||||
chord_tint_shader->setUniform("mix_amount", chord_config.chord_color_mix_amount);
|
||||
chord_marker_layer.draw(*t, &chord_tint_shader.value());
|
||||
} else {
|
||||
t->setColor(chord_config.chord_color);
|
||||
chord_marker_layer.draw(*t);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "better_note.hpp"
|
||||
#include "better_timing.hpp"
|
||||
#include "config.hpp"
|
||||
#include "ln_marker.hpp"
|
||||
#include "marker.hpp"
|
||||
#include "utf8_sfml_redefinitions.hpp"
|
||||
@ -22,8 +23,8 @@ public:
|
||||
sf::Sprite note_selected;
|
||||
sf::Sprite note_collision;
|
||||
|
||||
sf::RenderTexture marker_layer;
|
||||
sf::Sprite marker_sprite;
|
||||
sf::RenderTexture long_note_marker_layer;
|
||||
sf::RenderTexture chord_marker_layer;
|
||||
|
||||
struct LongNote {
|
||||
template<typename ...Ts>
|
||||
@ -50,12 +51,32 @@ public:
|
||||
|
||||
void draw_long_note(
|
||||
const better::LongNote& note,
|
||||
const sf::Time& playback_position,
|
||||
const better::Timing& timing,
|
||||
const Marker& marker,
|
||||
const Judgement& marker_ending_state,
|
||||
const std::optional<config::Playfield>& config
|
||||
);
|
||||
|
||||
void draw_chord_tap_note(
|
||||
const better::TapNote& note,
|
||||
const sf::Time& playbackPosition,
|
||||
const better::Timing& timing,
|
||||
const Marker& marker,
|
||||
const Judgement& markerEndingState
|
||||
const Judgement& markerEndingState,
|
||||
const config::Playfield& config
|
||||
);
|
||||
|
||||
private:
|
||||
|
||||
void draw_chord_tap_note(
|
||||
const sf::Time& offset,
|
||||
const better::Position& position,
|
||||
const Marker& marker,
|
||||
const Judgement& marker_ending_state,
|
||||
const config::Playfield& config
|
||||
);
|
||||
|
||||
const std::filesystem::path texture_path;
|
||||
std::optional<feis::Shader> chord_tint_shader;
|
||||
};
|
||||
|
@ -19,12 +19,13 @@ namespace feis {
|
||||
template<class T>
|
||||
class UTF8Loader : public T {
|
||||
public:
|
||||
bool load_from_path(const std::filesystem::path& file) {
|
||||
template<typename... Ts>
|
||||
bool load_from_path(const std::filesystem::path& file, const Ts&... args) {
|
||||
UTF8FileInputStream f;
|
||||
if (not f.open(file)) {
|
||||
return false;
|
||||
}
|
||||
return this->loadFromStream(f);
|
||||
return this->loadFromStream(f, args...);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <SFML/Audio.hpp>
|
||||
#include <SFML/Audio/InputSoundFile.hpp>
|
||||
#include <SFML/Audio/SoundBuffer.hpp>
|
||||
#include <SFML/Graphics/Shader.hpp>
|
||||
#include <SFML/Graphics/Texture.hpp>
|
||||
|
||||
#include "utf8_sfml.hpp"
|
||||
@ -12,4 +13,5 @@ namespace feis {
|
||||
using InputSoundFile = feis::UTF8Streamer<sf::InputSoundFile>;
|
||||
using SoundBuffer = feis::UTF8Loader<sf::SoundBuffer>;
|
||||
using Texture = feis::UTF8Loader<sf::Texture>;
|
||||
using Shader = feis::UTF8Loader<sf::Shader>;
|
||||
}
|
Loading…
Reference in New Issue
Block a user