diff --git a/coresdk/src/backend/graphics_driver.cpp b/coresdk/src/backend/graphics_driver.cpp index 8e394ca0..75f50956 100644 --- a/coresdk/src/backend/graphics_driver.cpp +++ b/coresdk/src/backend/graphics_driver.cpp @@ -2129,15 +2129,10 @@ namespace splashkit_lib return result; } - sk_drawing_surface sk_load_bitmap(const char * filename) + sk_drawing_surface sk_load_from_surface(SDL_Surface *surface) { - internal_sk_init(); sk_drawing_surface result = { SGDS_Unknown, 0, 0, nullptr }; - SDL_Surface *surface; - - surface = IMG_Load(filename); - if ( ! surface ) { std::cout << "error loading image " << IMG_GetError() << std::endl; return result; @@ -2171,7 +2166,23 @@ namespace splashkit_lib return result; } - + + sk_drawing_surface sk_load_bitmap(const char * filename) + { + internal_sk_init(); + + return sk_load_from_surface(IMG_Load(filename)); + } + + sk_drawing_surface sk_load_bitmap_from_memory(const vector& img_data) + { + internal_sk_init(); + + SDL_RWops *rw = SDL_RWFromConstMem(&img_data[0], img_data.size()); + + return sk_load_from_surface(IMG_LoadTyped_RW(rw, 1, "PNG")); + } + //x, y is the position to draw the bitmap to. As bitmaps scale around their centre, (x, y) is the top-left of the bitmap IF and ONLY IF scale = 1. //Angle is in degrees, 0 being right way up //Centre is the point to rotate around, relative to the bitmap centre (therefore (0,0) would rotate around the centre point) diff --git a/coresdk/src/backend/graphics_driver.h b/coresdk/src/backend/graphics_driver.h index 5e2af82c..d4048b0c 100644 --- a/coresdk/src/backend/graphics_driver.h +++ b/coresdk/src/backend/graphics_driver.h @@ -50,7 +50,7 @@ namespace splashkit_lib sk_drawing_surface sk_create_bitmap(int width, int height); sk_drawing_surface sk_load_bitmap(const char * filename); - + sk_drawing_surface sk_load_bitmap_from_memory(const vector& img_data); void sk_draw_bitmap( sk_drawing_surface * src, sk_drawing_surface * dst, double * src_data, int src_data_sz, double * dst_data, int dst_data_sz, sk_renderer_flip flip ); diff --git a/coresdk/src/backend/utility_functions.cpp b/coresdk/src/backend/utility_functions.cpp index 6cc4a6de..eb4229bd 100644 --- a/coresdk/src/backend/utility_functions.cpp +++ b/coresdk/src/backend/utility_functions.cpp @@ -587,4 +587,47 @@ namespace splashkit_lib double lin_interp(double v0, double v1, double t) { return v0 * (1.0 - t) + v1 * t; } + + vector base64_decode_data(const char* in) + { + const string BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + //Lookup decimal values for base64 string (base64 -> base10) + vector map; + for (int i = 0; i < strlen(in); i++) + { + for(int j = 0; j < 64; j++) + { + if(in[i] == BASE64_CHARS[j]) + { + //Remove 00 prefix from byte by bit shifting + map.push_back(j << 2); + } + } + } + + vector out; + int8_t a; + int8_t b; + int lb = 0; + int rb = 6; + //Base64 encoded data should always be divisible into chunks of four + for (int i = 0; i < map.size(); i++) + { + //Concatenate six bits of a and two bits of b to get val c + a = (map[i] << lb); + b = (map[i+1] >> rb); + out.push_back(a + b); + //Shift cursors to concatenate correct chunks + lb+=2; + rb-=2; + if(rb == 0) + { + lb = 0; + rb = 6; + ++i; + } + } + return out; + } } diff --git a/coresdk/src/backend/utility_functions.h b/coresdk/src/backend/utility_functions.h index 6ae0f118..10947026 100644 --- a/coresdk/src/backend/utility_functions.h +++ b/coresdk/src/backend/utility_functions.h @@ -195,6 +195,8 @@ collection.erase(collection.begin());\ double lin_interp(double v0, double v1, double t); + vector base64_decode_data(const char* in); + // Notify the listeners that a resource has been freed. Implemented in resources. void notify_of_free(void *resource); } diff --git a/coresdk/src/coresdk/circle_geometry.cpp b/coresdk/src/coresdk/circle_geometry.cpp index 92ee16b5..6ceeb93c 100644 --- a/coresdk/src/coresdk/circle_geometry.cpp +++ b/coresdk/src/coresdk/circle_geometry.cpp @@ -98,6 +98,41 @@ namespace splashkit_lib return (v - sqrt(d)); } + bool circle_ray_intersection(const point_2d &origin, const vector_2d &heading, const circle &circ) + { + point_2d hit_point; + double hit_distance; + return circle_ray_intersection(origin, heading, circ, hit_point, hit_distance); + } + + bool circle_ray_intersection(const point_2d &origin, const vector_2d &heading, const circle &circ, point_2d &hit_point, double &hit_distance) + { + vector_2d unit_heading = unit_vector(heading); + + // check whether unit heading is a zero vector + if (vector_magnitude_squared(unit_heading) < __DBL_EPSILON__) + { + return false; + } + + if (point_in_circle(origin, circ)) + { + hit_point = origin; + hit_distance = 0.0; + return true; + } + + float distance = ray_circle_intersect_distance(origin, unit_heading, circ); + if (distance < 0.0f) + { + return false; + } + + hit_distance = static_cast(distance); + hit_point = point_offset_by(origin, vector_multiply(unit_heading, hit_distance)); + return true; + } + bool circles_intersect(circle c1, circle c2) { return point_point_distance(c1.center, c2.center) < c1.radius + c2.radius; @@ -192,4 +227,19 @@ namespace splashkit_lib return true; } + + bool circle_quad_intersect(const circle &c, const quad &q) + { + vector q_tris = triangles_from(q); + + for (size_t i = 0; i < q_tris.size(); i++) + { + if (circle_triangle_intersect(c, q_tris[i])) + { + return true; + } + } + + return false; + } } diff --git a/coresdk/src/coresdk/circle_geometry.h b/coresdk/src/coresdk/circle_geometry.h index 62362d81..3744b3c8 100644 --- a/coresdk/src/coresdk/circle_geometry.h +++ b/coresdk/src/coresdk/circle_geometry.h @@ -150,6 +150,35 @@ namespace splashkit_lib */ float ray_circle_intersect_distance(const point_2d &ray_origin, const vector_2d &ray_heading, const circle &c); + /** + * Detects if a ray intersects a circle. + * + * @param origin The starting point of the ray + * @param heading The direction of the ray as a vector + * @param circ The circle to check for intersection + * @returns True if the ray intersects the circle, false otherwise + */ + bool circle_ray_intersection(const point_2d &origin, const vector_2d &heading, const circle &circ); + + /** + * Detects if a ray intersects a circle. If an intersection is found, the + * `hit_point` and `hit_distance` are set to the point of intersection and the + * distance from the ray's origin to the intersection point. If the ray's `origin` + * is contained within the circle, `hit_point` is set to the `origin` and `hit_distance` + * is set to 0. If no intersection is found, `hit_point` and `hit_distance` are not modified. + * + * @param origin The starting point of the ray + * @param heading The direction of the ray as a vector + * @param circ The circle to check for intersection + * @param hit_point The point to set to where the ray intersects the circle + * @param hit_distance The double to set to the distance from the ray's origin to + * the intersection point + * @returns True if the ray intersects the circle, false otherwise + * + * @attribute suffix with_hit_point_and_distance + */ + bool circle_ray_intersection(const point_2d &origin, const vector_2d &heading, const circle &circ, point_2d &hit_point, double &hit_distance); + /** * Returns the circle radius. * @@ -217,5 +246,13 @@ namespace splashkit_lib */ bool tangent_points(const point_2d &from_pt, const circle &c, point_2d &p1, point_2d &p2); + /** + * Detects if a circle intersects with a quad. + * + * @param c The circle to test + * @param q The quad to test + * @return True if the circle and quad intersect, false otherwise + */ + bool circle_quad_intersect(const circle &c, const quad &q); } #endif /* circle_geometry_hpp */ diff --git a/coresdk/src/coresdk/collisions.cpp b/coresdk/src/coresdk/collisions.cpp index 110503a7..f03730ce 100644 --- a/coresdk/src/coresdk/collisions.cpp +++ b/coresdk/src/coresdk/collisions.cpp @@ -17,23 +17,31 @@ #include "graphics.h" #include "utils.h" -constexpr double RAY_QUAD_LINE_THICKNESS = 1.0; - using std::function; namespace splashkit_lib { - //#define DEBUG_STEP + const vector_2d _DIRECTION_TOP = vector_to(0.0, -1.0); + const vector_2d _DIRECTION_BOTTOM = vector_to(0.0, 1.0); + const vector_2d _DIRECTION_LEFT = vector_to(-1.0, 0.0); + const vector_2d _DIRECTION_RIGHT = vector_to(1.0, 0.0); + const vector_2d _DIRECTION_TOP_LEFT = unit_vector(vector_to(-1.0, -1.0)); + const vector_2d _DIRECTION_TOP_RIGHT = unit_vector(vector_to(1.0, -1.0)); + const vector_2d _DIRECTION_BOTTOM_LEFT = unit_vector(vector_to(-1.0, 1.0)); + const vector_2d _DIRECTION_BOTTOM_RIGHT = unit_vector(vector_to(1.0, 1.0)); + const vector_2d _DIRECTION_NONE = vector_to(0.0, 0.0); + + // #define DEBUG_STEP // Step over pixels in the two areas based on the supplied matrix // // See http://www.austincc.edu/cchrist1/GAME1343/TransformedCollision/TransformedCollision.htm - bool _step_through_pixels ( - double w1, double h1, - const matrix_2d &matrix1, - double w2, double h2, - const matrix_2d &matrix2, - function end_fn ) + bool _step_through_pixels( + double w1, double h1, + const matrix_2d &matrix1, + double w2, double h2, + const matrix_2d &matrix2, + function end_fn) { bool a_is_1; double h_a, w_a; @@ -41,7 +49,7 @@ namespace splashkit_lib matrix_2d transform_a_to_b; // Determine the smaller area to step through. - if ( w1 * h1 <= w2 * h2 ) // use bitmap 1 as the one to scan + if (w1 * h1 <= w2 * h2) // use bitmap 1 as the one to scan { a_is_1 = true; h_a = h1; @@ -72,7 +80,7 @@ namespace splashkit_lib // Calculate the top left corner of A in B's local space // This variable will be reused to keep track of the start of each row - y_pos_in_b = matrix_multiply(transform_a_to_b, vector_to(0,0)); + y_pos_in_b = matrix_multiply(transform_a_to_b, vector_to(0, 0)); // When a point moves in A's local space, it moves in B's local space with a // fixed direction and distance proportional to the movement in A. @@ -100,10 +108,9 @@ namespace splashkit_lib y_b = trunc(pos_in_b.y); // If the pixel lies within the bounds of B - if ( (0 <= x_b) and (x_b < w_b) and (0 <= y_b) and (y_b < h_b) ) + if ((0 <= x_b) and (x_b < w_b) and (0 <= y_b) and (y_b < h_b)) { - if ( ( a_is_1 and end_fn(x_a, y_a, x_b, y_b) ) - or ( (not a_is_1) and end_fn(x_b, y_b, x_a, y_a)) ) + if ((a_is_1 and end_fn(x_a, y_a, x_b, y_b)) or ((not a_is_1) and end_fn(x_b, y_b, x_a, y_a))) { return true; } @@ -121,22 +128,22 @@ namespace splashkit_lib return false; } - bool _collision_within_bitmap_images_with_translation(bitmap bmp1, int c1, const matrix_2d& matrix1, bitmap bmp2, int c2, const matrix_2d& matrix2) + bool _collision_within_bitmap_images_with_translation(bitmap bmp1, int c1, const matrix_2d &matrix1, bitmap bmp2, int c2, const matrix_2d &matrix2) { return _step_through_pixels(bitmap_cell_width(bmp1), bitmap_cell_height(bmp1), matrix1, bitmap_cell_width(bmp2), bitmap_cell_height(bmp2), matrix2, - [&] (int ax, int ay, int bx, int by) + [&](int ax, int ay, int bx, int by) { #if DEBUG_STEP point_2d apt, bpt; - apt = matrix_multiply(matrix1, point_at(ax,ay)); - if ( pixel_drawn_at_point(bmp1, c1, ax, ay) ) + apt = matrix_multiply(matrix1, point_at(ax, ay)); + if (pixel_drawn_at_point(bmp1, c1, ax, ay)) draw_circle(COLOR_RED, apt.x, apt.y, 3); - bpt = matrix_multiply(matrix2, point_at(bx,by)); - if ( pixel_drawn_at_point(bmp2, c2, bx, by) ) + bpt = matrix_multiply(matrix2, point_at(bx, by)); + if (pixel_drawn_at_point(bmp2, c2, bx, by)) draw_circle(COLOR_PINK, bpt.x, bpt.y, 2); - if ( pixel_drawn_at_point(bmp1, c1, ax, ay) and pixel_drawn_at_point(bmp2, c2, bx, by) ) + if (pixel_drawn_at_point(bmp1, c1, ax, ay) and pixel_drawn_at_point(bmp2, c2, bx, by)) { fill_circle(COLOR_GREEN, apt.x, apt.y, 1); fill_circle(COLOR_YELLOW, bpt.x, bpt.y, 3); @@ -146,19 +153,466 @@ namespace splashkit_lib }); } - bool bitmap_point_collision(bitmap bmp, int cell, const matrix_2d& translation, const point_2d& pt ) + vector_2d _opposite_direction(const vector_2d &dir) + { + return vector_to(-dir.x, -dir.y); + } + + bool _test_collision(const sprite &s1, const sprite &s2) + { + return sprite_collision(s1, s2); + } + + bool _test_collision(const sprite &s, const rectangle &r) + { + return sprite_rectangle_collision(s, r); + } + + bool _test_collision(const sprite &s, const circle &c) + { + return sprite_circle_collision(s, c); + } + + bool _test_collision(const sprite &s, const triangle &t) + { + return sprite_triangle_collision(s, t); + } + + bool _test_collision(const sprite &s, const quad &q) + { + return sprite_quad_collision(s, q); + } + + bool _test_collision(const rectangle &r, const sprite &s) + { + return sprite_rectangle_collision(s, r); + } + + bool _test_collision(const rectangle &r1, const rectangle &r2) + { + return rectangles_intersect(r1, r2); + } + + bool _test_collision(const rectangle &r, const circle &c) + { + return rectangle_circle_intersect(r, c); + } + + bool _test_collision(const rectangle &r, const triangle &t) + { + return triangle_rectangle_intersect(t, r); + } + + bool _test_collision(const rectangle &r, const quad &q) + { + return quads_intersect(quad_from(r), q); + } + + bool _test_collision(const circle &c, const sprite &s) + { + return sprite_circle_collision(s, c); + } + + bool _test_collision(const circle &c, const rectangle &r) + { + return rectangle_circle_intersect(r, c); + } + + bool _test_collision(const circle &c1, const circle &c2) + { + return circles_intersect(c1, c2); + } + + bool _test_collision(const circle &c, const triangle &t) + { + return circle_triangle_intersect(c, t); + } + + bool _test_collision(const circle &c, const quad &q) + { + return circle_quad_intersect(c, q); + } + + bool _test_collision(const triangle &t, const sprite &s) + { + return sprite_triangle_collision(s, t); + } + + bool _test_collision(const triangle &t, const rectangle &r) + { + return triangle_rectangle_intersect(t, r); + } + + bool _test_collision(const triangle &t, const circle &c) + { + return circle_triangle_intersect(c, t); + } + + bool _test_collision(const triangle &t1, const triangle &t2) + { + return triangles_intersect(t1, t2); + } + + bool _test_collision(const triangle &t, const quad &q) + { + return triangle_quad_intersect(t, q); + } + + bool _test_collision(const quad &q, const sprite &s) + { + return sprite_quad_collision(s, q); + } + + bool _test_collision(const quad &q, const rectangle &r) + { + return quads_intersect(q, quad_from(r)); + } + + bool _test_collision(const quad &q, const circle &c) + { + return circle_quad_intersect(c, q); + } + + bool _test_collision(const quad &q, const triangle &t) + { + return triangle_quad_intersect(t, q); + } + + bool _test_collision(const quad &q1, const quad &q2) + { + return quads_intersect(q1, q2); + } + + void _move_obj_by_vector(sprite &obj, const vector_2d &amount) + { + sprite_set_x(obj, sprite_x(obj) + amount.x); + sprite_set_y(obj, sprite_y(obj) + amount.y); + } + + void _move_obj_by_vector(rectangle &obj, const vector_2d &amount) + { + obj.x += amount.x; + obj.y += amount.y; + } + + void _move_obj_by_vector(circle &obj, const vector_2d &amount) + { + obj.center.x += amount.x; + obj.center.y += amount.y; + } + + void _move_obj_by_vector(triangle &obj, const vector_2d &amount) + { + obj.points[0].x += amount.x; + obj.points[0].y += amount.y; + obj.points[1].x += amount.x; + obj.points[1].y += amount.y; + obj.points[2].x += amount.x; + obj.points[2].y += amount.y; + } + + void _move_obj_by_vector(quad &obj, const vector_2d &amount) + { + obj.points[0].x += amount.x; + obj.points[0].y += amount.y; + obj.points[1].x += amount.x; + obj.points[1].y += amount.y; + obj.points[2].x += amount.x; + obj.points[2].y += amount.y; + obj.points[3].x += amount.x; + obj.points[3].y += amount.y; + } + + rectangle _object_AABB(const sprite &obj) + { + return sprite_collision_rectangle(obj); + } + + rectangle _object_AABB(const rectangle &obj) + { + return obj; + } + + rectangle _object_AABB(const circle &obj) + { + return rectangle_around(obj); + } + + rectangle _object_AABB(const triangle &obj) + { + return rectangle_around(obj); + } + + rectangle _object_AABB(const quad &obj) + { + return rectangle_around(obj); + } + + template + collision_test_kind _collision_kind(const T &obj) + { + return PIXEL_COLLISIONS; + } + + template <> + collision_test_kind _collision_kind(const rectangle &obj) + { + return AABB_COLLISIONS; + } + + template <> + collision_test_kind _collision_kind(const sprite &obj) + { + return sprite_collision_kind(obj); + } + + vector_2d _compare_point_collision_depth_horizontal(const point_2d &collider, const point_2d &collidee) + { + if (collider.x < collidee.x) + { + return _DIRECTION_RIGHT; + } + return _DIRECTION_LEFT; + } + + vector_2d _compare_point_collision_depth_vertical(const point_2d &collider, const point_2d &collidee) + { + if (collider.y < collidee.y) + { + return _DIRECTION_BOTTOM; + } + return _DIRECTION_TOP; + } + + vector_2d _calculate_containing_collision_direction(const rectangle &collider, const rectangle &collidee) + { + // calculate the direction of the greatest distance between the two sprites + point_2d collider_center = rectangle_center(collider); + point_2d collidee_center = rectangle_center(collidee); + double x_distance = collider_center.x - collidee_center.x; + if (x_distance < 0.0) + { + x_distance *= -1; + } + + double y_distance = collider_center.y - collidee_center.y; + if (y_distance < 0.0) + { + y_distance *= -1; + } + + if (x_distance < y_distance) + { + return _compare_point_collision_depth_horizontal(collider_center, collidee_center); + } + return _compare_point_collision_depth_vertical(collider_center, collidee_center); + } + + vector_2d _rectangle_rectangle_collision_direction(const rectangle &collider, const rectangle &collidee) + { + vector collider_lines = lines_from(collider); + line collider_top_edge = collider_lines[0]; + line collider_left_edge = collider_lines[1]; + line collider_right_edge = collider_lines[2]; + line collider_bottom_edge = collider_lines[3]; + + bool left_edge = line_intersects_rect(collider_left_edge, collidee); + bool right_edge = line_intersects_rect(collider_right_edge, collidee); + bool top_edge = line_intersects_rect(collider_top_edge, collidee); + bool bottom_edge = line_intersects_rect(collider_bottom_edge, collidee); + + if (left_edge && right_edge && top_edge && bottom_edge) // collidee is equal size of collider + { + return _calculate_containing_collision_direction(collider, collidee); + } + if ((left_edge && right_edge && top_edge) || (top_edge && !(left_edge || right_edge || bottom_edge))) + { + return _DIRECTION_TOP; + } + if ((left_edge && right_edge && bottom_edge) || (bottom_edge && !(left_edge || right_edge || top_edge))) + { + return _DIRECTION_BOTTOM; + } + if ((top_edge && bottom_edge && right_edge) || (right_edge && !(left_edge || top_edge || bottom_edge))) + { + return _DIRECTION_RIGHT; + } + if ((top_edge && bottom_edge && left_edge) || (left_edge && !(right_edge || top_edge || bottom_edge))) + { + return _DIRECTION_LEFT; + } + if (left_edge && right_edge) + { + // check if the collider is more to the left or right of the collidee + return _compare_point_collision_depth_horizontal(rectangle_center(collider), rectangle_center(collidee)); + } + if (top_edge && bottom_edge) + { + // check if the collider is more to the top or bottom of the collidee + return _compare_point_collision_depth_vertical(rectangle_center(collider), rectangle_center(collidee)); + } + if (left_edge && top_edge) + { + return _DIRECTION_TOP_LEFT; + } + if (left_edge && bottom_edge) + { + return _DIRECTION_BOTTOM_LEFT; + } + if (right_edge && top_edge) + { + return _DIRECTION_TOP_RIGHT; + } + if (right_edge && bottom_edge) + { + return _DIRECTION_BOTTOM_RIGHT; + } + + // collider contains collidee or collidee contains collider + return _calculate_containing_collision_direction(collider, collidee); + } + + template + void _move_object_by_direction(T &obj, const vector_2d &movement_direction, const vector_2d &amount) + { + if (is_zero_vector(amount)) + { + return; + } + + _move_obj_by_vector(obj, vector_to(amount.x * movement_direction.x, + amount.y * movement_direction.y)); + } + + template + void _move_object_by_direction_relative_to_size(T &obj, const vector_2d &movement_direction, + double relative_amount = 1.0) + { + if (is_zero_vector(movement_direction)) + { + return; + } + + if (relative_amount == 0.0) + { + return; + } + + rectangle obj_aabb = _object_AABB(obj); + + double relative_width = obj_aabb.width * relative_amount; + double relative_height = obj_aabb.height * relative_amount; + + if (movement_direction.x == 0.0) + { + relative_width = 0.0; + } + else if (movement_direction.y == 0.0) + { + relative_height = 0.0; + } + + _move_object_by_direction(obj, movement_direction, vector_to(relative_width, relative_height)); + } + + /** + * Moves the object back and forth with decreasing step size for + * the given number of iterations. + */ + template + bool _bracket_obj_collision_single(bool colliding, int i, T &collider, + const vector_2d &collider_direction) + { + const double ITERATION_POWER = 1.5; + if (colliding) + { + _move_object_by_direction_relative_to_size(collider, collider_direction, + 1.0 / pow(ITERATION_POWER, static_cast(i))); + } + else if (i == 1) // no collision in the first iteration + { + return false; + } + else + { + _move_object_by_direction_relative_to_size(collider, _opposite_direction(collider_direction), + 1.0 / pow(ITERATION_POWER, static_cast(i))); + } + return true; + } + + template + void _bracket_obj_obj_collision(A &collider, const B &collidee, const vector_2d &collider_direction, + int iterations) + { + for (int i = 1; i <= iterations; i++) + { + if (!_bracket_obj_collision_single(_test_collision(collider, collidee), i, collider, + collider_direction)) + { + return; + } + } + } + + template + vector_2d _calculate_object_collision_direction(const A &collider, const B &collidee) + { + if (!_test_collision(collider, collidee)) + { + return vector_to(0.0, 0.0); + } + + return _rectangle_rectangle_collision_direction(_object_AABB(collider), _object_AABB(collidee)); + } + + template + void _resolve_object_AABB_collision(T &collider, const rectangle &collidee_rect, + const vector_2d &collision_direction) + { + // get the intersection rectangle + rectangle inter = intersection(_object_AABB(collider), collidee_rect); + vector_2d amount = vector_to(inter.width, inter.height); + + _move_object_by_direction(collider, _opposite_direction(collision_direction), amount); + } + + template + bool _resolve_object_collision(A &collider, const B &collidee, const vector_2d &collision_direction) + { + const int BRACKET_ITERATIONS = 40; + if (is_zero_vector(collision_direction) || !_test_collision(collider, collidee)) + { + return false; + } + + vector_2d unit_dir = unit_vector(collision_direction); + + if (_collision_kind(collider) == AABB_COLLISIONS && _collision_kind(collidee) == AABB_COLLISIONS) + { + _resolve_object_AABB_collision(collider, _object_AABB(collidee), unit_dir); + } + else // one or both of the sprites are using pixel collision + { + _bracket_obj_obj_collision(collider, collidee, _opposite_direction(unit_dir), BRACKET_ITERATIONS); + } + + return true; + } + + bool bitmap_point_collision(bitmap bmp, int cell, const matrix_2d &translation, const point_2d &pt) { if (INVALID_PTR(bmp, BITMAP_PTR)) { return false; } - if ( not point_in_quad(pt, quad_from(bitmap_cell_rectangle(bmp), translation)) ) + if (not point_in_quad(pt, quad_from(bitmap_cell_rectangle(bmp), translation))) { return false; } - return _step_through_pixels(1, 1, translation_matrix(pt.x, pt.y), bmp->cell_w, bmp->cell_h, translation, [&] (int ax, int ay, int bx, int by) + return _step_through_pixels(1, 1, translation_matrix(pt.x, pt.y), bmp->cell_w, bmp->cell_h, translation, [&](int ax, int ay, int bx, int by) { #if DEBUG_STEP point_2d bpt; @@ -166,16 +620,15 @@ namespace splashkit_lib if ( pixel_drawn_at_point(bmp, cell, bx, by) ) fill_rectangle(COLOR_PINK, bpt.x, bpt.y, translation.elements[0][0], translation.elements[1][1] ); #endif - return pixel_drawn_at_point(bmp, cell, bx, by); - }); + return pixel_drawn_at_point(bmp, cell, bx, by); }); } - bool bitmap_point_collision(bitmap bmp, const matrix_2d& translation, const point_2d& pt) + bool bitmap_point_collision(bitmap bmp, const matrix_2d &translation, const point_2d &pt) { return bitmap_point_collision(bmp, 0, translation, pt); } - bool bitmap_point_collision(bitmap bmp, const point_2d &bmp_pt, const point_2d& pt) + bool bitmap_point_collision(bitmap bmp, const point_2d &bmp_pt, const point_2d &pt) { return bitmap_point_collision(bmp, translation_matrix(bmp_pt), pt); } @@ -184,18 +637,18 @@ namespace splashkit_lib { return bitmap_point_collision(bmp, 0, translation_matrix(bmp_x, bmp_y), point_at(x, y)); } - - bool bitmap_point_collision(bitmap bmp, int cell, const point_2d &bmp_pt, const point_2d& pt) + + bool bitmap_point_collision(bitmap bmp, int cell, const point_2d &bmp_pt, const point_2d &pt) { return bitmap_point_collision(bmp, cell, translation_matrix(bmp_pt), pt); } - + bool bitmap_point_collision(bitmap bmp, int cell, double bmp_x, double bmp_y, double x, double y) { return bitmap_point_collision(bmp, cell, translation_matrix(bmp_x, bmp_y), point_at(x, y)); } - bool bitmap_rectangle_collision(bitmap bmp, int cell, const matrix_2d& translation, const rectangle& rect) + bool bitmap_rectangle_collision(bitmap bmp, int cell, const matrix_2d &translation, const rectangle &rect) { if (INVALID_PTR(bmp, BITMAP_PTR)) { @@ -207,100 +660,98 @@ namespace splashkit_lib q1 = quad_from(bitmap_cell_rectangle(bmp), translation); q2 = quad_from(rect); - if ( not quads_intersect(q1, q2) ) return false; + if (not quads_intersect(q1, q2)) + return false; - return _step_through_pixels(rect.width, rect.height, translation_matrix(rect.x, rect.y), bmp->cell_w, bmp->cell_h, translation, [&] (int ax, int ay, int bx, int by) - { - return pixel_drawn_at_point(bmp, cell, bx, by); - }); + return _step_through_pixels(rect.width, rect.height, translation_matrix(rect.x, rect.y), bmp->cell_w, bmp->cell_h, translation, [&](int ax, int ay, int bx, int by) + { return pixel_drawn_at_point(bmp, cell, bx, by); }); } - bool bitmap_rectangle_collision(bitmap bmp, int cell, const point_2d& pt, const rectangle& rect) + bool bitmap_rectangle_collision(bitmap bmp, int cell, const point_2d &pt, const rectangle &rect) { return bitmap_rectangle_collision(bmp, cell, translation_matrix(pt), rect); } - - bool bitmap_rectangle_collision(bitmap bmp, const point_2d& pt, const rectangle& rect) + + bool bitmap_rectangle_collision(bitmap bmp, const point_2d &pt, const rectangle &rect) { return bitmap_rectangle_collision(bmp, 0, translation_matrix(pt), rect); } - - bool bitmap_rectangle_collision(bitmap bmp, double x, double y, const rectangle& rect) + + bool bitmap_rectangle_collision(bitmap bmp, double x, double y, const rectangle &rect) { return bitmap_rectangle_collision(bmp, 0, translation_matrix(x, y), rect); } - - bool bitmap_rectangle_collision(bitmap bmp, int cell, double x, double y, const rectangle& rect) + + bool bitmap_rectangle_collision(bitmap bmp, int cell, double x, double y, const rectangle &rect) { return bitmap_rectangle_collision(bmp, cell, translation_matrix(x, y), rect); } - - bool bitmap_circle_collision(bitmap bmp, int cell, const matrix_2d& translation, const circle& circ) + + bool bitmap_circle_collision(bitmap bmp, int cell, const matrix_2d &translation, const circle &circ) { if (INVALID_PTR(bmp, BITMAP_PTR)) { return false; } - + quad q1, q2; - + q1 = quad_from(bitmap_cell_rectangle(bmp), translation); rectangle rect = rectangle_around(circ); q2 = quad_from(rect); - - if ( not quads_intersect(q1, q2) ) return false; - - return _step_through_pixels(rect.width, rect.height, translation_matrix(rect.x, rect.y), bmp->cell_w, bmp->cell_h, translation, [&] (int ax, int ay, int bx, int by) - { - return pixel_drawn_at_point(bmp, cell, bx, by) && point_in_circle(point_at(rect.x + ax, rect.y + ay), circ); - }); + + if (not quads_intersect(q1, q2)) + return false; + + return _step_through_pixels(rect.width, rect.height, translation_matrix(rect.x, rect.y), bmp->cell_w, bmp->cell_h, translation, [&](int ax, int ay, int bx, int by) + { return pixel_drawn_at_point(bmp, cell, bx, by) && point_in_circle(point_at(rect.x + ax, rect.y + ay), circ); }); } - - bool bitmap_circle_collision(bitmap bmp, int cell, const point_2d& pt, const circle& circ) + + bool bitmap_circle_collision(bitmap bmp, int cell, const point_2d &pt, const circle &circ) { return bitmap_circle_collision(bmp, cell, translation_matrix(pt), circ); } - bool bitmap_circle_collision(bitmap bmp, const point_2d& pt, const circle& circ) + bool bitmap_circle_collision(bitmap bmp, const point_2d &pt, const circle &circ) { return bitmap_circle_collision(bmp, 0, translation_matrix(pt), circ); } - - bool bitmap_circle_collision(bitmap bmp, double x, double y, const circle& circ) + + bool bitmap_circle_collision(bitmap bmp, double x, double y, const circle &circ) { return bitmap_circle_collision(bmp, 0, translation_matrix(x, y), circ); } - - bool bitmap_circle_collision(bitmap bmp, int cell, double x, double y, const circle& circ) + + bool bitmap_circle_collision(bitmap bmp, int cell, double x, double y, const circle &circ) { return bitmap_circle_collision(bmp, cell, translation_matrix(x, y), circ); } - bool bitmap_quad_collision(bitmap bmp, int cell, const matrix_2d &translation, const quad& q) + bool bitmap_quad_collision(bitmap bmp, int cell, const matrix_2d &translation, const quad &q) { if (INVALID_PTR(bmp, BITMAP_PTR)) { return false; } - + quad q1 = quad_from(bitmap_cell_rectangle(bmp), translation); rectangle rect = rectangle_around(q); - - if ( not quads_intersect(q1, q) ) return false; - - return _step_through_pixels(rect.width, rect.height, translation_matrix(rect.x, rect.y), bmp->cell_w, bmp->cell_h, translation, [&] (int ax, int ay, int bx, int by) - { - return pixel_drawn_at_point(bmp, cell, bx, by) && point_in_quad(point_at(rect.x + ax, rect.y + ay), q); - }); + + if (not quads_intersect(q1, q)) + return false; + + return _step_through_pixels(rect.width, rect.height, translation_matrix(rect.x, rect.y), bmp->cell_w, bmp->cell_h, translation, [&](int ax, int ay, int bx, int by) + { return pixel_drawn_at_point(bmp, cell, bx, by) && point_in_quad(point_at(rect.x + ax, rect.y + ay), q); }); } - bool bitmap_ray_collision(bitmap bmp, int cell, const matrix_2d& translation, const point_2d& origin, const vector_2d& heading) + bool bitmap_ray_collision(bitmap bmp, int cell, const matrix_2d &translation, const point_2d &origin, const vector_2d &heading) { + constexpr double RAY_QUAD_LINE_THICKNESS = 1.0; if (INVALID_PTR(bmp, BITMAP_PTR)) { return false; } - + point_2d bmp_position = matrix_multiply(translation, point_at(0.0, 0.0)); point_2d bmp_center = point_offset_by(bmp_position, vector_to(bitmap_center(bmp))); circle bmp_bounding_circle = bitmap_bounding_circle(bmp, bmp_center); @@ -315,26 +766,85 @@ namespace splashkit_lib return bitmap_quad_collision(bmp, cell, translation, ray_quad); } - bool bitmap_ray_collision(bitmap bmp, int cell, const point_2d& pt, const point_2d& origin, const vector_2d& heading) + bool bitmap_ray_collision(bitmap bmp, int cell, const point_2d &pt, const point_2d &origin, const vector_2d &heading) { return bitmap_ray_collision(bmp, cell, translation_matrix(pt), origin, heading); } - bool bitmap_ray_collision(bitmap bmp, int cell, double x, double y, const point_2d& origin, const vector_2d& heading) + bool bitmap_ray_collision(bitmap bmp, int cell, double x, double y, const point_2d &origin, const vector_2d &heading) { return bitmap_ray_collision(bmp, cell, translation_matrix(x, y), origin, heading); } - bool bitmap_ray_collision(bitmap bmp, double x, double y, const point_2d& origin, const vector_2d& heading) + bool bitmap_ray_collision(bitmap bmp, double x, double y, const point_2d &origin, const vector_2d &heading) { return bitmap_ray_collision(bmp, 0, translation_matrix(x, y), origin, heading); } - bool bitmap_ray_collision(bitmap bmp, const point_2d& pt, const point_2d& origin, const vector_2d& heading) + bool bitmap_ray_collision(bitmap bmp, const point_2d &pt, const point_2d &origin, const vector_2d &heading) { return bitmap_ray_collision(bmp, 0, translation_matrix(pt), origin, heading); } + bool bitmap_triangle_collision(bitmap bmp, int cell, const matrix_2d &translation, const triangle &tri) + { + if (INVALID_PTR(bmp, BITMAP_PTR)) + { + return false; + } + + quad q1 = quad_from(bitmap_cell_rectangle(bmp), translation); + rectangle rect = rectangle_around(tri); + + if (!triangle_quad_intersect(tri, q1)) + { + return false; + } + + return _step_through_pixels(rect.width, rect.height, translation_matrix(rect.x, rect.y), bmp->cell_w, bmp->cell_h, translation, [&](int ax, int ay, int bx, int by) + { return pixel_drawn_at_point(bmp, cell, bx, by) && point_in_triangle(point_at(rect.x + ax, rect.y + ay), tri); }); + } + + bool bitmap_triangle_collision(bitmap bmp, int cell, const point_2d &pt, const triangle &tri) + { + return bitmap_triangle_collision(bmp, cell, translation_matrix(pt), tri); + } + + bool bitmap_triangle_collision(bitmap bmp, int cell, double x, double y, const triangle &tri) + { + return bitmap_triangle_collision(bmp, cell, translation_matrix(x, y), tri); + } + + bool bitmap_triangle_collision(bitmap bmp, double x, double y, const triangle &tri) + { + return bitmap_triangle_collision(bmp, 0, translation_matrix(x, y), tri); + } + + bool bitmap_triangle_collision(bitmap bmp, const point_2d &pt, const triangle &tri) + { + return bitmap_triangle_collision(bmp, 0, translation_matrix(pt), tri); + } + + bool bitmap_quad_collision(bitmap bmp, int cell, const point_2d &pt, const quad &q) + { + return bitmap_quad_collision(bmp, cell, translation_matrix(pt), q); + } + + bool bitmap_quad_collision(bitmap bmp, int cell, double x, double y, const quad &q) + { + return bitmap_quad_collision(bmp, cell, translation_matrix(x, y), q); + } + + bool bitmap_quad_collision(bitmap bmp, double x, double y, const quad &q) + { + return bitmap_quad_collision(bmp, 0, translation_matrix(x, y), q); + } + + bool bitmap_quad_collision(bitmap bmp, const point_2d &pt, const quad &q) + { + return bitmap_quad_collision(bmp, 0, translation_matrix(pt), q); + } + bool sprite_bitmap_collision(sprite s, bitmap bmp, int cell, double x, double y) { if (!rectangles_intersect(sprite_collision_rectangle(s), bitmap_cell_rectangle(bmp, point_at(x, y)))) @@ -348,11 +858,11 @@ namespace splashkit_lib } return _collision_within_bitmap_images_with_translation( - sprite_collision_bitmap(s), sprite_current_cell(s), - sprite_location_matrix(s), - bmp, - cell, - translation_matrix(x, y)); + sprite_collision_bitmap(s), sprite_current_cell(s), + sprite_location_matrix(s), + bmp, + cell, + translation_matrix(x, y)); } bool sprite_bitmap_collision(sprite s, bitmap bmp, int cell, const point_2d &pt) @@ -364,7 +874,7 @@ namespace splashkit_lib { return sprite_bitmap_collision(s, bmp, 0, x, y); } - + bool sprite_point_collision(sprite s, const point_2d &pt) { if (!point_in_circle(pt, sprite_collision_circle(s))) @@ -380,42 +890,72 @@ namespace splashkit_lib return bitmap_point_collision(sprite_collision_bitmap(s), sprite_location_matrix(s), pt); } } - - bool sprite_rectangle_collision(sprite s, const rectangle& rect) + + bool sprite_rectangle_collision(sprite s, const rectangle &rect) { if (!rectangles_intersect(sprite_collision_rectangle(s), rect)) { return false; } - + return bitmap_rectangle_collision(sprite_collision_bitmap(s), sprite_current_cell(s), sprite_location_matrix(s), rect); } - bool sprite_ray_collision(sprite s, const point_2d& origin, const vector_2d& heading) + bool sprite_ray_collision(sprite s, const point_2d &origin, const vector_2d &heading) { return bitmap_ray_collision(sprite_collision_bitmap(s), sprite_current_cell(s), sprite_location_matrix(s), origin, heading); } - + + bool sprite_circle_collision(sprite s, const circle &c) + { + if (!circles_intersect(sprite_collision_circle(s), c)) + { + return false; + } + + return bitmap_circle_collision(sprite_collision_bitmap(s), sprite_current_cell(s), sprite_location_matrix(s), c); + } + + bool sprite_triangle_collision(sprite s, const triangle &t) + { + if (!triangle_rectangle_intersect(t, sprite_collision_rectangle(s))) + { + return false; + } + + return bitmap_triangle_collision(sprite_collision_bitmap(s), sprite_current_cell(s), sprite_location_matrix(s), t); + } + + bool sprite_quad_collision(sprite s, const quad &q) + { + if (!rectangles_intersect(sprite_collision_rectangle(s), rectangle_around(q))) + { + return false; + } + + return bitmap_quad_collision(sprite_collision_bitmap(s), sprite_current_cell(s), sprite_location_matrix(s), q); + } + bool sprite_collision(sprite s1, sprite s2) { if (!rectangles_intersect(sprite_collision_rectangle(s1), sprite_collision_rectangle(s2))) { return false; } - + if (sprite_collision_kind(s1) == AABB_COLLISIONS) { return sprite_rectangle_collision(s2, sprite_collision_rectangle(s1)); } - + if (sprite_collision_kind(s2) == AABB_COLLISIONS) { return sprite_rectangle_collision(s1, sprite_collision_rectangle(s2)); } - - return _collision_within_bitmap_images_with_translation ( - sprite_collision_bitmap(s1), sprite_current_cell(s1), sprite_location_matrix(s1), - sprite_collision_bitmap(s2), sprite_current_cell(s2), sprite_location_matrix(s2) ); + + return _collision_within_bitmap_images_with_translation( + sprite_collision_bitmap(s1), sprite_current_cell(s1), sprite_location_matrix(s1), + sprite_collision_bitmap(s2), sprite_current_cell(s2), sprite_location_matrix(s2)); } bool bitmap_collision(bitmap bmp1, int cell1, const matrix_2d &matrix1, bitmap bmp2, int cell2, const matrix_2d &matrix2) @@ -423,7 +963,7 @@ namespace splashkit_lib quad q1 = quad_from(bitmap_cell_rectangle(bmp1), matrix1); quad q2 = quad_from(bitmap_cell_rectangle(bmp2), matrix2); - if ( not quads_intersect(q1, q2) ) + if (not quads_intersect(q1, q2)) { return false; } @@ -452,4 +992,253 @@ namespace splashkit_lib return bitmap_collision(bmp1, 0, translation_matrix(x1, y1), bmp2, 0, translation_matrix(x2, y2)); } + vector_2d calculate_collision_direction(const sprite collider, const sprite collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const sprite collider, const rectangle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const sprite collider, const circle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const sprite collider, const triangle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const sprite collider, const quad &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const rectangle &collider, const sprite collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const rectangle &collider, const rectangle &collidee) + { + return _rectangle_rectangle_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const rectangle &collider, const circle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const rectangle &collider, const triangle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const rectangle &collider, const quad &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const circle &collider, const sprite collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const circle &collider, const rectangle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const circle &collider, const circle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const circle &collider, const triangle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const circle &collider, const quad &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const triangle &collider, const sprite collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const triangle &collider, const rectangle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const triangle &collider, const circle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const triangle &collider, const triangle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const triangle &collider, const quad &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const quad &collider, const sprite collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const quad &collider, const rectangle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const quad &collider, const circle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const quad &collider, const triangle &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + vector_2d calculate_collision_direction(const quad &collider, const quad &collidee) + { + return _calculate_object_collision_direction(collider, collidee); + } + + bool resolve_collision(sprite collider, const sprite collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(sprite collider, const rectangle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(sprite collider, const circle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(sprite collider, const triangle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(sprite collider, const quad &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(rectangle &collider, const sprite collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(rectangle &collider, const rectangle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(rectangle &collider, const circle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(rectangle &collider, const triangle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(rectangle &collider, const quad &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(circle &collider, const sprite collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(circle &collider, const rectangle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(circle &collider, const circle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(circle &collider, const triangle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(circle &collider, const quad &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(triangle &collider, const sprite collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(triangle &collider, const rectangle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(triangle &collider, const circle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(triangle &collider, const triangle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(triangle &collider, const quad &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(quad &collider, const sprite collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(quad &collider, const rectangle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(quad &collider, const circle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(quad &collider, const triangle &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } + + bool resolve_collision(quad &collider, const quad &collidee, const vector_2d &direction) + { + return _resolve_object_collision(collider, collidee, direction); + } } diff --git a/coresdk/src/coresdk/collisions.h b/coresdk/src/coresdk/collisions.h index 8a30b05d..ad2f428e 100644 --- a/coresdk/src/coresdk/collisions.h +++ b/coresdk/src/coresdk/collisions.h @@ -406,6 +406,177 @@ namespace splashkit_lib */ bool bitmap_ray_collision(bitmap bmp, const point_2d& pt, const point_2d& origin, const vector_2d& heading); + + /** + * Tests if a bitmap cell drawn using a passed in translation, will + * intersect with a triangle. You can use this to detect collisions between + * bitmaps and triangles. + * + * @param bmp The bitmap to test + * @param cell The cell of the bitmap to check + * @param translation The matrix used to transfrom the bitmap when drawing + * @param tri The triangle to test + * @return True if a drawn pixel in the cell of the bitmap will + * intersect with `tri` when drawn. + * + * @attribute suffix for_cell_with_translation + * + * @attribute class bitmap + * @attribute method triangle_collision + */ + bool bitmap_triangle_collision(bitmap bmp, int cell, const matrix_2d &translation, const triangle &tri); + + /** + * Tests if a bitmap cell drawn at `pt` would intersect with a triangle. + * + * @param bmp The bitmap to test + * @param cell The cell of the bitmap to check + * @param pt The location where the bitmap is drawn + * @param tri The triangle to test + * @return True if a drawn pixel in the cell of the bitmap will + * intersect with `tri` when drawn. + * + * @attribute suffix for_cell_at_point + * + * @attribute class bitmap + * @attribute method triangle_collision + */ + bool bitmap_triangle_collision(bitmap bmp, int cell, const point_2d& pt, const triangle &tri); + + /** + * Tests if a bitmap cell drawn at `x`, `y` would intersect with a triangle. + * + * @param bmp The bitmap to test + * @param cell The cell of the bitmap to check + * @param x The x location where the bitmap is drawn + * @param y The y location where the bitmap is drawn + * @param tri The triangle to test + * @return True if a drawn pixel in the bitmap will + * intersect with `tri` when drawn. + * + * @attribute suffix for_cell + * + * @attribute class bitmap + * @attribute method triangle_collision + */ + bool bitmap_triangle_collision(bitmap bmp, int cell, double x, double y, const triangle &tri); + + /** + * Tests if a bitmap drawn at `x`, `y` would intersect with a triangle. + * + * @param bmp The bitmap to test + * @param x The x location where the bitmap is drawn + * @param y The y location where the bitmap is drawn + * @param tri The triangle to test + * @return True if a drawn pixel in the bitmap will + * intersect with `tri` when drawn. + * + * @attribute class bitmap + * @attribute method triangle_collision + */ + bool bitmap_triangle_collision(bitmap bmp, double x, double y, const triangle &tri); + + /** + * Tests if a bitmap drawn at `pt` would intersect with a triangle. + * + * @param bmp The bitmap to test + * @param pt The location where the bitmap is drawn + * @param tri The triangle to test + * @return True if a drawn pixel in the cell of the bitmap will + * intersect with `tri` when drawn. + * + * @attribute suffix at_point + * + * @attribute class bitmap + * @attribute method triangle_collision + */ + bool bitmap_triangle_collision(bitmap bmp, const point_2d& pt, const triangle &tri); + + /** + * Tests if a bitmap cell drawn using a passed in translation, will + * intersect with a quad. You can use this to detect collisions between + * bitmaps and quads. + * + * @param bmp The bitmap to test + * @param cell The cell of the bitmap to check + * @param translation The matrix used to transfrom the bitmap when drawing + * @param q The quad to test + * @return True if a drawn pixel in the cell of the bitmap will + * intersect with `q` when drawn. + * + * @attribute suffix for_cell_with_translation + * + * @attribute class bitmap + * @attribute method quad_collision + */ + bool bitmap_quad_collision(bitmap bmp, int cell, const matrix_2d &translation, const quad &q); + + /** + * Tests if a bitmap cell drawn at `pt` would intersect with a quad. + * + * @param bmp The bitmap to test + * @param cell The cell of the bitmap to check + * @param pt The location where the bitmap is drawn + * @param q The quad to test + * @return True if a drawn pixel in the cell of the bitmap will + * intersect with `q` when drawn. + * + * @attribute suffix for_cell_at_point + * + * @attribute class bitmap + * @attribute method quad_collision + */ + bool bitmap_quad_collision(bitmap bmp, int cell, const point_2d& pt, const quad &q); + + /** + * Tests if a bitmap cell drawn at `x`, `y` would intersect with a quad. + * + * @param bmp The bitmap to test + * @param cell The cell of the bitmap to check + * @param x The x location where the bitmap is drawn + * @param y The y location where the bitmap is drawn + * @param q The quad to test + * @return True if a drawn pixel in the bitmap will + * intersect with `q` when drawn. + * + * @attribute suffix for_cell + * + * @attribute class bitmap + * @attribute method quad_collision + */ + bool bitmap_quad_collision(bitmap bmp, int cell, double x, double y, const quad &q); + + /** + * Tests if a bitmap drawn at `x`, `y` would intersect with a quad. + * + * @param bmp The bitmap to test + * @param x The x location where the bitmap is drawn + * @param y The y location where the bitmap is drawn + * @param q The quad to test + * @return True if a drawn pixel in the bitmap will + * intersect with `q` when drawn. + * + * @attribute class bitmap + * @attribute method quad_collision + */ + bool bitmap_quad_collision(bitmap bmp, double x, double y, const quad &q); + + /** + * Tests if a bitmap drawn at `pt` would intersect with a quad. + * + * @param bmp The bitmap to test + * @param pt The location where the bitmap is drawn + * @param q The quad to test + * @return True if a drawn pixel in the cell of the bitmap will + * intersect with `q` when drawn. + * + * @attribute suffix at_point + * + * @attribute class bitmap + * @attribute method quad_collision + */ + bool bitmap_quad_collision(bitmap bmp, const point_2d& pt, const quad &q); + /** * Tests if a sprite will collide with a bitmap drawn at the indicated * location. @@ -496,6 +667,42 @@ namespace splashkit_lib */ bool sprite_ray_collision(sprite s, const point_2d& origin, const vector_2d& heading); + /** + * Tests if a sprite is drawn within an given area (circle). + * + * @param s The sprite to test + * @param c The circle to check + * @return True if the sprite it drawn in the circle area + * + * @attribute class sprite + * @attribute method circle_collision + */ + bool sprite_circle_collision(sprite s, const circle &c); + + /** + * Tests if a sprite is drawn within an given area (triangle). + * + * @param s The sprite to test + * @param t The triangle to check + * @return True if the sprite it drawn in the triangle area + * + * @attribute class sprite + * @attribute method triangle_collision + */ + bool sprite_triangle_collision(sprite s, const triangle &t); + + /** + * Tests if a sprite is drawn within an given area (quad). + * + * @param s The sprite to test + * @param q The quad to check + * @return True if the sprite it drawn in the quad area + * + * @attribute class sprite + * @attribute method quad_collision + */ + bool sprite_quad_collision(sprite s, const quad &q); + /** * Tests if two given sprites `s1` and `s2` are collided * @param s1 the first `sprite` to test @@ -598,5 +805,880 @@ namespace splashkit_lib */ bool bitmap_collision(bitmap bmp1, double x1, double y1, bitmap bmp2, double x2, double y2); + /** + * Returns the direction of the collision between two sprites + * relative to the collider sprite. If the sprites are not colliding, + * this function will return a zero vector. + * + * @param collider The sprite that is colliding + * @param collidee The sprite that is being collided with + * @return The direction of the collision relative to the collider sprite, + * expressed as a unit vector. If the sprites are not colliding, + * this function will return a zero vector. + * + * @attribute class sprite + * @attribute suffix between_sprites + */ + vector_2d calculate_collision_direction(const sprite collider, const sprite collidee); + + /** + * Returns the direction of the collision between a sprite + * and a rectangle relative to the sprite. If the sprite and + * rectangle are not colliding, this function will return a zero vector. + * + * @param collider The sprite that is colliding + * @param collidee The rectangle that is being collided with + * @return The direction of the collision relative to the sprite, + * expressed as a unit vector. If the sprite and rectangle are not colliding, + * this function will return a zero vector. + * + * @attribute class sprite + * @attribute suffix between_sprite_and_rectangle + */ + vector_2d calculate_collision_direction(const sprite collider, const rectangle& collidee); + + /** + * Returns the direction of the collision between a sprite + * and a circle relative to the sprite. If the sprite and + * circle are not colliding, this function will return a zero vector. + * + * @param collider The sprite that is colliding + * @param collidee The circle that is being collided with + * @return The direction of the collision relative to the sprite, + * expressed as a unit vector. If the sprite and circle are not colliding, + * this function will return a zero vector. + * + * @attribute class sprite + * @attribute suffix between_sprite_and_circle + */ + vector_2d calculate_collision_direction(const sprite collider, const circle& collidee); + + /** + * Returns the direction of the collision between a sprite + * and a triangle relative to the sprite. If the sprite and + * triangle are not colliding, this function will return a zero vector. + * + * @param collider The sprite that is colliding + * @param collidee The triangle that is being collided with + * @return The direction of the collision relative to the sprite, + * expressed as a unit vector. If the sprite and triangle are not colliding, + * this function will return a zero vector. + * + * @attribute class sprite + * @attribute suffix between_sprite_and_triangle + */ + vector_2d calculate_collision_direction(const sprite collider, const triangle& collidee); + + /** + * Returns the direction of the collision between a sprite + * and a quad relative to the sprite. If the sprite and + * quad are not colliding, this function will return a zero vector. + * + * @param collider The sprite that is colliding + * @param collidee The quad that is being collided with + * @return The direction of the collision relative to the sprite, + * expressed as a unit vector. If the sprite and quad are not colliding, + * this function will return a zero vector. + * + * @attribute class sprite + * @attribute suffix between_sprite_and_quad + */ + vector_2d calculate_collision_direction(const sprite collider, const quad& collidee); + + /** + * Returns the direction of the collision between a rectangle + * and a sprite relative to the rectangle. If the rectangle and + * sprite are not colliding, this function will return a zero vector. + * + * @param collider The rectangle that is colliding + * @param collidee The sprite that is being collided with + * @return The direction of the collision relative to the rectangle, + * expressed as a unit vector. If the rectangle and sprite are not colliding, + * this function will return a zero vector. + * + * @attribute class rectangle + * @attribute suffix between_rectangle_and_sprite + */ + vector_2d calculate_collision_direction(const rectangle& collider, const sprite collidee); + + /** + * Returns the direction of the collision between a collider rectangle + * and a collidee rectangle relative to the collider rectangle. If the rectangles are not + * colliding, this function will return a zero vector. + * + * @param collider The rectangle that is colliding + * @param collidee The rectangle that is being collided with + * @return The direction of the collision relative to the collider rectangle, + * expressed as a unit vector. If the rectangles are not colliding, this function + * will return a zero vector. + * + * @attribute class rectangle + * @attribute suffix between_rectangles + */ + vector_2d calculate_collision_direction(const rectangle& collider, const rectangle& collidee); + + /** + * Returns the direction of the collision between a rectangle + * and a circle relative to the rectangle. If the rectangle and + * sprite are not colliding, this function will return a zero vector. + * + * @param collider The rectangle that is colliding + * @param collidee The circle that is being collided with + * @return The direction of the collision relative to the rectangle, + * expressed as a unit vector. If the rectangle and circle are not colliding, + * this function will return a zero vector. + * + * @attribute class rectangle + * @attribute suffix between_rectangle_and_circle + */ + vector_2d calculate_collision_direction(const rectangle& collider, const circle& collidee); + + /** + * Returns the direction of the collision between a rectangle + * and a triangle relative to the rectangle. If the rectangle and + * sprite are not colliding, this function will return a zero vector. + * + * @param collider The rectangle that is colliding + * @param collidee The triangle that is being collided with + * @return The direction of the collision relative to the rectangle, + * expressed as a unit vector. If the rectangle and triangle are not colliding, + * this function will return a zero vector. + * + * @attribute class rectangle + * @attribute suffix between_rectangle_and_triangle + */ + vector_2d calculate_collision_direction(const rectangle& collider, const triangle& collidee); + + /** + * Returns the direction of the collision between a rectangle + * and a quad relative to the rectangle. If the rectangle and + * sprite are not colliding, this function will return a zero vector. + * + * @param collider The rectangle that is colliding + * @param collidee The quad that is being collided with + * @return The direction of the collision relative to the rectangle, + * expressed as a unit vector. If the rectangle and quad are not colliding, + * this function will return a zero vector. + * + * @attribute class rectangle + * @attribute suffix between_rectangle_and_quad + */ + vector_2d calculate_collision_direction(const rectangle& collider, const quad& collidee); + + /** + * Returns the direction of the collision between a circle + * and a sprite relative to the circle. If the circle and + * sprite are not colliding, this function will return a zero vector. + * + * @param collider The circle that is colliding + * @param collidee The sprite that is being collided with + * @return The direction of the collision relative to the circle, + * expressed as a unit vector. If the circle and sprite are not colliding, + * this function will return a zero vector. + * + * @attribute class circle + * @attribute suffix between_circle_and_sprite + */ + vector_2d calculate_collision_direction(const circle& collider, const sprite collidee); + + /** + * Returns the direction of the collision between a circle + * and a rectangle relative to the circle. If the circle and + * rectangle are not colliding, this function will return a zero vector. + * + * @param collider The circle that is colliding + * @param collidee The rectangle that is being collided with + * @return The direction of the collision relative to the circle, + * expressed as a unit vector. If the circle and rectangle are not colliding, + * this function will return a zero vector. + * + * @attribute class circle + * @attribute suffix between_circle_and_rectangle + */ + vector_2d calculate_collision_direction(const circle& collider, const rectangle& collidee); + + /** + * Returns the direction of the collision between a collider circle + * and a collidee circle relative to the collider circle. If the circles are not + * colliding, this function will return a zero vector. + * + * @param collider The circle that is colliding + * @param collidee The circle that is being collided with + * @return The direction of the collision relative to the collider circle, + * expressed as a unit vector. If the circles are not colliding, this function + * will return a zero vector. + * + * @attribute class circle + * @attribute suffix between_circles + */ + vector_2d calculate_collision_direction(const circle& collider, const circle& collidee); + + /** + * Returns the direction of the collision between a circle + * and a triangle relative to the circle. If the circle and + * triangle are not colliding, this function will return a zero vector. + * + * @param collider The circle that is colliding + * @param collidee The triangle that is being collided with + * @return The direction of the collision relative to the circle, + * expressed as a unit vector. If the circle and triangle are not colliding, + * this function will return a zero vector. + * + * @attribute class circle + * @attribute suffix between_circle_and_triangle + */ + vector_2d calculate_collision_direction(const circle& collider, const triangle& collidee); + + /** + * Returns the direction of the collision between a circle + * and a quad relative to the circle. If the circle and + * quad are not colliding, this function will return a zero vector. + * + * @param collider The circle that is colliding + * @param collidee The quad that is being collided with + * @return The direction of the collision relative to the circle, + * expressed as a unit vector. If the circle and quad are not colliding, + * this function will return a zero vector. + * + * @attribute class circle + * @attribute suffix between_circle_and_quad + */ + vector_2d calculate_collision_direction(const circle& collider, const quad& collidee); + + /** + * Returns the direction of the collision between a triangle + * and a sprite relative to the triangle. If the triangle and + * sprite are not colliding, this function will return a zero vector. + * + * @param collider The triangle that is colliding + * @param collidee The sprite that is being collided with + * @return The direction of the collision relative to the triangle, + * expressed as a unit vector. If the triangle and sprite are not colliding, + * this function will return a zero vector. + * + * @attribute class triangle + * @attribute suffix between_triangle_and_sprite + */ + vector_2d calculate_collision_direction(const triangle& collider, const sprite collidee); + + /** + * Returns the direction of the collision between a triangle + * and a rectangle relative to the triangle. If the triangle and + * rectangle are not colliding, this function will return a zero vector. + * + * @param collider The triangle that is colliding + * @param collidee The rectangle that is being collided with + * @return The direction of the collision relative to the triangle, + * expressed as a unit vector. If the triangle and rectangle are not colliding, + * this function will return a zero vector. + * + * @attribute class triangle + * @attribute suffix between_triangle_and_rectangle + */ + vector_2d calculate_collision_direction(const triangle& collider, const rectangle& collidee); + + /** + * Returns the direction of the collision between a triangle + * and a circle relative to the triangle. If the triangle and + * circle are not colliding, this function will return a zero vector. + * + * @param collider The triangle that is colliding + * @param collidee The circle that is being collided with + * @return The direction of the collision relative to the triangle, + * expressed as a unit vector. If the triangle and circle are not colliding, + * this function will return a zero vector. + * + * @attribute class triangle + * @attribute suffix between_triangle_and_circle + */ + vector_2d calculate_collision_direction(const triangle& collider, const circle& collidee); + + /** + * Returns the direction of the collision between a collider triangle + * and a collidee triangle relative to the collider triangle. If the triangles are not + * colliding, this function will return a zero vector. + * + * @param collider The triangle that is colliding + * @param collidee The triangle that is being collided with + * @return The direction of the collision relative to the collider triangle, + * expressed as a unit vector. If the triangles are not colliding, this function + * will return a zero vector. + * + * @attribute class triangle + * @attribute suffix between_triangles + */ + vector_2d calculate_collision_direction(const triangle& collider, const triangle& collidee); + + /** + * Returns the direction of the collision between a triangle + * and a quad relative to the triangle. If the triangle and + * quad are not colliding, this function will return a zero vector. + * + * @param collider The triangle that is colliding + * @param collidee The quad that is being collided with + * @return The direction of the collision relative to the triangle, + * expressed as a unit vector. If the triangle and quad are not colliding, + * this function will return a zero vector. + * + * @attribute class triangle + * @attribute suffix between_triangle_and_quad + */ + vector_2d calculate_collision_direction(const triangle& collider, const quad& collidee); + + /** + * Returns the direction of the collision between a quad + * and a sprite relative to the quad. If the quad and + * sprite are not colliding, this function will return a zero vector. + * + * @param collider The quad that is colliding + * @param collidee The sprite that is being collided with + * @return The direction of the collision relative to the quad, + * expressed as a unit vector. If the quad and sprite are not colliding, + * this function will return a zero vector. + * + * @attribute class quad + * @attribute suffix between_quad_and_sprite + */ + vector_2d calculate_collision_direction(const quad& collider, const sprite collidee); + + /** + * Returns the direction of the collision between a quad + * and a rectangle relative to the quad. If the quad and + * rectangle are not colliding, this function will return a zero vector. + * + * @param collider The quad that is colliding + * @param collidee The rectangle that is being collided with + * @return The direction of the collision relative to the quad, + * expressed as a unit vector. If the quad and rectangle are not colliding, + * this function will return a zero vector. + * + * @attribute class quad + * @attribute suffix between_quad_and_rectangle + */ + vector_2d calculate_collision_direction(const quad& collider, const rectangle& collidee); + + /** + * Returns the direction of the collision between a quad + * and a circle relative to the quad. If the quad and + * circle are not colliding, this function will return a zero vector. + * + * @param collider The quad that is colliding + * @param collidee The circle that is being collided with + * @return The direction of the collision relative to the quad, + * expressed as a unit vector. If the quad and circle are not colliding, + * this function will return a zero vector. + * + * @attribute class quad + * @attribute suffix between_quad_and_circle + */ + vector_2d calculate_collision_direction(const quad& collider, const circle& collidee); + + /** + * Returns the direction of the collision between a quad + * and a triangle relative to the quad. If the quad and + * triangle are not colliding, this function will return a zero vector. + * + * @param collider The quad that is colliding + * @param collidee The triangle that is being collided with + * @return The direction of the collision relative to the quad, + * expressed as a unit vector. If the quad and triangle are not colliding, + * this function will return a zero vector. + * + * @attribute class quad + * @attribute suffix between_quad_and_triangle + */ + vector_2d calculate_collision_direction(const quad& collider, const triangle& collidee); + + /** + * Returns the direction of the collision between a collider quad + * and a collidee quad relative to the collider quad. If the quads are not + * colliding, this function will return a zero vector. + * + * @param collider The quad that is colliding + * @param collidee The quad that is being collided with + * @return The direction of the collision relative to the collider quad, + * expressed as a unit vector. If the quads are not colliding, this function + * will return a zero vector. + * + * @attribute class quad + * @attribute suffix between_quads + */ + vector_2d calculate_collision_direction(const quad& collider, const quad& collidee); + + /** + * Resolves the collision between two sprites by moving the + * collider sprite to the edge of the collidee sprite. The direction of the + * resolution is determined by the `direction` parameter. If the sprites are not + * colliding, this function will return false. + * + * @param collider The sprite which will be altered if there is a collision + * @param collidee The sprite which will not be altered + * @param direction The direction of the collision relative to the collider sprite, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the sprites are colliding and the collision was resolved, + * false if the sprites are not colliding + * + * @atrribute class sprite + * @attribute suffix between_sprites + */ + bool resolve_collision(sprite collider, const sprite collidee, const vector_2d& direction); + + /** + * Resolves the collision between a sprite and a rectangle by moving the + * sprite to the edge of the rectangle. The direction of the + * resolution is determined by the `direction` parameter. If the sprite and + * rectangle are not colliding, this function will return false. + * + * @param collider The sprite which will be altered if there is a collision + * @param collidee The rectangle which will not be altered + * @param direction The direction of the collision relative to the sprite, + * expressed as a vector. If a zero vector is passed, + * the function will not resolve the collision. + * @return True if the sprite and rectangle are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class sprite + * @attribute suffix between_sprite_and_rectangle + */ + bool resolve_collision(sprite collider, const rectangle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a sprite and a circle by moving the + * sprite to the edge of the circle. The direction of the + * resolution is determined by the `direction` parameter. If the sprite and + * circle are not colliding, this function will return false. + * + * @param collider The sprite which will be altered if there is a collision + * @param collidee The circle which will not be altered + * @param direction The direction of the collision relative to the sprite, + * expressed as a vector. If a zero vector is passed, + * the function will not resolve the collision. + * @return True if the sprite and circle are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class sprite + * @attribute suffix between_sprite_and_circle + */ + bool resolve_collision(sprite collider, const circle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a sprite and a triangle by moving the + * sprite to the edge of the triangle. The direction of the + * resolution is determined by the `direction` parameter. If the sprite and + * triangle are not colliding, this function will return false. + * + * @param collider The sprite which will be altered if there is a collision + * @param collidee The triangle which will not be altered + * @param direction The direction of the collision relative to the sprite, + * expressed as a vector. If a zero vector is passed, + * the function will not resolve the collision. + * @return True if the sprite and triangle are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class sprite + * @attribute suffix between_sprite_and_triangle + */ + bool resolve_collision(sprite collider, const triangle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a sprite and a quad by moving the + * sprite to the edge of the quad. The direction of the + * resolution is determined by the `direction` parameter. If the sprite and + * quad are not colliding, this function will return false. + * + * @param collider The sprite which will be altered if there is a collision + * @param collidee The quad which will not be altered + * @param direction The direction of the collision relative to the sprite, + * expressed as a vector. If a zero vector is passed, + * the function will not resolve the collision. + * @return True if the sprite and quad are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class sprite + * @attribute suffix between_sprite_and_quad + */ + bool resolve_collision(sprite collider, const quad& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a rectangle and a sprite by moving the + * rectangle to the edge of the sprite. The direction of the + * resolution is determined by the `direction` parameter. If the rectangle and + * sprite are not colliding, this function will return false. + * + * @param collider The rectangle which will be altered if there is a collision + * @param collidee The sprite which will not be altered + * @param direction The direction of the collision relative to the rectangle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the rectangle and sprite are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class rectangle + * @attribute suffix between_rectangle_and_sprite + */ + bool resolve_collision(rectangle& collider, const sprite collidee, const vector_2d& direction); + + /** + * Resolves the collision between two rectangles by moving the + * collider rectangle to the edge of the collidee rectangle. The direction of the + * resolution is determined by the `direction` parameter. If the rectangles are not + * colliding, this function will return false. + * + * @param collider The rectangle which will be altered if there is a collision + * @param collidee The rectangle which will not be altered + * @param direction The direction of the collision relative to the collider rectangle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the rectangles are colliding and the collision was resolved, + * false if the rectangles are not colliding + * + * @atrribute class rectangle + * @attribute suffix between_rectangles + */ + bool resolve_collision(rectangle& collider, const rectangle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a rectangle and a circle by moving the + * rectangle to the edge of the circle. The direction of the + * resolution is determined by the `direction` parameter. If the rectangle and + * circle are not colliding, this function will return false. + * + * @param collider The rectangle which will be altered if there is a collision + * @param collidee The circle which will not be altered + * @param direction The direction of the collision relative to the rectangle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the rectangle and circle are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class rectangle + * @attribute suffix between_rectangle_and_circle + */ + bool resolve_collision(rectangle& collider, const circle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a rectangle and a triangle by moving the + * rectangle to the edge of the triangle. The direction of the + * resolution is determined by the `direction` parameter. If the rectangle and + * triangle are not colliding, this function will return false. + * + * @param collider The rectangle which will be altered if there is a collision + * @param collidee The triangle which will not be altered + * @param direction The direction of the collision relative to the rectangle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the rectangle and triangle are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class rectangle + * @attribute suffix between_rectangle_and_triangle + */ + bool resolve_collision(rectangle& collider, const triangle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a rectangle and a quad by moving the + * rectangle to the edge of the quad. The direction of the + * resolution is determined by the `direction` parameter. If the rectangle and + * quad are not colliding, this function will return false. + * + * @param collider The rectangle which will be altered if there is a collision + * @param collidee The quad which will not be altered + * @param direction The direction of the collision relative to the rectangle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the rectangle and quad are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class rectangle + * @attribute suffix between_rectangle_and_quad + */ + bool resolve_collision(rectangle& collider, const quad& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a circle and a sprite by moving the + * circle to the edge of the sprite. The direction of the + * resolution is determined by the `direction` parameter. If the circle and + * sprite are not colliding, this function will return false. + * + * @param collider The circle which will be altered if there is a collision + * @param collidee The sprite which will not be altered + * @param direction The direction of the collision relative to the circle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the circle and sprite are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class circle + * @attribute suffix between_circle_and_sprite + */ + bool resolve_collision(circle& collider, const sprite collidee, const vector_2d& direction); + + /** + * Resolves the collision between a circle and a rectangle by moving the + * circle to the edge of the rectangle. The direction of the + * resolution is determined by the `direction` parameter. If the circle and + * rectangle are not colliding, this function will return false. + * + * @param collider The circle which will be altered if there is a collision + * @param collidee The rectangle which will not be altered + * @param direction The direction of the collision relative to the circle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the circle and rectangle are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class circle + * @attribute suffix between_circle_and_rectangle + */ + bool resolve_collision(circle& collider, const rectangle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a collider circle and a collidee circle by moving the + * collider circle to the edge of the collidee circle. The direction of the + * resolution is determined by the `direction` parameter. If the circles are not + * colliding, this function will return false. + * + * @param collider The circle which will be altered if there is a collision + * @param collidee The circle which will not be altered + * @param direction The direction of the collision relative to the collider circle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the circles are colliding and the collision was resolved, + * false if the circles are not colliding + * + * @atrribute class circle + * @attribute suffix between_circles + */ + bool resolve_collision(circle& collider, const circle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a circle and a triangle by moving the + * circle to the edge of the triangle. The direction of the + * resolution is determined by the `direction` parameter. If the circle and + * triangle are not colliding, this function will return false. + * + * @param collider The circle which will be altered if there is a collision + * @param collidee The triangle which will not be altered + * @param direction The direction of the collision relative to the circle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the circle and triangle are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class circle + * @attribute suffix between_circle_and_triangle + */ + bool resolve_collision(circle& collider, const triangle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a circle and a quad by moving the + * circle to the edge of the quad. The direction of the + * resolution is determined by the `direction` parameter. If the circle and + * quad are not colliding, this function will return false. + * + * @param collider The circle which will be altered if there is a collision + * @param collidee The quad which will not be altered + * @param direction The direction of the collision relative to the circle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the circle and quad are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class circle + * @attribute suffix between_circle_and_quad + */ + bool resolve_collision(circle& collider, const quad& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a triangle and a sprite by moving the + * triangle to the edge of the sprite. The direction of the + * resolution is determined by the `direction` parameter. If the triangle and + * sprite are not colliding, this function will return false. + * + * @param collider The triangle which will be altered if there is a collision + * @param collidee The sprite which will not be altered + * @param direction The direction of the collision relative to the triangle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the triangle and sprite are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class triangle + * @attribute suffix between_triangle_and_sprite + */ + bool resolve_collision(triangle& collider, const sprite collidee, const vector_2d& direction); + + /** + * Resolves the collision between a triangle and a rectangle by moving the + * triangle to the edge of the rectangle. The direction of the + * resolution is determined by the `direction` parameter. If the triangle and + * rectangle are not colliding, this function will return false. + * + * @param collider The triangle which will be altered if there is a collision + * @param collidee The rectangle which will not be altered + * @param direction The direction of the collision relative to the triangle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the triangle and rectangle are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class triangle + * @attribute suffix between_triangle_and_rectangle + */ + bool resolve_collision(triangle& collider, const rectangle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a triangle and a circle by moving the + * triangle to the edge of the circle. The direction of the + * resolution is determined by the `direction` parameter. If the triangle and + * circle are not colliding, this function will return false. + * + * @param collider The triangle which will be altered if there is a collision + * @param collidee The circle which will not be altered + * @param direction The direction of the collision relative to the triangle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the triangle and circle are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class triangle + * @attribute suffix between_triangle_and_circle + */ + bool resolve_collision(triangle& collider, const circle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a collider triangle and a collidee triangle by moving the + * collider triangle to the edge of the collidee triangle. The direction of the + * resolution is determined by the `direction` parameter. If the triangles are not + * colliding, this function will return false. + * + * @param collider The triangle which will be altered if there is a collision + * @param collidee The triangle which will not be altered + * @param direction The direction of the collision relative to the collider triangle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the triangles are colliding and the collision was resolved, + * false if the triangles are not colliding + * + * @atrribute class triangle + * @attribute suffix between_triangles + */ + bool resolve_collision(triangle& collider, const triangle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a triangle and a quad by moving the + * triangle to the edge of the quad. The direction of the + * resolution is determined by the `direction` parameter. If the triangle and + * quad are not colliding, this function will return false. + * + * @param collider The triangle which will be altered if there is a collision + * @param collidee The quad which will not be altered + * @param direction The direction of the collision relative to the triangle, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the triangle and quad are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class triangle + * @attribute suffix between_triangle_and_quad + */ + bool resolve_collision(triangle& collider, const quad& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a quad and a sprite by moving the + * quad to the edge of the sprite. The direction of the + * resolution is determined by the `direction` parameter. If the quad and + * sprite are not colliding, this function will return false. + * + * @param collider The quad which will be altered if there is a collision + * @param collidee The sprite which will not be altered + * @param direction The direction of the collision relative to the quad, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the quad and sprite are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class quad + * @attribute suffix between_quad_and_sprite + */ + bool resolve_collision(quad& collider, const sprite collidee, const vector_2d& direction); + + /** + * Resolves the collision between a quad and a rectangle by moving the + * quad to the edge of the rectangle. The direction of the + * resolution is determined by the `direction` parameter. If the quad and + * rectangle are not colliding, this function will return false. + * + * @param collider The quad which will be altered if there is a collision + * @param collidee The rectangle which will not be altered + * @param direction The direction of the collision relative to the quad, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the quad and rectangle are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class quad + * @attribute suffix between_quad_and_rectangle + */ + bool resolve_collision(quad& collider, const rectangle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a quad and a circle by moving the + * quad to the edge of the circle. The direction of the + * resolution is determined by the `direction` parameter. If the quad and + * circle are not colliding, this function will return false. + * + * @param collider The quad which will be altered if there is a collision + * @param collidee The circle which will not be altered + * @param direction The direction of the collision relative to the quad, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the quad and circle are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class quad + * @attribute suffix between_quad_and_circle + */ + bool resolve_collision(quad& collider, const circle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a quad and a triangle by moving the + * quad to the edge of the triangle. The direction of the + * resolution is determined by the `direction` parameter. If the quad and + * triangle are not colliding, this function will return false. + * + * @param collider The quad which will be altered if there is a collision + * @param collidee The triangle which will not be altered + * @param direction The direction of the collision relative to the quad, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the quad and triangle are colliding and the collision + * was resolved, false if they are not colliding + * + * @atrribute class quad + * @attribute suffix between_quad_and_triangle + */ + bool resolve_collision(quad& collider, const triangle& collidee, const vector_2d& direction); + + /** + * Resolves the collision between a collider quad and a collidee quad by moving the + * collider quad to the edge of the collidee quad. The direction of the + * resolution is determined by the `direction` parameter. If the quads are not + * colliding, this function will return false. + * + * @param collider The quad which will be altered if there is a collision + * @param collidee The quad which will not be altered + * @param direction The direction of the collision relative to the collider quad, + * expressed as a vector. If a zero vector is passed, the function will + * not resolve the collision. + * @return True if the quads are colliding and the collision was resolved, + * false if the quads are not colliding + * + * @atrribute class quad + * @attribute suffix between_quads + */ + bool resolve_collision(quad& collider, const quad& collidee, const vector_2d& direction); + } #endif /* collisions_h */ diff --git a/coresdk/src/coresdk/images.cpp b/coresdk/src/coresdk/images.cpp index 239e6c82..23878b44 100644 --- a/coresdk/src/coresdk/images.cpp +++ b/coresdk/src/coresdk/images.cpp @@ -132,6 +132,40 @@ namespace splashkit_lib return result; } + bitmap load_bitmap_base64(string name, const char * image) + { + if (has_bitmap(name)) return bitmap_named(name); + + sk_drawing_surface surface; + bitmap result = nullptr; + + surface = sk_load_bitmap_from_memory(base64_decode_data(image)); + if ( not surface._data ) + { + LOG(WARNING) << cat({ "Error loading image for ", name}) ; + return nullptr; + } + + result = new _bitmap_data; + result->image.surface = surface; + + result->id = BITMAP_PTR; + result->cell_w = surface.width; + result->cell_h = surface.height; + result->cell_cols = 1; + result->cell_rows = 1; + result->cell_count = 1; + result->pixel_mask = nullptr; + + result->name = name; + + setup_collision_mask(result); + + _bitmaps[name] = result; + + return result; + } + bitmap create_bitmap(string name, int width, int height) { bitmap result = new(_bitmap_data); diff --git a/coresdk/src/coresdk/images.h b/coresdk/src/coresdk/images.h index 35f8397b..2b3589fa 100644 --- a/coresdk/src/coresdk/images.h +++ b/coresdk/src/coresdk/images.h @@ -34,6 +34,21 @@ namespace splashkit_lib */ bitmap load_bitmap(string name, string filename); + /** + * Loads and returns a bitmap. The supplied `image` is a base64 encoded + * .png file. The supplied `name` indicates the + * name to use to refer to this Bitmap in SplashKit. The `bitmap` can then + * be retrieved by passing this `name` to the `bitmap_named` function. + * + * @param name The name of the bitmap resource in SplashKit + * @param image The base64 encoded image + * @return The loaded bitmap + * + * @attribute class bitmap + * @attribute constructor true + */ + bitmap load_bitmap_base64(string name, const char * image); + /** * Lets you test if bitmap value is valid. This will return true when it is a valid bitmap. * diff --git a/coresdk/src/coresdk/line_geometry.cpp b/coresdk/src/coresdk/line_geometry.cpp index 6e69411c..102a4308 100644 --- a/coresdk/src/coresdk/line_geometry.cpp +++ b/coresdk/src/coresdk/line_geometry.cpp @@ -173,6 +173,12 @@ namespace splashkit_lib bool line_intersects_rect(const line &l, const rectangle &rect) { + // if the line is completely within the rectangle, then it intersects + if (point_in_rectangle(l.start_point, rect) && point_in_rectangle(l.end_point, rect)) + { + return true; + } + return line_intersects_lines(l, lines_from(rect)); } diff --git a/coresdk/src/coresdk/quad_geometry.cpp b/coresdk/src/coresdk/quad_geometry.cpp index 77d87830..334523ea 100644 --- a/coresdk/src/coresdk/quad_geometry.cpp +++ b/coresdk/src/coresdk/quad_geometry.cpp @@ -92,6 +92,48 @@ namespace splashkit_lib return result; } + bool quad_ray_intersection(const point_2d &origin, const vector_2d &heading, const quad &q) + { + point_2d hit_point; + double hit_distance; + return quad_ray_intersection(origin, heading, q, hit_point, hit_distance); + } + + bool quad_ray_intersection(const point_2d &origin, const vector_2d &heading, const quad &q, point_2d &hit_point, double &hit_distance) + { + vector_2d unit_heading = unit_vector(heading); + + // check whether unit heading is a zero vector + if (vector_magnitude_squared(unit_heading) < __DBL_EPSILON__) + { + return false; + } + + vector tris = triangles_from(q); + + bool result = false; + double closest_distance = __DBL_MAX__; + + for (size_t i = 0; i < tris.size(); i++) + { + point_2d p; + double d; + if (triangle_ray_intersection(origin, unit_heading, tris[i], p, d)) + { + double distance_to_intersection = vector_magnitude(vector_point_to_point(origin, p)); + if (distance_to_intersection < closest_distance) + { + closest_distance = distance_to_intersection; + hit_point = p; + hit_distance = d; + result = true; + } + } + } + + return result; + } + bool quads_intersect(const quad &q1, const quad &q2) { vector q1_triangles = triangles_from(q1); diff --git a/coresdk/src/coresdk/quad_geometry.h b/coresdk/src/coresdk/quad_geometry.h index 8330c1eb..e9412d91 100644 --- a/coresdk/src/coresdk/quad_geometry.h +++ b/coresdk/src/coresdk/quad_geometry.h @@ -97,6 +97,35 @@ namespace splashkit_lib */ void set_quad_point(quad &q, int idx, const point_2d &value); + /** + * Detects if a ray intersects a quad. + * + * @param origin The starting point of the ray + * @param heading The direction of the ray as a vector + * @param q The quad to check for intersection + * @returns True if the ray intersects the quad, false otherwise + */ + bool quad_ray_intersection(const point_2d &origin, const vector_2d &heading, const quad &q); + + /** + * Detects if a ray intersects a quad. If an intersection is found, the + * `hit_point` and `hit_distance` are set to the point of intersection and the + * distance from the ray's origin to the intersection point. If the ray's `origin` + * is contained within the quad, `hit_point` is set to the `origin` and `hit_distance` + * is set to 0. If no intersection is found, `hit_point` and `hit_distance` are not modified. + * + * @param origin The starting point of the ray + * @param heading The direction of the ray as a vector + * @param q The quad to check for intersection + * @param hit_point The point to set to where the ray intersects the quad + * @param hit_distance The double to set to the distance from the ray's origin to + * the intersection point + * @returns True if the ray intersects the quad, false otherwise + * + * @attribute suffix with_hit_point_and_distance + */ + bool quad_ray_intersection(const point_2d &origin, const vector_2d &heading, const quad &q, point_2d &hit_point, double &hit_distance); + /** * Returns true if two quads intersect. * diff --git a/coresdk/src/coresdk/rectangle_geometry.cpp b/coresdk/src/coresdk/rectangle_geometry.cpp index 09a41943..cb9d8e47 100644 --- a/coresdk/src/coresdk/rectangle_geometry.cpp +++ b/coresdk/src/coresdk/rectangle_geometry.cpp @@ -223,4 +223,67 @@ namespace splashkit_lib return result; } + + bool rectangle_ray_intersection(const point_2d &origin, const vector_2d &heading, const rectangle &rect) + { + point_2d hit_point; + double hit_distance; + return rectangle_ray_intersection(origin, heading, rect, hit_point, hit_distance); + } + + bool rectangle_ray_intersection(const point_2d &origin, const vector_2d &heading, const rectangle &rect, point_2d &hit_point, double &hit_distance) + { + vector_2d unit_heading = unit_vector(heading); + + // check whether unit heading is a zero vector + if (vector_magnitude_squared(unit_heading) < __DBL_EPSILON__) + { + return false; + } + + if (point_in_rectangle(origin, rect)) + { + hit_point = origin; + hit_distance = 0.0; + return true; + } + + // Compute the inverse of the ray direction (for faster calculations) + vector_2d inv_dir = vector_to(1.0 / unit_heading.x, 1.0 / unit_heading.y); + + // Calculate entry and exit distances for the rectangle's x and y boundaries + double entry_distance_x = (rect.x - origin.x) * inv_dir.x; + double exit_distance_x = (rect.x + rect.width - origin.x) * inv_dir.x; + + double entry_distance_y = (rect.y - origin.y) * inv_dir.y; + double exit_distance_y = (rect.y + rect.height - origin.y) * inv_dir.y; + + // Determine the nearest entry point and the farthest exit point + double min_intersection_distance = std::max(std::min(entry_distance_x, exit_distance_x), std::min(entry_distance_y, exit_distance_y)); + double max_intersection_distance = std::min(std::max(entry_distance_x, exit_distance_x), std::max(entry_distance_y, exit_distance_y)); + + // If the ray misses the rectangle or the intersection is behind the ray's origin + if (min_intersection_distance > max_intersection_distance || max_intersection_distance < 0.0) + { + return false; + } + + // Compute the point of intersection + hit_distance = min_intersection_distance; + vector_2d hit_vector = vector_multiply(unit_heading, min_intersection_distance); + hit_point = point_at(origin.x + hit_vector.x, origin.y + hit_vector.y); + + return true; + } + + bool rectangle_circle_intersect(const rectangle &rect, const circle &c) + { + if (point_in_rectangle(c.center, rect)) + { + return true; + } + + point_2d closest = closest_point_on_rect_from_circle(c, rect); + return point_in_circle(closest, c); + } } diff --git a/coresdk/src/coresdk/rectangle_geometry.h b/coresdk/src/coresdk/rectangle_geometry.h index 32266caf..e5369064 100644 --- a/coresdk/src/coresdk/rectangle_geometry.h +++ b/coresdk/src/coresdk/rectangle_geometry.h @@ -178,5 +178,43 @@ namespace splashkit_lib */ rectangle inset_rectangle(const rectangle &rect, float inset_amount); + /** + * Detects if a ray intersects a rectangle. + * + * @param origin The starting point of the ray + * @param heading The direction of the ray as a vector + * @param rect The rectangle to check for intersection + * @returns True if the ray intersects the rectangle, false otherwise + */ + bool rectangle_ray_intersection(const point_2d &origin, const vector_2d &heading, const rectangle &rect); + + /** + * Detects if a ray intersects a rectangle. If an intersection is found, the + * `hit_point` and `hit_distance` are set to the point of intersection and the + * distance from the ray's origin to the intersection point. If the ray's `origin` + * is contained within the rectangle, `hit_point` is set to the `origin` and `hit_distance` + * is set to 0. If no intersection is found, `hit_point` and `hit_distance` are not modified. + * + * @param origin The starting point of the ray + * @param heading The direction of the ray as a vector + * @param rect The rectangle to check for intersection + * @param hit_point The point to set to where the ray intersects the rectangle + * @param hit_distance The double to set to the distance from the ray's origin to + * the intersection point + * @returns True if the ray intersects the rectangle, false otherwise + * + * @attribute suffix with_hit_point_and_distance + */ + bool rectangle_ray_intersection(const point_2d &origin, const vector_2d &heading, const rectangle &rect, point_2d &hit_point, double &hit_distance); + + /** + * Detects if a rectangle intersects with a circle. + * + * @param rect The rectangle to test + * @param c The circle to test + * @return True if the rectangle and circle intersect, false otherwise + */ + bool rectangle_circle_intersect(const rectangle &rect, const circle &c); + } #endif /* rectangle_geometry_H */ diff --git a/coresdk/src/coresdk/triangle_geometry.cpp b/coresdk/src/coresdk/triangle_geometry.cpp index 5c635133..652f12bb 100644 --- a/coresdk/src/coresdk/triangle_geometry.cpp +++ b/coresdk/src/coresdk/triangle_geometry.cpp @@ -7,6 +7,7 @@ // #include "geometry.h" +#include #include using std::vector; @@ -102,6 +103,87 @@ namespace splashkit_lib or point_in_triangle(point_at(r, b), tri); } + bool triangle_ray_intersection(const point_2d &origin, const vector_2d &heading, const triangle &tri) + { + point_2d hit_point; + double hit_distance; + return triangle_ray_intersection(origin, heading, tri, hit_point, hit_distance); + } + + bool triangle_ray_intersection(const point_2d &origin, const vector_2d &heading, const triangle &tri, point_2d &hit_point, double &hit_distance) + { + vector_2d unit_heading = unit_vector(heading); + + // check whether unit heading is a zero vector + if (vector_magnitude_squared(unit_heading) < __DBL_EPSILON__) + { + return false; + } + + if (point_in_triangle(origin, tri)) + { + hit_point = origin; + hit_distance = 0.0; + return true; + } + + bool has_collision = false; + double closest_distance = __DBL_MAX__; + + // Iterate through each edge of the triangle + for (int i = 0; i < 3; ++i) + { + point_2d start_point = tri.points[i]; // Start point of the edge + point_2d end_point = tri.points[(i + 1) % 3]; // End point of the edge + + vector_2d edge_vector = vector_point_to_point(start_point, end_point); // Edge vector + vector_2d origin_to_edge = vector_point_to_point(origin, start_point); + + // Cross product to determine parallelism + double cross_ray_and_edge = (unit_heading.x * edge_vector.y) - (unit_heading.y * edge_vector.x); + if (std::fabs(cross_ray_and_edge) < __DBL_EPSILON__) continue; // Skip edges nearly parallel to the ray + + // Calculate intersection parameters + double ray_parameter = ((origin_to_edge.x * edge_vector.y) - (origin_to_edge.y * edge_vector.x)) / cross_ray_and_edge; + double edge_parameter = ((origin_to_edge.x * unit_heading.y) - (origin_to_edge.y * unit_heading.x)) / cross_ray_and_edge; + + // Check if intersection occurs within the edge and along the ray + if (ray_parameter >= 0.0 && edge_parameter >= 0.0 && edge_parameter <= 1.0) + { + // Compute the intersection point + vector_2d scaled_ray = vector_multiply(unit_heading, ray_parameter); + point_2d current_intersection = point_offset_by(origin, scaled_ray); + + // Check if this is the closest intersection + double distance_to_intersection = vector_magnitude(vector_point_to_point(origin, current_intersection)); + if (distance_to_intersection < closest_distance) + { + closest_distance = distance_to_intersection; + hit_point = current_intersection; + has_collision = true; + } + } + } + + if (has_collision) + { + hit_distance = closest_distance; + } + return has_collision; + } + + bool triangle_quad_intersect(const triangle &tri, const quad &q) + { + vector q_tris = triangles_from(q); + + for (size_t i = 0; i < q_tris.size(); i++) + { + if (triangles_intersect(tri, q_tris[i])) return true; + } + + return false; + } + bool triangles_intersect(const triangle &t1, const triangle &t2) { // Test if any of the points lie within the other triangle. diff --git a/coresdk/src/coresdk/triangle_geometry.h b/coresdk/src/coresdk/triangle_geometry.h index 5044ecfd..83e15f8f 100644 --- a/coresdk/src/coresdk/triangle_geometry.h +++ b/coresdk/src/coresdk/triangle_geometry.h @@ -46,6 +46,44 @@ namespace splashkit_lib */ bool triangle_rectangle_intersect(const triangle &tri, const rectangle &rect); + /** + * Returns true if the triangle intersects with the quad. + * + * @param tri The triangle to test + * @param q The quad to test + * @return True if the triangle and quad intersect + */ + bool triangle_quad_intersect(const triangle &tri, const quad &q); + + /** + * Detects if a ray intersects a triangle. + * + * @param origin The starting point of the ray + * @param heading The direction of the ray as a vector + * @param tri The triangle to check for intersection + * @returns True if the ray intersects the triangle, false otherwise + */ + bool triangle_ray_intersection(const point_2d &origin, const vector_2d &heading, const triangle &tri); + + /** + * Detects if a ray intersects a triangle. If an intersection is found, the + * `hit_point` and `hit_distance` are set to the point of intersection and the + * distance from the ray's origin to the intersection point. If the ray's `origin` + * is contained within the triangle, `hit_point` is set to the `origin` and `hit_distance` + * is set to 0. If no intersection is found, `hit_point` and `hit_distance` are not modified. + * + * @param origin The starting point of the ray + * @param heading The direction of the ray as a vector + * @param tri The triangle to check for intersection + * @param hit_point The point to set to where the ray intersects the triangle + * @param hit_distance The double to set to the distance from the ray's origin to + * the intersection point + * @returns True if the ray intersects the triangle, false otherwise + * + * @attribute suffix with_hit_point_and_distance + */ + bool triangle_ray_intersection(const point_2d &origin, const vector_2d &heading, const triangle &tri, point_2d &hit_point, double &hit_distance); + /** * Returns true if the two triangles intersect. * diff --git a/coresdk/src/test/test_geometry.cpp b/coresdk/src/test/test_geometry.cpp index 693cd9cf..2ae362dc 100644 --- a/coresdk/src/test/test_geometry.cpp +++ b/coresdk/src/test/test_geometry.cpp @@ -16,6 +16,15 @@ using namespace std; using namespace splashkit_lib; +enum class geometry_test_shape_type +{ + RECTANGLE, + CIRCLE, + TRIANGLE, + QUAD, + NONE +}; + void test_points() { point_2d p = point_at(10, 20); @@ -239,6 +248,131 @@ void test_triangle() close_window(w1); } +void draw_ray_shape_intersection(const line& l, const point_2d& circ_center, color& clr) +{ + draw_line(COLOR_BLACK, l); + fill_circle(COLOR_RED, circle_at(circ_center, 5.0)); + clr = COLOR_RED; +} + +geometry_test_shape_type calculate_min_dist_shape(bool rect, bool circ, bool tri, bool quad, double rect_dist, + double circ_dist, double tri_dist, double quad_dist) +{ + double min_dist = __DBL_MAX__; + geometry_test_shape_type result = geometry_test_shape_type::NONE; + + if (rect && rect_dist < min_dist) + { + min_dist = rect_dist; + result = geometry_test_shape_type::RECTANGLE; + } + if (circ && circ_dist < min_dist) + { + min_dist = circ_dist; + result = geometry_test_shape_type::CIRCLE; + } + if (tri && tri_dist < min_dist) + { + min_dist = tri_dist; + result = geometry_test_shape_type::TRIANGLE; + } + if (quad && quad_dist < min_dist) + { + min_dist = quad_dist; + result = geometry_test_shape_type::QUAD; + } + + return result; +} + +struct test_shape +{ + color clr = COLOR_BLUE; + point_2d hit_point = point_at(0.0, 0.0); + double distance = 0.0; + bool intersection = false; +}; + +void test_rect_circ_tri_ray_intersection() +{ + auto r1 = rectangle_from(100.0, 100.0, 100.0, 100.0); + test_shape r1_shape; + auto c1 = circle_at(300.0, 200.0, 60.0); + test_shape c1_shape; + auto t1 = triangle_from(400.0, 400.0, 550.0, 410.0, 390.0, 550.0); + test_shape t1_shape; + auto q1 = quad_from(100.0, 300.0, 200.0, 350.0, 100.0, 550.0, 200.0, 500.0); + test_shape q1_shape; + auto player = point_at(300.0, 300.0); + + bool rect_true = rectangle_ray_intersection(point_at(90.0, 110.0), vector_to(1.0, 0.0), r1); + bool rect_false = rectangle_ray_intersection(point_at(90.0, 110.0), vector_to(__DBL_MIN__, 0.0), r1); + + cout << "Rectangle ray intersection test (should be true): " << rect_true << endl; + cout << "Rectangle ray intersection test (should be false): " << rect_false << endl; + + window w1 = open_window("Ray Intersection Tests", 600, 800); + while ( !window_close_requested(w1) ) { + process_events(); + + clear_screen(COLOR_WHEAT); + + if (key_down(UP_KEY)) + player.y -= 1; + if (key_down(DOWN_KEY)) + player.y += 1; + if (key_down(LEFT_KEY)) + player.x -= 1; + if (key_down(RIGHT_KEY)) + player.x += 1; + + vector_2d player_heading = vector_point_to_point(player, mouse_position()); + + r1_shape.intersection = rectangle_ray_intersection(player, player_heading, r1, r1_shape.hit_point, r1_shape.distance); + c1_shape.intersection = circle_ray_intersection(player, player_heading, c1, c1_shape.hit_point, c1_shape.distance); + t1_shape.intersection = triangle_ray_intersection(player, player_heading, t1, t1_shape.hit_point, t1_shape.distance); + q1_shape.intersection = quad_ray_intersection(player, player_heading, q1, q1_shape.hit_point, q1_shape.distance); + + r1_shape.clr = COLOR_BLUE; + c1_shape.clr = COLOR_BLUE; + t1_shape.clr = COLOR_BLUE; + q1_shape.clr = COLOR_BLUE; + + geometry_test_shape_type min_dist_shape = calculate_min_dist_shape(r1_shape.intersection, c1_shape.intersection, + t1_shape.intersection, q1_shape.intersection, + r1_shape.distance, c1_shape.distance, + t1_shape.distance, q1_shape.distance); + + switch (min_dist_shape) + { + case geometry_test_shape_type::RECTANGLE: + draw_ray_shape_intersection(line_from(player, r1_shape.hit_point), r1_shape.hit_point, r1_shape.clr); + break; + case geometry_test_shape_type::CIRCLE: + draw_ray_shape_intersection(line_from(player, c1_shape.hit_point), c1_shape.hit_point, c1_shape.clr); + break; + case geometry_test_shape_type::TRIANGLE: + draw_ray_shape_intersection(line_from(player, t1_shape.hit_point), t1_shape.hit_point, t1_shape.clr); + break; + case geometry_test_shape_type::QUAD: + draw_ray_shape_intersection(line_from(player, q1_shape.hit_point), q1_shape.hit_point, q1_shape.clr); + break; + default: // shape_type::NONE: + draw_line(COLOR_BLACK, player, point_offset_by(player, vector_multiply(unit_vector(player_heading), 1000.0))); + }; + + draw_rectangle(r1_shape.clr, r1); + draw_circle(c1_shape.clr, c1); + draw_triangle(t1_shape.clr, t1); + draw_quad(q1_shape.clr, q1); + + draw_circle(COLOR_BLACK, circle_at(player, 5.0)); + + refresh_screen(); + } + close_window(w1); +} + void test_tangent_points() { circle c1 = circle_at(100, 100, 50); @@ -382,7 +516,7 @@ void test_bitmap_ray_collision() circle ray_origin_circle = circle_at(ray_origin, 3.0); draw_circle(COLOR_BLUE, ray_origin_circle); - + refresh_screen(); } close_window(w1); @@ -394,6 +528,7 @@ void run_geometry_test() test_points(); test_lines(); test_triangle(); + test_rect_circ_tri_ray_intersection(); test_tangent_points(); test_triangle_rectangle_intersect(); test_bitmap_ray_collision(); diff --git a/coresdk/src/test/test_graphics.cpp b/coresdk/src/test/test_graphics.cpp index 616aafce..1ee09d2b 100644 --- a/coresdk/src/test/test_graphics.cpp +++ b/coresdk/src/test/test_graphics.cpp @@ -18,6 +18,8 @@ using namespace std; using namespace splashkit_lib; +extern const char splashkit_logo_base64[]; + void test_drawing_on_new_window() { bitmap user_image; @@ -130,6 +132,8 @@ void run_graphics_test() color in_clr = string_to_color("#ffeebbaa"); color clr; + + bitmap bmp = load_bitmap_base64("SplashScreen", splashkit_logo_base64); while ( ! window_close_requested(w1) ) { @@ -145,6 +149,8 @@ void run_graphics_test() } fill_rectangle(in_clr, 100, 100, 100, 100); + + draw_bitmap("SplashScreen", 5, 5); clr = get_pixel(mouse_position()); @@ -172,3 +178,5 @@ void run_graphics_test() close_window(w1); } + +const char splashkit_logo_base64[] = {"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"}; diff --git a/coresdk/src/test/test_sprites.cpp b/coresdk/src/test/test_sprites.cpp index 84fafccc..71162589 100644 --- a/coresdk/src/test/test_sprites.cpp +++ b/coresdk/src/test/test_sprites.cpp @@ -13,12 +13,159 @@ #include "input.h" #include "sprites.h" #include "window_manager.h" +#include +#include using namespace splashkit_lib; +enum class object_type +{ + SPRITE, + RECTANGLE, + CIRCLE, + TRIANGLE, + QUAD, +}; + +/* +* The type of collision test to perform. +* MULTIPLE_DYNAMIC uses classify_collision_direction on each object to determine +* the direction of the collision before calling resolve_collision. +* The FIXED types do not use classify_collision_direction and instead use fixed +* static collision directions determined by each object's position. For example, +* the top-most object will always use a collision direction of DIRECTION_TOP. +*/ +enum class collision_test_type +{ + MULTIPLE_DYNAMIC, + SPRITE_FIXED, + RECTANGLE_FIXED, + CIRCLE_FIXED, + TRIANGLE_FIXED, + QUAD_FIXED, +}; + +enum class sprite_perimeter_segment +{ + TOP_LEFT, + TOP_CENTER, + TOP_RIGHT, + BOTTOM_LEFT, + BOTTOM_CENTER, + BOTTOM_RIGHT, + LEFT_TOP, + LEFT_CENTER, + LEFT_BOTTOM, + RIGHT_TOP, + RIGHT_CENTER, + RIGHT_BOTTOM, +}; + +const vector_2d DIRECTION_TOP = vector_to(0.0, -1.0); +const vector_2d DIRECTION_BOTTOM = vector_to(0.0, 1.0); +const vector_2d DIRECTION_LEFT = vector_to(-1.0, 0.0); +const vector_2d DIRECTION_RIGHT = vector_to(1.0, 0.0); +const vector_2d DIRECTION_TOP_LEFT = unit_vector(vector_to(-1.0, -1.0)); +const vector_2d DIRECTION_TOP_RIGHT = unit_vector(vector_to(1.0, -1.0)); +const vector_2d DIRECTION_BOTTOM_LEFT = unit_vector(vector_to(-1.0, 1.0)); +const vector_2d DIRECTION_BOTTOM_RIGHT = unit_vector(vector_to(1.0, 1.0)); +const vector_2d DIRECTION_NONE = vector_to(0.0, 0.0); + +void draw_rect_perimeter_segment(const rectangle& r, sprite_perimeter_segment segment, color clr, int line_width) +{ + switch (segment) + { + case sprite_perimeter_segment::TOP_LEFT: + draw_line(clr, r.x, r.y, r.x + r.width / 3.0, r.y, option_line_width(line_width)); + break; + case sprite_perimeter_segment::TOP_CENTER: + draw_line(clr, r.x + r.width / 3.0, r.y, r.x + r.width * 2.0 / 3.0, r.y, option_line_width(line_width)); + break; + case sprite_perimeter_segment::TOP_RIGHT: + draw_line(clr, r.x + r.width * 2.0 / 3.0, r.y, r.x + r.width, r.y, option_line_width(line_width)); + break; + case sprite_perimeter_segment::BOTTOM_LEFT: + draw_line(clr, r.x, r.y + r.height, r.x + r.width / 3.0, r.y + r.height, option_line_width(line_width)); + break; + case sprite_perimeter_segment::BOTTOM_CENTER: + draw_line(clr, r.x + r.width / 3.0, r.y + r.height, r.x + r.width * 2.0 / 3.0, r.y + r.height, option_line_width(line_width)); + break; + case sprite_perimeter_segment::BOTTOM_RIGHT: + draw_line(clr, r.x + r.width * 2.0 / 3.0, r.y + r.height, r.x + r.width, r.y + r.height, option_line_width(line_width)); + break; + case sprite_perimeter_segment::LEFT_TOP: + draw_line(clr, r.x, r.y, r.x, r.y + r.height / 3.0, option_line_width(line_width)); + break; + case sprite_perimeter_segment::LEFT_CENTER: + draw_line(clr, r.x, r.y + r.height / 3.0, r.x, r.y + r.height * 2.0 / 3.0, option_line_width(line_width)); + break; + case sprite_perimeter_segment::LEFT_BOTTOM: + draw_line(clr, r.x, r.y + r.height * 2.0 / 3.0, r.x, r.y + r.height, option_line_width(line_width)); + break; + case sprite_perimeter_segment::RIGHT_TOP: + draw_line(clr, r.x + r.width, r.y, r.x + r.width, r.y + r.height / 3.0, option_line_width(line_width)); + break; + case sprite_perimeter_segment::RIGHT_CENTER: + draw_line(clr, r.x + r.width, r.y + r.height / 3.0, r.x + r.width, r.y + r.height * 2.0 / 3.0, option_line_width(line_width)); + break; + case sprite_perimeter_segment::RIGHT_BOTTOM: + draw_line(clr, r.x + r.width, r.y + r.height * 2.0 / 3.0, r.x + r.width, r.y + r.height, option_line_width(line_width)); + break; + }; +} + +void draw_rect_perimeter_segments(const rectangle& r, const std::vector& segments, color clr, int line_width) +{ + for (const auto& segment : segments) + { + draw_rect_perimeter_segment(r, segment, clr, line_width); + } +} + +void draw_rect_perimeter_by_collision(const rectangle& r, const vector_2d& dir, color clr, int line_width) +{ + if (dir.x == DIRECTION_NONE.x && dir.y == DIRECTION_NONE.y) + { + return; + } + + if (dir.x == DIRECTION_TOP.x && dir.y == DIRECTION_TOP.y) + { + draw_rect_perimeter_segments(r, {sprite_perimeter_segment::TOP_LEFT, sprite_perimeter_segment::TOP_CENTER, sprite_perimeter_segment::TOP_RIGHT}, clr, line_width); + } + else if (dir.x == DIRECTION_BOTTOM.x && dir.y == DIRECTION_BOTTOM.y) + { + draw_rect_perimeter_segments(r, {sprite_perimeter_segment::BOTTOM_LEFT, sprite_perimeter_segment::BOTTOM_CENTER, sprite_perimeter_segment::BOTTOM_RIGHT}, clr, line_width); + } + else if (dir.x == DIRECTION_LEFT.x && dir.y == DIRECTION_LEFT.y) + { + draw_rect_perimeter_segments(r, {sprite_perimeter_segment::LEFT_TOP, sprite_perimeter_segment::LEFT_CENTER, sprite_perimeter_segment::LEFT_BOTTOM}, clr, line_width); + } + else if (dir.x == DIRECTION_RIGHT.x && dir.y == DIRECTION_RIGHT.y) + { + draw_rect_perimeter_segments(r, {sprite_perimeter_segment::RIGHT_TOP, sprite_perimeter_segment::RIGHT_CENTER, sprite_perimeter_segment::RIGHT_BOTTOM}, clr, line_width); + } + else if (dir.x == DIRECTION_TOP_LEFT.x && dir.y == DIRECTION_TOP_LEFT.y) + { + draw_rect_perimeter_segments(r, {sprite_perimeter_segment::TOP_LEFT, sprite_perimeter_segment::LEFT_TOP}, clr, line_width); + } + else if (dir.x == DIRECTION_TOP_RIGHT.x && dir.y == DIRECTION_TOP_RIGHT.y) + { + draw_rect_perimeter_segments(r, {sprite_perimeter_segment::TOP_RIGHT, sprite_perimeter_segment::RIGHT_TOP}, clr, line_width); + } + else if (dir.x == DIRECTION_BOTTOM_LEFT.x && dir.y == DIRECTION_BOTTOM_LEFT.y) + { + draw_rect_perimeter_segments(r, {sprite_perimeter_segment::BOTTOM_LEFT, sprite_perimeter_segment::LEFT_BOTTOM}, clr, line_width); + } + else if (dir.x == DIRECTION_BOTTOM_RIGHT.x && dir.y == DIRECTION_BOTTOM_RIGHT.y) + { + draw_rect_perimeter_segments(r, {sprite_perimeter_segment::BOTTOM_RIGHT, sprite_perimeter_segment::RIGHT_BOTTOM}, clr, line_width); + } +} + void sprite_test() { - sprite sprt, s2; + sprite sprt, s2, s3, s4; triangle tri, init_tri; triangle tri_b, init_tri_b; rectangle r; @@ -38,6 +185,17 @@ void sprite_test() sprite_set_x(s2, 100); sprite_set_y(s2, 100); + s3 = create_sprite(bitmap_named("rocket_sprt.png")); + sprite_set_move_from_anchor_point(s3, true); + sprite_set_x(s3, 100); + sprite_set_y(s3, 400); + + s4 = create_sprite(bitmap_named("rocket_sprt.png")); + sprite_set_move_from_anchor_point(s4, true); + sprite_set_x(s4, 400); + sprite_set_y(s4, 400); + sprite_set_collision_kind(s4, AABB_COLLISIONS); + r = rectangle_from(400, 100, 100, 50); q = quad_from(r); apply_matrix(matrix_multiply(translation_matrix(0, 50), rotation_matrix(45)), q); @@ -97,6 +255,8 @@ void sprite_test() draw_sprite(sprt); draw_sprite(s2); + draw_sprite(s3); + draw_sprite(s4); if (sprite_rectangle_collision(sprt, r)) { @@ -132,7 +292,22 @@ void sprite_test() draw_circle(COLOR_RED, sprite_collision_circle(s2)); } + if (sprite_collision(sprt, s3)) + { + vector_2d dir = calculate_collision_direction(sprt, s3); + resolve_collision(sprt, s3, dir); + draw_rect_perimeter_by_collision(sprite_collision_rectangle(sprt), dir, COLOR_RED, 4); + } + + if (sprite_collision(sprt, s4)) + { + vector_2d dir = calculate_collision_direction(sprt, s4); + resolve_collision(sprt, s4, dir); + draw_rect_perimeter_by_collision(sprite_collision_rectangle(sprt), dir, COLOR_RED, 4); + } + draw_rectangle(COLOR_GREEN, sprite_collision_rectangle(sprt)); + draw_rectangle(COLOR_RED, sprite_collision_rectangle(s4)); draw_line(COLOR_GREEN, line_from(sprite_center_point(sprt), matrix_multiply(rotation_matrix(sprite_rotation(sprt)), vector_multiply(sprite_velocity(sprt), 30.0)))); @@ -144,6 +319,665 @@ void sprite_test() close_all_windows(); } +void reset_sprite(sprite s) +{ + sprite_set_x(s, 300.0); + sprite_set_y(s, 300.0); + sprite_set_rotation(s, 0.0f); + sprite_set_scale(s, 1.0f); + sprite_set_dx(s, 0.0); + sprite_set_dy(s, 0.0); + sprite_set_collision_kind(s, PIXEL_COLLISIONS); +} + +void reset_rectangle(rectangle &r) +{ + r = rectangle_from(300.0, 300.0, 150.0, 50.0); +} + +void reset_circle(circle &c) +{ + c = circle_at(300.0, 300.0, 40.0); +} + +void reset_triangle(triangle &t) +{ + t = triangle_from(300.0, 300.0, 400.0, 300.0, 350.0, 250.0); +} + +void reset_quad(quad &q) +{ + rectangle r = rectangle_from(300, 300, 100, 50); + q = quad_from(r); + apply_matrix(matrix_multiply(translation_matrix(0.0, -150.0), rotation_matrix(45.0)), q); +} + +void _move_obj_by_vector(sprite& obj, const vector_2d& amount) +{ + sprite_set_x(obj, sprite_x(obj) + amount.x); + sprite_set_y(obj, sprite_y(obj) + amount.y); +} + +void _move_obj_by_vector(rectangle& obj, const vector_2d& amount) +{ + obj.x += amount.x; + obj.y += amount.y; +} + +void _move_obj_by_vector(circle& obj, const vector_2d& amount) +{ + obj.center.x += amount.x; + obj.center.y += amount.y; +} + +void _move_obj_by_vector(triangle& obj, const vector_2d& amount) +{ + obj.points[0].x += amount.x; + obj.points[0].y += amount.y; + obj.points[1].x += amount.x; + obj.points[1].y += amount.y; + obj.points[2].x += amount.x; + obj.points[2].y += amount.y; +} + +void _move_obj_by_vector(quad& obj, const vector_2d& amount) +{ + obj.points[0].x += amount.x; + obj.points[0].y += amount.y; + obj.points[1].x += amount.x; + obj.points[1].y += amount.y; + obj.points[2].x += amount.x; + obj.points[2].y += amount.y; + obj.points[3].x += amount.x; + obj.points[3].y += amount.y; +} + +template +void move_obj_by_arrows(T& obj) +{ + if ( key_down(LEFT_KEY) ) + { + _move_obj_by_vector(obj, DIRECTION_LEFT); + } + if ( key_down(RIGHT_KEY) ) + { + _move_obj_by_vector(obj, DIRECTION_RIGHT); + } + if ( key_down(UP_KEY) ) + { + _move_obj_by_vector(obj, DIRECTION_TOP); + } + if ( key_down(DOWN_KEY) ) + { + _move_obj_by_vector(obj, DIRECTION_BOTTOM); + } +} + +rectangle _object_AABB(const sprite& obj) +{ + return sprite_collision_rectangle(obj); +} + +rectangle _object_AABB(const rectangle& obj) +{ + return obj; +} + +rectangle _object_AABB(const circle& obj) +{ + return rectangle_around(obj); +} + +rectangle _object_AABB(const triangle& obj) +{ + return rectangle_around(obj); +} + +rectangle _object_AABB(const quad& obj) +{ + return rectangle_around(obj); +} + +template +void resolve_and_draw(T1& collider, const T2& collidee, const vector_2d& direction) +{ + constexpr int COLLISION_INDICATOR_WIDTH = 4; + if (is_zero_vector(direction)) + { + return; + } + + bool collision = false; + rectangle perimeter = _object_AABB(collider); + + collision = resolve_collision(collider, collidee, direction); + + if (collision) + { + draw_rect_perimeter_by_collision(perimeter, direction, COLOR_RED, COLLISION_INDICATOR_WIDTH); + } +} + +void draw_object(sprite& obj, bool draw_AABB) +{ + draw_sprite(obj); + if (draw_AABB) + { + draw_rectangle(COLOR_GREEN, sprite_collision_rectangle(obj)); + } +} + +void draw_object(rectangle& obj, bool draw_AABB) +{ + fill_rectangle(COLOR_ORANGE, obj); + if (draw_AABB) + { + draw_rectangle(COLOR_GREEN, obj); + } +} + +void draw_object(circle& obj, bool draw_AABB) +{ + fill_circle(COLOR_ORANGE, obj); + if (draw_AABB) + { + draw_rectangle(COLOR_GREEN, rectangle_around(obj)); + } +} + +void draw_object(triangle& obj, bool draw_AABB) +{ + fill_triangle(COLOR_ORANGE, obj); + if (draw_AABB) + { + draw_rectangle(COLOR_GREEN, rectangle_around(obj)); + } +} + +void draw_object(quad& obj, bool draw_AABB) +{ + fill_quad(COLOR_ORANGE, obj); + if (draw_AABB) + { + draw_rectangle(COLOR_GREEN, rectangle_around(obj)); + } +} + +template +void draw_objects(std::vector& objects, bool draw_AABB = false) +{ + for (size_t i = 0; i < objects.size(); i++) + { + draw_object(objects[i], draw_AABB); + } +} + +template +void multiple_dynamic_resolve_and_draw(T& collider, const sprite sprt_pixel, const sprite sprt_AABB, const rectangle& rect, + const circle& circ, const triangle& tri, const quad& q) +{ + resolve_and_draw(collider, sprt_pixel, calculate_collision_direction(collider, sprt_pixel)); + resolve_and_draw(collider, sprt_AABB, calculate_collision_direction(collider, sprt_AABB)); + resolve_and_draw(collider, rect, calculate_collision_direction(collider, rect)); + resolve_and_draw(collider, circ, calculate_collision_direction(collider, circ)); + resolve_and_draw(collider, tri, calculate_collision_direction(collider, tri)); + resolve_and_draw(collider, q, calculate_collision_direction(collider, q)); + + draw_object(collider, true); +} + +vector_2d collision_direction_by_order(size_t i) +{ + if (i > 7) + { + i = i % 8; + } + + switch (i) + { + case 0: + return DIRECTION_TOP; + case 1: + return DIRECTION_BOTTOM; + case 2: + return DIRECTION_LEFT; + case 3: + return DIRECTION_RIGHT; + case 4: + return DIRECTION_TOP_LEFT; + case 5: + return DIRECTION_TOP_RIGHT; + case 6: + return DIRECTION_BOTTOM_LEFT; + case 7: + return DIRECTION_BOTTOM_RIGHT; + default: + return DIRECTION_NONE; + } +} + +template +void fixed_resolve_and_draw(T1& collider, const std::vector& collidees) +{ + vector_2d direction; + + for (size_t i = 0; i < collidees.size(); i++) + { + direction = collision_direction_by_order(i); + resolve_and_draw(collider, collidees[i], direction); + } + + draw_object(collider, true); +} + +void multi_object_collision_resolution_test() +{ + object_type collider_type = object_type::SPRITE; + collision_test_type test_type = collision_test_type::MULTIPLE_DYNAMIC; + + sprite collider_sprt, sprt_pixel, sprt_AABB, sprt_TOP, sprt_BOTTOM, sprt_LEFT, + sprt_RIGHT, sprt_TOP_LEFT, sprt_TOP_RIGHT, sprt_BOTTOM_LEFT, sprt_BOTTOM_RIGHT; + bitmap bmp; + rectangle collider_rect, rect; + circle collider_circ, circ; + triangle collider_tri, tri; + quad collider_quad, q; + std::vector sprites_fixed; + std::vector rectangles_fixed; + std::vector circles_fixed; + std::vector triangles_fixed; + std::vector quads_fixed; + + open_window("Object Collision Resolution", 600, 600); + + hide_mouse(); + + collider_sprt = create_sprite("rocket_sprt.png"); + reset_sprite(collider_sprt); + reset_rectangle(collider_rect); + reset_circle(collider_circ); + reset_triangle(collider_tri); + reset_quad(collider_quad); + + sprt_pixel = create_sprite("rocket_sprt.png"); + sprite_set_x(sprt_pixel, 100.0); + sprite_set_y(sprt_pixel, 100.0); + sprite_set_collision_kind(sprt_pixel, PIXEL_COLLISIONS); + + sprt_AABB = create_sprite("rocket_sprt.png"); + sprite_set_x(sprt_AABB, 50.0); + sprite_set_y(sprt_AABB, 400.0); + sprite_set_collision_kind(sprt_AABB, AABB_COLLISIONS); + + sprt_TOP = create_sprite("rocket_sprt.png"); + sprite_set_x(sprt_TOP, 300.0); + sprite_set_y(sprt_TOP, 100.0); + sprite_set_collision_kind(sprt_TOP, PIXEL_COLLISIONS); + sprites_fixed.push_back(sprt_TOP); + + sprt_BOTTOM = create_sprite("rocket_sprt.png"); + sprite_set_x(sprt_BOTTOM, 300.0); + sprite_set_y(sprt_BOTTOM, 500.0); + sprite_set_collision_kind(sprt_BOTTOM, PIXEL_COLLISIONS); + sprites_fixed.push_back(sprt_BOTTOM); + + sprt_LEFT = create_sprite("rocket_sprt.png"); + sprite_set_x(sprt_LEFT, 100.0); + sprite_set_y(sprt_LEFT, 300.0); + sprite_set_collision_kind(sprt_LEFT, PIXEL_COLLISIONS); + sprites_fixed.push_back(sprt_LEFT); + + sprt_RIGHT = create_sprite("rocket_sprt.png"); + sprite_set_x(sprt_RIGHT, 500.0); + sprite_set_y(sprt_RIGHT, 300.0); + sprite_set_collision_kind(sprt_RIGHT, PIXEL_COLLISIONS); + sprites_fixed.push_back(sprt_RIGHT); + + sprt_TOP_LEFT = create_sprite("rocket_sprt.png"); + sprite_set_x(sprt_TOP_LEFT, 100.0); + sprite_set_y(sprt_TOP_LEFT, 100.0); + sprite_set_collision_kind(sprt_TOP_LEFT, PIXEL_COLLISIONS); + sprites_fixed.push_back(sprt_TOP_LEFT); + + sprt_TOP_RIGHT = create_sprite("rocket_sprt.png"); + sprite_set_x(sprt_TOP_RIGHT, 500.0); + sprite_set_y(sprt_TOP_RIGHT, 100.0); + sprite_set_collision_kind(sprt_TOP_RIGHT, PIXEL_COLLISIONS); + sprites_fixed.push_back(sprt_TOP_RIGHT); + + sprt_BOTTOM_LEFT = create_sprite("rocket_sprt.png"); + sprite_set_x(sprt_BOTTOM_LEFT, 100.0); + sprite_set_y(sprt_BOTTOM_LEFT, 500.0); + sprite_set_collision_kind(sprt_BOTTOM_LEFT, PIXEL_COLLISIONS); + sprites_fixed.push_back(sprt_BOTTOM_LEFT); + + sprt_BOTTOM_RIGHT = create_sprite("rocket_sprt.png"); + sprite_set_x(sprt_BOTTOM_RIGHT, 500.0); + sprite_set_y(sprt_BOTTOM_RIGHT, 500.0); + sprite_set_collision_kind(sprt_BOTTOM_RIGHT, PIXEL_COLLISIONS); + sprites_fixed.push_back(sprt_BOTTOM_RIGHT); + + rect = rectangle_from(400.0, 450.0, 100.0, 50.0); + + rectangles_fixed.push_back(rectangle_from(300.0, 100.0, 100.0, 50.0)); // rect_TOP + rectangles_fixed.push_back(rectangle_from(300.0, 500.0, 100.0, 50.0)); // rect_BOTTOM + rectangles_fixed.push_back(rectangle_from(100.0, 300.0, 50.0, 100.0)); // rect_LEFT + rectangles_fixed.push_back(rectangle_from(500.0, 300.0, 50.0, 100.0)); // rect_RIGHT + rectangles_fixed.push_back(rectangle_from(100.0, 100.0, 50.0, 50.0)); // rect_TOP_LEFT + rectangles_fixed.push_back(rectangle_from(500.0, 100.0, 50.0, 50.0)); // rect_TOP_RIGHT + rectangles_fixed.push_back(rectangle_from(100.0, 500.0, 50.0, 50.0)); // rect_BOTTOM_LEFT + rectangles_fixed.push_back(rectangle_from(500.0, 500.0, 50.0, 50.0)); // rect_BOTTOM_RIGHT + + circ = circle_at(400.0, 300.0, 50.0); + + circles_fixed.push_back(circle_at(300.0, 100.0, 50.0)); // circ_TOP + circles_fixed.push_back(circle_at(300.0, 500.0, 50.0)); // circ_BOTTOM + circles_fixed.push_back(circle_at(100.0, 300.0, 50.0)); // circ_LEFT + circles_fixed.push_back(circle_at(500.0, 300.0, 50.0)); // circ_RIGHT + circles_fixed.push_back(circle_at(100.0, 100.0, 50.0)); // circ_TOP_LEFT + circles_fixed.push_back(circle_at(500.0, 100.0, 50.0)); // circ_TOP_RIGHT + circles_fixed.push_back(circle_at(100.0, 500.0, 50.0)); // circ_BOTTOM_LEFT + circles_fixed.push_back(circle_at(500.0, 500.0, 50.0)); // circ_BOTTOM_RIGHT + + tri = triangle_from(400.0, 100.0, 500.0, 100.0, 450.0, 50.0); + + triangles_fixed.push_back(triangle_from(300.0, 100.0, 400.0, 100.0, 350.0, 150.0)); // tri_TOP + triangles_fixed.push_back(triangle_from(300.0, 500.0, 400.0, 500.0, 350.0, 450.0)); // tri_BOTTOM + triangles_fixed.push_back(triangle_from(100.0, 300.0, 100.0, 400.0, 150.0, 350.0)); // tri_LEFT + triangles_fixed.push_back(triangle_from(500.0, 300.0, 500.0, 400.0, 450.0, 350.0)); // tri_RIGHT + triangles_fixed.push_back(triangle_from(100.0, 150.0, 150.0, 100.0, 150.0, 150.0)); // tri_TOP_LEFT + triangles_fixed.push_back(triangle_from(500.0, 150.0, 450.0, 100.0, 450.0, 150.0)); // tri_TOP_RIGHT + triangles_fixed.push_back(triangle_from(100.0, 450.0, 150.0, 500.0, 150.0, 450.0)); // tri_BOTTOM_LEFT + triangles_fixed.push_back(triangle_from(500.0, 450.0, 450.0, 500.0, 450.0, 450.0)); // tri_BOTTOM_RIGHT + + rectangle r = rectangle_from(400, 100, 100, 50); + q = quad_from(r); + apply_matrix(matrix_multiply(translation_matrix(0.0, 50.0), rotation_matrix(45.0)), q); + + r = rectangle_from(0.0, 0.0, 100.0, 50.0); + quads_fixed.push_back(quad_from(r)); + apply_matrix(matrix_multiply(rotation_matrix(45.0), translation_matrix(300.0, 20.0)), quads_fixed[0]); // quad_TOP + quads_fixed.push_back(quad_from(r)); + apply_matrix(matrix_multiply(rotation_matrix(45.0), translation_matrix(300.0, 470.0)), quads_fixed[1]); // quad_BOTTOM + quads_fixed.push_back(quad_from(r)); + apply_matrix(matrix_multiply(rotation_matrix(45.0), translation_matrix(50.0, 270.0)), quads_fixed[2]); // quad_LEFT + quads_fixed.push_back(quad_from(r)); + apply_matrix(matrix_multiply(rotation_matrix(45.0), translation_matrix(500.0, 270.0)), quads_fixed[3]); // quad_RIGHT + quads_fixed.push_back(quad_from(r)); + apply_matrix(matrix_multiply(rotation_matrix(45.0), translation_matrix(50.0, 20.0)), quads_fixed[4]); // quad_TOP_LEFT + quads_fixed.push_back(quad_from(r)); + apply_matrix(matrix_multiply(rotation_matrix(45.0), translation_matrix(500.0, 20.0)), quads_fixed[5]); // quad_TOP_RIGHT + quads_fixed.push_back(quad_from(r)); + apply_matrix(matrix_multiply(rotation_matrix(45.0), translation_matrix(50.0, 470.0)), quads_fixed[6]); // quad_BOTTOM_LEFT + quads_fixed.push_back(quad_from(r)); + apply_matrix(matrix_multiply(rotation_matrix(45.0), translation_matrix(500.0, 470.0)), quads_fixed[7]); // quad_BOTTOM_RIGHT + + while (! quit_requested()) + { + process_events(); + + clear_screen(COLOR_WHITE); + + // Change the object type with number keys + if (key_typed(NUM_1_KEY)) + { + collider_type = object_type::SPRITE; + reset_sprite(collider_sprt); + } + if (key_typed(NUM_2_KEY)) + { + collider_type = object_type::RECTANGLE; + reset_rectangle(collider_rect); + } + if (key_typed(NUM_3_KEY)) + { + collider_type = object_type::CIRCLE; + reset_circle(collider_circ); + } + if (key_typed(NUM_4_KEY)) + { + collider_type = object_type::TRIANGLE; + reset_triangle(collider_tri); + } + if (key_typed(NUM_5_KEY)) + { + collider_type = object_type::QUAD; + reset_quad(collider_quad); + } + if (key_typed(NUM_6_KEY)) + { + test_type = collision_test_type::MULTIPLE_DYNAMIC; + } + if (key_typed(NUM_7_KEY)) + { + test_type = collision_test_type::SPRITE_FIXED; + } + if (key_typed(NUM_8_KEY)) + { + test_type = collision_test_type::RECTANGLE_FIXED; + } + if (key_typed(NUM_9_KEY)) + { + test_type = collision_test_type::CIRCLE_FIXED; + } + if (key_typed(NUM_0_KEY)) + { + test_type = collision_test_type::TRIANGLE_FIXED; + } + if (key_typed(MINUS_KEY)) + { + test_type = collision_test_type::QUAD_FIXED; + } + + if (collider_type == object_type::SPRITE) + { + if ( key_down(LEFT_KEY) ) + sprite_set_rotation(collider_sprt, sprite_rotation(collider_sprt) - 5.0); + + if ( key_down(RIGHT_KEY) ) + sprite_set_rotation(collider_sprt, sprite_rotation(collider_sprt) + 5.0); + + if ( key_down(LEFT_SHIFT_KEY) or key_down(RIGHT_SHIFT_KEY) ) + { + if ( key_down(UP_KEY) ) + sprite_set_scale(collider_sprt, sprite_scale(collider_sprt) + 0.1); + + if ( key_down(DOWN_KEY) ) + sprite_set_scale(collider_sprt, sprite_scale(collider_sprt) - 0.1); + } + else + { + if ( key_down(UP_KEY) ) + sprite_set_dy(collider_sprt, sprite_dy(collider_sprt) - 0.1); + + if ( key_down(DOWN_KEY) ) + sprite_set_dy(collider_sprt, sprite_dy(collider_sprt) + 0.1); + } + + update_sprite(collider_sprt); + } + else if (collider_type == object_type::RECTANGLE) + { + move_obj_by_arrows(collider_rect); + } + else if (collider_type == object_type::CIRCLE) + { + move_obj_by_arrows(collider_circ); + } + else if (collider_type == object_type::TRIANGLE) + { + move_obj_by_arrows(collider_tri); + } + else if (collider_type == object_type::QUAD) + { + move_obj_by_arrows(collider_quad); + } + + if (test_type == collision_test_type::MULTIPLE_DYNAMIC) + { + draw_rectangle(COLOR_RED, sprite_collision_rectangle(sprt_AABB)); + fill_rectangle(COLOR_GREEN, rect); + fill_circle(COLOR_GREEN, circ); + fill_triangle(COLOR_GREEN, tri); + fill_quad(COLOR_GREEN, q); + draw_sprite(sprt_pixel); + draw_sprite(sprt_AABB); + + if (collider_type == object_type::SPRITE) // sprite vs. multiple dynamic objects + { + multiple_dynamic_resolve_and_draw(collider_sprt, sprt_pixel, sprt_AABB, rect, circ, tri, q); + } + else if (collider_type == object_type::RECTANGLE) // rect vs. multiple dynamic objects + { + multiple_dynamic_resolve_and_draw(collider_rect, sprt_pixel, sprt_AABB, rect, circ, tri, q); + } + else if (collider_type == object_type::CIRCLE) // circle vs. multiple dynamic objects + { + multiple_dynamic_resolve_and_draw(collider_circ, sprt_pixel, sprt_AABB, rect, circ, tri, q); + } + else if (collider_type == object_type::TRIANGLE) // triangle vs. multiple dynamic objects + { + multiple_dynamic_resolve_and_draw(collider_tri, sprt_pixel, sprt_AABB, rect, circ, tri, q); + } + else if (collider_type == object_type::QUAD) // quad vs. multiple dynamic objects + { + multiple_dynamic_resolve_and_draw(collider_quad, sprt_pixel, sprt_AABB, rect, circ, tri, q); + } + } + else if (test_type == collision_test_type::SPRITE_FIXED) + { + draw_objects(sprites_fixed, false); + + if (collider_type == object_type::SPRITE) // sprite vs. fixed sprite objects + { + fixed_resolve_and_draw(collider_sprt, sprites_fixed); + } + else if (collider_type == object_type::RECTANGLE) // rect vs. fixed sprite objects + { + fixed_resolve_and_draw(collider_rect, sprites_fixed); + } + else if (collider_type == object_type::CIRCLE) // circle vs. fixed sprite objects + { + fixed_resolve_and_draw(collider_circ, sprites_fixed); + } + else if (collider_type == object_type::TRIANGLE) // triangle vs. fixed sprite objects + { + fixed_resolve_and_draw(collider_tri, sprites_fixed); + } + else if (collider_type == object_type::QUAD) // quad vs. fixed sprite objects + { + fixed_resolve_and_draw(collider_quad, sprites_fixed); + } + } + else if (test_type == collision_test_type::RECTANGLE_FIXED) + { + draw_objects(rectangles_fixed, false); + + if (collider_type == object_type::SPRITE) // sprite vs. fixed rectangle objects + { + fixed_resolve_and_draw(collider_sprt, rectangles_fixed); + } + else if (collider_type == object_type::RECTANGLE) // rect vs. fixed rectangle objects + { + fixed_resolve_and_draw(collider_rect, rectangles_fixed); + } + else if (collider_type == object_type::CIRCLE) // circle vs. fixed rectangle objects + { + fixed_resolve_and_draw(collider_circ, rectangles_fixed); + } + else if (collider_type == object_type::TRIANGLE) // triangle vs. fixed rectangle objects + { + fixed_resolve_and_draw(collider_tri, rectangles_fixed); + } + else if (collider_type == object_type::QUAD) // quad vs. fixed rectangle objects + { + fixed_resolve_and_draw(collider_quad, rectangles_fixed); + } + } + else if (test_type == collision_test_type::CIRCLE_FIXED) + { + draw_objects(circles_fixed, false); + + if (collider_type == object_type::SPRITE) // sprite vs. fixed circle objects + { + fixed_resolve_and_draw(collider_sprt, circles_fixed); + } + else if (collider_type == object_type::RECTANGLE) // rect vs. fixed circle objects + { + fixed_resolve_and_draw(collider_rect, circles_fixed); + } + else if (collider_type == object_type::CIRCLE) // circle vs. fixed circle objects + { + fixed_resolve_and_draw(collider_circ, circles_fixed); + } + else if (collider_type == object_type::TRIANGLE) // triangle vs. fixed circle objects + { + fixed_resolve_and_draw(collider_tri, circles_fixed); + } + else if (collider_type == object_type::QUAD) // quad vs. fixed circle objects + { + fixed_resolve_and_draw(collider_quad, circles_fixed); + } + } + else if (test_type == collision_test_type::TRIANGLE_FIXED) + { + draw_objects(triangles_fixed, false); + + if (collider_type == object_type::SPRITE) // sprite vs. fixed triangle objects + { + fixed_resolve_and_draw(collider_sprt, triangles_fixed); + } + else if (collider_type == object_type::RECTANGLE) // rect vs. fixed triangle objects + { + fixed_resolve_and_draw(collider_rect, triangles_fixed); + } + else if (collider_type == object_type::CIRCLE) // circle vs. fixed triangle objects + { + fixed_resolve_and_draw(collider_circ, triangles_fixed); + } + else if (collider_type == object_type::TRIANGLE) // triangle vs. fixed triangle objects + { + fixed_resolve_and_draw(collider_tri, triangles_fixed); + } + else if (collider_type == object_type::QUAD) // quad vs. fixed triangle objects + { + fixed_resolve_and_draw(collider_quad, triangles_fixed); + } + } + else if (test_type == collision_test_type::QUAD_FIXED) + { + draw_objects(quads_fixed, false); + + if (collider_type == object_type::SPRITE) // sprite vs. fixed quad objects + { + fixed_resolve_and_draw(collider_sprt, quads_fixed); + } + else if (collider_type == object_type::RECTANGLE) // rect vs. fixed quad objects + { + fixed_resolve_and_draw(collider_rect, quads_fixed); + } + else if (collider_type == object_type::CIRCLE) // circle vs. fixed quad objects + { + fixed_resolve_and_draw(collider_circ, quads_fixed); + } + else if (collider_type == object_type::TRIANGLE) // triangle vs. fixed quad objects + { + fixed_resolve_and_draw(collider_tri, quads_fixed); + } + else if (collider_type == object_type::QUAD) // quad vs. fixed quad objects + { + fixed_resolve_and_draw(collider_quad, quads_fixed); + } + } // end if + + refresh_screen(60); + + } // end while + + show_mouse(); + close_all_windows(); +} + void test_sprite_ray_collision() { window w1 = open_window("Sprite Ray Collision", 800, 600); @@ -212,5 +1046,6 @@ void test_sprite_ray_collision() void run_sprite_test() { sprite_test(); + multi_object_collision_resolution_test(); test_sprite_ray_collision(); -} \ No newline at end of file +} diff --git a/coresdk/src/test/unit_tests/unit_test_bitmap.cpp b/coresdk/src/test/unit_tests/unit_test_bitmap.cpp index 7d41d3f5..22fefc67 100644 --- a/coresdk/src/test/unit_tests/unit_test_bitmap.cpp +++ b/coresdk/src/test/unit_tests/unit_test_bitmap.cpp @@ -10,8 +10,6 @@ using namespace splashkit_lib; -constexpr int ROCKET_WIDTH = 36, ROCKET_HEIGHT = 72; - TEST_CASE("bitmaps can be created and freed", "[bitmap]") { SECTION("can detect non-existent bitmap") @@ -84,6 +82,7 @@ TEST_CASE("bitmaps can be created and freed", "[bitmap]") } TEST_CASE("bitmap bounding details can be retrieved", "[bitmap]") { + constexpr int ROCKET_WIDTH = 36, ROCKET_HEIGHT = 72; bitmap bmp = load_bitmap("rocket", "rocket_sprt.png"); REQUIRE(bmp != nullptr); REQUIRE(bitmap_valid(bmp)); diff --git a/coresdk/src/test/unit_tests/unit_test_geometry.cpp b/coresdk/src/test/unit_tests/unit_test_geometry.cpp index e44b2eba..c71e9a20 100644 --- a/coresdk/src/test/unit_tests/unit_test_geometry.cpp +++ b/coresdk/src/test/unit_tests/unit_test_geometry.cpp @@ -412,6 +412,18 @@ TEST_CASE("can perform circle geometry", "[geometry]") p = point_at(100.0, 100.0); REQUIRE_FALSE(tangent_points(p, c, p1, p2)); } + SECTION("can detect circle-quad intersection") +{ + circle c = circle_at(100.0, 100.0, 50.0); + quad q = quad_from(rectangle_from(50.0, 50.0, 100.0, 100.0)); + REQUIRE(circle_quad_intersect(c, q)); + q = quad_from(rectangle_from(0.0, 0.0, 500.0, 500.0)); + REQUIRE(circle_quad_intersect(c, q)); + q = quad_from(rectangle_from(99.0, 99.0, 5.0, 5.0)); + REQUIRE(circle_quad_intersect(c, q)); + q = quad_from(rectangle_from(200.0, 200.0, 100.0, 100.0)); + REQUIRE_FALSE(circle_quad_intersect(c, q)); +} } TEST_CASE("can perform rectangle geometry", "[rectangle]") { @@ -548,6 +560,18 @@ TEST_CASE("can perform rectangle geometry", "[rectangle]") REQUIRE(inset.width == 100.0); REQUIRE(inset.height == 100.0); } + SECTION("can detect rectangle-circle intersection") + { + rectangle r = rectangle_from(100.0, 100.0, 100.0, 100.0); + circle c = circle_at(150.0, 150.0, 50.0); + REQUIRE(rectangle_circle_intersect(r, c)); + c = circle_at(150.0, 150.0, 1.0); + REQUIRE(rectangle_circle_intersect(r, c)); + c = circle_at(150.0, 150.0, 500.0); + REQUIRE(rectangle_circle_intersect(r, c)); + c = circle_at(300.0, 300.0, 50.0); + REQUIRE_FALSE(rectangle_circle_intersect(r, c)); + } } TEST_CASE("can perform triangle geometry", "[triangle]") { @@ -596,6 +620,18 @@ TEST_CASE("can perform triangle geometry", "[triangle]") triangle t = triangle_from(point_at(0.0, 0.0), point_at(200.0, 0.0), point_at(100.0, 200.0)); REQUIRE(triangle_to_string(t) == "Triangle @Pt @0.000000:0.000000 - Pt @200.000000:0.000000 - Pt @100.000000:200.000000"); } + SECTION("can detect triangle-quad intersection") + { + triangle t = triangle_from(100.0, 100.0, 200.0, 100.0, 150.0, 150.0); + quad q = quad_from(rectangle_from(50.0, 50.0, 100.0, 100.0)); + REQUIRE(triangle_quad_intersect(t, q)); + q = quad_from(rectangle_from(0.0, 0.0, 500.0, 500.0)); + REQUIRE(triangle_quad_intersect(t, q)); + q = quad_from(rectangle_from(150.0, 101.0, 5.0, 5.0)); + REQUIRE(triangle_quad_intersect(t, q)); + q = quad_from(rectangle_from(200.0, 200.0, 100.0, 100.0)); + REQUIRE_FALSE(triangle_quad_intersect(t, q)); + } } TEST_CASE("can perform line geometry", "[line]") { @@ -734,7 +770,7 @@ TEST_CASE("can perform line geometry", "[line]") r = rectangle_from(300.0, 300.0, 200.0, 200.0); REQUIRE_FALSE(line_intersects_rect(l, r)); r = rectangle_from(50.0, 50.0, 500.0, 500.0); - REQUIRE_FALSE(line_intersects_rect(l, r)); + REQUIRE(line_intersects_rect(l, r)); } SECTION("can calculate line midpoint") { @@ -771,6 +807,16 @@ TEST_CASE("can perform line geometry", "[line]") lines = {l1, l2}; REQUIRE_FALSE(line_intersects_lines(l, lines)); } + SECTION("can detect line-rectangle intersection") + { + line l = line_from(100.0, 100.0, 200.0, 200.0); + rectangle r = rectangle_from(150.0, 150.0, 100.0, 100.0); + REQUIRE(line_intersects_rect(l, r)); + r = rectangle_from(90.0, 90.0, 200.0, 200.0); + REQUIRE(line_intersects_rect(l, r)); + r = rectangle_from(250.0, 250.0, 100.0, 100.0); + REQUIRE_FALSE(line_intersects_rect(l, r)); + } } TEST_CASE("can perform quad geometry", "[quad]") { diff --git a/coresdk/src/test/unit_tests/unit_test_sprites.cpp b/coresdk/src/test/unit_tests/unit_test_sprites.cpp index c5c058c9..91a34e4f 100644 --- a/coresdk/src/test/unit_tests/unit_test_sprites.cpp +++ b/coresdk/src/test/unit_tests/unit_test_sprites.cpp @@ -10,13 +10,13 @@ using namespace splashkit_lib; constexpr int ROCKET_WIDTH = 36, ROCKET_HEIGHT = 72, - FROG_WIDTH = 294, FROG_HEIGHT = 422, BACKGROUND_WIDTH = 864, BACKGROUND_HEIGHT = 769; bitmap rocket_bmp, frog_bmp, background_bmp; TEST_CASE("bitmaps can be created", "[bitmap]") { + constexpr int FROG_WIDTH = 294, FROG_HEIGHT = 422; rocket_bmp = load_bitmap("rocket_sprt", "rocket_sprt.png"); REQUIRE(bitmap_valid(rocket_bmp)); REQUIRE(rocket_bmp != nullptr);