diff --git a/LibCmo/CK2/ObjImpls/CK3dEntity.cpp b/LibCmo/CK2/ObjImpls/CK3dEntity.cpp index 5dfa551..80836b1 100644 --- a/LibCmo/CK2/ObjImpls/CK3dEntity.cpp +++ b/LibCmo/CK2/ObjImpls/CK3dEntity.cpp @@ -1,8 +1,24 @@ #include "CK3dEntity.hpp" #include "../CKStateChunk.hpp" +#include "../CKContext.hpp" +#include "CKMesh.hpp" namespace LibCmo::CK2::ObjImpls { + 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(0)) {} + + CK3dEntity::~CK3dEntity() {} + bool CK3dEntity::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) { bool suc = CKRenderObject::Save(chunk, file, flags); if (!suc) return false; @@ -14,6 +30,117 @@ namespace LibCmo::CK2::ObjImpls { bool suc = CKRenderObject::Load(chunk, file); if (!suc) return false; + // 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 + CK_ID currentMeshId; + chunk->ReadObjectID(currentMeshId); + CKObject* findobj = m_Context->GetObject(currentMeshId); + if (findobj != nullptr && findobj->GetClassID() == CK_CLASSID::CKCID_MESH) { + m_CurrentMesh = static_cast(findobj); + } + + // read other meshs + XContainer::XObjectPointerArray potentials; + chunk->ReadXObjectPointerArray(potentials); + for (const auto& ptr : potentials) { + XContainer::NSXObjectPointerArray::AddIfNotHere(m_PotentialMeshes, ptr); + } + + } + + // 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 + // force read as vector3 + chunk->ReadStruct(reinterpret_cast(&m_WorldMatrix[0])); + chunk->ReadStruct(reinterpret_cast(&m_WorldMatrix[1])); + chunk->ReadStruct(reinterpret_cast(&m_WorldMatrix[2])); + chunk->ReadStruct(reinterpret_cast(&m_WorldMatrix[3])); + // MARK: check right-hand? + // I don't know how it checked, just reinterpter IDA code. + VxMath::VxVector3 col2(*reinterpret_cast(&m_WorldMatrix[2])), + col1(*reinterpret_cast(&m_WorldMatrix[1])), + col0(*reinterpret_cast(&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 + // todo add if visible + // and set or unset VX_MOVEABLE_VISIBLE + + // process indirect visible + if (EnumsHelper::Has(m_ObjectFlags, CK_OBJECT_FLAGS::CKBEHAVIORLINK_ACTIVATEDLASTFRAME)) { + 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); + } + + // 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); + } + + // read priority (non-zero zorder) + if (EnumsHelper::Has(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_ZORDERVALID)) { + chunk->ReadStruct(m_ZOrder); + } + + } + return true; } diff --git a/LibCmo/CK2/ObjImpls/CK3dEntity.hpp b/LibCmo/CK2/ObjImpls/CK3dEntity.hpp index f9ac37d..1d17630 100644 --- a/LibCmo/CK2/ObjImpls/CK3dEntity.hpp +++ b/LibCmo/CK2/ObjImpls/CK3dEntity.hpp @@ -7,11 +7,8 @@ namespace LibCmo::CK2::ObjImpls { class CK3dEntity : public CKRenderObject { public: - CK3dEntity(CKContext* ctx, CK_ID ckid, CKSTRING name) : - CKRenderObject(ctx, ckid, name), - m_Meshes(), m_CurrentMesh(nullptr), m_WorldMatrix() - {} - virtual ~CK3dEntity() {} + CK3dEntity(CKContext* ctx, CK_ID ckid, CKSTRING name); + virtual ~CK3dEntity(); LIBCMO_DISABLE_COPY_MOVE(CK3dEntity); virtual CK_CLASSID GetClassID(void) override { @@ -23,10 +20,14 @@ namespace LibCmo::CK2::ObjImpls { //virtual void PostLoad() override; protected: - XContainer::XObjectPointerArray m_Meshes; + XContainer::XObjectPointerArray m_PotentialMeshes; CKMesh* m_CurrentMesh; VxMath::VxMatrix m_WorldMatrix; + CKDWORD m_ZOrder; // replace the whole heavy CKSceneGraphNode + VxMath::VX_MOVEABLE_FLAGS m_MoveableFlags; + CK_3DENTITY_FLAGS m_3dEntityFlags; + }; } diff --git a/LibCmo/VxMath/VxTypes.hpp b/LibCmo/VxMath/VxTypes.hpp index ca17276..1dce618 100644 --- a/LibCmo/VxMath/VxTypes.hpp +++ b/LibCmo/VxMath/VxTypes.hpp @@ -371,16 +371,22 @@ namespace LibCmo::VxMath { CKFLOAT m_Data[4][4]; VxMatrix() : m_Data() { - std::memset(m_Data, 0, sizeof(m_Data)); - m_Data[0][0] = m_Data[1][1] = m_Data[2][2] = m_Data[3][3] = 1.0f; + ResetToIdentity(); } VxMatrix(CKFLOAT m[4][4]) : m_Data() { std::memcpy(m_Data, m, sizeof(m_Data)); } LIBCMO_DEFAULT_COPY_MOVE(VxMatrix); - + void ResetToIdentity() { + std::memset(m_Data, 0, sizeof(m_Data)); + m_Data[0][0] = m_Data[1][1] = m_Data[2][2] = m_Data[3][3] = 1.0f; + } VxVector4& operator[](size_t i) { if (i >= 4) i = 0; return *(reinterpret_cast(m_Data) + i); } + const VxVector4& operator[](size_t i) const { + if (i >= 4) i = 0; + return *(reinterpret_cast(m_Data) + i); + } bool operator==(const VxMatrix& rhs) const { return std::memcmp(m_Data, rhs.m_Data, sizeof(m_Data)) == 0; } diff --git a/LibCmo/XContainer/XTypes.cpp b/LibCmo/XContainer/XTypes.cpp index e9ff9e0..0d2247e 100644 --- a/LibCmo/XContainer/XTypes.cpp +++ b/LibCmo/XContainer/XTypes.cpp @@ -3,6 +3,7 @@ #include "../CK2/MgrImpls/CKObjectManager.hpp" #include "../CK2/ObjImpls/CKObject.hpp" #include +#include namespace LibCmo::XContainer { @@ -143,6 +144,13 @@ namespace LibCmo::XContainer { } namespace NSXObjectPointerArray { + void AddIfNotHere(XObjectPointerArray& objarray, CK2::ObjImpls::CKObject* const obj) { + auto finder = std::find(objarray.begin(), objarray.end(), obj); + if (finder == objarray.end()) { + objarray.emplace_back(obj); + } + } + void PreDeletedCheck(XObjectPointerArray& objarray, CK2::CKContext* ctx) { if (ctx == nullptr) return; std::erase_if(objarray, [ctx](CK2::ObjImpls::CKObject* const& item) -> bool { diff --git a/LibCmo/XContainer/XTypes.hpp b/LibCmo/XContainer/XTypes.hpp index d7f33f5..75ba78d 100644 --- a/LibCmo/XContainer/XTypes.hpp +++ b/LibCmo/XContainer/XTypes.hpp @@ -180,6 +180,13 @@ namespace LibCmo::XContainer { namespace NSXObjectPointerArray { + /** + * @brief Add object pointer if it is not list. + * @param objarray + * @param obj + */ + void AddIfNotHere(XObjectPointerArray& objarray, CK2::ObjImpls::CKObject* const obj); + /** * @brief Check Object pointer validation and remove invalid pointers before deletion. * @param objarray diff --git a/Unvirt/StructFormatter.cpp b/Unvirt/StructFormatter.cpp index 45b8966..a6ccf97 100644 --- a/Unvirt/StructFormatter.cpp +++ b/Unvirt/StructFormatter.cpp @@ -118,37 +118,37 @@ namespace Unvirt::StructFormatter { PrintCKBeObjectDetail(obj); fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("CKMesh\n")), stdout); - fputs("Vertex:\n", stdout); fprintf(stdout, "Vertex Count: %" PRIuCKDWORD "\n", obj->GetVertexCount()); + fputs("Type\tAddress\tSize\n", stdout); - fputs("VertexPositions: ", stdout); + fputs("Positions\t", stdout); PrintPointer(obj->GetVertexPositions()); - fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetVertexCount() * CKSizeof(LibCmo::VxMath::VxVector3)); - fputs("VertexNormals: ", stdout); + fprintf(stdout, "\t0x%" PRIxCKDWORD " bytes\n", obj->GetVertexCount() * CKSizeof(LibCmo::VxMath::VxVector3)); + fputs("Normals\t", stdout); PrintPointer(obj->GetVertexNormals()); - fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetVertexCount() * CKSizeof(LibCmo::VxMath::VxVector3)); - fputs("VertexUVs: ", stdout); + fprintf(stdout, "\t0x%" PRIxCKDWORD " bytes\n", obj->GetVertexCount() * CKSizeof(LibCmo::VxMath::VxVector3)); + fputs("UVs\t", stdout); PrintPointer(obj->GetVertexUVs()); - fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetVertexCount() * CKSizeof(LibCmo::VxMath::VxVector2)); - fputs("VertexColors: ", stdout); + fprintf(stdout, "\t0x%" PRIxCKDWORD " bytes\n", obj->GetVertexCount() * CKSizeof(LibCmo::VxMath::VxVector2)); + fputs("Colors\t", stdout); PrintPointer(obj->GetVertexColors()); - fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetVertexCount() * CKSizeof(LibCmo::CKDWORD)); - fputs("VertexSpecularColors: ", stdout); + fprintf(stdout, "\t0x%" PRIxCKDWORD " bytes\n", obj->GetVertexCount() * CKSizeof(LibCmo::CKDWORD)); + fputs("SpecularColors\t", stdout); PrintPointer(obj->GetVertexSpecularColors()); - fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetVertexCount() * CKSizeof(LibCmo::CKDWORD)); - fputs("VertexWeights: ", stdout); + fprintf(stdout, "\t0x%" PRIxCKDWORD " bytes\n", obj->GetVertexCount() * CKSizeof(LibCmo::CKDWORD)); + fputs("Weights\t", stdout); PrintPointer(obj->GetVertexWeights()); - fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetVertexCount() * CKSizeof(LibCmo::CKFLOAT)); + fprintf(stdout, "\t0x%" PRIxCKDWORD " bytes\n", obj->GetVertexCount() * CKSizeof(LibCmo::CKFLOAT)); - fputs("Face:\n", stdout); fprintf(stdout, "Face Count: %" PRIuCKDWORD "\n", obj->GetFaceCount()); + fputs("Type\tAddress\tSize\n", stdout); - fputs("FaceIndices: ", stdout); + fputs("Indices\t", stdout); PrintPointer(obj->GetFaceIndices()); - fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetFaceCount() * 3 * CKSizeof(LibCmo::CKWORD)); - fputs("FaceMaterialSlotIndexs: ", stdout); + fprintf(stdout, "\t0x%" PRIxCKDWORD " bytes\n", obj->GetFaceCount() * 3 * CKSizeof(LibCmo::CKWORD)); + fputs("MaterialSlotIndexs\t", stdout); PrintPointer(obj->GetFaceMaterialSlotIndexs()); - fprintf(stdout, " (0x%" PRIxCKDWORD " bytes)\n", obj->GetFaceCount() * CKSizeof(LibCmo::CKWORD)); + fprintf(stdout, "\t0x%" PRIxCKDWORD " bytes\n", obj->GetFaceCount() * CKSizeof(LibCmo::CKWORD)); }