diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d34c87..a471fa6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/include") include(TargetArch) include(FindPkgConfig) -set(common_gcc_flags "-Wall -Wextra -pedantic") +set(common_gcc_flags "-Wall -Wextra -pedantic -Wconversion") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${common_gcc_flags}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${common_gcc_flags}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${common_gcc_flags}") diff --git a/src/gamescenebase.cpp b/src/gamescenebase.cpp index 856fef5..df01248 100644 --- a/src/gamescenebase.cpp +++ b/src/gamescenebase.cpp @@ -1,8 +1,13 @@ #include "gamescenebase.hpp" #include "inputbag.hpp" #include "sdlmain.hpp" +#include "rect_to_sdl.hpp" +#include "rect.hpp" +#include "texture.hpp" #include #include +#include +#include namespace curry { namespace { @@ -38,12 +43,56 @@ namespace curry { } return false; } + + ///---------------------------------------------------------------------- + ///Adjustes parSrc and parDest so that parDest will fit into parClip and + ///parSrc to be the relevant part of the source texture to be drawn. + ///Returns true if such operation was possible and parSrc and parDest + ///got set to a valid value, otherwise false. + ///---------------------------------------------------------------------- + bool clip_rect (Rect& parSrc, Rect& parDest, const Rect& parClip) { + typedef Rect RectFloat; + assert(parSrc.is_valid()); + assert(parDest.is_valid()); + + //If the dest rect is completely out of the clipping region, there + //is nothing to do at all. + if (parDest.to <= parClip.from or parDest.from >= parClip.to) + return false; + + { + const RectFloat clip(vec2f(0.0f), vec2f(1.0f)); + const vec2f srcWidthHeight(parSrc.width_height()); + const vec2f scaledOffs((parDest.from - parClip.from) / parClip.width_height()); + parSrc.from -= vec2f(std::min(0.0f, scaledOffs.x() * srcWidthHeight.x()), std::min(0.0f, scaledOffs.y() * srcWidthHeight.y())); + const vec2f scaledCrop((parClip.to - parDest.to) / parClip.width_height()); + parSrc.to += vec2f(std::min(0.0f, scaledCrop.x() * srcWidthHeight.x()), std::min(0.0f, scaledCrop.y() * srcWidthHeight.y())); + assert(parSrc.is_valid()); + } + + RectFloat dst(parDest.from - parClip.from, parDest.to - parClip.from); + dst.from.x() = std::max(dst.from.x(), parClip.from.x()); + dst.from.y() = std::max(dst.from.y(), parClip.from.y()); + dst.to.x() = std::min(dst.to.x(), parClip.to.x()); + dst.to.y() = std::min(dst.to.y(), parClip.to.y()); + + if (not dst.is_valid()) + return false; + + parDest.from += parClip.from; + parDest.to += parClip.from; + + assert(parDest.is_valid()); + return true; + } } //unnamed namespace GameSceneBase::GameSceneBase (cloonel::SDLMain* parSdlMain) : m_time0(std::chrono::steady_clock::now()), m_input(std::make_unique()), m_sdlmain(parSdlMain), + m_screen_width(static_cast(m_sdlmain->WidthHeight().x())), + m_screen_height(static_cast(m_sdlmain->WidthHeight().y())), m_wants_to_quit(false) { assert(m_sdlmain); @@ -82,6 +131,7 @@ namespace curry { const float delta = std::chrono::duration(time1 - m_time0).count(); m_time0 = time1; this->on_update(delta); + SDL_RenderPresent(this->sdl_main()->GetRenderer()); } cloonel::SDLMain* GameSceneBase::sdl_main() { @@ -91,4 +141,14 @@ namespace curry { cloonel::InputBag& GameSceneBase::input_bag() { return *m_input; } + + void GameSceneBase::draw_clipped (Texture& parTexture, Rect parSrc, Rect parDest) { + const vec2f lefttop(0.0f); + const vec2f rightbottom(m_screen_width, m_screen_height); + clip_rect(parSrc, parDest, Rect(lefttop, rightbottom)); + + auto src = make_sdlrect(parSrc); + auto dst = make_sdlrect(parDest); + SDL_RenderCopy(m_sdlmain->GetRenderer(), parTexture.texture(), &src, &dst); + } } //namespace curry diff --git a/src/gamescenebase.hpp b/src/gamescenebase.hpp index f523e2e..d2a05ca 100644 --- a/src/gamescenebase.hpp +++ b/src/gamescenebase.hpp @@ -9,6 +9,9 @@ namespace cloonel { } //namespace cloonel namespace curry { + class Texture; + template class Rect; + class GameSceneBase { public: explicit GameSceneBase (cloonel::SDLMain* parSdlMain); @@ -28,11 +31,14 @@ namespace curry { virtual void on_update (float parDeltaT) = 0; cloonel::SDLMain* sdl_main(); cloonel::InputBag& input_bag(); + void draw_clipped (Texture& parTexture, Rect parSrc, Rect parDest); private: std::chrono::time_point m_time0; std::unique_ptr m_input; cloonel::SDLMain* m_sdlmain; + float m_screen_width; + float m_screen_height; bool m_wants_to_quit; }; } //namespace curry diff --git a/src/ingamescene.cpp b/src/ingamescene.cpp index a0f6fb2..96f60ab 100644 --- a/src/ingamescene.cpp +++ b/src/ingamescene.cpp @@ -7,6 +7,7 @@ #include "worldgrid.hpp" #include "worldviewport.hpp" #include "texture.hpp" +#include "rect.hpp" #include #include @@ -18,22 +19,12 @@ namespace curry { ActionRight, ActionDown }; - - void clip (SDL_Rect& parSource, SDL_Rect& parDest, const vec2i& parTopLeft, const vec2i& parBotRight) { - if (parDest.x < parTopLeft.x()) { - const auto diff = parTopLeft.x() - parDest.x; - parDest.x = parTopLeft.x(); - parDest.w -= diff; - parSource.x += diff; - parSource.w -= diff; - } - } } //unnamed namespace struct IngameScene::LocalData { LocalData (const vec2us& parTileSize, const vec2us& parTileCount, cloonel::SDLMain* parSDLMain) : world(parTileSize, parTileCount), - viewport(&world, vec2i(parSDLMain->WidthHeight())), + viewport(&world, vec2f(parSDLMain->WidthHeight())), player(parTileSize) { } @@ -82,18 +73,17 @@ namespace curry { if (cloonel::IsPressed(input_bag(), ActionDown)) viewport.set_position(viewport.position() + vec2f(0, speed)); - const auto tilesize(world.tile_size()); + const auto tilesize(static_cast(world.tile_size())); for (auto tile : viewport) { - vec2us idx(tile.index & 1, (tile.index >> 1) & 1); - vec2us src_rect_xy = idx * tilesize; + vec2f idx(static_cast(tile.index & 1), static_cast((tile.index >> 1) & 1)); + vec2f src_rect_xy = idx * tilesize; + vec2f pixel_pos = vector_cast(tile.pixel_pos); //std::cout << "Drawing src " << src_rect_xy << " dst " << tile.pixel_pos << '\n'; - SDL_Rect src{src_rect_xy.x(), src_rect_xy.y(), tilesize.x(), tilesize.y()}; - SDL_Rect dst{tile.pixel_pos.x(), tile.pixel_pos.y(), tilesize.x(), tilesize.y()}; - clip(src, dst, vec2i(0), sdl_main()->WidthHeight()); - SDL_RenderCopy(this->sdl_main()->GetRenderer(), m_local_data->worldtiles.texture(), &src, &dst); + Rect src_rect(src_rect_xy, src_rect_xy + tilesize); + Rect dst_rect(pixel_pos, pixel_pos + tilesize); + this->draw_clipped(m_local_data->worldtiles, src_rect, dst_rect); } - SDL_RenderPresent(this->sdl_main()->GetRenderer()); } void IngameScene::on_destroy() noexcept { diff --git a/src/rect.hpp b/src/rect.hpp new file mode 100644 index 0000000..126c5aa --- /dev/null +++ b/src/rect.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include "vector.hpp" +#if !defined(NDEBUG) +# include +#endif + +namespace curry { + template + class Rect { + public: + typedef vwr::Vec> VecType; + + Rect (T parLeft, T parTop, T parRight, T parBottom); + template + Rect (const vwr::Vec& parLeftTop, const vwr::Vec& parBottomRight); + + T left() const { return from.x(); } + T top() const { return from.y(); } + T right() const { return to.x(); } + T bottom() const { return to.y(); } + T& left() { return from.x(); } + T& top() { return from.y(); } + T& right() { return to.x(); } + T& bottom() { return to.y(); } + T width() const { return right() - left(); } + T height() const { return bottom() - top(); } + VecType width_height() const { return VecType(width(), height()); } + + bool is_valid() const; + + VecType from; + VecType to; + }; + + template + Rect::Rect (T parLeft, T parTop, T parRight, T parBottom) : + from(parLeft, parTop), + to(parRight, parBottom) + { + } + + template + template + Rect::Rect (const vwr::Vec& parLeftTop, const vwr::Vec& parBottomRight) : + Rect(parLeftTop.x(), parLeftTop.y(), parBottomRight.x(), parBottomRight.y()) + { + } + + template + bool Rect::is_valid() const { + return from <= to; + } + +#if !defined(NDEBUG) + template + std::ostream& operator<< (std::ostream& parStream, const Rect& parRect) { + parStream << '{' << parRect.from << '-' << parRect.to << '}'; + return parStream; + } +#endif +} //namespace curry diff --git a/src/rect_to_sdl.hpp b/src/rect_to_sdl.hpp new file mode 100644 index 0000000..88bf9bb --- /dev/null +++ b/src/rect_to_sdl.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "rect.hpp" +#include +#include + +namespace curry { + template + inline SDL_Rect make_sdlrect (const Rect& parOther) { + typedef decltype(std::declval().w) ValueType; + + return SDL_Rect{ + static_cast(parOther.left()), + static_cast(parOther.top()), + static_cast(parOther.width()), + static_cast(parOther.height()) + }; + } +} //namespace curry diff --git a/src/worldviewport.cpp b/src/worldviewport.cpp index 3e286a8..469f749 100644 --- a/src/worldviewport.cpp +++ b/src/worldviewport.cpp @@ -3,7 +3,7 @@ #include namespace curry { - WorldViewport::WorldViewport (WorldGrid* parWorld, vec2i parSize) : + WorldViewport::WorldViewport (WorldGrid* parWorld, vec2f parSize) : m_position(0), m_size(parSize), m_world(parWorld) diff --git a/src/worldviewport.hpp b/src/worldviewport.hpp index eb0bee8..5ebdaae 100644 --- a/src/worldviewport.hpp +++ b/src/worldviewport.hpp @@ -10,7 +10,7 @@ namespace curry { public: typedef TileIterator iterator; - WorldViewport (WorldGrid* parWorld, vec2i parSize); + WorldViewport (WorldGrid* parWorld, vec2f parSize); const WorldGrid* world() const; const vec2f& position() const; const vec2f& size() const;