From d2ca5d65061eacbd03e4c553e81d551fb7c39299 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 26 Apr 2023 14:31:00 -0700 Subject: [PATCH] v_draw.cpp, v_draw.hpp: add srb2::Draw, 2D drawing abstraction --- src/CMakeLists.txt | 1 + src/v_draw.cpp | 138 +++++++++++++++++++++++++ src/v_draw.hpp | 234 ++++++++++++++++++++++++++++++++++++++++++ src/v_draw_setter.hpp | 118 +++++++++++++++++++++ 4 files changed, 491 insertions(+) create mode 100644 src/v_draw.cpp create mode 100644 src/v_draw.hpp create mode 100644 src/v_draw_setter.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c1084448f..43ad42f65 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -85,6 +85,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 r_portal.c screen.c taglist.c + v_draw.cpp v_video.cpp s_sound.c sounds.c diff --git a/src/v_draw.cpp b/src/v_draw.cpp new file mode 100644 index 000000000..7b994330e --- /dev/null +++ b/src/v_draw.cpp @@ -0,0 +1,138 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#include "doomdef.h" // skincolornum_t +#include "doomtype.h" +#include "hu_stuff.h" +#include "k_hud.h" +#include "m_fixed.h" +#include "r_draw.h" +#include "v_draw.hpp" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" + +using srb2::Draw; +using Chain = Draw::Chain; + +int Draw::TextElement::width() const +{ + return font_ ? font_width(*font_, default_font_flags(*font_) | flags_.value_or(0), string_.c_str()) / FRACUNIT : 0; +} + +void Chain::patch(patch_t* patch) const +{ + const auto _ = Clipper(*this); + + const bool stretchH = stretch_ == Stretch::kWidth || stretch_ == Stretch::kBoth; + const bool stretchV = stretch_ == Stretch::kHeight || stretch_ == Stretch::kBoth; + + const fixed_t h = stretchH ? FloatToFixed(width_ / patch->width) : FRACUNIT; + const fixed_t v = stretchV ? FloatToFixed(height_ / patch->height) : FRACUNIT; + + V_DrawStretchyFixedPatch(FloatToFixed(x_), FloatToFixed(y_), h * scale_, v * scale_, flags_, patch, colormap_); +} + +void Chain::patch(const char* name) const +{ + patch(static_cast(W_CachePatchName(name, PU_CACHE))); +} + +void Chain::thumbnail(UINT16 mapnum) const +{ + const auto _ = Clipper(*this); + + K_DrawMapThumbnail(FloatToFixed(x_), FloatToFixed(y_), FloatToFixed(width_), flags_, mapnum, colormap_); +} + +void Chain::fill(UINT8 color) const +{ + const auto _ = Clipper(*this); + + V_DrawFill(x_, y_, width_, height_, color); +} + +void Chain::string(const char* str, INT32 flags, Font font) const +{ + const auto _ = Clipper(*this); + + flags |= default_font_flags(font); + + fixed_t x = FloatToFixed(x_); + fixed_t y = FloatToFixed(y_); + + switch (align_) + { + case Align::kLeft: + break; + + case Align::kCenter: + x -= (font_width(font, flags, str) / 2) * scale_; + break; + + case Align::kRight: + x -= font_width(font, flags, str) * scale_; + break; + } + + V_DrawStringScaled(x, y, FloatToFixed(scale_), FRACUNIT, FRACUNIT, flags, colormap_, font_to_fontno(font), str); +} + +Chain::Clipper::Clipper(const Chain& chain) +{ + V_SetClipRect( + FloatToFixed(chain.clipx1_), + FloatToFixed(chain.clipy1_), + FloatToFixed(chain.clipx2_ - chain.clipx1_), + FloatToFixed(chain.clipy2_ - chain.clipy1_), + 0 + ); +} + +Chain::Clipper::~Clipper() +{ + V_ClearClipRect(); +} + +int Draw::font_to_fontno(Font font) +{ + switch (font) + { + case Font::kThin: + return TINY_FONT; + + case Font::kGamemode: + return GM_FONT; + + case Font::kConsole: + return HU_FONT; + + case Font::kFreeplay: + return KART_FONT; + } + + return TINY_FONT; +}; + +INT32 Draw::default_font_flags(Font font) +{ + INT32 flags = V_ALLOWLOWERCASE; + + if (font == Font::kThin) + { + flags |= V_6WIDTHSPACE; + } + + return flags; +}; + +fixed_t Draw::font_width(Font font, INT32 flags, const char* string) +{ + return V_StringScaledWidth(FRACUNIT, FRACUNIT, FRACUNIT, flags, font_to_fontno(font), string); +} diff --git a/src/v_draw.hpp b/src/v_draw.hpp new file mode 100644 index 000000000..7acd82bfb --- /dev/null +++ b/src/v_draw.hpp @@ -0,0 +1,234 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef __V_DRAW_HPP__ +#define __V_DRAW_HPP__ + +#include +#include +#include + +#include + +#include "doomdef.h" // skincolornum_t +#include "doomtype.h" +#include "screen.h" // BASEVIDWIDTH +#include "typedef.h" +#include "v_video.h" + +namespace srb2 +{ + +class Draw +{ +public: + enum class Font + { + kThin, + kGamemode, + kConsole, + kFreeplay, + }; + + enum class Align + { + kLeft, + kCenter, + kRight, + }; + + enum class Stretch + { + kNone, + kWidth, + kHeight, + kBoth, + }; + + class TextElement + { + public: + explicit TextElement(std::string string) : string_(string) {} + + template + explicit TextElement(fmt::format_string format, Args&&... args) : + TextElement(fmt::format(format, args...)) + { + } + + const std::string& string() const { return string_; } + std::optional font() const { return font_; } + std::optional flags() const { return flags_; } + + int width() const; + + TextElement& string(std::string string) + { + string_ = string; + return *this; + } + + TextElement& font(Font font) + { + font_ = font; + return *this; + } + + TextElement& flags(INT32 flags) + { + flags_ = flags; + return *this; + } + + private: + std::string string_; + std::optional font_; + std::optional flags_; + }; + + class Chain + { + public: + float x() const { return x_; } + float y() const { return y_; } + + // Methods add relative to the current state + Chain& x(float x); + Chain& y(float y); + Chain& xy(float x, float y); + Chain& flags(INT32 flags); + + // Methods overwrite the current state + Chain& width(float width); + Chain& height(float height); + Chain& size(float width, float height); + Chain& scale(float scale); + Chain& font(Font font); + Chain& align(Align align); + Chain& stretch(Stretch stretch); + + // Absolute screen coordinates + Chain& clipx(float left, float right); // 0 to BASEVIDWIDTH + Chain& clipy(float top, float bottom); // 0 to BASEVIDHEIGHT + + Chain& clipx() { return clipx(x_, x_ + width_); } + Chain& clipy() { return clipy(y_, y_ + height_); } + + Chain& colormap(const UINT8* colormap); + Chain& colormap(skincolornum_t color); + Chain& colormap(INT32 skin, skincolornum_t color); + Chain& colorize(skincolornum_t color); + + void text(const char* str) const { string(str, flags_, font_); } + void text(const std::string& str) const { text(str.c_str()); } + void text(const TextElement& elm) const + { + string(elm.string().c_str(), elm.flags().value_or(flags_), elm.font().value_or(font_)); + } + + template + void text(fmt::format_string format, Args&&... args) const { text(fmt::format(format, args...)); } + + void patch(patch_t* patch) const; + void patch(const char* name) const; + + void thumbnail(UINT16 mapnum) const; + + void fill(UINT8 color) const; + + private: + constexpr Chain() {} + explicit Chain(float x, float y) : x_(x), y_(y) {} + Chain(const Chain&) = default; + + struct Clipper + { + explicit Clipper(const Chain& chain); + ~Clipper(); + }; + + float x_ = 0.f; + float y_ = 0.f; + float width_ = 0.f; + float height_ = 0.f; + float scale_ = 1.f; + + float clipx1_ = 0.f; + float clipx2_ = BASEVIDWIDTH; + float clipy1_ = 0.f; + float clipy2_ = BASEVIDHEIGHT; + + INT32 flags_ = 0; + + Font font_ = Font::kThin; + Align align_ = Align::kLeft; + Stretch stretch_ = Stretch::kNone; + + const UINT8* colormap_ = nullptr; + + void string(const char* str, INT32 flags, Font font) const; + + friend Draw; + }; + + constexpr Draw() {} + explicit Draw(float x, float y) : chain_(x, y) {} + Draw(const Chain& chain) : chain_(chain) {} + + // See class Chain for documentation + + float x() const { return chain_.x(); } + float y() const { return chain_.y(); } + +#define METHOD(Name) \ + template \ + Chain Name (Args&&... args) const { return Chain(chain_).Name(std::forward(args)...); } + + METHOD(x); + METHOD(y); + METHOD(xy); + METHOD(flags); + METHOD(width); + METHOD(height); + METHOD(size); + METHOD(scale); + METHOD(font); + METHOD(align); + METHOD(stretch); + METHOD(clipx); + METHOD(clipy); + METHOD(colormap); + METHOD(colorize); + +#undef METHOD + +#define VOID_METHOD(Name) \ + template \ + void Name (Args&&... args) const { return chain_.Name(std::forward(args)...); } + + VOID_METHOD(text); + VOID_METHOD(patch); + VOID_METHOD(thumbnail); + VOID_METHOD(fill); + +#undef VOID_METHOD + +private: + Chain chain_; + + static int font_to_fontno(Font font); + static INT32 default_font_flags(Font font); + static fixed_t font_width(Font font, INT32 flags, const char* string); +}; + +#include "v_draw_setter.hpp" + +}; // namespace srb2 + +#endif // __V_DRAW_HPP__ diff --git a/src/v_draw_setter.hpp b/src/v_draw_setter.hpp new file mode 100644 index 000000000..1085d3087 --- /dev/null +++ b/src/v_draw_setter.hpp @@ -0,0 +1,118 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef __V_DRAW_SETTER_HPP__ +#define __V_DRAW_SETTER_HPP__ + +#include "r_draw.h" // R_GetTranslationColormap + +inline Draw::Chain& Draw::Chain::x(float x) +{ + x_ += x; + return *this; +} + +inline Draw::Chain& Draw::Chain::y(float y) +{ + y_ += y; + return *this; +} + +inline Draw::Chain& Draw::Chain::xy(float x, float y) +{ + x_ += x; + y_ += y; + return *this; +} + +inline Draw::Chain& Draw::Chain::flags(INT32 flags) +{ + flags_ |= flags; + return *this; +} + +inline Draw::Chain& Draw::Chain::width(float width) +{ + width_ = width; + return *this; +} + +inline Draw::Chain& Draw::Chain::height(float height) +{ + height_ = height; + return *this; +} + +inline Draw::Chain& Draw::Chain::size(float width, float height) +{ + width_ = width; + height_ = height; + return *this; +} + +inline Draw::Chain& Draw::Chain::scale(float scale) +{ + scale_ = scale; + return *this; +} + +inline Draw::Chain& Draw::Chain::font(Font font) +{ + font_ = font; + return *this; +} + +inline Draw::Chain& Draw::Chain::align(Align align) +{ + align_ = align; + return *this; +} + +inline Draw::Chain& Draw::Chain::stretch(Stretch stretch) +{ + stretch_ = stretch; + return *this; +} + +inline Draw::Chain& Draw::Chain::clipx(float left, float right) +{ + clipx1_ = left; + clipx2_ = right; + return *this; +} + +inline Draw::Chain& Draw::Chain::clipy(float top, float bottom) +{ + clipy1_ = top; + clipy2_ = bottom; + return *this; +} + +inline Draw::Chain& Draw::Chain::colormap(const UINT8* colormap) +{ + colormap_ = colormap; + return *this; +} + +inline Draw::Chain& Draw::Chain::colormap(skincolornum_t color) +{ + return colormap(R_GetTranslationColormap(TC_DEFAULT, color, GTC_CACHE)); +} + +inline Draw::Chain& Draw::Chain::colormap(INT32 skin, skincolornum_t color) +{ + return colormap(R_GetTranslationColormap(skin, color, GTC_CACHE)); +} + +inline Draw::Chain& Draw::Chain::colorize(skincolornum_t color) +{ + return colormap(R_GetTranslationColormap(TC_RAINBOW, color, GTC_CACHE)); +} + +#endif // __V_DRAW_SETTER_HPP__