/*---------------------------------------------------------------------------* Copyright 2010-2012 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. *---------------------------------------------------------------------------*/ ////=========================================================================== /// demoFont.c /// /// This is font code for the demo library. /// ////=========================================================================== #include #include #include #include // Font Data #include //--------------------------------------------------------------------------- // Shader Data //--------------------------------------------------------------------------- static const char * const FONT_GSH_SHADER_FILE = "shaders/lib_demo/cafe/demoFont.gsh"; // -------------------------------------------------------------------------- // Macro definitions // -------------------------------------------------------------------------- // Max Buffer Size for vsnprintf_s #define FONT_MAX_BUFSIZ 512 // Simple structure that contains all the data // we need to know about characters in the font texture typedef struct { u32 id; s32 minS; s32 minT; s32 maxS; s32 maxT; s32 minX; s32 minY; s32 maxX; s32 maxY; s32 xAdvance; } CharContainer_t; typedef struct { // Contains all the data we need to render each character CharContainer_t* pCharDataBuffer; // Font texture height f32 fontTextureWidth; // Font texture width f32 fontTextureHeight; // The number of characters stored in the raw char data u32 numCharData; // The offset height of grid f32 gridOffsetY; // The offset width of grid f32 gridOffsetX; // Pointer to Font Image Data u8* pFontImageData; // --- GPU MEM --- // includes pointer to buffer containing the Texture image Data GX2Texture TexData; } FontData_t; // DEMO Font Context State static GX2ContextState *DEMOFontContextState; // P: Proportional, F: Fixed mono_space static FontData_t s_FontP, s_FontF; static GX2Sampler s_sampler = {0}; static DEMOGfxShader s_Shader = {0}; static BOOL s_enabled=TRUE; // Max number of unique characters defined in each font #define MAX_NUM_CHARS 100 static CharContainer_t s_CharDataBufferP[MAX_NUM_CHARS]; static CharContainer_t s_CharDataBufferF[MAX_NUM_CHARS]; // Instance static DEMOFontInstance* gDemoFontCurInstance = NULL; //-------------------------------------------------------------------------- // Forward references //-------------------------------------------------------------------------- static FontData_t* GetCurrentFont(void); static void UpdateScale(DEMOFontFontData* pFont, f32 scaleX, f32 scaleY); static void SetFontTexture(DEMOGfxShader* pShader, FontData_t* pFont); static void GetFontCharData(FontData_t* pFont, const u32* pFontHeader, const u32* pCharData, const u8* pFontImageData); static void InitFontTexture(DEMOGfxShader* pShader, FontData_t* pFont); static void InitShader(DEMOGfxShader* pShader); static u32 BSearchIndex(FontData_t* pFont, u32 id); // -------------------------------------------------------------------------- // Init // -------------------------------------------------------------------------- DEMOFontInstance* DEMOFontInit() { // Get Font char data GetFontCharData(&s_FontF, s_FontHeaderF, (u32*)s_CharDataF, s_FontImageDataF); GetFontCharData(&s_FontP, s_FontHeaderP, (u32*)s_CharDataP, s_FontImageDataP); // Initialise Shader InitShader(&s_Shader); // Initialize Font Texture InitFontTexture(&s_Shader, &s_FontP); InitFontTexture(&s_Shader, &s_FontF); DEMOFontAddInstance(); // Restore state DEMOGfxSetContextState(); return gDemoFontCurInstance; } // -------------------------------------------------------------------------- // ShutDown // -------------------------------------------------------------------------- void DEMOFontShutdown(void) { // Delete the instance DEMOFontDeleteInstance(gDemoFontCurInstance); // Free shaders DEMOGfxFreeShaders(&s_Shader); // Free texture if (s_FontP.TexData.surface.imagePtr) { DEMOGfxFreeMEM2(s_FontP.TexData.surface.imagePtr); } if (s_FontF.TexData.surface.imagePtr) { DEMOGfxFreeMEM2(s_FontF.TexData.surface.imagePtr); } } // -------------------------------------------------------------------------- // Context State // -------------------------------------------------------------------------- void DEMOFontSetContextState(void) { GX2UTDebugTagIndent("DEMOFontSetContextState()"); GX2SetContextState(DEMOFontContextState); GX2UTDebugTagUndent(); } // -------------------------------------------------------------------------- // Enable // -------------------------------------------------------------------------- void DEMOFontDrawEnable(BOOL enable) { s_enabled = enable; // MUST verify the instance exists before setting it! if (gDemoFontCurInstance) gDemoFontCurInstance->enabled = enable; } // -------------------------------------------------------------------------- // Printf // -------------------------------------------------------------------------- void DEMOFontPrintf(f32 column, f32 line, const char* pFmt, ... ) { char str[FONT_MAX_BUFSIZ]; va_list args; s32 stringSize; // Don't draw if fonts are disabled if (!gDemoFontCurInstance->enabled) return; // Get string va_start(args, pFmt); stringSize = vsnprintf( str, FONT_MAX_BUFSIZ, pFmt, args ); // Assert for over string size if ( stringSize < 0 ) { DEMOAssert(!"String is too long\n"); } va_end(args); DEMOFontPuts( column, line, str ); } void DEMOFontPuts(f32 column, f32 line, const char* pStr) { u32 stringLength; s32 offsetX; s32 offsetY; FontData_t* pFont = GetCurrentFont(); DEMOFontFontData* pFontData = &gDemoFontCurInstance->font; f32 basePos[3]; u32 advanceX = 0; u32 indices[FONT_MAX_BUFSIZ*4]; u32 indCount; u32 cIn = 0; // Don't draw if fonts are disabled if (!gDemoFontCurInstance->enabled) return; // Check the initialize DEMOAssert(pStr && "Need to initialize pStr.\n"); DEMOAssert(pFont->pCharDataBuffer && "Need to call DEMOFontInit(). Before this function.\n"); stringLength = (u32)(strlen((const char *)pStr)); // Calc offsets offsetX = (s32)(column * pFont->gridOffsetX); offsetY = -(s32)((line + 1) * pFont->gridOffsetY); // Set up rendering state SetFontTexture(&s_Shader, pFont); // Update scale GX2SetVertexUniformReg(s_Shader.uniformsVS.location[1], 1*4, &pFontData->charMagScale[0]); // Update color uniform GX2SetPixelUniformReg(s_Shader.uniformsPS.location[0], 1*4, &pFontData->color); basePos[2] = pFontData->depth; while (cIn < stringLength) { basePos[0] = (f32) offsetX; basePos[1] = (f32) offsetY; indCount = 0; for(; cIn < stringLength; cIn++) { u32 id = (u32) pStr[cIn]; u32 index = 0; u32 px1, px2, py1, py2; u32 ps1, ps2, pt1, pt2; if(id == 10) { advanceX = 0; offsetY -= pFont->gridOffsetY; cIn++; break; } else if(id >= 32 && id <= 127) { // Get index of character id index = BSearchIndex(pFont,id); } px1 = (u32)(pFont->pCharDataBuffer[index].minX + advanceX); px2 = (u32)(pFont->pCharDataBuffer[index].maxX + advanceX); py1 = (u32)(pFont->pCharDataBuffer[index].minY); py2 = (u32)(pFont->pCharDataBuffer[index].maxY); // If we exceed the bitfield for advanceX, reset base if (px2 > 0xfff) { offsetX += advanceX; advanceX = 0; break; } ps1 = (u32)pFont->pCharDataBuffer[index].minS; ps2 = (u32)pFont->pCharDataBuffer[index].maxS; pt1 = (u32)pFont->pCharDataBuffer[index].minT; pt2 = (u32)pFont->pCharDataBuffer[index].maxT; #define CHECK(px, py, tx, ty) \ DEMOAssert(!((px)&~0xfff)&&!((py)&~0x1f)&&!((tx)&~0xff)&&!((ty)&~0x7f)) #define PACK(px, py, tx, ty) (((px)<<20)|((py)<<15)|((tx)<<7)|(ty)) CHECK(px1, py1, ps1, pt1); CHECK(px2, py2, ps2, pt2); indices[indCount++] = PACK(px1, py1, ps1, pt1); indices[indCount++] = PACK(px1, py2, ps1, pt2); indices[indCount++] = PACK(px2, py2, ps2, pt2); indices[indCount++] = PACK(px2, py1, ps2, pt1); advanceX += pFont->pCharDataBuffer[index].xAdvance; } // Skip blank lines if (indCount == 0) continue; // Set base position GX2SetVertexUniformReg(s_Shader.uniformsVS.location[0], 1*4, &basePos); // Draw the characters GX2DrawIndexedImmediate(GX2_PRIMITIVE_QUADS, indCount, GX2_INDEX_FORMAT_U32, indices); } } // -------------------------------------------------------------------------- // Update // -------------------------------------------------------------------------- void DEMOFontSetSpacing(BOOL proportional) { // Check the initialize DEMOAssert(s_FontP.pCharDataBuffer && "Need to call DEMOFontInit(). Before this function.\n"); // Update proportional boolean gDemoFontCurInstance->proportional = proportional; // Switch which scale we use if (proportional) gDemoFontCurInstance->font.charMagScale = gDemoFontCurInstance->font.charMagScaleP; else gDemoFontCurInstance->font.charMagScale = gDemoFontCurInstance->font.charMagScaleF; } void DEMOFontSetGridSize(f32 xGrid, f32 yGrid) { // Check the initialize DEMOAssert(s_FontP.pCharDataBuffer && "Need to call DEMOFontInit(). Before this function.\n"); // Update scale UpdateScale(&gDemoFontCurInstance->font, xGrid, yGrid); } void DEMOFontSetColor(f32 r, f32 g, f32 b, f32 a) { // Check the initialize DEMOAssert(s_FontP.pCharDataBuffer && "Need to call DEMOFontInit(). Before this function.\n"); // Update color gDemoFontCurInstance->font.color[0] = r; gDemoFontCurInstance->font.color[1] = g; gDemoFontCurInstance->font.color[2] = b; gDemoFontCurInstance->font.color[3] = a; } void DEMOFontSetZValue(f32 zValue) { // Check the initialize DEMOAssert(s_FontP.pCharDataBuffer && "Need to call DEMOFontInit(). Before this function.\n"); // Update depth value gDemoFontCurInstance->font.depth = zValue; } // -------------------------------------------------------------------------- // Getter // -------------------------------------------------------------------------- void DEMOFontGetCharSize(f32 *pCharWidth, f32 *pCharHeight) { FontData_t* pFont = GetCurrentFont(); // Get size of font *pCharWidth = pFont->gridOffsetX; *pCharHeight = pFont->gridOffsetY; } // -------------------------------------------------------------------------- // Private Functions // -------------------------------------------------------------------------- /// Check Proportional boolean and return Font handle static FontData_t* GetCurrentFont(void) { if(gDemoFontCurInstance->proportional) { return &s_FontP; } else { return &s_FontF; } } /// Update scale static void UpdateScale(DEMOFontFontData* pFont, f32 scaleX, f32 scaleY) { // Calculate char scale pFont->charMagScaleF[0] = 2.0f/(scaleX * s_FontF.gridOffsetX); pFont->charMagScaleF[1] = 2.0f/(scaleY * s_FontF.gridOffsetY); pFont->charMagScaleP[0] = 2.0f/(scaleX * s_FontP.gridOffsetX); pFont->charMagScaleP[1] = 2.0f/(scaleY * s_FontP.gridOffsetY); } /// Get Font char data from demoFontData.h static void GetFontCharData(FontData_t* pFont, const u32* pFontHeader, const u32* pCharData, const u8* pFontImageData) { u32 i; f32 lineHeight; f32 maxCharWidth = 0.0f; f32 maxCharHeight = 0.0f; // Check Font Texture Data DEMOAssert(pFontHeader != NULL && pCharData != NULL && pFontImageData != NULL && "No texture data.\n"); // Check char data buffer if (pFont->pCharDataBuffer) { // Skip Data Initialization return; } // Set Font Texture Data Information pFont->fontTextureWidth = (f32)pFontHeader[0]; pFont->fontTextureHeight = (f32)pFontHeader[1]; lineHeight = (f32)pFontHeader[4]; pFont->numCharData = pFontHeader[5]; pFont->gridOffsetX = 0; pFont->gridOffsetY = 0; if(pFont == &s_FontP) { pFont->pCharDataBuffer = s_CharDataBufferP; } else { pFont->pCharDataBuffer = s_CharDataBufferF; } // Check the max number DEMOAssert(pFont->numCharData <= MAX_NUM_CHARS && "Font has over the max number of characters.\n"); // Format of data is: id, x, y, width, height, xOffset, yOffset, xAdvance for(i = 0; i < pFont->numCharData; ++i) { u32 id = pCharData[i*8 + 0]; u32 x = pCharData[i*8 + 1]; u32 y = pCharData[i*8 + 2]; u32 w = pCharData[i*8 + 3]; u32 h = pCharData[i*8 + 4]; // +3 below is a hack to deal with negative offsets s32 xOffset = (s32)pCharData[i*8 + 5] + 3; s32 yOffset = (s32)pCharData[i*8 + 6]; u32 xAdvance = pCharData[i*8 + 7]; f32 charHeight = (f32)(h + yOffset); pFont->pCharDataBuffer[i].id = id; pFont->pCharDataBuffer[i].minS = (s32)x; pFont->pCharDataBuffer[i].minT = (s32)(pFont->fontTextureHeight - h - y - 1); pFont->pCharDataBuffer[i].maxS = (s32)(x + w); pFont->pCharDataBuffer[i].maxT = (s32)(pFont->fontTextureHeight - y - 1); pFont->pCharDataBuffer[i].minX = (s32)xOffset; pFont->pCharDataBuffer[i].minY = (s32)(lineHeight - yOffset - h); pFont->pCharDataBuffer[i].maxX = (s32)(xOffset + w); pFont->pCharDataBuffer[i].maxY = (s32)(lineHeight - yOffset); pFont->pCharDataBuffer[i].xAdvance = (s32)xAdvance; // Set max height of char in GL cordinate space if(charHeight > maxCharHeight) { maxCharHeight = charHeight; } if(pFont->pCharDataBuffer[i].xAdvance > maxCharWidth) { maxCharWidth = (f32)pFont->pCharDataBuffer[i].xAdvance; } } // Set grid offsetX pFont->gridOffsetX = maxCharWidth; // Set grid offsetY pFont->gridOffsetY = maxCharHeight; // Set pointer of Font Image Data pFont->pFontImageData = (u8*)pFontImageData; } /// Init Font Texture static void InitFontTexture(DEMOGfxShader* pShader, FontData_t* pFont) { // Texture setup u32 width =(u32)pFont->fontTextureWidth; u32 height=(u32)pFont->fontTextureHeight; GX2InitTexture(&pFont->TexData, width, // width height, // height 1, // depth 0, // num mips GX2_SURFACE_FORMAT_TC_R8_UNORM, GX2_SURFACE_DIM_2D); // Allocate from graphics memory GX2InitTexturePtrs(&pFont->TexData, DEMOGfxAllocMEM2(pFont->TexData.surface.imageSize, pFont->TexData.surface.alignment), 0); // add dummy alloc notify because invalidate inside GX2TileTexture creates orphaned upload otherwise GX2NotifyMemAlloc(pFont->pFontImageData, width*height, 32); // Then we retile the image // This function calls Invalidate for us GX2TileTexture(&pFont->TexData, // dst pFont->pFontImageData); // src GX2NotifyMemFree(pFont->pFontImageData); // Set up the sampler object GX2InitSampler(&s_sampler, GX2_TEX_CLAMP_WRAP, GX2_TEX_XY_FILTER_BILINEAR); } // Set Font Texture static void SetFontTexture(DEMOGfxShader* pShader, FontData_t* pFont) { // Set Texture GX2SetPixelTexture(&pFont->TexData, pShader->samplersPS.location[0]); } /// Initialize Font shader static void InitShader(DEMOGfxShader* pShader) { void * gshBuf; u32 gshLen; // Load shader binary to memory gshBuf = DEMOGfxLoadAssetFile(FONT_GSH_SHADER_FILE, &gshLen); // Load demoFont shader DEMOGfxLoadShaders(pShader, 0, gshBuf); GX2RSetBufferName(&pShader->pVertexShader->shaderProgram, "demoFont VS"); GX2RSetBufferName(&pShader->pPixelShader->shaderProgram, "demoFont PS"); // Free memory used to load the shader file DEMOFree(gshBuf); // Init attribute to shader DEMOGfxGetVertexShaderUniformLocation(pShader, "u_BasePosition"); DEMOGfxGetVertexShaderUniformLocation(pShader, "u_Scale"); DEMOGfxGetPixelShaderUniformLocation(pShader, "u_Color"); DEMOGfxGetPixelShaderSamplerLocation(pShader, "u_CharTex"); // Set fetch Shader buffer DEMOGfxInitFetchShader(pShader); } // Search index of character id static u32 BSearchIndex(FontData_t* pFont, u32 id) { u32 first; u32 last; u32 index = 0; // quick check for sanely-ordered fonts if (id >= 32 && id < 32+pFont->numCharData && pFont->pCharDataBuffer[id-32].id == id ) return id-32; first = 0; last = pFont->numCharData-1; while(first <= last) { u32 mid = first + (last - first) / 2; if( pFont->pCharDataBuffer[mid].id < id ) { first = mid + 1; } else if( id < pFont->pCharDataBuffer[mid].id ) { last = mid - 1; } else { index = mid; break; } } return index; } DEMOFontInstance* DEMOFontAddInstance(void) { // Setup Context State buffer DEMOFontContextState = DEMOGfxAllocMEM2(sizeof(GX2ContextState), GX2_CONTEXT_STATE_ALIGNMENT); // Create a state context with profiling disabled // GX2SetupContextState will invalidate CPU cache for us GX2SetupContextStateEx(DEMOFontContextState, GX2_DISABLE); // Setup default render buffers to be used now DEMOFontSetTarget(&DEMOColorBuffer); // Set Depth Control GX2SetDepthOnlyControl(GX2_DISABLE, GX2_DISABLE, GX2_COMPARE_LESS); // Set Blend Enable GX2SetColorControl(GX2_LOGIC_OP_COPY, GX2_ENABLE, GX2_DISABLE, GX2_ENABLE); // Set Blend Function & Combine Mode GX2SetBlendControl(GX2_RENDER_TARGET_0, GX2_BLEND_SRC_ALPHA, GX2_BLEND_ONE_MINUS_SRC_ALPHA, GX2_BLEND_COMBINE_ADD, GX2_ENABLE, GX2_BLEND_SRC_ALPHA, GX2_BLEND_ONE_MINUS_SRC_ALPHA, GX2_BLEND_COMBINE_ADD ); ///////////////////////////////////////////////////////////////////////////// // Create the new instance gDemoFontCurInstance = DEMOAlloc(sizeof(DEMOFontInstance)); memset(gDemoFontCurInstance, 0, sizeof(DEMOFontInstance)); gDemoFontCurInstance->contextState = DEMOFontContextState; ///////////////////////////////////////////////////////////////////////////// // Set shaders GX2SetShaders(&s_Shader.fetchShader, s_Shader.pVertexShader, s_Shader.pPixelShader); // Set up sampler GX2SetPixelSampler(&s_sampler, s_Shader.samplersPS.location[0]); // Initialize Proportional Font DEMOFontSetSpacing(1); DEMOFontSetGridSize(60, 24); DEMOFontSetColor(1.0f, 1.0f, 1.0f, 1.0f); DEMOFontSetZValue(-1.0f); gDemoFontCurInstance->enabled = s_enabled; s_enabled = TRUE; return gDemoFontCurInstance; } void DEMOFontDeleteInstance(DEMOFontInstance *instance) { DEMOGfxFreeMEM2(instance->contextState); DEMOFree(instance); // Edge case: delete current if (gDemoFontCurInstance == instance) { gDemoFontCurInstance = NULL; DEMOFontContextState = NULL; } } void DEMOFontSetInstance(DEMOFontInstance *instance) { gDemoFontCurInstance = instance; DEMOFontContextState = instance->contextState; GX2SetContextState(DEMOFontContextState); } DEMOFontInstance* DEMOFontGetInstance(void) { return gDemoFontCurInstance; }