2D rendering abstraction

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
3
down vote

favorite












This is a simple 2D rendering abstraction that I've been using for my game. It's basically a vector of quads. It has functions for defining quads (position, rotation, texture coordinates) and a function to render the quads using my OpenGL rendering abstraction. I had to comment out the render function because it depends on the OpenGL renderer which depends on some other stuff (this is the top layer of many layers of abstraction). The vector is broken up into sections. Each section corresponds to its own set of uniforms and a separate call to glDrawElements.



I'd like to know how this can be made faster/cleaner or anything you'd like to say about the code.



quad writer.hpp



namespace Math 
// this is a full class in it's own header
template <typename T>
struct RectPP
glm::tvec2<T> min;
glm::tvec2<T> max;
;


namespace G2D
struct Vertex
glm::vec3 pos;
glm::vec2 texCoord;
;
using Quad = std::array<Vertex, 4>;

// the values of the uniforms
struct RenderParams
// camera matrix
glm::mat3 viewProj = ;
// ID of a texture stored in G2D::Renderer
TextureID tex = 0;
// color to be multiplied by the texture sample in the fragment shader
glm::vec4 color = glm::vec4(1.0f);
;

enum class PlusXY
RIGHT_UP,
LEFT_UP,
RIGHT_DOWN,
LEFT_DOWN
;

enum class Origin
TOP_LEFT,
TOP_MID,
TOP_RIGHT,
MID_RIGHT,
BOTTOM_RIGHT,
BOTTOM_MID,
BOTTOM_LEFT,
MID_LEFT,
CENTER
;

/// Get the depth of a depth enum. The last enumerator must be COUNT
template <typename Enum>
constexpr float depth(const Enum e)
return static_cast<float>(e) / static_cast<float>(Enum::COUNT);


class QuadWriter
public:
QuadWriter();

/// Remove all of the sections
void clear();

/// Start a new section with the given rendering parameters
void section(const RenderParams &);
/// Make space for the given number of quads to avoid further reallocations
void sectionSize(size_t);

/// Sort the quads in the current section by the given sorting predicate
template <typename Function>
void sort(Function &&);

/// Start a new quad and return it
Quad &quad();
/// Start a new quad the is a duplicate of the previous quad and return it
Quad &dup();

/// Set the depth of the current quad
void depth(float);
/// Set the depth of the current quad using an enum. The last enumerator
/// must be COUNT
template <typename Enum>
void depth(Enum);

/// Copy the positions of the verticies from the previous quad into the
/// current quad
void dupPos();
/// Copy the positions of the verticies and the depth from the previous
/// quad into the current quad
void dupPosDepth();
/// Write positions of verticies on the current quad as an axis-aligned
/// rectangle.
void tilePos(glm::vec2, glm::vec2 = 1.0f, 1.0f);
/// Write positions of verticies on the current quad as a rectangle rotated
/// around it's center. The quad is position relative to it's bottom left
/// corner like tilePos.
void rotTilePos(float, glm::vec2, glm::vec2 = 1.0f, 1.0f);
/// Write positions of verticies on the current quad as rectangle rotated
/// around a given origin. The quad is positioned relative to the origin.
/// This function isn't quite as fast as rotTilePos without an origin.
template <Origin ORIGIN>
void rotTilePos(float, glm::vec2, glm::vec2 = 1.0f, 1.0f);

/// Copy the texture coordinates of the verticies on the previous quad onto
/// the current quad
void dupTex();
/// Write texture coordinates of vertices on the current quad assuming that
/// the texture is sampled as an axis-aligned rectangle
template <PlusXY PLUS_XY = PlusXY::RIGHT_UP>
void tileTex(glm::vec2, glm::vec2);
/// Write texture coordinates of vertices on the current quad assuming that
/// the texture is sampled as an axis-aligned rectangle
template <PlusXY PLUS_XY = PlusXY::RIGHT_UP>
void tileTex(Math::RectPP<float>);

/// Copy the quads into GPU memory and issue an number of draw calls
//void render(Renderer &) const;

private:
std::vector<Quad> quads;
// each section is an index to it's first quad
std::vector<size_t> sections;
std::vector<RenderParams> params;
;


#include "quad writer.inl"


quad writer.inl



#include <algorithm>
#include <glm/gtc/constants.hpp>

inline G2D::QuadWriter::QuadWriter()
quads.reserve(2048);
sections.reserve(64);
params.reserve(64);


inline void G2D::QuadWriter::clear()
quads.clear();
sections.clear();
params.clear();


inline void G2D::QuadWriter::section(const RenderParams &param)
sections.push_back(quads.size());
params.push_back(param);


inline void G2D::QuadWriter::sectionSize(const size_t size)
quads.reserve(quads.size() + size);


template <typename Function>
void G2D::QuadWriter::sort(Function &&function)
assert(sections.size());

std::sort(
quads.data() + sections.back(),
quads.data() + quads.size(),
function
);


inline G2D::Quad &G2D::QuadWriter::quad()
assert(sections.size());

quads.push_back();
return quads.back();


inline G2D::Quad &G2D::QuadWriter::dup()
assert(sections.size() && quads.size());

quads.push_back(quads.back());
return quads.back();


inline void G2D::QuadWriter::depth(const float depth)
assert(quads.size() && sections.size());

Quad &quad = quads.back();
quad[0].pos.z =
quad[1].pos.z =
quad[2].pos.z =
quad[3].pos.z = depth;


template <typename Enum>
void G2D::QuadWriter::depth(const Enum e)
depth(G2D::depth(e));


namespace G2D::detail
inline void setPos(glm::vec3 &dst, const glm::vec2 src)
dst.x = src.x;
dst.y = src.y;



inline void G2D::QuadWriter::dupPos()
assert(quads.size() > 1 && sections.size());

Quad &quad = quads.back();
const Quad &prev = *(quads.cend() - 2);
for (size_t i = 0; i != 4; ++i)
detail::setPos(quad[i].pos, prev[i].pos);



inline void G2D::QuadWriter::dupPosDepth()
assert(quads.size() > 1 && sections.size());

Quad &quad = quads.back();
const Quad &prev = *(quads.cend() - 2);
for (size_t i = 0; i != 4; ++i)
quad[i].pos = prev[i].pos;



inline void G2D::QuadWriter::tilePos(
const glm::vec2 pos,
const glm::vec2 size
)
assert(quads.size() && sections.size());

Quad &quad = quads.back();
detail::setPos(quad[0].pos, pos);
detail::setPos(quad[1].pos, pos.x + size.x, pos.y);
detail::setPos(quad[2].pos, pos + size);
detail::setPos(quad[3].pos, pos.x, pos.y + size.y);


inline void G2D::QuadWriter::rotTilePos(
const float angle,
const glm::vec2 pos,
const glm::vec2 size
)
assert(quads.size() && sections.size());

const glm::vec2 halfSize = size * 0.5f;
const glm::vec2 topRight = glm::vec2(
std::cos(angle + glm::quarter_pi<float>()),
std::sin(angle + glm::quarter_pi<float>())
) * halfSize * glm::root_two<float>();
const glm::vec2 topLeft = -topRight.y, topRight.x;
const glm::vec2 botLeft = -topRight;
const glm::vec2 botRight = -topLeft;
const glm::vec2 shift = pos + halfSize;

Quad &quad = quads.back();
detail::setPos(quad[0].pos, botLeft + shift);
detail::setPos(quad[1].pos, botRight + shift);
detail::setPos(quad[2].pos, topRight + shift);
detail::setPos(quad[3].pos, topLeft + shift);


template <G2D::Origin ORIGIN>
void G2D::QuadWriter::rotTilePos(
const float angle,
const glm::vec2 pos,
const glm::vec2 size
)
assert(quads.size() && sections.size());

constexpr glm::vec2 ORIGIN_POS[9] =
0.5f, -0.5f, 0.0f, -0.5f, -0.5f, -0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f, 0.5f, 0.5f, 0.5f, 0.5f, 0.0f,
0.0f, 0.0f
;
constexpr glm::vec2 originPos = ORIGIN_POS[static_cast<size_t>(ORIGIN)];

const glm::vec2 origin = originPos * size;

const float c = std::cos(angle);
const float s = std::sin(angle);
const glm::mat2 rot =
c, s,
-s, c
;

const glm::vec2 halfSize = size * 0.5f;
const glm::vec2 tr = halfSize;
const glm::vec2 bl = -halfSize;
const glm::vec2 tl = bl.x, tr.y;
const glm::vec2 br = tr.x, bl.y;

Quad &quad = quads.back();
detail::setPos(quad[0].pos, pos + rot * (bl + origin));
detail::setPos(quad[1].pos, pos + rot * (br + origin));
detail::setPos(quad[2].pos, pos + rot * (tr + origin));
detail::setPos(quad[3].pos, pos + rot * (tl + origin));


inline void G2D::QuadWriter::dupTex()
assert(quads.size() > 1 && sections.size());

Quad &quad = quads.back();
const Quad &prev = *(quads.cend() - 2);
for (size_t i = 0; i != 4; ++i)
quad[i].texCoord = prev[i].texCoord;



template <G2D::PlusXY PLUS_XY>
void G2D::QuadWriter::tileTex(const glm::vec2 min, const glm::vec2 max)
assert(quads.size() && sections.size());

constexpr size_t Is[4][4] =
// +x +y
0, 1, 2, 3, // right up
1, 0, 3, 2, // left up
3, 2, 1, 0, // right down
2, 3, 0, 1 // left down
;
constexpr size_t i = static_cast<size_t>(PLUS_XY);

Quad &quad = quads.back();
quad[Is[i][0]].texCoord = min;
quad[Is[i][1]].texCoord = max.x, min.y;
quad[Is[i][2]].texCoord = max;
quad[Is[i][3]].texCoord = min.x, max.y;


template <G2D::PlusXY PLUS_XY>
void G2D::QuadWriter::tileTex(const Math::RectPP<float> coords)
tileTex<PLUS_XY>(coords.min, coords.max);


/*
Copy all of the quads directly into the GPU buffer with a single call to
glBufferSubData

For each section,
set uniforms to the values in RenderParams
make a call glDrawElements

inline void G2D::QuadWriter::render(Renderer &renderer) const
renderer.writeQuads(0, quads.size(), quads.data());
if (sections.empty())
return;

QuadRange range;
range.begin = sections[0];
for (size_t s = 1; s != sections.size(); ++s)
range.end = sections[s];
renderer.render(range, params[s - 1]);
range.begin = range.end;

range.end = quads.size();
renderer.render(range, params.back());
*/


Example usage



enum class Depth 
// probably some other stuff above
RENDERABLE,
// probably some other stuff below

COUNT
;

G2D::QuadWriter writer;
entt::DefaultRegistry reg; // ECS container. Stores entities and components

// for each entity with both a Position component and a Renderable component
const auto renderables = reg.view<Position, Renderable>();
for (const uint32_t entity : renderables)
writer.quad();
writer.depth(Depth::RENDERABLE);
writer.tilePos(renderables.get<Position>(entity).pos);
writer.tileTex(renderables.get<Renderable>(entity).spriteRect);







share|improve this question





















  • How is this used? Is your game mostly a 2D tiled game? Are your quads mostly convex quads that are usually squares/rectangles, or do you have many concave quads, trapezoids, and parallelograms?
    – user1118321
    May 24 at 6:00










  • The functions are intended to be used for rectangular and square quads but there's nothing stopping the user from defining an arbitrary quad. The user could just set the Vertexs (vertices) of the Quad returned by quad()
    – Kerndog73
    May 24 at 6:05










  • Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
    – Mathias Ettinger
    May 24 at 9:17
















up vote
3
down vote

favorite












This is a simple 2D rendering abstraction that I've been using for my game. It's basically a vector of quads. It has functions for defining quads (position, rotation, texture coordinates) and a function to render the quads using my OpenGL rendering abstraction. I had to comment out the render function because it depends on the OpenGL renderer which depends on some other stuff (this is the top layer of many layers of abstraction). The vector is broken up into sections. Each section corresponds to its own set of uniforms and a separate call to glDrawElements.



I'd like to know how this can be made faster/cleaner or anything you'd like to say about the code.



quad writer.hpp



namespace Math 
// this is a full class in it's own header
template <typename T>
struct RectPP
glm::tvec2<T> min;
glm::tvec2<T> max;
;


namespace G2D
struct Vertex
glm::vec3 pos;
glm::vec2 texCoord;
;
using Quad = std::array<Vertex, 4>;

// the values of the uniforms
struct RenderParams
// camera matrix
glm::mat3 viewProj = ;
// ID of a texture stored in G2D::Renderer
TextureID tex = 0;
// color to be multiplied by the texture sample in the fragment shader
glm::vec4 color = glm::vec4(1.0f);
;

enum class PlusXY
RIGHT_UP,
LEFT_UP,
RIGHT_DOWN,
LEFT_DOWN
;

enum class Origin
TOP_LEFT,
TOP_MID,
TOP_RIGHT,
MID_RIGHT,
BOTTOM_RIGHT,
BOTTOM_MID,
BOTTOM_LEFT,
MID_LEFT,
CENTER
;

/// Get the depth of a depth enum. The last enumerator must be COUNT
template <typename Enum>
constexpr float depth(const Enum e)
return static_cast<float>(e) / static_cast<float>(Enum::COUNT);


class QuadWriter
public:
QuadWriter();

/// Remove all of the sections
void clear();

/// Start a new section with the given rendering parameters
void section(const RenderParams &);
/// Make space for the given number of quads to avoid further reallocations
void sectionSize(size_t);

/// Sort the quads in the current section by the given sorting predicate
template <typename Function>
void sort(Function &&);

/// Start a new quad and return it
Quad &quad();
/// Start a new quad the is a duplicate of the previous quad and return it
Quad &dup();

/// Set the depth of the current quad
void depth(float);
/// Set the depth of the current quad using an enum. The last enumerator
/// must be COUNT
template <typename Enum>
void depth(Enum);

/// Copy the positions of the verticies from the previous quad into the
/// current quad
void dupPos();
/// Copy the positions of the verticies and the depth from the previous
/// quad into the current quad
void dupPosDepth();
/// Write positions of verticies on the current quad as an axis-aligned
/// rectangle.
void tilePos(glm::vec2, glm::vec2 = 1.0f, 1.0f);
/// Write positions of verticies on the current quad as a rectangle rotated
/// around it's center. The quad is position relative to it's bottom left
/// corner like tilePos.
void rotTilePos(float, glm::vec2, glm::vec2 = 1.0f, 1.0f);
/// Write positions of verticies on the current quad as rectangle rotated
/// around a given origin. The quad is positioned relative to the origin.
/// This function isn't quite as fast as rotTilePos without an origin.
template <Origin ORIGIN>
void rotTilePos(float, glm::vec2, glm::vec2 = 1.0f, 1.0f);

/// Copy the texture coordinates of the verticies on the previous quad onto
/// the current quad
void dupTex();
/// Write texture coordinates of vertices on the current quad assuming that
/// the texture is sampled as an axis-aligned rectangle
template <PlusXY PLUS_XY = PlusXY::RIGHT_UP>
void tileTex(glm::vec2, glm::vec2);
/// Write texture coordinates of vertices on the current quad assuming that
/// the texture is sampled as an axis-aligned rectangle
template <PlusXY PLUS_XY = PlusXY::RIGHT_UP>
void tileTex(Math::RectPP<float>);

/// Copy the quads into GPU memory and issue an number of draw calls
//void render(Renderer &) const;

private:
std::vector<Quad> quads;
// each section is an index to it's first quad
std::vector<size_t> sections;
std::vector<RenderParams> params;
;


#include "quad writer.inl"


quad writer.inl



#include <algorithm>
#include <glm/gtc/constants.hpp>

inline G2D::QuadWriter::QuadWriter()
quads.reserve(2048);
sections.reserve(64);
params.reserve(64);


inline void G2D::QuadWriter::clear()
quads.clear();
sections.clear();
params.clear();


inline void G2D::QuadWriter::section(const RenderParams &param)
sections.push_back(quads.size());
params.push_back(param);


inline void G2D::QuadWriter::sectionSize(const size_t size)
quads.reserve(quads.size() + size);


template <typename Function>
void G2D::QuadWriter::sort(Function &&function)
assert(sections.size());

std::sort(
quads.data() + sections.back(),
quads.data() + quads.size(),
function
);


inline G2D::Quad &G2D::QuadWriter::quad()
assert(sections.size());

quads.push_back();
return quads.back();


inline G2D::Quad &G2D::QuadWriter::dup()
assert(sections.size() && quads.size());

quads.push_back(quads.back());
return quads.back();


inline void G2D::QuadWriter::depth(const float depth)
assert(quads.size() && sections.size());

Quad &quad = quads.back();
quad[0].pos.z =
quad[1].pos.z =
quad[2].pos.z =
quad[3].pos.z = depth;


template <typename Enum>
void G2D::QuadWriter::depth(const Enum e)
depth(G2D::depth(e));


namespace G2D::detail
inline void setPos(glm::vec3 &dst, const glm::vec2 src)
dst.x = src.x;
dst.y = src.y;



inline void G2D::QuadWriter::dupPos()
assert(quads.size() > 1 && sections.size());

Quad &quad = quads.back();
const Quad &prev = *(quads.cend() - 2);
for (size_t i = 0; i != 4; ++i)
detail::setPos(quad[i].pos, prev[i].pos);



inline void G2D::QuadWriter::dupPosDepth()
assert(quads.size() > 1 && sections.size());

Quad &quad = quads.back();
const Quad &prev = *(quads.cend() - 2);
for (size_t i = 0; i != 4; ++i)
quad[i].pos = prev[i].pos;



inline void G2D::QuadWriter::tilePos(
const glm::vec2 pos,
const glm::vec2 size
)
assert(quads.size() && sections.size());

Quad &quad = quads.back();
detail::setPos(quad[0].pos, pos);
detail::setPos(quad[1].pos, pos.x + size.x, pos.y);
detail::setPos(quad[2].pos, pos + size);
detail::setPos(quad[3].pos, pos.x, pos.y + size.y);


inline void G2D::QuadWriter::rotTilePos(
const float angle,
const glm::vec2 pos,
const glm::vec2 size
)
assert(quads.size() && sections.size());

const glm::vec2 halfSize = size * 0.5f;
const glm::vec2 topRight = glm::vec2(
std::cos(angle + glm::quarter_pi<float>()),
std::sin(angle + glm::quarter_pi<float>())
) * halfSize * glm::root_two<float>();
const glm::vec2 topLeft = -topRight.y, topRight.x;
const glm::vec2 botLeft = -topRight;
const glm::vec2 botRight = -topLeft;
const glm::vec2 shift = pos + halfSize;

Quad &quad = quads.back();
detail::setPos(quad[0].pos, botLeft + shift);
detail::setPos(quad[1].pos, botRight + shift);
detail::setPos(quad[2].pos, topRight + shift);
detail::setPos(quad[3].pos, topLeft + shift);


template <G2D::Origin ORIGIN>
void G2D::QuadWriter::rotTilePos(
const float angle,
const glm::vec2 pos,
const glm::vec2 size
)
assert(quads.size() && sections.size());

constexpr glm::vec2 ORIGIN_POS[9] =
0.5f, -0.5f, 0.0f, -0.5f, -0.5f, -0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f, 0.5f, 0.5f, 0.5f, 0.5f, 0.0f,
0.0f, 0.0f
;
constexpr glm::vec2 originPos = ORIGIN_POS[static_cast<size_t>(ORIGIN)];

const glm::vec2 origin = originPos * size;

const float c = std::cos(angle);
const float s = std::sin(angle);
const glm::mat2 rot =
c, s,
-s, c
;

const glm::vec2 halfSize = size * 0.5f;
const glm::vec2 tr = halfSize;
const glm::vec2 bl = -halfSize;
const glm::vec2 tl = bl.x, tr.y;
const glm::vec2 br = tr.x, bl.y;

Quad &quad = quads.back();
detail::setPos(quad[0].pos, pos + rot * (bl + origin));
detail::setPos(quad[1].pos, pos + rot * (br + origin));
detail::setPos(quad[2].pos, pos + rot * (tr + origin));
detail::setPos(quad[3].pos, pos + rot * (tl + origin));


inline void G2D::QuadWriter::dupTex()
assert(quads.size() > 1 && sections.size());

Quad &quad = quads.back();
const Quad &prev = *(quads.cend() - 2);
for (size_t i = 0; i != 4; ++i)
quad[i].texCoord = prev[i].texCoord;



template <G2D::PlusXY PLUS_XY>
void G2D::QuadWriter::tileTex(const glm::vec2 min, const glm::vec2 max)
assert(quads.size() && sections.size());

constexpr size_t Is[4][4] =
// +x +y
0, 1, 2, 3, // right up
1, 0, 3, 2, // left up
3, 2, 1, 0, // right down
2, 3, 0, 1 // left down
;
constexpr size_t i = static_cast<size_t>(PLUS_XY);

Quad &quad = quads.back();
quad[Is[i][0]].texCoord = min;
quad[Is[i][1]].texCoord = max.x, min.y;
quad[Is[i][2]].texCoord = max;
quad[Is[i][3]].texCoord = min.x, max.y;


template <G2D::PlusXY PLUS_XY>
void G2D::QuadWriter::tileTex(const Math::RectPP<float> coords)
tileTex<PLUS_XY>(coords.min, coords.max);


/*
Copy all of the quads directly into the GPU buffer with a single call to
glBufferSubData

For each section,
set uniforms to the values in RenderParams
make a call glDrawElements

inline void G2D::QuadWriter::render(Renderer &renderer) const
renderer.writeQuads(0, quads.size(), quads.data());
if (sections.empty())
return;

QuadRange range;
range.begin = sections[0];
for (size_t s = 1; s != sections.size(); ++s)
range.end = sections[s];
renderer.render(range, params[s - 1]);
range.begin = range.end;

range.end = quads.size();
renderer.render(range, params.back());
*/


Example usage



enum class Depth 
// probably some other stuff above
RENDERABLE,
// probably some other stuff below

COUNT
;

G2D::QuadWriter writer;
entt::DefaultRegistry reg; // ECS container. Stores entities and components

// for each entity with both a Position component and a Renderable component
const auto renderables = reg.view<Position, Renderable>();
for (const uint32_t entity : renderables)
writer.quad();
writer.depth(Depth::RENDERABLE);
writer.tilePos(renderables.get<Position>(entity).pos);
writer.tileTex(renderables.get<Renderable>(entity).spriteRect);







share|improve this question





















  • How is this used? Is your game mostly a 2D tiled game? Are your quads mostly convex quads that are usually squares/rectangles, or do you have many concave quads, trapezoids, and parallelograms?
    – user1118321
    May 24 at 6:00










  • The functions are intended to be used for rectangular and square quads but there's nothing stopping the user from defining an arbitrary quad. The user could just set the Vertexs (vertices) of the Quad returned by quad()
    – Kerndog73
    May 24 at 6:05










  • Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
    – Mathias Ettinger
    May 24 at 9:17












up vote
3
down vote

favorite









up vote
3
down vote

favorite











This is a simple 2D rendering abstraction that I've been using for my game. It's basically a vector of quads. It has functions for defining quads (position, rotation, texture coordinates) and a function to render the quads using my OpenGL rendering abstraction. I had to comment out the render function because it depends on the OpenGL renderer which depends on some other stuff (this is the top layer of many layers of abstraction). The vector is broken up into sections. Each section corresponds to its own set of uniforms and a separate call to glDrawElements.



I'd like to know how this can be made faster/cleaner or anything you'd like to say about the code.



quad writer.hpp



namespace Math 
// this is a full class in it's own header
template <typename T>
struct RectPP
glm::tvec2<T> min;
glm::tvec2<T> max;
;


namespace G2D
struct Vertex
glm::vec3 pos;
glm::vec2 texCoord;
;
using Quad = std::array<Vertex, 4>;

// the values of the uniforms
struct RenderParams
// camera matrix
glm::mat3 viewProj = ;
// ID of a texture stored in G2D::Renderer
TextureID tex = 0;
// color to be multiplied by the texture sample in the fragment shader
glm::vec4 color = glm::vec4(1.0f);
;

enum class PlusXY
RIGHT_UP,
LEFT_UP,
RIGHT_DOWN,
LEFT_DOWN
;

enum class Origin
TOP_LEFT,
TOP_MID,
TOP_RIGHT,
MID_RIGHT,
BOTTOM_RIGHT,
BOTTOM_MID,
BOTTOM_LEFT,
MID_LEFT,
CENTER
;

/// Get the depth of a depth enum. The last enumerator must be COUNT
template <typename Enum>
constexpr float depth(const Enum e)
return static_cast<float>(e) / static_cast<float>(Enum::COUNT);


class QuadWriter
public:
QuadWriter();

/// Remove all of the sections
void clear();

/// Start a new section with the given rendering parameters
void section(const RenderParams &);
/// Make space for the given number of quads to avoid further reallocations
void sectionSize(size_t);

/// Sort the quads in the current section by the given sorting predicate
template <typename Function>
void sort(Function &&);

/// Start a new quad and return it
Quad &quad();
/// Start a new quad the is a duplicate of the previous quad and return it
Quad &dup();

/// Set the depth of the current quad
void depth(float);
/// Set the depth of the current quad using an enum. The last enumerator
/// must be COUNT
template <typename Enum>
void depth(Enum);

/// Copy the positions of the verticies from the previous quad into the
/// current quad
void dupPos();
/// Copy the positions of the verticies and the depth from the previous
/// quad into the current quad
void dupPosDepth();
/// Write positions of verticies on the current quad as an axis-aligned
/// rectangle.
void tilePos(glm::vec2, glm::vec2 = 1.0f, 1.0f);
/// Write positions of verticies on the current quad as a rectangle rotated
/// around it's center. The quad is position relative to it's bottom left
/// corner like tilePos.
void rotTilePos(float, glm::vec2, glm::vec2 = 1.0f, 1.0f);
/// Write positions of verticies on the current quad as rectangle rotated
/// around a given origin. The quad is positioned relative to the origin.
/// This function isn't quite as fast as rotTilePos without an origin.
template <Origin ORIGIN>
void rotTilePos(float, glm::vec2, glm::vec2 = 1.0f, 1.0f);

/// Copy the texture coordinates of the verticies on the previous quad onto
/// the current quad
void dupTex();
/// Write texture coordinates of vertices on the current quad assuming that
/// the texture is sampled as an axis-aligned rectangle
template <PlusXY PLUS_XY = PlusXY::RIGHT_UP>
void tileTex(glm::vec2, glm::vec2);
/// Write texture coordinates of vertices on the current quad assuming that
/// the texture is sampled as an axis-aligned rectangle
template <PlusXY PLUS_XY = PlusXY::RIGHT_UP>
void tileTex(Math::RectPP<float>);

/// Copy the quads into GPU memory and issue an number of draw calls
//void render(Renderer &) const;

private:
std::vector<Quad> quads;
// each section is an index to it's first quad
std::vector<size_t> sections;
std::vector<RenderParams> params;
;


#include "quad writer.inl"


quad writer.inl



#include <algorithm>
#include <glm/gtc/constants.hpp>

inline G2D::QuadWriter::QuadWriter()
quads.reserve(2048);
sections.reserve(64);
params.reserve(64);


inline void G2D::QuadWriter::clear()
quads.clear();
sections.clear();
params.clear();


inline void G2D::QuadWriter::section(const RenderParams &param)
sections.push_back(quads.size());
params.push_back(param);


inline void G2D::QuadWriter::sectionSize(const size_t size)
quads.reserve(quads.size() + size);


template <typename Function>
void G2D::QuadWriter::sort(Function &&function)
assert(sections.size());

std::sort(
quads.data() + sections.back(),
quads.data() + quads.size(),
function
);


inline G2D::Quad &G2D::QuadWriter::quad()
assert(sections.size());

quads.push_back();
return quads.back();


inline G2D::Quad &G2D::QuadWriter::dup()
assert(sections.size() && quads.size());

quads.push_back(quads.back());
return quads.back();


inline void G2D::QuadWriter::depth(const float depth)
assert(quads.size() && sections.size());

Quad &quad = quads.back();
quad[0].pos.z =
quad[1].pos.z =
quad[2].pos.z =
quad[3].pos.z = depth;


template <typename Enum>
void G2D::QuadWriter::depth(const Enum e)
depth(G2D::depth(e));


namespace G2D::detail
inline void setPos(glm::vec3 &dst, const glm::vec2 src)
dst.x = src.x;
dst.y = src.y;



inline void G2D::QuadWriter::dupPos()
assert(quads.size() > 1 && sections.size());

Quad &quad = quads.back();
const Quad &prev = *(quads.cend() - 2);
for (size_t i = 0; i != 4; ++i)
detail::setPos(quad[i].pos, prev[i].pos);



inline void G2D::QuadWriter::dupPosDepth()
assert(quads.size() > 1 && sections.size());

Quad &quad = quads.back();
const Quad &prev = *(quads.cend() - 2);
for (size_t i = 0; i != 4; ++i)
quad[i].pos = prev[i].pos;



inline void G2D::QuadWriter::tilePos(
const glm::vec2 pos,
const glm::vec2 size
)
assert(quads.size() && sections.size());

Quad &quad = quads.back();
detail::setPos(quad[0].pos, pos);
detail::setPos(quad[1].pos, pos.x + size.x, pos.y);
detail::setPos(quad[2].pos, pos + size);
detail::setPos(quad[3].pos, pos.x, pos.y + size.y);


inline void G2D::QuadWriter::rotTilePos(
const float angle,
const glm::vec2 pos,
const glm::vec2 size
)
assert(quads.size() && sections.size());

const glm::vec2 halfSize = size * 0.5f;
const glm::vec2 topRight = glm::vec2(
std::cos(angle + glm::quarter_pi<float>()),
std::sin(angle + glm::quarter_pi<float>())
) * halfSize * glm::root_two<float>();
const glm::vec2 topLeft = -topRight.y, topRight.x;
const glm::vec2 botLeft = -topRight;
const glm::vec2 botRight = -topLeft;
const glm::vec2 shift = pos + halfSize;

Quad &quad = quads.back();
detail::setPos(quad[0].pos, botLeft + shift);
detail::setPos(quad[1].pos, botRight + shift);
detail::setPos(quad[2].pos, topRight + shift);
detail::setPos(quad[3].pos, topLeft + shift);


template <G2D::Origin ORIGIN>
void G2D::QuadWriter::rotTilePos(
const float angle,
const glm::vec2 pos,
const glm::vec2 size
)
assert(quads.size() && sections.size());

constexpr glm::vec2 ORIGIN_POS[9] =
0.5f, -0.5f, 0.0f, -0.5f, -0.5f, -0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f, 0.5f, 0.5f, 0.5f, 0.5f, 0.0f,
0.0f, 0.0f
;
constexpr glm::vec2 originPos = ORIGIN_POS[static_cast<size_t>(ORIGIN)];

const glm::vec2 origin = originPos * size;

const float c = std::cos(angle);
const float s = std::sin(angle);
const glm::mat2 rot =
c, s,
-s, c
;

const glm::vec2 halfSize = size * 0.5f;
const glm::vec2 tr = halfSize;
const glm::vec2 bl = -halfSize;
const glm::vec2 tl = bl.x, tr.y;
const glm::vec2 br = tr.x, bl.y;

Quad &quad = quads.back();
detail::setPos(quad[0].pos, pos + rot * (bl + origin));
detail::setPos(quad[1].pos, pos + rot * (br + origin));
detail::setPos(quad[2].pos, pos + rot * (tr + origin));
detail::setPos(quad[3].pos, pos + rot * (tl + origin));


inline void G2D::QuadWriter::dupTex()
assert(quads.size() > 1 && sections.size());

Quad &quad = quads.back();
const Quad &prev = *(quads.cend() - 2);
for (size_t i = 0; i != 4; ++i)
quad[i].texCoord = prev[i].texCoord;



template <G2D::PlusXY PLUS_XY>
void G2D::QuadWriter::tileTex(const glm::vec2 min, const glm::vec2 max)
assert(quads.size() && sections.size());

constexpr size_t Is[4][4] =
// +x +y
0, 1, 2, 3, // right up
1, 0, 3, 2, // left up
3, 2, 1, 0, // right down
2, 3, 0, 1 // left down
;
constexpr size_t i = static_cast<size_t>(PLUS_XY);

Quad &quad = quads.back();
quad[Is[i][0]].texCoord = min;
quad[Is[i][1]].texCoord = max.x, min.y;
quad[Is[i][2]].texCoord = max;
quad[Is[i][3]].texCoord = min.x, max.y;


template <G2D::PlusXY PLUS_XY>
void G2D::QuadWriter::tileTex(const Math::RectPP<float> coords)
tileTex<PLUS_XY>(coords.min, coords.max);


/*
Copy all of the quads directly into the GPU buffer with a single call to
glBufferSubData

For each section,
set uniforms to the values in RenderParams
make a call glDrawElements

inline void G2D::QuadWriter::render(Renderer &renderer) const
renderer.writeQuads(0, quads.size(), quads.data());
if (sections.empty())
return;

QuadRange range;
range.begin = sections[0];
for (size_t s = 1; s != sections.size(); ++s)
range.end = sections[s];
renderer.render(range, params[s - 1]);
range.begin = range.end;

range.end = quads.size();
renderer.render(range, params.back());
*/


Example usage



enum class Depth 
// probably some other stuff above
RENDERABLE,
// probably some other stuff below

COUNT
;

G2D::QuadWriter writer;
entt::DefaultRegistry reg; // ECS container. Stores entities and components

// for each entity with both a Position component and a Renderable component
const auto renderables = reg.view<Position, Renderable>();
for (const uint32_t entity : renderables)
writer.quad();
writer.depth(Depth::RENDERABLE);
writer.tilePos(renderables.get<Position>(entity).pos);
writer.tileTex(renderables.get<Renderable>(entity).spriteRect);







share|improve this question













This is a simple 2D rendering abstraction that I've been using for my game. It's basically a vector of quads. It has functions for defining quads (position, rotation, texture coordinates) and a function to render the quads using my OpenGL rendering abstraction. I had to comment out the render function because it depends on the OpenGL renderer which depends on some other stuff (this is the top layer of many layers of abstraction). The vector is broken up into sections. Each section corresponds to its own set of uniforms and a separate call to glDrawElements.



I'd like to know how this can be made faster/cleaner or anything you'd like to say about the code.



quad writer.hpp



namespace Math 
// this is a full class in it's own header
template <typename T>
struct RectPP
glm::tvec2<T> min;
glm::tvec2<T> max;
;


namespace G2D
struct Vertex
glm::vec3 pos;
glm::vec2 texCoord;
;
using Quad = std::array<Vertex, 4>;

// the values of the uniforms
struct RenderParams
// camera matrix
glm::mat3 viewProj = ;
// ID of a texture stored in G2D::Renderer
TextureID tex = 0;
// color to be multiplied by the texture sample in the fragment shader
glm::vec4 color = glm::vec4(1.0f);
;

enum class PlusXY
RIGHT_UP,
LEFT_UP,
RIGHT_DOWN,
LEFT_DOWN
;

enum class Origin
TOP_LEFT,
TOP_MID,
TOP_RIGHT,
MID_RIGHT,
BOTTOM_RIGHT,
BOTTOM_MID,
BOTTOM_LEFT,
MID_LEFT,
CENTER
;

/// Get the depth of a depth enum. The last enumerator must be COUNT
template <typename Enum>
constexpr float depth(const Enum e)
return static_cast<float>(e) / static_cast<float>(Enum::COUNT);


class QuadWriter
public:
QuadWriter();

/// Remove all of the sections
void clear();

/// Start a new section with the given rendering parameters
void section(const RenderParams &);
/// Make space for the given number of quads to avoid further reallocations
void sectionSize(size_t);

/// Sort the quads in the current section by the given sorting predicate
template <typename Function>
void sort(Function &&);

/// Start a new quad and return it
Quad &quad();
/// Start a new quad the is a duplicate of the previous quad and return it
Quad &dup();

/// Set the depth of the current quad
void depth(float);
/// Set the depth of the current quad using an enum. The last enumerator
/// must be COUNT
template <typename Enum>
void depth(Enum);

/// Copy the positions of the verticies from the previous quad into the
/// current quad
void dupPos();
/// Copy the positions of the verticies and the depth from the previous
/// quad into the current quad
void dupPosDepth();
/// Write positions of verticies on the current quad as an axis-aligned
/// rectangle.
void tilePos(glm::vec2, glm::vec2 = 1.0f, 1.0f);
/// Write positions of verticies on the current quad as a rectangle rotated
/// around it's center. The quad is position relative to it's bottom left
/// corner like tilePos.
void rotTilePos(float, glm::vec2, glm::vec2 = 1.0f, 1.0f);
/// Write positions of verticies on the current quad as rectangle rotated
/// around a given origin. The quad is positioned relative to the origin.
/// This function isn't quite as fast as rotTilePos without an origin.
template <Origin ORIGIN>
void rotTilePos(float, glm::vec2, glm::vec2 = 1.0f, 1.0f);

/// Copy the texture coordinates of the verticies on the previous quad onto
/// the current quad
void dupTex();
/// Write texture coordinates of vertices on the current quad assuming that
/// the texture is sampled as an axis-aligned rectangle
template <PlusXY PLUS_XY = PlusXY::RIGHT_UP>
void tileTex(glm::vec2, glm::vec2);
/// Write texture coordinates of vertices on the current quad assuming that
/// the texture is sampled as an axis-aligned rectangle
template <PlusXY PLUS_XY = PlusXY::RIGHT_UP>
void tileTex(Math::RectPP<float>);

/// Copy the quads into GPU memory and issue an number of draw calls
//void render(Renderer &) const;

private:
std::vector<Quad> quads;
// each section is an index to it's first quad
std::vector<size_t> sections;
std::vector<RenderParams> params;
;


#include "quad writer.inl"


quad writer.inl



#include <algorithm>
#include <glm/gtc/constants.hpp>

inline G2D::QuadWriter::QuadWriter()
quads.reserve(2048);
sections.reserve(64);
params.reserve(64);


inline void G2D::QuadWriter::clear()
quads.clear();
sections.clear();
params.clear();


inline void G2D::QuadWriter::section(const RenderParams &param)
sections.push_back(quads.size());
params.push_back(param);


inline void G2D::QuadWriter::sectionSize(const size_t size)
quads.reserve(quads.size() + size);


template <typename Function>
void G2D::QuadWriter::sort(Function &&function)
assert(sections.size());

std::sort(
quads.data() + sections.back(),
quads.data() + quads.size(),
function
);


inline G2D::Quad &G2D::QuadWriter::quad()
assert(sections.size());

quads.push_back();
return quads.back();


inline G2D::Quad &G2D::QuadWriter::dup()
assert(sections.size() && quads.size());

quads.push_back(quads.back());
return quads.back();


inline void G2D::QuadWriter::depth(const float depth)
assert(quads.size() && sections.size());

Quad &quad = quads.back();
quad[0].pos.z =
quad[1].pos.z =
quad[2].pos.z =
quad[3].pos.z = depth;


template <typename Enum>
void G2D::QuadWriter::depth(const Enum e)
depth(G2D::depth(e));


namespace G2D::detail
inline void setPos(glm::vec3 &dst, const glm::vec2 src)
dst.x = src.x;
dst.y = src.y;



inline void G2D::QuadWriter::dupPos()
assert(quads.size() > 1 && sections.size());

Quad &quad = quads.back();
const Quad &prev = *(quads.cend() - 2);
for (size_t i = 0; i != 4; ++i)
detail::setPos(quad[i].pos, prev[i].pos);



inline void G2D::QuadWriter::dupPosDepth()
assert(quads.size() > 1 && sections.size());

Quad &quad = quads.back();
const Quad &prev = *(quads.cend() - 2);
for (size_t i = 0; i != 4; ++i)
quad[i].pos = prev[i].pos;



inline void G2D::QuadWriter::tilePos(
const glm::vec2 pos,
const glm::vec2 size
)
assert(quads.size() && sections.size());

Quad &quad = quads.back();
detail::setPos(quad[0].pos, pos);
detail::setPos(quad[1].pos, pos.x + size.x, pos.y);
detail::setPos(quad[2].pos, pos + size);
detail::setPos(quad[3].pos, pos.x, pos.y + size.y);


inline void G2D::QuadWriter::rotTilePos(
const float angle,
const glm::vec2 pos,
const glm::vec2 size
)
assert(quads.size() && sections.size());

const glm::vec2 halfSize = size * 0.5f;
const glm::vec2 topRight = glm::vec2(
std::cos(angle + glm::quarter_pi<float>()),
std::sin(angle + glm::quarter_pi<float>())
) * halfSize * glm::root_two<float>();
const glm::vec2 topLeft = -topRight.y, topRight.x;
const glm::vec2 botLeft = -topRight;
const glm::vec2 botRight = -topLeft;
const glm::vec2 shift = pos + halfSize;

Quad &quad = quads.back();
detail::setPos(quad[0].pos, botLeft + shift);
detail::setPos(quad[1].pos, botRight + shift);
detail::setPos(quad[2].pos, topRight + shift);
detail::setPos(quad[3].pos, topLeft + shift);


template <G2D::Origin ORIGIN>
void G2D::QuadWriter::rotTilePos(
const float angle,
const glm::vec2 pos,
const glm::vec2 size
)
assert(quads.size() && sections.size());

constexpr glm::vec2 ORIGIN_POS[9] =
0.5f, -0.5f, 0.0f, -0.5f, -0.5f, -0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f, 0.5f, 0.5f, 0.5f, 0.5f, 0.0f,
0.0f, 0.0f
;
constexpr glm::vec2 originPos = ORIGIN_POS[static_cast<size_t>(ORIGIN)];

const glm::vec2 origin = originPos * size;

const float c = std::cos(angle);
const float s = std::sin(angle);
const glm::mat2 rot =
c, s,
-s, c
;

const glm::vec2 halfSize = size * 0.5f;
const glm::vec2 tr = halfSize;
const glm::vec2 bl = -halfSize;
const glm::vec2 tl = bl.x, tr.y;
const glm::vec2 br = tr.x, bl.y;

Quad &quad = quads.back();
detail::setPos(quad[0].pos, pos + rot * (bl + origin));
detail::setPos(quad[1].pos, pos + rot * (br + origin));
detail::setPos(quad[2].pos, pos + rot * (tr + origin));
detail::setPos(quad[3].pos, pos + rot * (tl + origin));


inline void G2D::QuadWriter::dupTex()
assert(quads.size() > 1 && sections.size());

Quad &quad = quads.back();
const Quad &prev = *(quads.cend() - 2);
for (size_t i = 0; i != 4; ++i)
quad[i].texCoord = prev[i].texCoord;



template <G2D::PlusXY PLUS_XY>
void G2D::QuadWriter::tileTex(const glm::vec2 min, const glm::vec2 max)
assert(quads.size() && sections.size());

constexpr size_t Is[4][4] =
// +x +y
0, 1, 2, 3, // right up
1, 0, 3, 2, // left up
3, 2, 1, 0, // right down
2, 3, 0, 1 // left down
;
constexpr size_t i = static_cast<size_t>(PLUS_XY);

Quad &quad = quads.back();
quad[Is[i][0]].texCoord = min;
quad[Is[i][1]].texCoord = max.x, min.y;
quad[Is[i][2]].texCoord = max;
quad[Is[i][3]].texCoord = min.x, max.y;


template <G2D::PlusXY PLUS_XY>
void G2D::QuadWriter::tileTex(const Math::RectPP<float> coords)
tileTex<PLUS_XY>(coords.min, coords.max);


/*
Copy all of the quads directly into the GPU buffer with a single call to
glBufferSubData

For each section,
set uniforms to the values in RenderParams
make a call glDrawElements

inline void G2D::QuadWriter::render(Renderer &renderer) const
renderer.writeQuads(0, quads.size(), quads.data());
if (sections.empty())
return;

QuadRange range;
range.begin = sections[0];
for (size_t s = 1; s != sections.size(); ++s)
range.end = sections[s];
renderer.render(range, params[s - 1]);
range.begin = range.end;

range.end = quads.size();
renderer.render(range, params.back());
*/


Example usage



enum class Depth 
// probably some other stuff above
RENDERABLE,
// probably some other stuff below

COUNT
;

G2D::QuadWriter writer;
entt::DefaultRegistry reg; // ECS container. Stores entities and components

// for each entity with both a Position component and a Renderable component
const auto renderables = reg.view<Position, Renderable>();
for (const uint32_t entity : renderables)
writer.quad();
writer.depth(Depth::RENDERABLE);
writer.tilePos(renderables.get<Position>(entity).pos);
writer.tileTex(renderables.get<Renderable>(entity).spriteRect);









share|improve this question












share|improve this question




share|improve this question








edited May 24 at 9:17









Mathias Ettinger

21.8k32875




21.8k32875









asked May 23 at 10:35









Kerndog73

23316




23316











  • How is this used? Is your game mostly a 2D tiled game? Are your quads mostly convex quads that are usually squares/rectangles, or do you have many concave quads, trapezoids, and parallelograms?
    – user1118321
    May 24 at 6:00










  • The functions are intended to be used for rectangular and square quads but there's nothing stopping the user from defining an arbitrary quad. The user could just set the Vertexs (vertices) of the Quad returned by quad()
    – Kerndog73
    May 24 at 6:05










  • Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
    – Mathias Ettinger
    May 24 at 9:17
















  • How is this used? Is your game mostly a 2D tiled game? Are your quads mostly convex quads that are usually squares/rectangles, or do you have many concave quads, trapezoids, and parallelograms?
    – user1118321
    May 24 at 6:00










  • The functions are intended to be used for rectangular and square quads but there's nothing stopping the user from defining an arbitrary quad. The user could just set the Vertexs (vertices) of the Quad returned by quad()
    – Kerndog73
    May 24 at 6:05










  • Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
    – Mathias Ettinger
    May 24 at 9:17















How is this used? Is your game mostly a 2D tiled game? Are your quads mostly convex quads that are usually squares/rectangles, or do you have many concave quads, trapezoids, and parallelograms?
– user1118321
May 24 at 6:00




How is this used? Is your game mostly a 2D tiled game? Are your quads mostly convex quads that are usually squares/rectangles, or do you have many concave quads, trapezoids, and parallelograms?
– user1118321
May 24 at 6:00












The functions are intended to be used for rectangular and square quads but there's nothing stopping the user from defining an arbitrary quad. The user could just set the Vertexs (vertices) of the Quad returned by quad()
– Kerndog73
May 24 at 6:05




The functions are intended to be used for rectangular and square quads but there's nothing stopping the user from defining an arbitrary quad. The user could just set the Vertexs (vertices) of the Quad returned by quad()
– Kerndog73
May 24 at 6:05












Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
– Mathias Ettinger
May 24 at 9:17




Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
– Mathias Ettinger
May 24 at 9:17










1 Answer
1






active

oldest

votes

















up vote
1
down vote













// this is a full class in it's own header


The word is “its”. “it's” is a contraction for “it is”.




Include all the proper library headers. I don’t see <array> or anything else.




Your techniques are modern, except:



glm::vec4 color = glm::vec4(1.0f);


You don’t need to name the type twice. Just use



glm::vec4 color 1.0f;


(I think it is fine even with the = added, in C++17)




⧺ES.9




Avoid ALL_CAPS names.

Do not use ALL_CAPS for constants just because constants used to be macros.





The style in C++ is to put the * or & with the type, not the identifier. This is called out specifically near the beginning of Stroustrup’s first book, and is an intentional difference from C style.




assert(sections.size());


Use std::vector::empty() not size here.




quad[0].pos.z =
quad[1].pos.z =
quad[2].pos.z =
quad[3].pos.z = depth;


Look at std::fill or fill_n.




I think you are doing well, just looking at the code itself — I’m not familiar with the library you are using, so I don’t know if you are approaching it well.



Keep it up!






share|improve this answer





















  • I always get "its" and "it's" mixed up!
    – Kerndog73
    May 24 at 2:59










  • Also, #include <array> isn't there because I copy-and-pasted some stuff from other headers to try to keep my post short so I just forgot to #include the dependencies.
    – Kerndog73
    May 24 at 3:01










  • @Kerndog73, it would be great to see the code as you see it in your IDE, i.e. with includes, formatting and everything.
    – Incomputable
    May 24 at 9:17










Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f195014%2f2d-rendering-abstraction%23new-answer', 'question_page');

);

Post as a guest






























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
1
down vote













// this is a full class in it's own header


The word is “its”. “it's” is a contraction for “it is”.




Include all the proper library headers. I don’t see <array> or anything else.




Your techniques are modern, except:



glm::vec4 color = glm::vec4(1.0f);


You don’t need to name the type twice. Just use



glm::vec4 color 1.0f;


(I think it is fine even with the = added, in C++17)




⧺ES.9




Avoid ALL_CAPS names.

Do not use ALL_CAPS for constants just because constants used to be macros.





The style in C++ is to put the * or & with the type, not the identifier. This is called out specifically near the beginning of Stroustrup’s first book, and is an intentional difference from C style.




assert(sections.size());


Use std::vector::empty() not size here.




quad[0].pos.z =
quad[1].pos.z =
quad[2].pos.z =
quad[3].pos.z = depth;


Look at std::fill or fill_n.




I think you are doing well, just looking at the code itself — I’m not familiar with the library you are using, so I don’t know if you are approaching it well.



Keep it up!






share|improve this answer





















  • I always get "its" and "it's" mixed up!
    – Kerndog73
    May 24 at 2:59










  • Also, #include <array> isn't there because I copy-and-pasted some stuff from other headers to try to keep my post short so I just forgot to #include the dependencies.
    – Kerndog73
    May 24 at 3:01










  • @Kerndog73, it would be great to see the code as you see it in your IDE, i.e. with includes, formatting and everything.
    – Incomputable
    May 24 at 9:17














up vote
1
down vote













// this is a full class in it's own header


The word is “its”. “it's” is a contraction for “it is”.




Include all the proper library headers. I don’t see <array> or anything else.




Your techniques are modern, except:



glm::vec4 color = glm::vec4(1.0f);


You don’t need to name the type twice. Just use



glm::vec4 color 1.0f;


(I think it is fine even with the = added, in C++17)




⧺ES.9




Avoid ALL_CAPS names.

Do not use ALL_CAPS for constants just because constants used to be macros.





The style in C++ is to put the * or & with the type, not the identifier. This is called out specifically near the beginning of Stroustrup’s first book, and is an intentional difference from C style.




assert(sections.size());


Use std::vector::empty() not size here.




quad[0].pos.z =
quad[1].pos.z =
quad[2].pos.z =
quad[3].pos.z = depth;


Look at std::fill or fill_n.




I think you are doing well, just looking at the code itself — I’m not familiar with the library you are using, so I don’t know if you are approaching it well.



Keep it up!






share|improve this answer





















  • I always get "its" and "it's" mixed up!
    – Kerndog73
    May 24 at 2:59










  • Also, #include <array> isn't there because I copy-and-pasted some stuff from other headers to try to keep my post short so I just forgot to #include the dependencies.
    – Kerndog73
    May 24 at 3:01










  • @Kerndog73, it would be great to see the code as you see it in your IDE, i.e. with includes, formatting and everything.
    – Incomputable
    May 24 at 9:17












up vote
1
down vote










up vote
1
down vote









// this is a full class in it's own header


The word is “its”. “it's” is a contraction for “it is”.




Include all the proper library headers. I don’t see <array> or anything else.




Your techniques are modern, except:



glm::vec4 color = glm::vec4(1.0f);


You don’t need to name the type twice. Just use



glm::vec4 color 1.0f;


(I think it is fine even with the = added, in C++17)




⧺ES.9




Avoid ALL_CAPS names.

Do not use ALL_CAPS for constants just because constants used to be macros.





The style in C++ is to put the * or & with the type, not the identifier. This is called out specifically near the beginning of Stroustrup’s first book, and is an intentional difference from C style.




assert(sections.size());


Use std::vector::empty() not size here.




quad[0].pos.z =
quad[1].pos.z =
quad[2].pos.z =
quad[3].pos.z = depth;


Look at std::fill or fill_n.




I think you are doing well, just looking at the code itself — I’m not familiar with the library you are using, so I don’t know if you are approaching it well.



Keep it up!






share|improve this answer













// this is a full class in it's own header


The word is “its”. “it's” is a contraction for “it is”.




Include all the proper library headers. I don’t see <array> or anything else.




Your techniques are modern, except:



glm::vec4 color = glm::vec4(1.0f);


You don’t need to name the type twice. Just use



glm::vec4 color 1.0f;


(I think it is fine even with the = added, in C++17)




⧺ES.9




Avoid ALL_CAPS names.

Do not use ALL_CAPS for constants just because constants used to be macros.





The style in C++ is to put the * or & with the type, not the identifier. This is called out specifically near the beginning of Stroustrup’s first book, and is an intentional difference from C style.




assert(sections.size());


Use std::vector::empty() not size here.




quad[0].pos.z =
quad[1].pos.z =
quad[2].pos.z =
quad[3].pos.z = depth;


Look at std::fill or fill_n.




I think you are doing well, just looking at the code itself — I’m not familiar with the library you are using, so I don’t know if you are approaching it well.



Keep it up!







share|improve this answer













share|improve this answer



share|improve this answer











answered May 23 at 13:53









JDługosz

5,047731




5,047731











  • I always get "its" and "it's" mixed up!
    – Kerndog73
    May 24 at 2:59










  • Also, #include <array> isn't there because I copy-and-pasted some stuff from other headers to try to keep my post short so I just forgot to #include the dependencies.
    – Kerndog73
    May 24 at 3:01










  • @Kerndog73, it would be great to see the code as you see it in your IDE, i.e. with includes, formatting and everything.
    – Incomputable
    May 24 at 9:17
















  • I always get "its" and "it's" mixed up!
    – Kerndog73
    May 24 at 2:59










  • Also, #include <array> isn't there because I copy-and-pasted some stuff from other headers to try to keep my post short so I just forgot to #include the dependencies.
    – Kerndog73
    May 24 at 3:01










  • @Kerndog73, it would be great to see the code as you see it in your IDE, i.e. with includes, formatting and everything.
    – Incomputable
    May 24 at 9:17















I always get "its" and "it's" mixed up!
– Kerndog73
May 24 at 2:59




I always get "its" and "it's" mixed up!
– Kerndog73
May 24 at 2:59












Also, #include <array> isn't there because I copy-and-pasted some stuff from other headers to try to keep my post short so I just forgot to #include the dependencies.
– Kerndog73
May 24 at 3:01




Also, #include <array> isn't there because I copy-and-pasted some stuff from other headers to try to keep my post short so I just forgot to #include the dependencies.
– Kerndog73
May 24 at 3:01












@Kerndog73, it would be great to see the code as you see it in your IDE, i.e. with includes, formatting and everything.
– Incomputable
May 24 at 9:17




@Kerndog73, it would be great to see the code as you see it in your IDE, i.e. with includes, formatting and everything.
– Incomputable
May 24 at 9:17












 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f195014%2f2d-rendering-abstraction%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Greedy Best First Search implementation in Rust

Function to Return a JSON Like Objects Using VBA Collections and Arrays

C++11 CLH Lock Implementation