Newer
Older
Import / projects / Gameloft / bne_lib / code / GUI / DrawCanvas.h
#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_