spacebox/src/Model.hpp
Cocktail Frank ac8d023bf4 Add visibility flag to sprite objects
Make Reaction type public so users can store reaction functions in
variables.

A lot of line length linting.
2025-02-12 23:53:15 -05:00

387 lines
15 KiB
C++

/* +=======================================================+
____/ \____ /: Open source game framework licensed to freely use, :
\ / / : copy, and modify - created for dank.game :
+==\ ^__^ /==+ : :
: ~/ \~ : : Download at https://open.shampoo.ooo/shampoo/spacebox :
: ~~~~~~~~~~~~ : +=======================================================+
: SPACE ~~~~~ : /
: ~~~~~~~ BOX :/
+=============*/
#pragma once
#include <iostream>
#include <string>
#include <map>
#include <memory>
#include <iterator>
#include <stdexcept>
#include "glm/glm.hpp"
#include "gl.h"
#include "Attributes.hpp"
#include "Texture.hpp"
#include "Carousel.hpp"
#include "VBO.hpp"
namespace sb
{
class Model
{
private:
/* When a texture is copied, it copies its shared pointer to its ID that identifies the texture memory on the
* GPU. Therefore, when this object is copied, its textures are copied with reference to the same GPU memory
* preserved. */
std::vector<sb::Texture> _textures;
/* The attributes are associated with vertex data copied on the GPU. Shared pointers are used to store the
* attributes, so that if a copy of this object is made, each attributes in the copied object will point to its
* originally constructed attributes object, preserving the association with the vertex data on the GPU. */
std::map<std::string, std::shared_ptr<sb::Attributes>> _attributes;
/* The model keeps a single transformation matrix. Specific types of transformation, like scale, translate, etc,
* can be combined into this transformation. */
glm::mat4 _transformation {1.0f};
public:
/*!
* Construct a sb::Model object with no attributes or textures.
*/
Model() {};
/*!
* Construct a sb::Model, adding sb::Attributes each already wrapped in a shared pointer. The attributes should
* be passed as a map with each key being a name and each value being a shared pointer to attributes.
*
* @param attributes_pack map of names to attribute objects wrapped in shared pointers
*/
Model(const std::map<std::string, std::shared_ptr<sb::Attributes>>& attributes_pack);
/*!
* Construct a sb::Model, adding sb::Attributes, which will each be wrapped in a shared pointer and stored in the
* created object. The attributes should be passed as a map with each key being a name and each value being an
* attributes object.
*
* @param attributes_pack map of names to attribute objects to be wrapped in a shared pointer and added
*/
Model(const std::map<std::string, sb::Attributes>& attributes_pack);
/*!
* Construct a new model object by passing a list of names which will be used to initialize empty attributes
* objects with the given names.
*
* @param names attributes names
*/
Model(const std::initializer_list<std::string>& names);
/*!
* Get a writable reference to the entire map of attributes, each wrapped in its shared pointer held by this
* object.
*
* @return writable reference to the entire map of attributes
*/
std::map<std::string, std::shared_ptr<sb::Attributes>>& attributes();
/*!
* Get a constant reference to the entire map of attributes, each wrapped in its shared pointer held by this
* object.
*
* @return constant reference to the entire map of attributes
*/
const std::map<std::string, std::shared_ptr<sb::Attributes>>& attributes() const;
/*!
* Get a writable reference to the attributes at name, wrapped in the shared pointer held by this object.
*
* This function uses the at method of std::map, so name must refer to attributes already stored in this model.
* Use this function to share ownership of the attributes or to gain access to the public interface of the
* attributes.
*
* @param name name of the attributes held by this model to retrieve
*
* @return writable reference to the attributes at name
*/
std::shared_ptr<sb::Attributes>& attributes(const std::string& name);
/*!
* Get a constant reference to the attributes at name, wrapped in the shared pointer held by this object.
*
* @see Model::attributes(const std::string&)
*
* @param name name of the attributes held by this model to retrieve
*
* @return constant reference to the attributes at name
*/
const std::shared_ptr<sb::Attributes>& attributes(const std::string& name) const;
/*!
* Assign name to attributes, copy and wrap in a shared pointer. The model can share ownership of the created
* attribute memory with callers that request it.
*
* Any existing attributes with the same name will be replaced with the given attributes.
*
* @param attributes attributes object to copy and wrap
* @param name name the model will associate with the attributes
*/
void attributes(const sb::Attributes& attributes, const std::string& name);
/*!
* Assign name to attributes (for example, "position" or "uv") and share ownership.
*
* Any existing attributes with the same name will be replaced with the given attributes.
*
* @param attributes attributes object to share with this model
* @param name name the model will associate with the attributes
*/
void attributes(const std::shared_ptr<sb::Attributes>& attributes, const std::string& name);
/*!
* Get the attributes under name, wrapped in the shared pointer held by this object.
*
* This function uses operator[] or std::map, so this can be used to add new attributes to the object if they
* are wrapped in a shared pointer or the return value is deferenced.
*
* sb::Plane plane;
* sb::Attributes color_attributes;
* color_attributes.extend({255.0f, 0.0f, 0.0f, 255.0f}, plane["position"]->count());
* plane["color"] = std::make_shared<sb::Attributes>(color_attributes);
* // *plane["color"] = color_attributes; // same effect
* // plane.attributes(color_attributes, "color"); // same effect
* sprite = sb::Sprite(plane);
*
* @param name name of the attributes to get
*
* @return writable reference to the attributes at name
*/
std::shared_ptr<sb::Attributes>& operator[](const std::string& name);
/*!
* Enable all attributes.
*/
void enable() const;
/*!
* Disable all attributes.
*/
void disable() const;
/*!
* @return a constant reference to the texture container
*/
const std::vector<sb::Texture>& textures() const;
/*!
* @return a reference to the texture container
*/
std::vector<sb::Texture>& textures();
/*!
* Get a constant reference to the texture at the given index. If there are no textures, an exception will be
* thrown. If the index is greater than the number of textures or is a negative number, modulus will be be used
* to wrap the value of the index to the size of the texture list.
*
* @param index index of texture to get
*/
const sb::Texture& texture(int index) const;
/*!
* Get the texture at the given index. If there are no textures, an exception will be thrown. If the index is
* greater than the number of textures or is a negative number, modulus will be be used to wrap the value of the
* index to the size of the texture list.
*
* @param index index of texture to get
*/
sb::Texture& texture(int index);
/*!
* Add a copy of the given texture to model's list of textures.
*
* Note: the texture object is copied, but the texture data is not copied. The texture data is stored as a
* shared pointer in the texture object, so only the pointer is copied.
*
* @param texture texture to add to model
*/
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();
/*!
* Add all attributes to the given vertex buffer object. The buffer object should have been previously allocated
* to at least the size of this model by passing Model::size() to VBO::allocate(GLsizeiptr, GLenum).
*
* The VBO must currently be bound.
*
* @param vbo vertex buffer object that the model's attribute vertices will be added to
*/
void add(sb::VBO& vbo);
/*!
* Bind all of this model's textures by calling each of their bind methods. Textures must already have GL
* indices set, for example by calling Texture::generate() on each.
*/
virtual void bind_textures() const;
/*!
* Bind all of this model's attributes by calling each of their bind methods. Attributes must already have GL
* indices set, for example by calling Attributes::index(GLint) on each.
*/
virtual void bind_attributes() const;
/*!
* Bind all of this model's attributes and textures by calling each of their bind methods. Textures and
* attributes all must already have GL indices set, for example by calling Texture::generate() and
* Attributes::index(GLint) on each.
*/
virtual void bind() const;
/*!
* @return a constanst reference to the model's transformation matrix
*/
const glm::mat4& transformation() const;
/*!
* Apply the given transformation matrix to the model's current transformation matrix.
*
* @return the model's new transformation matrix
*/
const glm::mat4& transform(const glm::mat4& transformation);
/*!
* Resets the model's transformation to the identity matrix.
*
* @return the model's new transformation matrix, the identity matrix
*/
const glm::mat4& untransform();
/*!
* Specialized version of transform(const glm::mat4&) that builds a scale transformation from an x, y, z vector
* and applies it to the model's transformation matrix.
*
* @param scale x, y, z scale
*
* @return the model's new transformation matrix
*/
const glm::mat4& scale(const glm::vec3& scale);
/*!
* Specialized version of transform(const glm::mat4&) that builds a rotation matrix from an angle in radians and
* an axis vector and applies it to the model's transformation matrix.
*
* The axis is the vector around which the model will be rotated. For example, to rotate a 2D plane around the
* origin, use the z-axis: `glm::vec3(0.0f, 0.0f, 1.0f)`.
*
* @param angle angle in radians
* @param axis axis to rotate the model around
*
* @return the model's new transformation matrix
*/
const glm::mat4& rotate(float angle, glm::vec3 axis);
/*!
* Specialized version of transform(const glm::mat4&) that builds a translation matrix from a 3D translation
* vector indicating the distance to translate along each axis and applies it to the model's transformation
* matrix.
*
* @param translation distance to translate in the x, y, and z dimensions
*
* @return the model's new transformation matrix
*/
const glm::mat4& translate(glm::vec3 translation);
/*!
* @return total size in bytes of the attributes
*/
std::size_t size() const;
/*!
* @return the transformation matrix
*/
operator glm::mat4() const;
/*!
* Convert to a string with some debugging information about the model.
*/
operator std::string() const;
/*!
* Overload the stream operator to support model objects. Add a string representation of the model to the output
* stream. Since this is defined as a friend function and isn't in the global scope, it should prevent it being
* looked up with arguments other than model objects.
*
* @param out output stream
* @param model model object to print
*
* @return edited output stream
*/
friend std::ostream& operator<<(std::ostream& out, const Model& model);
/*!
* It is required for a base class to define a destructor. See https://stackoverflow.com/a/10024812
*/
virtual ~Model() = default;
};
class Plane : public Model
{
public:
/* A plane in the X and Y dimensions, from (-1.0, -1.0) to (1.0, 1.0) */
inline const static std::shared_ptr<sb::Attributes> position = std::make_shared<sb::Attributes>(sb::Attributes{
{-1.0f, 1.0f}, {1.0f, 1.0f}, {-1.0f, -1.0f},
{1.0f, 1.0f}, {1.0f, -1.0f}, {-1.0f, -1.0f}
});
/* Create texture map that fills the plane */
inline const static std::shared_ptr<sb::Attributes> uv = std::make_shared<sb::Attributes>(sb::Attributes{
{0.0f, 1.0f}, {1.0f, 1.0f}, {0.0f, 0.0f},
{1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f}
});
/* Use transparent black color so it can easily be added to in the shader */
inline const static std::shared_ptr<sb::Attributes> color = std::make_shared<sb::Attributes>(sb::Attributes{
{0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}
});
Plane() : Model(std::map<std::string, std::shared_ptr<sb::Attributes>>(
{{"position", position}, {"uv", uv}, {"color", color}})) {}
};
/*!
* A version of `Plane` which contains two texture objects, one of which is active at a time. A reference
* to the active `sb::Texture` object is available from `PlaneDoubleBuffer.active`, and the inactive object is
* available from `PlaneDoubleBuffer.inactive`. The buffers can be swapped using `PlaneDoubleBuffer.swap`.
*/
class PlaneDoubleBuffer : public Plane
{
private:
bool swapped = false;
enum { FRONT, BACK };
public:
PlaneDoubleBuffer();
void generate(const glm::vec2&);
sb::Texture& active();
sb::Texture& inactive();
void swap();
};
}