move sprite and pad into sb namespace
This commit is contained in:
parent
7bbc0f851d
commit
d1fe6b927c
423
src/Pad.hpp
423
src/Pad.hpp
|
@ -16,226 +16,229 @@
|
||||||
#include "Switch.hpp"
|
#include "Switch.hpp"
|
||||||
#include "math.hpp"
|
#include "math.hpp"
|
||||||
|
|
||||||
/*!
|
namespace sb
|
||||||
* A Pad is an object containing an sb::Plane which can be clicked to launch an arbitrary user function. It can be sized and placed by setting its
|
|
||||||
* translation and scale values.
|
|
||||||
*
|
|
||||||
* Each instance:
|
|
||||||
*
|
|
||||||
* - Has an sb::Plane or derivative (either a custom one provided, or a default constructed one automatically)
|
|
||||||
* - Has an arbitrary response function for when pressed
|
|
||||||
* - Can collide with a point (for example, mouse click)
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
*
|
|
||||||
* glm::vec3 w = glm::mat3({{1, 0, 0}, {0, 1, 0}, {-0.6739, -0.74, 1}}) * glm::mat3({{.1, 0, 0}, {0, .1 * (460.0 / 768.0), 0}, {0, 0, 1}}) *
|
|
||||||
* glm::vec3({-1, -1, 1});
|
|
||||||
* std::cout << w << std::endl << glm::translate(glm::vec3{-0.6739, -0.74, 0}) *
|
|
||||||
* glm::scale(glm::vec3{.1, .1 * (460.0 / 768.0), 1}) * glm::vec4{-1, -1, 0, 1} << std::endl;
|
|
||||||
* Pad p {background.current(), {-0.6739f, -0.74f}, 0.1f, get_display().window_box().aspect(), std::function<void()>()};
|
|
||||||
* const std::vector<glm::vec2>& p_position = *p.attributes("position");
|
|
||||||
* glm::vec4 final_position = p.transformation() * glm::vec4{p_position[2].x, p_position[2].y, 0, 1};
|
|
||||||
* std::cout << p.transformation() << std::endl << final_position << std::endl;
|
|
||||||
* assert(final_position == glm::vec4({w.x, w.y, 0, 1}));
|
|
||||||
*/
|
|
||||||
template<typename ReturnType = void, typename... Arguments>
|
|
||||||
class Pad
|
|
||||||
{
|
{
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
using Reaction = std::function<ReturnType(bool, Arguments...)>;
|
|
||||||
sb::Switch<ReturnType, Arguments...> connection;
|
|
||||||
sb::Plane plane;
|
|
||||||
Box box;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Construct a Pad object with a default constructed sb::Plane.
|
* A Pad is an object containing an sb::Plane which can be clicked to launch an arbitrary user function. It can be sized and placed by setting its
|
||||||
|
* translation and scale values.
|
||||||
*
|
*
|
||||||
* @see Pad(sb::Plane, const glm::vec2&, float, float, Reaction, float)
|
* Each instance:
|
||||||
|
*
|
||||||
|
* - Has an sb::Plane or derivative (either a custom one provided, or a default constructed one automatically)
|
||||||
|
* - Has an arbitrary response function for when pressed
|
||||||
|
* - Can collide with a point (for example, mouse click)
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* glm::vec3 w = glm::mat3({{1, 0, 0}, {0, 1, 0}, {-0.6739, -0.74, 1}}) * glm::mat3({{.1, 0, 0}, {0, .1 * (460.0 / 768.0), 0}, {0, 0, 1}}) *
|
||||||
|
* glm::vec3({-1, -1, 1});
|
||||||
|
* std::cout << w << std::endl << glm::translate(glm::vec3{-0.6739, -0.74, 0}) *
|
||||||
|
* glm::scale(glm::vec3{.1, .1 * (460.0 / 768.0), 1}) * glm::vec4{-1, -1, 0, 1} << std::endl;
|
||||||
|
* Pad p {background.current(), {-0.6739f, -0.74f}, 0.1f, get_display().window_box().aspect(), std::function<void()>()};
|
||||||
|
* const std::vector<glm::vec2>& p_position = *p.attributes("position");
|
||||||
|
* glm::vec4 final_position = p.transformation() * glm::vec4{p_position[2].x, p_position[2].y, 0, 1};
|
||||||
|
* std::cout << p.transformation() << std::endl << final_position << std::endl;
|
||||||
|
* assert(final_position == glm::vec4({w.x, w.y, 0, 1}));
|
||||||
*/
|
*/
|
||||||
Pad(const glm::vec2& translation = {0.0f, 0.0f}, float scale = 1.0f, float ratio = 1.0f, Reaction on_state_change = Reaction(), float rotation = 0.0f) :
|
template<typename ReturnType = void, typename... Arguments>
|
||||||
Pad(sb::Plane(), translation, scale, ratio, on_state_change, rotation) {}
|
class Pad
|
||||||
|
|
||||||
/*!
|
|
||||||
* Construct a pad object from an sb::Plane, a translation amount, a scale factor, and a reaction function. The translation is relative
|
|
||||||
* to (0.0, 0.0), and the scale is relative to the plane object.
|
|
||||||
*
|
|
||||||
* The reaction function must accept a boolean as its first argument, which will be given the state of the pad object's switch.
|
|
||||||
*
|
|
||||||
* The plane object will be copied into the pad, so any edits must be made before constructing the pad, unless there is a Pad function
|
|
||||||
* that applies the edit, such as Pad::scale(float, float).
|
|
||||||
*
|
|
||||||
* @param plane plane or plane derivative to represent the pad visually
|
|
||||||
* @param translation x, y amount to translate the position
|
|
||||||
* @param scale amount to scale both x and y
|
|
||||||
* @param ratio ratio to adjust scale of x and y
|
|
||||||
* @param on_state_change reaction function which accepts a boolean as its first argument
|
|
||||||
* @param rotation angle in radians to rotate the pad
|
|
||||||
*/
|
|
||||||
Pad(const sb::Plane& plane, glm::vec2 translation = {0.0f, 0.0f}, float scale = 1.0f, float ratio = 1.0f,
|
|
||||||
Reaction on_state_change = Reaction(), float rotation = 0.0f) : plane(plane)
|
|
||||||
{
|
{
|
||||||
box.size({2.0f, 2.0f}, true);
|
|
||||||
if (translation != glm::vec2{0.0f, 0.0f})
|
private:
|
||||||
|
|
||||||
|
using Reaction = std::function<ReturnType(bool, Arguments...)>;
|
||||||
|
sb::Switch<ReturnType, Arguments...> connection;
|
||||||
|
sb::Plane plane;
|
||||||
|
Box box;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Construct a Pad object with a default constructed sb::Plane.
|
||||||
|
*
|
||||||
|
* @see Pad(sb::Plane, const glm::vec2&, float, float, Reaction, float)
|
||||||
|
*/
|
||||||
|
Pad(const glm::vec2& translation = {0.0f, 0.0f}, float scale = 1.0f, float ratio = 1.0f, Reaction on_state_change = Reaction(), float rotation = 0.0f) :
|
||||||
|
Pad(sb::Plane(), translation, scale, ratio, on_state_change, rotation) {}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Construct a pad object from an sb::Plane, a translation amount, a scale factor, and a reaction function. The translation is relative
|
||||||
|
* to (0.0, 0.0), and the scale is relative to the plane object.
|
||||||
|
*
|
||||||
|
* The reaction function must accept a boolean as its first argument, which will be given the state of the pad object's switch.
|
||||||
|
*
|
||||||
|
* The plane object will be copied into the pad, so any edits must be made before constructing the pad, unless there is a Pad function
|
||||||
|
* that applies the edit, such as Pad::scale(float, float).
|
||||||
|
*
|
||||||
|
* @param plane plane or plane derivative to represent the pad visually
|
||||||
|
* @param translation x, y amount to translate the position
|
||||||
|
* @param scale amount to scale both x and y
|
||||||
|
* @param ratio ratio to adjust scale of x and y
|
||||||
|
* @param on_state_change reaction function which accepts a boolean as its first argument
|
||||||
|
* @param rotation angle in radians to rotate the pad
|
||||||
|
*/
|
||||||
|
Pad(const sb::Plane& plane, glm::vec2 translation = {0.0f, 0.0f}, float scale = 1.0f, float ratio = 1.0f,
|
||||||
|
Reaction on_state_change = Reaction(), float rotation = 0.0f) : plane(plane)
|
||||||
{
|
{
|
||||||
this->translate(translation);
|
box.size({2.0f, 2.0f}, true);
|
||||||
}
|
if (translation != glm::vec2{0.0f, 0.0f})
|
||||||
if (scale != 1.0f || ratio != 1.0f)
|
|
||||||
{
|
|
||||||
this->scale(scale, ratio);
|
|
||||||
}
|
|
||||||
if (rotation)
|
|
||||||
{
|
|
||||||
this->rotate(rotation);
|
|
||||||
}
|
|
||||||
this->on_state_change(on_state_change);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Rotate the pad around its center by 90 degrees. If a count is given, rotate by 90 degrees that many times, so for example,
|
|
||||||
* a count of 3 will be a 270 degree rotation. If the count is negative, rotate -90 degrees.
|
|
||||||
*
|
|
||||||
* @param count number of 90 degree rotations to make, or use a negative count to rotate -90 degrees
|
|
||||||
*/
|
|
||||||
void rotate(int count = 1)
|
|
||||||
{
|
|
||||||
plane.transform(glm::rotate(count * glm::half_pi<float>(), glm::vec3{0.0f, 0.0f, 1.0f}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Scale using a factor and ratio that will transform the pad in the X and Y dimensions. The ratio determines how much each axis
|
|
||||||
* is scaled. If the ratio is above one, the X-axis's scale will be divided by the ratio. If the ratio is below one, the Y-axis's
|
|
||||||
* scale will be multiplied by the aspect ratio. If the aspect ratio of the window is given, this will force the pad to display as
|
|
||||||
* a square, and the ratio will be relative to the shorter axis.
|
|
||||||
*
|
|
||||||
* The collision box will be scaled by the same factors.
|
|
||||||
*
|
|
||||||
* @param factor amount to scale in both x and y directions
|
|
||||||
* @param ratio amount to adjust scaling, set to the window aspect ratio to make the pad appear square
|
|
||||||
*/
|
|
||||||
const glm::mat4& scale(float factor, float ratio = 1.0f)
|
|
||||||
{
|
|
||||||
glm::vec3 scale { factor, factor, 1.0f };
|
|
||||||
if (ratio > 1.0f)
|
|
||||||
{
|
|
||||||
scale.x /= ratio;
|
|
||||||
}
|
|
||||||
else if (ratio < 1.0f)
|
|
||||||
{
|
|
||||||
scale.y *= ratio;
|
|
||||||
}
|
|
||||||
box.scale({scale.x, scale.y}, true);
|
|
||||||
return plane.scale(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set the pad's location in the X and Y dimension using a 2D vector. The collision box will be moved by the same translation.
|
|
||||||
*
|
|
||||||
* @param translation x, y distance to translate the pad
|
|
||||||
*/
|
|
||||||
const glm::mat4& translate(const glm::vec2& translation)
|
|
||||||
{
|
|
||||||
box.move(translation);
|
|
||||||
return plane.translate({translation.x, translation.y, 0.0f});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set the function that will run when a pad object is clicked.
|
|
||||||
*
|
|
||||||
* Example, always keep state true and print "Hello, World!" whenever the pad is clicked,
|
|
||||||
*
|
|
||||||
* start_button.on_state_change([&](bool state, int count){
|
|
||||||
* if (state) {
|
|
||||||
* std::ostringstream message;
|
|
||||||
* message << "Hello, " << state << " World! " << count;
|
|
||||||
* sb::Log::log(message);
|
|
||||||
* start_button.press(1);
|
|
||||||
* }});
|
|
||||||
*
|
|
||||||
* @param on_state_change reaction function which accepts a boolean as its first argument
|
|
||||||
*/
|
|
||||||
void on_state_change(Reaction reaction)
|
|
||||||
{
|
|
||||||
connection.on_state_change(reaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns true if the point at given NDC coordinates collides with the pad's collision box. This only works properly if the
|
|
||||||
* Pad is flat in the z-dimension.
|
|
||||||
*
|
|
||||||
* @param position x, y NDC coordinates to check for collision
|
|
||||||
* @return true if the point is inside Pad::box, false otherwise
|
|
||||||
*/
|
|
||||||
bool collide(const glm::vec2& position, const glm::mat4& view, const glm::mat4& projection) const
|
|
||||||
{
|
|
||||||
/* Corners of the box */
|
|
||||||
std::vector<glm::vec3> corners;
|
|
||||||
|
|
||||||
/* Transform each of the corners into NDC coordinates */
|
|
||||||
for (const glm::vec2& vertex : {box.sw(), box.nw(), box.ne(), box.se()})
|
|
||||||
{
|
|
||||||
corners.push_back(sb::world_to_ndc(vertex, projection * view));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create a new box using the NDC bottom-left corner */
|
|
||||||
glm::vec2 control = corners[0];
|
|
||||||
float width = glm::distance(corners[3], corners[0]);
|
|
||||||
float height = glm::distance(corners[1], corners[0]);
|
|
||||||
sb::Box transformed {control, {width, height}};
|
|
||||||
|
|
||||||
/* Collide the NDC box with the caller's coordinates */
|
|
||||||
return transformed.collide(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set the transformation uniform, bind a texture if any are attached, and draw the vertices. Build the full transformation matrix
|
|
||||||
* by combining the Pad object's transformation with the supplied projection and view matrices, and pass it to the shader.
|
|
||||||
*
|
|
||||||
* @param transformation_uniform transformation uniform ID
|
|
||||||
* @param view the view matrix for transforming from world space to camera space
|
|
||||||
* @param projection projection matrix for transforming from camera space to clip space
|
|
||||||
* @param texture_flag_uniform uniform ID for boolean enabling or disabling texture display
|
|
||||||
*/
|
|
||||||
void draw(GLuint transformation_uniform, glm::mat4 view, glm::mat4 projection, std::optional<GLuint> texture_flag_uniform = std::nullopt)
|
|
||||||
{
|
|
||||||
if (!plane.textures().empty())
|
|
||||||
{
|
|
||||||
if (texture_flag_uniform.has_value())
|
|
||||||
{
|
{
|
||||||
glUniform1i(texture_flag_uniform.value(), true);
|
this->translate(translation);
|
||||||
}
|
}
|
||||||
plane.texture().bind();
|
if (scale != 1.0f || ratio != 1.0f)
|
||||||
|
{
|
||||||
|
this->scale(scale, ratio);
|
||||||
|
}
|
||||||
|
if (rotation)
|
||||||
|
{
|
||||||
|
this->rotate(rotation);
|
||||||
|
}
|
||||||
|
this->on_state_change(on_state_change);
|
||||||
}
|
}
|
||||||
else if (texture_flag_uniform.has_value())
|
|
||||||
|
/*!
|
||||||
|
* Rotate the pad around its center by 90 degrees. If a count is given, rotate by 90 degrees that many times, so for example,
|
||||||
|
* a count of 3 will be a 270 degree rotation. If the count is negative, rotate -90 degrees.
|
||||||
|
*
|
||||||
|
* @param count number of 90 degree rotations to make, or use a negative count to rotate -90 degrees
|
||||||
|
*/
|
||||||
|
void rotate(int count = 1)
|
||||||
{
|
{
|
||||||
glUniform1i(texture_flag_uniform.value(), false);
|
plane.transform(glm::rotate(count * glm::half_pi<float>(), glm::vec3{0.0f, 0.0f, 1.0f}));
|
||||||
}
|
}
|
||||||
glm::mat4 mvp = projection * view * plane.transformation();
|
|
||||||
glUniformMatrix4fv(transformation_uniform, 1, GL_FALSE, &mvp[0][0]);
|
|
||||||
plane.enable();
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, plane.attributes("position")->count());
|
|
||||||
plane.disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Run the reaction function.
|
* Scale using a factor and ratio that will transform the pad in the X and Y dimensions. The ratio determines how much each axis
|
||||||
*
|
* is scaled. If the ratio is above one, the X-axis's scale will be divided by the ratio. If the ratio is below one, the Y-axis's
|
||||||
* @param args arguments to pass to the reaction function
|
* scale will be multiplied by the aspect ratio. If the aspect ratio of the window is given, this will force the pad to display as
|
||||||
* @return result of the reaction function if it returns a value, or void otherwise
|
* a square, and the ratio will be relative to the shorter axis.
|
||||||
*/
|
*
|
||||||
ReturnType press(Arguments... args)
|
* The collision box will be scaled by the same factors.
|
||||||
{
|
*
|
||||||
return connection.flip(args...);
|
* @param factor amount to scale in both x and y directions
|
||||||
}
|
* @param ratio amount to adjust scaling, set to the window aspect ratio to make the pad appear square
|
||||||
|
*/
|
||||||
|
const glm::mat4& scale(float factor, float ratio = 1.0f)
|
||||||
|
{
|
||||||
|
glm::vec3 scale { factor, factor, 1.0f };
|
||||||
|
if (ratio > 1.0f)
|
||||||
|
{
|
||||||
|
scale.x /= ratio;
|
||||||
|
}
|
||||||
|
else if (ratio < 1.0f)
|
||||||
|
{
|
||||||
|
scale.y *= ratio;
|
||||||
|
}
|
||||||
|
box.scale({scale.x, scale.y}, true);
|
||||||
|
return plane.scale(scale);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @return size in bytes of the pad object's plane object
|
* Set the pad's location in the X and Y dimension using a 2D vector. The collision box will be moved by the same translation.
|
||||||
*/
|
*
|
||||||
std::size_t size() const
|
* @param translation x, y distance to translate the pad
|
||||||
{
|
*/
|
||||||
return plane.size();
|
const glm::mat4& translate(const glm::vec2& translation)
|
||||||
}
|
{
|
||||||
};
|
box.move(translation);
|
||||||
|
return plane.translate({translation.x, translation.y, 0.0f});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set the function that will run when a pad object is clicked.
|
||||||
|
*
|
||||||
|
* Example, always keep state true and print "Hello, World!" whenever the pad is clicked,
|
||||||
|
*
|
||||||
|
* start_button.on_state_change([&](bool state, int count){
|
||||||
|
* if (state) {
|
||||||
|
* std::ostringstream message;
|
||||||
|
* message << "Hello, " << state << " World! " << count;
|
||||||
|
* sb::Log::log(message);
|
||||||
|
* start_button.press(1);
|
||||||
|
* }});
|
||||||
|
*
|
||||||
|
* @param on_state_change reaction function which accepts a boolean as its first argument
|
||||||
|
*/
|
||||||
|
void on_state_change(Reaction reaction)
|
||||||
|
{
|
||||||
|
connection.on_state_change(reaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Returns true if the point at given NDC coordinates collides with the pad's collision box. This only works properly if the
|
||||||
|
* Pad is flat in the z-dimension.
|
||||||
|
*
|
||||||
|
* @param position x, y NDC coordinates to check for collision
|
||||||
|
* @return true if the point is inside Pad::box, false otherwise
|
||||||
|
*/
|
||||||
|
bool collide(const glm::vec2& position, const glm::mat4& view, const glm::mat4& projection) const
|
||||||
|
{
|
||||||
|
/* Corners of the box */
|
||||||
|
std::vector<glm::vec3> corners;
|
||||||
|
|
||||||
|
/* Transform each of the corners into NDC coordinates */
|
||||||
|
for (const glm::vec2& vertex : {box.sw(), box.nw(), box.ne(), box.se()})
|
||||||
|
{
|
||||||
|
corners.push_back(sb::world_to_ndc(vertex, projection * view));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a new box using the NDC bottom-left corner */
|
||||||
|
glm::vec2 control = corners[0];
|
||||||
|
float width = glm::distance(corners[3], corners[0]);
|
||||||
|
float height = glm::distance(corners[1], corners[0]);
|
||||||
|
sb::Box transformed {control, {width, height}};
|
||||||
|
|
||||||
|
/* Collide the NDC box with the caller's coordinates */
|
||||||
|
return transformed.collide(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set the transformation uniform, bind a texture if any are attached, and draw the vertices. Build the full transformation matrix
|
||||||
|
* by combining the Pad object's transformation with the supplied projection and view matrices, and pass it to the shader.
|
||||||
|
*
|
||||||
|
* @param transformation_uniform transformation uniform ID
|
||||||
|
* @param view the view matrix for transforming from world space to camera space
|
||||||
|
* @param projection projection matrix for transforming from camera space to clip space
|
||||||
|
* @param texture_flag_uniform uniform ID for boolean enabling or disabling texture display
|
||||||
|
*/
|
||||||
|
void draw(GLuint transformation_uniform, glm::mat4 view, glm::mat4 projection, std::optional<GLuint> texture_flag_uniform = std::nullopt)
|
||||||
|
{
|
||||||
|
if (!plane.textures().empty())
|
||||||
|
{
|
||||||
|
if (texture_flag_uniform.has_value())
|
||||||
|
{
|
||||||
|
glUniform1i(texture_flag_uniform.value(), true);
|
||||||
|
}
|
||||||
|
plane.texture().bind();
|
||||||
|
}
|
||||||
|
else if (texture_flag_uniform.has_value())
|
||||||
|
{
|
||||||
|
glUniform1i(texture_flag_uniform.value(), false);
|
||||||
|
}
|
||||||
|
glm::mat4 mvp = projection * view * plane.transformation();
|
||||||
|
glUniformMatrix4fv(transformation_uniform, 1, GL_FALSE, &mvp[0][0]);
|
||||||
|
plane.enable();
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, plane.attributes("position")->count());
|
||||||
|
plane.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Run the reaction function.
|
||||||
|
*
|
||||||
|
* @param args arguments to pass to the reaction function
|
||||||
|
* @return result of the reaction function if it returns a value, or void otherwise
|
||||||
|
*/
|
||||||
|
ReturnType press(Arguments... args)
|
||||||
|
{
|
||||||
|
return connection.flip(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @return size in bytes of the pad object's plane object
|
||||||
|
*/
|
||||||
|
std::size_t size() const
|
||||||
|
{
|
||||||
|
return plane.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
533
src/Sprite.hpp
533
src/Sprite.hpp
|
@ -16,285 +16,288 @@
|
||||||
#include "filesystem.hpp"
|
#include "filesystem.hpp"
|
||||||
#include "Model.hpp"
|
#include "Model.hpp"
|
||||||
|
|
||||||
/*!
|
namespace sb
|
||||||
* A Sprite is a container for an sb::Plane object that resets and stores scale, translation, and rotation matrices every time they are set
|
|
||||||
* and combines them automatically when getting the full transformation. This allows those transformations to be set repeatedly without having
|
|
||||||
* to call sb::Plane::untransform() after each set. In the case of translation, there is also a move function that allows the translation to be
|
|
||||||
* modified without resetting, so the sprite can move relative amounts.
|
|
||||||
*/
|
|
||||||
class Sprite
|
|
||||||
{
|
{
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/* The plane is included using composition instead of inheritance, allowing the user to define a custom plane. */
|
|
||||||
sb::Plane plane;
|
|
||||||
|
|
||||||
/* Keep a reference of the matrix transformations generated when the user applies a transformation, so that each can be reapplied
|
|
||||||
* without having to set all the transformations every time one is changed. */
|
|
||||||
glm::mat4 _scale {1.0f}, _translation {1.0f}, _rotation {1.0f};
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Construct an instance of Sprite using an existing plane object. The plane will be copied into the Sprite, so further edits will
|
* A Sprite is a container for an sb::Plane object that resets and stores scale, translation, and rotation matrices every time they are set
|
||||||
* need to go through the Sprite class.
|
* and combines them automatically when getting the full transformation. This allows those transformations to be set repeatedly without having
|
||||||
*
|
* to call sb::Plane::untransform() after each set. In the case of translation, there is also a move function that allows the translation to be
|
||||||
* @param plane flat model of the sprite
|
* modified without resetting, so the sprite can move relative amounts.
|
||||||
*/
|
*/
|
||||||
Sprite(const sb::Plane& plane) : plane(plane) {};
|
class Sprite
|
||||||
|
|
||||||
/*!
|
|
||||||
* Construct a Sprite with a default constructed sb::Plane and optional scale amount.
|
|
||||||
*
|
|
||||||
* @param scale amount to scale
|
|
||||||
*/
|
|
||||||
Sprite(glm::vec2 scale = glm::vec2{1.0f}) : Sprite(sb::Plane())
|
|
||||||
{
|
{
|
||||||
this->scale(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
private:
|
||||||
* Construct a Sprite with a default constructed sb::Plane and attach a list of textures to the plane. Each texture is a frame of the
|
|
||||||
* sprite's animation. The texture is the 2D graphic that displays in the sprite object's location.
|
/* The plane is included using composition instead of inheritance, allowing the user to define a custom plane. */
|
||||||
*
|
sb::Plane plane;
|
||||||
* @param textures list of textures
|
|
||||||
* @param scale amount to scale
|
/* Keep a reference of the matrix transformations generated when the user applies a transformation, so that each can be reapplied
|
||||||
*/
|
* without having to set all the transformations every time one is changed. */
|
||||||
Sprite(std::initializer_list<sb::Texture> textures, glm::vec2 scale = glm::vec2{1.0f}) : Sprite(scale)
|
glm::mat4 _scale {1.0f}, _translation {1.0f}, _rotation {1.0f};
|
||||||
{
|
|
||||||
for (const sb::Texture& texture : textures)
|
public:
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Construct an instance of Sprite using an existing plane object. The plane will be copied into the Sprite, so further edits will
|
||||||
|
* need to go through the Sprite class.
|
||||||
|
*
|
||||||
|
* @param plane flat model of the sprite
|
||||||
|
*/
|
||||||
|
Sprite(const sb::Plane& plane) : plane(plane) {};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Construct a Sprite with a default constructed sb::Plane and optional scale amount.
|
||||||
|
*
|
||||||
|
* @param scale amount to scale
|
||||||
|
*/
|
||||||
|
Sprite(glm::vec2 scale = glm::vec2{1.0f}) : Sprite(sb::Plane())
|
||||||
{
|
{
|
||||||
this->texture(texture);
|
this->scale(scale);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Construct a Sprite with a default constructed sb::Plane and give the plane a texture. The texture is the 2D graphic that displays
|
* Construct a Sprite with a default constructed sb::Plane and attach a list of textures to the plane. Each texture is a frame of the
|
||||||
* in the sprite object's location.
|
* sprite's animation. The texture is the 2D graphic that displays in the sprite object's location.
|
||||||
*
|
*
|
||||||
* @param texture sprite's 2D graphic
|
* @param textures list of textures
|
||||||
* @param scale amount to scale
|
* @param scale amount to scale
|
||||||
*/
|
*/
|
||||||
Sprite(const sb::Texture& texture, glm::vec2 scale = glm::vec2{1.0f}) : Sprite({texture}, scale) {};
|
Sprite(std::initializer_list<sb::Texture> textures, glm::vec2 scale = glm::vec2{1.0f}) : Sprite(scale)
|
||||||
|
{
|
||||||
|
for (const sb::Texture& texture : textures)
|
||||||
|
{
|
||||||
|
this->texture(texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Construct a ::Sprite object from a list of paths to image files which will be converted into textures.
|
* Construct a Sprite with a default constructed sb::Plane and give the plane a texture. The texture is the 2D graphic that displays
|
||||||
*
|
* in the sprite object's location.
|
||||||
* The textures are loaded into GPU memory if the GL context is active. Otherwise, the path is just attached to
|
*
|
||||||
* each texture, and they must be loaded with a call to Sprite::load after the GL context is active.
|
* @param texture sprite's 2D graphic
|
||||||
*
|
* @param scale amount to scale
|
||||||
* @see sb::Texture::load()
|
*/
|
||||||
*
|
Sprite(const sb::Texture& texture, glm::vec2 scale = glm::vec2{1.0f}) : Sprite({texture}, scale) {};
|
||||||
* @param paths list of paths to images
|
|
||||||
* @param scale amount to scale
|
/*!
|
||||||
*/
|
* Construct a ::Sprite object from a list of paths to image files which will be converted into textures.
|
||||||
Sprite(std::initializer_list<fs::path> paths, glm::vec2 scale = glm::vec2{1.0f}) : Sprite(scale)
|
*
|
||||||
{
|
* The textures are loaded into GPU memory if the GL context is active. Otherwise, the path is just attached to
|
||||||
for (const fs::path& path : paths)
|
* each texture, and they must be loaded with a call to Sprite::load after the GL context is active.
|
||||||
|
*
|
||||||
|
* @see sb::Texture::load()
|
||||||
|
*
|
||||||
|
* @param paths list of paths to images
|
||||||
|
* @param scale amount to scale
|
||||||
|
*/
|
||||||
|
Sprite(std::initializer_list<fs::path> paths, glm::vec2 scale = glm::vec2{1.0f}) : Sprite(scale)
|
||||||
|
{
|
||||||
|
for (const fs::path& path : paths)
|
||||||
|
{
|
||||||
|
this->texture(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Construct a ::Sprite object from a path to an image file which will be converted into a texture.
|
||||||
|
*
|
||||||
|
* @see ::Sprite(std::initializer_list<fs::path>, glm::vec2)
|
||||||
|
*
|
||||||
|
* @param path path to an image
|
||||||
|
* @param scale amount to scale
|
||||||
|
*/
|
||||||
|
Sprite(const fs::path& path, glm::vec2 scale = glm::vec2{1.0f}) : Sprite({path}, scale)
|
||||||
{
|
{
|
||||||
this->texture(path);
|
this->texture(path);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Construct a ::Sprite object from a path to an image file which will be converted into a texture.
|
* Add a previously constructed sb::Texture to the sprite's plane object.
|
||||||
*
|
|
||||||
* @see ::Sprite(std::initializer_list<fs::path>, glm::vec2)
|
|
||||||
*
|
|
||||||
* @param path path to an image
|
|
||||||
* @param scale amount to scale
|
|
||||||
*/
|
|
||||||
Sprite(const fs::path& path, glm::vec2 scale = glm::vec2{1.0f}) : Sprite({path}, scale)
|
|
||||||
{
|
|
||||||
this->texture(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Add a previously constructed sb::Texture to the sprite's plane object.
|
|
||||||
*
|
|
||||||
* @param texture sb::Texture object to add
|
|
||||||
*/
|
|
||||||
void texture(const sb::Texture& texture)
|
|
||||||
{
|
|
||||||
plane.texture(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Add a new texture to the sprite's plane object from a path to an image file which will converted into a new texture.
|
|
||||||
*
|
|
||||||
* The texture is loaded into GPU memory if the GL context is active. Otherwise, the path is just attached to the texture,
|
|
||||||
* and it must be loaded with a call to Sprite::load.
|
|
||||||
*
|
|
||||||
* @param path path to an image
|
|
||||||
*/
|
|
||||||
void texture(const fs::path& path)
|
|
||||||
{
|
|
||||||
sb::Texture texture;
|
|
||||||
if (SDL_GL_GetCurrentContext() != nullptr)
|
|
||||||
{
|
|
||||||
texture.load(path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
texture.associate(path);
|
|
||||||
}
|
|
||||||
this->texture(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Get a constant reference to the texture attached to the sprite's plane object 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
|
|
||||||
*/
|
|
||||||
const sb::Texture& texture(int index = 0) const
|
|
||||||
{
|
|
||||||
return plane.texture(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* If the GL context is active, this can be called to load image paths previously associated with textures attached to the
|
|
||||||
* sprite's plane object.
|
|
||||||
*/
|
|
||||||
void load()
|
|
||||||
{
|
|
||||||
plane.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Get a reference to the plane object's shared pointer to the attributes with the given name. The underlying attributes
|
|
||||||
* object is fully exposed, meaning it can be edited, and both its const and non-const methods can be used.
|
|
||||||
*
|
|
||||||
* @param name name of the attributes, see Model::attributes(const sb::Attributes&, const std::string&)
|
|
||||||
* @return const reference to a shared pointer held by the plane object that points to the attributes with the given name
|
|
||||||
*/
|
|
||||||
const std::shared_ptr<sb::Attributes>& attributes(const std::string name) const
|
|
||||||
{
|
|
||||||
return plane.attributes(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set the sprite plane's translation transformation using an x, y, and z offset. Any previous translation will be reset. Can
|
|
||||||
* be used to move the sprite relative to the origin.
|
|
||||||
*
|
|
||||||
* @param translation transformation along the x, y, and z axes
|
|
||||||
*/
|
|
||||||
void translate(const glm::vec3& translation)
|
|
||||||
{
|
|
||||||
plane.untransform();
|
|
||||||
_translation = plane.translate(translation);
|
|
||||||
transform();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Add to the sprite's current translation transformation. Can be used to move the sprite relative to its current position.
|
|
||||||
*
|
|
||||||
* @param step amount to move sprite's translation transformation in three dimensions
|
|
||||||
*/
|
|
||||||
void move(const glm::vec3& step)
|
|
||||||
{
|
|
||||||
plane.untransform();
|
|
||||||
_translation += plane.translate(step);
|
|
||||||
transform();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set the sprite plane's scale transformation in the x and y dimensions. Any previous scale will be reset.
|
|
||||||
*
|
|
||||||
* @param scale sprite plane's new scale transformation
|
|
||||||
*/
|
|
||||||
void scale(const glm::vec2& scale)
|
|
||||||
{
|
|
||||||
plane.untransform();
|
|
||||||
_scale = plane.scale({scale, 1.0f});
|
|
||||||
transform();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set the sprite plane's rotation transformation to a rotation around the given axis by a given angle. Any previous
|
|
||||||
* rotation will be reset. This does not rotate the sprite relative to its current rotation, it rotates relative to
|
|
||||||
* the initial rotation of zero.
|
|
||||||
*
|
|
||||||
* @param angle angle in radians amount to rotate
|
|
||||||
* @param axis three dimensional axis around which to rotate the sprite
|
|
||||||
*/
|
|
||||||
void rotate(float angle, const glm::vec3& axis)
|
|
||||||
{
|
|
||||||
plane.untransform();
|
|
||||||
_rotation = plane.rotate(angle, axis);
|
|
||||||
transform();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* The translation, scale, and rotation transformations, if previously set, are applied to the object's transformation
|
|
||||||
* property, along with the optional additional transformation matrix argument.
|
|
||||||
*
|
|
||||||
* The existing transformation property will be reset to the identity matrix before this transformation is applied.
|
|
||||||
*
|
|
||||||
* @warning This function works differently than Model::transform(const glm::mat4&). To apply an arbitrary transformation
|
|
||||||
* without having the non-arbitrary transformations applied as well, the rotate, scale, and translate transformations should
|
|
||||||
* be set to the identity matrix.
|
|
||||||
*
|
|
||||||
* @param transformation optional additional transformation to apply
|
|
||||||
*/
|
|
||||||
void transform(glm::mat4 transformation = glm::mat4{1.0f})
|
|
||||||
{
|
|
||||||
plane.untransform();
|
|
||||||
plane.transform(_translation * _scale * _rotation * transformation);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Get the sprite's transformation from Sprite::transform(glm::mat4), which combines the translation, scale, rotation, and an
|
|
||||||
* optional arbitrary transformation, apply the given view and projection transformations, and pass the transformation to the
|
|
||||||
* shader at the given transformation uniform. The uniform is not checked for existence, so it must be present in the shader.
|
|
||||||
*
|
|
||||||
* Then enable the plane's attributes, and draw the amount of vertices in the plane's position attributes using `glDrawArrays`.
|
|
||||||
* Disable the plane's attributes after the draw.
|
|
||||||
*
|
|
||||||
* The optional texture flag uniform can be passed to automatically set that uniform to true if there are textures attached to
|
|
||||||
* this sprite, and false if not. The currently bound shader should be written to use that flag. For example, it could use the
|
|
||||||
* flag to choose whether to use the UV or the color attributes.
|
|
||||||
*
|
|
||||||
* @param transformation_uniform transformation uniform ID
|
|
||||||
* @param view the view matrix for transforming from world space to camera space
|
|
||||||
* @param projection projection matrix for transforming from camera space to clip space
|
|
||||||
* @param texture_flag_uniform uniform ID for boolean enabling or disabling texture display
|
|
||||||
*/
|
|
||||||
void draw(GLuint transformation_uniform, const glm::mat4 view = glm::mat4{1.0f}, const glm::mat4 projection = glm::mat4{1.0f},
|
|
||||||
std::optional<GLuint> texture_flag_uniform = std::nullopt) const
|
|
||||||
{
|
|
||||||
if (!plane.textures().empty())
|
|
||||||
{
|
|
||||||
if (texture_flag_uniform.has_value())
|
|
||||||
{
|
|
||||||
glUniform1i(texture_flag_uniform.value(), true);
|
|
||||||
}
|
|
||||||
plane.texture().bind();
|
|
||||||
}
|
|
||||||
else if (texture_flag_uniform.has_value())
|
|
||||||
{
|
|
||||||
glUniform1i(texture_flag_uniform.value(), false);
|
|
||||||
}
|
|
||||||
glm::mat4 mvp = projection * view * plane.transformation();
|
|
||||||
|
|
||||||
/* It's possible to use glGetActiveUniformName to test for existence of the given uniform index before proceeding, but the
|
|
||||||
* test is left out to optimize speed since the draw call is expected to be used every frame.
|
|
||||||
*
|
*
|
||||||
* If the index is -1, the check could be skipped since -1 is a special case where the uniform is not expected to exist.
|
* @param texture sb::Texture object to add
|
||||||
*/
|
*/
|
||||||
|
void texture(const sb::Texture& texture)
|
||||||
|
{
|
||||||
|
plane.texture(texture);
|
||||||
|
}
|
||||||
|
|
||||||
glUniformMatrix4fv(transformation_uniform, 1, GL_FALSE, &mvp[0][0]);
|
/*!
|
||||||
plane.enable();
|
* Add a new texture to the sprite's plane object from a path to an image file which will converted into a new texture.
|
||||||
glDrawArrays(GL_TRIANGLES, 0, plane.attributes("position")->count());
|
*
|
||||||
plane.disable();
|
* The texture is loaded into GPU memory if the GL context is active. Otherwise, the path is just attached to the texture,
|
||||||
}
|
* and it must be loaded with a call to Sprite::load.
|
||||||
|
*
|
||||||
|
* @param path path to an image
|
||||||
|
*/
|
||||||
|
void texture(const fs::path& path)
|
||||||
|
{
|
||||||
|
sb::Texture texture;
|
||||||
|
if (SDL_GL_GetCurrentContext() != nullptr)
|
||||||
|
{
|
||||||
|
texture.load(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
texture.associate(path);
|
||||||
|
}
|
||||||
|
this->texture(texture);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @return size in bytes of the sprite's plane object
|
* Get a constant reference to the texture attached to the sprite's plane object 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.
|
||||||
std::size_t size() const
|
*
|
||||||
{
|
* @param index index of texture to get
|
||||||
return plane.size();
|
*/
|
||||||
}
|
const sb::Texture& texture(int index = 0) const
|
||||||
};
|
{
|
||||||
|
return plane.texture(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* If the GL context is active, this can be called to load image paths previously associated with textures attached to the
|
||||||
|
* sprite's plane object.
|
||||||
|
*/
|
||||||
|
void load()
|
||||||
|
{
|
||||||
|
plane.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get a reference to the plane object's shared pointer to the attributes with the given name. The underlying attributes
|
||||||
|
* object is fully exposed, meaning it can be edited, and both its const and non-const methods can be used.
|
||||||
|
*
|
||||||
|
* @param name name of the attributes, see Model::attributes(const sb::Attributes&, const std::string&)
|
||||||
|
* @return const reference to a shared pointer held by the plane object that points to the attributes with the given name
|
||||||
|
*/
|
||||||
|
const std::shared_ptr<sb::Attributes>& attributes(const std::string name) const
|
||||||
|
{
|
||||||
|
return plane.attributes(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set the sprite plane's translation transformation using an x, y, and z offset. Any previous translation will be reset. Can
|
||||||
|
* be used to move the sprite relative to the origin.
|
||||||
|
*
|
||||||
|
* @param translation transformation along the x, y, and z axes
|
||||||
|
*/
|
||||||
|
void translate(const glm::vec3& translation)
|
||||||
|
{
|
||||||
|
plane.untransform();
|
||||||
|
_translation = plane.translate(translation);
|
||||||
|
transform();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Add to the sprite's current translation transformation. Can be used to move the sprite relative to its current position.
|
||||||
|
*
|
||||||
|
* @param step amount to move sprite's translation transformation in three dimensions
|
||||||
|
*/
|
||||||
|
void move(const glm::vec3& step)
|
||||||
|
{
|
||||||
|
plane.untransform();
|
||||||
|
_translation += plane.translate(step);
|
||||||
|
transform();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set the sprite plane's scale transformation in the x and y dimensions. Any previous scale will be reset.
|
||||||
|
*
|
||||||
|
* @param scale sprite plane's new scale transformation
|
||||||
|
*/
|
||||||
|
void scale(const glm::vec2& scale)
|
||||||
|
{
|
||||||
|
plane.untransform();
|
||||||
|
_scale = plane.scale({scale, 1.0f});
|
||||||
|
transform();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set the sprite plane's rotation transformation to a rotation around the given axis by a given angle. Any previous
|
||||||
|
* rotation will be reset. This does not rotate the sprite relative to its current rotation, it rotates relative to
|
||||||
|
* the initial rotation of zero.
|
||||||
|
*
|
||||||
|
* @param angle angle in radians amount to rotate
|
||||||
|
* @param axis three dimensional axis around which to rotate the sprite
|
||||||
|
*/
|
||||||
|
void rotate(float angle, const glm::vec3& axis)
|
||||||
|
{
|
||||||
|
plane.untransform();
|
||||||
|
_rotation = plane.rotate(angle, axis);
|
||||||
|
transform();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The translation, scale, and rotation transformations, if previously set, are applied to the object's transformation
|
||||||
|
* property, along with the optional additional transformation matrix argument.
|
||||||
|
*
|
||||||
|
* The existing transformation property will be reset to the identity matrix before this transformation is applied.
|
||||||
|
*
|
||||||
|
* @warning This function works differently than Model::transform(const glm::mat4&). To apply an arbitrary transformation
|
||||||
|
* without having the non-arbitrary transformations applied as well, the rotate, scale, and translate transformations should
|
||||||
|
* be set to the identity matrix.
|
||||||
|
*
|
||||||
|
* @param transformation optional additional transformation to apply
|
||||||
|
*/
|
||||||
|
void transform(glm::mat4 transformation = glm::mat4{1.0f})
|
||||||
|
{
|
||||||
|
plane.untransform();
|
||||||
|
plane.transform(_translation * _scale * _rotation * transformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get the sprite's transformation from Sprite::transform(glm::mat4), which combines the translation, scale, rotation, and an
|
||||||
|
* optional arbitrary transformation, apply the given view and projection transformations, and pass the transformation to the
|
||||||
|
* shader at the given transformation uniform. The uniform is not checked for existence, so it must be present in the shader.
|
||||||
|
*
|
||||||
|
* Then enable the plane's attributes, and draw the amount of vertices in the plane's position attributes using `glDrawArrays`.
|
||||||
|
* Disable the plane's attributes after the draw.
|
||||||
|
*
|
||||||
|
* The optional texture flag uniform can be passed to automatically set that uniform to true if there are textures attached to
|
||||||
|
* this sprite, and false if not. The currently bound shader should be written to use that flag. For example, it could use the
|
||||||
|
* flag to choose whether to use the UV or the color attributes.
|
||||||
|
*
|
||||||
|
* @param transformation_uniform transformation uniform ID
|
||||||
|
* @param view the view matrix for transforming from world space to camera space
|
||||||
|
* @param projection projection matrix for transforming from camera space to clip space
|
||||||
|
* @param texture_flag_uniform uniform ID for boolean enabling or disabling texture display
|
||||||
|
*/
|
||||||
|
void draw(GLuint transformation_uniform, const glm::mat4 view = glm::mat4{1.0f}, const glm::mat4 projection = glm::mat4{1.0f},
|
||||||
|
std::optional<GLuint> texture_flag_uniform = std::nullopt) const
|
||||||
|
{
|
||||||
|
if (!plane.textures().empty())
|
||||||
|
{
|
||||||
|
if (texture_flag_uniform.has_value())
|
||||||
|
{
|
||||||
|
glUniform1i(texture_flag_uniform.value(), true);
|
||||||
|
}
|
||||||
|
plane.texture().bind();
|
||||||
|
}
|
||||||
|
else if (texture_flag_uniform.has_value())
|
||||||
|
{
|
||||||
|
glUniform1i(texture_flag_uniform.value(), false);
|
||||||
|
}
|
||||||
|
glm::mat4 mvp = projection * view * plane.transformation();
|
||||||
|
|
||||||
|
/* It's possible to use glGetActiveUniformName to test for existence of the given uniform index before proceeding, but the
|
||||||
|
* test is left out to optimize speed since the draw call is expected to be used every frame.
|
||||||
|
*
|
||||||
|
* If the index is -1, the check could be skipped since -1 is a special case where the uniform is not expected to exist.
|
||||||
|
*/
|
||||||
|
|
||||||
|
glUniformMatrix4fv(transformation_uniform, 1, GL_FALSE, &mvp[0][0]);
|
||||||
|
plane.enable();
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, plane.attributes("position")->count());
|
||||||
|
plane.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @return size in bytes of the sprite's plane object
|
||||||
|
*/
|
||||||
|
std::size_t size() const
|
||||||
|
{
|
||||||
|
return plane.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue