diff --git a/primedev/engine/gl_matsysiface.cpp b/primedev/engine/gl_matsysiface.cpp index 075a56ac..6c88c38f 100644 --- a/primedev/engine/gl_matsysiface.cpp +++ b/primedev/engine/gl_matsysiface.cpp @@ -19,26 +19,46 @@ static void __fastcall h_CC_mat_crosshair_printmaterial_f(const CCommand& args) if (!pGlue) { - spdlog::info("|-- {} is NULL", szName); + spdlog::info("├ No reference material for {}", szName); return; } - spdlog::info("|-- Name: {}", szName); - spdlog::info("|-- GUID: {:#x}", pGlue->m_GUID); - spdlog::info("|-- Name: {}", pGlue->m_pszName); - spdlog::info("|-- Width : {}", pGlue->m_iWidth); - spdlog::info("|-- Height: {}", pGlue->m_iHeight); + spdlog::info("├ {}", szName); + spdlog::info("│├── GUID: {:#x}", pGlue->material.guid); + spdlog::info("│└── Name: {}", pGlue->material.name); }; - spdlog::info("|- GUID: {:#x}", pMat->m_GUID); - spdlog::info("|- Name: {}", pMat->m_pszName); - spdlog::info("|- Width : {}", pMat->m_iWidth); - spdlog::info("|- Height: {}", pMat->m_iHeight); + spdlog::info("────────────────────────────────────────────────────────────"); + spdlog::info("┌ Name: {}", pMat->material.name); + spdlog::info("├ GUID: {:#x}", pMat->material.guid); + spdlog::info("├ Width : {}", pMat->material.width); + spdlog::info("├ Height: {}", pMat->material.height); + spdlog::info("├ Shaderset: {}", pMat->material.shaderSet->inner.name); - fnPrintGlue(pMat->m_pDepthShadow, "DepthShadow"); - fnPrintGlue(pMat->m_pDepthPrepass, "DepthPrepass"); - fnPrintGlue(pMat->m_pDepthVSM, "DepthVSM"); - fnPrintGlue(pMat->m_pColPass, "ColPass"); + fnPrintGlue(pMat->material.DepthShadow_ref, "DepthShadow"); + fnPrintGlue(pMat->material.DepthPrepass_ref, "DepthPrepass"); + fnPrintGlue(pMat->material.DepthVSM_ref, "DepthVSM"); + fnPrintGlue(pMat->material.Colpass_ref, "Colpass"); + + if (pMat && pMat->material.shaderSet && pMat->material.shaderSet->inner.textureInputCount >= 1 && pMat->material.textureHandles) + { + spdlog::info("├ Textures"); + for (size_t slot = 0; slot < pMat->material.shaderSet->inner.textureInputCount + 1; ++slot) + { + RpakTextureHeader* currentTexture = pMat->material.textureHandles[slot]; + + if (currentTexture && currentTexture->name) + { + if (slot == pMat->material.shaderSet->inner.textureInputCount) + spdlog::info("│└[{}][{:#x}]{}", slot, currentTexture->guid, currentTexture->name); + else + spdlog::info("│├[{}][{:#x}]{}", slot, currentTexture->guid, currentTexture->name); + } + } + } + spdlog::info("├ Glueflags: {:#x}", pMat->material.flags); + spdlog::info("└ Glueflags2: {:#x}", pMat->material.flags2); + spdlog::info("────────────────────────────────────────────────────────────"); } ON_DLL_LOAD("engine.dll", GlMatSysIFace, (CModule module)) diff --git a/primedev/materialsystem/cmaterialglue.h b/primedev/materialsystem/cmaterialglue.h index 1738a91a..f57d6221 100644 --- a/primedev/materialsystem/cmaterialglue.h +++ b/primedev/materialsystem/cmaterialglue.h @@ -1,47 +1,124 @@ #pragma once #include "materialsystem/cshaderglue.h" +#include +class RpakTextureHeader // Expected size: 0x130 +{ +public: + uint64_t guid; + const char* name; + uint16_t width; + uint16_t height; + int16_t depth; + uint16_t dxgiFormat; + uint32_t dataSize; + uint8_t compressionType; + uint8_t optStreamedMipCount; + uint8_t arraySize; + uint8_t layerCount; + uint8_t mipFlags; + uint8_t permanentMipCount; + uint8_t streamedMipCount; + uint8_t unk[13]; + int64_t numPixels; + uint16_t unknownWord38; + uint8_t unknownByte3A; + uint8_t unknownByte3B; + uint32_t unknownDword3C; + uint32_t unknownDword40; + uint32_t unknownDword44; + float transform[16]; + uint64_t unknownQword88; + uint64_t unknownQword90; + uint32_t unknownArray98[16]; + uint32_t unknownArrayD8[16]; + ID3D11Resource* d3d11Resource; + ID3D11ShaderResourceView* shaderResourceView; + uint8_t unknownByte128; + uint8_t padding[7]; +}; + +struct CBufUberStatic // sizeof = 0xE0 +{ + float c_uv1RotScaleX[2]; // 0x00 + float c_uv1RotScaleY[2]; // 0x08 + float c_uv1Translate[2]; // 0x10 + float c_uv2RotScaleX[2]; // 0x18 + float c_uv2RotScaleY[2]; // 0x20 + float c_uv2Translate[2]; // 0x28 + float c_uv3RotScaleX[2]; // 0x30 + float c_uv3RotScaleY[2]; // 0x38 + float c_uv3Translate[2]; // 0x40 + float c_uvDistortionIntensity[2]; // 0x48 + float c_uvDistortion2Intensity[2]; // 0x50 + float c_fogColorFactor; // 0x58 + float c_layerBlendRamp; // 0x5C + float c_albedoTint[3]; // 0x60 + float c_opacity; // 0x6C + float c_useAlphaModulateSpecular; // 0x70 + float c_alphaEdgeFadeExponent; // 0x74 + float c_alphaEdgeFadeInner; // 0x78 + float c_alphaEdgeFadeOuter; // 0x7C + float c_useAlphaModulateEmissive; // 0x80 + float c_emissiveEdgeFadeExponent; // 0x84 + float c_emissiveEdgeFadeInner; // 0x88 + float c_emissiveEdgeFadeOuter; // 0x8C + float c_alphaDistanceFadeScale; // 0x90 + float c_alphaDistanceFadeBias; // 0x94 + float c_alphaTestReference; // 0x98 + float c_aspectRatioMulV; // 0x9C + float c_emissiveTint[3]; // 0xA0 + float c_shadowBias; // 0xAC + float c_tsaaDepthAlphaThreshold; // 0xB0 + float c_tsaaMotionAlphaThreshold; // 0xB4 + float c_tsaaMotionAlphaRamp; // 0xB8 + uint32_t c_tsaaResponsiveFlag; // 0xBC + float c_dofOpacityLuminanceScale; // 0xC0 + float c_glitchStrength; // 0xC4 + float c_padding[2]; // 0xC8 + float c_perfGloss; // 0xD0 + float c_perfSpecColor[3]; // 0xD4 +}; + +class CMaterialGlue; + +class CMaterialGlue_short +{ +public: + uint64_t guid; + const char* name; + uint64_t* surfaceProps[2]; + CMaterialGlue* DepthShadow_ref; + CMaterialGlue* DepthPrepass_ref; + CMaterialGlue* DepthVSM_ref; + CMaterialGlue* Colpass_ref; + uint8_t gap_50[64]; + ShaderGlue* shaderSet; + RpakTextureHeader** textureHandles; + RpakTextureHeader** streamingTextures; + int16_t streamingTextureCount; + uint8_t samplersIndices[4]; + int16_t unknown; + uint8_t gap_B0[12]; + uint16_t unknownWord[2]; + uint32_t flags; + uint32_t flags2; + uint16_t width; + uint16_t height; + uint8_t gap_CC[2]; + uint16_t word_CE; + void** pointer_D0; + CBufUberStatic* cbufUberStatic; + ID3D11Buffer* buffer; + uint32_t* atlasBufferIndices; + uint32_t dword_F0; + uint8_t gap_F4[12]; +}; class CMaterialGlue { public: void* m_pVFTable; char m_unk[8]; - - uint64_t m_GUID; - - const char* m_pszName; - const char* m_pszSurfaceProp; - const char* m_pszSurfaceProp2; - - CMaterialGlue* m_pDepthShadow; - CMaterialGlue* m_pDepthPrepass; - CMaterialGlue* m_pDepthVSM; - CMaterialGlue* m_pColPass; - - char gap_50[64]; - - CShaderGlue* m_pShaderGlue; - void** m_pTextureHandles; - void** m_pStreamingTextures; - int16_t m_iStreamingTextureCount; - uint8_t m_iSamplersIndices[4]; - int16_t m_iUnknown0; - char gap_B0[12]; - - int16_t aword_BC[2]; - int32_t flags2; - int32_t flags3; - int16_t m_iWidth; - int16_t m_iHeight; - int16_t m_iUnknown1; - int16_t m_iUnknown2; - - void** m_pUnkD3D11Ptr; - void* m_pD3D11Buffer; - void* qword_E0; - void* pointer_E8; - - int32_t dword_F0; - char gap_F4[12]; + CMaterialGlue_short material; }; diff --git a/primedev/materialsystem/cshaderglue.h b/primedev/materialsystem/cshaderglue.h index 8194f1fb..85653b7d 100644 --- a/primedev/materialsystem/cshaderglue.h +++ b/primedev/materialsystem/cshaderglue.h @@ -5,3 +5,28 @@ class CShaderGlue public: void* vftable; }; + +struct ShaderGlue_inner +{ + const char* name; + uint64_t flagsMaybe; + + uint16_t resourceBindingSlot; + uint16_t textureInputCount; + int16_t shadowSamplerCount; + uint16_t unkBindingSlot; + uint16_t unkBindingCount; + + uint8_t bytes_22[6]; + + uint64_t unk3[4]; + + void* vertexShader; + void* pixelShader; +}; + +struct ShaderGlue +{ + void* vtable; + ShaderGlue_inner inner; +}; diff --git a/primedev/materialsystem/nscustomdxbuffer.cpp b/primedev/materialsystem/nscustomdxbuffer.cpp index 56dbe1ea..7269124e 100644 --- a/primedev/materialsystem/nscustomdxbuffer.cpp +++ b/primedev/materialsystem/nscustomdxbuffer.cpp @@ -6,6 +6,8 @@ #include #include #include +#include "core/filesystem/rpakfilesystem.h" +#include "cmaterialglue.h" static ID3D11DeviceContext** DeviceContext; static ID3D11Device** D3D11Device_14E8DD0; @@ -15,6 +17,12 @@ struct Ns_Constant_Buffer float data[320]; }; +struct MaterialTextureMappings +{ + + std::array slots; +}; + AUTOHOOK_INIT() static Ns_Constant_Buffer NSCustomDXBuffer; @@ -22,15 +30,48 @@ static std::mutex NSCustomDXBufferMutex; // map to later on associate guid > buffer static std::map NSCustomBuffersPerMaterial = {}; +static std::map NSMaterialTextureSlotBindings = {}; +static std::unordered_set NSRegisteredCustomBufferMaterials = {}; +static std::unordered_set NSRegisteredTextureOverrides = {}; -AUTOHOOK(SUB_511D0, materialsystem_dx11.dll + 0x511D0, __int64, __fastcall, (__int64 a1, __int64 a2, __int64 a3, __int64 a4)) +AUTOHOOK(SUB_511D0, materialsystem_dx11.dll + 0x511D0, __int64, __fastcall, (__int64 a1, __int64 a2, __int64 a3, CMaterialGlue_short* a4)) { - int64_t subResult = SUB_511D0(a1, a2, a3, a4); + CMaterialGlue_short* internal_logic_material = a4; - uint32_t glueFlags = *(uint32_t*)((uint8_t*)a4 + 176); + int64_t subResult = SUB_511D0(a1, a2, a3, internal_logic_material); + + if (!DeviceContext || !D3D11Device_14E8DD0 || !*DeviceContext || !*D3D11Device_14E8DD0) + return subResult; + + // bind textures to slots if existing + if (NSMaterialTextureSlotBindings.contains(internal_logic_material->guid)) + { + + auto& mappings = NSMaterialTextureSlotBindings[internal_logic_material->guid]; + + for (size_t slot = 0; slot < mappings.slots.size(); ++slot) + { + uint64_t textureGUID = mappings.slots[slot]; + + if (textureGUID == 0) + continue; + + __int64 texturePointer = g_pakLoadApi->GetAssetByHash(textureGUID); + if (!texturePointer) + continue; + + RpakTextureHeader* TextureHeader = (RpakTextureHeader*)texturePointer; + ID3D11ShaderResourceView* TextureSRV = TextureHeader->shaderResourceView; + + if (!TextureSRV) + continue; + + (*DeviceContext)->PSSetShaderResources(slot, 1, &TextureSRV); + } + } // bind custom buffer when flag is 0x04089901 in rpak mat - if ((glueFlags & 0x04089901) == 0x04089901) + if (NSRegisteredCustomBufferMaterials.contains(internal_logic_material->guid)) { D3D11_BUFFER_DESC desc {}; @@ -62,7 +103,7 @@ AUTOHOOK(SUB_511D0, materialsystem_dx11.dll + 0x511D0, __int64, __fastcall, (__i std::lock_guard lock(NSCustomDXBufferMutex); - memcpy(pData, &NSCustomBuffersPerMaterial[*(uint64_t*)a4], sizeof(Ns_Constant_Buffer)); + memcpy(pData, &NSCustomBuffersPerMaterial[internal_logic_material->guid], sizeof(Ns_Constant_Buffer)); (*DeviceContext)->Unmap(resource, 0); (*DeviceContext)->PSSetConstantBuffers(4, 1, &resource); @@ -86,7 +127,73 @@ bool isValidMaterialGUID(const std::string& str) return true; } -template SQRESULT NSSetCustomDXBuffer(HSQUIRRELVM sqvm) +SQRESULT NSRegisterCustomDXBufferForGUID(HSQUIRRELVM sqvm) +{ + + auto rPakMaterialGUIDString = (g_pSquirrel[ScriptContext::CLIENT]->getstring(sqvm, 1)); + uint64_t rPakMaterialGUID = std::stoull(rPakMaterialGUIDString, nullptr, 16); + + __int64 AssetFromGUID = g_pakLoadApi->GetAssetByHash(rPakMaterialGUID); + + if (!AssetFromGUID) + { + g_pSquirrel[ScriptContext::CLIENT]->raiseerror( + sqvm, fmt::format("Asset with GUID {} Doesnt Exist", rPakMaterialGUIDString).c_str()); + return SQRESULT_ERROR; + } + + // we need to add 16 to the pointer, matglueshort is matglue without the first 16 bytes. GetAssetByHash returns a pointer to the full + auto* base = reinterpret_cast(AssetFromGUID); + auto* GUIDMaterialGlue_short = reinterpret_cast(base + 16); + + if (!NSRegisteredCustomBufferMaterials.contains(GUIDMaterialGlue_short->guid)) + { + NS::log::SCRIPT_CL->info("Registered GUID: {} to use the NSCustomDXBuffer system", GUIDMaterialGlue_short->guid); + + NSRegisteredCustomBufferMaterials.insert(GUIDMaterialGlue_short->guid); + } + else + { + NS::log::SCRIPT_CL->warn( + "Attempted to register GUID: {} to the NSCustomDXBuffer system, GUID was already registered", GUIDMaterialGlue_short->guid); + } + return SQRESULT_NULL; +} + +SQRESULT NSDeregisterCustomDXBufferForGUID(HSQUIRRELVM sqvm) +{ + + auto rPakMaterialGUIDString = (g_pSquirrel[ScriptContext::CLIENT]->getstring(sqvm, 1)); + uint64_t rPakMaterialGUID = std::stoull(rPakMaterialGUIDString, nullptr, 16); + + __int64 AssetFromGUID = g_pakLoadApi->GetAssetByHash(rPakMaterialGUID); + + if (!AssetFromGUID) + { + g_pSquirrel[ScriptContext::CLIENT]->raiseerror( + sqvm, fmt::format("Asset with GUID {} Doesnt Exist", rPakMaterialGUIDString).c_str()); + return SQRESULT_ERROR; + } + + // we need to add 16 to the pointer, matglueshort is matglue without the first 16 bytes. GetAssetByHash returns a pointer to the full + auto* base = reinterpret_cast(AssetFromGUID); + auto* GUIDMaterialGlue_short = reinterpret_cast(base + 16); + + if (NSRegisteredCustomBufferMaterials.contains(GUIDMaterialGlue_short->guid)) + { + NS::log::SCRIPT_CL->info("Deregistered GUID: {} from the NSCustomDXBuffer system", GUIDMaterialGlue_short->guid); + + NSRegisteredCustomBufferMaterials.erase(GUIDMaterialGlue_short->guid); + } + else + { + NS::log::SCRIPT_CL->warn( + "Attempted to deregister GUID: {} from the NSCustomDXBuffer system, GUID was not registered", GUIDMaterialGlue_short->guid); + } + return SQRESULT_NULL; +} + +SQRESULT NSUpdateCustomDXBufferForGUID(HSQUIRRELVM sqvm) { // get the guid as a string to later conv @@ -107,7 +214,7 @@ template SQRESULT NSSetCustomDXBuffer(HSQUIRRELVM sqvm) if (!isValidMaterialGUID(guidString)) { - g_pSquirrel[ScriptContext::SERVER]->raiseerror( + g_pSquirrel[ScriptContext::CLIENT]->raiseerror( sqvm, fmt::format("Malformed Material GUID", (NSCustomBufferDataArray->_usedSlots) * 4, sizeof(Ns_Constant_Buffer)).c_str()); return SQRESULT_ERROR; } @@ -118,7 +225,7 @@ template SQRESULT NSSetCustomDXBuffer(HSQUIRRELVM sqvm) if ((NSCustomBufferDataArray->_usedSlots) * 4 > sizeof(Ns_Constant_Buffer)) { - g_pSquirrel[ScriptContext::SERVER]->raiseerror( + g_pSquirrel[ScriptContext::CLIENT]->raiseerror( sqvm, fmt::format( "Size of Squirrel array exceeds NSCustomDXBuffer size\n\nSquirrel " @@ -136,16 +243,75 @@ template SQRESULT NSSetCustomDXBuffer(HSQUIRRELVM sqvm) return SQRESULT_NULL; } +SQRESULT NSBindTextureToMaterial(HSQUIRRELVM sqvm) +{ + + auto rPakMaterialGUIDString = (g_pSquirrel[ScriptContext::CLIENT]->getstring(sqvm, 1)); + auto rPakTextureGUIDString = (g_pSquirrel[ScriptContext::CLIENT]->getstring(sqvm, 2)); + auto rPakShaderSlotBindingInt = (g_pSquirrel[ScriptContext::CLIENT]->getinteger(sqvm, 3)); + + uint64_t rPakMaterialGUID = std::stoull(rPakMaterialGUIDString, nullptr, 16); + __int64 MatAssetFromGUID = g_pakLoadApi->GetAssetByHash(rPakMaterialGUID); + + if (rPakShaderSlotBindingInt > 60) + { + g_pSquirrel[ScriptContext::CLIENT]->raiseerror(sqvm, fmt::format("TextureOverrides only support 30 custom bindings").c_str()); + return SQRESULT_ERROR; + } + + if (!MatAssetFromGUID) + { + g_pSquirrel[ScriptContext::CLIENT]->raiseerror( + sqvm, fmt::format("Material with GUID {} Doesnt Exist", rPakMaterialGUIDString).c_str()); + return SQRESULT_ERROR; + } + + auto* base = reinterpret_cast(MatAssetFromGUID); + auto* GUIDMaterialGlue_short = reinterpret_cast(base + 16); + + if (!rPakTextureGUIDString == 0) + { + uint64_t rPakTextureGUID = std::stoull(rPakTextureGUIDString, nullptr, 16); + __int64 TexAssetFromGUID = g_pakLoadApi->GetAssetByHash(rPakTextureGUID); + + if (!TexAssetFromGUID) + { + g_pSquirrel[ScriptContext::CLIENT]->raiseerror( + sqvm, fmt::format("Texture with GUID {} Doesnt Exist", rPakTextureGUIDString).c_str()); + return SQRESULT_ERROR; + } + NSMaterialTextureSlotBindings[GUIDMaterialGlue_short->guid].slots[rPakShaderSlotBindingInt] = rPakTextureGUID; + } + else + NSMaterialTextureSlotBindings[GUIDMaterialGlue_short->guid].slots[rPakShaderSlotBindingInt] = 0; + + return SQRESULT_NULL; +} + ON_DLL_LOAD_CLIENT("materialsystem_dx11.dll", SUB_511D0, (CModule module)) { AUTOHOOK_DISPATCH_MODULE(materialsystem_dx11.dll) DeviceContext = module.Offset(0x14E8DD8).RCast(); D3D11Device_14E8DD0 = module.Offset(0x14E8DD0).RCast(); + g_pSquirrel[ScriptContext::CLIENT]->AddFuncRegistration( "void", - "NSSetCustomDXBuffer", + "NSUpdateCustomDXBufferForGUID", "string rPakMaterialGUID array NSCustomBufferPerMaterialData", "", - NSSetCustomDXBuffer); + NSUpdateCustomDXBufferForGUID); + + g_pSquirrel[ScriptContext::CLIENT]->AddFuncRegistration( + "void", "NSRegisterCustomDXBufferForGUID", "string rPakMaterialGUID", "", NSRegisterCustomDXBufferForGUID); + + g_pSquirrel[ScriptContext::CLIENT]->AddFuncRegistration( + "void", "NSDeregisterCustomDXBufferForGUID", "string rPakMaterialGUID", "", NSDeregisterCustomDXBufferForGUID); + + g_pSquirrel[ScriptContext::CLIENT]->AddFuncRegistration( + "void", + "NSBindTextureToMaterial", + "string rPakMaterialGUID string rPakTextureGUID int shaderBindingSlot", + "", + NSBindTextureToMaterial); }