/******************************************************************************
@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)
*****************************************************************************/