Như đã trình bày ở phần trươc, CGraphics2D là một lớp tổng quát, xây dựng cho nhiều nền video driver bên dưới. Do đó, cần thiết có một interface tổng quát cho tất cả các driver.
Interface này được thiết kế dưới dạng một lớp pure virtual
IGraphics2D
class IGraphics2D { public: virtual void Reset() = 0; virtual void Clear(SColor c) = 0; virtual void Clear(SColor<__UINT8> c) = 0; virtual void SetColor(SColor c) = 0; virtual void SetColor(SColor<__UINT8> c) = 0; virtual void Flush() = 0; virtual void DrawRect(SRect rect) = 0; virtual void DrawRect(SRect<__INT32> rect) = 0; virtual void DrawLine(SPosition2D startpoint, SPosition2D endpoint) = 0; virtual void DrawLine(SPosition2D<__INT32> startpoint, SPosition2D<__INT32> endpoint) = 0; virtual void DrawTriangle(SPosition2D p1, SPosition2Dp2, SPosition2Dp3) = 0; virtual void DrawTriangle(SPosition2D<__INT32> p1, SPosition2D<__INT32> p2, SPosition2D<__INT32> p3) = 0; virtual void FillRect(SRect rect) = 0; virtual void FillRect(SRect<__INT32> rect) = 0; virtual void FillTriangle(SPosition2D p1, SPosition2Dp2, SPosition2Dp3) = 0; virtual void FillTriangle(SPosition2D<__INT32> p1, SPosition2D<__INT32> p2, SPosition2D<__INT32> p3) = 0; };
Trong đó:
Các hàm khác sẽ được bổ sau nếu cần.
Qui tắt IGraphics: tất cả các lời gọi hàm sẽ không được thực thi (vẽ) ngay, mà được lưu lại trong bộ đệm, và chỉ vẽ ra một lần khi gọi Flush.
Lớp CGraphics2D phải thỏa yêu cầu:
Lớp CGraphics2D tổng quát
CGraphics2D
template <class VideoDriver> class CGraphics2D:public CSingleton<CGraphics2D<VideoDriver>>, public IGraphics2D { friend class CSingleton<CGraphics2D<VideoDriver>>; protected: CGraphics2D(); public: void Reset() {} void Clear() {} void Flush() {} virtual void Clear(SColor<float> c) {} virtual void Clear(SColor<__UINT8> c) {} void SetColor(SColor<float> c) {} void SetColor(SColor<__UINT8> c) {} void DrawRect(SRect<float> rect) {} void DrawRect(SRect<__INT32> rect) {} void DrawLine(SPosition2D<float> startpoint, SPosition2D<float> endpoint) {} void DrawLine(SPosition2D<__INT32> startpoint, SPosition2D<__INT32> endpoint) {} void DrawTriangle(SPosition2D<float> p1, SPosition2D<float>p2, SPosition2D<float>p3) {} void DrawTriangle(SPosition2D<__INT32> p1, SPosition2D<__INT32> p2, SPosition2D<__INT32> p3) {} void FillRect(SRect<float> rect) {} void FillRect(SRect<__INT32> rect) {} void FillTriangle(SPosition2D<float> p1, SPosition2D<float>p2, SPosition2D<float>p3) {} void FillTriangle(SPosition2D<__INT32> p1, SPosition2D<__INT32> p2, SPosition2D<__INT32> p3) {} };
Trong lớp tổng quát, tất cả các hàm đều "rỗng", đồng nghĩa với, nếu driver không phù hợp, các hàm trên sẽ không thực hiện gì cả.
Trong trường hợp driver sử dụng là CGLPipelineDriver, ta có một trường hợp đặc biệt của CGraphics2D là CGraphics2D<CGLPipelineDriver>
CGraphics2D_GLPipeline.h
#define EG2D_PIPELINE_MAX_VERTEX_STACK 1000 enum EG2DGLPipelineElementType { EG2DP_ELETYPE_LINE, EG2DP_ELETYPE_TRIANGLE, EG2DP_ELETYPE_RECT, }; template <> class CGraphics2D<CGLPipelineDriver>:public CSingleton<CGraphics2D<CGLPipelineDriver>> { struct SGraphics2DGLPipelineVertexData { SPosition2D<float> Pos; SColor<float> Color; SPosition2D<float> TexCoor; }; friend class CSingleton<CGraphics2D<CGLPipelineDriver>>; protected: CGraphics2D() { m_pVideoDriver = CGLPipelineDriver::GetInstance(); m_iNumberOfElement = 0; m_iStreamOffset = 0; m_Color = SColor<float>(0.0, 0.0, 0.0, 1.0); } CGLPipelineDriver *m_pVideoDriver; public: void Reset() { __UINT32 w = (CViewController<CVSView>::GetInstance()->GetView()->GetWidth()); __UINT32 h = (CViewController<CVSView>::GetInstance()->GetView()->GetHeight()); Clear(SColor<float>(0, 0, 0, 1)); m_pVideoDriver->Viewport(SRect<__INT32>(0, 0, w, h)); m_pVideoDriver->LoadIdentity(EGLPD_MATRIXMODE_PROJECTION); m_pVideoDriver->Ortho(0, float(w), 0, float(h) , 0, 1000 ); m_pVideoDriver->EnableAttribPointer(EGLPD_ATTRIB_VERTEX, false); m_pVideoDriver->EnableAttribPointer(EGLPD_ATTRIB_COLOR, false); m_pVideoDriver->EnableAttribPointer(EGLPD_ATTRIB_NORMAL, false); m_pVideoDriver->EnableAttribPointer(EGLPD_ATTRIB_TEXCOOR, false); } void Clear(SColor<float>c) { m_pVideoDriver->Clear(c); } void Clear(SColor<__UINT8> c) { m_pVideoDriver->Clear(SColor<float>(c.Red/255.0f, c.Green/255.0f, c.Blue/255.0f, c.Alpha/255.0f)); } void SetColor(SColor<float> c) { memcpy(&m_Color, &c, sizeof(m_Color)); } void SetColor(SColor<__UINT8> c) { m_Color.Alpha = c.Alpha/255.0f; m_Color.Blue = c.Blue/255.0f; m_Color.Green = c.Green/255.0f; m_Color.Red = c.Red/255.0f; } void DrawRect(SRect<float> rect) { TODO("DrawRect is not implemented"); } void DrawRect(SRect<__INT32> rect) { TODO("DrawRect is not implemented"); } void DrawLine(SPosition2D<float> startpoint, SPosition2D<float> endpoint) { TODO("DrawLine is not implemented"); } void DrawLine(SPosition2D<__INT32> startpoint, SPosition2D<__INT32> endpoint) { TODO("DrawLine is not implemented"); } void DrawTriangle(SPosition2D<float> p1, SPosition2D<float>p2, SPosition2D<float>p3) { TODO("DrawTriangle is not implemented"); } void DrawTriangle(SPosition2D<__INT32> p1, SPosition2D<__INT32> p2, SPosition2D<__INT32> p3) { TODO("DrawTriangle is not implemented"); } void FillRect(SRect<float> rect) { m_Type[m_iNumberOfElement] = EG2DP_ELETYPE_RECT; SGraphics2DGLPipelineVertexData vertex[4] = { {SPosition2D<float>(rect.X, rect.Y + rect.H), m_Color, SPosition2D<float>(0, 0)}, {SPosition2D<float>(rect.X, rect.Y), m_Color, SPosition2D<float>(0, 0)}, {SPosition2D<float>(rect.X + rect.W, rect.Y + rect.H), m_Color, SPosition2D<float>(0, 0)}, {SPosition2D<float>(rect.X + rect.W, rect.Y), m_Color, SPosition2D<float>(0, 0)} }; memcpy(m_pStream + m_iStreamOffset, vertex, sizeof(SGraphics2DGLPipelineVertexData)*4); m_iNumberOfElement++; m_iStreamOffset += 4; } void FillRect(SRect<__INT32> rect) { SRect<float> rect2(float(rect.X), float(rect.Y), float(rect.W), float(rect.H)); FillRect(rect2); } void FillTriangle(SPosition2D<float> p1, SPosition2D<float>p2, SPosition2D<float>p3) { m_Type[m_iNumberOfElement] = EG2DP_ELETYPE_TRIANGLE; SGraphics2DGLPipelineVertexData vertex[3] = { {p1, m_Color, SPosition2D<float>(0, 0)}, {p2, m_Color, SPosition2D<float>(0, 0)}, {p3, m_Color, SPosition2D<float>(0, 0)} }; memcpy(m_pStream + m_iStreamOffset, vertex, sizeof(SGraphics2DGLPipelineVertexData)*3); m_iNumberOfElement++; m_iStreamOffset += 3; } void FillTriangle(SPosition2D<__INT32> p1, SPosition2D<__INT32> p2, SPosition2D<__INT32> p3) { FillTriangle(SPosition2D<float>(float(p1.X), float(p1.Y)), SPosition2D<float>(float(p2.X), float(p2.Y)), SPosition2D<float>(float(p3.X), float(p3.Y))); } void Flush() { __UINT8 * bstream = (__UINT8 *)(m_pStream); __UINT32 stride = sizeof(SGraphics2DGLPipelineVertexData); BREAK_IF(m_iNumberOfElement >= EG2D_PIPELINE_MAX_VERTEX_STACK, "m_iNumberOfElement is out of range"); int numVertex = 0; for (int i = 0; i < m_iNumberOfElement; i++) { switch (m_Type[i]) { case EG2DP_ELETYPE_RECT: numVertex = 4; break; case EG2DP_ELETYPE_TRIANGLE: numVertex = 3; break; } if (numVertex) { m_pVideoDriver->EnableAttribPointer(EGLPD_ATTRIB_VERTEX, true); m_pVideoDriver->EnableAttribPointer(EGLPD_ATTRIB_COLOR, true); m_pVideoDriver->EnableAttribPointer(EGLPD_ATTRIB_NORMAL, false); m_pVideoDriver->EnableAttribPointer(EGLPD_ATTRIB_TEXCOOR, false); m_pVideoDriver->EnableBlending(m_Color.Alpha != 1); m_pVideoDriver->AttributePointer( EGLPD_ATTRIB_VERTEX, 2, EGLPD_PRIYTPE_FLOAT, stride, bstream); m_pVideoDriver->AttributePointer( EGLPD_ATTRIB_COLOR, 4, EGLPD_PRIYTPE_FLOAT, stride, bstream + sizeof(SPosition2D<float>)); m_pVideoDriver->DrawElements(EGLPD_RENDERMODE_TRIANGLESTRIP, numVertex); bstream += stride*numVertex; } } m_iNumberOfElement = 0; m_iStreamOffset = 0; } protected: SColor<float> m_Color; int m_iNumberOfElement; int m_iStreamOffset; SGraphics2DGLPipelineVertexData m_pStream[EG2D_PIPELINE_MAX_VERTEX_STACK]; enum EG2DGLPipelineElementType m_Type[EG2D_PIPELINE_MAX_VERTEX_STACK]; };
Dựa trên qui tắt thiết kế ban đầu của IGraphics, các hàm vẽ thực chất là lưu lại thông tin các đỉnh (tọa độ, màu sắc, ...) vào trong một mãng. Hàm Flush sẽ duyệt qua từng phần tử và tiến hành vẽ một lượt
Các điểm đáng lưu ý:
Ví dụ, để add một tam giác:
SGraphics2DGLPipelineVertexData vertex[3] = {
{p1, m_Color, SPosition2D<float>(0, 0)},
{p2, m_Color, SPosition2D<float>(0, 0)},
{p3, m_Color, SPosition2D<float>(0, 0)}
};
Để vẽ các hình:
Trong bài này vẽ các hình bằng các sử dụng gldrawElements. Thông thường, khi mới bắt đầu với OpenGL, ta hay dùng glBegin, glEnd, ... để vẽ. Tuy nhiên, cách này gây chậm, và không tương thích với GLES. Các dùng gldrawElements cho phép tăng tốc thực thi. Để hiểu thêm về cách dùng này, bạn có thể xem thêm ở đây.