diff --git a/BMEdit/Editor/Include/Widgets/SceneRenderWidget.h b/BMEdit/Editor/Include/Widgets/SceneRenderWidget.h index 0029c58..9b6dbe5 100644 --- a/BMEdit/Editor/Include/Widgets/SceneRenderWidget.h +++ b/BMEdit/Editor/Include/Widgets/SceneRenderWidget.h @@ -190,6 +190,9 @@ namespace widgets void resourcesReady(); void resourceLoadFailed(const QString& reason); + public slots: + void onRedrawRequested(); + protected: void initializeGL() override; void paintGL() override; diff --git a/BMEdit/Editor/Source/Widgets/SceneRenderWidget.cpp b/BMEdit/Editor/Source/Widgets/SceneRenderWidget.cpp index ef76e87..0f9adae 100644 --- a/BMEdit/Editor/Source/Widgets/SceneRenderWidget.cpp +++ b/BMEdit/Editor/Source/Widgets/SceneRenderWidget.cpp @@ -72,6 +72,7 @@ namespace widgets uint16_t height { 0 }; GLuint texture { kInvalidResource }; std::optional index {}; /// Index of texture from TEX container + std::optional texPath {}; /// [Optional] Path to texture in TEX container (path may not be defined in TEX!) void discard(QOpenGLFunctions_3_3_Core* gapi) { @@ -304,6 +305,7 @@ namespace widgets std::vector m_textures {}; std::vector m_shaders {}; std::vector m_models {}; + std::unordered_map m_modelsCache {}; /// primitive index to model index in m_models GLuint m_iGLDebugTexture { 0 }; size_t m_iTexturedShaderIdx = 0; size_t m_iGizmoShaderIdx = 0; @@ -313,6 +315,7 @@ namespace widgets void discard(QOpenGLFunctions_3_3_Core* gapi) { + // Destroy textures { for (auto& texture : m_textures) { @@ -322,6 +325,7 @@ namespace widgets m_textures.clear(); } + // Destroy shaders { for (auto& shader : m_shaders) { @@ -331,6 +335,7 @@ namespace widgets m_shaders.clear(); } + // Destroy models { for (auto& model : m_models) { @@ -340,7 +345,13 @@ namespace widgets m_models.clear(); } + // Empty cache + m_modelsCache.clear(); + + // Release refs m_iGLDebugTexture = 0u; + m_iTexturedShaderIdx = 0u; + m_iGizmoShaderIdx = 0u; } [[nodiscard]] bool hasResources() const @@ -383,11 +394,19 @@ namespace widgets } // Begin frame - funcs->glEnable(GL_DEPTH_TEST); funcs->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); funcs->glClearColor(0.15f, 0.2f, 0.45f, 1.0f); - funcs->glDepthFunc(GL_ALWAYS); + // Z-Buffer testing + funcs->glEnable(GL_DEPTH_TEST); + funcs->glDepthFunc(GL_LESS); + + // Blending + funcs->glEnable(GL_BLEND); + funcs->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // NOTE: Before render anything we need to look at material and check MATRenderState. + // If it's applied we need to setup OpenGL into correct state to make perfect rendering switch (m_eState) { case ELevelState::LS_NONE: @@ -453,7 +472,11 @@ namespace widgets { bool bMoved = false; constexpr float kBaseDt = 1.f / 60.f; - constexpr float kSpeedUp = 100.f; + float kSpeedUp = 100.f; + + if (event->modifiers().testFlag(Qt::KeyboardModifier::ShiftModifier)) + kSpeedUp *= 4.f; + if (event->key() == Qt::Key_W) { @@ -607,6 +630,12 @@ namespace widgets repaint(); } + void SceneRenderWidget::onRedrawRequested() + { + if (m_pLevel) + repaint(); + } + #define LEVEL_SAFE_CHECK() \ if (!m_pLevel) \ { \ @@ -648,6 +677,7 @@ namespace widgets // Store texture index from TEX container newTexture.index = std::make_optional(texture.m_index); + newTexture.texPath = texture.m_fileName; // just copy file name from tex (if it defined!) // Create GL resource glFunctions->glGenTextures(1, &newTexture.texture); @@ -677,12 +707,16 @@ namespace widgets qDebug() << "All textures (" << m_pLevel->getSceneTextures()->entries.size() << ") are loaded and ready to be used"; m_eState = ELevelState::LS_LOAD_GEOMETRY; + repaint(); // call to force jump into next state } void SceneRenderWidget::doLoadGeometry(QOpenGLFunctions_3_3_Core* glFunctions) { LEVEL_SAFE_CHECK() + // To avoid of future problems we've allocating null model at slot #0 and assign to it chunk #0 + m_resources->m_models.emplace_back().chunkId = 0; + // TODO: Optimize and load "chunk by chunk" for (const auto& model : m_pLevel->getLevelGeometry()->primitives.models) { @@ -697,6 +731,10 @@ namespace widgets GLResources::Model& glModel = m_resources->m_models.emplace_back(); glModel.chunkId = model.chunk; + // Store cache + m_resources->m_modelsCache[model.chunk] = m_resources->m_models.size() - 1; + + // Lookup mesh int meshIdx = 0; for (const auto& mesh : model.meshes) { @@ -790,16 +828,6 @@ namespace widgets // Shadows - do not use texturing (and don't show for now) glMesh.glTextureId = GLResources::kInvalidResource; } - else if (parentName == "Old" || parentName == "Glow") - { - // TODO: Impl later - glMesh.glTextureId = GLResources::kInvalidResource; - } - else if (matInstance.getBinders().empty()) - { - // No texture at all - glMesh.glTextureId = GLResources::kInvalidResource; - } else if (parentName == "Bad") { // Use 'bad' debug texture @@ -817,18 +845,46 @@ namespace widgets for (const auto& texture : binder.textures) { - if (texture.getName() == "mapDiffuse" && texture.getTextureId() != 0) + if (texture.getName() == "mapDiffuse" && (texture.getTextureId() != 0 || !texture.getTexturePath().empty())) { // And find texture in textures pool for (const auto& textureResource : m_resources->m_textures) { - if (textureResource.index.has_value() && textureResource.index.value() == texture.getTextureId()) + switch (texture.getPresentedTextureSources()) { - glMesh.glTextureId = textureResource.texture; - - // Ok, we are ready to show this - bTextureFound = true; + case gamelib::mat::PresentedTextureSource::PTS_NOTHING: + continue; // Nothing + + case gamelib::mat::PresentedTextureSource::PTS_TEXTURE_ID: + { + // Only texture id + if (textureResource.index.has_value() && textureResource.index.value() == texture.getTextureId()) + { + // Good + glMesh.glTextureId = textureResource.texture; + bTextureFound = true; + break; + } + } + break; + case gamelib::mat::PresentedTextureSource::PTS_TEXTURE_PATH: + { + // Only path + if (textureResource.texPath.has_value() && textureResource.texPath.value() == texture.getTexturePath()) + { + // Good + glMesh.glTextureId = textureResource.texture; + bTextureFound = true; + break; + } + } break; + default: + { + // Bad case! Undefined behaviour! + assert(false && "Impossible case!"); + continue; + } } } @@ -846,6 +902,12 @@ namespace widgets } } } + + if (matInstance.getBinders().empty()) + { + // use debug texture + glMesh.glTextureId = m_resources->m_iGLDebugTexture; + } } } else if (mesh.textureId > 0) @@ -869,6 +931,7 @@ namespace widgets qDebug() << "All models (" << m_pLevel->getLevelGeometry()->primitives.models.size() << ") are loaded & ready to use!"; m_eState = ELevelState::LS_COMPILE_SHADERS; + repaint(); // call to force jump into next state } void SceneRenderWidget::doCompileShaders(QOpenGLFunctions_3_3_Core* glFunctions) @@ -1002,6 +1065,7 @@ void main() qDebug() << "Shaders (" << m_resources->m_shaders.size() << ") compiled and ready to use!"; m_eState = ELevelState::LS_RESET_CAMERA_STATE; + repaint(); // call to force jump into next state } void SceneRenderWidget::doResetCameraState(QOpenGLFunctions_3_3_Core* glFunctions) @@ -1028,6 +1092,7 @@ void main() emit resourcesReady(); m_eState = ELevelState::LS_READY; // Done! + repaint(); // call to force jump into next state } void SceneRenderWidget::doRenderScene(QOpenGLFunctions_3_3_Core* glFunctions) @@ -1040,10 +1105,35 @@ void main() updateProjectionMatrix(QWidget::width(), QWidget::height()); } - // Out ROOT is always first object. Start tree hierarchy visit from ROOT - const gamelib::scene::SceneObject::Ptr& root = m_pLevel->getSceneObjects()[0]; + // First of all we need to find ZBackdrop and render scene from this geom + m_pLevel->forEachObjectOfType("ZBackdrop", [this, glFunctions](const gamelib::scene::SceneObject::Ptr& sceneObject) -> bool { + doRenderGeom(glFunctions, sceneObject.get()); + + // Render only 1 ZBackdrop + return false; + }); - doRenderGeom(glFunctions, root.get()); + // Then we need to find our 'current room' + // How to find current room? Idk, let's find all 'rooms'? + std::vector roomsToRender; + roomsToRender.reserve(16); + + m_pLevel->forEachObjectOfTypeWithInheritance("ZROOM", [&roomsToRender](const gamelib::scene::SceneObject::Ptr& sceneObject) -> bool { + if (sceneObject->getName() != "ROOT" && sceneObject->getType()->getName() != "ZBackdrop") + { + // Save room + roomsToRender.emplace_back(sceneObject); + } + + // Render every room + return true; + }); + + // Ok, we have rooms to draw, let's draw 'em all + for (const auto& room : roomsToRender) + { + doRenderGeom(glFunctions, room.get()); + } } void SceneRenderWidget::discardResources(QOpenGLFunctions_3_3_Core* glFunctions) @@ -1066,7 +1156,6 @@ void main() const bool bInvisible = geom->getProperties().getObject("Invisible", false); const auto vPosition = geom->getProperties().getObject("Position", glm::vec3(0.f)); const auto mMatrix = geom->getProperties().getObject("Matrix", glm::mat3(1.f)); - const auto mMatrixT = glm::transpose(mMatrix); // Don't draw invisible things if (bInvisible && !bIgnoreVisibility) @@ -1078,23 +1167,16 @@ void main() if (primId != 0) { // Extract matrix from properties - const glm::mat4 transformMatrix = glm::mat4(mMatrixT); + const glm::mat4 transformMatrix = glm::mat4(glm::transpose(mMatrix)); const glm::mat4 translateMatrix = glm::translate(glm::mat4(1.f), vPosition); // Am I right here? const glm::mat4 modelMatrix = translateMatrix * transformMatrix; - // And bind required resources - // TODO: Optimize me - const auto& models = m_resources->m_models; - auto modelIt = std::find_if(models.begin(), models.end(), - [&primId](const GLResources::Model& model) -> bool { - return model.chunkId == (primId - 1); - }); - - if (modelIt != models.end()) + // RenderGod + if (m_resources->m_modelsCache.contains(primId)) { - const GLResources::Model& model = *modelIt; + const GLResources::Model& model = m_resources->m_models[m_resources->m_modelsCache[primId]]; // Render all meshes for (const auto& mesh : model.meshes) diff --git a/BMEdit/GameLib/Include/GameLib/Level.h b/BMEdit/GameLib/Include/GameLib/Level.h index 154d2e4..42a7440 100644 --- a/BMEdit/GameLib/Include/GameLib/Level.h +++ b/BMEdit/GameLib/Include/GameLib/Level.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -67,10 +68,13 @@ namespace gamelib [[nodiscard]] const LevelMaterials* getLevelMaterials() const; [[nodiscard]] LevelMaterials* getLevelMaterials(); - [[nodiscard]] const std::vector &getSceneObjects() const; + [[nodiscard]] const std::vector& getSceneObjects() const; void dumpAsset(io::AssetKind assetKind, std::vector &outBuffer) const; + void forEachObjectOfType(const std::string& objectTypeName, const std::function& pred) const; + void forEachObjectOfTypeWithInheritance(const std::string& objectBaseType, const std::function& pred) const; + private: bool loadLevelProperties(); bool loadLevelScene(); diff --git a/BMEdit/GameLib/Include/GameLib/MAT/MATBind.h b/BMEdit/GameLib/Include/GameLib/MAT/MATBind.h index 5321ed7..6627321 100644 --- a/BMEdit/GameLib/Include/GameLib/MAT/MATBind.h +++ b/BMEdit/GameLib/Include/GameLib/MAT/MATBind.h @@ -2,8 +2,10 @@ #include +#include #include #include +#include #include #include #include @@ -29,6 +31,9 @@ namespace gamelib::mat std::vector textures; std::vector colorChannels; std::vector sprites; + std::vector options; + std::vector scrolls; + std::vector floats; /// ... another fields? }; } \ No newline at end of file diff --git a/BMEdit/GameLib/Include/GameLib/MAT/MATColorChannel.h b/BMEdit/GameLib/Include/GameLib/MAT/MATColorChannel.h index bed0c89..7f39c36 100644 --- a/BMEdit/GameLib/Include/GameLib/MAT/MATColorChannel.h +++ b/BMEdit/GameLib/Include/GameLib/MAT/MATColorChannel.h @@ -1,9 +1,8 @@ #pragma once +#include #include #include -#include -#include namespace ZBio::ZBinaryReader { @@ -12,25 +11,23 @@ namespace ZBio::ZBinaryReader namespace gamelib::mat { - using MATColorRGBA = std::variant; - /** * @brief Present option for color channel (able to pass rgba as float & int values) */ class MATColorChannel { public: - MATColorChannel(std::string name, bool bEnabled, MATColorRGBA&& color); + MATColorChannel(std::string name, bool bEnabled, MATValU&& color); static MATColorChannel makeFromStream(ZBio::ZBinaryReader::BinaryReader* binaryReader, int propertiesCount); [[nodiscard]] const std::string& getName() const; [[nodiscard]] bool isEnabled() const; - [[nodiscard]] const MATColorRGBA& getColor() const; + [[nodiscard]] const MATValU& getColor() const; private: std::string m_name {}; bool m_bEnabled { false }; - MATColorRGBA m_color {}; + MATValU m_color {}; }; } \ No newline at end of file diff --git a/BMEdit/GameLib/Include/GameLib/MAT/MATEntries.h b/BMEdit/GameLib/Include/GameLib/MAT/MATEntries.h index 7b56c38..c491d83 100644 --- a/BMEdit/GameLib/Include/GameLib/MAT/MATEntries.h +++ b/BMEdit/GameLib/Include/GameLib/MAT/MATEntries.h @@ -76,7 +76,7 @@ namespace gamelib::mat PK_TEXTURE_ID = 0x54584944u, ///< [TRIVIAL] Hold uint32 value, in `reference` stored ID of texture (TEXEntry id, see TEX parser for details) PK_TILINIG_U = 0x54494C55u, ///< [STRREF] Reference to string represents U tiling mode. Possible values: NONE, TILED. See MATTilingMode enum for details PK_TILINIG_V = 0x54494C56u, ///< [STRREF] Reference to string represents V tiling mode. Possible values: NONE, TILED - PK_TILINIG_W = 0x574C4954u, ///< [STRREF] Reference to string represents W tiling mode. Possible values are unknown. Probably unused, but declared in Glacier1 code. + PK_TILINIG_W = 0x54494C57u, ///< [STRREF] Reference to string represents W tiling mode. Possible values are unknown. Probably unused, but declared in Glacier1 code. PK_VAL_I = 0x56414C49u, ///< [STRREF] Reference to string. I guess it's something about 'set boolean parameter by string literal'. `ReflectionEnabled` as example. PK_VAL_U = 0x56414C55u, ///< [TRIVIAL] Hold float or int value PK_VAL_F = 0x554C4156u, ///< Idk, unused @@ -84,13 +84,13 @@ namespace gamelib::mat PK_TEXTURE = 0x54455854u, ///< [MATTexture] Reference to list of properties represents texture (PK_PATH - path to texture/PK_TEXTURE_ID - index of texture) PK_COLOR = 0x434F4C4Fu, ///< [MATColorChannel] Reference to list of properties represents usage of color channel (as example v4IlluminationColor, presented via 2 properties: PK_NAME (name of channel) and PK_ENABLED) PK_BOOLEAN = 0x424F4F4Cu, ///< [MATOption] Reference to list of properties represents boolean flag. Presented via 3 properties: PK_NAME: AlphaFadeEnabled, PK_ENABLED - use parameter or not and PK_VAL_U - value of flag - PK_FLOAT_VALUE = 0x464C5456u, ///< Weird to see this here. OK, maybe supports (at least referenced) + PK_FLOAT_VALUE = 0x464C5456u, ///< [MATFloat] Reference to list of properties represents some float argument (or group of floats) PK_DMAP = 0x50414D44u, ///< Idk, unused (Glacier supports, but no usage) PK_FMIN = 0x4E494D46u, ///< Min filter (Idk, looks like legacy from OpenGL times) PK_FMAG = 0x47414D46u, ///< Mag filter (Idk, looks like legacy from OpenGL times) PK_FMIP = 0x50494D46u, ///< Idk, unused - PK_SCROLL = 0x4C524353u, ///< Idk, unused - PK_SCROLL_SPEED = 0x44455053u, ///< Idk, unused + PK_SCROLL = 0x5343524Cu, ///< [MATScroll] Some scrollable something... + PK_SCROLL_SPEED = 0x53504544u, ///< Idk, unused PK_ENUM = 0x4D554E45u ///< Idk, unused }; diff --git a/BMEdit/GameLib/Include/GameLib/MAT/MATFloat.h b/BMEdit/GameLib/Include/GameLib/MAT/MATFloat.h new file mode 100644 index 0000000..8ed449a --- /dev/null +++ b/BMEdit/GameLib/Include/GameLib/MAT/MATFloat.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include + + +namespace ZBio::ZBinaryReader +{ + class BinaryReader; +} + +namespace gamelib::mat +{ + using IntOrFloat = std::variant; + + class MATFloat + { + public: + MATFloat(std::string name, bool bEnabled, MATValU&& valU); + + static MATFloat makeFromStream(ZBio::ZBinaryReader::BinaryReader* binaryReader, int propertiesCount); + + [[nodiscard]] const std::string& getName() const { return m_name; } + [[nodiscard]] bool isEnabled() const { return m_bEnabled; } + [[nodiscard]] const MATValU& getValU() const { return m_valU; } + + private: + std::string m_name {}; + bool m_bEnabled { false }; + MATValU m_valU; + }; +} \ No newline at end of file diff --git a/BMEdit/GameLib/Include/GameLib/MAT/MATOption.h b/BMEdit/GameLib/Include/GameLib/MAT/MATOption.h index a27e6b8..d4e8030 100644 --- a/BMEdit/GameLib/Include/GameLib/MAT/MATOption.h +++ b/BMEdit/GameLib/Include/GameLib/MAT/MATOption.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -14,18 +15,18 @@ namespace gamelib::mat class MATOption { public: - MATOption(std::string name, bool bEnabled, bool bDefault); + MATOption(std::string name, bool bEnabled, MATValU&& valU); static MATOption makeFromStream(ZBio::ZBinaryReader::BinaryReader* binaryReader, int propertiesCount); [[nodiscard]] const std::string& getName() const; [[nodiscard]] bool isEnabled() const; - [[nodiscard]] bool getDefault() const; + [[nodiscard]] const MATValU& getValU() const; private: std::string m_name{}; bool m_bEnabled {}; - bool m_bDefault {}; + MATValU m_valU {}; }; } \ No newline at end of file diff --git a/BMEdit/GameLib/Include/GameLib/MAT/MATRenderState.h b/BMEdit/GameLib/Include/GameLib/MAT/MATRenderState.h index 7076e7f..0f17b03 100644 --- a/BMEdit/GameLib/Include/GameLib/MAT/MATRenderState.h +++ b/BMEdit/GameLib/Include/GameLib/MAT/MATRenderState.h @@ -2,6 +2,7 @@ #include #include +#include #include @@ -19,12 +20,14 @@ namespace gamelib::mat bool bEnabled, bool bBlendEnabled, bool bAlphaTest, bool bFogEnabled, bool bZBias, float fOpacity, float fZOffset, uint32_t iAlphaReference, - MATCullMode cullMode, MATBlendMode blendMode): + MATCullMode cullMode, MATBlendMode blendMode, + MATValU&& valU): m_name(std::move(name)), m_bEnabled(bEnabled), m_bEnableBlend(bBlendEnabled), m_bAlphaTest(bAlphaTest), m_bFogEnabled(bFogEnabled), m_bZBias(bZBias), m_fOpacity(fOpacity), m_fZOffset(fZOffset), m_iAlphaReference(iAlphaReference), - m_eCullMode(cullMode), m_eBlendMode(blendMode) + m_eCullMode(cullMode), m_eBlendMode(blendMode), + m_valU(std::move(valU)) { } @@ -54,5 +57,6 @@ namespace gamelib::mat uint32_t m_iAlphaReference { 0u }; float m_fOpacity { 1.f }; float m_fZOffset { .0f }; + MATValU m_valU {}; }; } \ No newline at end of file diff --git a/BMEdit/GameLib/Include/GameLib/MAT/MATScroll.h b/BMEdit/GameLib/Include/GameLib/MAT/MATScroll.h new file mode 100644 index 0000000..1ac2f7e --- /dev/null +++ b/BMEdit/GameLib/Include/GameLib/MAT/MATScroll.h @@ -0,0 +1,29 @@ +#pragma once + +#include + + +namespace ZBio::ZBinaryReader +{ + class BinaryReader; +} + +namespace gamelib::mat +{ + class MATScroll + { + public: + MATScroll(std::string name, bool bEnabled, std::vector&& speedVector); + + static MATScroll makeFromStream(ZBio::ZBinaryReader::BinaryReader* binaryReader, int propertiesCount); + + [[nodiscard]] const std::string& getName() const { return m_name; } + [[nodiscard]] bool isEnabled() const { return m_bEnabled; } + [[nodiscard]] const std::vector& getSpeedVec() const { return m_vfSpeed; } + + private: + std::string m_name {}; + bool m_bEnabled { false }; + std::vector m_vfSpeed {}; + }; +} \ No newline at end of file diff --git a/BMEdit/GameLib/Include/GameLib/MAT/MATTexture.h b/BMEdit/GameLib/Include/GameLib/MAT/MATTexture.h index 4ab1e2a..b3ad8e9 100644 --- a/BMEdit/GameLib/Include/GameLib/MAT/MATTexture.h +++ b/BMEdit/GameLib/Include/GameLib/MAT/MATTexture.h @@ -13,24 +13,37 @@ namespace ZBio::ZBinaryReader namespace gamelib::mat { + enum PresentedTextureSource : uint8_t + { + PTS_NOTHING, + PTS_TEXTURE_ID, + PTS_TEXTURE_PATH, + PTS_TEXTURE_ID_AND_PATH + }; + class MATTexture { public: - MATTexture(std::string name, bool bEnabled, uint32_t textureId, MATTilingMode tilingU, MATTilingMode tilingV); + MATTexture(std::string name, bool bEnabled, uint32_t textureId, std::string path, MATTilingMode tilingU, MATTilingMode tilingV, MATTilingMode tilingW); static MATTexture makeFromStream(ZBio::ZBinaryReader::BinaryReader* binaryReader, int propertiesCount); [[nodiscard]] const std::string& getName() const; [[nodiscard]] bool isEnabled() const; [[nodiscard]] uint32_t getTextureId() const; + [[nodiscard]] const std::string& getTexturePath() const; [[nodiscard]] MATTilingMode getTilingU() const; [[nodiscard]] MATTilingMode getTilingV() const; + [[nodiscard]] MATTilingMode getTilingW() const; + [[nodiscard]] PresentedTextureSource getPresentedTextureSources() const; private: std::string m_name {}; + std::string m_texturePath {}; std::uint32_t m_iTextureId { 0u }; bool m_bEnabled { false }; MATTilingMode m_tileU { MATTilingMode::TM_NONE }; MATTilingMode m_tileV { MATTilingMode::TM_NONE }; + MATTilingMode m_tileW { MATTilingMode::TM_NONE }; }; } \ No newline at end of file diff --git a/BMEdit/GameLib/Include/GameLib/MAT/MATTilingMode.h b/BMEdit/GameLib/Include/GameLib/MAT/MATTilingMode.h index 559baca..f7f00d7 100644 --- a/BMEdit/GameLib/Include/GameLib/MAT/MATTilingMode.h +++ b/BMEdit/GameLib/Include/GameLib/MAT/MATTilingMode.h @@ -6,6 +6,7 @@ namespace gamelib::mat enum class MATTilingMode { TM_NONE, - TM_TILED + TM_TILED, + TM_MIRRORED }; } \ No newline at end of file diff --git a/BMEdit/GameLib/Include/GameLib/MAT/MATValU.h b/BMEdit/GameLib/Include/GameLib/MAT/MATValU.h new file mode 100644 index 0000000..4a7ff28 --- /dev/null +++ b/BMEdit/GameLib/Include/GameLib/MAT/MATValU.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + + +namespace ZBio::ZBinaryReader +{ + class BinaryReader; +} + +namespace gamelib::mat +{ + class MATValU + { + public: + using Value = std::variant; + + MATValU(); + explicit MATValU(std::vector&& values); + + MATValU& operator=(std::vector&& values) noexcept; + + static MATValU makeFromStream(ZBio::ZBinaryReader::BinaryReader* binaryReader, const MATPropertyEntry& selfDecl); + + [[nodiscard]] const std::vector& getValues() const { return m_values; } + + private: + std::vector m_values {}; + }; +} \ No newline at end of file diff --git a/BMEdit/GameLib/Source/GameLib/Level.cpp b/BMEdit/GameLib/Source/GameLib/Level.cpp index 67aa193..1a048bf 100644 --- a/BMEdit/GameLib/Source/GameLib/Level.cpp +++ b/BMEdit/GameLib/Source/GameLib/Level.cpp @@ -6,8 +6,9 @@ #include #include -#include #include +#include +#include namespace gamelib @@ -130,6 +131,39 @@ namespace gamelib } } + void Level::forEachObjectOfType(const std::string& objectTypeName, const std::function& pred) const + { + for (const auto& object: m_sceneObjects) + { + if (object->getType()->getName() == objectTypeName) + { + if (pred(object)) + continue; + + return; + } + } + } + + bool isComplexTypeInheritedOf(const std::string& baseTypeName, const TypeComplex* pType) + { + return pType != nullptr && (pType->getName() == baseTypeName || isComplexTypeInheritedOf(baseTypeName, reinterpret_cast(pType->getParent()))); + } + + void Level::forEachObjectOfTypeWithInheritance(const std::string& objectBaseType, const std::function& pred) const + { + for (const auto& object: m_sceneObjects) + { + if (object->getType()->getKind() == TypeKind::COMPLEX && isComplexTypeInheritedOf(objectBaseType, reinterpret_cast(object->getType()))) + { + if (pred(object)) + continue; + + return; + } + } + } + bool Level::loadLevelProperties() { int64_t prpFileSize = 0; diff --git a/BMEdit/GameLib/Source/GameLib/MAT/MATBind.cpp b/BMEdit/GameLib/Source/GameLib/MAT/MATBind.cpp index 2efc579..9a36fcd 100644 --- a/BMEdit/GameLib/Source/GameLib/MAT/MATBind.cpp +++ b/BMEdit/GameLib/Source/GameLib/MAT/MATBind.cpp @@ -52,6 +52,31 @@ namespace gamelib::mat b.sprites.emplace_back(MATSprite::makeFromStream(binaryReader, entry.containerCapacity)); } + else if (entry.kind == MATPropertyKind::PK_BOOLEAN) + { + ZBioSeekGuard guard { binaryReader }; + binaryReader->seek(entry.reference); + + b.options.emplace_back(MATOption::makeFromStream(binaryReader, entry.containerCapacity)); + } + else if (entry.kind == MATPropertyKind::PK_SCROLL) + { + ZBioSeekGuard guard { binaryReader }; + binaryReader->seek(entry.reference); + + b.scrolls.emplace_back(MATScroll::makeFromStream(binaryReader, entry.containerCapacity)); + } + else if (entry.kind == MATPropertyKind::PK_FLOAT_VALUE) + { + ZBioSeekGuard guard { binaryReader }; + binaryReader->seek(entry.reference); + + b.floats.emplace_back(MATFloat::makeFromStream(binaryReader, entry.containerCapacity)); + } + else + { + assert(false && "Unprocessed entry!"); + } } return b; diff --git a/BMEdit/GameLib/Source/GameLib/MAT/MATColorChannel.cpp b/BMEdit/GameLib/Source/GameLib/MAT/MATColorChannel.cpp index a2afd1f..83e72ab 100644 --- a/BMEdit/GameLib/Source/GameLib/MAT/MATColorChannel.cpp +++ b/BMEdit/GameLib/Source/GameLib/MAT/MATColorChannel.cpp @@ -7,7 +7,7 @@ namespace gamelib::mat { - MATColorChannel::MATColorChannel(std::string name, bool bEnabled, MATColorRGBA&& color) + MATColorChannel::MATColorChannel(std::string name, bool bEnabled, MATValU&& color) : m_name(std::move(name)), m_bEnabled(bEnabled), m_color(color) { } @@ -16,7 +16,7 @@ namespace gamelib::mat { std::string name {}; bool bEnabled { false }; - MATColorRGBA color; + MATValU color; for (int i = 0; i < propertiesCount; i++) { @@ -36,52 +36,12 @@ namespace gamelib::mat } else if (kind == MATPropertyKind::PK_VAL_U) { - // We will jump to unmarked region (raw buffer) - ZBioSeekGuard guard { binaryReader }; - binaryReader->seek(entry.reference); - - if (entry.containerCapacity == 3) - { - // RGB - if (entry.valueType == MATValueType::PT_UINT32) - { - color.emplace( - binaryReader->read(), - binaryReader->read(), - binaryReader->read() - ); - } - else if (entry.valueType == MATValueType::PT_FLOAT) - { - color.emplace( - binaryReader->read(), - binaryReader->read(), - binaryReader->read() - ); - } - } - else if (entry.containerCapacity == 4) - { - // RGBA - if (entry.valueType == MATValueType::PT_UINT32) - { - color.emplace( - binaryReader->read(), - binaryReader->read(), - binaryReader->read(), - binaryReader->read() - ); - } - else if (entry.valueType == MATValueType::PT_FLOAT) - { - color.emplace( - binaryReader->read(), - binaryReader->read(), - binaryReader->read(), - binaryReader->read() - ); - } - } + // Read ValU + color = MATValU::makeFromStream(binaryReader, entry); + } + else + { + assert(false && "Unprocessed entry!"); } } @@ -98,7 +58,7 @@ namespace gamelib::mat return m_bEnabled; } - const MATColorRGBA& MATColorChannel::getColor() const + const MATValU& MATColorChannel::getColor() const { return m_color; } diff --git a/BMEdit/GameLib/Source/GameLib/MAT/MATFloat.cpp b/BMEdit/GameLib/Source/GameLib/MAT/MATFloat.cpp new file mode 100644 index 0000000..3fda106 --- /dev/null +++ b/BMEdit/GameLib/Source/GameLib/MAT/MATFloat.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include + + +namespace gamelib::mat +{ + MATFloat::MATFloat(std::string name, bool bEnabled, MATValU&& valU) + : m_name(std::move(name)), m_bEnabled(bEnabled), m_valU(std::move(valU)) + { + } + + MATFloat MATFloat::makeFromStream(ZBio::ZBinaryReader::BinaryReader* binaryReader, int propertiesCount) + { + std::string name {}; + bool bEnabled { false }; + MATValU valU {}; + + + for (int i = 0; i < propertiesCount; i++) + { + MATPropertyEntry entry; + MATPropertyEntry::deserialize(entry, binaryReader); + + if (entry.kind == MATPropertyKind::PK_NAME) + { + ZBioSeekGuard guard { binaryReader }; + binaryReader->seek(entry.reference); + + name = binaryReader->readCString(); + } + else if (entry.kind == MATPropertyKind::PK_ENABLE) + { + bEnabled = static_cast(entry.reference); + } + else if (entry.kind == MATPropertyKind::PK_VAL_U) + { + // Make ValU by right way + valU = MATValU::makeFromStream(binaryReader, entry); + } + else + { + assert(false && "Unprocessed case"); + } + } + + return MATFloat(std::move(name), bEnabled, std::move(valU)); + } +} \ No newline at end of file diff --git a/BMEdit/GameLib/Source/GameLib/MAT/MATLayer.cpp b/BMEdit/GameLib/Source/GameLib/MAT/MATLayer.cpp index e3f46ee..d9f0779 100644 --- a/BMEdit/GameLib/Source/GameLib/MAT/MATLayer.cpp +++ b/BMEdit/GameLib/Source/GameLib/MAT/MATLayer.cpp @@ -68,6 +68,10 @@ namespace gamelib::mat { valI = binaryReader->readCString(); } + else + { + assert(false && "Unprocessed entry!"); + } } return MATLayer(std::move(name), std::move(type), std::move(shaderPath), std::move(identity), std::move(valI)); diff --git a/BMEdit/GameLib/Source/GameLib/MAT/MATOption.cpp b/BMEdit/GameLib/Source/GameLib/MAT/MATOption.cpp index 3dc36ac..0d0729c 100644 --- a/BMEdit/GameLib/Source/GameLib/MAT/MATOption.cpp +++ b/BMEdit/GameLib/Source/GameLib/MAT/MATOption.cpp @@ -7,8 +7,8 @@ namespace gamelib::mat { - MATOption::MATOption(std::string name, bool bEnabled, bool bDefault) - : m_name(std::move(name)), m_bEnabled(bEnabled), m_bDefault(bDefault) + MATOption::MATOption(std::string name, bool bEnabled, MATValU&& valU) + : m_name(std::move(name)), m_bEnabled(bEnabled), m_valU(std::move(valU)) { } @@ -22,15 +22,16 @@ namespace gamelib::mat return m_bEnabled; } - bool MATOption::getDefault() const + const MATValU& MATOption::getValU() const { - return m_bDefault; + return m_valU; } MATOption MATOption::makeFromStream(ZBio::ZBinaryReader::BinaryReader* binaryReader, int propertiesCount) { std::string name {}; - bool bEnabled = false, bDefault = false; + bool bEnabled = false; + MATValU valU {}; for (int i = 0; i < propertiesCount; i++) { @@ -50,10 +51,14 @@ namespace gamelib::mat } else if (kind == MATPropertyKind::PK_VAL_U) { - bDefault = static_cast(entry.reference); + valU = MATValU::makeFromStream(binaryReader, entry); + } + else + { + assert(false && "Unprocessed entry!"); } } - return MATOption(std::move(name), bEnabled, bDefault); + return MATOption(std::move(name), bEnabled, std::move(valU)); } } \ No newline at end of file diff --git a/BMEdit/GameLib/Source/GameLib/MAT/MATReader.cpp b/BMEdit/GameLib/Source/GameLib/MAT/MATReader.cpp index 66e5c05..e03cb1b 100644 --- a/BMEdit/GameLib/Source/GameLib/MAT/MATReader.cpp +++ b/BMEdit/GameLib/Source/GameLib/MAT/MATReader.cpp @@ -214,7 +214,7 @@ namespace gamelib::mat matReader->seek(properties[i].reference); - binders.emplace_back(MATBind::makeFromStream(matReader, properties[i].containerCapacity)); + binders.emplace_back(MATBind::makeFromStream(matReader, static_cast(properties[i].containerCapacity))); } } diff --git a/BMEdit/GameLib/Source/GameLib/MAT/MATRenderState.cpp b/BMEdit/GameLib/Source/GameLib/MAT/MATRenderState.cpp index 3cfed2d..e3a72b5 100644 --- a/BMEdit/GameLib/Source/GameLib/MAT/MATRenderState.cpp +++ b/BMEdit/GameLib/Source/GameLib/MAT/MATRenderState.cpp @@ -14,6 +14,7 @@ namespace gamelib::mat uint32_t iAlphaReference { 0u }; MATCullMode cullMode { MATCullMode::CM_DontCare }; MATBlendMode blendMode { MATBlendMode::BM_ADD }; + MATValU valU {}; for (int i = 0; i < propertiesCount; i++) { @@ -116,11 +117,19 @@ namespace gamelib::mat } } break; + case MATPropertyKind::PK_VAL_U: + { + assert(valU.getValues().empty() && "Must be empty here!"); + + valU = MATValU::makeFromStream(binaryReader, entry); + } + break; default: + assert(false && "Unprocessed entry!"); break; } } - return MATRenderState(std::move(name), bEnabled, bBlendEnabled, bAlphaTest, bFogEnabled, bZBias, fOpacity, fZOffset, iAlphaReference, cullMode, blendMode); + return MATRenderState(std::move(name), bEnabled, bBlendEnabled, bAlphaTest, bFogEnabled, bZBias, fOpacity, fZOffset, iAlphaReference, cullMode, blendMode, std::move(valU)); } } \ No newline at end of file diff --git a/BMEdit/GameLib/Source/GameLib/MAT/MATScroll.cpp b/BMEdit/GameLib/Source/GameLib/MAT/MATScroll.cpp new file mode 100644 index 0000000..9898b14 --- /dev/null +++ b/BMEdit/GameLib/Source/GameLib/MAT/MATScroll.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + + +namespace gamelib::mat +{ + MATScroll::MATScroll(std::string name, bool bEnabled, std::vector&& speedVector) + : m_name(std::move(name)), m_bEnabled(bEnabled), m_vfSpeed(std::move(speedVector)) + { + } + + MATScroll MATScroll::makeFromStream(ZBio::ZBinaryReader::BinaryReader* binaryReader, int propertiesCount) + { + std::string name {}; + bool bEnabled { false }; + std::vector speedVector {}; + + for (int i = 0; i < propertiesCount; i++) + { + MATPropertyEntry entry; + MATPropertyEntry::deserialize(entry, binaryReader); + + if (entry.kind == MATPropertyKind::PK_NAME) + { + ZBioSeekGuard guard { binaryReader }; + binaryReader->seek(entry.reference); + + name = binaryReader->readCString(); + } + else if (entry.kind == MATPropertyKind::PK_ENABLE) + { + bEnabled = static_cast(entry.reference); + } + else if (entry.kind == MATPropertyKind::PK_SCROLL_SPEED) + { + ZBioSeekGuard guard { binaryReader }; + binaryReader->seek(entry.reference); + + assert(speedVector.empty() && "It should be empty here!"); + + speedVector.resize(entry.containerCapacity); + binaryReader->read(speedVector.data(), entry.containerCapacity); + } + else + { + assert(false && "Unprocessed case"); + } + } + + return MATScroll(std::move(name), bEnabled, std::move(speedVector)); + } +} \ No newline at end of file diff --git a/BMEdit/GameLib/Source/GameLib/MAT/MATSprite.cpp b/BMEdit/GameLib/Source/GameLib/MAT/MATSprite.cpp index e721298..644e956 100644 --- a/BMEdit/GameLib/Source/GameLib/MAT/MATSprite.cpp +++ b/BMEdit/GameLib/Source/GameLib/MAT/MATSprite.cpp @@ -38,6 +38,10 @@ namespace gamelib::mat ++lastUpdatedUnk; } + else + { + assert(false && "Unprocessed entry!"); + } } return MATSprite(std::move(name), bUnk0, bUnk1); diff --git a/BMEdit/GameLib/Source/GameLib/MAT/MATSubClass.cpp b/BMEdit/GameLib/Source/GameLib/MAT/MATSubClass.cpp index 673ead7..091905e 100644 --- a/BMEdit/GameLib/Source/GameLib/MAT/MATSubClass.cpp +++ b/BMEdit/GameLib/Source/GameLib/MAT/MATSubClass.cpp @@ -11,6 +11,7 @@ namespace gamelib::mat { std::string name {}, oTyp {}, sTyp {}; std::vector layers {}; + std::vector valI {}; for (int i = 0; i < propertiesCount; i++) { @@ -46,6 +47,17 @@ namespace gamelib::mat layers.emplace_back(MATLayer::makeFromStream(binaryReader, entry.containerCapacity)); } + else if (entry.kind == MATPropertyKind::PK_VAL_I) + { + ZBioSeekGuard guard { binaryReader }; + binaryReader->seek(entry.reference); + + valI.emplace_back(binaryReader->readCString()); + } + else + { + assert(false && "Unprocessed entry!"); + } } return MATSubClass(std::move(name), std::move(oTyp), std::move(sTyp), std::move(layers)); diff --git a/BMEdit/GameLib/Source/GameLib/MAT/MATTexture.cpp b/BMEdit/GameLib/Source/GameLib/MAT/MATTexture.cpp index e7a962f..bc4befb 100644 --- a/BMEdit/GameLib/Source/GameLib/MAT/MATTexture.cpp +++ b/BMEdit/GameLib/Source/GameLib/MAT/MATTexture.cpp @@ -7,17 +7,18 @@ namespace gamelib::mat { - MATTexture::MATTexture(std::string name, bool bEnabled, uint32_t textureId, MATTilingMode tilingU, MATTilingMode tilingV) - : m_name(std::move(name)), m_bEnabled(bEnabled), m_iTextureId(textureId), m_tileU(tilingU), m_tileV(tilingV) + MATTexture::MATTexture(std::string name, bool bEnabled, uint32_t textureId, std::string path, MATTilingMode tilingU, MATTilingMode tilingV, MATTilingMode tilingW) + : m_name(std::move(name)), m_texturePath(std::move(path)), m_bEnabled(bEnabled), m_iTextureId(textureId), m_tileU(tilingU), m_tileV(tilingV), m_tileW(tilingW) { } MATTexture MATTexture::makeFromStream(ZBio::ZBinaryReader::BinaryReader* binaryReader, int propertiesCount) { std::string name{}; + std::string path{}; bool bEnabled{false}; uint32_t textureId {0}; - MATTilingMode tileU { MATTilingMode::TM_NONE }, tileV { MATTilingMode::TM_NONE }; + MATTilingMode tileU { MATTilingMode::TM_NONE }, tileV { MATTilingMode::TM_NONE }, tileW { MATTilingMode::TM_NONE }; for (int i = 0; i < propertiesCount; i++) { @@ -39,7 +40,7 @@ namespace gamelib::mat { textureId = entry.reference; } - else if (kind == MATPropertyKind::PK_TILINIG_U || kind == MATPropertyKind::PK_TILINIG_V) + else if (kind == MATPropertyKind::PK_TILINIG_U || kind == MATPropertyKind::PK_TILINIG_V || kind == MATPropertyKind::PK_TILINIG_W) { ZBioSeekGuard guard { binaryReader }; binaryReader->seek(entry.reference); @@ -55,6 +56,10 @@ namespace gamelib::mat { tempMode = MATTilingMode::TM_TILED; } + else if (temp == "MIRRORED") + { + tempMode = MATTilingMode::TM_MIRRORED; + } else { assert(false && "Unsupported mode!"); @@ -63,10 +68,23 @@ namespace gamelib::mat if (kind == MATPropertyKind::PK_TILINIG_U) tileU = tempMode; if (kind == MATPropertyKind::PK_TILINIG_V) tileV = tempMode; + if (kind == MATPropertyKind::PK_TILINIG_W) tileW = tempMode; + } + else if (kind == MATPropertyKind::PK_PATH) + { + // Path to the texture + ZBioSeekGuard guard { binaryReader }; + binaryReader->seek(entry.reference); + + path = binaryReader->readCString(); + } + else + { + assert(false && "Unsupported entry! Need to support!"); } } - return MATTexture(std::move(name), bEnabled, textureId, tileU, tileV); + return MATTexture(std::move(name), bEnabled, textureId, path, tileU, tileV, tileW); } const std::string& MATTexture::getName() const @@ -74,11 +92,21 @@ namespace gamelib::mat return m_name; } + bool MATTexture::isEnabled() const + { + return m_bEnabled; + } + uint32_t MATTexture::getTextureId() const { return m_iTextureId; } + const std::string& MATTexture::getTexturePath() const + { + return m_texturePath; + } + MATTilingMode MATTexture::getTilingU() const { return m_tileU; @@ -88,4 +116,23 @@ namespace gamelib::mat { return m_tileV; } + + MATTilingMode MATTexture::getTilingW() const + { + return m_tileW; + } + + PresentedTextureSource MATTexture::getPresentedTextureSources() const + { + if (m_texturePath.empty() && m_iTextureId == 0) + return PresentedTextureSource::PTS_NOTHING; + + if (m_texturePath.empty() && m_iTextureId > 0) + return PresentedTextureSource::PTS_TEXTURE_ID; + + if (!m_texturePath.empty() && m_iTextureId == 0) + return PresentedTextureSource::PTS_TEXTURE_PATH; + + return PresentedTextureSource::PTS_TEXTURE_ID_AND_PATH; + } } diff --git a/BMEdit/GameLib/Source/GameLib/MAT/MATValU.cpp b/BMEdit/GameLib/Source/GameLib/MAT/MATValU.cpp new file mode 100644 index 0000000..987c55e --- /dev/null +++ b/BMEdit/GameLib/Source/GameLib/MAT/MATValU.cpp @@ -0,0 +1,71 @@ +#include +#include +#include + + + +namespace gamelib::mat +{ + MATValU::MATValU() = default; + + MATValU::MATValU(std::vector&& values) : m_values(std::move(values)) + { + } + + MATValU &MATValU::operator=(std::vector &&values) noexcept + { + m_values = std::move(values); + return *this; + } + + MATValU MATValU::makeFromStream(ZBio::ZBinaryReader::BinaryReader* binaryReader, const MATPropertyEntry& selfDecl) + { + assert(selfDecl.kind == MATPropertyKind::PK_VAL_U); + + std::vector values {}; + values.resize(selfDecl.containerCapacity); + + if (selfDecl.containerCapacity == 1) + { + // Parse single value + if (selfDecl.valueType == MATValueType::PT_UINT32) + { + // Single uint32 + values[0] = static_cast(selfDecl.reference); + } + else if (selfDecl.valueType == MATValueType::PT_FLOAT) + { + // Single float + values[0] = static_cast(selfDecl.reference); + } + else + { + assert(false && "Unsupported & impossible case!"); + } + } + else + { + // Parse group + ZBioSeekGuard guard { binaryReader }; + binaryReader->seek(selfDecl.reference); + + for (int i = 0; i < selfDecl.containerCapacity; i++) + { + if (selfDecl.valueType == MATValueType::PT_UINT32) + { + values[i] = binaryReader->read(); + } + else if (selfDecl.valueType == MATValueType::PT_FLOAT) + { + values[i] = binaryReader->read(); + } + else + { + assert(false && "Unsupported & impossible case!"); + } + } + } + + return MATValU(std::move(values)); + } +} \ No newline at end of file