spacebox/src/Model.hpp

332 lines
13 KiB
C++

/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#pragma once
/* GL functions */
#if defined(__EMSCRIPTEN__)
#include <GL/glew.h>
#elif defined(__ANDROID__) || defined(ANDROID)
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
#else
#include "glew/glew.h"
#endif
#include <iostream>
#include <string>
#include <map>
#include <memory>
#include <iterator>
#include <stdexcept>
#include "glm/glm.hpp"
#include "Attributes.hpp"
#include "Texture.hpp"
#include "Carousel.hpp"
#include "VBO.hpp"
namespace sb
{
class Model
{
private:
std::vector<sb::Texture> _textures;
std::map<std::string, std::shared_ptr<sb::Attributes>> _attributes;
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 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;
/*!
* Get 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
*/
sb::Texture& texture(int index = 0);
/*!
* Add a copy of the given texture to model's list of textures.
*
* @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 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.
*/
void bind();
/*!
* @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;
};
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}
});
/* Map a texture to fill 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}
});
/* A gradient from magenta to yellow */
inline const static std::shared_ptr<sb::Attributes> color = std::make_shared<sb::Attributes>(sb::Attributes{
{1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 0.0f, 1.0f}, {0.8f, 0.0f, 0.3f, 1.0f},
{1.0f, 1.0f, 0.0f, 1.0f}, {0.8f, 0.0f, 0.3f, 1.0f}, {0.8f, 0.0f, 0.3f, 1.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();
};
}