SpriteGles.h#
↰ Parent directory (OpenGLES
)
Contains the Sprite classes and framework objects used by the UIRenderer (Sprite, Text, Image, Font, Group).
Includes#
PVRCore/math/AxisAlignedBox.h
PVRCore/math/Rectangle.h
PVRUtils/OpenGLES/BindingsGles.h
Included By#
Namespaces#
Classes#
Enums#
Typedefs#
Source Code#
#pragma once
#if SC_ENABLED
#include "PVRUtils/OpenGLSC/BindingsGlsc.h"
#else
#include "PVRUtils/OpenGLES/BindingsGles.h"
#endif
#include "PVRCore/math/Rectangle.h"
#include "PVRCore/math/AxisAlignedBox.h"
#define NUM_BITS_GROUP_ID 8
namespace pvr {
class Texture;
typedef Rectangle<int32_t> Rectanglei;
namespace ui {
class UIRenderer;
namespace impl {
class Font_;
class Text_;
class TextElement_;
class Image_;
class Group_;
class MatrixGroup_;
class PixelGroup_;
class Sprite_;
} // namespace impl
typedef std::shared_ptr<impl::Group_> Group;
typedef std::shared_ptr<impl::MatrixGroup_> MatrixGroup;
typedef std::shared_ptr<impl::PixelGroup_> PixelGroup;
typedef std::shared_ptr<impl::Sprite_> Sprite;
typedef std::weak_ptr<impl::Sprite_> SpriteWeakRef;
typedef std::shared_ptr<impl::Text_> Text;
typedef std::weak_ptr<impl::Text_> TextWeakRef;
typedef std::shared_ptr<impl::Font_> Font;
typedef std::weak_ptr<impl::Font_> FontWeakRef;
typedef std::shared_ptr<impl::TextElement_> TextElement;
typedef std::weak_ptr<impl::TextElement_> TextElementWeakRef;
typedef std::shared_ptr<impl::Image_> Image;
typedef std::weak_ptr<impl::Image_> ImageWeakRef;
enum class Anchor
{
TopLeft,
TopCenter,
TopRight,
CenterLeft,
Center,
CenterRight,
BottomLeft,
BottomCenter,
BottomRight
};
namespace impl {
class Sprite_
{
private:
friend class pvr::ui::UIRenderer;
friend class pvr::ui::impl::Group_;
friend class pvr::ui::impl::Font_;
friend class pvr::ui::impl::PixelGroup_;
friend class pvr::ui::impl::MatrixGroup_;
friend class pvr::ui::impl::Image_;
friend class pvr::ui::impl::TextElement_;
friend class pvr::ui::impl::Text_;
Sprite_(UIRenderer& uiRenderer);
virtual void calculateMvp(uint64_t parentIds, glm::mat4 const& srt, const glm::mat4& viewProj, pvr::Rectanglei const& viewport) const = 0;
/********************************************************************************************************
\brief Do not call directly. Render will call this function.
If writing new sprites, implement this function to render a sprite with the provided
transformation matrix. Necessary to support instanced rendering of the same sprite from
different groups.
********************************************************************************************************/
virtual void onRender(uint64_t parentId) const { (void)parentId; }
protected:
void setUIRenderer(UIRenderer* uiRenderer) { _uiRenderer = uiRenderer; }
mutable math::AxisAlignedBox _boundingRect;
mutable glm::vec4 _color;
mutable int32_t _alphaMode;
UIRenderer* _uiRenderer;
mutable glm::mat4 _cachedMatrix;
glm::mat4 _viewProj;
public:
virtual void commitUpdates() const;
virtual ~Sprite_() {}
glm::vec2 getDimensions() const { return glm::vec2(_boundingRect.getSize()); }
void render() const;
void setAlphaRenderingMode(bool isAlphaOnly) const { _alphaMode = isAlphaOnly; }
void setColor(glm::vec4 color) const { _color = color; }
void setColor(uint32_t r, uint32_t g, uint32_t b, uint32_t a) const
{
_color[0] = static_cast<float>(r / 255.f);
_color[1] = static_cast<float>(g / 255.f);
_color[2] = static_cast<float>(b / 255.f);
_color[3] = static_cast<float>(a / 255.f);
}
void setColor(float r, float g, float b, float a) const
{
_color.r = r;
_color.g = g;
_color.b = b;
_color.a = a;
}
void setColor(uint32_t rgba) const
{
_color[0] = static_cast<float>(rgba & 0xFF) / 255.f;
_color[1] = static_cast<float>(rgba >> 8 & 0xFF) / 255.f;
_color[2] = static_cast<float>(rgba >> 16 & 0xFF) / 255.f;
_color[3] = static_cast<float>(rgba >> 24 & 0xFF) / 255.f;
}
const glm::vec4& getColor() const { return _color; }
bool getAlphaRenderingMode() const { return _alphaMode == 1; }
const glm::mat4& getMatrix() const { return _cachedMatrix; }
math::AxisAlignedBox const& getBoundingBox() const { return _boundingRect; }
virtual glm::vec2 getScaledDimension() const = 0;
};
class I2dComponent
{
private:
friend class ::pvr::ui::UIRenderer;
friend class ::pvr::ui::impl::PixelGroup_;
friend class ::pvr::ui::impl::Text_;
friend class ::pvr::ui::impl::Image_;
I2dComponent()
: _anchor(Anchor::Center), _position(0.f, 0.f), _scale(1.f, 1.f), _rotation(0.f), _isPositioningDirty(true), _pixelOffset(0, 0), _uv(0.0f, 0.0f, 1.0, 1.0f), _isUVDirty(true)
{}
protected:
mutable Anchor _anchor;
mutable glm::vec2 _position;
mutable glm::vec2 _scale;
mutable float _rotation;
mutable bool _isPositioningDirty;
mutable glm::vec2 _pixelOffset;
mutable Rectanglef _uv;
mutable bool _isUVDirty;
public:
virtual ~I2dComponent() {}
I2dComponent const* setAnchor(Anchor anchor, const glm::vec2& ndcPos)
{
setAnchor(anchor, ndcPos.x, ndcPos.y);
return this;
}
I2dComponent const* setAnchor(Anchor anchor, float ndcPosX = -1.f, float ndcPosY = -1.f) const
{
_anchor = anchor;
_position.x = ndcPosX;
_position.y = ndcPosY;
_isPositioningDirty = true;
return this;
}
I2dComponent const* setPixelOffset(float offsetX, float offsetY) const
{
_pixelOffset.x = offsetX, _pixelOffset.y = offsetY;
_isPositioningDirty = true;
return this;
}
I2dComponent const* setPixelOffset(glm::vec2 offset) const { return setPixelOffset(offset.x, offset.y); }
I2dComponent const* setScale(glm::vec2 const& scale) const
{
_scale = scale;
_isPositioningDirty = true;
return this;
}
I2dComponent const* setScale(float scaleX, float scaleY) const
{
_scale = glm::vec2(scaleX, scaleY);
_isPositioningDirty = true;
return this;
}
I2dComponent const* setRotation(float radians) const
{
_rotation = radians;
_isPositioningDirty = true;
return this;
}
I2dComponent const* setUV(const pvr::Rectanglef& uv) const
{
_uv = uv;
_isUVDirty = true;
return this;
}
};
class Image_ : public Sprite_, public I2dComponent
{
private:
friend class pvr::ui::UIRenderer;
class make_shared_enabler
{
protected:
make_shared_enabler() {}
friend class Image_;
};
static Image constructShared(UIRenderer& uiRenderer, const GLuint& texture, uint32_t width, uint32_t height, bool useMipmapped, const GLuint& sampler)
{
return std::make_shared<Image_>(make_shared_enabler{}, uiRenderer, texture, width, height, useMipmapped, sampler);
}
void calculateMvp(uint64_t parentIds, glm::mat4 const& srt, const glm::mat4& viewProj, pvr::Rectanglei const& viewport) const;
void onRender(uint64_t parentId) const;
protected:
struct MvpData
{
glm::mat4 mvp;
MvpData() {}
};
uint32_t _texW;
uint32_t _texH;
GLuint _texture;
GLuint _sampler;
mutable std::map<uint64_t, MvpData> _mvpData;
mutable bool _isTextureDirty;
public:
Image_(make_shared_enabler, UIRenderer& uiRenderer, const GLuint& texture, uint32_t width, uint32_t height, bool useMipmapped, const GLuint& sampler);
virtual ~Image_() {}
uint32_t getWidth() const { return _texW; }
uint32_t getHeight() const { return _texH; }
const GLuint& getTexture() const { return _texture; }
GLuint& getTexture() { return _texture; }
const GLuint& getSampler() const { return _sampler; }
GLuint& getSampler() { return _sampler; }
glm::vec2 getScaledDimension() const { return getDimensions() * _scale; }
};
class Font_
{
public:
struct CharacterUV
{
float ul;
float vt;
float ur;
float vb;
};
struct CharMetrics
{
int16_t xOff;
uint16_t characterWidth;
};
enum
{
InvalidChar = 0xFDFDFDFD,
FontHeader = 0xFCFC0050,
FontCharList = 0xFCFC0051,
FontRects = 0xFCFC0052,
FontMetrics = 0xFCFC0053,
FontYoffset = 0xFCFC0054,
FontKerning = 0xFCFC0055,
MaxRenderableLetters = 0xFFFF >> 2,
FontElement = MaxRenderableLetters * 6,
};
private:
friend class ::pvr::ui::UIRenderer;
class make_shared_enabler
{
protected:
make_shared_enabler() {}
friend class Font_;
};
static Font constructShared(UIRenderer& uiRenderer, const GLuint& tex2D, const Texture& tex, const GLuint& sampler)
{
return std::make_shared<Font_>(make_shared_enabler{}, uiRenderer, tex2D, tex, sampler);
}
static int32_t characterCompFunc(const void* a, const void* b);
static int32_t kerningCompFunc(const void* a, const void* b);
void setUIRenderer(UIRenderer* uiRenderer) { _uiRenderer = uiRenderer; }
struct Header // 12 bytes
{
uint8_t version;
uint8_t spaceWidth;
int16_t numCharacters;
int16_t numKerningPairs;
int16_t ascent;
int16_t lineSpace;
int16_t borderWidth;
} _header;
#pragma pack(push, 4) // force 4byte alignment
struct KerningPair
{
uint64_t pair;
int32_t offset;
};
#pragma pack(pop)
std::vector<uint32_t> _characters;
std::vector<KerningPair> _kerningPairs;
std::vector<CharMetrics> _charMetrics;
std::vector<CharacterUV> _characterUVs;
std::vector<Rectanglei> _rects;
std::vector<int32_t> _yOffsets;
GLuint _texture;
GLuint _sampler;
glm::uvec2 _dim;
uint32_t _alphaRenderingMode;
UIRenderer* _uiRenderer;
public:
Font_(make_shared_enabler, UIRenderer& uiRenderer, const GLuint& tex2D, const Texture& tex, const GLuint& sampler);
void loadFontData(const Texture& texture);
uint32_t findCharacter(uint32_t character) const;
void applyKerning(uint32_t charA, uint32_t charB, float& offset);
const CharMetrics& getCharMetrics(uint32_t index) const { return _charMetrics[index]; }
const CharacterUV& getCharacterUV(uint32_t index) const { return _characterUVs[index]; }
const Rectanglei& getRectangle(uint32_t index) const { return _rects[index]; }
int16_t getFontLineSpacing() const { return _header.lineSpace; }
int16_t getAscent() const { return _header.ascent; }
uint8_t getSpaceWidth() const { return _header.spaceWidth; }
int32_t getYOffset(uint32_t index) const { return _yOffsets[index]; }
bool isAlphaRendering() const { return _alphaRenderingMode != 0; }
GLuint getSampler() const { return _sampler; }
GLuint getTexture() const { return _texture; }
};
struct Vertex
{
float x;
float y;
float z;
float rhw;
float tu;
float tv;
void setData(float inX, float inY, float inZ, float inRhw, float inU, float inV)
{
this->x = inX;
this->y = inY;
this->z = inZ;
this->rhw = inRhw;
this->tu = inU;
this->tv = inV;
}
};
class TextElement_
{
private:
friend class ::pvr::ui::impl::Text_;
friend class ::pvr::ui::UIRenderer;
class make_shared_enabler
{
protected:
make_shared_enabler() {}
friend class TextElement_;
};
static TextElement constructShared(UIRenderer& uiRenderer, const Font& font) { return std::make_shared<TextElement_>(make_shared_enabler{}, uiRenderer, font); }
static TextElement constructShared(UIRenderer& uiRenderer, const std::wstring& str, const Font& font)
{
return std::make_shared<TextElement_>(make_shared_enabler{}, uiRenderer, str, font);
}
static TextElement constructShared(UIRenderer& uiRenderer, const std::string& str, const Font& font)
{
return std::make_shared<TextElement_>(make_shared_enabler{}, uiRenderer, str, font);
}
bool updateText() const
{
if (_isTextDirty)
{
regenerateText();
updateVbo();
_isTextDirty = false;
return true;
}
return false;
}
void setUIRenderer(UIRenderer* uiRenderer) { _uiRenderer = uiRenderer; }
void regenerateText() const;
void updateVbo() const;
void onRender() const;
uint32_t updateVertices(float fZPos, float xPos, float yPos, const std::vector<uint32_t>& text, Vertex* const pVertices) const;
bool _isUtf8;
mutable bool _isTextDirty;
mutable Font _font;
mutable GLuint _vbo;
mutable bool _vboCreated;
mutable uint32_t _vboSize;
mutable std::string _textStr;
mutable std::wstring _textWStr;
mutable std::vector<uint32_t> _utf32;
mutable std::vector<Vertex> _vertices;
mutable int32_t _numCachedVerts;
UIRenderer* _uiRenderer;
mutable math::AxisAlignedBox _boundingRect; //< Bounding rectangle of the sprite
public:
TextElement_(make_shared_enabler, UIRenderer& uiRenderer, const Font& font)
: _isTextDirty(false), _font(font), _vbo(static_cast<GLuint>(-1)), _vboCreated(false), _uiRenderer(&uiRenderer), _isUtf8(false)
{}
TextElement_(make_shared_enabler, UIRenderer& uiRenderer, const std::string& str, const Font& font)
: _font(font), _vbo(static_cast<GLuint>(-1)), _vboCreated(false), _uiRenderer(&uiRenderer)
{
setText(str);
updateText();
}
TextElement_(make_shared_enabler, UIRenderer& uiRenderer, const std::wstring& str, const Font& font)
: _font(font), _vbo(static_cast<uint32_t>(-1)), _vboCreated(false), _uiRenderer(&uiRenderer)
{
setText(str);
updateText();
}
enum
{
MaxLetters = 5120
};
glm::vec2 getDimensions() const { return glm::vec2(_boundingRect.getSize()); }
math::AxisAlignedBox const& getBoundingBox() const { return _boundingRect; }
TextElement_& setText(const std::string& str);
TextElement_& setText(std::string&& str);
TextElement_& setText(const std::wstring& str);
TextElement_& setText(std::wstring&& str);
const std::string& getString() const { return _textStr; }
const std::wstring& getWString() const { return _textWStr; }
const Font& getFont() const { return _font; }
};
class Text_ : public Sprite_, public I2dComponent
{
private:
friend class pvr::ui::UIRenderer;
class make_shared_enabler
{
protected:
make_shared_enabler() {}
friend class Text_;
};
static Text constructShared(UIRenderer& uiRenderer, const TextElement& textElement) { return std::make_shared<Text_>(make_shared_enabler{}, uiRenderer, textElement); }
struct MvpData
{
glm::mat4 mvp;
};
void calculateMvp(uint64_t parentIds, glm::mat4 const& srt, const glm::mat4& viewProj, pvr::Rectanglei const& viewport) const;
void onRender(uint64_t parentId) const;
mutable TextElement _textElement;
mutable std::map<uint64_t, MvpData> _mvpData;
public:
Text_(make_shared_enabler, UIRenderer& uiRenderer, const TextElement& textElement);
virtual ~Text_() {}
const Font getFont() const { return getTextElement()->getFont(); }
TextElement getTextElement() { return _textElement; }
const TextElement getTextElement() const { return _textElement; }
glm::vec2 getScaledDimension() const { return getDimensions() * _scale; }
Text_& setText(const std::string& str)
{
getTextElement()->setText(str);
return *this;
}
Text_& setText(std::string&& str)
{
getTextElement()->setText(std::forward<std::string>(str));
return *this;
}
Text_& setText(const std::wstring& str)
{
getTextElement()->setText(str);
return *this;
}
Text_& setText(std::wstring&& str)
{
getTextElement()->setText(std::forward<std::wstring>(str));
return *this;
}
};
class Group_ : public Sprite_
{
private:
friend class ::pvr::ui::impl::PixelGroup_;
friend class ::pvr::ui::impl::MatrixGroup_;
void calculateMvp(uint64_t parentIds, glm::mat4 const& srt, const glm::mat4& viewProjection, pvr::Rectanglei const& viewport) const
{
glm::mat4 tmpMatrix = srt * _cachedMatrix;
// My cached matrix should always be up-to-date unless overridden. No effect.
for (ChildContainer::iterator it = _children.begin(); it != _children.end(); ++it) { (*it)->calculateMvp(packId(parentIds, _id), tmpMatrix, viewProjection, viewport); }
}
virtual void onRender(uint64_t parentId) const
{
for (ChildContainer::iterator it = _children.begin(); it != _children.end(); ++it) { (*it)->onRender(packId(parentId, _id)); }
}
Group_(UIRenderer& uiRenderer, uint64_t groupid) : Sprite_(uiRenderer), _id(groupid) {}
struct SpriteEntryEquals
{
Sprite sprite;
SpriteEntryEquals(const Sprite& sprite) : sprite(sprite) {}
bool operator()(const Sprite& rhs) { return sprite == rhs; }
};
protected:
uint64_t packId(uint64_t parentIds, uint64_t id) const
{
uint64_t packed = parentIds << NUM_BITS_GROUP_ID;
return packed | id;
}
typedef std::vector<Sprite> ChildContainer;
mutable ChildContainer _children;
uint64_t _id;
public:
Group_* add(const Sprite& sprite);
void add(const Sprite* sprites, uint32_t numSprites)
{
std::for_each(sprites, sprites + numSprites, [&](const Sprite& sprite) { add(sprite); });
}
void remove(const Sprite& sprite)
{
ChildContainer::iterator it = std::find_if(_children.begin(), _children.end(), SpriteEntryEquals(sprite));
if (it != _children.end()) { _children.erase(it); }
// reconstruct the bounding box
_boundingRect.clear();
for (Sprite& currentSprite : _children) { _boundingRect.add(currentSprite->getBoundingBox()); }
}
void removeAll()
{
_children.erase(_children.begin(), _children.end());
_boundingRect.clear();
}
glm::vec2 getScaledDimension() const
{
glm::vec2 dim(0);
for (uint32_t i = 0; i < _children.size(); ++i) { dim += _children[i]->getScaledDimension(); }
return dim;
}
};
class MatrixGroup_ : public Group_
{
private:
friend class pvr::ui::UIRenderer;
class make_shared_enabler
{
protected:
make_shared_enabler() {}
friend class MatrixGroup_;
};
static MatrixGroup constructShared(UIRenderer& uiRenderer, uint64_t id) { return std::make_shared<MatrixGroup_>(make_shared_enabler{}, uiRenderer, id); }
glm::mat4 _viewProj;
void calculateMvp(uint64_t parentIds, glm::mat4 const& srt, const glm::mat4& viewProj, pvr::Rectanglei const& viewport) const
{
glm::mat4 tmpMatrix = srt * _cachedMatrix;
// My cached matrix should always be up-to-date unless overridden. No effect.
for (ChildContainer::iterator it = _children.begin(); it != _children.end(); ++it) { (*it)->calculateMvp(packId(parentIds, _id), tmpMatrix, viewProj, viewport); }
}
public:
MatrixGroup_(make_shared_enabler, UIRenderer& uiRenderer, uint64_t id);
void setScaleRotateTranslate(const glm::mat4& srt) { _cachedMatrix = srt; }
void setViewProjection(const glm::mat4& viewProj) { _viewProj = viewProj; }
void commitUpdates() const;
};
class PixelGroup_ : public Group_, public I2dComponent
{
private:
friend class pvr::ui::UIRenderer;
class make_shared_enabler
{
protected:
make_shared_enabler() {}
friend class PixelGroup_;
};
static PixelGroup constructShared(UIRenderer& uiRenderer, uint64_t id) { return std::make_shared<PixelGroup_>(make_shared_enabler{}, uiRenderer, id); }
void calculateMvp(uint64_t parentIds, glm::mat4 const& srt, const glm::mat4& viewProj, pvr::Rectanglei const& viewport) const;
public:
PixelGroup_(make_shared_enabler, UIRenderer& uiRenderer, uint64_t id) : Group_(uiRenderer, id) {}
PixelGroup_* setSize(glm::vec2 const& size)
{
_boundingRect.setMinMax(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(size.x, size.y, 0.0f));
return this;
}
};
} // namespace impl
} // namespace ui
} // namespace pvr