diff --git a/LibCmo/CK2/CKStateChunk.hpp b/LibCmo/CK2/CKStateChunk.hpp index 0cb6736..f66fe15 100644 --- a/LibCmo/CK2/CKStateChunk.hpp +++ b/LibCmo/CK2/CKStateChunk.hpp @@ -312,9 +312,13 @@ namespace LibCmo::CK2 { /* ========== Complex Data Read Functions ==========*/ bool ReadObjectID(CK_ID* id); + bool ReadObjectPointer(ObjImpls::CKObject** obj); inline bool ReadObjectID(CK_ID& id) { return ReadObjectID(&id); } + inline bool ReadObjectPointer(ObjImpls::CKObject*& id) { + return ReadObjectPointer(&id); + } bool ReadManagerInt(CKGUID* guid, CKINT* intval); inline bool ReadManagerInt(CKGUID& guid, CKINT& intval) { @@ -424,8 +428,8 @@ namespace LibCmo::CK2 { /// /// /// - bool ReadObjectIDSequence(XContainer::XArray* ls); - inline bool ReadObjectIDSequence(XContainer::XArray& ls) { + bool ReadObjectIDSequence(XContainer::XObjectArray* ls); + inline bool ReadObjectIDSequence(XContainer::XObjectArray& ls) { return ReadObjectIDSequence(&ls); } diff --git a/LibCmo/CK2/CKStateChunkReader.cpp b/LibCmo/CK2/CKStateChunkReader.cpp index 4223aab..78ca5bb 100644 --- a/LibCmo/CK2/CKStateChunkReader.cpp +++ b/LibCmo/CK2/CKStateChunkReader.cpp @@ -183,6 +183,17 @@ namespace LibCmo::CK2 { return false; } + bool CKStateChunk::ReadObjectPointer(ObjImpls::CKObject** obj) { + CK_ID cache; + bool ret = ReadObjectID(&cache); + if (ret) { + *obj = m_BindContext->GetObject(cache); + } else { + *obj = nullptr; + } + return ret; + } + bool CKStateChunk::ReadManagerInt(CKGUID* guid, CKINT* intval) { if (guid == nullptr || intval == nullptr) return false; @@ -374,7 +385,7 @@ namespace LibCmo::CK2 { /* ========== Sequence Functions ==========*/ - bool CKStateChunk::ReadObjectIDSequence(XContainer::XArray* ls) { + bool CKStateChunk::ReadObjectIDSequence(XContainer::XObjectArray* ls) { if (ls == nullptr) return false; ls->clear(); diff --git a/LibCmo/CK2/ObjImpls/CK3dEntity.cpp b/LibCmo/CK2/ObjImpls/CK3dEntity.cpp index f245c7f..1feafd0 100644 --- a/LibCmo/CK2/ObjImpls/CK3dEntity.cpp +++ b/LibCmo/CK2/ObjImpls/CK3dEntity.cpp @@ -42,11 +42,10 @@ namespace LibCmo::CK2::ObjImpls { 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); + CKObject* pendingMesh = nullptr; + chunk->ReadObjectPointer(pendingMesh); + if (pendingMesh != nullptr && pendingMesh->GetClassID() == CK_CLASSID::CKCID_MESH) { + m_CurrentMesh = static_cast(pendingMesh); } // read other meshs diff --git a/LibCmo/CK2/ObjImpls/CKMaterial.cpp b/LibCmo/CK2/ObjImpls/CKMaterial.cpp index 6cb5b22..ba46dbf 100644 --- a/LibCmo/CK2/ObjImpls/CKMaterial.cpp +++ b/LibCmo/CK2/ObjImpls/CKMaterial.cpp @@ -70,9 +70,8 @@ namespace LibCmo::CK2::ObjImpls { chunk->ReadStruct(m_SpecularPower); // main texture - CK_ID objid; - chunk->ReadObjectID(objid); - CKObject* tex = m_Context->GetObject(objid); + CKObject* tex = nullptr; + chunk->ReadObjectPointer(tex); if (tex != nullptr && tex->GetClassID() == CK_CLASSID::CKCID_TEXTURE) { m_Textures[0] = static_cast(tex); } @@ -125,12 +124,10 @@ namespace LibCmo::CK2::ObjImpls { // extra texture data if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA2)) { // read 3 extra texture - CK_ID objid; CKObject* tex = nullptr; for (size_t i = 1; i < 4; ++i) { - chunk->ReadObjectID(objid); - tex = m_Context->GetObject(objid); + chunk->ReadObjectPointer(tex); if (tex != nullptr && tex->GetClassID() == CK_CLASSID::CKCID_TEXTURE) { m_Textures[i] = static_cast(tex); } diff --git a/LibCmo/CK2/ObjImpls/CKMesh.cpp b/LibCmo/CK2/ObjImpls/CKMesh.cpp index ee2ead7..298c3a6 100644 --- a/LibCmo/CK2/ObjImpls/CKMesh.cpp +++ b/LibCmo/CK2/ObjImpls/CKMesh.cpp @@ -6,7 +6,32 @@ namespace LibCmo::CK2::ObjImpls { CKMesh::CKMesh(CKContext* ctx, CK_ID ckid, CKSTRING name) : - CKBeObject(ctx, ckid, name) {} + CKBeObject(ctx, ckid, name), + // init vertex + m_VertexCount(0), + m_VertexPosition(), m_VertexNormal(), m_VertexUV(), + m_VertexColor(), m_VertexSpecularColor(), + m_VertexWeight(), m_NoVertexWeight(true), + // init mtl slots + m_MtlSlotCount(0), + m_MaterialSlot(), + // init face data + m_FaceCount(0), + m_FaceIndices(), m_FaceMtlIndex(), m_Faces(), + // init line + m_LineCount(0), + m_LineIndices(), + // init mtl channels + m_MtlChannelCount(0), + m_MaterialChannels(), + // init flags + m_Flags(EnumsHelper::Merge({ + VxMath::VXMESH_FLAGS::VXMESH_FORCETRANSPARENCY, + VxMath::VXMESH_FLAGS::VXMESH_HASTRANSPARENCY + })) { + // set visible in default + EnumsHelper::Add(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE); + } CKMesh::~CKMesh() {} @@ -51,17 +76,15 @@ namespace LibCmo::CK2::ObjImpls { SetMaterialSlotCount(mtlCount); // read slot - CK_ID mtlId; CKDWORD ph; - CKObject* objptr; + CKObject* objptr = nullptr; for (auto& mtlSlot : m_MaterialSlot) { // read id - chunk->ReadObjectID(mtlId); + chunk->ReadObjectPointer(objptr); // and read a place holder idk what the fuck it is. chunk->ReadStruct(ph); - // try getting object pointer and assign - objptr = m_Context->GetObject(mtlId); + // try to assign if (objptr != nullptr && objptr->GetClassID() == CK_CLASSID::CKCID_MATERIAL) { mtlSlot = static_cast(objptr); } else { @@ -220,6 +243,99 @@ namespace LibCmo::CK2::ObjImpls { BuildFaceNormals(); } + // read material channels + if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHCHANNELS)) { + // read size and resize it + CKDWORD chlSize; + chunk->ReadStruct(chlSize); + SetMtlChannelCount(chlSize); + + for (auto& chl : m_MaterialChannels) { + // read material + CKObject* mtlobj = nullptr; + chunk->ReadObjectPointer(mtlobj); + if (mtlobj != nullptr && mtlobj->GetClassID() == CK_CLASSID::CKCID_MATERIAL) { + chl.m_Material = static_cast(mtlobj); + } + + // read flags and call function to make sure a custom uv can be created if existed. + chunk->ReadStruct(chl.m_Flags); + SyncVertexCountToMtlChannel(); + + // read blend modes + chunk->ReadStruct(chl.m_SourceBlend); + chunk->ReadStruct(chl.m_DestBlend); + + // read custom vertex + CKDWORD uvcount; + chunk->ReadStruct(uvcount); + if (uvcount != 0) { + // make sure no overflow + uvcount = std::min(uvcount, chl.m_CustomUV.size()); + + CKDWORD bufsize = uvcount * CKSizeof(VxMath::VxVector2); + auto locker = chunk->LockReadBufferWrapper(bufsize); + std::memcpy(chl.m_CustomUV.data(), locker.get(), bufsize); + locker.reset(); + } + } + } + + // vertex weight + CKDWORD weightSize; + m_NoVertexWeight = true; + if (chunk->SeekIdentifierAndReturnSize(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHWEIGHTS, &weightSize)) { + // set it has + m_NoVertexWeight = false; + // set count + CKDWORD weightCount; + chunk->ReadStruct(weightCount); + + if (weightSize > CKSizeof(CKFLOAT)) { + // a float series + // read as a copy, to make sure no memory overflow + // because i couldn't understand how original CKMesh operate vertex weight count + // seperated with vertex count. + auto buf = chunk->ReadBufferWrapper(); + CKDWORD bufsize = std::min(buf.get_deleter().GetBufferSize(), static_cast(m_VertexWeight.size()) * CKSizeof(CKFLOAT)); + std::memcpy(m_VertexWeight.data(), buf.get(), bufsize); + buf.reset(); + + } else { + // a single float + CKFLOAT single; + chunk->ReadStruct(single); + + for (auto& weight : m_VertexWeight) { + weight = single; + } + } + } + + // face mask + if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHFACECHANMASK)) { + // 2 face mask (2 WORD) are compressed into a single DWORD. + // and if there is a remained WORD, read it as a single WORD. + // according to little endian, the actually stored data is just the mask placed + // one by one. + // so we just need to allocated it directly + + // read mask count, and limit it to face count + CKDWORD maskCount; + chunk->ReadStruct(maskCount); + maskCount = std::min(maskCount, m_FaceCount); + + auto locker = chunk->LockReadBufferWrapper(maskCount * CKSizeof(CKWORD)); + const CKWORD* rawptr = static_cast(locker.get()); + for (auto& f : m_Faces) { + f.m_ChannelMask = *rawptr; + ++rawptr; + } + locker.reset(); + } + + // MARK: progressive mesh data is dropper. + return true; } @@ -240,6 +356,8 @@ namespace LibCmo::CK2::ObjImpls { SetMtlChannelCount(0); // then clear other SetVertexCount(0); + m_NoVertexWeight = true; + SetMaterialSlotCount(0); SetFaceCount(0); SetLineCount(0); @@ -406,12 +524,16 @@ namespace LibCmo::CK2::ObjImpls { } void CKMesh::SetMtlChannelCount(CKDWORD count) { + // backup old count + CKDWORD oldcount = m_MtlChannelCount; + + // set and resize m_MtlChannelCount = count; m_MaterialChannels.resize(count); // sync mask to each face. // each face accept all mask in default - SyncMtlChannelToFaceMask(); + SyncMtlChannelToFaceMask(oldcount, count); } CKMaterial** CKMesh::GetMtlChannelMaterials(CKDWORD& stride) { @@ -457,8 +579,20 @@ namespace LibCmo::CK2::ObjImpls { } } - void CKMesh::SyncMtlChannelToFaceMask() { - CKWORD mask = static_cast(~(0xFFFF << m_MtlChannelCount)); + void CKMesh::SyncMtlChannelToFaceMask(CKDWORD oldsize, CKDWORD newsize) { + // use oldsize and newsize to build mask + if (oldsize == newsize) return; + + CKWORD mask = 0xFFFF; + if (oldsize > newsize) { + // channels shrinks + // set already removed bits to 1 + mask = static_cast(~(0xFFFF << newsize)); + } else { + // channels expand + // set new added bits to 1 + mask = static_cast(~(0xFFFF << oldsize)); + } for (auto& face : m_Faces) { face.m_ChannelMask |= mask; } diff --git a/LibCmo/CK2/ObjImpls/CKMesh.hpp b/LibCmo/CK2/ObjImpls/CKMesh.hpp index 609d970..fc1a7f9 100644 --- a/LibCmo/CK2/ObjImpls/CKMesh.hpp +++ b/LibCmo/CK2/ObjImpls/CKMesh.hpp @@ -30,7 +30,7 @@ namespace LibCmo::CK2::ObjImpls { void BuildNormals(); void BuildFaceNormals(); - // ===== Line Section ===== + // ===== Vertex Section ===== public: CKDWORD GetVertexCount(); void SetVertexCount(CKDWORD count); @@ -76,7 +76,7 @@ namespace LibCmo::CK2::ObjImpls { protected: // 2 sync functions served for material channels. void SyncVertexCountToMtlChannel(); // setup material channel custom uv properly - void SyncMtlChannelToFaceMask(); // request all face accept all material channels. + void SyncMtlChannelToFaceMask(CKDWORD oldsize, CKDWORD newsize); // request all face accept all material channels. protected: enum class VertexSaveFlags : CKDWORD { @@ -123,6 +123,7 @@ namespace LibCmo::CK2::ObjImpls { XContainer::XArray m_VertexColor; XContainer::XArray m_VertexSpecularColor; XContainer::XArray m_VertexWeight; + bool m_NoVertexWeight; // true if there is actually no vertex weight XContainer::XArray m_MaterialSlot;