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(EnumsHelper::Merge({
|
|
|
|
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
|
|
|
|
})),
|
|
|
|
m_3dEntityFlags(static_cast<CK_3DENTITY_FLAGS>(0)) {}
|
|
|
|
|
|
|
|
CK3dEntity::~CK3dEntity() {}
|
|
|
|
|
2023-09-20 15:25:43 +08:00
|
|
|
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
|
|
|
|
EnumsHelper::Rm(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PARENTVALID);
|
|
|
|
// MARK: originally we should check grouped into CKPlace here.
|
|
|
|
// but we do not support CKPlace, so we simply remove this flag
|
|
|
|
EnumsHelper::Rm(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PLACEVALID);
|
|
|
|
// check z-order, if not zero, save it
|
|
|
|
if (m_ZOrder != 0) {
|
|
|
|
EnumsHelper::Add(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_ZORDERVALID);
|
|
|
|
} else {
|
|
|
|
EnumsHelper::Rm(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_ZORDERVALID);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-10-02 11:32:18 +08:00
|
|
|
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 = EnumsHelper::Has(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_WORLDALIGNED);
|
|
|
|
|
|
|
|
// 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
|
|
|
|
EnumsHelper::Rm(m_3dEntityFlags, EnumsHelper::Merge({
|
|
|
|
CK_3DENTITY_FLAGS::CK_3DENTITY_UPDATELASTFRAME,
|
|
|
|
CK_3DENTITY_FLAGS::CK_3DENTITY_RESERVED0
|
|
|
|
}));
|
|
|
|
EnumsHelper::Rm(m_MoveableFlags, EnumsHelper::Merge({
|
|
|
|
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
|
|
|
|
}));
|
|
|
|
if (hasWorldAligned) {
|
|
|
|
EnumsHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_WORLDALIGNED);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if order render first
|
|
|
|
if (EnumsHelper::Has(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_RENDERFIRST)) {
|
|
|
|
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) {
|
|
|
|
EnumsHelper::Rm(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_INDIRECTMATRIX);
|
|
|
|
} else {
|
|
|
|
EnumsHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_INDIRECTMATRIX);
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy visible data
|
|
|
|
// process direct visible
|
2023-09-20 13:13:08 +08:00
|
|
|
if (EnumsHelper::Has(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE)) {
|
|
|
|
EnumsHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE);
|
|
|
|
} else {
|
|
|
|
EnumsHelper::Rm(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE);
|
|
|
|
}
|
2023-09-19 22:32:07 +08:00
|
|
|
// process indirect visible
|
2023-09-20 13:13:08 +08:00
|
|
|
if (EnumsHelper::Has(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_HIERACHICALHIDE)) {
|
2023-09-19 22:32:07 +08:00
|
|
|
EnumsHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HIERARCHICALHIDE);
|
|
|
|
} else {
|
|
|
|
EnumsHelper::Rm(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HIERARCHICALHIDE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// read associated CKPlace
|
|
|
|
if (EnumsHelper::Has(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PLACEVALID)) {
|
|
|
|
// 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
|
|
|
|
EnumsHelper::Rm(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PLACEVALID);
|
2023-09-19 22:32:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// read parent
|
|
|
|
if (EnumsHelper::Has(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PARENTVALID)) {
|
|
|
|
// 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
|
|
|
|
EnumsHelper::Rm(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PARENTVALID);
|
2023-09-19 22:32:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// read priority (non-zero zorder)
|
|
|
|
if (EnumsHelper::Has(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_ZORDERVALID)) {
|
|
|
|
chunk->ReadStruct(m_ZOrder);
|
|
|
|
}
|
|
|
|
|
2023-09-30 16:01:39 +08:00
|
|
|
}
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
EnumsHelper::Rm(m_MoveableFlags, EnumsHelper::Merge({
|
|
|
|
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE,
|
|
|
|
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HIERARCHICALHIDE,
|
|
|
|
}));
|
|
|
|
switch (show) {
|
|
|
|
case CK_OBJECT_SHOWOPTION::CKSHOW:
|
|
|
|
EnumsHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE);
|
|
|
|
break;
|
|
|
|
case CK_OBJECT_SHOWOPTION::CKHIERARCHICALHIDE:
|
|
|
|
EnumsHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HIERARCHICALHIDE);
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2023-09-22 14:48:45 +08:00
|
|
|
#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);
|
|
|
|
}
|
2023-09-22 14:48:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma endregion
|
|
|
|
|
|
|
|
|
2023-09-01 14:55:31 +08:00
|
|
|
}
|