Để vẽ ảnh bằng thư viện sẵn có, ta tiến hành một số thay đổi sau:
IVideoDriver
#ifndef __CIVIDEODRIVER_H__ #define __CIVIDEODRIVER_H__ #include "Header.h" #include "CSingleton.h" namespace GameTutor { enum EVideoDriver { EVIDEO_DRIVER_OGLPIPELINE, }; class IVideoDriver { public: IVideoDriver(EVideoDriver driver) { m_CIVideoDriver = driver; } bool IsVideoDriver(EVideoDriver driver) {return m_CIVideoDriver == driver;} virtual void PrintInfomation() = 0; virtual __UINT32 GetError() = 0; protected: EVideoDriver m_CIVideoDriver; }; } #endif
Bản thân lớp CGLPipelineDriver cũng là một Video Driver, do đó sẽ nhận lớp IVideoDriver làm lớp cha
CGLPipelineDriver
class CGLPipelineDriver:public IVideoDriver, public CSingleton<CGLPipelineDriver> { friend class CSingleton<CGLPipelineDriver>; protected: CGLPipelineDriver():m_CurrentMatrixMode(EGLPD_MATRIXMODE_PROJECTION), m_isUseAlpha(false), m_isUse2DTexture(false), IVideoDriver(EVIDEO_DRIVER_OGLPIPELINE) { ............. } ............ }
Đồng thời cung cấp một số hàm làm việc với texture2D
CGLPipelineDriver - Texture
//----------------------------------------------- // Texture //----------------------------------------------- void EnableTexture2D(bool val) { if (val != m_isUse2DTexture) { m_isUse2DTexture = val; if (m_isUse2DTexture) { glEnable (GL_TEXTURE_2D); } else { glDisable(GL_TEXTURE_2D); } } } __UINT32 AddTexure2D(int level, EGLPipelineDriverPixelFormat format, int width, int height, int border, __UINT8 *pixel) { EnableTexture2D(true); __UINT32 texname = 0; glGenTextures(1, &texname); glBindTexture(GL_TEXTURE_2D, texname); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//GL_LINEAR glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//GL_LINEAR glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexImage2D(GL_TEXTURE_2D, level, format, width, height, border, format, GL_UNSIGNED_BYTE, pixel); return texname; } void BindTexture2D(__UINT32 texname) { glBindTexture(GL_TEXTURE_2D, texname); } void FreeTexture2D(__UINT32 texname) { glDeleteTextures(1, &texname ); }
Nói thêm về khái niệm texture2D của OpenGL. Hiện tại, bạn có thể xem texture2D như một tấm hình bình thường. Để tìm hiểu thêm về khái niệm texture, bạn có thể xem ở đây.
Do sự thay đổi từ bước 1, ta có thể đưa ra một số hiệu chỉnh tinh giảm cho bước 3:
CGraphics2D
#ifndef __CGRAPHICS2D_H__ #define __CGRAPHICS2D_H__ #include "Header.h" #include "CSingleton.h" #include "CGLPipelineDriver.h" #include "SGraphics.h" #include "CGLPipelineDriver.h" #include "CImage.h" #define EG2D_PIPELINE_MAX_VERTEX_STACK 1000 namespace GameTutor { enum EG2DGLPipelineElementType { EG2DP_ELETYPE_LINE, EG2DP_ELETYPE_TRIANGLE, EG2DP_ELETYPE_RECT, EG2DP_ELETYPE_IMG, }; struct SGraphics2DGLPipelineVertexData { SPosition2D Pos; SColor Color; SPosition2D TexCoor; }; class CGraphics2D:public CSingleton { friend class CSingleton; protected: CGraphics2D() { m_pVideoDriver = CGLPipelineDriver::GetInstance(); m_iNumberOfElement = 0; m_iStreamOffset = 0; m_Color = SColor(0.0, 0.0, 0.0, 1.0); } IVideoDriver *m_pVideoDriver; public: void SetVideoDriver(IVideoDriver *vd) { m_pVideoDriver = vd; } void Reset() { if (m_pVideoDriver->IsVideoDriver(EVIDEO_DRIVER_OGLPIPELINE)) { CGLPipelineDriver* vd = (CGLPipelineDriver*)m_pVideoDriver; __UINT32 w = (CViewController::GetInstance()->GetView()->GetWidth()); __UINT32 h = (CViewController::GetInstance()->GetView()->GetHeight()); Clear(SColor(0, 0, 0, 1)); vd->Viewport(SRect<__INT32>(0, 0, w, h)); vd->LoadIdentity(EGLPD_MATRIXMODE_PROJECTION); vd->Ortho(0, float(w), 0, float(h) , 0, 1000 ); vd->EnableAttribPointer(EGLPD_ATTRIB_VERTEX, false); vd->EnableAttribPointer(EGLPD_ATTRIB_COLOR, false); vd->EnableAttribPointer(EGLPD_ATTRIB_NORMAL, false); vd->EnableAttribPointer(EGLPD_ATTRIB_TEXCOOR, false); } } void Clear(SColorc) { if (m_pVideoDriver->IsVideoDriver(EVIDEO_DRIVER_OGLPIPELINE)) { CGLPipelineDriver* vd = (CGLPipelineDriver*)m_pVideoDriver; m_iNumberOfElement = 0; m_iStreamOffset = 0; vd->Clear(c); } } void Clear(SColor<__UINT8> c) { if (m_pVideoDriver->IsVideoDriver(EVIDEO_DRIVER_OGLPIPELINE)) { CGLPipelineDriver* vd = (CGLPipelineDriver*)m_pVideoDriver; vd->Clear(SColor(c.Red/255.0f, c.Green/255.0f, c.Blue/255.0f, c.Alpha/255.0f)); } } void SetColor(SColor 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 rect) { TODO("DrawRect is not implemented"); } void DrawRect(SRect<__INT32> rect) { TODO("DrawRect is not implemented"); } void DrawLine(SPosition2D startpoint, SPosition2D endpoint) { TODO("DrawLine is not implemented"); } void DrawLine(SPosition2D<__INT32> startpoint, SPosition2D<__INT32> endpoint) { TODO("DrawLine is not implemented"); } void DrawTriangle(SPosition2D p1, SPosition2Dp2, SPosition2Dp3) { TODO("DrawTriangle is not implemented"); } void DrawTriangle(SPosition2D<__INT32> p1, SPosition2D<__INT32> p2, SPosition2D<__INT32> p3) { TODO("DrawTriangle is not implemented"); } void FillRect(SRect rect) { m_Type[m_iNumberOfElement] = EG2DP_ELETYPE_RECT; SGraphics2DGLPipelineVertexData vertex[4] = { {SPosition2D(rect.X, rect.Y + rect.H), m_Color, SPosition2D(0, 0)}, {SPosition2D(rect.X, rect.Y), m_Color, SPosition2D(0, 0)}, {SPosition2D(rect.X + rect.W, rect.Y + rect.H), m_Color, SPosition2D(0, 0)}, {SPosition2D(rect.X + rect.W, rect.Y), m_Color, SPosition2D(0, 0)} }; memcpy(m_pStream + m_iStreamOffset, vertex, sizeof(SGraphics2DGLPipelineVertexData)*4); m_iNumberOfElement++; m_iStreamOffset += 4; }#ifndef __CGRAPHICS2D_H__
Điểm đáng lưu ý nhất trong CGraphics là việc thêm hàm void DrawImage. Về bản chất, hàm DrawImage khá giống vớiFillRect, chỉ có khác là có chỉ định textureID và textcoord (xem thêm texture opengl để có thêm thông tin). Đồng thời, hàm Flush cũng có thay đổi để đáp ứng cho việc vẽ texture
Ở lớp CImage, ta cần các phương thức liên kết giữa CImage với texure của OpenGL. Để làm được việc này:
CImage
//CImage.h #ifndef __CIMAGE_H__ #define __CIMAGE_H__ #include "Header.h" #include "SGraphics.h" #include "IVideoDriver.h" namespace GameTutor { enum EImagePixelFormat { EIMAGE_FORMAT_R8G8B8, EIMAGE_FORMAT_R8G8B8A8, }; class CImage { public: CImage(int width, int height, EImagePixelFormat format); virtual ~CImage(); __UINT32 GetDataSize() { return GetPixelSize()*m_iWidth*m_iHeight; } __UINT32 GetPixelSize() { switch (m_eFormat) { case EIMAGE_FORMAT_R8G8B8: return 3; case EIMAGE_FORMAT_R8G8B8A8: return 4; default: return 0; } } void GetPixel(__UINT32 row, __UINT32 col, SColor<__UINT8> &c); void SetPixel(__UINT32 row, __UINT32 col, SColor<__UINT8> c); void ClearColor(SColor<__UINT8> c); __UINT32 GetTextureID() {return m_iTextureID;} __UINT32 GetWidth() {return this->m_iWidth;} __UINT32 GetHeight() {return this->m_iHeight;} EImagePixelFormat GetPixelFormat() {return m_eFormat;} public: void BindGPU(IVideoDriver *driver); void UnbindGPU(IVideoDriver *driver); protected: __UINT32 m_iWidth; __UINT32 m_iHeight; EImagePixelFormat m_eFormat; __UINT8 *m_pData; __UINT32 m_iTextureID; }; } #endif //CImage.cpp #include "CImage.h" #include "CGLPipelineDriver.h" namespace GameTutor { CImage::CImage(int width, int height, EImagePixelFormat format ):m_iWidth(width), m_iHeight(height), m_eFormat(format), m_pData(0), m_iTextureID(0) { int dataSize = GetDataSize(); m_pData = new __UINT8[dataSize]; } CImage::~CImage() { SAFE_DEL(m_pData); } void CImage::GetPixel(__UINT32 row, __UINT32 col, SColor<__UINT8> &c) { int offset = 0; if (row >= 0 && row < m_iHeight && col >= 0 && col c) { int offset = 0; if (row >= 0 && row < m_iHeight && col >= 0 && col c) { int dataSize = GetDataSize(); switch (m_eFormat) { case EIMAGE_FORMAT_R8G8B8: for (int i = 0; i < dataSize; i+=3) { memcpy(m_pData + i, &c, 3); } break; case EIMAGE_FORMAT_R8G8B8A8: for (int i = 0; i < dataSize; i+=4) { memcpy(m_pData + i, &c, 4); } break; } } void CImage::BindGPU(IVideoDriver *driver) { if (driver->IsVideoDriver(EVIDEO_DRIVER_OGLPIPELINE)) { CGLPipelineDriver *glplDriver = (CGLPipelineDriver *)driver; if (m_iTextureID > 0) { this->UnbindGPU(driver); } switch (m_eFormat) { case EIMAGE_FORMAT_R8G8B8: this->m_iTextureID = glplDriver->AddTexure2D(0, EGLPD_PIXFMT_R8G8B8, this->m_iWidth, this->m_iHeight, 0, this->m_pData); break; case EIMAGE_FORMAT_R8G8B8A8: this->m_iTextureID = glplDriver->AddTexure2D(0, EGLPD_PIXFMT_R8G8B8A8, this->m_iWidth, this->m_iHeight, 0, this->m_pData); break; default: break; } } } void CImage::UnbindGPU(IVideoDriver *driver) { if (driver->IsVideoDriver(EVIDEO_DRIVER_OGLPIPELINE)) { CGLPipelineDriver *glplDriver = (CGLPipelineDriver *)driver; if (m_iTextureID > 0) { glplDriver->FreeTexture2D(this->m_iTextureID); } } } }
Bằng việc thay đổi một ít ở state CStatePoster, ta có thêm demo cho việc vẽ hình.
CStatePoster
//CStatePoster .h #ifndef __CSTATEPOSTER_H__ #define __CSTATEPOSTER_H__ #include "gametutor.h" class CStatePoster :public CState { public: CStatePoster(); ~CStatePoster() {} void Init(); void Update(); void Render(); void Exit(); private: __INT32 m_iCount; CImage *m_img; CGraphics2D *g; IVideoDriver *m_pVideoDriver; }; #endif //CStatePoster.cpp #include "CStatePoster.h" CStatePoster::CStatePoster(): m_iCount(0), CState() { } void CStatePoster::Init() { m_pVideoDriver = CGLPipelineDriver::GetInstance(); Log("State Poster: Init"); m_iCount = 10; g = CGraphics2D::GetInstance(); g->Reset(); m_img = new CImage(100, 100, EIMAGE_FORMAT_R8G8B8A8); m_img->ClearColor(SColor<__UINT8>(255, 0, 255, 127)); m_img->BindGPU(m_pVideoDriver); } void CStatePoster::Update() { m_iCount--; } void CStatePoster::Render() { g->Clear(SColor<float>(0, 0, 0, 1)); g->SetColor(SColor<float>(1, 0, 0, 1)); g->FillRect(SRect<float>(10, 10, 100, 100)); g->SetColor(SColor<float>(1, 1, 0, 1)); g->FillRect(SRect<float>(100, 100, 100, 100)); g->SetColor(SColor<float>(0.0f, 1.0f, 1.0f, 0.8f)); g->FillTriangle(SPosition2D<float>(150, 150), SPosition2D<float>(150, 100), SPosition2D<float>(300, 100)); g->DrawImage(SPosition2D<float>(0, 0), m_img); g->Flush(); } void CStatePoster::Exit() { Log("State Poster: Exit"); }
Download source
View Diagram
#define __CGRAPHICS2D_H__ #include "Header.h" #include "CSingleton.h" #include "CGLPipelineDriver.h" #include "SGraphics.h" #include "CGLPipelineDriver.h" #include "CImage.h" #define EG2D_PIPELINE_MAX_VERTEX_STACK 1000 namespace GameTutor { enum EG2DGLPipelineElementType { EG2DP_ELETYPE_LINE, EG2DP_ELETYPE_TRIANGLE, EG2DP_ELETYPE_RECT, EG2DP_ELETYPE_IMG, }; struct SGraphics2DGLPipelineVertexData { SPosition2D<float> Pos; SColor<float> Color; SPosition2D<float> TexCoor; }; class CGraphics2D:public CSingleton<CGraphics2D> { friend class CSingleton<CGraphics2D>; protected: CGraphics2D() { m_pVideoDriver = CGLPipelineDriver::GetInstance(); m_iNumberOfElement = 0; m_iStreamOffset = 0; m_Color = SColor<float>(0.0, 0.0, 0.0, 1.0); } IVideoDriver *m_pVideoDriver; public: void SetVideoDriver(IVideoDriver *vd) { m_pVideoDriver = vd; } void Reset() { if (m_pVideoDriver->IsVideoDriver(EVIDEO_DRIVER_OGLPIPELINE)) { CGLPipelineDriver* vd = (CGLPipelineDriver*)m_pVideoDriver; __UINT32 w = (CViewController<CVSView>::GetInstance()->GetView()->GetWidth()); __UINT32 h = (CViewController<CVSView>::GetInstance()->GetView()->GetHeight()); Clear(SColor<float>(0, 0, 0, 1)); vd->Viewport(SRect<__INT32>(0, 0, w, h)); vd->LoadIdentity(EGLPD_MATRIXMODE_PROJECTION); vd->Ortho(0, float(w), 0, float(h) , 0, 1000 ); vd->EnableAttribPointer(EGLPD_ATTRIB_VERTEX, false); vd->EnableAttribPointer(EGLPD_ATTRIB_COLOR, false); vd->EnableAttribPointer(EGLPD_ATTRIB_NORMAL, false); vd->EnableAttribPointer(EGLPD_ATTRIB_TEXCOOR, false); } } void Clear(SColor<float>c) { if (m_pVideoDriver->IsVideoDriver(EVIDEO_DRIVER_OGLPIPELINE)) { CGLPipelineDriver* vd = (CGLPipelineDriver*)m_pVideoDriver; m_iNumberOfElement = 0; m_iStreamOffset = 0; vd->Clear(c); } } void Clear(SColor<__UINT8> c) { if (m_pVideoDriver->IsVideoDriver(EVIDEO_DRIVER_OGLPIPELINE)) { CGLPipelineDriver* vd = (CGLPipelineDriver*)m_pVideoDriver; vd->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 DrawImage(SPosition2D<float> pos, CImage *img) { m_Type[m_iNumberOfElement] = EG2DP_ELETYPE_IMG; SRect<float> rect(pos.X, pos.Y, float(img->GetWidth()), float(img->GetHeight())); 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>(1, 0)}, {SPosition2D<float>(rect.X + rect.W, rect.Y + rect.H), m_Color, SPosition2D<float>(0, 1)}, {SPosition2D<float>(rect.X + rect.W, rect.Y), m_Color, SPosition2D<float>(1, 1)} }; m_pTextureID[m_iNumberOfElement] = img->GetTextureID(); m_pIsUseAlpha[m_iNumberOfElement] = (img->GetPixelFormat() == EIMAGE_FORMAT_R8G8B8A8); memcpy(m_pStream + m_iStreamOffset, vertex, sizeof(SGraphics2DGLPipelineVertexData)*4); m_iNumberOfElement++; m_iStreamOffset += 4; } void DrawImage(SPosition2D<__INT32> pos, CImage *img) { SPosition2D<float> pos2(float(pos.X), float(pos.Y)); DrawImage(pos2, img); } void Flush() { if (m_pVideoDriver->IsVideoDriver(EVIDEO_DRIVER_OGLPIPELINE)) { CGLPipelineDriver* vd = (CGLPipelineDriver*)m_pVideoDriver; __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: case EG2DP_ELETYPE_TRIANGLE: m_pIsUseAlpha[i] = (m_Color.Alpha != 1); vd->EnableTexture2D(false); vd->EnableAttribPointer(EGLPD_ATTRIB_VERTEX, true); vd->EnableAttribPointer(EGLPD_ATTRIB_NORMAL, false); vd->EnableAttribPointer(EGLPD_ATTRIB_COLOR, true); vd->EnableAttribPointer(EGLPD_ATTRIB_TEXCOOR, false); numVertex = 4; if (m_Type[i] == EG2DP_ELETYPE_TRIANGLE) { numVertex = 3; } vd->AttributePointer( EGLPD_ATTRIB_COLOR, 4, EGLPD_PRIYTPE_FLOAT, stride, bstream + sizeof(SPosition2D<float>)); break; case EG2DP_ELETYPE_IMG: vd->EnableTexture2D(true); vd->EnableAttribPointer(EGLPD_ATTRIB_VERTEX, true); vd->EnableAttribPointer(EGLPD_ATTRIB_NORMAL, false); vd->EnableAttribPointer(EGLPD_ATTRIB_COLOR, false); vd->EnableAttribPointer(EGLPD_ATTRIB_TEXCOOR, true); vd->BindTexture2D(m_pTextureID[i]); vd->AttributePointer( EGLPD_ATTRIB_TEXCOOR, 2, EGLPD_PRIYTPE_FLOAT, stride, bstream + sizeof(SPosition2D<float>) + sizeof(SColor<float>)); numVertex = 4; break; } if (numVertex) { vd->EnableBlending(m_pIsUseAlpha[i]); vd->AttributePointer( EGLPD_ATTRIB_VERTEX, 2, EGLPD_PRIYTPE_FLOAT, stride, bstream); vd->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]; __UINT32 m_pTextureID[EG2D_PIPELINE_MAX_VERTEX_STACK]; bool m_pIsUseAlpha[EG2D_PIPELINE_MAX_VERTEX_STACK]; enum EG2DGLPipelineElementType m_Type[EG2D_PIPELINE_MAX_VERTEX_STACK]; }; } #endif