libcmo21/LibCmo/CK2/ObjImpls/CK3dEntity.cpp

322 lines
11 KiB
C++
Raw Permalink Normal View History

2023-09-01 14:55:31 +08:00
#include "CK3dEntity.hpp"
#include "../CKStateChunk.hpp"
2023-09-19 22:32:07 +08:00
#include "../CKContext.hpp"
#include "CKMesh.hpp"
2023-09-01 14:55:31 +08:00
namespace LibCmo::CK2::ObjImpls {
2023-09-19 22:32:07 +08:00
CK3dEntity::CK3dEntity(CKContext* ctx, CK_ID ckid, CKSTRING name) :
CKRenderObject(ctx, ckid, name),
m_PotentialMeshes(), m_CurrentMesh(nullptr),
m_WorldMatrix(), m_ZOrder(0),
m_MoveableFlags(YYCC::EnumHelper::Merge(
2023-09-19 22:32:07 +08:00
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_PICKABLE,
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE,
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_RENDERCHANNELS,
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_INVERSEWORLDMATVALID
)),
2023-09-19 22:32:07 +08:00
m_3dEntityFlags(static_cast<CK_3DENTITY_FLAGS>(0)) {}
CK3dEntity::~CK3dEntity() {}
void CK3dEntity::CheckPreDeletion() {
CKRenderObject::CheckPreDeletion();
// check active mesh
if (m_CurrentMesh->IsToBeDeleted()) {
m_CurrentMesh = nullptr;
}
// check potential meshes
XContainer::NSXObjectPointerArray::PreDeletedCheck(m_PotentialMeshes, m_Context);
}
2023-09-01 14:55:31 +08:00
bool CK3dEntity::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) {
bool suc = CKRenderObject::Save(chunk, file, flags);
if (!suc) return false;
2023-10-02 11:04:00 +08:00
// write associated mesh data
if (m_CurrentMesh != nullptr || m_PotentialMeshes.size() != 0) {
chunk->WriteIdentifier(CK_STATESAVEFLAGS_3DENTITY::CK_STATESAVE_MESHS);
// write current mesh
chunk->WriteObjectPointer(m_CurrentMesh);
// write potential meshes
chunk->WriteXObjectPointerArray(m_PotentialMeshes);
}
// write core entity data
{
chunk->WriteIdentifier(CK_STATESAVEFLAGS_3DENTITY::CK_STATESAVE_3DENTITYNDATA);
// regulate self flag again
// MARK: originally we should check parent here.
// but we do not support parent and hierarchy feature, so we simply remove flag
YYCC::EnumHelper::Remove(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PARENTVALID);
2023-10-02 11:04:00 +08:00
// MARK: originally we should check grouped into CKPlace here.
// but we do not support CKPlace, so we simply remove this flag
YYCC::EnumHelper::Remove(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PLACEVALID);
2023-10-02 11:04:00 +08:00
// check z-order, if not zero, save it
if (m_ZOrder != 0) {
YYCC::EnumHelper::Add(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_ZORDERVALID);
2023-10-02 11:04:00 +08:00
} else {
YYCC::EnumHelper::Remove(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_ZORDERVALID);
2023-10-02 11:04:00 +08:00
}
// write 2 flags
chunk->WriteStruct(m_3dEntityFlags);
chunk->WriteStruct(m_MoveableFlags);
// write world matrix
chunk->WriteStruct(reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[0]));
chunk->WriteStruct(reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[1]));
chunk->WriteStruct(reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[2]));
chunk->WriteStruct(reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[3]));
// MARK: because we do not support Parent and CKPlace,
// and the IDA code also instruct that no need to write any data if no Parent or CKPlace.
// so we skip the Parent and CKPlace writing
// write z-order
if (m_ZOrder != 0) {
chunk->WriteStruct(m_ZOrder);
}
}
chunk->SetClassId(CK_CLASSID::CKCID_3DENTITY);
2023-09-01 14:55:31 +08:00
return true;
}
bool CK3dEntity::Load(CKStateChunk* chunk, CKFileVisitor* file) {
bool suc = CKRenderObject::Load(chunk, file);
if (!suc) return false;
2023-09-19 22:32:07 +08:00
// backup moveable flags
bool hasWorldAligned = YYCC::EnumHelper::Has(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_WORLDALIGNED);
2023-09-19 22:32:07 +08:00
// MARK: object animation is skipped
// read associated meshs data
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_3DENTITY::CK_STATESAVE_MESHS)) {
// MARK: I don't know why origianl code do not clear potential mesh list
// so I clear it in there.
m_PotentialMeshes.clear();
// read current mesh
2023-09-20 14:42:44 +08:00
CKObject* pendingMesh = nullptr;
chunk->ReadObjectPointer(pendingMesh);
if (pendingMesh != nullptr && pendingMesh->GetClassID() == CK_CLASSID::CKCID_MESH) {
m_CurrentMesh = static_cast<CKMesh*>(pendingMesh);
2023-09-19 22:32:07 +08:00
}
// read other meshs
XContainer::XObjectPointerArray potentials;
chunk->ReadXObjectPointerArray(potentials);
for (const auto& ptr : potentials) {
2023-09-20 10:49:32 +08:00
if (ptr == nullptr) continue;
2023-09-19 22:32:07 +08:00
XContainer::NSXObjectPointerArray::AddIfNotHere(m_PotentialMeshes, ptr);
}
2023-09-20 10:49:32 +08:00
// add current mesh to potential meshes
if (m_CurrentMesh != nullptr) {
XContainer::NSXObjectPointerArray::AddIfNotHere(m_PotentialMeshes, m_CurrentMesh);
}
2023-09-19 22:32:07 +08:00
}
// read core entity data
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_3DENTITY::CK_STATESAVE_3DENTITYNDATA)) {
// read 2 flags
chunk->ReadStruct(m_3dEntityFlags);
chunk->ReadStruct(m_MoveableFlags);
// remove some properties
YYCC::EnumHelper::Remove(m_3dEntityFlags,
2023-09-19 22:32:07 +08:00
CK_3DENTITY_FLAGS::CK_3DENTITY_UPDATELASTFRAME,
CK_3DENTITY_FLAGS::CK_3DENTITY_RESERVED0
);
YYCC::EnumHelper::Remove(m_MoveableFlags,
2023-09-19 22:32:07 +08:00
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_RESERVED2,
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_STENCILONLY,
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_DONTUPDATEFROMPARENT,
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_INVERSEWORLDMATVALID,
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HASMOVED,
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_BOXVALID,
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_USERBOX,
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_UPTODATE
);
2023-09-19 22:32:07 +08:00
if (hasWorldAligned) {
YYCC::EnumHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_WORLDALIGNED);
2023-09-19 22:32:07 +08:00
}
// if order render first
if (YYCC::EnumHelper::Has(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_RENDERFIRST)) {
2023-09-19 22:32:07 +08:00
m_ZOrder = 10000;
}
// read matrix
2023-09-20 10:49:32 +08:00
// reset
m_WorldMatrix.ResetToIdentity();
2023-09-19 22:32:07 +08:00
// force read as vector3
chunk->ReadStruct(reinterpret_cast<VxMath::VxVector3*>(&m_WorldMatrix[0]));
chunk->ReadStruct(reinterpret_cast<VxMath::VxVector3*>(&m_WorldMatrix[1]));
chunk->ReadStruct(reinterpret_cast<VxMath::VxVector3*>(&m_WorldMatrix[2]));
chunk->ReadStruct(reinterpret_cast<VxMath::VxVector3*>(&m_WorldMatrix[3]));
// MARK: check right-hand?
// I don't know how it checked, just reinterpter IDA code.
VxMath::VxVector3 col2(*reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[2])),
col1(*reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[1])),
col0(*reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[0]));
VxMath::VxVector3 crossProduct = VxMath::NSVxVector::CrossProduct(col0, col1);
CKFLOAT dotProduct = VxMath::NSVxVector::DotProduct(crossProduct, col2);
if (dotProduct >= 0.0f) {
YYCC::EnumHelper::Remove(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_INDIRECTMATRIX);
2023-09-19 22:32:07 +08:00
} else {
YYCC::EnumHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_INDIRECTMATRIX);
2023-09-19 22:32:07 +08:00
}
// copy visible data
// process direct visible
if (YYCC::EnumHelper::Has(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE)) {
YYCC::EnumHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE);
2023-09-20 13:13:08 +08:00
} else {
YYCC::EnumHelper::Remove(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE);
2023-09-20 13:13:08 +08:00
}
2023-09-19 22:32:07 +08:00
// process indirect visible
if (YYCC::EnumHelper::Has(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_HIERACHICALHIDE)) {
YYCC::EnumHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HIERARCHICALHIDE);
2023-09-19 22:32:07 +08:00
} else {
YYCC::EnumHelper::Remove(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HIERARCHICALHIDE);
2023-09-19 22:32:07 +08:00
}
// read associated CKPlace
if (YYCC::EnumHelper::Has(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PLACEVALID)) {
2023-09-19 22:32:07 +08:00
// MARK: we drop the support of CKPlace.
// so we just read it and skip it.
CK_ID placeid;
chunk->ReadObjectID(placeid);
2023-09-23 15:55:57 +08:00
// and remove this flag
YYCC::EnumHelper::Remove(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PLACEVALID);
2023-09-19 22:32:07 +08:00
}
// read parent
if (YYCC::EnumHelper::Has(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PARENTVALID)) {
2023-09-19 22:32:07 +08:00
// MAKR: we drop the support of parent and the whole 3dentity hierarchy system
// we ignore this field.
CK_ID parentid;
chunk->ReadObjectID(parentid);
2023-09-23 15:55:57 +08:00
// and remove this flag
YYCC::EnumHelper::Remove(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PARENTVALID);
2023-09-19 22:32:07 +08:00
}
// read priority (non-zero zorder)
if (YYCC::EnumHelper::Has(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_ZORDERVALID)) {
2023-09-19 22:32:07 +08:00
chunk->ReadStruct(m_ZOrder);
}
}
// MARK: compatibility alternative core data read code removed because I don't need them
2023-09-20 10:49:32 +08:00
// MARK: skin and bone are skipped.
2023-09-19 22:32:07 +08:00
2023-09-01 14:55:31 +08:00
return true;
}
2023-09-20 13:13:08 +08:00
void CK3dEntity::Show(CK_OBJECT_SHOWOPTION show) {
CKObject::Show(show);
YYCC::EnumHelper::Remove(m_MoveableFlags,
2023-09-20 13:13:08 +08:00
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE,
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HIERARCHICALHIDE
);
2023-09-20 13:13:08 +08:00
switch (show) {
case CK_OBJECT_SHOWOPTION::CKSHOW:
YYCC::EnumHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE);
2023-09-20 13:13:08 +08:00
break;
case CK_OBJECT_SHOWOPTION::CKHIERARCHICALHIDE:
YYCC::EnumHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HIERARCHICALHIDE);
2023-09-20 13:13:08 +08:00
break;
case CK_OBJECT_SHOWOPTION::CKHIDE:
break;
}
}
bool CK3dEntity::IsVisible() const {
// MARK: originally there is a call to this->IsHiddenByParent.
// but we drop the support of parent, so we drop that condition.
return CKObject::IsVisible();
}
#pragma region Misc Oper
const VxMath::VxMatrix& CK3dEntity::GetWorldMatrix() const {
return m_WorldMatrix;
}
void CK3dEntity::SetWorldMatrix(const VxMath::VxMatrix& mat) {
m_WorldMatrix = mat;
}
CK_3DENTITY_FLAGS CK3dEntity::GetEntityFlags() const {
return m_3dEntityFlags;
}
void CK3dEntity::SetEntityFlags(CK_3DENTITY_FLAGS flags) {
m_3dEntityFlags = flags;
}
VxMath::VX_MOVEABLE_FLAGS CK3dEntity::GetMoveableFlags() const {
return m_MoveableFlags;
}
void CK3dEntity::SetMoveableFlags(VxMath::VX_MOVEABLE_FLAGS flags) {
m_MoveableFlags = flags;
}
CKDWORD CK3dEntity::GetZOrder() const {
return m_ZOrder;
}
void CK3dEntity::SetZOrder(CKDWORD ord) {
m_ZOrder = ord;
}
#pragma endregion
#pragma region Mesh Oper
void CK3dEntity::AddPotentialMesh(CKMesh* mesh) {
XContainer::NSXObjectPointerArray::AddIfNotHere(m_PotentialMeshes, mesh);
}
void CK3dEntity::RemovePotentialMesh(CKMesh* mesh) {
std::erase(m_PotentialMeshes, mesh);
}
CKDWORD CK3dEntity::GetPotentialMeshCount() const {
return static_cast<CKDWORD>(m_PotentialMeshes.size());
}
CKMesh* CK3dEntity::GetPotentialMesh(CKDWORD idx) const {
if (idx >= m_PotentialMeshes.size()) return nullptr;
return static_cast<CKMesh*>(m_PotentialMeshes[idx]);
}
CKMesh* CK3dEntity::GetCurrentMesh() const {
return m_CurrentMesh;
}
void CK3dEntity::SetCurrentMesh(CKMesh* mesh) {
m_CurrentMesh = mesh;
2023-10-08 20:56:29 +08:00
if (mesh != nullptr) {
AddPotentialMesh(mesh);
}
}
#pragma endregion
2023-09-01 14:55:31 +08:00
}