/* 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 #include #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); }