From 82349d343277983889b5dc98e9ad6cc1ac167f51 Mon Sep 17 00:00:00 2001 From: Jan Date: Fri, 15 Jul 2022 11:21:54 +0200 Subject: [PATCH] Material loading base --- src/Common/Game/IW4/IW4_Assets.h | 6 +- src/ObjCommon/Game/IW4/ObjConstantsIW4.h | 1 + .../AssetLoading/AbstractGdtEntryReader.cpp | 82 +++++ .../AssetLoading/AbstractGdtEntryReader.h | 29 ++ .../IW4/AssetLoaders/AssetLoaderMaterial.cpp | 323 ++++++++++++++++++ .../IW4/AssetLoaders/AssetLoaderMaterial.h | 2 + .../IW4/AssetDumpers/AssetDumperMaterial.cpp | 3 +- 7 files changed, 442 insertions(+), 4 deletions(-) create mode 100644 src/ObjLoading/AssetLoading/AbstractGdtEntryReader.cpp create mode 100644 src/ObjLoading/AssetLoading/AbstractGdtEntryReader.h diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index 85a5148b..10ef87e7 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -960,9 +960,9 @@ namespace IW4 struct GfxImage { GfxTexture texture; - char mapType; - char semantic; - char category; + unsigned char mapType; + unsigned char semantic; + unsigned char category; bool useSrgbReads; Picmip picmip; bool noPicmip; diff --git a/src/ObjCommon/Game/IW4/ObjConstantsIW4.h b/src/ObjCommon/Game/IW4/ObjConstantsIW4.h index 68a8fa4a..3f43d9d0 100644 --- a/src/ObjCommon/Game/IW4/ObjConstantsIW4.h +++ b/src/ObjCommon/Game/IW4/ObjConstantsIW4.h @@ -12,6 +12,7 @@ namespace IW4 static constexpr const char* INFO_STRING_PREFIX_VEHICLE = "VEHICLEFILE"; static constexpr const char* INFO_STRING_PREFIX_WEAPON = "WEAPONFILE"; + static constexpr const char* GDF_FILENAME_MATERIAL = "material.gdf"; static constexpr const char* GDF_FILENAME_PHYS_PRESET = "physpreset.gdf"; static constexpr const char* GDF_FILENAME_TRACER = "tracer.gdf"; static constexpr const char* GDF_FILENAME_VEHICLE = "vehicle.gdf"; diff --git a/src/ObjLoading/AssetLoading/AbstractGdtEntryReader.cpp b/src/ObjLoading/AssetLoading/AbstractGdtEntryReader.cpp new file mode 100644 index 00000000..f6e834d9 --- /dev/null +++ b/src/ObjLoading/AssetLoading/AbstractGdtEntryReader.cpp @@ -0,0 +1,82 @@ +#include "AbstractGdtEntryReader.h" + +#include + +GdtReadingException::GdtReadingException(std::string message) + : exception(message.c_str()), + m_message(std::move(message)) +{ +} + +const char* GdtReadingException::what() const +{ + return m_message.c_str(); +} + +AbstractGdtEntryReader::AbstractGdtEntryReader(const GdtEntry& entry) + : m_entry(entry) +{ +} + +std::string AbstractGdtEntryReader::ReadStringProperty(const std::string& propertyName, std::string defaultValue) const +{ + const auto foundProperty = m_entry.m_properties.find(propertyName); + if (foundProperty == m_entry.m_properties.end()) + return defaultValue; + + return foundProperty->second; +} + +bool AbstractGdtEntryReader::ReadBoolProperty(const std::string& propertyName, const bool defaultValue) const +{ + const auto foundProperty = m_entry.m_properties.find(propertyName); + if (foundProperty == m_entry.m_properties.end()) + return defaultValue; + + char* endPtr = nullptr; + const auto result = std::strtol(foundProperty->second.c_str(), &endPtr, 10); + if (endPtr != &foundProperty->second[foundProperty->second.size()]) + { + std::ostringstream ss; + ss << "\"" << foundProperty->second << "\" is not a valid boolean value"; + throw GdtReadingException(ss.str()); + } + + return result != 0; +} + +int AbstractGdtEntryReader::ReadIntegerProperty(const std::string& propertyName, const int defaultValue) const +{ + const auto foundProperty = m_entry.m_properties.find(propertyName); + if (foundProperty == m_entry.m_properties.end()) + return defaultValue; + + char* endPtr = nullptr; + const auto result = std::strtol(foundProperty->second.c_str(), &endPtr, 10); + if (endPtr != &foundProperty->second[foundProperty->second.size()]) + { + std::ostringstream ss; + ss << "\"" << foundProperty->second << "\" is not a valid integer value"; + throw GdtReadingException(ss.str()); + } + + return result; +} + +float AbstractGdtEntryReader::ReadFloatProperty(const std::string& propertyName, const float defaultValue) const +{ + const auto foundProperty = m_entry.m_properties.find(propertyName); + if (foundProperty == m_entry.m_properties.end()) + return defaultValue; + + char* endPtr = nullptr; + const auto result = std::strtof(foundProperty->second.c_str(), &endPtr); + if (endPtr != &foundProperty->second[foundProperty->second.size()]) + { + std::ostringstream ss; + ss << "\"" << foundProperty->second << "\" is not a valid float value"; + throw GdtReadingException(ss.str()); + } + + return result; +} diff --git a/src/ObjLoading/AssetLoading/AbstractGdtEntryReader.h b/src/ObjLoading/AssetLoading/AbstractGdtEntryReader.h new file mode 100644 index 00000000..10d4a54e --- /dev/null +++ b/src/ObjLoading/AssetLoading/AbstractGdtEntryReader.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "Utils/ClassUtils.h" +#include "Obj/Gdt/GdtEntry.h" + +class GdtReadingException : public std::exception +{ +public: + explicit GdtReadingException(std::string message); + _NODISCARD const char* what() const override; + +private: + std::string m_message; +}; + +class AbstractGdtEntryReader +{ +protected: + explicit AbstractGdtEntryReader(const GdtEntry& entry); + + _NODISCARD std::string ReadStringProperty(const std::string& propertyName, std::string defaultValue = std::string()) const; + _NODISCARD bool ReadBoolProperty(const std::string& propertyName, bool defaultValue = false) const; + _NODISCARD int ReadIntegerProperty(const std::string& propertyName, int defaultValue = 0) const; + _NODISCARD float ReadFloatProperty(const std::string& propertyName, float defaultValue = 0.0f) const; + + const GdtEntry& m_entry; +}; diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp index 36c31da9..a494d1ec 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp @@ -1,13 +1,309 @@ #include "AssetLoaderMaterial.h" #include +#include +#include #include "ObjLoading.h" +#include "AssetLoading/AbstractGdtEntryReader.h" +#include "Game/IW4/CommonIW4.h" #include "Game/IW4/IW4.h" +#include "Game/IW4/ObjConstantsIW4.h" #include "Pool/GlobalAssetPool.h" using namespace IW4; +namespace IW4 +{ + class MaterialGdtLoader : AbstractGdtEntryReader + { + public: + MaterialGdtLoader(const GdtEntry& entry, MemoryManager* memory, IAssetLoadingManager* manager) + : AbstractGdtEntryReader(entry), + m_memory(memory), + m_manager(manager), + m_material(nullptr) + { + } + + bool Load() + { + m_material = m_memory->Create(); + memset(m_material, 0, sizeof(Material)); + + m_material->info.name = m_memory->Dup(m_entry.m_name.c_str()); + material_template(); + + FinalizeMaterial(); + return true; + } + + _NODISCARD Material* GetMaterial() const + { + return m_material; + } + + _NODISCARD std::vector GetDependencies() + { + return std::move(m_dependencies); + } + + private: + void material_template() + { + const auto materialType = ReadStringProperty("materialType"); + + if (materialType == "model phong" || materialType == "world phong" || materialType == "impact mark") + { + mtl_phong_template(); + } + else if (materialType == "model ambient") + { + mtl_ambient_template(); + } + else if (materialType == "2d") + { + mtl_2d_template(); + } + else if (materialType == "model unlit" || materialType == "world unlit") + { + mtl_unlit_template(); + } + else if (materialType == "unlit") + { + mtl_unlit_deprecated_template(); + } + else if (materialType == "effect") + { + mtl_effect_template(); + } + else if (materialType == "distortion") + { + mtl_distortion_template(); + } + else if (materialType == "particle cloud") + { + mtl_particlecloud_template(); + } + else if (materialType == "tools") + { + mtl_tools_template(); + } + else if (materialType == "sky") + { + mtl_sky_template(); + } + else if (materialType == "water") + { + mtl_water_template(); + } + else if (materialType == "objective") + { + mtl_objective_template(); + } + else if (materialType == "custom") + { + custom_template(); + } + else + { + std::ostringstream ss; + ss << "Unknown material type: \"" << materialType << "\""; + throw GdtReadingException(ss.str()); + } + } + + void mtl_phong_template() + { + } + + void mtl_ambient_template() + { + } + + void mtl_2d_template() + { + commonsetup_template(); + + SetTechniqueSet("2d"); + + const auto colorMapName = ReadStringProperty("colorMap"); + + if (!colorMapName.empty()) + AddMapTexture("colorMap", TS_2D, colorMapName); + else + throw GdtReadingException("ColorMap may not be blank in 2d materials"); + } + + void mtl_unlit_template() + { + } + + void mtl_unlit_deprecated_template() + { + } + + void mtl_effect_template() + { + } + + void mtl_distortion_template() + { + } + + void mtl_particlecloud_template() + { + } + + void mtl_tools_template() + { + } + + void mtl_sky_template() + { + } + + void mtl_water_template() + { + } + + void mtl_objective_template() + { + } + + void custom_template() + { + const auto customTemplate = ReadStringProperty("customTemplate"); + + if (customTemplate == "mtl_custom") + { + mtl_custom_template(); + } + else if (customTemplate == "mtl_phong_flag") + { + mtl_phong_flag_template(); + } + else if (customTemplate == "mtl_grain_overlay") + { + mtl_grain_overlay_template(); + } + else if (customTemplate == "mtl_effect_eyeoffset") + { + mtl_effect_eyeoffset_template(); + } + else if (customTemplate == "mtl_reflexsight") + { + mtl_reflexsight_template(); + } + else if (customTemplate == "mtl_shadowclear") + { + mtl_shadowclear_template(); + } + else if (customTemplate == "mtl_shadowoverlay") + { + mtl_shadowoverlay_template(); + } + else if (customTemplate == "mtl_splatter") + { + mtl_splatter_template(); + } + else + { + std::ostringstream ss; + ss << "Unknown custom template: \"" << customTemplate << "\""; + throw GdtReadingException(ss.str()); + } + } + + void mtl_custom_template() + { + } + + void mtl_phong_flag_template() + { + } + + void mtl_grain_overlay_template() + { + } + + void mtl_effect_eyeoffset_template() + { + } + + void mtl_reflexsight_template() + { + } + + void mtl_shadowclear_template() + { + } + + void mtl_shadowoverlay_template() + { + } + + void mtl_splatter_template() + { + } + + void commonsetup_template() + { + } + + void SetTechniqueSet(const std::string& techsetName) + { + auto* techset = reinterpret_cast*>(m_manager->LoadDependency(ASSET_TYPE_TECHNIQUE_SET, techsetName)); + + if (techset == nullptr) + { + std::ostringstream ss; + ss << "Could not load techset: \"" << techsetName << "\""; + throw GdtReadingException(ss.str()); + } + + m_dependencies.push_back(techset); + m_material->techniqueSet = techset->Asset(); + } + + void AddMapTexture(const std::string& typeName, const TextureSemantic semantic, const std::string& textureName) + { + MaterialTextureDef textureDef{}; + textureDef.nameHash = Common::R_HashString(typeName.c_str()); + textureDef.nameStart = typeName[0]; + textureDef.nameEnd = typeName[typeName.size() - 1]; + textureDef.samplerState = 0; // TODO + textureDef.semantic = static_cast(semantic); + + auto* image = reinterpret_cast*>(m_manager->LoadDependency(ASSET_TYPE_IMAGE, textureName)); + + if (image == nullptr) + { + std::ostringstream ss; + ss << "Could not load image: \"" << textureName << "\""; + throw GdtReadingException(ss.str()); + } + + m_dependencies.push_back(image); + textureDef.u.image = image->Asset(); + + m_texture.push_back(textureDef); + } + + void FinalizeMaterial() + { + // TODO Realloc all arrays and assign to material + } + + MemoryManager* m_memory; + IAssetLoadingManager* m_manager; + std::vector m_dependencies; + + Material* m_material; + std::vector m_texture; + }; +} + void* AssetLoaderMaterial::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) { auto* material = memory->Create(); @@ -15,3 +311,30 @@ void* AssetLoaderMaterial::CreateEmptyAsset(const std::string& assetName, Memory material->info.name = memory->Dup(assetName.c_str()); return material; } + +bool AssetLoaderMaterial::CanLoadFromGdt() const +{ + return true; +} + +bool AssetLoaderMaterial::LoadFromGdt(const std::string& assetName, IGdtQueryable* gdtQueryable, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const +{ + const auto* entry = gdtQueryable->GetGdtEntryByGdfAndName(ObjConstants::GDF_FILENAME_MATERIAL, assetName); + if (!entry) + return false; + + MaterialGdtLoader loader(*entry, memory, manager); + + try + { + if (loader.Load()) + manager->AddAsset(ASSET_TYPE_MATERIAL, assetName, loader.GetMaterial(), loader.GetDependencies(), std::vector()); + } + catch (const GdtReadingException& e) + { + std::cerr << "Error while trying to load material from gdt: " << e.what() << " @ GdtEntry \"" << entry->m_name << "\"\n"; + return false; + } + + return true; +} diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.h b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.h index d1f15b67..26c36333 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.h +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.h @@ -10,5 +10,7 @@ namespace IW4 { public: _NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override; + _NODISCARD bool CanLoadFromGdt() const override; + bool LoadFromGdt(const std::string& assetName, IGdtQueryable* gdtQueryable, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const override; }; } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp index 87ecbff3..17a416fa 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp @@ -9,6 +9,7 @@ #include "Utils/ClassUtils.h" #include "Game/IW4/MaterialConstantsIW4.h" +#include "Game/IW4/ObjConstantsIW4.h" #include "Game/IW4/TechsetConstantsIW4.h" #include "Math/Vector.h" @@ -1431,7 +1432,7 @@ namespace IW4 GdtEntry& CreateGdtEntry() { m_entry = GdtEntry(); - m_entry.m_gdf_name = "material.gdf"; + m_entry.m_gdf_name = ObjConstants::GDF_FILENAME_MATERIAL; m_entry.m_name = m_material->info.name; SetCommonValues();