WiiFlow_Lite/source/gui/video.cpp
fledge68 4a566375e0 -added flash screen image to appear before wiiflow wait animation. i hope the image isn't too big to cause problems with wfl.
-added 'noflash' arg for meta.xml if you don't want the flash screen image to appear. and no i can't make it load a image from a folder. it must be compiled in the code.
-added 'sdonly' arg for meta.xml for those of you that have everything on SD and hate the 20 second wait for wiiflow to search for usb devices. thanks einsteinx2!
-added meta.xml args support. meaning when a homebrew boot.dol or elf is launched the meta.xml (if exist) will be read for arguments and those args sent when the app is launched. this fixes the issue of custom wait images when changing themes.
-added spanish translation file 'spanish.ini'. thanks to ravmn for sending it to me!
-other minor changes, mainly remarks added for myself.
2018-06-27 12:47:03 +00:00

921 lines
29 KiB
C++

#include <string.h>
#include <unistd.h>
#include <malloc.h>
#include <ogc/machine/processor.h>
#include <algorithm>
#include "memory/mem2.hpp"
#include "video.hpp"
#include "pngu.h"
#include "hw/Gekko.h"
#include "WiiMovie.hpp"
#include "gecko/gecko.hpp"
#include "loader/sys.h"
#include "loader/utils.h"
#include "list/ListGenerator.hpp"
#include "text.hpp"
#include "fileOps/fileOps.h"
#define DEFAULT_FIFO_SIZE (256 * 1024)
const float CVideo::_jitter2[2][2] = {
{ 0.246490f, 0.249999f },
{ -0.246490f, -0.249999f }
};
const float CVideo::_jitter3[3][2] = {
{ -0.373411f, -0.250550f },
{ 0.256263f, 0.368119f },
{ 0.117148f, -0.117570f }
};
const float CVideo::_jitter4[4][2] = {
{ -0.208147f, 0.353730f },
{ 0.203849f, -0.353780f },
{ -0.292626f, -0.149945f },
{ 0.296924f, 0.149994f }
};
const float CVideo::_jitter5[5][2] = {
{ 0.5f, 0.5f },
{ 0.3f, 0.1f },
{ 0.7f, 0.9f },
{ 0.9f, 0.3f },
{ 0.1f, 0.7f }
};
const float CVideo::_jitter6[6][2] = {
{ 0.4646464646f, 0.4646464646f },
{ 0.1313131313f, 0.7979797979f },
{ 0.5353535353f, 0.8686868686f },
{ 0.8686868686f, 0.5353535353f },
{ 0.7979797979f, 0.1313131313f },
{ 0.2020202020f, 0.2020202020f }
};
const float CVideo::_jitter8[8][2] = {
{ -0.334818f, 0.435331f },
{ 0.286438f, -0.393495f },
{ 0.459462f, 0.141540f },
{ -0.414498f, -0.192829f },
{ -0.183790f, 0.082102f },
{ -0.079263f, -0.317383f },
{ 0.102254f, 0.299133f },
{ 0.164216f, -0.054399f }
};
GXRModeObj TVPal574IntDfScale =
{
VI_TVMODE_PAL_INT, // viDisplayMode
640, // fbWidth
480, // efbHeight
574, // xfbHeight
(VI_MAX_WIDTH_PAL - 640)/2, // viXOrigin
(VI_MAX_HEIGHT_PAL - 574)/2, // viYOrigin
640, // viWidth
574, // viHeight
VI_XFBMODE_DF, // xFBmode
GX_FALSE, // field_rendering
GX_FALSE, // aa
// sample points arranged in increasing Y order
{
{6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each
{6,6},{6,6},{6,6}, // pix 1
{6,6},{6,6},{6,6}, // pix 2
{6,6},{6,6},{6,6} // pix 3
},
// vertical filter[7], 1/64 units, 6 bits each
{
8, // line n-1
8, // line n-1
10, // line n
12, // line n
10, // line n
8, // line n+1
8 // line n+1
}
};
vector<TexData> m_defaultWaitMessages;
CVideo m_vid;
CVideo::CVideo(void) :
m_rmode(NULL), m_frameBuf(), m_curFB(0), m_fifo(NULL),
m_yScale(0.0f), m_xfbHeight(0), m_wide(false),
m_width2D(640), m_height2D(480), m_x2D(0), m_y2D(0), m_aa(0), m_aaAlpha(false),
m_aaWidth(0), m_aaHeight(0), m_screensaver_alpha(0), m_showWaitMessage(false),
m_WaitThreadRunning(false), m_showingWaitMessages(false)
{
memset(m_frameBuf, 0, sizeof m_frameBuf);
}
void CVideo::init(void)
{
/* General Video Init */
VIDEO_Init();
m_wide = CONF_GetAspectRatio() == CONF_ASPECT_16_9;
m_rmode = VIDEO_GetPreferredMode(NULL);
u32 type = CONF_GetVideo();
m_50hz = false;
if(m_rmode == &TVPal528IntDf)
{
m_rmode= &TVPal574IntDfScale;
m_50hz = true;
}
m_rmode->viWidth = m_wide ? 700 : 672;
//CONF_VIDEO_NTSC and CONF_VIDEO_MPAL and m_rmode TVEurgb60Hz480IntDf are the same max height and width.
if(type == CONF_VIDEO_PAL && m_rmode != &TVEurgb60Hz480IntDf)
{
m_rmode->viHeight = VI_MAX_HEIGHT_PAL;
m_rmode->viXOrigin = (VI_MAX_WIDTH_PAL - m_rmode->viWidth) / 2;
m_rmode->viYOrigin = (VI_MAX_HEIGHT_PAL - m_rmode->viHeight) / 2;
}
else
{
m_rmode->viHeight = VI_MAX_HEIGHT_NTSC;
m_rmode->viXOrigin = (VI_MAX_WIDTH_NTSC - m_rmode->viWidth) / 2;
m_rmode->viYOrigin = (VI_MAX_HEIGHT_NTSC - m_rmode->viHeight) / 2;
}
s8 hoffset = 0; //Use horizontal offset set in wii menu.
if(CONF_GetDisplayOffsetH(&hoffset) == 0)
m_rmode->viXOrigin += hoffset;
/* Widescreen Fix by tueidj, WiiU Check by crediar, thanks alot */
if(m_wide && AHBPROT_Patched() && IsOnWiiU())
{
write32(0xd8006a0, 0x30000004);
mask32(0xd8006a8, 0, 2);
}
/* GX Init */
m_frameBuf[0] = MEM_K0_TO_K1(MEM1_memalign(32, VIDEO_GetFrameBufferSize(m_rmode)));
m_frameBuf[1] = MEM_K0_TO_K1(MEM1_memalign(32, VIDEO_GetFrameBufferSize(m_rmode)));
m_curFB = 0;
m_fifo = MEM1_memalign(32, DEFAULT_FIFO_SIZE);
memset(m_fifo, 0, DEFAULT_FIFO_SIZE);
GX_Init(m_fifo, DEFAULT_FIFO_SIZE);
GX_SetCopyClear(CColor(0), 0x00FFFFFF);
_setViewPort(0, 0, m_rmode->fbWidth, m_rmode->efbHeight);
m_yScale = GX_GetYScaleFactor(m_rmode->efbHeight, m_rmode->xfbHeight);
m_xfbHeight = GX_SetDispCopyYScale(m_yScale);
GX_SetScissor(0, 0, m_rmode->fbWidth, m_rmode->efbHeight);
GX_SetDispCopySrc(0, 0, m_rmode->fbWidth, m_rmode->efbHeight);
GX_SetDispCopyDst(m_rmode->fbWidth, m_xfbHeight);
GX_SetCopyFilter(m_rmode->aa, m_rmode->sample_pattern, GX_TRUE, m_rmode->vfilter);
GX_SetFieldMode(m_rmode->field_rendering, ((m_rmode->viHeight == 2 * m_rmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
GX_SetCullMode(GX_CULL_NONE);
GX_CopyDisp(m_frameBuf[m_curFB], GX_TRUE);
GX_SetDispCopyGamma(GX_GM_1_0);
GX_ClearVtxDesc();
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGB8, 0);
GX_SetNumTexGens(0);
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLOR0A0);
GX_SetNumChans(0);
GX_SetZCompLoc(GX_ENABLE);
setup2DProjection();
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
for(u32 i = 0; i < 8; i++)
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0+i, GX_TEX_ST, GX_F32, 0);
m_stencil = MEM1_memalign(32, CVideo::_stencilWidth * CVideo::_stencilHeight);
memset(m_stencil, 0, CVideo::_stencilWidth * CVideo::_stencilHeight);
/* Configure Video */
VIDEO_Configure(m_rmode);
_clearScreen();
VIDEO_SetBlack(FALSE);
VIDEO_Flush();
VIDEO_WaitVSync();
if(m_rmode->viTVMode & VI_NON_INTERLACE)
VIDEO_WaitVSync();
}
void CVideo::cleanup(void)
{
//gprintf("Cleaning up video...\n");
hideWaitMessage();
_clearScreen();
VIDEO_SetBlack(TRUE);
VIDEO_Flush();
GX_DrawDone();
GX_AbortFrame();
for(u8 i = 0; i < sizeof m_aaBuffer / sizeof m_aaBuffer[0]; ++i)
{
if(m_aaBuffer[i])
free(m_aaBuffer[i]);
m_aaBuffer[i] = NULL;
m_aaBufferSize[i] = 0;
}
for(u8 i = 0; i < m_defaultWaitMessages.size(); i++)
{
if(m_defaultWaitMessages[i].data != NULL)
free(m_defaultWaitMessages[i].data);
m_defaultWaitMessages[i].data = NULL;
}
MEM1_free(MEM_K1_TO_K0(m_frameBuf[0]));
m_frameBuf[0] = NULL;
MEM1_free(MEM_K1_TO_K0(m_frameBuf[1]));
m_frameBuf[1] = NULL;
MEM1_free(m_stencil);
m_stencil = NULL;
MEM1_free(m_fifo);
m_fifo = NULL;
}
void CVideo::_clearScreen()
{
VIDEO_ClearFrameBuffer(m_rmode, m_frameBuf[0], COLOR_BLACK);
VIDEO_ClearFrameBuffer(m_rmode, m_frameBuf[1], COLOR_BLACK);
render();
render();
}
void CVideo::prepare(void)
{
GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
_setViewPort(0.f, 0.f, (float)m_rmode->fbWidth, (float)m_rmode->efbHeight);
GX_SetScissor(0, 0, m_rmode->fbWidth, m_rmode->efbHeight);
GX_InvVtxCache();
GX_InvalidateTexAll();
}
void CVideo::setup2DProjection(bool setViewPort, bool noScale)
{
Mtx44 projMtx;
float width2D = noScale ? 640.f : (float)m_width2D;
float height2D = noScale ? 480.f : (float)m_height2D;
float x = noScale ? 0.f : (float)(640 - width2D) * 0.5f + (float)m_x2D;
float y = noScale ? 0.f : (float)(480 - height2D) * 0.5f + (float)m_y2D;
if (setViewPort)
_setViewPort(0, 0, m_rmode->fbWidth, m_rmode->efbHeight);
guOrtho(projMtx, y, height2D + y, x, width2D + x, 0.f, 1000.0f);
GX_LoadProjectionMtx(projMtx, GX_ORTHOGRAPHIC);
}
void CVideo::_setViewPort(float x, float y, float w, float h)
{
m_vpX = x;
m_vpY = y;
m_vpW = w;
m_vpH = h;
GX_SetViewport(x, y, w, h, 0.f, 1.f);
}
void CVideo::shiftViewPort(float x, float y)
{
GX_SetViewport(m_vpX + x, m_vpY + y, m_vpW, m_vpH, 0.f, 1.f);
}
void CVideo::set2DViewport(u32 w, u32 h, int x, int y)
{
m_width2D = std::min(std::max(512ul, w), 800ul);
m_height2D = std::min(std::max(384ul, h), 600ul);
m_x2D = std::min(std::max(-50, x), 50);
m_y2D = std::min(std::max(-50, y), 50);
}
void CVideo::renderToTexture(TexData &tex, bool clear)
{
if(tex.data == NULL)
{
tex.dataSize = GX_GetTexBufferSize(tex.width, tex.height, tex.format, GX_FALSE, 0);
tex.data = (u8*)MEM2_alloc(tex.dataSize);
if(tex.data == NULL)
return;
}
GX_DrawDone();
GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL);
GX_SetTexCopySrc(0, 0, tex.width, tex.height);
GX_SetTexCopyDst(tex.width, tex.height, tex.format, GX_FALSE);
GX_CopyTex(tex.data, clear ? GX_TRUE : GX_FALSE);
GX_PixModeSync();
GX_SetCopyFilter(m_rmode->aa, m_rmode->sample_pattern, GX_TRUE, m_rmode->vfilter);
DCFlushRange(tex.data, tex.dataSize);
GX_SetScissor(0, 0, m_rmode->fbWidth, m_rmode->efbHeight);
}
void CVideo::prepareAAPass(int aaStep)
{
float x = 0.f;
float y = 0.f;
u32 w = m_aaWidth <= 0 ? m_rmode->fbWidth : (u32)m_aaWidth;
u32 h = m_aaHeight <= 0 ? m_rmode->efbHeight: (u32)m_aaHeight;
switch (m_aa)
{
case 2:
x += CVideo::_jitter2[aaStep][0];
y += CVideo::_jitter2[aaStep][1];
break;
case 3:
x += CVideo::_jitter3[aaStep][0];
y += CVideo::_jitter3[aaStep][1];
break;
case 4:
x += CVideo::_jitter4[aaStep][0];
y += CVideo::_jitter4[aaStep][1];
break;
case 5:
x += CVideo::_jitter5[aaStep][0];
y += CVideo::_jitter5[aaStep][1];
break;
case 6:
x += CVideo::_jitter6[aaStep][0];
y += CVideo::_jitter6[aaStep][1];
break;
case 8:
x += CVideo::_jitter8[aaStep][0];
y += CVideo::_jitter8[aaStep][1];
break;
}
GX_SetPixelFmt(m_aaAlpha ? GX_PF_RGBA6_Z24 : GX_PF_RGB8_Z24, GX_ZC_LINEAR);
_setViewPort(x, y, (float)w, (float)h);
GX_SetScissor(0, 0, w, h);
GX_InvVtxCache();
GX_InvalidateTexAll();
}
void CVideo::drawAAScene(bool fs)
{
GXTexObj texObj[8];
Mtx modelViewMtx;
u8 texFmt = GX_TF_RGBA8;
u32 tw = m_aaWidth <= 0 ? m_rmode->fbWidth : (u32)m_aaWidth;
u32 th = m_aaHeight <= 0 ? m_rmode->efbHeight: (u32)m_aaHeight;
float w = fs ? 640.f : (float)tw;
float h = fs ? 480.f : (float)th;
float x = 0.f;
float y = 0.f;
int aa;
if (m_aa <= 0 || m_aa > 8)
return;
for (aa = 0; aa < m_aa; ++aa)
if (!m_aaBuffer[aa])
break;
if (aa == 7)
aa = 6;
GX_SetNumChans(0);
for (int i = 0; i < aa; ++i)
{
GX_InitTexObj(&texObj[i], m_aaBuffer[i], tw , th, texFmt, GX_CLAMP, GX_CLAMP, GX_FALSE);
GX_LoadTexObj(&texObj[i], GX_TEXMAP0 + i);
}
GX_SetNumTexGens(1);
GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
GX_SetTevKColor(GX_KCOLOR0, CColor(0xFF / 1, 0xFF / 5, 0xFF, 0xFF)); // Renders better gradients than 0xFF / aa
GX_SetTevKColor(GX_KCOLOR1, CColor(0xFF / 2, 0xFF / 6, 0xFF, 0xFF));
GX_SetTevKColor(GX_KCOLOR2, CColor(0xFF / 3, 0xFF / 7, 0xFF, 0xFF));
GX_SetTevKColor(GX_KCOLOR3, CColor(0xFF / 4, 0xFF / 8, 0xFF, 0xFF));
for (int i = 0; i < aa; ++i)
{
GX_SetTevKColorSel(GX_TEVSTAGE0 + i, GX_TEV_KCSEL_K0_R + i);
GX_SetTevKAlphaSel(GX_TEVSTAGE0 + i, GX_TEV_KASEL_K0_R + i);
GX_SetTevOrder(GX_TEVSTAGE0 + i, GX_TEXCOORD0, GX_TEXMAP0 + i, GX_COLORNULL);
GX_SetTevColorIn(GX_TEVSTAGE0 + i, i == 0 ? GX_CC_ZERO : GX_CC_CPREV, GX_CC_TEXC, GX_CC_KONST, GX_CC_ZERO);
GX_SetTevAlphaIn(GX_TEVSTAGE0 + i, i == 0 ? GX_CA_ZERO : GX_CA_APREV, GX_CA_TEXA, GX_CA_KONST, GX_CA_ZERO);
GX_SetTevColorOp(GX_TEVSTAGE0 + i, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GX_SetTevAlphaOp(GX_TEVSTAGE0 + i, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
}
GX_SetNumTevStages(aa);
GX_SetAlphaUpdate(GX_TRUE);
GX_SetCullMode(GX_CULL_NONE);
GX_SetZMode(GX_DISABLE, GX_ALWAYS, GX_FALSE);
GX_SetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
GX_ClearVtxDesc();
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
guMtxIdentity(modelViewMtx);
GX_LoadPosMtxImm(modelViewMtx, GX_PNMTX0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
GX_Position3f32(x, y, 0.f);
GX_TexCoord2f32(0.f, 0.f);
GX_Position3f32(x + w, y, 0.f);
GX_TexCoord2f32(1.f, 0.f);
GX_Position3f32(x + w, y + h, 0.f);
GX_TexCoord2f32(1.f, 1.f);
GX_Position3f32(x, y + h, 0.f);
GX_TexCoord2f32(0.f, 1.f);
GX_End();
GX_SetNumChans(1);
GX_SetNumTexGens(1);
GX_SetNumTevStages(1);
}
void CVideo::renderAAPass(int aaStep)
{
u8 texFmt = GX_TF_RGBA8;
u32 w = m_aaWidth <= 0 ? m_rmode->fbWidth : (u32)m_aaWidth;
u32 h = m_aaHeight <= 0 ? m_rmode->efbHeight: (u32)m_aaHeight;
u32 bufLen = GX_GetTexBufferSize(w, h, texFmt, GX_FALSE, 0);
if (!m_aaBuffer[aaStep] || m_aaBufferSize[aaStep] < bufLen)
{
m_aaBuffer[aaStep] = (u8*)MEM2_alloc(bufLen);
if (m_aaBuffer[aaStep] != NULL)
m_aaBufferSize[aaStep] = bufLen;
}
if (!m_aaBuffer[aaStep] || m_aaBufferSize[aaStep] < bufLen)
return;
GX_SetZMode(GX_DISABLE, GX_ALWAYS, GX_TRUE);
GX_DrawDone();
GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL);
GX_SetTexCopySrc(0, 0, w, h);
GX_SetTexCopyDst(w, h, texFmt, GX_FALSE);
GX_CopyTex(m_aaBuffer[aaStep], GX_TRUE);
GX_PixModeSync();
GX_SetCopyFilter(m_rmode->aa, m_rmode->sample_pattern, GX_TRUE, m_rmode->vfilter);
}
void CVideo::render(void)
{
GX_DrawDone();
GX_SetZMode(GX_DISABLE, GX_ALWAYS, GX_TRUE);
GX_SetColorUpdate(GX_TRUE);
GX_CopyDisp(MEM_K1_TO_K0(m_frameBuf[m_curFB]), GX_TRUE);
DCFlushRange(m_frameBuf[m_curFB], 2 * m_rmode->fbWidth * m_rmode->xfbHeight);
VIDEO_SetNextFramebuffer(m_frameBuf[m_curFB]);
VIDEO_Flush();
VIDEO_WaitVSync();
m_curFB ^= 1;
GX_InvalidateTexAll();
}
/* wait animation control */
extern const u8 splash_jpg[];
extern const u32 splash_jpg_size;
extern const u8 wait_01_jpg[];
extern const u32 wait_01_jpg_size;
extern const u8 wait_02_jpg[];
extern const u32 wait_02_jpg_size;
extern const u8 wait_03_jpg[];
extern const u32 wait_03_jpg_size;
extern const u8 wait_04_jpg[];
extern const u32 wait_04_jpg_size;
extern const u8 wait_05_jpg[];
extern const u32 wait_05_jpg_size;
extern const u8 wait_06_jpg[];
extern const u32 wait_06_jpg_size;
extern const u8 wait_07_jpg[];
extern const u32 wait_07_jpg_size;
extern const u8 wait_08_jpg[];
extern const u32 wait_08_jpg_size;
static lwp_t waitThread = LWP_THREAD_NULL;
u8 CVideo::waitMessageStack[2048] ATTRIBUTE_ALIGN(32);
const u32 CVideo::waitMessageStackSize = 2048;
bool custom = false;
bool waitLoop = false;
static vector<string> waitImgs;
static void GrabWaitFiles(char *FullPath)
{
waitImgs.push_back(FullPath);
}
void CVideo::setCustomWaitImgs(const char *path, bool wait_loop)
{
waitImgs.clear();
if(path != NULL && fsop_FolderExist(path))
{
GetFiles(path, stringToVector(".png|.jpg", '|'), GrabWaitFiles, false, 1);
if(waitImgs.size() > 0)
{
sort(waitImgs.begin(), waitImgs.end());
custom = true;
waitLoop = wait_loop;
}
}
}
void CVideo::waitMessage(float delay)// set wait images from custom or internal
{
if(m_defaultWaitMessages.size() == 0)
{
if(custom)
{
u8 waitImgs_cnt = waitImgs.size();
TexData m_wTextures[waitImgs_cnt];
for(u8 i = 0; i < waitImgs_cnt; i++)
{
TexHandle.fromImageFile(m_wTextures[i], waitImgs[i].c_str());
}
for(u8 i = 0; i < waitImgs_cnt; i++)
m_defaultWaitMessages.push_back(m_wTextures[i]);
}
else
{
TexData m_wTextures[8];
TexHandle.fromJPG(m_wTextures[0], wait_01_jpg, wait_01_jpg_size);
TexHandle.fromJPG(m_wTextures[1], wait_02_jpg, wait_02_jpg_size);
TexHandle.fromJPG(m_wTextures[2], wait_03_jpg, wait_03_jpg_size);
TexHandle.fromJPG(m_wTextures[3], wait_04_jpg, wait_04_jpg_size);
TexHandle.fromJPG(m_wTextures[4], wait_05_jpg, wait_05_jpg_size);
TexHandle.fromJPG(m_wTextures[5], wait_06_jpg, wait_06_jpg_size);
TexHandle.fromJPG(m_wTextures[6], wait_07_jpg, wait_07_jpg_size);
TexHandle.fromJPG(m_wTextures[7], wait_08_jpg, wait_08_jpg_size);
for(int i = 0; i < 8; i++)
m_defaultWaitMessages.push_back(m_wTextures[i]);
}
}
waitMessage(m_defaultWaitMessages, delay);
}
void CVideo::waitMessage(const vector<TexData> &tex, float delay)// start wait images and wii slot light threads
{
hideWaitMessage();
if(tex.size() == 0)
{
m_waitMessages = m_defaultWaitMessages;
m_waitMessageDelay = 0.15f;
}
else
{
m_waitMessages = tex;
m_waitMessageDelay = delay;
}
if(m_waitMessages.size() == 1)
{
waitMessage(m_waitMessages[0]);// draws frame image using function below (for one frame image only) but no render?
render();
}
else if(m_waitMessages.size() > 1)// if more than one frame
{
m_WaitThreadRunning = true;
/* changing light */
wiiLightSetLevel(0);
wiiLightStartThread();// start thread in gekko.c that pulses the wii disc slot light on and off
/* onscreen animation */
m_showWaitMessage = true;// start wait images thread to animate them
LWP_CreateThread(&waitThread, _showWaitMessages, this, waitMessageStack, waitMessageStackSize, LWP_PRIO_HIGHEST);
}
}
void * CVideo::_showWaitMessages(void *obj)// wait images thread
{
CVideo *m = static_cast<CVideo *>(obj);
m->m_showingWaitMessages = true;
u32 frames = m->m_waitMessageDelay * 50;// set delay start
u32 waitFrames = frames;// set delay counter to delay start
s8 fadeDirection = 1;
s8 PNGfadeDirection = 1;// set frames movement direction
s16 currentLightLevel = 0;
vector<TexData>::iterator waitItr = m->m_waitMessages.begin();// set start frame image
m->_clearScreen();
m->prepare();
m->setup2DProjection();
//gprintf("Wait Message Thread: Start\nDelay: %d, Images: %d\n", waitFrames, m->m_waitMessages.size());
while(m->m_showWaitMessage)
{
currentLightLevel += fadeDirection * 5;
if(currentLightLevel >= 255)
{
currentLightLevel = 255;
fadeDirection = -1;
}
else if(currentLightLevel <= 0)
{
currentLightLevel = 0;
fadeDirection = 1;
}
wiiLightSetLevel(currentLightLevel);
if(waitFrames == 0)// if delay count reaches 0
{
m->waitMessage(*waitItr);// draw frame image
waitItr += PNGfadeDirection;// move to next image
if(waitLoop && waitItr == m->m_waitMessages.end())
waitItr = m->m_waitMessages.begin();
else if(!waitLoop && (waitItr + 1 == m->m_waitMessages.end() || waitItr == m->m_waitMessages.begin()))
PNGfadeDirection *= (-1);// change direction if at beginning or end
waitFrames = frames;// reset delay count
m->render();
}
else
VIDEO_WaitVSync();
waitFrames--;// decrement delay count
}
//gprintf("Wait Message Thread: End\n");
m->m_showingWaitMessages = false;
return NULL;
}
void CVideo::hideWaitMessage()// stop wait images thread and wii disc slot light thread
{
m_showWaitMessage = false;
if(waitThread != LWP_THREAD_NULL)
{
/* end animation */
if(LWP_ThreadIsSuspended(waitThread))
LWP_ResumeThread(waitThread);
while(m_showingWaitMessages)
usleep(50);
LWP_JoinThread(waitThread, NULL);
/* end light thread */
wiiLightEndThread();
m_WaitThreadRunning = false;
}
waitThread = LWP_THREAD_NULL;
}
/* draws wait animation image centered on screen */
/* can be used to draw any texture centered on screen */
void CVideo::waitMessage(const TexData &tex)
{
Mtx modelViewMtx;
GXTexObj texObj;
GX_ClearVtxDesc();
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
GX_SetNumTexGens(1);
GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC);
GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA);
GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL);
GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
GX_SetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
GX_SetAlphaUpdate(GX_TRUE);
GX_SetCullMode(GX_CULL_NONE);
GX_SetZMode(GX_DISABLE, GX_ALWAYS, GX_FALSE);
guMtxIdentity(modelViewMtx);
GX_LoadPosMtxImm(modelViewMtx, GX_PNMTX0);
GX_InitTexObj(&texObj, tex.data, tex.width, tex.height, tex.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
GX_LoadTexObj(&texObj, GX_TEXMAP0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
u32 texWidth = m_wide ? tex.width * .75 : tex.width;
GX_Position3f32((float)((640 - texWidth) / 2), (float)((480 - tex.height) / 2), 0.f);
GX_TexCoord2f32(0.f, 0.f);
GX_Position3f32((float)((640 + texWidth) / 2), (float)((480 - tex.height) / 2), 0.f);
GX_TexCoord2f32(1.f, 0.f);
GX_Position3f32((float)((640 + texWidth) / 2), (float)((480 + tex.height) / 2), 0.f);
GX_TexCoord2f32(1.f, 1.f);
GX_Position3f32((float)((640 - texWidth) / 2), (float)((480 + tex.height) / 2), 0.f);
GX_TexCoord2f32(0.f, 1.f);
GX_End();
}
/* draw and render startup splash screen */
void CVideo::startImage(void)
{
TexData splashTex;
TexHandle.fromJPG(splashTex, splash_jpg, splash_jpg_size);
waitMessage(splashTex);
render();
TexHandle.Cleanup(splashTex);
}
/* save screenshot */
s32 CVideo::TakeScreenshot(const char *path)
{
IMGCTX ctx = PNGU_SelectImageFromDevice(path);
s32 ret = PNGU_EncodeFromYCbYCr(ctx, m_rmode->fbWidth, m_rmode->efbHeight, m_frameBuf[m_curFB], 1);
PNGU_ReleaseImageContext (ctx);
return ret;
}
/* used by coverflow to draw cover texture. used in mainloopcommon() in menu.cpp */
/* this draws a texture in the upper left at 0,0 */
void DrawTexture(TexData * &tex)
{
if(tex == NULL)
return;
Mtx modelViewMtx;
GXTexObj texObj;
GX_SetNumChans(1);
GX_ClearVtxDesc();
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0);
GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
GX_SetNumTexGens(1);
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
GX_SetAlphaUpdate(GX_TRUE);
GX_SetCullMode(GX_CULL_NONE);
GX_SetZMode(GX_DISABLE, GX_LEQUAL, GX_TRUE);
guMtxIdentity(modelViewMtx);
GX_LoadPosMtxImm(modelViewMtx, GX_PNMTX0);
GX_InitTexObj(&texObj, tex->data, tex->width, tex->height, tex->format, GX_CLAMP, GX_CLAMP, GX_FALSE);
GX_LoadTexObj(&texObj, GX_TEXMAP0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
GX_Position2f32(0.f, 0.f);
GX_Color4u8(0xFF, 0xFF, 0xFF, 0xFF);
GX_TexCoord2f32(0.f, 0.f);
GX_Position2f32(tex->width, 0.f);
GX_Color4u8(0xFF, 0xFF, 0xFF, 0xFF);
GX_TexCoord2f32(1.f, 0.f);
GX_Position2f32(tex->width, tex->height);
GX_Color4u8(0xFF, 0xFF, 0xFF, 0xFF);
GX_TexCoord2f32(1.f, 1.f);
GX_Position2f32(0.f, tex->height);
GX_Color4u8(0xFF, 0xFF, 0xFF, 0xFF);
GX_TexCoord2f32(0.f, 1.f);
GX_End();
}
/* draws movie frame texture when playing movie in menu_game.cpp */
struct movieP normalMoviePos = { 410, 31, 610, 181 };
struct movieP zoomedMoviePos = { 0, 0, 640, 480 };
struct movieP currentMoviePos = zoomedMoviePos;
/* draws a texture/image at a position and width and height specified by struct movieP currentMoviePos */
/* set currentMoviePos before calling this. example - {x, y, x+w, y+h} */
void DrawTexturePos(const TexData *tex)
{
Mtx modelViewMtx;
GXTexObj texObj;
GX_ClearVtxDesc();
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0);
GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
GX_SetNumTexGens(1);
GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC);
GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL);
GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
GX_SetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
GX_SetAlphaUpdate(GX_FALSE);
GX_SetCullMode(GX_CULL_NONE);
GX_SetZMode(GX_DISABLE, GX_ALWAYS, GX_FALSE);
guMtxIdentity(modelViewMtx);
GX_LoadPosMtxImm(modelViewMtx, GX_PNMTX0);
GX_InitTexObj(&texObj, tex->data, tex->width, tex->height, tex->format, GX_CLAMP, GX_CLAMP, GX_FALSE);
GX_LoadTexObj(&texObj, GX_TEXMAP0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
GX_Position2f32(currentMoviePos.x1, currentMoviePos.y1);
GX_TexCoord2f32(0.f, 0.f);
GX_Position2f32(currentMoviePos.x2, currentMoviePos.y1);
GX_TexCoord2f32(1.f, 0.f);
GX_Position2f32(currentMoviePos.x2, currentMoviePos.y2);
GX_TexCoord2f32(1.f, 1.f);
GX_Position2f32(currentMoviePos.x1, currentMoviePos.y2);
GX_TexCoord2f32(0.f, 1.f);
GX_End();
}
/* screen saver and full banner frame */
void DrawRectangle(f32 x, f32 y, f32 width, f32 height, GXColor color)
{
Mtx modelViewMtx;
guMtxIdentity(modelViewMtx);
GX_LoadPosMtxImm(modelViewMtx, GX_PNMTX0);
GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
GX_ClearVtxDesc();
GX_InvVtxCache();
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
GX_SetVtxDesc(GX_VA_TEX0, GX_NONE);
int i;
f32 x2 = x + width;
f32 y2 = y + height;
guVector v[] = { { x, y, 0.0f }, { x2, y, 0.0f }, { x2, y2, 0.0f }, { x, y2, 0.0f }, { x, y, 0.0f } };
GX_Begin(GX_TRIANGLEFAN, GX_VTXFMT0, 4);
for(i = 0; i < 4; i++)
{
GX_Position3f32(v[i].x, v[i].y, v[i].z);
GX_Color4u8(color.r, color.g, color.b, color.a);
}
GX_End();
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
}
void CVideo::screensaver(u32 no_input, u32 max_no_input)
{
if(no_input == 0)// no idle time so reset alpha to 0 for next time and don't draw the rectangle on this pass
{
m_screensaver_alpha = 0;
return;
}
if(no_input > max_no_input)// if idle time > max idle draw rectangle full screen increasing alpha each time
{
DrawRectangle(0, 0, 640, 480, (GXColor){0,0,0,m_screensaver_alpha});
if(m_screensaver_alpha < 150)
m_screensaver_alpha+=2;
}
}
/* misc settings */
void CColor::blend(const CColor &src)
{
if (src.a == 0) return;
r = (u8)(((int)src.r * (int)src.a + (int)r * (0xFF - (int)src.a)) / 0xFF);
g = (u8)(((int)src.g * (int)src.a + (int)g * (0xFF - (int)src.a)) / 0xFF);
b = (u8)(((int)src.b * (int)src.a + (int)b * (0xFF - (int)src.a)) / 0xFF);
}
CColor CColor::interpolate(const CColor &c1, const CColor &c2, u8 n)
{
CColor c;
c.r = (u8)(((int)c2.r * (int)n + (int)c1.r * (0xFF - (int)n)) / 0xFF);
c.g = (u8)(((int)c2.g * (int)n + (int)c1.g * (0xFF - (int)n)) / 0xFF);
c.b = (u8)(((int)c2.b * (int)n + (int)c1.b * (0xFF - (int)n)) / 0xFF);
c.a = (u8)(((int)c2.a * (int)n + (int)c1.a * (0xFF - (int)n)) / 0xFF);
return c;
}
void CVideo::setAA(u8 aa, bool alpha, int width, int height)
{
if (aa <= 8 && aa != 7 && width <= m_rmode->fbWidth && height <= m_rmode->efbHeight && ((width | height) & 3) == 0)
{
m_aa = aa;
m_aaAlpha = alpha;
m_aaWidth = width;
m_aaHeight = height;
}
}
/* stencil used by coverflow */
const int CVideo::_stencilWidth = 128;
const int CVideo::_stencilHeight = 128;
static inline u32 coordsI8(u32 x, u32 y, u32 w)// used by stencilval below
{
return (((y >> 2) * (w >> 3) + (x >> 3)) << 5) + ((y & 3) << 3) + (x & 7);
}
int CVideo::stencilVal(int x, int y)
{
if ((u32)x >= m_rmode->fbWidth || (u32)y >= m_rmode->efbHeight)
return 0;
x = x * CVideo::_stencilWidth / 640;
y = y * CVideo::_stencilHeight / 480;
u32 i = coordsI8(x, y, (u32)CVideo::_stencilWidth);
if (i >= (u32)(CVideo::_stencilWidth * CVideo::_stencilHeight))
return 0;
return ((u8*)m_stencil)[i];
}
void CVideo::prepareStencil(void)
{
GX_SetPixelFmt(GX_PF_Y8, GX_ZC_LINEAR);
_setViewPort(0.f, 0.f, (float)CVideo::_stencilWidth, (float)CVideo::_stencilHeight);
GX_SetScissor(0, 0, CVideo::_stencilWidth, CVideo::_stencilHeight);
GX_InvVtxCache();
GX_InvalidateTexAll();
}
void CVideo::renderStencil(void)
{
GX_DrawDone();
GX_SetZMode(GX_DISABLE, GX_ALWAYS, GX_TRUE);
GX_SetColorUpdate(GX_TRUE);
GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL);
GX_SetTexCopySrc(0, 0, CVideo::_stencilWidth, CVideo::_stencilHeight);
GX_SetTexCopyDst(CVideo::_stencilWidth, CVideo::_stencilHeight, GX_CTF_R8, GX_FALSE);
GX_CopyTex(m_stencil, GX_TRUE);
GX_PixModeSync();
DCFlushRange(m_stencil, CVideo::_stencilWidth * CVideo::_stencilHeight);
GX_SetCopyFilter(m_rmode->aa, m_rmode->sample_pattern, GX_TRUE, m_rmode->vfilter);
}