diff --git a/LibCmo/CK2/ObjImpls/CK3dEntity.hpp b/LibCmo/CK2/ObjImpls/CK3dEntity.hpp index ff4a28a..a15e17a 100644 --- a/LibCmo/CK2/ObjImpls/CK3dEntity.hpp +++ b/LibCmo/CK2/ObjImpls/CK3dEntity.hpp @@ -48,7 +48,7 @@ namespace LibCmo::CK2::ObjImpls { CKDWORD m_ZOrder; // replace the whole heavy CKSceneGraphNode VxMath::VX_MOVEABLE_FLAGS m_MoveableFlags; - // YYCMARK: This field is called m_EntityFlags in reverse project. + // MARK: This field is called m_EntityFlags in reverse project. // I change this because I want to give it a more explicit name to make it is different with other flags. CK_3DENTITY_FLAGS m_3dEntityFlags; diff --git a/LibCmo/CK2/ObjImpls/CKLight.cpp b/LibCmo/CK2/ObjImpls/CKLight.cpp index feadab1..5054138 100644 --- a/LibCmo/CK2/ObjImpls/CKLight.cpp +++ b/LibCmo/CK2/ObjImpls/CKLight.cpp @@ -3,4 +3,246 @@ namespace LibCmo::CK2::ObjImpls { + CKLight::CKLight(CKContext* ctx, CK_ID ckid, CKSTRING name) : + CK3dEntity(ctx, ckid, name), + m_LightData(), m_LightFlags(LightFlags::Active), m_LightPower(1.0f) { + // Setup light data + m_LightData.m_Type = VxMath::VXLIGHT_TYPE::VX_LIGHTPOINT; + m_LightData.m_Diffuse = VxMath::VxColor(1.0f, 1.0f, 1.0f); + m_LightData.m_Specular = VxMath::VxColor(0); + m_LightData.m_Ambient = VxMath::VxColor(0.0f, 0.0f, 0.0f); + m_LightData.m_Range = 5000.0f; + m_LightData.m_Falloff = 1.0f; + m_LightData.m_Attenuation0 = 1.0f; + m_LightData.m_Attenuation1 = 0.0f; + m_LightData.m_Attenuation2 = 0.0f; + m_LightData.m_InnerSpotCone = 0.69813174f; // MARK: Perhaps 40 deg in rad. + m_LightData.m_OuterSpotCone = 0.78539819f; // MARK: Perhaps 45 deg in rad. + } + + CKLight::~CKLight() {} + + bool CKLight::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) { + bool suc = CK3dEntity::Save(chunk, file, flags); + if (!suc) return false; + + // Save main data + { + chunk->WriteIdentifier(CK_STATESAVEFLAGS_LIGHT::CK_STATESAVE_LIGHTDATA); + + // Combine light type and flags data. + CKDWORD light_type_and_flags = static_cast(m_LightFlags) & 0xFFFFFF00u; + light_type_and_flags |= static_cast(m_LightData.m_Type) & 0xFFu; + chunk->WriteStruct(light_type_and_flags); + + // Save diffuse color with constant 1.0 alpha factor. + chunk->WriteStruct(m_LightData.m_Diffuse.ToARGB() | 0xFF000000u); + + chunk->WriteStruct(m_LightData.m_Attenuation0); + chunk->WriteStruct(m_LightData.m_Attenuation1); + chunk->WriteStruct(m_LightData.m_Attenuation2); + + chunk->WriteStruct(m_LightData.m_Range); + + if (m_LightData.m_Type == VxMath::VXLIGHT_TYPE::VX_LIGHTSPOT) { + chunk->WriteStruct(m_LightData.m_OuterSpotCone); + chunk->WriteStruct(m_LightData.m_InnerSpotCone); + chunk->WriteStruct(m_LightData.m_Falloff); + } + + } + + // Save light power + if (m_LightPower != 1.0f) { + chunk->WriteIdentifier(CK_STATESAVEFLAGS_LIGHT::CK_STATESAVE_LIGHTDATA2); + chunk->WriteStruct(m_LightPower); + } + + chunk->SetClassId(CK_CLASSID::CKCID_LIGHT); + return true; + } + + bool CKLight::Load(CKStateChunk* chunk, CKFileVisitor* file) { + bool suc = CK3dEntity::Load(chunk, file); + if (!suc) return false; + + // MARK: I drop the read process for too low version. + // return false anyway. + if (chunk->GetDataVersion() < CK_STATECHUNK_DATAVERSION::CHUNK_MAJORCHANGE_VERSION) { + return false; + } + + // Read main data + if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_LIGHT::CK_STATESAVE_LIGHTDATA)) { + // Read a DWORD storing light type and flags + // Because the lowest byte in light flags always is 0x00, + // so Virtools use it to store light type. + // After removing light type component, the rest of data is light flags. + // (do not need any SHIFT! It's okey that just set the lowest byte to zero.) + CKDWORD light_type_and_flags; + chunk->ReadStruct(light_type_and_flags); + m_LightData.m_Type = static_cast(light_type_and_flags & 0xFFu); + m_LightFlags = static_cast(light_type_and_flags & 0xFFFFFF00u); + + CKDWORD dword_diffuse; + chunk->ReadStruct(dword_diffuse); + m_LightData.m_Diffuse = VxMath::VxColor(dword_diffuse); + + chunk->ReadStruct(m_LightData.m_Attenuation0); + chunk->ReadStruct(m_LightData.m_Attenuation1); + chunk->ReadStruct(m_LightData.m_Attenuation2); + + chunk->ReadStruct(m_LightData.m_Range); + + if (m_LightData.m_Type == VxMath::VXLIGHT_TYPE::VX_LIGHTSPOT) { + chunk->ReadStruct(m_LightData.m_OuterSpotCone); + chunk->ReadStruct(m_LightData.m_InnerSpotCone); + chunk->ReadStruct(m_LightData.m_Falloff); + } + + } + + // Read light power + if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_LIGHT::CK_STATESAVE_LIGHTDATA2)) { + chunk->ReadStruct(m_LightPower); + } else { + m_LightPower = 1.0f; + } + + // Correct light type to prevent accident out of range value. + switch (m_LightData.m_Type) { + case VxMath::VXLIGHT_TYPE::VX_LIGHTPOINT: + case VxMath::VXLIGHT_TYPE::VX_LIGHTSPOT: + case VxMath::VXLIGHT_TYPE::VX_LIGHTDIREC: + case VxMath::VXLIGHT_TYPE::VX_LIGHTPARA: + // do nothing + break; + default: + // reset it to point + m_LightData.m_Type = VxMath::VXLIGHT_TYPE::VX_LIGHTPOINT; + break; + } + + return true; + } + +#pragma region Class Operations + + VxMath::VXLIGHT_TYPE CKLight::GetType() const { + return m_LightData.m_Type; + } + + void CKLight::SetType(VxMath::VXLIGHT_TYPE light_type) { + m_LightData.m_Type = light_type; + } + + const VxMath::VxColor& CKLight::GetColor() const { + return m_LightData.m_Diffuse; + } + + void CKLight::SetColor(const VxMath::VxColor& c) { + m_LightData.m_Diffuse = c; + } + + CKFLOAT CKLight::GetConstantAttenuation() const { + return m_LightData.m_Attenuation0; + } + + CKFLOAT CKLight::GetLinearAttenuation() const { + return m_LightData.m_Attenuation1; + } + + CKFLOAT CKLight::GetQuadraticAttenuation() const { + return m_LightData.m_Attenuation2; + } + + void CKLight::SetConstantAttenuation(CKFLOAT value) { + m_LightData.m_Attenuation0 = value; + } + + void CKLight::SetLinearAttenuation(CKFLOAT value) { + m_LightData.m_Attenuation1 = value; + } + + void CKLight::SetQuadraticAttenuation(CKFLOAT value) { + m_LightData.m_Attenuation2 = value; + } + + CKFLOAT CKLight::GetRange() const { + return m_LightData.m_Range; + } + + void CKLight::SetRange(CKFLOAT value) { + m_LightData.m_Range = value; + } + + CKFLOAT CKLight::GetHotSpot() const { + return m_LightData.m_InnerSpotCone; + } + + CKFLOAT CKLight::GetFalloff() const { + return m_LightData.m_OuterSpotCone; + } + + CKFLOAT CKLight::GetFalloffShape() const { + return m_LightData.m_Falloff; + } + + void CKLight::SetHotSpot(CKFLOAT value) { + m_LightData.m_InnerSpotCone = value; + } + + void CKLight::SetFalloff(CKFLOAT value) { + m_LightData.m_OuterSpotCone = value; + } + + void CKLight::SetFalloffShape(CKFLOAT value) { + m_LightData.m_Falloff = value; + } + + bool CKLight::GetActivity() const { + return YYCC::EnumHelper::Has(m_LightFlags, LightFlags::Active); + } + + void CKLight::Active(bool active) { + if (active) { + YYCC::EnumHelper::Add(m_LightFlags, LightFlags::Active); + } else { + YYCC::EnumHelper::Remove(m_LightFlags, LightFlags::Active); + } + } + + bool CKLight::GetSpecularFlag() const { + return YYCC::EnumHelper::Has(m_LightFlags, LightFlags::Specular); + } + + void CKLight::SetSpecularFlag(bool specular) { + if (specular) { + YYCC::EnumHelper::Add(m_LightFlags, LightFlags::Specular); + } else { + YYCC::EnumHelper::Remove(m_LightFlags, LightFlags::Specular); + } + } + + CK3dEntity* CKLight::GetTarget() const { + // Normal light do not support target. + // So it always return nullptr. + return nullptr; + } + + void CKLight::SetTarget(CK3dEntity* target) { + // Normal light do not support target. + // So, do nothing. + } + + CKFLOAT CKLight::GetLightPower() const { + return m_LightPower; + } + + void CKLight::SetLightPower(CKFLOAT power) { + m_LightPower = power; + } + +#pragma endregion + } diff --git a/LibCmo/CK2/ObjImpls/CKLight.hpp b/LibCmo/CK2/ObjImpls/CKLight.hpp index e5c803a..765ca9b 100644 --- a/LibCmo/CK2/ObjImpls/CKLight.hpp +++ b/LibCmo/CK2/ObjImpls/CKLight.hpp @@ -75,10 +75,15 @@ namespace LibCmo::CK2::ObjImpls { CKFLOAT m_InnerSpotCone; CKFLOAT m_OuterSpotCone; }; + enum class LightFlags : CKDWORD { + None = 0, + Active = 0x100u, /**< if set, this light is active. */ + Specular = 0x200u, /**< if set, this light has specular flag. */ + }; CKLightData m_LightData; - // YYCMARK: This variable is called in m_Flags in reverse code. - DWORD m_LightFlags; + // MARK: This variable is called in m_Flags in reverse code. + LightFlags m_LightFlags; CKFLOAT m_LightPower; }; diff --git a/LibCmo/VxMath/VxTypes.hpp b/LibCmo/VxMath/VxTypes.hpp index 6fef4f8..7eade8f 100644 --- a/LibCmo/VxMath/VxTypes.hpp +++ b/LibCmo/VxMath/VxTypes.hpp @@ -375,33 +375,6 @@ namespace LibCmo::VxMath { VxColor(CKFLOAT _r, CKFLOAT _g, CKFLOAT _b, CKFLOAT _a) : r(_r), g(_g), b(_b), a(_a) {} VxColor(CKDWORD argb) { FromARGB(argb); } YYCC_DEF_CLS_COPY_MOVE(VxColor); - void FromARGB(CKDWORD argb) { - a = ((argb & 0xFF000000) >> 24) / 255.0f; - r = ((argb & 0x00FF0000) >> 16) / 255.0f; - g = ((argb & 0x0000FF00) >> 8) / 255.0f; - b = ((argb & 0x000000FF) >> 0) / 255.0f; - } - CKDWORD ToARGB() const { - CKDWORD argb = 0; - argb |= static_cast(a * 255.0f); - argb <<= 8; - argb |= static_cast(r * 255.0f); - argb <<= 8; - argb |= static_cast(g * 255.0f); - argb <<= 8; - argb |= static_cast(b * 255.0f); - return argb; - } - void Regulate() { - if (r > 1.0f) r = 1.0f; - else if (r < 0.0f) r = 0.0f; - if (g > 1.0f) g = 1.0f; - else if (g < 0.0f) g = 0.0f; - if (b > 1.0f) b = 1.0f; - else if (b < 0.0f) b = 0.0f; - if (a > 1.0f) a = 1.0f; - else if (a < 0.0f) a = 0.0f; - } CKFLOAT& operator[](size_t i) { switch (i) { case 0: return r; @@ -429,6 +402,35 @@ namespace LibCmo::VxMath { if (auto cmp = b <=> rhs.b; cmp != 0) return cmp; return a <=> rhs.a; } + + VxColor(CKFLOAT _r, CKFLOAT _g, CKFLOAT _b) : r(_r), g(_g), b(_b), a(1.0f) {} + void FromARGB(CKDWORD argb) { + a = ((argb & 0xFF000000) >> 24) / 255.0f; + r = ((argb & 0x00FF0000) >> 16) / 255.0f; + g = ((argb & 0x0000FF00) >> 8) / 255.0f; + b = ((argb & 0x000000FF) >> 0) / 255.0f; + } + CKDWORD ToARGB() const { + CKDWORD argb = 0; + argb |= static_cast(a * 255.0f); + argb <<= 8; + argb |= static_cast(r * 255.0f); + argb <<= 8; + argb |= static_cast(g * 255.0f); + argb <<= 8; + argb |= static_cast(b * 255.0f); + return argb; + } + void Regulate() { + if (r > 1.0f) r = 1.0f; + else if (r < 0.0f) r = 0.0f; + if (g > 1.0f) g = 1.0f; + else if (g < 0.0f) g = 0.0f; + if (b > 1.0f) b = 1.0f; + else if (b < 0.0f) b = 0.0f; + if (a > 1.0f) a = 1.0f; + else if (a < 0.0f) a = 0.0f; + } }; /**