Skip to content

Commit

Permalink
Fix sub-pixel drawing issues and fix Collision which was working with…
Browse files Browse the repository at this point in the history
… sub-pixel perfect positions
  • Loading branch information
Laguna1989 committed Nov 2, 2023
1 parent 391258e commit f0416b4
Show file tree
Hide file tree
Showing 13 changed files with 114 additions and 83 deletions.
9 changes: 6 additions & 3 deletions impl/jamtemplate/common/collision.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,13 @@ class Collision {
auto const Radius1 = (Obj1Size.x + Obj1Size.y) / 4.0f;
auto const Radius2 = (Obj2Size.x + Obj2Size.y) / 4.0f;

auto const distance = getCenter(obj1) - getCenter(obj2);
auto const center1 = getCenter(obj1);
auto const center2 = getCenter(obj2);
auto const distance = center1 - center2;

return (
jt::MathHelper::lengthSquared(distance) < ((Radius1 + Radius2) * (Radius1 + Radius2)));
auto const thresholdR = (Radius1 + Radius2) * (Radius1 + Radius2);
auto const lengthSquared = jt::MathHelper::lengthSquared(distance);
return (lengthSquared < thresholdR);
}

private:
Expand Down
6 changes: 6 additions & 0 deletions impl/jamtemplate/common/math_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,9 @@ bool jt::MathHelper::checkIsIn(jt::Rectf const& rect, jt::Vector2f const& point)
bool const overlapsY = point.y > rect.top && point.y < rect.top + rect.height;
return (overlapsX && overlapsY);
}

jt::Vector2f jt::MathHelper::castToInteger(jt::Vector2f const& input)
{
return jt::Vector2f { static_cast<float>(static_cast<int>(input.x)),
static_cast<float>(static_cast<int>(input.y)) };
}
5 changes: 5 additions & 0 deletions impl/jamtemplate/common/math_helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ float dot(Vector2f const& a, Vector2f const& b);
/// \return true if point is in rect, false otherwise
bool checkIsIn(jt::Rectf const& rect, jt::Vector2f const& point);

/// Cast a jt::Vector2f to it's integer values. This is needed e.g. for drawing pixel-perfect.
/// \param input input vector
/// \return cast to integer position
jt::Vector2f castToInteger(jt::Vector2f const& input);

} // namespace MathHelper
} // namespace jt

Expand Down
8 changes: 3 additions & 5 deletions impl/jamtemplate/common/screeneffects/star.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@ void jt::Star::setPosition(jt::Vector2f const& screenSizeHint)
if (!m_shape) {
throw std::logic_error { "Cannot set star position before create has been called." };
}
auto p = jt::Random::getRandomPointIn(
jt::Rectf { 0.0f, 0.0f, screenSizeHint.x, screenSizeHint.y });

p.x = static_cast<float>(static_cast<int>(p.x));
p.y = static_cast<float>(static_cast<int>(p.y));
auto const p = jt::MathHelper::castToInteger(
jt::Random::getRandomPointIn(jt::Rectf { 0.0f, 0.0f, screenSizeHint.x, screenSizeHint.y }));

m_shape->setPosition(p);
m_shape->setScreenSizeHint(screenSizeHint);
Expand Down Expand Up @@ -89,6 +86,7 @@ void jt::Star::doDraw() const
m_glow->draw(getGame()->gfx().target());
m_shape->draw(getGame()->gfx().target());
}

void jt::Star::setCamMovementFactor(float factor)
{
m_shape->setCamMovementFactor(factor);
Expand Down
24 changes: 11 additions & 13 deletions impl/jamtemplate/sfml/gfx_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,25 +59,23 @@ void jt::GfxImpl::clear() { m_target->clearPixels(); }

void jt::GfxImpl::display()
{
m_target->forall([this](auto t) {
if (t == nullptr) {
throw std::invalid_argument {
"GfXImpl::display called with nullptr jt::RenderTargetLayer"
};
}
drawOneZLayer(*t);
});
m_target->forall([this](auto& layer) { drawOneZLayer(layer); });
m_window.display();
}

void jt::GfxImpl::drawOneZLayer(jt::RenderTargetLayer& rt)
void jt::GfxImpl::drawOneZLayer(std::shared_ptr<jt::RenderTargetLayer>& layer)
{
// convert renderTarget to sprite and draw that.
auto spriteForDrawing = std::make_unique<Sprite>();
spriteForDrawing->fromTexture(rt.getTexture());
if (layer == nullptr) {
throw std::invalid_argument {
"GfXImpl::display called with nullptr jt::RenderTargetLayer"
};
}
// convert renderTarget to sprite and draw that
auto spriteForDrawing = std::make_unique<jt::Sprite>();
spriteForDrawing->fromTexture(layer->getTexture());
auto const shakeOffset = m_camera.getShakeOffset();
spriteForDrawing->setPosition(shakeOffset);
// Note: RenderTexture has a bug and is displayed upside down.
// Note: RenderTexture has a bug and is displayed upside down
horizontalFlip(spriteForDrawing, m_camera.getZoom(), m_window.getSize().y);
m_window.draw(spriteForDrawing);
}
Expand Down
2 changes: 1 addition & 1 deletion impl/jamtemplate/sfml/gfx_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class GfxImpl : public GfxInterface {
std::shared_ptr<jt::RenderTarget> m_target { nullptr };
std::optional<jt::TextureManagerImpl> m_textureManager;
std::shared_ptr<sf::View> m_view { nullptr };
void drawOneZLayer(jt::RenderTargetLayer& rt);
void drawOneZLayer(std::shared_ptr<jt::RenderTargetLayer>& layer);
};

} // namespace jt
Expand Down
2 changes: 1 addition & 1 deletion impl/jamtemplate/sfml/render_target_lib.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "render_target_lib.hpp"

void jt::RenderTarget::forall(std::function<void(std::shared_ptr<jt::RenderTargetLayer>)> func)
void jt::RenderTarget::forall(std::function<void(std::shared_ptr<jt::RenderTargetLayer>&)> func)
{
for (auto& kvp : m_targets) {
func(kvp.second);
Expand Down
2 changes: 1 addition & 1 deletion impl/jamtemplate/sfml/render_target_lib.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class RenderTarget : public RenderTargetInterface {
public:
std::shared_ptr<jt::RenderTargetLayer> get(int z) override;

void forall(std::function<void(std::shared_ptr<jt::RenderTargetLayer>)> func);
void forall(std::function<void(std::shared_ptr<jt::RenderTargetLayer>&)> func);
void add(int z, std::shared_ptr<jt::RenderTargetLayer> target);
void clearPixels();

Expand Down
64 changes: 34 additions & 30 deletions impl/jamtemplate/sfml/shape.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "shape.hpp"
#include <color_lib.hpp>
#include <math_helper.hpp>
#include <rect_lib.hpp>
#include <vector_lib.hpp>

Expand Down Expand Up @@ -63,17 +64,18 @@ void jt::Shape::setOriginInternal(jt::Vector2f const& origin)
}
}

void jt::Shape::doDraw(std::shared_ptr<jt::RenderTargetLayer> const sptr) const
{
sf::RenderStates states { getSfBlendMode() };
sptr->draw(*m_shape, states);
}

void jt::Shape::doDrawFlash(std::shared_ptr<jt::RenderTargetLayer> const sptr) const
void jt::Shape::doUpdate(float /*elapsed*/)
{
if (sptr) {
sptr->draw(*m_flashShape);
if (!m_shape) {
return;
}

auto const floatPos = getPosition() + getShakeOffset() + getOffset() + getCompleteCamOffset();

auto const screenPosition = jt::MathHelper::castToInteger(floatPos);
m_shape->setPosition(screenPosition.x, screenPosition.y);
m_flashShape->setPosition(screenPosition.x, screenPosition.y);
m_flashShape->setFillColor(toLib(getFlashColor()));
}

void jt::Shape::doDrawShadow(std::shared_ptr<jt::RenderTargetLayer> const sptr) const
Expand All @@ -82,7 +84,7 @@ void jt::Shape::doDrawShadow(std::shared_ptr<jt::RenderTargetLayer> const sptr)
jt::Vector2f const oldPos = fromLib(m_shape->getPosition());
auto const oldCol = fromLib(m_shape->getFillColor());

m_shape->setPosition(toLib(oldPos + getShadowOffset()));
m_shape->setPosition(toLib(jt::MathHelper::castToInteger(oldPos + getShadowOffset())));
m_shape->setFillColor(toLib(getShadowColor()));
sptr->draw(*m_shape);

Expand All @@ -91,25 +93,6 @@ void jt::Shape::doDrawShadow(std::shared_ptr<jt::RenderTargetLayer> const sptr)
}
}

void jt::Shape::doUpdate(float /*elapsed*/)
{
if (m_shape) {
auto const screenPosition
= getPosition() + getShakeOffset() + getOffset() + getCompleteCamOffset();
m_shape->setPosition(screenPosition.x, screenPosition.y);
m_flashShape->setPosition(screenPosition.x, screenPosition.y);
m_flashShape->setFillColor(toLib(getFlashColor()));
}
}

void jt::Shape::doRotate(float rot)
{
if (m_shape) {
m_shape->setRotation(rot);
m_flashShape->setRotation(rot);
}
}

void jt::Shape::doDrawOutline(std::shared_ptr<jt::RenderTargetLayer> const sptr) const
{
jt::Vector2f const oldPos = fromLib(m_shape->getPosition());
Expand All @@ -118,10 +101,31 @@ void jt::Shape::doDrawOutline(std::shared_ptr<jt::RenderTargetLayer> const sptr)
m_shape->setFillColor(toLib(getOutlineColor()));

for (auto const outlineOffset : getOutlineOffsets()) {
m_shape->setPosition(toLib(oldPos + outlineOffset));
m_shape->setPosition(toLib(jt::MathHelper::castToInteger(oldPos + outlineOffset)));
sptr->draw(*m_shape);
}

m_shape->setPosition(toLib(oldPos));
m_shape->setFillColor(toLib(oldCol));
}

void jt::Shape::doDraw(std::shared_ptr<jt::RenderTargetLayer> const sptr) const
{
sf::RenderStates states { getSfBlendMode() };
sptr->draw(*m_shape, states);
}

void jt::Shape::doDrawFlash(std::shared_ptr<jt::RenderTargetLayer> const sptr) const
{
if (sptr) {
sptr->draw(*m_flashShape);
}
}

void jt::Shape::doRotate(float rot)
{
if (m_shape) {
m_shape->setRotation(rot);
m_flashShape->setRotation(rot);
}
}
9 changes: 5 additions & 4 deletions impl/jamtemplate/sfml/sprite.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "sprite.hpp"
#include <color_lib.hpp>
#include <math_helper.hpp>
#include <rect_lib.hpp>
#include <vector_lib.hpp>

Expand Down Expand Up @@ -65,8 +66,8 @@ void jt::Sprite::cleanImage()

void jt::Sprite::doUpdate(float /*elapsed*/)
{
auto const screenPosition
= getPosition() + getShakeOffset() + getOffset() + getCompleteCamOffset();
auto const screenPosition = jt::MathHelper::castToInteger(
getPosition() + getShakeOffset() + getOffset() + getCompleteCamOffset());
m_sprite.setPosition(screenPosition.x, screenPosition.y);
m_flashSprite.setPosition(screenPosition.x, screenPosition.y);
m_flashSprite.setColor(toLib(getFlashColor()));
Expand All @@ -78,7 +79,7 @@ void jt::Sprite::doDrawShadow(std::shared_ptr<jt::RenderTargetLayer> const sptr)
jt::Vector2f const oldPos = fromLib(m_sprite.getPosition());
auto const oldCol = fromLib(m_sprite.getColor());

m_sprite.setPosition(toLib(oldPos + getShadowOffset()));
m_sprite.setPosition(toLib(jt::MathHelper::castToInteger(oldPos + getShadowOffset())));
m_sprite.setColor(toLib(getShadowColor()));
sptr->draw(m_sprite);

Expand All @@ -95,7 +96,7 @@ void jt::Sprite::doDrawOutline(std::shared_ptr<jt::RenderTargetLayer> const sptr
m_sprite.setColor(toLib(getOutlineColor()));

for (auto const outlineOffset : getOutlineOffsets()) {
m_sprite.setPosition(toLib(oldPos + outlineOffset));
m_sprite.setPosition(toLib(jt::MathHelper::castToInteger(oldPos + outlineOffset)));
sptr->draw(m_sprite);
}

Expand Down
28 changes: 9 additions & 19 deletions impl/jamtemplate/sfml/text.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "text.hpp"
#include <color_lib.hpp>
#include <math_helper.hpp>
#include <rect_lib.hpp>
#include <vector_lib.hpp>
#include <iostream>
Expand Down Expand Up @@ -76,14 +77,12 @@ void jt::Text::doUpdate(float /*elapsed*/)
alignOffset.x = -m_text->getGlobalBounds().width;
}

jt::Vector2f const position
= m_position + getShakeOffset() + alignOffset + getCompleteCamOffset();
auto const position = jt::MathHelper::castToInteger(
m_position + getShakeOffset() + alignOffset + getCompleteCamOffset());
// casting to int and back to float avoids blurry text when rendered on non-integer positions
jt::Vector2f const pos = jt::Vector2f { static_cast<float>(static_cast<int>(position.x)),
static_cast<float>(static_cast<int>(position.y)) };

m_text->setPosition(toLib(pos));
m_flashText->setPosition(toLib(pos));
m_text->setPosition(toLib(position));
m_flashText->setPosition(toLib(position));
m_flashText->setScale(m_text->getScale());
}

Expand All @@ -94,10 +93,7 @@ void jt::Text::doDrawShadow(std::shared_ptr<jt::RenderTargetLayer> const sptr) c

auto const position = oldPos + getShadowOffset();

jt::Vector2f const pos = jt::Vector2f { static_cast<float>(static_cast<int>(position.x)),
static_cast<float>(static_cast<int>(position.y)) };

m_text->setPosition(toLib(pos));
m_text->setPosition(toLib(jt::MathHelper::castToInteger(position)));
m_text->setFillColor(toLib(getShadowColor()));
sptr->draw(*m_text);

Expand All @@ -112,15 +108,9 @@ void jt::Text::doDrawOutline(std::shared_ptr<jt::RenderTargetLayer> const sptr)

m_text->setFillColor(toLib(getOutlineColor()));

auto const maxWidth = getOutlineWidth();
for (auto currentWidth = 1; currentWidth != maxWidth + 1; ++currentWidth) {
for (auto i = -currentWidth; i != currentWidth + 1; ++i) {
for (auto j = -currentWidth; j != currentWidth + 1; ++j) {
m_text->setPosition(
toLib(oldPos + jt::Vector2f { static_cast<float>(i), static_cast<float>(j) }));
sptr->draw(*m_text);
}
}
for (auto const outlineOffset : getOutlineOffsets()) {
m_text->setPosition(toLib(jt::MathHelper::castToInteger(oldPos + outlineOffset)));
sptr->draw(*m_text);
}

m_text->setPosition(toLib(oldPos));
Expand Down
15 changes: 9 additions & 6 deletions test/unit/jt_test/common/collision_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ std::shared_ptr<jt::Shape> makeShape(
s->setPosition(jt::Vector2f { px, py });
// note: by default the top left corner of the shape is the origin. This needs to be corrected
// here, so the center of the shape is used.
s->setOffset(jt::Vector2f { -sx / 2.0f, -sy / 2.0f });
s->setOffset(jt::OffsetMode::CENTER);
s->update(0.0f);
return s;
}
Expand All @@ -42,11 +42,14 @@ TEST_P(CollisionCircleNoOverlapPositionParametrizedTestFixture, ShapePtrNoOverla

INSTANTIATE_TEST_SUITE_P(CollisionCircleNoOverlapPositionParametrizedTest,
CollisionCircleNoOverlapPositionParametrizedTestFixture,
::testing::Values(
std::make_pair(makeShapePtr(1.0f, 1.0f, 0.0f, 0.0f), makeShapePtr(1.0f, 1.0f, 1.0f, 1.0f)),
std::make_pair(makeShapePtr(1.0f, 1.0f, 1.0f, 1.0f), makeShapePtr(1.0f, 1.0f, 0.0f, 0.0f)),
std::make_pair(makeShapePtr(1.0f, 1.0f, 0.0f, 0.0f), makeShapePtr(1.0f, 1.0f, 1.0f, 0.0f)),
std::make_pair(makeShapePtr(1.0f, 1.0f, 0.0f, 0.0f), makeShapePtr(1.0f, 1.0f, 0.0f, 1.0f)),
::testing::Values(std::make_pair(makeShapePtr(10.0f, 10.0f, 0.0f, 0.0f),
makeShapePtr(10.0f, 10.0f, 10.0f, 10.0f)),
std::make_pair(
makeShapePtr(10.0f, 10.0f, 10.0f, 10.0f), makeShapePtr(10.0f, 10.0f, 0.0f, 0.0f)),
std::make_pair(
makeShapePtr(10.0f, 10.0f, 0.0f, 0.0f), makeShapePtr(10.0f, 10.0f, 10.0f, 0.0f)),
std::make_pair(
makeShapePtr(10.0f, 10.0f, 0.0f, 0.0f), makeShapePtr(10.0f, 10.0f, 0.0f, 10.0f)),
std::make_pair(
makeShapePtr(99.0f, 99.0f, 0.0f, 0.0f), makeShapePtr(20.0f, 20.0f, 100.0f, 100.0f)),
std::make_pair(
Expand Down
23 changes: 23 additions & 0 deletions test/unit/jt_test/common/math_helper_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ TEST(ClampVectorTest, upperX)
ASSERT_EQ(upper, out.x);
ASSERT_EQ(0.5f, out.y);
}

TEST(ClampVectorTest, upperY)
{
jt::Vector2f const in { 0.5f, 9999.9f };
Expand Down Expand Up @@ -370,3 +371,25 @@ TEST(CheckIsIn, OutsideY)
jt::Vector2f const point { 1.5f, 5.5f };
ASSERT_FALSE(jt::MathHelper::checkIsIn(rect, point));
}

TEST(CastToInteger, CorrectlyCastsDown)
{
std::vector<std::pair<float, float>> const testData {
// clang-format off
std::make_pair(0.0f, 0.0f),
std::make_pair(100.0f, 100.0f),
std::make_pair(0.9f, 0.0f),
std::make_pair(0.1f, 0.0f),
std::make_pair(-0.1f, -0.0f),
std::make_pair(1000.1f, 1000.0f),
// clang-format on
};

for (auto const& kvp : testData) {
auto const castedVector
= jt::MathHelper::castToInteger(jt::Vector2f { kvp.first, kvp.first });
auto const expectedResult = jt::Vector2f { kvp.second, kvp.second };
ASSERT_NEAR(castedVector.x, expectedResult.x, 0.0001f);
ASSERT_NEAR(castedVector.y, expectedResult.y, 0.0001f);
}
}

0 comments on commit f0416b4

Please sign in to comment.