New collision detection

This commit is contained in:
zv0n 2021-03-13 15:05:16 +01:00
parent 258ce51cfe
commit d2cf54556e
12 changed files with 186 additions and 125 deletions

View File

@ -1,47 +1,34 @@
#include "sdlpp_circlecolider.hpp"
#include "sdlpp_geometry.hpp"
namespace SDLPP {
CircleColider::CircleColider( double x, double y, double rad )
: CircleColider( {x, y}, rad ) {};
: CircleColider( { x, y }, rad ){};
CircleColider::CircleColider( const Vec2D< double > &center, double rad )
: CollisionPolygon( center ), original_rad( rad ) {}
bool CircleColider::colidesWith( const SDLPP::CollisionPolygon &other ) const {
if ( other.isCircle() ) {
int otherRad = ( other.rightmost() - other.leftmost() ) / 2;
/* check if distance of centers is lower than radiuses added together */
int otherRad =
dynamic_cast< const SDLPP::CircleColider & >( other ).getRadius();
int thisRad = getRadius();
int totalDist = otherRad + thisRad;
int xdiff = other.leftmost() + otherRad - ( leftmost() + thisRad );
int ydiff = other.topmost() + otherRad - ( topmost() + thisRad );
int xdiff = other.getX() - getX();
int ydiff = other.getY() - getY();
return ( xdiff * xdiff + ydiff * ydiff ) <= totalDist * totalDist;
} else if ( other.isInfinite() ) {
return infinityIntersection( other, *this );
}
int rad = rad_;
int centerx = getX();
int centery = getY();
if ( other.topmost() <= centery && other.bottommost() >= centery ) {
return other.leftmost() <= rightmost() &&
other.rightmost() >= leftmost();
} else if ( other.leftmost() <= centerx && other.rightmost() >= centerx ) {
return other.topmost() <= bottommost() &&
other.bottommost() >= topmost();
/* other is not a circle */
int rad = getRadius();
for ( auto &line : other.getLines() ) {
auto distance = pointLineDistance( position, line );
if ( distance * distance <= rad * rad )
return true;
}
int pointx = 0, pointy = 0;
if ( centerx > other.rightmost() ) {
pointx = other.rightmost();
} else {
pointx = other.leftmost();
}
if ( centery < other.topmost() ) {
pointy = other.topmost();
} else {
pointy = other.bottommost();
}
int distancesquared = ( pointx - centerx ) * ( pointx - centerx ) +
( pointy - centery ) * ( pointy - centery );
return distancesquared <= rad * rad;
return false;
}
bool CircleColider::isCircle() const {
@ -61,12 +48,12 @@ int CircleColider::rightmost() const {
}
void CircleColider::updateCollision( int x, int y, int w, int h ) {
position = Vec2D<int> ( original.getX() * w + x, original.getY() * h + y );
position = Vec2D< int >( original.getX() * w + x, original.getY() * h + y );
rad_ = original_rad * w;
}
void CircleColider::render( Renderer &renderer,
const std::tuple< int, int, int, int > &color ) {
void CircleColider::render( Renderer &renderer, const SDL_Color &color,
const SDL_Color &outline_color ) {
std::vector< int > rect = { leftmost(), topmost(), rightmost(),
bottommost() };
auto center_x = getX();
@ -76,14 +63,13 @@ void CircleColider::render( Renderer &renderer,
auto xdiff = center_x - i;
auto xdist = xdiff * xdiff;
auto allowed_rad = sqrt( radsq - xdist );
SDL_SetRenderDrawColor( renderer.getRendererPtr(),
std::get< 0 >( color ), std::get< 1 >( color ),
std::get< 2 >( color ), 0x40 );
SDL_SetRenderDrawColor( renderer.getRendererPtr(), color.r, color.g,
color.b, color.a );
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
center_y - allowed_rad, i, center_y + allowed_rad );
SDL_SetRenderDrawColor( renderer.getRendererPtr(),
std::get< 0 >( color ), std::get< 1 >( color ),
std::get< 2 >( color ), 0x80 );
SDL_SetRenderDrawColor( renderer.getRendererPtr(), outline_color.r,
outline_color.g, outline_color.b,
outline_color.a );
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
center_y - allowed_rad, i,
center_y - allowed_rad + 2 );
@ -91,39 +77,18 @@ void CircleColider::render( Renderer &renderer,
center_y + allowed_rad, i,
center_y + allowed_rad - 2 );
}
SDL_SetRenderDrawColor( renderer.getRendererPtr(), 0xFF, 0, 0, 0xFF );
SDL_RenderDrawLine( renderer.getRendererPtr(), center_x, center_y,
center_x + rad_, center_y );
SDL_RenderDrawLine( renderer.getRendererPtr(), center_x, center_y, center_x,
center_y + rad_ );
SDL_RenderDrawLine( renderer.getRendererPtr(), center_x, center_y,
center_x - rad_, center_y );
SDL_RenderDrawLine( renderer.getRendererPtr(), center_x, center_y, center_x,
center_y - rad_ );
}
void CircleColider::render( Renderer &renderer, const SDL_Color &color ) {
auto input_color = color;
auto outline_color = color;
input_color.a = 0x40;
outline_color.a = 0x80;
render( renderer, input_color, outline_color );
}
void CircleColider::render( Renderer &renderer ) {
std::vector< int > rect = { leftmost(), topmost(), rightmost(),
bottommost() };
auto center_x = getX();
auto center_y = getY();
auto radsq = rad_ * rad_;
for ( int i = rect[0]; i <= rect[2]; i++ ) {
auto xdiff = center_x - i;
auto xdist = xdiff * xdiff;
auto allowed_rad = sqrt( radsq - xdist );
SDL_SetRenderDrawColor( renderer.getRendererPtr(), sdl_color.r,
sdl_color.g, sdl_color.b, sdl_color.a );
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
center_y - allowed_rad, i, center_y + allowed_rad );
SDL_SetRenderDrawColor( renderer.getRendererPtr(), sdl_outline.r,
sdl_outline.g, sdl_outline.b, sdl_outline.a );
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
center_y - allowed_rad, i,
center_y - allowed_rad + 2 );
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
center_y + allowed_rad, i,
center_y + allowed_rad - 2 );
}
render( renderer, sdl_color, sdl_outline );
}
int CircleColider::getRadius() const {
@ -133,4 +98,8 @@ int CircleColider::getRadius() const {
std::shared_ptr< CollisionPolygon > CircleColider::copySelf() {
return std::make_shared< CircleColider >( *this );
}
std::vector< Line< int > > CircleColider::getLines() const {
return {};
}
} // namespace SDLPP

View File

@ -21,11 +21,12 @@ public:
virtual int rightmost() const override;
virtual void updateCollision( int x, int y, int w, int h ) override;
virtual void
render( Renderer &renderer,
const std::tuple< int, int, int, int > &color ) override;
virtual void render( Renderer &renderer, const SDL_Color &color,
const SDL_Color &outline_color ) override;
virtual void render( Renderer &renderer, const SDL_Color &color ) override;
virtual void render( Renderer &renderer ) override;
virtual std::shared_ptr<CollisionPolygon> copySelf() override;
virtual std::vector<Line<int>> getLines() const override;
private:
int getRadius() const;

View File

@ -28,7 +28,7 @@ CircleRender::CircleRender( Vec2D< double > center, double rad,
CircleRender::CircleRender( Vec2D< double > center, double rad,
std::shared_ptr< Renderer > &r,
std::shared_ptr< Texture > &t )
std::shared_ptr< Texture > &/*UNUSED*/ )
: CircleRender( center, rad, r ) {
throw "I don't support textures yet!!!";
}

View File

@ -1,4 +1,5 @@
#include "sdlpp_collision.hpp"
#include "sdlpp_geometry.hpp"
namespace SDLPP {
CollisionPolygon::CollisionPolygon( double x, double y )
@ -53,8 +54,14 @@ bool infinityIntersection( const SDLPP::CollisionPolygon &infinite,
bool intersects( const SDLPP::CollisionPolygon &p1,
const SDLPP::CollisionPolygon &p2 ) {
return !(
p1.rightmost() < p2.leftmost() || p2.rightmost() < p1.leftmost() ||
p1.topmost() > p2.bottommost() || p2.topmost() > p1.bottommost() );
if(p1.rightmost() < p2.leftmost() || p2.rightmost() < p1.leftmost() || p1.bottommost() < p2.topmost() || p2.bottommost() < p1.topmost())
return false;
for(auto &line : p1.getLines()) {
for(auto &line2 : p2.getLines()) {
if(linesCross(line, line2))
return true;
}
}
return false;
}
} // namespace SDLPP

View File

@ -4,13 +4,15 @@
#include "sdlpp_common.hpp"
#include "sdlpp_renderer.hpp"
#include "sdlpp_vector.hpp"
#include "sdlpp_line.hpp"
#include <memory>
#include <vector>
namespace SDLPP {
class SDLPPSCOPE CollisionPolygon {
public:
CollisionPolygon( double x, double y );
CollisionPolygon( const Vec2D<double> &input );
CollisionPolygon( const Vec2D< double > &input );
virtual ~CollisionPolygon() {}
virtual bool colidesWith( const CollisionPolygon &other ) const = 0;
virtual bool isCircle() const = 0;
@ -21,18 +23,20 @@ public:
virtual int leftmost() const = 0;
virtual int rightmost() const = 0;
virtual void updateCollision( int x, int y, int w, int h );
virtual void render( Renderer &renderer,
const std::tuple< int, int, int, int > &color ) = 0;
virtual void render( Renderer &renderer, const SDL_Color &color,
const SDL_Color &outline_color ) = 0;
virtual void render( Renderer &renderer, const SDL_Color &color ) = 0;
virtual void render( Renderer &renderer ) = 0;
int getX() const;
int getY() const;
void setColor( const std::string &color );
void setOutlineColor( const std::string &color );
virtual std::shared_ptr< CollisionPolygon > copySelf() = 0;
virtual std::vector< Line< int > > getLines() const = 0;
protected:
Vec2D<double> original;
Vec2D<int> position;
Vec2D< double > original;
Vec2D< int > position;
bool infinite = false;
SDL_Color sdl_color = { 0, 0, 0, 0 };
SDL_Color sdl_outline = { 0, 0, 0, 0 };

View File

@ -5,10 +5,12 @@
#include "sdlpp_vector.hpp"
#include "sdlpp_line.hpp"
#include <algorithm>
#include <iostream>
namespace SDLPP {
template<typename T>
Vec2D<T> pointProjectionOnLine( const Vec2D<T> &point, const Line<T> &line ) {
template < typename T >
Vec2D< T > pointProjectionOnLine( const Vec2D< T > &point,
const Line< T > &line ) {
/* from here -
* https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
*/
@ -21,10 +23,49 @@ Vec2D<T> pointProjectionOnLine( const Vec2D<T> &point, const Line<T> &line ) {
length_squared ) );
return line.getStart() + t * ( line.getEnd() - line.getStart() );
}
template<typename T>
double pointLineDistance( const Vec2D<T> &point, const Line<T> &line ) {
template < typename T >
double pointLineDistance( const Vec2D< T > &point, const Line< T > &line ) {
return vecDistance( point, pointProjectionOnLine( point, line ) );
}
template < typename T >
int _determinant( const Line< T > &line, const Vec2D< T > &point ) {
auto res = ( line.getEnd().getX() - line.getStart().getX() ) *
( point.getY() - line.getStart().getY() ) -
( line.getEnd().getY() - line.getStart().getY() ) *
( point.getX() - line.getStart().getX() );
if ( res < 0 )
return -1;
if ( res > 0 )
return 1;
return 0;
}
template < typename T >
bool linesCross( const Line< T > &a, const Line< T > &b ) {
auto det_a_s = _determinant( a, b.getStart() );
auto det_a_e = _determinant( a, b.getEnd() );
if ( det_a_s == det_a_e && det_a_s != 0 )
return false;
// det_a_s == det_a_e == 0, means that lines have the same vector,
// we need to find out if they overlap
if ( det_a_s == det_a_e ) {
if ( a.getStart().getX() == a.getEnd().getX() ) {
// vertical lines
return a.bottomost().getY() > b.topmost().getY() &&
a.topmost().getY() < b.bottomost().getY();
} else {
// horizontal lines
return a.leftmost().getX() < b.rightmost().getX() &&
a.rightmost().getX() > b.leftmost().getX();
}
}
auto det_b_s = _determinant( b, a.getStart() );
auto det_b_e = _determinant( b, a.getEnd() );
if ( det_b_s == det_b_e )
return false;
return true;
}
} // namespace SDLPP
#endif

View File

@ -3,6 +3,7 @@
#include "sdlpp_common.hpp"
#include "sdlpp_vector.hpp"
#include <iostream>
namespace SDLPP {
template<typename T>
@ -11,14 +12,38 @@ public:
Line() = delete;
~Line() = default;
Line( const Vec2D<T> &start, const Vec2D<T> &end )
: _start( start ), _end( end ) {}
: _start( start ), _end( end ) {
updateMost();
}
Line( T x_1, T y_1, T x_2, T y_2 )
: _start( { x_1, y_1 } ), _end( { x_2, y_2 } ) {}
: Line( { x_1, y_1 }, { x_2, y_2 } ) {}
Line( const Vec2D<T> &start, const Vec2D<T> &end, bool infinite )
: _start( start ), _end( end ), _infinite( infinite ) {}
: Line( start, end ), _infinite( infinite ) {}
Line( T x_1, T y_1, T x_2, T y_2, bool infinite )
: _start( { x_1, y_1 } ), _end( { x_2, y_2 } ),
_infinite( infinite ) {}
: Line( { x_1, y_1 }, { x_2, y_2 }, infinite ) {}
Line(const Line &input) : Line(input.getStart(), input.getEnd()) {}
Line &operator=(const Line &input) {
_start = input.getStart();
_end = input.getEnd();
updateMost();
return *this;
}
void updateMost() {
if(_start.getY() < _end.getY()) {
top = &_start;
bottom = &_end;
} else {
top = &_end;
bottom = &_start;
}
if(_start.getX() < _end.getX()) {
left = &_start;
right = &_end;
} else {
left = &_end;
right = &_start;
}
}
const Vec2D<T> &getStart() const {
return _start;
}
@ -41,10 +66,26 @@ public:
_start += vec;
_end += vec;
}
const Vec2D<T> &topmost() const {
return *top;
}
const Vec2D<T> &bottomost() const {
return *bottom;
}
const Vec2D<T> &leftmost() const {
return *left;
}
const Vec2D<T> &rightmost() const {
return *right;
}
private:
Vec2D<T> _start;
Vec2D<T> _end;
Vec2D<T> *top = nullptr;
Vec2D<T> *bottom = nullptr;
Vec2D<T> *left = nullptr;
Vec2D<T> *right = nullptr;
bool _infinite = false;
};
} // namespace SDLPP

View File

@ -62,29 +62,27 @@ void RectColider::updateCollision( int x, int y, int w, int h ) {
_size_pixel = Vec2D< int >( width() * w, height() * h );
}
void RectColider::render( Renderer &renderer,
const std::tuple< int, int, int, int > &color ) {
void RectColider::render( Renderer &renderer, const SDL_Color &color,
const SDL_Color &outline_color ) {
auto rect = getRect();
// outline with desired color at 50% opacity
SDL_SetRenderDrawColor( renderer.getRendererPtr(), std::get< 0 >( color ),
std::get< 1 >( color ), std::get< 2 >( color ),
0x80 );
SDL_RenderDrawRect( renderer.getRendererPtr(), &rect );
// fill with desired color at 25% opacity
SDL_SetRenderDrawColor( renderer.getRendererPtr(), std::get< 0 >( color ),
std::get< 1 >( color ), std::get< 2 >( color ),
0x40 );
SDL_SetRenderDrawColor( renderer.getRendererPtr(), color.r, color.g,
color.b, color.a );
SDL_RenderFillRect( renderer.getRendererPtr(), &rect );
SDL_SetRenderDrawColor( renderer.getRendererPtr(), outline_color.r,
outline_color.g, outline_color.b, outline_color.a );
SDL_RenderDrawRect( renderer.getRendererPtr(), &rect );
}
void RectColider::render( Renderer &renderer, const SDL_Color &color ) {
auto input_color = color;
auto outline_color = color;
input_color.a = 0x40;
outline_color.a = 0x80;
render( renderer, input_color, outline_color );
}
void RectColider::render( Renderer &renderer ) {
auto rect = getRect();
SDL_SetRenderDrawColor( renderer.getRendererPtr(), sdl_color.r, sdl_color.g,
sdl_color.b, sdl_color.a );
SDL_RenderFillRect( renderer.getRendererPtr(), &rect );
SDL_SetRenderDrawColor( renderer.getRendererPtr(), sdl_outline.r,
sdl_outline.g, sdl_outline.b, sdl_outline.a );
SDL_RenderDrawRect( renderer.getRendererPtr(), &rect );
render( renderer, sdl_color, sdl_outline );
}
SDL_Rect RectColider::getRect() {
@ -111,14 +109,14 @@ std::shared_ptr< CollisionPolygon > RectColider::copySelf() {
return std::make_shared< RectColider >( *this );
}
std::vector< Line< double > > RectColider::getLines() {
std::vector< Line< double > > ret{};
ret.emplace_back( original, original + Vec2D< double >( width(), 0 ) );
ret.emplace_back( original + Vec2D< double >( width(), 0 ),
original + _size );
ret.emplace_back( original, original + Vec2D< double >( 0, height() ) );
ret.emplace_back( original + Vec2D< double >( 0, height() ),
original + _size );
std::vector< Line< int > > RectColider::getLines() const {
std::vector< Line< int > > ret{};
ret.emplace_back( position, position + Vec2D< int >( pixel_width(), 0 ) );
ret.emplace_back( position + Vec2D< int >( pixel_width(), 0 ),
position + _size_pixel );
ret.emplace_back( position, position + Vec2D< int >( 0, pixel_height() ) );
ret.emplace_back( position + Vec2D< int >( 0, pixel_height() ),
position + _size_pixel );
return ret;
}

View File

@ -13,7 +13,7 @@ namespace SDLPP {
class SDLPPSCOPE RectColider : public CollisionPolygon {
public:
RectColider( double x, double y, double w, double h );
RectColider( const Vec2D<double> &top_left, const Vec2D<double> &size );
RectColider( const Vec2D< double > &top_left, const Vec2D< double > &size );
virtual ~RectColider() {}
virtual bool colidesWith( const CollisionPolygon &other ) const override;
virtual bool isCircle() const override;
@ -22,12 +22,12 @@ public:
virtual int leftmost() const override;
virtual int rightmost() const override;
virtual void updateCollision( int x, int y, int w, int h ) override;
virtual void
render( Renderer &renderer,
const std::tuple< int, int, int, int > &color ) override;
virtual void render( Renderer &renderer, const SDL_Color &color,
const SDL_Color &outline_color ) override;
virtual void render( Renderer &renderer, const SDL_Color &color ) override;
virtual void render( Renderer &renderer ) override;
virtual std::shared_ptr<CollisionPolygon> copySelf() override;
virtual std::vector<Line<double>> getLines();
virtual std::shared_ptr< CollisionPolygon > copySelf() override;
virtual std::vector< Line< int > > getLines() const override;
private:
SDL_Rect getRect();
@ -37,8 +37,8 @@ private:
int pixel_width() const;
int pixel_height() const;
Vec2D<double> _size;
Vec2D<int> _size_pixel;
Vec2D< double > _size;
Vec2D< int > _size_pixel;
};
} // end of namespace SDLPP
#endif

View File

@ -137,7 +137,7 @@ bool RenderObject::getKilled() {
return kill;
}
void RenderObject::setColiderColor( const std::string &color ) {
colider_color = getColorsHEX( color );
colider_color = getSDLColorHEX( color );
}
void RenderObject::animate( int ticks ) {

View File

@ -114,7 +114,7 @@ protected:
uint64_t id = -1;
bool hidden = false;
bool kill = false;
std::tuple< int, int, int, int > colider_color = { 0x00, 0xFF, 0xFF, 0xFF };
SDL_Color colider_color = { 0x00, 0xFF, 0xFF, 0xFF };
uint64_t scene_id = -1;
bool permanent = false;
bool is_static = true;

View File

@ -10,7 +10,7 @@ OUTPUTFLAG = -Fo
else
UNAME_S := $(shell uname -s)
CXX ?= g++
CXXFLAGS = -std=c++14 -Wall -Wextra -pedantic -O2 -DDEBUG -DFEATURE -g -fsanitize=address
CXXFLAGS = -std=c++14 -Wall -Wextra -pedantic -O2 -DDEBUG -DFEATURE #-g -fsanitize=address
OBJEXT = o
LDFLAGS ?= -lSDL2 -lSDL2_image -lSDL2_gfx -lSDL2_ttf -pthread
OUTPUTFLAG = -o