#ifndef _DRAW_CANVAS_H_
#define _DRAW_CANVAS_H_
#include "RKList.h"
#include "RKMatrix.h"
class RKMaterial;
class RKGeometryChunk;
class RKIndexBuffer;
class RKVertexDeclaration;
class RKRenderLayer;
/// \brief A draw canvas constains rendering for basic UI elements.
/// Each UI element is made up of textured quads that are batched together.
class DrawCanvas
{
public:
/// \brief Get if the canvas is enabled for drawing.
inline bool GetEnabled() const;
/// \brief Set if the canvas is enabled for drawing.
inline void SetEnabled(bool a_isEnabled);
/// \brief Set the projection draw area based on the passed width/height. The origin is set to lower left corner. (Calls GetProjectionDrawArea())
/// \param a_width The width of the draw area
/// \param a_height The height of the draw area
/// \param a_crop If set to true, the canvas will be cropped if the aspect ratio of a_width/a_height does not match the device.
/// If set to false, the canvas will have a padding area in one dimension.
void SetProjectionDrawArea(float a_width, float a_height, bool a_crop);
/// \brief Get the projection draw area based on the passed width/height. The origin is set to lower left corner.
/// \param a_width The width of the draw area
/// \param a_height The height of the draw area
/// \param a_crop If set to true, the canvas will be cropped if the aspect ratio of a_width/a_height does not match the device.
/// If set to false, the canvas will have a padding area in one dimension.
/// \param a_retStartX Return the start x position that will be on screen
/// \param a_retStartY Return the start y position that will be on screen
/// \param a_retWidth Return the width that will be on screen
/// \param a_retHeight Return the height that will be on screen
static void GetProjectionDrawArea(float a_width, float a_height, bool a_crop,
float& a_retStartX, float& a_retStartY, float& a_retWidth, float& a_retHeight);
/// \brief Directly set the projection matrix to be used when rendering all the batches on the canvas
/// (use SetProjectionDrawArea for instead for typical 2D drawing)
/// \param a_projMatrix The projection matrix
inline void SetProjectionMatrix(const RKMatrix& a_projMatrix);
/// \brief Create a drawing batch. The batch will exist for the lifetime of the canvas, but can be updated
/// \param a_layer The render layer to render the batch on
/// \param a_maxQuads The maximum number of quads needed for rendering
/// \param a_materialName The material name to use
/// \param a_textureName The texture to use when rendering the batch
/// \param a_useBlending If blending is to be used on the batch
/// \param a_material User supplied custom material (must exist for the lifetime of the canvas)
/// \return The index of the new batch
uint32_t CreateBatchFromTexture(RKRenderLayer* a_layer, uint32_t a_maxQuads, const char* a_textureName, bool a_useBlending);
uint32_t CreateBatchFromMaterial(RKRenderLayer* a_layer, uint32_t a_maxQuads, const char* a_materialName);
uint32_t CreateBatch(RKRenderLayer* a_layer, uint32_t a_maxQuads, RKMaterial* a_material);
/// \brief Create a shared drawing batch. The batch will share the geometry/material with another batch.
/// Each batch can still have a unique position.
/// \param a_shareBatch The batch to share data with
/// \param a_material User supplied custom material (must exist for the lifetime of the canvas)
/// \return The index of the new batch
uint32_t CreateBatchShare(uint32_t a_shareBatch);
uint32_t CreateBatchShare(uint32_t a_shareBatch, RKMaterial* a_material);
/// \brief Set the model-view matrix on the batch
/// \param a_batch The batch index
/// \param a_mvMatrix The model view matrix to set
inline void SetMatrix(uint32_t a_batch, const RKMatrix& a_mvMatrix);
/// \brief Set the user vector on the given batch
/// \param a_batch The batch index
/// \param a_vectorIndex The user vector index
/// \param a_vectorData The vector data
void SetUserVector(uint32_t a_batch, uint32_t a_vectorIndex, const RKVector& a_vectorData);
/// \brief Get the material used in rendering the batch
/// \param a_batch The batch to get the material from
/// \return The material for the batch is returned
RKMaterial* GetBatchMaterial(uint32_t a_batch);
/// \brief Set a basic rectangle quad for the given batch. (Assumes origin is lower left)
/// \param a_batch The batch to set the quad on
/// \param a_x The X position of the quad on the canvas
/// \param a_y The Y position of the quad on the canvas
/// \param a_w The width of the quad on the canvas
/// \param a_h The height of the quad on the canvas
/// \param a_u The texture U start position (normalized texture space)
/// \param a_v The texture V start position (normalized texture space)
/// \param a_texWidth The texture width (normalized texture space)
/// \param a_texHeight The texture height (normalized texture space)
void SetQuad(uint32_t a_batch, float a_x, float a_y, float a_w, float a_h,
float a_u = 0.0f, float a_v = 1.0f, float a_texWidth = 1.0f, float a_texHeight = 1.0f);
/// \brief Begin Setting vertex data on the passed batch (OpenGL immediate mode style)
/// \param a_batch The batch to set the vertex data on
void Begin(uint32_t a_batch);
void Texcoord(float a_u, float a_v);
void Color(float a_r, float a_g, float a_b, float a_a);
void Vertex(float a_x, float a_y, float a_z = 0.0f);
/// \Brief High level quad specification - issues 4 Vertex calls
void Quad(float a_x, float a_y, float a_w, float a_h,
float a_u = 0.0f, float a_v = 1.0f, float a_texWidth = 1.0f, float a_texHeight = 1.0f);
void End();
static const char *GetAlphaMaterialName();
static const char *GetSolidMaterialName();
protected:
friend class DrawCanvasManager;
/// A draw batch on the canvas - has a material and a buffer
class Batch
{
public:
RKMatrix m_mvMatrix = RKMatrix::identity; //!< The matrix to use when rendering
RKGeometryChunk* m_geometryChunk = nullptr; //!< The geometry chunk for rendering
int32_t m_shareIndex = -1; //!< Index of parent if this batch is sharing geometry
};
/// The layout of the vertex data to update
struct UpdateData
{
float m_position[3];
float m_texCoord[2];
float m_color[4];
};
static_assert(sizeof(UpdateData) == (sizeof(float) * 9), "Bad type size");
bool m_isEnabled = true; //!< Denotes if the canvas should be drawn
RKVertexDeclaration* m_vertexDecl = nullptr; //!< The vertex format
RKMatrix m_projMatrix = RKMatrix::identity; //!< The projection matrix for the canvas
RKIndexBuffer* m_drawIndexBuffer = nullptr; //!< The index buffer used for renering
RKList<RKMaterial*> m_materials; //!< The list of internally stored materials
RKList<Batch> m_batches; //!< The list of render batches
RKGeometryChunk* m_updateChunk = nullptr; //!< The current update chunk
uint32_t m_updateVertexCount = 0; //!< How many vertices have been updated
UpdateData* m_updateVertexData = nullptr; //!< The current update vertex data offset
UpdateData m_updateData = {}; //!< The current vertex data to set
/// Protected constructor/destructor - only constructed from a manager
explicit DrawCanvas(RKVertexDeclaration* a_vertexDecl);
#if RKHEAP_ENABLE_ELEPHANT == 0
~DrawCanvas();
#else
public:
~DrawCanvas();
protected:
#endif
/// Draw the canvas objects
void Draw();
/// \brief Create a material from a given texture (and indicates if blending is used)
/// The returned material is stored internally and will be deleted when the canvas is
/// \param a_textureName The texture name to use
/// \param a_useBlending If the material is a alpha blending material
/// \return Returns the new material on success (managed internally)
RKMaterial* CreateMaterialFromTexture(const char* a_textureName, bool a_useBlending);
};
inline void DrawCanvas::SetProjectionMatrix(const RKMatrix& a_projMatrix)
{
m_projMatrix = a_projMatrix;
}
inline void DrawCanvas::SetMatrix(uint32_t a_batch, const RKMatrix& a_mvMatrix)
{
m_batches[a_batch].m_mvMatrix = a_mvMatrix;
}
inline bool DrawCanvas::GetEnabled() const
{
return m_isEnabled;
}
inline void DrawCanvas::SetEnabled(bool a_isEnabled)
{
m_isEnabled = a_isEnabled;
}
#endif //_DRAW_CANVAS_H_