From 7b40c6447095c673a0284571fa870a321c88c5a1 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Wed, 4 Feb 2026 20:46:04 +0800 Subject: [PATCH] feat: add more rules in BMapInspector --- Ballance/BMapInspector/CMakeLists.txt | 2 + Ballance/BMapInspector/Rule.cpp | 7 ++- Ballance/BMapInspector/Rule/GpRules.cpp | 50 ++++++++++--------- Ballance/BMapInspector/Rule/GpRules.hpp | 55 +++++++++++---------- Ballance/BMapInspector/Rule/SOneRules.cpp | 39 +++++++++++++++ Ballance/BMapInspector/Rule/SOneRules.hpp | 23 +++++++++ Ballance/BMapInspector/Rule/Shared.cpp | 33 +++++++++---- Ballance/BMapInspector/Rule/Shared.hpp | 15 ++++-- Ballance/BMapInspector/Rule/YYCRules.cpp | 58 +++++++++++++++++++++-- Ballance/BMapInspector/Rule/YYCRules.hpp | 19 +++++++- 10 files changed, 234 insertions(+), 67 deletions(-) create mode 100644 Ballance/BMapInspector/Rule/SOneRules.cpp create mode 100644 Ballance/BMapInspector/Rule/SOneRules.hpp diff --git a/Ballance/BMapInspector/CMakeLists.txt b/Ballance/BMapInspector/CMakeLists.txt index f88eb6f..ffde74e 100644 --- a/Ballance/BMapInspector/CMakeLists.txt +++ b/Ballance/BMapInspector/CMakeLists.txt @@ -17,6 +17,7 @@ PRIVATE Rule/YYCRules.cpp Rule/ZZQRules.cpp Rule/BBugRules.cpp + Rule/SOneRules.cpp ) # Setup headers target_sources(BMapInspector @@ -36,6 +37,7 @@ FILES Rule/YYCRules.hpp Rule/ZZQRules.hpp Rule/BBugRules.hpp + Rule/SOneRules.hpp ) # Setup header infomation target_include_directories(BMapInspector diff --git a/Ballance/BMapInspector/Rule.cpp b/Ballance/BMapInspector/Rule.cpp index 2eb7935..e5f6230 100644 --- a/Ballance/BMapInspector/Rule.cpp +++ b/Ballance/BMapInspector/Rule.cpp @@ -5,6 +5,7 @@ #include "Rule/YYCRules.hpp" #include "Rule/BBugRules.hpp" #include "Rule/ZZQRules.hpp" +#include "Rule/SOneRules.hpp" namespace BMapInspector::Rule { @@ -20,11 +21,13 @@ namespace BMapInspector::Rule { Ruleset::Ruleset() : rules() { // Add rule into list. - //rules.emplace_back(new Gp1Rule()); - //rules.emplace_back(new Gp2Rule()); + //rules.emplace_back(new GpRule1()); + //rules.emplace_back(new GpRule2()); //rules.emplace_back(new Gp3Rule()); //rules.emplace_back(new Chirs1Rule()); rules.emplace_back(new YYCRule1()); + rules.emplace_back(new YYCRule2()); + rules.emplace_back(new SOneRule1()); // Add more rules... } diff --git a/Ballance/BMapInspector/Rule/GpRules.cpp b/Ballance/BMapInspector/Rule/GpRules.cpp index c104fd7..4dd52c8 100644 --- a/Ballance/BMapInspector/Rule/GpRules.cpp +++ b/Ballance/BMapInspector/Rule/GpRules.cpp @@ -4,46 +4,50 @@ namespace BMapInspector::Rule { #pragma region GP1 Rule - Gp1Rule::Gp1Rule() : IRule() {} + GpRule1::GpRule1() : IRule() {} - Gp1Rule::~Gp1Rule() {} + GpRule1::~GpRule1() {} - std::u8string_view Gp1Rule::GetRuleName() const { + std::u8string_view GpRule1::GetRuleName() const { return u8"GP1"; } - void Gp1Rule::Check(Reporter::Reporter& reporter, Map::Level& level) const {} + void GpRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {} #pragma endregion #pragma region GP2 Rule - Gp2Rule::Gp2Rule() : IRule() {} + constexpr char8_t GP2[] = u8"GP2"; - Gp2Rule::~Gp2Rule() {} + GpRule2::GpRule2() : IRule() {} - std::u8string_view Gp2Rule::GetRuleName() const { - return u8"GP2"; + GpRule2::~GpRule2() {} + + std::u8string_view GpRule2::GetRuleName() const { + return GP2; } - void Gp2Rule::Check(Reporter::Reporter& reporter, Map::Level& level) const {} + void GpRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const { + + } #pragma endregion #pragma region GP3 Rule - - Gp3Rule::Gp3Rule() : IRule() {} - - Gp3Rule::~Gp3Rule() {} - - std::u8string_view Gp3Rule::GetRuleName() const { - return u8"GP3"; - } - - void Gp3Rule::Check(Reporter::Reporter& reporter, Map::Level& level) const { - // TODO: Mesh hash is not implemented. - } - -#pragma endregion +// +// Gp3Rule::Gp3Rule() : IRule() {} +// +// Gp3Rule::~Gp3Rule() {} +// +// std::u8string_view Gp3Rule::GetRuleName() const { +// return u8"GP3"; +// } +// +// void Gp3Rule::Check(Reporter::Reporter& reporter, Map::Level& level) const { +// // TODO: Mesh hash is not implemented. +// } +// +//#pragma endregion } // namespace BMapInspector::Rule diff --git a/Ballance/BMapInspector/Rule/GpRules.hpp b/Ballance/BMapInspector/Rule/GpRules.hpp index aa47d21..393e8bb 100644 --- a/Ballance/BMapInspector/Rule/GpRules.hpp +++ b/Ballance/BMapInspector/Rule/GpRules.hpp @@ -10,11 +10,11 @@ namespace BMapInspector::Rule { * @details * The most comprehensive group checker inspired from Ballance Blender Plugin. */ - class Gp1Rule : public IRule { + class GpRule1 : public IRule { public: - Gp1Rule(); - virtual ~Gp1Rule(); - YYCC_DELETE_COPY_MOVE(Gp1Rule) + GpRule1(); + virtual ~GpRule1(); + YYCC_DELETE_COPY_MOVE(GpRule1) public: std::u8string_view GetRuleName() const override; @@ -24,35 +24,40 @@ namespace BMapInspector::Rule { /** * @brief Gamepiaynmo Rule 2 * @details - * This rule make sure that one Ballance element must be grouped into only one sector group. - * Multiple grouping and none grouping will throw error. + * Every Ballance group should not have any groups with same name. */ - class Gp2Rule : public IRule { + class GpRule2 : public IRule { public: - Gp2Rule(); - virtual ~Gp2Rule(); - YYCC_DELETE_COPY_MOVE(Gp2Rule) + GpRule2(); + virtual ~GpRule2(); + YYCC_DELETE_COPY_MOVE(GpRule2) public: std::u8string_view GetRuleName() const override; void Check(Reporter::Reporter& reporter, Map::Level& level) const override; }; - /** - * @brief Gamepiaynmo Rule 3 - * @details - * This rule make sure that all Ballance element is grouped into correct element group. - * This rule will check the mesh of PH and guess which element it is. - */ - class Gp3Rule : public IRule { - public: - Gp3Rule(); - virtual ~Gp3Rule(); - YYCC_DELETE_COPY_MOVE(Gp3Rule) + ///** + // * @brief Gamepiaynmo Rule 2 + // * @details + // * This rule make sure that one Ballance element must be grouped into only one sector group. + // * Multiple grouping and none grouping will throw error. + // */ + ///** + // * @brief Gamepiaynmo Rule 3 + // * @details + // * This rule make sure that all Ballance element is grouped into correct element group. + // * This rule will check the mesh of PH and guess which element it is. + // */ + //class Gp3Rule : public IRule { + //public: + // Gp3Rule(); + // virtual ~Gp3Rule(); + // YYCC_DELETE_COPY_MOVE(Gp3Rule) - public: - std::u8string_view GetRuleName() const override; - void Check(Reporter::Reporter& reporter, Map::Level& level) const override; - }; + //public: + // std::u8string_view GetRuleName() const override; + // void Check(Reporter::Reporter& reporter, Map::Level& level) const override; + //}; } diff --git a/Ballance/BMapInspector/Rule/SOneRules.cpp b/Ballance/BMapInspector/Rule/SOneRules.cpp new file mode 100644 index 0000000..3c2a5ff --- /dev/null +++ b/Ballance/BMapInspector/Rule/SOneRules.cpp @@ -0,0 +1,39 @@ +#include "SOneRules.hpp" +#include "Shared.hpp" + +namespace L = LibCmo; +namespace C = LibCmo::CK2; +namespace O = LibCmo::CK2::ObjImpls; + +namespace BMapInspector::Rule { + +#pragma region SOne Rule 1 + + constexpr char8_t SONE1[] = u8"SONE1"; + + SOneRule1::SOneRule1() : IRule() {} + + SOneRule1::~SOneRule1() {} + + std::u8string_view SOneRule1::GetRuleName() const { + return SONE1; + } + + void SOneRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const { + auto* ctx = level.GetCKContext(); + auto physicalized_3dobjects = Shared::FetchPhysicalized3dObjects(ctx); + + for (auto* physicalized_3dobject : physicalized_3dobjects) { + auto* mesh = physicalized_3dobject->GetCurrentMesh(); + if (mesh == nullptr) { + reporter.FormatError( + SONE1, + u8R"(Object "%s" is grouped into physicalization group, but it doesn't have any associated mesh. This will cause itself and following objects can not be physicalized.)", + Shared::RenderObjectName(physicalized_3dobject)); + } + } + } + +#pragma endregion + +} // namespace BMapInspector::Rule diff --git a/Ballance/BMapInspector/Rule/SOneRules.hpp b/Ballance/BMapInspector/Rule/SOneRules.hpp new file mode 100644 index 0000000..b8a2edf --- /dev/null +++ b/Ballance/BMapInspector/Rule/SOneRules.hpp @@ -0,0 +1,23 @@ +#pragma once +#include "../Rule.hpp" + +namespace BMapInspector::Rule { + + /** + * @brief SomeOne_001 Rule 1 + * @details + * If there is a physicalized object without any mesh, + * itself and following objects will not be physicalized. + */ + class SOneRule1 : public IRule { + public: + SOneRule1(); + virtual ~SOneRule1(); + YYCC_DELETE_COPY_MOVE(SOneRule1) + + public: + std::u8string_view GetRuleName() const override; + void Check(Reporter::Reporter& reporter, Map::Level& level) const override; + }; + +} // namespace BMapInspector::Rule diff --git a/Ballance/BMapInspector/Rule/Shared.cpp b/Ballance/BMapInspector/Rule/Shared.cpp index b0d8cb6..54b1140 100644 --- a/Ballance/BMapInspector/Rule/Shared.cpp +++ b/Ballance/BMapInspector/Rule/Shared.cpp @@ -19,6 +19,28 @@ namespace BMapInspector::Rule::Shared { return static_cast(ctx->GetObjectByNameAndClass(name, C::CK_CLASSID::CKCID_GROUP, nullptr)); } + static void Iter3dObjectsEx(std::vector& container, O::CKGroup* group) { + for (L::CKDWORD obj_idx = 0; obj_idx < group->GetObjectCount(); ++obj_idx) { + auto* group_beobject = group->GetObject(obj_idx); + if (!C::CKIsChildClassOf(group_beobject->GetClassID(), C::CK_CLASSID::CKCID_3DOBJECT)) continue; + auto* group_3dobject = static_cast(group_beobject); + container.emplace_back(group_3dobject); + } + } + + std::vector FetchPhysicalized3dObjects(C::CKContext* ctx) { + std::vector rv; + + auto* phys_floors = FetchGroup(ctx, GroupNames::PHYS_FLOORS); + if (phys_floors != nullptr) Iter3dObjectsEx(rv, phys_floors); + auto* phys_floorrails = FetchGroup(ctx, GroupNames::PHYS_FLOORRAILS); + if (phys_floorrails != nullptr) Iter3dObjectsEx(rv, phys_floorrails); + auto* phys_floorstopper = FetchGroup(ctx, GroupNames::PHYS_FLOORSTOPPER); + if (phys_floorstopper != nullptr) Iter3dObjectsEx(rv, phys_floorstopper); + + return rv; + } + bool CheckTextureFileName(O::CKTexture* tex, L::CKSTRING name) { // Get file name auto filename = tex->GetUnderlyingData().GetSlotFileName(0); @@ -30,14 +52,9 @@ namespace BMapInspector::Rule::Shared { return C::CKStrEqualI(filename_part.c_str(), name); } - std::vector Iter3dEntities(O::CKGroup* group) { - std::vector rv; - for (L::CKDWORD obj_idx = 0; obj_idx < group->GetObjectCount(); ++obj_idx) { - auto* group_object = group->GetObject(obj_idx); - if (!C::CKIsChildClassOf(group_object->GetClassID(), C::CK_CLASSID::CKCID_3DENTITY)) continue; - auto* group_3dentity = static_cast(group_object); - rv.emplace_back(group_3dentity); - } + std::vector Iter3dObjects(O::CKGroup* group) { + std::vector rv; + Iter3dObjectsEx(rv, group); return rv; } diff --git a/Ballance/BMapInspector/Rule/Shared.hpp b/Ballance/BMapInspector/Rule/Shared.hpp index a99bc76..00c1b6d 100644 --- a/Ballance/BMapInspector/Rule/Shared.hpp +++ b/Ballance/BMapInspector/Rule/Shared.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace BMapInspector::Rule::Shared { @@ -11,12 +12,19 @@ namespace BMapInspector::Rule::Shared { #pragma region Constant Values namespace GroupNames { + // clang-format off + constexpr char8_t PHYS_FLOORS[] = u8"Phys_Floors"; constexpr char8_t PHYS_FLOORRAILS[] = u8"Phys_FloorRails"; - } + constexpr char8_t PHYS_FLOORSTOPPER[] = u8"Phys_FloorStopper"; + // clang-format on + } // namespace GroupNames namespace TextureNames { + // clang-format off constexpr char8_t RAIL_ENVIRONMENT[] = u8"Rail_Environment.bmp"; - } + // clang-format on + + } // namespace TextureNames #pragma endregion @@ -29,6 +37,7 @@ namespace BMapInspector::Rule::Shared { * @return */ O::CKGroup* FetchGroup(C::CKContext* ctx, L::CKSTRING name); + std::vector FetchPhysicalized3dObjects(C::CKContext* ctx); /** * @brief Check whether given CKTexture has the given file name (case-insensitive). * @param[in] tex Can not be nullptr. @@ -42,7 +51,7 @@ namespace BMapInspector::Rule::Shared { * @param[in] group Can not be nullptr. * @return All objects is the child class of CK3dEntity. */ - std::vector Iter3dEntities(O::CKGroup* group); + std::vector Iter3dObjects(O::CKGroup* group); /** * @brief * @param[in] mesh Can not be nullptr. diff --git a/Ballance/BMapInspector/Rule/YYCRules.cpp b/Ballance/BMapInspector/Rule/YYCRules.cpp index 6e4ef54..8ad8248 100644 --- a/Ballance/BMapInspector/Rule/YYCRules.cpp +++ b/Ballance/BMapInspector/Rule/YYCRules.cpp @@ -1,6 +1,8 @@ #include "YYCRules.hpp" #include "Shared.hpp" +#include #include +#include namespace L = LibCmo; namespace C = LibCmo::CK2; @@ -12,7 +14,7 @@ namespace BMapInspector::Rule { constexpr char8_t YYC1[] = u8"YYC1"; - YYCRule1::YYCRule1() {} + YYCRule1::YYCRule1() : IRule() {} YYCRule1::~YYCRule1() {} @@ -30,10 +32,10 @@ namespace BMapInspector::Rule { // Create container holding smooth meshes std::set smooth_meshes; // We iterate all object grouped into it. - auto group_3dentities = Shared::Iter3dEntities(phys_floorrails); - for (auto* group_3dentity : group_3dentities) { + auto group_3dobjects = Shared::Iter3dObjects(phys_floorrails); + for (auto* group_3dobject : group_3dobjects) { // Then we iterate their current meshes - auto* mesh = group_3dentity->GetCurrentMesh(); + auto* mesh = group_3dobject->GetCurrentMesh(); if (mesh == nullptr) continue; // Iterate all meshes @@ -47,7 +49,7 @@ namespace BMapInspector::Rule { reporter.FormatError( YYC1, u8R"(Object "%s" is grouped into Phys_FloorRails, but its texture "%s" (referred by mesh %s and material %s) seems not the rail texture. This will cause this object be smooth unexpectly.)", - Shared::RenderObjectName(group_3dentity), + Shared::RenderObjectName(group_3dobject), Shared::RenderObjectName(texture), Shared::RenderObjectName(mesh), Shared::RenderObjectName(mtl)); @@ -81,4 +83,50 @@ namespace BMapInspector::Rule { #pragma endregion +#pragma region YYC Rule 2 + + constexpr char8_t YYC2[] = u8"YYC2"; + + YYCRule2::YYCRule2() : IRule() {} + + YYCRule2::~YYCRule2() {} + + std::u8string_view YYCRule2::GetRuleName() const { + return YYC2; + } + + void YYCRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const { + auto* ctx = level.GetCKContext(); + auto physicalized_3dobjects = Shared::FetchPhysicalized3dObjects(ctx); + + // Iterate all physicalized 3dobject + for (auto* physicalized_3dobject : physicalized_3dobjects) { + // Get its mesh + auto* mesh = physicalized_3dobject->GetCurrentMesh(); + if (mesh == nullptr) continue; + + // Create a bool vector with vertex count and false init value. + std::vector used_vertex(mesh->GetVertexCount(), false); + // Iterate all face and set their vertex as used. + auto* face_indices = mesh->GetFaceIndices(); + for (L::CKDWORD face_idx = 0; face_idx < mesh->GetFaceCount(); ++face_idx) { + used_vertex[face_indices[face_idx * 3]] = true; + used_vertex[face_indices[face_idx * 3 + 1]] = true; + used_vertex[face_indices[face_idx * 3 + 2]] = true; + } + // Check whether there is unused vertex + auto has_unused_vertex = std::any_of(used_vertex.begin(), used_vertex.end(), [](bool v) { return v == false; }); + // If there is unused vertex, report error + if (has_unused_vertex) { + reporter.FormatError( + YYC2, + u8R"(Object "%s" is grouped into physicalization groups, and its referred mesh "%s" has isolated vertex. This will cause it can not be physicalized.)", + Shared::RenderObjectName(physicalized_3dobject), + Shared::RenderObjectName(mesh)); + } + } + } + +#pragma endregion + } // namespace BMapInspector::Rule diff --git a/Ballance/BMapInspector/Rule/YYCRules.hpp b/Ballance/BMapInspector/Rule/YYCRules.hpp index 5bb1fc7..b145f61 100644 --- a/Ballance/BMapInspector/Rule/YYCRules.hpp +++ b/Ballance/BMapInspector/Rule/YYCRules.hpp @@ -12,7 +12,7 @@ namespace BMapInspector::Rule { class YYCRule1 : public IRule { public: YYCRule1(); - ~YYCRule1(); + virtual ~YYCRule1(); YYCC_DELETE_COPY_MOVE(YYCRule1) public: @@ -20,4 +20,21 @@ namespace BMapInspector::Rule { void Check(Reporter::Reporter& reporter, Map::Level& level) const override; }; + /** + * @brief YYC12345 Rule 2 + * @details + * The object grouped into physicalization group should not have isolated vertex, + * otherwise it will fail to be physicalized. + */ + class YYCRule2 : public IRule { + public: + YYCRule2(); + virtual ~YYCRule2(); + YYCC_DELETE_COPY_MOVE(YYCRule2) + + public: + std::u8string_view GetRuleName() const override; + void Check(Reporter::Reporter& reporter, Map::Level& level) const override; + }; + }