2D rendering abstraction
Clash 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 ¶m)
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);
c++ c++17 opengl
add a comment |Â
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 ¶m)
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);
c++ c++17 opengl
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 theVertex
s (vertices) of theQuad
returned byquad()
â 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
add a comment |Â
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 ¶m)
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);
c++ c++17 opengl
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 ¶m)
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);
c++ c++17 opengl
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 theVertex
s (vertices) of theQuad
returned byquad()
â 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
add a comment |Â
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 theVertex
s (vertices) of theQuad
returned byquad()
â 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
Vertex
s (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
Vertex
s (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
add a comment |Â
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!
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
add a comment |Â
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!
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
add a comment |Â
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!
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
add a comment |Â
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!
// 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!
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
add a comment |Â
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
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
Vertex
s (vertices) of theQuad
returned byquad()
â 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