367 lines
15 KiB
C++
367 lines
15 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
|
|
|
|
#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);
|
|
|
|
};
|
|
|
|
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();
|
|
|
|
};
|
|
}
|