feat: finish CKLight class.

- basically finish CKLight but no test now.
This commit is contained in:
yyc12345 2024-12-24 10:59:19 +08:00
parent ff5a590cf4
commit 3eeb1f6cb6
4 changed files with 279 additions and 30 deletions

View File

@ -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;

View File

@ -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<CKDWORD>(m_LightFlags) & 0xFFFFFF00u;
light_type_and_flags |= static_cast<CKDWORD>(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<VxMath::VXLIGHT_TYPE>(light_type_and_flags & 0xFFu);
m_LightFlags = static_cast<LightFlags>(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
}

View File

@ -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;
};

View File

@ -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<CKDWORD>(a * 255.0f);
argb <<= 8;
argb |= static_cast<CKDWORD>(r * 255.0f);
argb <<= 8;
argb |= static_cast<CKDWORD>(g * 255.0f);
argb <<= 8;
argb |= static_cast<CKDWORD>(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<CKDWORD>(a * 255.0f);
argb <<= 8;
argb |= static_cast<CKDWORD>(r * 255.0f);
argb <<= 8;
argb |= static_cast<CKDWORD>(g * 255.0f);
argb <<= 8;
argb |= static_cast<CKDWORD>(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;
}
};
/**