WiiFlow_Lite/source/banner/Material.cpp

592 lines
15 KiB
C++
Raw Normal View History

/*
Copyright (c) 2010 - Wii Banner Player Project
Copyright (c) 2012 - Dimok and giantpune
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include <algorithm>
#include <math.h>
#include "Layout.h"
#include "Material.h"
Material::Material()
: flags(0)
, texture_maps(0)
, texture_srts(0)
, texture_coord_gens(0)
, chan_control(0)
, mat_color(0)
, tev_swap_table(0)
, ind_srt(0)
, ind_stage(0)
, tev_stages(0)
, alpha_compare(0)
, blend_mode(0)
, header(0)
{
for( int i = 0; i < 8; i++ )
palette_texture[i] = DEFAULT_PALETTE;
}
void Material::Load(Material::Header *file)
{
header = file;
// Flags
flags = (MatFlags *) &header->flags;
flags->texture_map = std::min((int)MAX_TEX_MAP, (int)flags->texture_map);
flags->texture_srt = std::min((int)MAX_TEX_SRT, (int)flags->texture_srt);
flags->texture_coord_gen = std::min((int)MAX_TEX_GEN, (int)flags->texture_coord_gen);
flags->ind_srt = flags->ind_srt;
flags->ind_stage = std::min((int)MAX_IND_STAGES, (int)flags->ind_stage);
flags->tev_stages = std::min((int)MAX_TEV_STAGES, (int)flags->tev_stages);
u8 *buf_offset = (u8 *) (header+1);
// texture map
if(flags->texture_map)
{
texture_maps = (TextureMap *) buf_offset;
buf_offset += sizeof(TextureMap) * flags->texture_map;
}
// texture srt
if(flags->texture_srt)
{
texture_srts = (TextureSrt *) buf_offset;
buf_offset += sizeof(TextureSrt) * flags->texture_srt;
}
// texture coord gen
if(flags->texture_coord_gen)
{
texture_coord_gens = (TextureCoordGen *) buf_offset;
buf_offset += sizeof(TextureCoordGen) * flags->texture_coord_gen;
}
// channel control
if (flags->channel_control)
{
chan_control = (ChannelControl *) buf_offset;
buf_offset += sizeof(ChannelControl);
}
// material color
if (flags->material_color)
{
mat_color = (GXColor *) buf_offset;
buf_offset += sizeof(GXColor);
}
//else Default to 0xFFFFFFFF
// tev swap table
if (flags->tev_swap_table)
{
tev_swap_table = (TevSwap *) buf_offset;
buf_offset += sizeof(TevSwap) * 4;
}
// ind srt
if(flags->ind_srt)
{
ind_srt = (IndSrt *) buf_offset;
buf_offset += sizeof(IndSrt) * flags->ind_srt;
}
// ind stage
if(flags->ind_stage)
{
ind_stage = (IndStage *) buf_offset;
buf_offset += sizeof(IndStage) * flags->ind_stage;
}
// tev stage
if(flags->tev_stages)
{
tev_stages = (TevStage *) buf_offset;
buf_offset += sizeof(TevStage) * flags->tev_stages;
}
// alpha compare
if (flags->alpha_compare)
{
alpha_compare = (AlphaCompareModes *) buf_offset;
buf_offset += sizeof(AlphaCompareModes);
}
// blend mode
if (flags->blend_mode)
{
blend_mode = (BlendModes *) buf_offset;
buf_offset += sizeof(BlendModes);
}
}
inline void Material::ApplyChannelControl(u8 render_alpha, bool &modulate_colors) const
{
if(flags->channel_control)
{
GX_SetChanCtrl(0, 0, 0, chan_control->color_matsrc, 0, 0, 2 );
GX_SetChanCtrl(2, 0, 0, chan_control->alpha_matsrc, 0, 0, 2 );
if(chan_control->alpha_matsrc != 1 && chan_control->color_matsrc != 1)
modulate_colors = false;
if(!chan_control->alpha_matsrc || !chan_control->color_matsrc)
{
GXColor matColor = (GXColor){0xff, 0xff, 0xff, MultiplyAlpha(0xff, render_alpha) };
if(flags->material_color)
matColor = (GXColor){ mat_color->r, mat_color->g, mat_color->b,
MultiplyAlpha(mat_color->a, render_alpha) };
GX_SetChanMatColor(4, matColor);
if((*(u32 *)&matColor) == 0xFFFFFFFF)
modulate_colors = true;
}
}
else
{
GX_SetChanCtrl(4, 0, 0, 1, 0, 0, 2);
}
GX_SetNumChans(1);
}
inline void Material::ApplyTexCoordGens(void) const
{
// texture coord gen
for(u32 i = 0; i != flags->texture_coord_gen; ++i)
{
const TextureCoordGen &tcg = texture_coord_gens[i];
GX_SetTexCoordGen(GX_TEXCOORD0 + i, tcg.tgen_typ, tcg.tgen_src, tcg.mtxsrc);
const u8 mtrx = (tcg.mtxsrc - GX_TEXMTX0) / 3;
if (tcg.tgen_typ == 1 && tcg.mtxsrc != GX_IDENTITY && mtrx < flags->texture_srt)
{
const TextureSrt& srt = texture_srts[mtrx];
const float rotate_rad = DegToRad(srt.rotate);
const float cosF = cosf(rotate_rad);
const float sinF = sinf(rotate_rad);
// setup texture matrix
Mtx m;
m[0][0] = srt.scale_x * cosF;
m[0][1] = srt.scale_y * -sinF;
m[0][2] = 0.0f;
m[0][3] = -0.5f * (m[0][0] + m[0][1]) + srt.translate_x + 0.5f;
m[1][0] = srt.scale_x * sinF;
m[1][1] = srt.scale_y * cosF;
m[1][2] = 0.0f;
m[1][3] = -0.5f * (m[1][0] + m[1][1]) + srt.translate_y + 0.5f;
m[2][0] = 0.0f;
m[2][1] = 0.0f;
m[2][2] = 1.0f;
m[2][3] = 0.0f;
GX_LoadTexMtxImm(m, tcg.mtxsrc, GX_MTX3x4);
}
}
GX_SetNumTexGens(flags->texture_coord_gen);
}
inline void Material::ApplyTevSwapTable(void) const
{
if (flags->tev_swap_table)
{
for(int i = 0; i < 4; i++)
GX_SetTevSwapModeTable(GX_TEV_SWAP0 + i,
tev_swap_table[i].r, tev_swap_table[i].g,
tev_swap_table[i].b, tev_swap_table[i].a);
}
else
{
GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA);
GX_SetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_ALPHA);
GX_SetTevSwapModeTable(GX_TEV_SWAP2, GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN, GX_CH_ALPHA);
GX_SetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, GX_CH_ALPHA);
}
}
inline void Material::ApplyTevStages(bool modulate_colors) const
{
u32 tev_stages_cnt = 0;
if(flags->tev_stages)
{
// tev stages
for(u32 i = 0; i < flags->tev_stages; ++i)
{
const TevStage &ts = tev_stages[i];
GX_SetTevOrder(i, ts.texcoord, ts.tex_map | (ts.lowBit << 8), ts.color );
GX_SetTevSwapMode(i, ts.ras_sel, ts.tex_sel);
GX_SetTevColorIn(i, ts.color_in.a, ts.color_in.b, ts.color_in.c, ts.color_in.d);
GX_SetTevColorOp(i, ts.color_in.tevop, ts.color_in.tevbias, ts.color_in.tevscale, ts.color_in.clamp, ts.color_in.tevregid );
GX_SetTevKColorSel(i, ts.color_in.sel );
GX_SetTevAlphaIn(i, ts.alpha_in.a, ts.alpha_in.b, ts.alpha_in.c, ts.alpha_in.d);
GX_SetTevAlphaOp(i, ts.alpha_in.tevop, ts.alpha_in.tevbias, ts.alpha_in.tevscale, ts.alpha_in.clamp, ts.alpha_in.tevregid );
GX_SetTevKAlphaSel(i, ts.alpha_in.sel );
GX_SetTevIndirect(i, ts.ind.indtexid, ts.ind.format, ts.ind.bias, ts.ind.mtxid,
ts.ind.wrap_s, ts.ind.wrap_t, ts.ind.addprev, ts.ind.utclod, ts.ind.a);
tev_stages_cnt++;
}
}
else
{
if(flags->texture_map == 0)
{
// 1st stage
GX_SetTevOrder(GX_TEVSTAGE0, 0xFF, 0xFF, 4);
GX_SetTevColorIn(GX_TEVSTAGE0, 0xF, 4, 0xA, 0xF);
GX_SetTevAlphaIn(GX_TEVSTAGE0, 0x7, 2, 0x5, 0x7);
tev_stages_cnt++;
}
else if(flags->texture_map == 1)
{
// 1st stage
GX_SetTevOrder(GX_TEVSTAGE0, 0, 0, 0xFF);
GX_SetTevColorIn(GX_TEVSTAGE0, 2, 4, 8, 0xF);
GX_SetTevAlphaIn(GX_TEVSTAGE0, 1, 2, 4, 7);
tev_stages_cnt++;
// 2nd stage
if(modulate_colors)
{
GX_SetTevOrder(GX_TEVSTAGE0 + tev_stages_cnt, 0xFF, 0xFF, 4);
GX_SetTevColorIn(GX_TEVSTAGE0 + tev_stages_cnt, 0xF, 0, 0xA, 0xF);
GX_SetTevAlphaIn(GX_TEVSTAGE0 + tev_stages_cnt, 7, 0, 5, 7);
tev_stages_cnt++;
}
}
else if(flags->texture_map == 2)
{
// 1st stage
GX_SetTevOrder(GX_TEVSTAGE0, 0, 0, 0xFF);
GX_SetTevColorIn(GX_TEVSTAGE0, 0xF, 0xF, 0xF, 8);
GX_SetTevAlphaIn(GX_TEVSTAGE0, 7, 7, 7, 4);
tev_stages_cnt++;
// 2nd stage
GX_SetTevOrder(GX_TEVSTAGE0 + tev_stages_cnt, 1, 1, 0xFF);
GX_SetTevColorIn(GX_TEVSTAGE0 + tev_stages_cnt, 8, 0, 0xE, 0xF);
GX_SetTevAlphaIn(GX_TEVSTAGE0 + tev_stages_cnt, 4, 0, 6, 7);
GX_SetTevKColorSel(GX_TEVSTAGE0 + tev_stages_cnt, 0x1f);
GX_SetTevKAlphaSel(GX_TEVSTAGE0 + tev_stages_cnt, 0x1f);
tev_stages_cnt++;
// 3rd stage
if(modulate_colors)
{
GX_SetTevOrder(GX_TEVSTAGE0 + tev_stages_cnt, 0xFF, 0xFF, 4);
GX_SetTevColorIn(GX_TEVSTAGE0 + tev_stages_cnt, 0xF, 0, 0xA, 0xF);
GX_SetTevAlphaIn(GX_TEVSTAGE0 + tev_stages_cnt, 7, 0, 5, 7);
tev_stages_cnt++;
}
}
else
{
u32 TevKDefault[] = { 0x1F, 0x1B, 0x17, 0x13, 0x1E, 0x1A, 0x16, 0x12 };
for(int i = 0; i < flags->texture_map; i++)
{
GX_SetTevOrder(i, i, i, 0xff );
GX_SetTevColorIn(i, 0xf, 8, 0xe, i ? 0xf : 0 );
GX_SetTevAlphaIn(i, 7, 4, 6, i ? 7 : 0 );
GX_SetTevKColorSel(i, TevKDefault[i] );
GX_SetTevKAlphaSel(i, TevKDefault[i] );
tev_stages_cnt++;
}
GX_SetTevOrder(GX_TEVSTAGE0 + tev_stages_cnt, 0xff, 0xff, 0xff );
GX_SetTevColorIn(GX_TEVSTAGE0 + tev_stages_cnt, 2, 4, 0, 0xf );
GX_SetTevAlphaIn(GX_TEVSTAGE0 + tev_stages_cnt, 1, 2, 0, 7 );
tev_stages_cnt++;
if(modulate_colors)
{
GX_SetTevOrder(GX_TEVSTAGE0 + tev_stages_cnt, 0xFF, 0xFF, 4);
GX_SetTevColorIn(GX_TEVSTAGE0 + tev_stages_cnt, 0xF, 0, 0xA, 0xF);
GX_SetTevAlphaIn(GX_TEVSTAGE0 + tev_stages_cnt, 7, 0, 5, 7);
tev_stages_cnt++;
}
}
for(u32 i = 0; i < tev_stages_cnt; i++)
{
GX_SetTevColorOp(GX_TEVSTAGE0 + i, 0, 0, 0, 1, 0);
GX_SetTevAlphaOp(GX_TEVSTAGE0 + i, 0, 0, 0, 1, 0);
GX_SetTevDirect(GX_TEVSTAGE0 + i);
GX_SetTevSwapMode(GX_TEVSTAGE0 + i, 0, 0);
}
}
// enable correct number of tev stages
GX_SetNumTevStages(tev_stages_cnt);
}
inline void Material::ApplyIndStages(void) const
{
for( int i = 0; i < flags->ind_srt; i++ )
{
const IndSrt &ind = ind_srt[i];
const float rotate_rad = DegToRad(ind.rotate);
// maybe add a look up table
float cosF = cosf(rotate_rad);
float sinF = sinf(rotate_rad);
int scale_exp = 0;
f32 mtx23[2][3];
f32 mtxabs23[2][3];
mtx23[0][0] = ind.scale_x * cosF;
mtx23[0][1] = ind.scale_y * -sinF;
mtx23[0][2] = ind.translate_x;
mtx23[1][0] = ind.scale_x * sinF;
mtx23[1][1] = ind.scale_y * cosF;
mtx23[1][2] = ind.translate_y;
// create matrix with abs values
// compiler will optimize the loops
for(int n = 0; n < 2; n++)
for(int m = 0; m < 3; m++)
mtxabs23[n][m] = fabs(mtx23[n][m]);
// hardcore clamping going on here
if( (mtxabs23[0][0] >= 1.0f)
|| (mtxabs23[0][1] >= 1.0f)
|| (mtxabs23[0][2] >= 1.0f)
|| (mtxabs23[1][0] >= 1.0f)
|| (mtxabs23[1][1] >= 1.0f)
|| (mtxabs23[1][2] >= 1.0f))
{
while( scale_exp < 0x2E
&& ((mtxabs23[0][0] >= 1.0f)
|| (mtxabs23[0][1] >= 1.0f)
|| (mtxabs23[0][2] >= 1.0f)
|| (mtxabs23[1][0] >= 1.0f)
|| (mtxabs23[1][1] >= 1.0f)
|| (mtxabs23[1][2] >= 1.0f)))
{
for(int n = 0; n < 2; n++)
{
for(int m = 0; m < 3; m++)
{
mtx23[n][m] *= 0.5f;
mtxabs23[n][m] *= 0.5f;
}
}
scale_exp++;
}
}
else if( (mtxabs23[0][0] < 0.5f)
&& (mtxabs23[0][1] < 0.5f)
&& (mtxabs23[0][2] < 0.5f)
&& (mtxabs23[1][0] < 0.5f)
&& (mtxabs23[1][1] < 0.5f)
&& (mtxabs23[1][2] < 0.5f))
{
while( scale_exp > -0x11
&& (mtxabs23[0][0] < 0.5f)
&& (mtxabs23[0][1] < 0.5f)
&& (mtxabs23[0][2] < 0.5f)
&& (mtxabs23[1][0] < 0.5f)
&& (mtxabs23[1][1] < 0.5f)
&& (mtxabs23[1][2] < 0.5f))
{
for(int n = 0; n < 2; n++)
{
for(int m = 0; m < 3; m++)
{
mtx23[n][m] *= 2.0f;
mtxabs23[n][m] *= 2.0f;
}
}
scale_exp--;
}
}
GX_SetIndTexMatrix(GX_ITM_0 + i, mtx23, scale_exp);
}
for( int i = 0; i < flags->ind_stage; i++ )
{
const IndStage &stage = ind_stage[i];
GX_SetIndTexOrder(i, stage.texcoord, stage.tex_map);
GX_SetIndTexCoordScale(i, stage.scale_s, stage.scale_t);
}
GX_SetNumIndStages(flags->ind_stage);
}
inline void Material::ApplyTextures(const BannerResources& resources) const
{
u8 tlut_name = 0;
for(u32 i = 0; i < flags->texture_map; ++i)
{
const TextureMap &tr = texture_maps[i];
if(palette_texture[i] == DEFAULT_PALETTE)
{
if (tr.tex_index < resources.textures.size())
resources.textures[tr.tex_index]->Apply(tlut_name, i, tr.wrap_s, tr.wrap_t);
}
else
{
// find texture from palette
if(palette_texture[i] >= resources.palettes[resources.cur_set].size())
{
gprintf( "palette index is out of range %i\n", palette_texture[i]);
return;
}
for(u32 n = 0; n < resources.textures.size(); n++)
{
if(resources.textures[n]->getName() == resources.palettes[resources.cur_set][palette_texture[i]])
{
resources.textures[n]->Apply(tlut_name, i, tr.wrap_s, tr.wrap_t);
break;
}
}
}
}
// invalidate texture cache
GX_InvalidateTexAll();
}
void Material::Apply(const BannerResources& resources, u8 render_alpha, bool modulate_colors) const
{
// channel control and material color
ApplyChannelControl(render_alpha, modulate_colors);
// texture coordinates gen
ApplyTexCoordGens();
// bind textures
ApplyTextures(resources);
for (u32 i = 0; i < 4; ++i)
{
// tev reg colors
if(i < 3)
GX_SetTevColorS10(GX_TEVREG0 + i, header->color_regs[i]);
// tev k colors
GX_SetTevKColor(GX_KCOLOR0 + i, header->color_constants[i]);
}
// tev swap colors
ApplyTevSwapTable();
// tev stages
ApplyTevStages(modulate_colors);
// ind stages
ApplyIndStages();
// alpha compare
if(flags->alpha_compare)
GX_SetAlphaCompare(alpha_compare->compare & 0xf, alpha_compare->ref0,
alpha_compare->op, alpha_compare->compare >> 4, alpha_compare->ref1);
else
GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0);
// blend mode
if (flags->blend_mode)
GX_SetBlendMode(blend_mode->type, blend_mode->src_factor, blend_mode->dst_factor, blend_mode->logical_op);
else
GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET);
}
void Material::ProcessHermiteKey(const KeyType& type, float value)
{
if (type.type == ANIMATION_TYPE_TEXTURE_SRT) // texture scale/rotate/translate
{
if (type.target < 5 && type.index < flags->texture_srt)
{
(&texture_srts[type.index].translate_x)[type.target] = value;
return;
}
// TODO: Something is still here: target 0-4 and index 1-9 while texture_srt is 1, value is always 0 or 1
return; // TODO remove this
}
else if (type.type == ANIMATION_TYPE_IND_MATERIAL) // ind texture crap
{
if (type.target < 5 && type.index < flags->ind_srt)
{
(&ind_srt[type.index].translate_x)[type.target] = value;
return;
}
return; // TODO remove this
}
else if (type.type == ANIMATION_TYPE_MATERIAL_COLOR) // material color
{
if (type.target < 4)
{
// mat_color
if(flags->material_color)
(&mat_color->r)[type.target] = FLOAT_2_U8(value);
return;
}
else if (type.target < 0x10)
{
(&header->color_regs->r)[type.target - 4] = FLOAT_2_S16(value);
return;
}
else if (type.target < 0x20)
{
(&header->color_constants->r)[type.target - 0x10] = FLOAT_2_U8(value);
return;
}
}
Base::ProcessHermiteKey(type, value);
}
void Material::ProcessStepKey(const KeyType& type, StepKeyHandler::KeyData data)
{
if (type.type == ANIMATION_TYPE_TEXTURE_PALETTE) // tpl palette
{
if(type.index < MAX_TEX_MAP)
{
palette_texture[type.index] = data.data2;
return;
}
}
Base::ProcessStepKey(type, data);
}