#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/MaterialConstantsIW4.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 == GDT_MATERIAL_TYPE_MODEL_PHONG || materialType == GDT_MATERIAL_TYPE_WORLD_PHONG || materialType == GDT_MATERIAL_TYPE_IMPACT_MARK) { mtl_phong_template(); } else if (materialType == GDT_MATERIAL_TYPE_MODEL_AMBIENT) { mtl_ambient_template(); } else if (materialType == GDT_MATERIAL_TYPE_2D) { mtl_2d_template(); } else if (materialType == GDT_MATERIAL_TYPE_MODEL_UNLIT || materialType == GDT_MATERIAL_TYPE_WORLD_UNLIT) { mtl_unlit_template(); } else if (materialType == GDT_MATERIAL_TYPE_UNLIT) { mtl_unlit_deprecated_template(); } else if (materialType == GDT_MATERIAL_TYPE_EFFECT) { mtl_effect_template(); } else if (materialType == GDT_MATERIAL_TYPE_DISTORTION) { mtl_distortion_template(); } else if (materialType == GDT_MATERIAL_TYPE_PARTICLE_CLOUD) { mtl_particlecloud_template(); } else if (materialType == GDT_MATERIAL_TYPE_TOOLS) { mtl_tools_template(); } else if (materialType == GDT_MATERIAL_TYPE_SKY) { mtl_sky_template(); } else if (materialType == GDT_MATERIAL_TYPE_WATER) { mtl_water_template(); } else if (materialType == GDT_MATERIAL_TYPE_OBJECTIVE) { mtl_objective_template(); } else if (materialType == GDT_MATERIAL_TYPE_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 == GDT_CUSTOM_MATERIAL_TYPE_CUSTOM) { mtl_custom_template(); } else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_PHONG_FLAG) { mtl_phong_flag_template(); } else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_GRAIN_OVERLAY) { mtl_grain_overlay_template(); } else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_EFFECT_EYE_OFFSET) { mtl_effect_eyeoffset_template(); } else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_REFLEXSIGHT) { mtl_reflexsight_template(); } else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_SHADOWCLEAR) { mtl_shadowclear_template(); } else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_SHADOWOVERLAY) { mtl_shadowoverlay_template(); } else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_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() { refblend_template(); sort_template(); clamp_template(); // tessSize textureAtlas_template(); // hasEditorMaterial // allocLightmap statebits_template(); } void refblend_template() { const auto blendFunc = ReadStringProperty("blendFunc"); } void sort_template() { const auto sort = ReadStringProperty("sort"); const auto materialType = ReadStringProperty("materialType"); const auto polygonOffset = ReadStringProperty("polygonOffset"); const auto blendFunc = ReadStringProperty("blendFunc"); std::string sortKey; if (sort.empty() || sort == GDT_SORTKEY_DEFAULT) { if (materialType == GDT_MATERIAL_TYPE_DISTORTION) sortKey = GDT_SORTKEY_DISTORTION; else if (polygonOffset == "Static Decal") sortKey = GDT_SORTKEY_DECAL_STATIC; else if (polygonOffset == "Weapon Impact") sortKey = GDT_SORTKEY_DECAL_WEAPON_IMPACT; else if (materialType == GDT_MATERIAL_TYPE_EFFECT) sortKey = GDT_SORTKEY_EFFECT_AUTO_SORT; else if (materialType == GDT_MATERIAL_TYPE_OBJECTIVE || blendFunc == "Blend" || blendFunc == "Add" || blendFunc == "Screen Add") sortKey = GDT_SORTKEY_BLEND_ADDITIVE; // else if (blendFunc == "Multiply") // TODO // sortKey = GDT_SORTKEY_MULTIPLICATIVE; else if (materialType == GDT_MATERIAL_TYPE_SKY) sortKey = GDT_SORTKEY_SKY; else if (materialType == GDT_MATERIAL_TYPE_MODEL_AMBIENT) sortKey = GDT_SORTKEY_OPAQUE_AMBIENT; else sortKey = GDT_SORTKEY_OPAQUE; } else sortKey = sort; bool foundSortKey = false; for (auto sortKeyIndex = 0u; sortKeyIndex < SORTKEY_MAX; sortKeyIndex++) { if (sortKey == SortKeyNames[sortKeyIndex]) { SetSort(static_cast(sortKeyIndex)); foundSortKey = true; break; } } if (!foundSortKey) { char* endPtr; const auto sortKeyNum = strtoul(sortKey.c_str(), &endPtr, 10); if (endPtr != &sortKey[sortKey.size()]) { std::ostringstream ss; ss << "Invalid sortkey value: \"" << sortKey << "\""; throw GdtReadingException(ss.str()); } SetSort(static_cast(sortKeyNum)); } } void clamp_template() { } void textureAtlas_template() { } void statebits_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_textures.push_back(textureDef); } void SetSort(const unsigned char sort) const { m_material->info.sortKey = sort; } void FinalizeMaterial() const { if (!m_textures.empty()) { m_material->textureTable = static_cast(m_memory->Alloc(sizeof(MaterialTextureDef) * m_textures.size())); m_material->textureCount = static_cast(m_textures.size()); memcpy(m_material->textureTable, m_textures.data(), sizeof(MaterialTextureDef) * m_textures.size()); } else { m_material->textureTable = nullptr; m_material->textureCount = 0u; } } MemoryManager* m_memory; IAssetLoadingManager* m_manager; std::vector m_dependencies; Material* m_material; std::vector m_textures; }; } void* AssetLoaderMaterial::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) { auto* material = memory->Create(); memset(material, 0, sizeof(Material)); 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; }