add exception checks to Texture::bind and GLObject::id, make collision info storage a std::optional object
This commit is contained in:
parent
c0b55752e1
commit
12e5a15d1c
57
src/Box.cpp
57
src/Box.cpp
|
@ -10,9 +10,6 @@
|
|||
|
||||
#include "Box.hpp"
|
||||
|
||||
/* Construct a Box by giving a corner coordinate (top left if Y-axis is not inverted, bottom left otherwise) and
|
||||
* size (as width, height). The invert_y argument indicates that the Y-coordinate increases from the bottom of
|
||||
* the screen to the top rather than the default case where it increases from the top to the bottom. */
|
||||
Box::Box(const glm::vec2& corner, const glm::vec2& size, bool invert_y)
|
||||
{
|
||||
x = corner.x;
|
||||
|
@ -22,11 +19,8 @@ Box::Box(const glm::vec2& corner, const glm::vec2& size, bool invert_y)
|
|||
this->invert_y(invert_y);
|
||||
}
|
||||
|
||||
/* Construct a Box from float arguments: x, y, width, height. If invert_y is set, the Y-axis increases from bottom to
|
||||
* top instead of top to bottom. */
|
||||
Box::Box(float x, float y, float width, float height, bool invert_y) : Box({x, y}, {width, height}, invert_y) {}
|
||||
|
||||
/* Construct a Box by passing an SDL_Rect struct, which is of the form {x, y, w, h} and limited to int arguments. */
|
||||
Box::Box(const SDL_Rect& rect, bool invert_y) : Box({rect.x, rect.y}, {rect.w, rect.h}, invert_y) {}
|
||||
|
||||
/* Set inverted Y mode to true, meaning the Y-coordinate increases from the bottom to the top, or false, meaning the
|
||||
|
@ -67,13 +61,11 @@ void Box::height(float height)
|
|||
h = std::max(height, 0.0f);
|
||||
}
|
||||
|
||||
/* Return the size as a vector {width, height} */
|
||||
glm::vec2 Box::size() const
|
||||
{
|
||||
return {width(), height()};
|
||||
}
|
||||
|
||||
/* Set the size. Negative values will be clamped to zero. */
|
||||
void Box::size(const glm::vec2& size, bool preserve_center)
|
||||
{
|
||||
glm::vec2 center = this->center();
|
||||
|
@ -514,36 +506,28 @@ void Box::crop(const Box& area)
|
|||
}
|
||||
}
|
||||
|
||||
/* Returns true if point is inside the box, false otherwise. Includes the edges, so points equal to the edges
|
||||
* will return true. */
|
||||
bool Box::collide(const glm::vec2& point) const
|
||||
{
|
||||
return point.x >= left() && point.x <= right() && point.y >= (inverted_y() ? bottom() : top()) && point.y <= (inverted_y() ? top() : bottom());
|
||||
}
|
||||
|
||||
/* Returns true if the line segment intersects the box, false otherwise. If intersection is passed and there is a
|
||||
* collision, intersection will be filled with the coordinates of the first intersection found unless the segment
|
||||
* is fully within the box not touching any edges. */
|
||||
bool Box::collide(const Segment& segment, glm::vec2* intersection) const
|
||||
bool Box::collide(const Segment& segment, std::optional<std::reference_wrapper<glm::vec2>> intersection) const
|
||||
{
|
||||
/* do the faster check of whether the square represented by the segment diagonal collides to determine
|
||||
* if we should look more closely */
|
||||
/* Do the faster check of whether the square represented by the segment diagonal collides to determine whether to look more closely. */
|
||||
if (collide(segment.box()))
|
||||
{
|
||||
/* check if segment intersects any edges, storing the intersection point if so */
|
||||
if (segment.intersect({nw(), ne()}, intersection) ||
|
||||
segment.intersect({ne(), se()}, intersection) ||
|
||||
segment.intersect({sw(), se()}, intersection) ||
|
||||
segment.intersect({nw(), sw()}, intersection))
|
||||
/* Check if segment intersects any edges, storing the intersection point if so. */
|
||||
if (segment.intersect({nw(), ne()}, intersection) || segment.intersect({ne(), se()}, intersection) ||
|
||||
segment.intersect({sw(), se()}, intersection) || segment.intersect({nw(), sw()}, intersection))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/* check if segment is fully inside the box */
|
||||
/* Check if segment is fully inside the box. */
|
||||
else if (collide(segment.start()) && collide(segment.end()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/* otherwise, segment must be outside the box even though its box has collided */
|
||||
/* Otherwise, segment must be outside the box even though its box has collided. */
|
||||
else
|
||||
{
|
||||
return false;
|
||||
|
@ -555,15 +539,7 @@ bool Box::collide(const Segment& segment, glm::vec2* intersection) const
|
|||
}
|
||||
}
|
||||
|
||||
/* Do segment collision with intersection specified by reference rather than pointer */
|
||||
bool Box::collide(const Segment& segment, glm::vec2& intersection) const
|
||||
{
|
||||
return collide(segment, &intersection);
|
||||
}
|
||||
|
||||
/* Return true if box collides with the passed box, false otherwise. If overlap is passed, it is set to
|
||||
* the box representing the area where the two boxes overlap. */
|
||||
bool Box::collide(const Box& box, Box* overlap) const
|
||||
bool Box::collide(const Box& box, std::optional<std::reference_wrapper<Box>> overlap) const
|
||||
{
|
||||
float top, bottom, h;
|
||||
if (inverted_y())
|
||||
|
@ -582,22 +558,17 @@ bool Box::collide(const Box& box, Box* overlap) const
|
|||
float left = std::max(this->left(), box.left());
|
||||
float w = right - left;
|
||||
bool collide = w > 0 && h > 0;
|
||||
if (collide && overlap != nullptr)
|
||||
if (collide && overlap.has_value())
|
||||
{
|
||||
overlap->left(left);
|
||||
overlap->top(top);
|
||||
overlap->width(w);
|
||||
overlap->height(h);
|
||||
Box& overlap_box = overlap.value();
|
||||
overlap_box.left(left);
|
||||
overlap_box.top(top);
|
||||
overlap_box.width(w);
|
||||
overlap_box.height(h);
|
||||
}
|
||||
return collide;
|
||||
}
|
||||
|
||||
/* Do a box to box collision test with overlap passed by reference instead of pointer */
|
||||
bool Box::collide(const Box& box, Box& overlap) const
|
||||
{
|
||||
return collide(box, &overlap);
|
||||
}
|
||||
|
||||
/* Return the string representation of a Box "{left, top, width, height}" */
|
||||
std::string Box::string() const
|
||||
{
|
||||
|
|
120
src/Box.hpp
120
src/Box.hpp
|
@ -1,18 +1,20 @@
|
|||
/* /\ +--------------------------------------------------------------+
|
||||
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
|
||||
\ / / | copy, and modify without restriction |
|
||||
+--\ ^__^ /--+ | |
|
||||
| ~/ \~ | | - originally created at [http://nugget.fun] |
|
||||
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
|
||||
| SPACE ~~~~~ | /
|
||||
| ~~~~~~~ BOX |/
|
||||
+-------------*/
|
||||
/* +------------------------------------------------------+
|
||||
____/ \____ /| - Open source game framework licensed to freely use, |
|
||||
\ / / | copy, modify and sell without restriction |
|
||||
+--\ ^__^ /--+ | |
|
||||
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
|
||||
| ~~~~~~~~~~~~ | +------------------------------------------------------+
|
||||
| SPACE ~~~~~ | /
|
||||
| ~~~~~~~ BOX |/
|
||||
+-------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
|
@ -22,6 +24,12 @@
|
|||
|
||||
class Segment;
|
||||
|
||||
/*!
|
||||
* A ::Box object represents a rectangle, a two-dimensional four-sided polygon with two parallel sides.
|
||||
*
|
||||
* Rotating by three dimensions into the Z-dimension is not currently supported and must be implemented by the user,
|
||||
* but 3D transformation is planned to be added in a future update.
|
||||
*/
|
||||
class Box : public SDL_FRect
|
||||
{
|
||||
|
||||
|
@ -31,21 +39,66 @@ private:
|
|||
|
||||
public:
|
||||
|
||||
Box(const glm::vec2& = {0, 0}, const glm::vec2& = {0, 0}, bool = false);
|
||||
Box(float, float, float, float, bool = false);
|
||||
Box(const SDL_Rect&, bool = false);
|
||||
/*!
|
||||
* Construct a Box object by giving a corner coordinate (top left if Y-axis is not inverted, bottom left otherwise) and
|
||||
* size (as width, height). The invert_y argument indicates that the Y-coordinate increases from the bottom of the screen
|
||||
* to the top rather than the default case where it increases from the top to the bottom.
|
||||
*
|
||||
* @param corner top left coordinate in the x, y axis
|
||||
* @param size size of the box in width and height
|
||||
* @param invert_y if true, y-coordinates increase as they move up on the screen, rather than decrease
|
||||
*/
|
||||
Box(const glm::vec2& corner = {0, 0}, const glm::vec2& size = {0, 0}, bool invert_y = false);
|
||||
|
||||
/*!
|
||||
* Construct a Box object lfrom float arguments: x, y, width, height. If invert_y is set, the Y-axis increases from bottom to
|
||||
* top instead of top to bottom.
|
||||
*
|
||||
* @see Box::Box(const glm::vec2&, const glm::vec2&, bool)
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param width size of the x-dimension
|
||||
* @param height size of the y-dimension
|
||||
* @param invert_y if true, y-coordinates increase as they move up on the screen, rather than decrease
|
||||
*/
|
||||
Box(float x, float y, float width, float height, bool invert_y = false);
|
||||
|
||||
/*!
|
||||
* Construct a ::Box object by passing an SDL_Rect struct, which is of the form {x, y, w, h} and limited to int arguments.
|
||||
*
|
||||
* @param rect see SDL's rect documentation http://doc.here/sdlwiki/SDL_FRect.html
|
||||
* @param invert_y if true, y-coordinates increase as they move up on the screen, rather than decrease
|
||||
*/
|
||||
Box(const SDL_Rect& rect, bool invert_y = false);
|
||||
|
||||
void invert_y(bool);
|
||||
bool inverted_y() const;
|
||||
float width() const;
|
||||
void width(float);
|
||||
float height() const;
|
||||
void height(float);
|
||||
glm::vec2 size() const;
|
||||
void size(const glm::vec2&, bool = false);
|
||||
|
||||
/* If the flag is not set, this will return width divided by height. Otherwise, it will check which side is longer and return the longer side
|
||||
* divided by the shorter side. */
|
||||
float aspect(bool = false) const;
|
||||
/*!
|
||||
* @return the size as a vector `{width, height}`
|
||||
*/
|
||||
glm::vec2 size() const;
|
||||
|
||||
/*!
|
||||
* Set the size. Negative values will be clamped to zero.
|
||||
*
|
||||
* @param size new size in width, height
|
||||
* @param preserve_center if true, expand or contract the box from the center, leaving its center value unchanged
|
||||
*/
|
||||
void size(const glm::vec2& size, bool preserve_center = false);
|
||||
|
||||
/*!
|
||||
* If the flag is not set, this will return width divided by height. Otherwise, it will check which side is longer and return the longer side
|
||||
* divided by the shorter side.
|
||||
*
|
||||
* @param larger_ratio if true, divide by the shorter side, causing the result to always be 1.0 or greater
|
||||
*/
|
||||
float aspect(bool larger_ratio = false) const;
|
||||
|
||||
float area() const;
|
||||
float top() const;
|
||||
|
@ -92,12 +145,33 @@ public:
|
|||
Box stamp(const glm::vec2&) const;
|
||||
bool fits(const Box&) const;
|
||||
void crop(const Box&);
|
||||
bool collide(const glm::vec2&) const;
|
||||
bool collide(const Segment&, glm::vec2* = nullptr) const;
|
||||
bool collide(const Segment&, glm::vec2&) const;
|
||||
bool collide(const Box&, Box* = nullptr) const;
|
||||
bool collide(const Box&, Box&) const;
|
||||
virtual std::string class_name() const { return "Box"; }
|
||||
|
||||
/*!
|
||||
* Collide a 2D point with the box. Includes the edges, so points on the edges will return `true`.
|
||||
*
|
||||
* @return `true` if point is inside the box, `false` otherwise
|
||||
*/
|
||||
bool collide(const glm::vec2& point) const;
|
||||
|
||||
/*!
|
||||
* Collide a line segment with the box. If intersection is passed and there is a collision, intersection will be filled with
|
||||
* the coordinates of the first intersection found unless the segment is fully within the box not touching any edges.
|
||||
*
|
||||
* @param segment line segment to collide with the box
|
||||
* @param intersection optional reference to 2D vector to fill with the first intersection point
|
||||
* @return `true` if segment collides with the box, `false` otherwies
|
||||
*/
|
||||
bool collide(const Segment& segment, std::optional<std::reference_wrapper<glm::vec2>> intersection = std::nullopt) const;
|
||||
|
||||
/*!
|
||||
* Collide with another box object. If overlap is passed, it is set to the box representing the area where the two boxes overlap.
|
||||
*
|
||||
* @param box another box object to collide with
|
||||
* @param overlap optional reference to a box object to be filled with the overlapping area
|
||||
* @return `true` if box collides with the passed box, `false` otherwise
|
||||
*/
|
||||
bool collide(const Box& box, std::optional<std::reference_wrapper<Box>> overlap = std::nullopt) const;
|
||||
|
||||
std::string string() const;
|
||||
|
||||
/*!
|
||||
|
|
|
@ -61,7 +61,14 @@ void GLObject::id(GLuint id)
|
|||
/* Return the GL ID that was set for this object */
|
||||
GLuint GLObject::id() const
|
||||
{
|
||||
return *object_id;
|
||||
if (generated())
|
||||
{
|
||||
return *object_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Cannot get ID for GL object that has no ID set yet");
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if an ID has been generated */
|
||||
|
|
|
@ -129,6 +129,14 @@ void sb::Model::texture(const sb::Texture& texture)
|
|||
textures().push_back(texture);
|
||||
}
|
||||
|
||||
void sb::Model::load()
|
||||
{
|
||||
for (sb::Texture& texture : textures())
|
||||
{
|
||||
texture.load();
|
||||
}
|
||||
}
|
||||
|
||||
const glm::mat4& sb::Model::transformation() const
|
||||
{
|
||||
return _transformation;
|
||||
|
|
|
@ -158,7 +158,7 @@ namespace sb
|
|||
std::vector<sb::Texture>& textures();
|
||||
|
||||
/*!
|
||||
* Get a constant refernce to the texture at the given index. If no index is given, get the texture at index 0. If
|
||||
* Get a constant reference to the texture at the given index. If no index is given, get the texture at index 0. If
|
||||
* there are no textures, an exception will be thrown.
|
||||
*
|
||||
* @param index index of texture to get
|
||||
|
@ -180,6 +180,14 @@ namespace sb
|
|||
*/
|
||||
void texture(const sb::Texture& texture);
|
||||
|
||||
/*!
|
||||
* Call the load method with no arguments on all textures attached to the model, causing them to load the image data they have
|
||||
* associated with the paths previously set on them. If no textures are attached, this does nothing.
|
||||
*
|
||||
* @see sb::Texture::load()
|
||||
*/
|
||||
void load();
|
||||
|
||||
/*!
|
||||
* @return a constanst reference to the model's transformation matrix
|
||||
*/
|
||||
|
|
|
@ -35,8 +35,7 @@ void Segment::end(const glm::vec2& end)
|
|||
end_ = end;
|
||||
}
|
||||
|
||||
// taken from http://www.realtimerendering.com/resources/GraphicsGems/gemsii/xlines.c
|
||||
bool Segment::intersect(const Segment& segment, glm::vec2* intersection) const
|
||||
bool Segment::intersect(const Segment& segment, std::optional<std::reference_wrapper<glm::vec2>> intersection) const
|
||||
{
|
||||
float x1 = start_.x, y1 = start_.y, x2 = end_.x, y2 = end_.y, x3 = segment.start_.x,
|
||||
y3 = segment.start_.y, x4 = segment.end_.x, y4 = segment.end_.y;
|
||||
|
@ -85,24 +84,19 @@ bool Segment::intersect(const Segment& segment, glm::vec2* intersection) const
|
|||
}
|
||||
|
||||
num = b1 * c2 - b2 * c1;
|
||||
if (intersection != NULL)
|
||||
if (intersection.has_value())
|
||||
{
|
||||
intersection->x = num / denom;
|
||||
intersection.value().get().x = num / denom;
|
||||
}
|
||||
num = a2 * c1 - a1 * c2;
|
||||
if (intersection != NULL)
|
||||
if (intersection.has_value())
|
||||
{
|
||||
intersection->y = num / denom;
|
||||
intersection.value().get().y = num / denom;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Segment::intersect(const Segment& segment, glm::vec2& intersection) const
|
||||
{
|
||||
return intersect(segment, &intersection);
|
||||
}
|
||||
|
||||
float Segment::dx() const
|
||||
{
|
||||
return end_.x - start_.x;
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
#include "glm/vec2.hpp"
|
||||
|
||||
|
@ -36,8 +38,19 @@ public:
|
|||
void start(const glm::vec2&);
|
||||
glm::vec2 end() const;
|
||||
void end(const glm::vec2&);
|
||||
bool intersect(const Segment&, glm::vec2* = nullptr) const;
|
||||
bool intersect(const Segment&, glm::vec2&) const;
|
||||
|
||||
/*!
|
||||
* Check for an intersection with another segment object. If the intersection argument is given a reference to a 2D vector,
|
||||
* it will be filled with the point of intersection.
|
||||
*
|
||||
* Original source: http://www.realtimerendering.com/resources/GraphicsGems/gemsii/xlines.c
|
||||
*
|
||||
* @param segment segment to collide with
|
||||
* @param intersection optional reference to a 2D vector to fill with the intersection point
|
||||
* @return `true` if the two segments intersect, `false` otherwise
|
||||
*/
|
||||
bool intersect(const Segment& segment, std::optional<std::reference_wrapper<glm::vec2>> intersection = std::nullopt) const;
|
||||
|
||||
float dx() const;
|
||||
float dy() const;
|
||||
float length() const;
|
||||
|
|
|
@ -11,10 +11,8 @@
|
|||
#include "Texture.hpp"
|
||||
using namespace sb;
|
||||
|
||||
/* Have to pass our deleter to abstract base class at instantiation */
|
||||
Texture::Texture() : GLObject(texture_deleter) {}
|
||||
|
||||
/* If an image path is passed at creation, the path will be stored in the class for later loading */
|
||||
Texture::Texture(fs::path path) : Texture()
|
||||
{
|
||||
associate(path);
|
||||
|
@ -127,20 +125,27 @@ void Texture::load(void* pixels, glm::vec2 size, GLenum format, GLenum type)
|
|||
|
||||
void Texture::bind() const
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, this->id());
|
||||
if (generated())
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, this->id());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Cannot bind texture that has not been generated yet.");
|
||||
}
|
||||
}
|
||||
|
||||
/* glGetTexlevelparameteriv is not available in OpenGL ES 3.0 */
|
||||
#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !defined(ANDROID)
|
||||
void Texture::load(void* pixels, GLenum format, GLenum type)
|
||||
{
|
||||
if (!generated())
|
||||
if (generated())
|
||||
{
|
||||
throw std::invalid_argument("This texture has not been generated and has no size property, so a size must be provided");
|
||||
load(pixels, size(), format, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
load(pixels, size(), format, type);
|
||||
throw std::invalid_argument("This texture has not been generated and has no size property, so a size must be provided");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,8 +46,17 @@ namespace sb
|
|||
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Construct an empty Texture. The deleter will be passed to the base sb::GLObject class.
|
||||
*/
|
||||
Texture();
|
||||
Texture(fs::path);
|
||||
|
||||
/*!
|
||||
* If an image path is passed at creation, the path will be stored in the class for later loading.
|
||||
*
|
||||
* @param path path to an image to be used as the default texture, which will be loaded later with Texture::load()
|
||||
*/
|
||||
Texture(fs::path path);
|
||||
|
||||
/*!
|
||||
* Store an image path as a member variable for loading later. Each texture should have one image
|
||||
|
|
Loading…
Reference in New Issue