#include "CKMaterial.hpp" #include "../CKStateChunk.hpp" #include "../CKContext.hpp" #include "CKTexture.hpp" namespace LibCmo::CK2::ObjImpls { CKMaterial::CKMaterial(CKContext* ctx, CK_ID ckid, CKSTRING name) : CKBeObject(ctx, ckid, name), // following init are gotten from SDK document. m_Diffuse(0.7f, 0.7f, 0.7f, 1.0f), m_Ambient(0.3f, 0.3f, 0.3f, 1.0f), m_Specular(0.5f, 0.5f, 0.5f, 1.0f), m_SpecularPower(0.0f), m_Emissive(0.0f, 0.0f, 0.0f, 1.0f), m_EnableTwoSided(false), m_Textures { nullptr, nullptr, nullptr, nullptr }, m_TextureMinMode(VxMath::VXTEXTURE_FILTERMODE::VXTEXTUREFILTER_LINEAR), m_TextureMagMode(VxMath::VXTEXTURE_FILTERMODE::VXTEXTUREFILTER_LINEAR), m_SourceBlend(VxMath::VXBLEND_MODE::VXBLEND_ONE), m_DestBlend(VxMath::VXBLEND_MODE::VXBLEND_ZERO), m_EnableAlphaBlend(false), m_ShadeMode(VxMath::VXSHADE_MODE::VXSHADE_GOURAUD), m_FillMode(VxMath::VXFILL_MODE::VXFILL_SOLID), m_EnableAlphaTest(false), m_EnableZWrite(true), // following init are gotten from IDA. m_EnablePerspectiveCorrection(true), m_TextureBlendMode(VxMath::VXTEXTURE_BLENDMODE::VXTEXTUREBLEND_MODULATEALPHA), m_TextureAddressMode(VxMath::VXTEXTURE_ADDRESSMODE::VXTEXTURE_ADDRESSWRAP), m_ZFunc(VxMath::VXCMPFUNC::VXCMP_LESSEQUAL), m_AlphaFunc(VxMath::VXCMPFUNC::VXCMP_ALWAYS), m_Effect(VxMath::VX_EFFECT::VXEFFECT_NONE), m_TextureBorderColor(0), m_AlphaRef(0) {} CKMaterial::~CKMaterial() {} void CKMaterial::CheckPreDeletion() { CKBeObject::CheckPreDeletion(); // check 4 textures for (auto& tex : m_Textures) { if (tex != nullptr && tex->IsToBeDeleted()) { tex = nullptr; } } } bool CKMaterial::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) { bool suc = CKBeObject::Save(chunk, file, flags); if (!suc) return false; // save main data { chunk->WriteIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA); // 4 basic color and some power CKDWORD col; col = m_Diffuse.ToARGB(); chunk->WriteStruct(col); col = m_Ambient.ToARGB(); chunk->WriteStruct(col); col = m_Specular.ToARGB(); chunk->WriteStruct(col); col = m_Emissive.ToARGB(); chunk->WriteStruct(col); chunk->WriteStruct(m_SpecularPower); // write main texture chunk->WriteObjectPointer(m_Textures[0]); // misc data chunk->WriteStruct(m_TextureBorderColor); // write mix data 1 // construct mix data, see Read for more info about this mix data CKDWORD mixdata = 0; mixdata |= static_cast(m_TextureAddressMode) & 0xF; mixdata <<= 4; mixdata |= static_cast(m_FillMode) & 0xF; mixdata <<= 4; mixdata |= static_cast(m_ShadeMode) & 0xF; mixdata <<= 4; mixdata |= static_cast(m_DestBlend) & 0xF; mixdata <<= 4; mixdata |= static_cast(m_SourceBlend) & 0xF; mixdata <<= 4; mixdata |= static_cast(m_TextureMagMode) & 0xF; mixdata <<= 4; mixdata |= static_cast(m_TextureMinMode) & 0xF; mixdata <<= 4; mixdata |= static_cast(m_TextureBlendMode) & 0xF; // write it chunk->WriteStruct(mixdata); // write mix data 2 // construct it first, see Read for more info about this mix data mixdata = 0; mixdata |= static_cast(m_AlphaRef) & 0xFF; mixdata <<= 8; mixdata |= static_cast(m_AlphaFunc) & 0xFF; mixdata <<= 8; mixdata |= static_cast(m_ZFunc) & 0xFF; mixdata <<= 8; // sub mix flags CKDWORD mixflags = 0; if (m_EnableTwoSided) mixflags |= 0b1; if (m_EnableZWrite) mixflags |= 0b10; if (m_EnablePerspectiveCorrection) mixflags |= 0b100; if (m_EnableAlphaBlend) mixflags |= 0b1000; if (m_EnableAlphaTest) mixflags |= 0b10000; // merge into mix data mixdata |= mixflags & 0xFF; // write it chunk->WriteStruct(mixdata); } // write effect and extra texture. // it seems that extra textures only available when a valid effect existing if (m_Effect != VxMath::VX_EFFECT::VXEFFECT_NONE) { // we only support no-parameter effect, write it chunk->WriteIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA3); chunk->WriteStruct(m_Effect); // if have extra textures, write it if (m_Textures[1] != nullptr || m_Textures[2] != nullptr || m_Textures[3] != nullptr) { chunk->WriteIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA2); chunk->WriteObjectPointer(m_Textures[1]); chunk->WriteObjectPointer(m_Textures[2]); chunk->WriteObjectPointer(m_Textures[3]); } } chunk->SetClassId(CK_CLASSID::CKCID_MATERIAL); return true; } bool CKMaterial::Load(CKStateChunk* chunk, CKFileVisitor* file) { bool suc = CKBeObject::Load(chunk, file); if (!suc) return false; // clear textures for (auto& tex : m_Textures) { tex = nullptr; } // read main data if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA)) { if (chunk->GetDataVersion() < CK_STATECHUNK_DATAVERSION::CHUNK_MAJORCHANGE_VERSION) { // MARK: old data process. i don't want to implement // because it is not related to my work. return false return false; } else { // 4 basic color and some power CKDWORD col; chunk->ReadStruct(col); m_Diffuse.FromARGB(col); chunk->ReadStruct(col); m_Ambient.FromARGB(col); chunk->ReadStruct(col); m_Specular.FromARGB(col); chunk->ReadStruct(col); m_Emissive.FromARGB(col); chunk->ReadStruct(m_SpecularPower); // main texture CKObject* tex = nullptr; chunk->ReadObjectPointer(tex); if (tex != nullptr && tex->GetClassID() == CK_CLASSID::CKCID_TEXTURE) { m_Textures[0] = static_cast(tex); } // misc data chunk->ReadStruct(m_TextureBorderColor); // mix data 1, including some blend enums // 32bit data. each 4 bit store a value. total 8 data. // HIGH >>> m_TextureAddressMode, m_FillMode, m_ShadeMode, m_DestBlend, m_SourceBlend, m_TextureMagMode, m_TextureMinMode, m_TextureBlendMode <<< LOW CKDWORD mixdata; chunk->ReadStruct(mixdata); m_TextureBlendMode = static_cast(mixdata & 0xF); mixdata >>= 4; m_TextureMinMode = static_cast(mixdata & 0xF); mixdata >>= 4; m_TextureMagMode = static_cast(mixdata & 0xF); mixdata >>= 4; m_SourceBlend = static_cast(mixdata & 0xF); mixdata >>= 4; m_DestBlend = static_cast(mixdata & 0xF); mixdata >>= 4; m_ShadeMode = static_cast(mixdata & 0xF); mixdata >>= 4; m_FillMode = static_cast(mixdata & 0xF); mixdata >>= 4; m_TextureAddressMode = static_cast(mixdata & 0xF); // mix data 2, including enable flag and transparent data // 32bit data. // HIGH >>> 0xFFFF(m_AlphaRef) 0xFFFF(m_AlphaFunc) 0xFFFF(m_ZFunc) 0xFFFF(enable flags) <<< LOW // for enable flags, total 8 bit. only low 5 bit used. // HIGH >>> 0(not used) 0(not used) 0(not used) 1(m_EnableAlphaTest) 1(m_EnableAlphaBlend) 1(m_EnablePerspectiveCorrection) 1(m_EnableZWrite) 1(m_EnableTwoSided) <<< LOW chunk->ReadStruct(mixdata); m_EnableTwoSided = mixdata & 0b1; m_EnableZWrite = mixdata & 0b10; m_EnablePerspectiveCorrection = mixdata & 0b100; m_EnableAlphaBlend = mixdata & 0b1000; m_EnableAlphaTest = mixdata & 0b10000; mixdata >>= 8; m_ZFunc = static_cast(mixdata & 0xFF); mixdata >>= 8; m_AlphaFunc = static_cast(mixdata & 0xFF); mixdata >>= 8; m_AlphaRef = static_cast(mixdata & 0xFF); } } // extra texture data if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA2)) { // read 3 extra texture CKObject* tex = nullptr; for (size_t i = 1; i < 4; ++i) { chunk->ReadObjectPointer(tex); if (tex != nullptr && tex->GetClassID() == CK_CLASSID::CKCID_TEXTURE) { m_Textures[i] = static_cast(tex); } } } // single effect if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA3)) { chunk->ReadStruct(m_Effect); } // effect with parameter if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA5)) { // MARK: i do not support CKParameter anymore. // so we downgrade it into single effect type. // hope this convertion will not break anything. // drop parameter id. CK_ID paramid; chunk->ReadObjectID(paramid); // read effect self chunk->ReadStruct(m_Effect); } return true; } #pragma region Data Visitor const VxMath::VxColor& CKMaterial::GetDiffuse() const { return m_Diffuse; } void CKMaterial::SetDiffuse(const VxMath::VxColor& col) { m_Diffuse = col; } const VxMath::VxColor& CKMaterial::GetAmbient() const { return m_Ambient; } void CKMaterial::SetAmbient(const VxMath::VxColor& col) { m_Ambient = col; } const VxMath::VxColor& CKMaterial::GetSpecular() const { return m_Specular; } void CKMaterial::SetSpecular(const VxMath::VxColor& col) { m_Specular = col; } const VxMath::VxColor& CKMaterial::GetEmissive() const { return m_Emissive; } void CKMaterial::SetEmissive(const VxMath::VxColor& col) { m_Emissive = col; } CKFLOAT CKMaterial::GetSpecularPower() const { return m_SpecularPower; } void CKMaterial::SetSpecularPower(CKFLOAT val) { m_SpecularPower = val; } CKTexture* CKMaterial::GetTexture(CKDWORD idx) const { if (idx >= m_Textures.size()) return nullptr; return m_Textures[idx]; } void CKMaterial::SetTexture(CKTexture* tex, CKDWORD idx) { if (idx >= m_Textures.size()) return; m_Textures[idx] = tex; } CKDWORD CKMaterial::GetTextureBorderColor() const { return m_TextureBorderColor; } void CKMaterial::SetTextureBorderColor(CKDWORD val) { m_TextureBorderColor = val; } VxMath::VXTEXTURE_BLENDMODE CKMaterial::GetTextureBlendMode() const { return m_TextureBlendMode; } void CKMaterial::SetTextureBlendMode(VxMath::VXTEXTURE_BLENDMODE val) { m_TextureBlendMode = val; } VxMath::VXTEXTURE_FILTERMODE CKMaterial::GetTextureMinMode() const { return m_TextureMinMode; } void CKMaterial::SetTextureMinMode(VxMath::VXTEXTURE_FILTERMODE val) { m_TextureMinMode = val; } VxMath::VXTEXTURE_FILTERMODE CKMaterial::GetTextureMagMode() const { return m_TextureMagMode; } void CKMaterial::SetTextureMagMode(VxMath::VXTEXTURE_FILTERMODE val) { m_TextureMagMode = val; } VxMath::VXTEXTURE_ADDRESSMODE CKMaterial::GetTextureAddressMode() const { return m_TextureAddressMode; } void CKMaterial::SetTextureAddressMode(VxMath::VXTEXTURE_ADDRESSMODE val) { m_TextureAddressMode = val; } VxMath::VXBLEND_MODE CKMaterial::GetSourceBlend() const { return m_SourceBlend; } void CKMaterial::SetSourceBlend(VxMath::VXBLEND_MODE val) { m_SourceBlend = val; } VxMath::VXBLEND_MODE CKMaterial::GetDestBlend() const { return m_DestBlend; } void CKMaterial::SetDestBlend(VxMath::VXBLEND_MODE val) { m_DestBlend = val; } VxMath::VXFILL_MODE CKMaterial::GetFillMode() const { return m_FillMode; } void CKMaterial::SetFillMode(VxMath::VXFILL_MODE val) { m_FillMode = val; } VxMath::VXSHADE_MODE CKMaterial::GetShadeMode() const { return m_ShadeMode; } void CKMaterial::SetShadeMode(VxMath::VXSHADE_MODE val) { m_ShadeMode = val; } bool CKMaterial::GetAlphaTestEnabled() const { return m_EnableAlphaTest; } void CKMaterial::SetAlphaTestEnabled(bool enabled) { m_EnableAlphaTest = enabled; } bool CKMaterial::GetAlphaBlendEnabled() const { return m_EnableAlphaBlend; } void CKMaterial::SetAlphaBlendEnabled(bool enabled) { m_EnableAlphaBlend = enabled; } bool CKMaterial::GetPerspectiveCorrectionEnabled() const { return m_EnablePerspectiveCorrection; } void CKMaterial::SetPerspectiveCorrectionEnabled(bool enabled) { m_EnablePerspectiveCorrection = enabled; } bool CKMaterial::GetZWriteEnabled() const { return m_EnableZWrite; } void CKMaterial::SetZWriteEnabled(bool enabled) { m_EnableZWrite = enabled; } bool CKMaterial::GetTwoSidedEnabled() const { return m_EnableTwoSided; } void CKMaterial::SetTwoSidedEnabled(bool enabled) { m_EnableTwoSided = enabled; } CKBYTE CKMaterial::GetAlphaRef() const { return m_AlphaRef; } void CKMaterial::SetAlphaRef(CKBYTE val) { m_AlphaRef = val; } VxMath::VXCMPFUNC CKMaterial::GetAlphaFunc() const { return m_AlphaFunc; } void CKMaterial::SetAlphaFunc(VxMath::VXCMPFUNC val) { m_AlphaFunc = val; } VxMath::VXCMPFUNC CKMaterial::GetZFunc() const { return m_ZFunc; } void CKMaterial::SetZFunc(VxMath::VXCMPFUNC val) { m_ZFunc = val; } VxMath::VX_EFFECT CKMaterial::GetEffect() const { return m_Effect; } void CKMaterial::SetEffect(VxMath::VX_EFFECT val) { m_Effect = val; } #pragma endregion }