/****************************************************************************** @File OGLES2/PVRTPrint3DAPI.cpp @Title OGLES2/PVRTPrint3DAPI @Version @Copyright Copyright (c) Imagination Technologies Limited. @Platform ANSI compatible @Description Displays a text string using 3D polygons. Can be done in two ways: using a window defined by the user or writing straight on the screen. ******************************************************************************/ /**************************************************************************** ** Includes ****************************************************************************/ #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "PVRTContext.h" #include "PVRTFixedPoint.h" #include "PVRTMatrix.h" #include "PVRTTexture.h" #include "PVRTTextureAPI.h" #include "PVRTPrint3D.h" #include "PVRTString.h" #include "PVRTShader.h" #include "PVRTMap.h" #include "PVRTPrint3DShaders.h" /**************************************************************************** ** Defines ****************************************************************************/ #define VERTEX_ARRAY 0 #define UV_ARRAY 1 #define COLOR_ARRAY 2 #define INIT_PRINT3D_STATE 0 #define DEINIT_PRINT3D_STATE 1 #define UNDEFINED_HANDLE 0xFAFAFAFA const GLenum c_eMagTable[] = { GL_NEAREST, GL_LINEAR, }; const GLenum c_eMinTable[] = { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST, GL_LINEAR, }; /**************************************************************************** ** Enums ****************************************************************************/ enum eFunction { eFunc_DelProg, eFunc_DelShader, eFunc_DelTex }; /**************************************************************************** ** Auxiliary functions ****************************************************************************/ static void DeleteResource(eFunction eType, GLuint& handle) { if(handle == UNDEFINED_HANDLE) return; switch(eType) { case eFunc_DelProg: glDeleteProgram(handle); break; case eFunc_DelShader: glDeleteShader(handle); break; case eFunc_DelTex: glDeleteTextures(1, &handle); break; } handle = UNDEFINED_HANDLE; } /**************************************************************************** ** Structures ****************************************************************************/ struct SPVRTPrint3DAPI { GLuint m_uTextureFont; static int s_iRefCount; struct SInstanceData { GLuint uTextureIMGLogo; GLuint uTexturePowerVRLogo; GLuint uVertexShaderLogo; GLuint uFragmentShaderLogo; GLuint uProgramLogo; GLint mvpLocationLogo; GLuint uVertexShaderFont; GLuint uFragmentShaderFont; GLuint uProgramFont; GLint mvpLocationFont; SInstanceData() : uTextureIMGLogo(UNDEFINED_HANDLE), uTexturePowerVRLogo(UNDEFINED_HANDLE), uVertexShaderLogo(UNDEFINED_HANDLE), uFragmentShaderLogo(UNDEFINED_HANDLE), uProgramLogo(UNDEFINED_HANDLE), mvpLocationLogo(-1), uVertexShaderFont(UNDEFINED_HANDLE), uFragmentShaderFont(UNDEFINED_HANDLE), uProgramFont(UNDEFINED_HANDLE), mvpLocationFont(-1) { } void Release() { DeleteResource(eFunc_DelProg, uProgramLogo); DeleteResource(eFunc_DelShader, uFragmentShaderLogo); DeleteResource(eFunc_DelShader, uVertexShaderLogo); DeleteResource(eFunc_DelProg, uProgramLogo); DeleteResource(eFunc_DelShader, uFragmentShaderLogo); DeleteResource(eFunc_DelShader, uVertexShaderLogo); DeleteResource(eFunc_DelTex, uTextureIMGLogo); DeleteResource(eFunc_DelTex, uTexturePowerVRLogo); } }; // Optional per-instance data SInstanceData* m_pInstanceData; // Shared data across all Print3D instances static SInstanceData s_InstanceData; // Used to save the OpenGL state to restore them after drawing */ GLboolean isCullFaceEnabled; GLboolean isBlendEnabled; GLboolean isDepthTestEnabled; GLint nArrayBufferBinding; GLint nCurrentProgram; GLint nTextureBinding2D; GLint eFrontFace; GLint eCullFaceMode; SPVRTPrint3DAPI() : m_pInstanceData(NULL) {} ~SPVRTPrint3DAPI() { if(m_pInstanceData) { delete m_pInstanceData; m_pInstanceData = NULL; } } }; int SPVRTPrint3DAPI::s_iRefCount = 0; SPVRTPrint3DAPI::SInstanceData SPVRTPrint3DAPI::s_InstanceData; /**************************************************************************** ** Class: CPVRTPrint3D ****************************************************************************/ /*!*************************************************************************** @Function ReleaseTextures @Description Deallocate the memory allocated in SetTextures(...) *****************************************************************************/ void CPVRTPrint3D::ReleaseTextures() { #if !defined (DISABLE_PRINT3D) if(m_pAPI) { // Has local copy if(m_pAPI->m_pInstanceData) { m_pAPI->m_pInstanceData->Release(); } else { if(SPVRTPrint3DAPI::s_iRefCount != 0) { // Just decrease the reference count --SPVRTPrint3DAPI::s_iRefCount; } else { m_pAPI->s_InstanceData.Release(); } } } // Only release textures if they've been allocated if (!m_bTexturesSet) return; // Release IndexBuffer FREE(m_pwFacesFont); FREE(m_pPrint3dVtx); // Delete textures glDeleteTextures(1, &m_pAPI->m_uTextureFont); m_bTexturesSet = false; FREE(m_pVtxCache); APIRelease(); #endif } /*!*************************************************************************** @Function Flush @Description Flushes all the print text commands *****************************************************************************/ int CPVRTPrint3D::Flush() { #if !defined (DISABLE_PRINT3D) int nTris, nVtx, nVtxBase, nTrisTot = 0; _ASSERT((m_nVtxCache % 4) == 0); _ASSERT(m_nVtxCache <= m_nVtxCacheMax); // Save render states APIRenderStates(INIT_PRINT3D_STATE); // Draw font if(m_nVtxCache) { SPVRTPrint3DAPI::SInstanceData& Data = (m_pAPI->m_pInstanceData ? *m_pAPI->m_pInstanceData : SPVRTPrint3DAPI::s_InstanceData); float fW = m_fScreenScale[0] * 640.0f; float fH = m_fScreenScale[1] * 480.0f; PVRTMat4 mxOrtho = PVRTMat4::Ortho(0.0f, 0.0f, fW, -fH, -1.0f, 1.0f, PVRTMat4::OGL, m_bRotate); if(m_bRotate) { PVRTMat4 mxTrans = PVRTMat4::Translation(-fH,fW,0.0f); mxOrtho = mxOrtho * mxTrans; } // Use the shader _ASSERT(Data.uProgramFont != UNDEFINED_HANDLE); glUseProgram(Data.uProgramFont); // Bind the projection and modelview matrices to the shader PVRTMat4& mProj = (m_bUsingProjection ? m_mProj : mxOrtho); PVRTMat4 mMVP = mProj * m_mModelView; glUniformMatrix4fv(Data.mvpLocationFont, 1, GL_FALSE, mMVP.f); // Reset m_bUsingProjection = false; PVRTMatrixIdentity(m_mModelView); // Set client states glEnableVertexAttribArray(VERTEX_ARRAY); glEnableVertexAttribArray(COLOR_ARRAY); glEnableVertexAttribArray(UV_ARRAY); // texture glBindTexture(GL_TEXTURE_2D, m_pAPI->m_uTextureFont); unsigned int uiIndex = m_eFilterMethod[eFilterProc_Min] + (m_eFilterMethod[eFilterProc_Mip]*2); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, c_eMagTable[m_eFilterMethod[eFilterProc_Mag]]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c_eMinTable[uiIndex]); nTrisTot = m_nVtxCache >> 1; // Render the text then. Might need several submissions. nVtxBase = 0; while(m_nVtxCache) { nVtx = PVRT_MIN(m_nVtxCache, 0xFFFC); nTris = nVtx >> 1; _ASSERT(nTris <= (PVRTPRINT3D_MAX_RENDERABLE_LETTERS*2)); _ASSERT((nVtx % 4) == 0); // Draw triangles glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(SPVRTPrint3DAPIVertex), (const void*)&m_pVtxCache[nVtxBase].sx); glVertexAttribPointer(COLOR_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SPVRTPrint3DAPIVertex), (const void*)&m_pVtxCache[nVtxBase].color); glVertexAttribPointer(UV_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(SPVRTPrint3DAPIVertex), (const void*)&m_pVtxCache[nVtxBase].tu); glDrawElements(GL_TRIANGLES, nTris * 3, GL_UNSIGNED_SHORT, m_pwFacesFont); if(glGetError()) { PVRTERROR_OUTPUT_DEBUG("glDrawElements(GL_TRIANGLES, (VertexCount/2)*3, GL_UNSIGNED_SHORT, m_pFacesFont); failed\n"); } nVtxBase += nVtx; m_nVtxCache -= nVtx; } // Restore render states glDisableVertexAttribArray(VERTEX_ARRAY); glDisableVertexAttribArray(COLOR_ARRAY); glDisableVertexAttribArray(UV_ARRAY); } // Draw a logo if requested #if !defined(FORCE_NO_LOGO) // User selected logos if(m_uLogoToDisplay & ePVRTPrint3DLogoPowerVR && m_uLogoToDisplay & ePVRTPrint3DLogoIMG) { APIDrawLogo(ePVRTPrint3DLogoIMG, eBottom | eRight); // IMG to the right APIDrawLogo(ePVRTPrint3DLogoPowerVR, eBottom | eLeft); // PVR to the left } else if(m_uLogoToDisplay & ePVRTPrint3DLogoPowerVR) { APIDrawLogo(ePVRTPrint3DLogoPowerVR, eBottom | eRight); // logo to the right } else if(m_uLogoToDisplay & ePVRTPrint3DLogoIMG) { APIDrawLogo(ePVRTPrint3DLogoIMG, eBottom | eRight); // logo to the right } #endif // Restore render states APIRenderStates(DEINIT_PRINT3D_STATE); return nTrisTot; #else return 0; #endif } /************************************************************* * PRIVATE FUNCTIONS * **************************************************************/ /*!*************************************************************************** @Function APIInit @Description Initialisation and texture upload. Should be called only once for a given context. *****************************************************************************/ bool CPVRTPrint3D::APIInit(const SPVRTContext * const pContext, bool bMakeCopy) { PVRT_UNREFERENCED_PARAMETER(pContext); m_pAPI = new SPVRTPrint3DAPI; if(!m_pAPI) return false; if(bMakeCopy) m_pAPI->m_pInstanceData = new SPVRTPrint3DAPI::SInstanceData(); SPVRTPrint3DAPI::SInstanceData& Data = (m_pAPI->m_pInstanceData ? *m_pAPI->m_pInstanceData : SPVRTPrint3DAPI::s_InstanceData); // Check to see if these shaders have already been loaded previously. Optimisation as we don't want to load many copies of the same shader! if( Data.uFragmentShaderLogo != UNDEFINED_HANDLE && Data.uVertexShaderLogo != UNDEFINED_HANDLE && Data.uProgramLogo != UNDEFINED_HANDLE && Data.uFragmentShaderFont != UNDEFINED_HANDLE && Data.uVertexShaderFont != UNDEFINED_HANDLE && Data.uProgramFont != UNDEFINED_HANDLE ) { ++SPVRTPrint3DAPI::s_iRefCount; return true; } // Compiles the shaders. For a more detailed explanation, see IntroducingPVRTools CPVRTString error; GLint Linked; bool bRes = true; bRes &= (PVRTShaderLoadSourceFromMemory(_Print3DFragShaderLogo_fsh, GL_FRAGMENT_SHADER, &Data.uFragmentShaderLogo, &error) == PVR_SUCCESS); bRes &= (PVRTShaderLoadSourceFromMemory(_Print3DVertShaderLogo_vsh, GL_VERTEX_SHADER, &Data.uVertexShaderLogo, &error) == PVR_SUCCESS); _ASSERT(bRes); // Create the 'text' program Data.uProgramLogo = glCreateProgram(); glAttachShader(Data.uProgramLogo, Data.uVertexShaderLogo); glAttachShader(Data.uProgramLogo, Data.uFragmentShaderLogo); glBindAttribLocation(Data.uProgramLogo, VERTEX_ARRAY, "myVertex"); glBindAttribLocation(Data.uProgramLogo, UV_ARRAY, "myUV"); glLinkProgram(Data.uProgramLogo); glGetProgramiv(Data.uProgramLogo, GL_LINK_STATUS, &Linked); if (!Linked) bRes = false; bRes &= (PVRTShaderLoadSourceFromMemory(_Print3DFragShader_fsh, GL_FRAGMENT_SHADER, &Data.uFragmentShaderFont, &error) == PVR_SUCCESS); bRes &= (PVRTShaderLoadSourceFromMemory(_Print3DVertShader_vsh, GL_VERTEX_SHADER, &Data.uVertexShaderFont, &error) == PVR_SUCCESS); _ASSERT(bRes); // Create the 'text' program Data.uProgramFont = glCreateProgram(); glAttachShader(Data.uProgramFont, Data.uVertexShaderFont); glAttachShader(Data.uProgramFont, Data.uFragmentShaderFont); glBindAttribLocation(Data.uProgramFont, VERTEX_ARRAY, "myVertex"); glBindAttribLocation(Data.uProgramFont, UV_ARRAY, "myUV"); glBindAttribLocation(Data.uProgramFont, COLOR_ARRAY, "myColour"); glLinkProgram(Data.uProgramFont); glGetProgramiv(Data.uProgramFont, GL_LINK_STATUS, &Linked); if (!Linked) bRes = false; Data.mvpLocationLogo = glGetUniformLocation(Data.uProgramFont, "myMVPMatrix"); Data.mvpLocationFont = glGetUniformLocation(Data.uProgramLogo, "myMVPMatrix"); _ASSERT(bRes && Data.mvpLocationLogo != -1 && Data.mvpLocationFont != -1); return bRes; } /*!*************************************************************************** @Function APIRelease @Description Deinitialisation. *****************************************************************************/ void CPVRTPrint3D::APIRelease() { delete m_pAPI; m_pAPI = 0; } /*!*************************************************************************** @Function APIUpLoadIcons @Description Initialisation and texture upload. Should be called only once for a given context. *****************************************************************************/ bool CPVRTPrint3D::APIUpLoadIcons(const PVRTuint8 * const pIMG, const PVRTuint8 * const pPowerVR) { SPVRTPrint3DAPI::SInstanceData& Data = (m_pAPI->m_pInstanceData ? *m_pAPI->m_pInstanceData : SPVRTPrint3DAPI::s_InstanceData); // Load Icon texture if(Data.uTextureIMGLogo == UNDEFINED_HANDLE) // Static, so might already be initialized. if(PVRTTextureLoadFromPointer((unsigned char*)pIMG, &Data.uTextureIMGLogo) != PVR_SUCCESS) return false; if(Data.uTexturePowerVRLogo == UNDEFINED_HANDLE) // Static, so might already be initialized. if(PVRTTextureLoadFromPointer((unsigned char*)pPowerVR, &Data.uTexturePowerVRLogo) != PVR_SUCCESS) return false; glBindTexture(GL_TEXTURE_2D, 0); return true; } /*!*************************************************************************** @Function APIUpLoadTexture @Input pSource @Output header @Return bool true if successful. @Description Loads and uploads the font texture from a PVR file. *****************************************************************************/ bool CPVRTPrint3D::APIUpLoadTexture(const PVRTuint8* pSource, const PVRTextureHeaderV3* header, CPVRTMap<PVRTuint32, CPVRTMap<PVRTuint32, MetaDataBlock> >& MetaDataMap) { if(PVRTTextureLoadFromPointer(pSource, &m_pAPI->m_uTextureFont, header, true, 0U, NULL, &MetaDataMap) != PVR_SUCCESS) return false; glBindTexture(GL_TEXTURE_2D, 0); return true; } /*!*************************************************************************** @Function APIRenderStates @Description Stores, writes and restores Render States *****************************************************************************/ void CPVRTPrint3D::APIRenderStates(int nAction) { // Saving or restoring states ? switch (nAction) { case INIT_PRINT3D_STATE: { // Get previous render states m_pAPI->isCullFaceEnabled = glIsEnabled(GL_CULL_FACE); m_pAPI->isBlendEnabled = glIsEnabled(GL_BLEND); m_pAPI->isDepthTestEnabled = glIsEnabled(GL_DEPTH_TEST); glGetIntegerv(GL_FRONT_FACE, &m_pAPI->eFrontFace); glGetIntegerv(GL_CULL_FACE_MODE, &m_pAPI->eCullFaceMode); glGetIntegerv(GL_ARRAY_BUFFER_BINDING,&m_pAPI->nArrayBufferBinding); glGetIntegerv(GL_CURRENT_PROGRAM, &m_pAPI->nCurrentProgram); glGetIntegerv(GL_TEXTURE_BINDING_2D, &m_pAPI->nTextureBinding2D); /****************************** ** SET PRINT3D RENDER STATES ** ******************************/ // Culling glFrontFace(GL_CCW); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); // Set blending mode glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Set Z compare properties glDisable(GL_DEPTH_TEST); // Set the default GL_ARRAY_BUFFER glBindBuffer(GL_ARRAY_BUFFER, 0); // texture glActiveTexture(GL_TEXTURE0); break; } case DEINIT_PRINT3D_STATE: // Restore some values if (!m_pAPI->isCullFaceEnabled) glDisable(GL_CULL_FACE); if (!m_pAPI->isBlendEnabled) glDisable(GL_BLEND); if (m_pAPI->isDepthTestEnabled) glEnable(GL_DEPTH_TEST); glCullFace((GLenum)m_pAPI->eCullFaceMode); glFrontFace((GLenum)m_pAPI->eFrontFace); glBindBuffer(GL_ARRAY_BUFFER,m_pAPI->nArrayBufferBinding); glBindTexture(GL_TEXTURE_2D, m_pAPI->nTextureBinding2D); glUseProgram(m_pAPI->nCurrentProgram); // Unset print3ds program break; } } /**************************************************************************** ** Local code ****************************************************************************/ /*!*************************************************************************** @Function APIDrawLogo @Description *****************************************************************************/ void CPVRTPrint3D::APIDrawLogo(const EPVRTPrint3DLogo uLogoToDisplay, const int ePos) { GLuint tex = 0; float fScale = 1.0f; if(m_ui32ScreenDim[1] >= 720) fScale = 2.0f; SPVRTPrint3DAPI::SInstanceData& Data = (m_pAPI->m_pInstanceData ? *m_pAPI->m_pInstanceData : SPVRTPrint3DAPI::s_InstanceData); switch(uLogoToDisplay) { case ePVRTPrint3DLogoIMG: tex = Data.uTextureIMGLogo; break; case ePVRTPrint3DLogoPowerVR: tex = Data.uTexturePowerVRLogo; break; default: return; // Logo not recognised } const float fLogoXSizeHalf = (128.0f / m_ui32ScreenDim[0]); const float fLogoYSizeHalf = (64.0f / m_ui32ScreenDim[1]); const float fLogoXShift = 0.035f / fScale; const float fLogoYShift = 0.035f / fScale; const float fLogoSizeXHalfShifted = fLogoXSizeHalf + fLogoXShift; const float fLogoSizeYHalfShifted = fLogoYSizeHalf + fLogoYShift; static float Vertices[] = { -fLogoXSizeHalf, fLogoYSizeHalf , 0.5f, -fLogoXSizeHalf, -fLogoYSizeHalf, 0.5f, fLogoXSizeHalf , fLogoYSizeHalf , 0.5f, fLogoXSizeHalf , -fLogoYSizeHalf, 0.5f }; static float UVs[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f }; float *pVertices = ( (float*)&Vertices ); float *pUV = ( (float*)&UVs ); // Matrices PVRTMATRIX matModelView; PVRTMATRIX matTransform; PVRTMatrixIdentity(matModelView); PVRTMatrixScaling(matTransform, f2vt(fScale), f2vt(fScale), f2vt(1.0f)); PVRTMatrixMultiply(matModelView, matModelView, matTransform); int nXPos = (ePos & eLeft) ? -1 : 1; int nYPos = (ePos & eTop) ? 1 : -1; PVRTMatrixTranslation(matTransform, nXPos - (fLogoSizeXHalfShifted * fScale * nXPos), nYPos - (fLogoSizeYHalfShifted * fScale * nYPos), 0.0f); PVRTMatrixMultiply(matModelView, matModelView, matTransform); if(m_bRotate) { PVRTMatrixRotationZ(matTransform, -90.0f*PVRT_PI/180.0f); PVRTMatrixMultiply(matModelView, matModelView, matTransform); } _ASSERT(Data.uProgramLogo != UNDEFINED_HANDLE); glUseProgram(Data.uProgramLogo); // Bind the model-view-projection to the shader glUniformMatrix4fv(Data.mvpLocationLogo, 1, GL_FALSE, matModelView.f); // Render states glActiveTexture(GL_TEXTURE0); _ASSERT(tex != UNDEFINED_HANDLE); glBindTexture(GL_TEXTURE_2D, tex); // Vertices glEnableVertexAttribArray(VERTEX_ARRAY); glEnableVertexAttribArray(UV_ARRAY); glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, (const void*)pVertices); glVertexAttribPointer(UV_ARRAY, 2, GL_FLOAT, GL_FALSE, 0, (const void*)pUV); glDrawArrays(GL_TRIANGLE_STRIP,0,4); glDisableVertexAttribArray(VERTEX_ARRAY); glDisableVertexAttribArray(UV_ARRAY); } /***************************************************************************** End of file (PVRTPrint3DAPI.cpp) *****************************************************************************/