diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a16c6a76..890c02ae5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +### Fixed + +- **Material sampler discovery now uses the supported API on each engine version.** The material and generic-asset index paths share one compatibility boundary that calls `UMaterialExpressionTextureBase` on UE 5.7 and `MaterialExpressionUtils` on UE 5.8+, removing both sampler deprecation warnings without duplicating version branches. + ## [0.22.0] - 2026-08-01 ### Internal diff --git a/Docs/specs/SPEC_MonolithCore.md b/Docs/specs/SPEC_MonolithCore.md index 3a8ba4848..a9076173f 100644 --- a/Docs/specs/SPEC_MonolithCore.md +++ b/Docs/specs/SPEC_MonolithCore.md @@ -34,6 +34,7 @@ | Symbol | Header | Responsibility | |--------|--------|---------------| +| `MonolithMaterialSamplerCompat::GetSamplerTypeForTexture` | `MonolithMaterialSamplerCompat.h` (header-only inline) | Single UE 5.7/5.8 compatibility boundary for recommended material sampler selection. UE 5.7 routes to `UMaterialExpressionTextureBase`; UE 5.8+ routes to `MaterialExpressionUtils`, so consumers never call a deprecated engine API directly. | | `MonolithPinTypeGrammar::{TryParsePinType, ParsePinTypeFromString, PinTypeToString, ContainerPrefix, ResolveEnumByNameOrPath}` | `MonolithPinTypeGrammar.h` (header-only inline) | The single implementation of the MCP-friendly pin-type token grammar and its inverse — `bool` / `int` / `struct:Vector` / `enum:ESlateVisibility` / `array:object:StaticMesh` / `map:string:int` and so on. It previously existed twice (MonolithBlueprint + MonolithUI) and the copies drifted, which is what shipped enum widget variables that compiled to `int` (issue #115). `TryParsePinType` is the preferred entry point: it fails **by token** — an `object:` / `class:` / `struct:` / `enum:` / `softobject:` / `softclass:` sub-object that does not resolve, an unknown base token, or a bad container value type is a hard `false` with a caller-facing reason, and `Out` is left untouched. `ParsePinTypeFromString` keeps the historical best-effort shape (bool fallback, null sub-object) for un-migrated call sites. **Linkage invariant:** MonolithCore does not link `BlueprintGraph`, so this header is inline-only and must NEVER be included from a MonolithCore `.cpp` (or a MonolithCore test) — the `UEdGraphSchema_K2::PC_*` constants are dllimport'd from `BlueprintGraph` and referencing them from a MonolithCore translation unit is an LNK2019. Same pattern, and same reason, as `MonolithPropertyAccessReader.h` and `MonolithAnimNodeBindingReader.h`. The modules that do link `BlueprintGraph` and may include it from a `.cpp`: MonolithAI, MonolithAnimation, MonolithBlueprint, MonolithComboGraph, MonolithGAS, MonolithIndex, MonolithLevelSequence, MonolithLogicDriver, MonolithUI. | | `MonolithCore::ValidatePackagePath(const FString&)` | `MonolithPackagePathValidator.h` (inline) | Wraps `FPackageName::IsValidLongPackageName` with an empty-string-on-success / error-msg-on-failure contract. Rejects empty input, double-slash (`//Game/...`), missing `/Game/` root, trailing slash, illegal chars. Added `dv.367` after a fatal `UObjectGlobals.cpp:1012` ensure from a malformed `//Game/...` JSON payload reaching `CreatePackage`. Routing is incremental and module-keyed. Current owners: MonolithUI (`HandleCreateWidgetBlueprint`, the original crash site), MonolithAI (`MonolithAIInternal::GetOrCreatePackage`), MonolithGAS (`MonolithGASInternal::GetOrCreatePackage`); MonolithBlueprint, MonolithMaterial and MonolithNiagara are being wired now. Grep the Source tree for `ValidatePackagePath` for the current owner list rather than trusting a count here. | diff --git a/Docs/specs/SPEC_MonolithIndex.md b/Docs/specs/SPEC_MonolithIndex.md index 6cd777d6f..cd8513c38 100644 --- a/Docs/specs/SPEC_MonolithIndex.md +++ b/Docs/specs/SPEC_MonolithIndex.md @@ -27,7 +27,7 @@ | `FGameplayTagIndexer` | GameplayTag containers — tag hierarchies and references | | `FConfigIndexer` | Config/INI files — sections, keys, values across config hierarchy | | `FCppIndexer` | C++ source files — classes, functions, includes (project-level source) | -| `FGenericAssetIndexer` | StaticMesh, SkeletalMesh, Texture2D, SoundWave, etc. — metadata nodes | +| `FGenericAssetIndexer` | StaticMesh, SkeletalMesh, Texture2D, SoundWave, etc. — metadata nodes. Texture `recommended_sampler_type` uses the shared `MonolithMaterialSamplerCompat` UE 5.7/5.8 boundary. | | `FDependencyIndexer` | Hard + Soft package dependencies (runs after all other indexers) | | `FMonolithIndexNotification` | Slate notification bar with throbber + percentage | diff --git a/Docs/specs/SPEC_MonolithMaterial.md b/Docs/specs/SPEC_MonolithMaterial.md index 5becd828a..95b4934c1 100644 --- a/Docs/specs/SPEC_MonolithMaterial.md +++ b/Docs/specs/SPEC_MonolithMaterial.md @@ -15,7 +15,7 @@ | Class | Responsibility | |-------|---------------| | `FMonolithMaterialModule` | Registers 63 material actions | -| `FMonolithMaterialActions` | Static handlers + helpers for loading materials and serializing expressions | +| `FMonolithMaterialActions` | Static handlers + helpers for loading materials and serializing expressions. Texture metadata routes sampler recommendation through the shared `MonolithMaterialSamplerCompat` UE 5.7/5.8 boundary. | ### Actions (63 — namespace: "material") diff --git a/Docs/testing/2026-08-04-ue58-material-sampler-api.md b/Docs/testing/2026-08-04-ue58-material-sampler-api.md new file mode 100644 index 000000000..278286e96 --- /dev/null +++ b/Docs/testing/2026-08-04-ue58-material-sampler-api.md @@ -0,0 +1,37 @@ +# UE 5.8 Material Sampler API Verification + +**Date:** 2026-08-04 +**Scope:** `MonolithCore`, `MonolithIndex`, and `MonolithMaterial` +**Related:** issue #121 + +--- + +## 1. Regression + +UE 5.8 deprecates `UMaterialExpressionTextureBase::GetSamplerTypeForTexture` in favor of `MaterialExpressionUtils::GetSamplerTypeForTexture`. UE 5.7 does not expose the replacement header, so an unconditional migration would break the supported compile floor. The deprecated call existed in both texture metadata consumers. + +## 2. Compatibility contract + +| Engine | Routed API | +|---|---| +| UE 5.7 | `UMaterialExpressionTextureBase::GetSamplerTypeForTexture` | +| UE 5.8+ | `MaterialExpressionUtils::GetSamplerTypeForTexture` | + +Both consumers call `MonolithMaterialSamplerCompat::GetSamplerTypeForTexture`; the engine-version boundary is defined once in `Source/MonolithCore/Public/MonolithMaterialSamplerCompat.h`. + +## 3. Verification + +| Gate | Expected result | Result | +|---|---|---| +| Repository diff audit | One compatibility boundary and two migrated consumers | Pass — `git diff --check` returned no errors | +| UE 5.7 plugin package build | Editor plugin compiles with the legacy API path | Pass — 434/434 actions, UAT exit 0 | +| UE 5.8 plugin package build | Editor plugin compiles without either sampler deprecation warning | Pass — 434/434 actions, UAT exit 0, zero matching sampler C4996 warnings | + +Both builds compiled `GenericAssetIndexer.cpp` and `MonolithMaterialActions.cpp` as individual actions. Engine roots were resolved from validation-host `.uproject` `EngineAssociation` values through `Build/BatchFiles/Script/ResolveUnrealEngine.ps1`. UAT logs were redirected to external evidence paths: + +- `D:\P4\MonolithValidation20260804\02-ue58-material-sampler\Logs\UE57` +- `D:\P4\MonolithValidation20260804\02-ue58-material-sampler\Logs\UE58` + +The authoritative command shape was `RunUAT BuildPlugin -NoTargetPlatforms -Rocket`. An earlier UE 5.7 run also completed all 434 compile actions, but its final UAT log flush failed because the system drive was full; the redirected-log exit-0 run above supersedes it. + +Issue #121 contains other engine deprecations that are intentionally outside this single-concern change. diff --git a/Source/MonolithCore/Public/MonolithMaterialSamplerCompat.h b/Source/MonolithCore/Public/MonolithMaterialSamplerCompat.h new file mode 100644 index 000000000..e812fefff --- /dev/null +++ b/Source/MonolithCore/Public/MonolithMaterialSamplerCompat.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Runtime/Launch/Resources/Version.h" + +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8 +#include "Materials/MaterialExpressionUtils.h" +#else +#include "Materials/MaterialExpressionTextureBase.h" +#endif + +class UTexture; + +namespace MonolithMaterialSamplerCompat +{ +#if WITH_EDITOR +/** + * Return Unreal's recommended sampler type without exposing engine-version API + * drift to each material or index consumer. + */ +FORCEINLINE EMaterialSamplerType GetSamplerTypeForTexture( + const UTexture* Texture, + bool bForceNoVirtualTexture = false) +{ +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8 + return MaterialExpressionUtils::GetSamplerTypeForTexture(Texture, bForceNoVirtualTexture); +#else + return UMaterialExpressionTextureBase::GetSamplerTypeForTexture(Texture, bForceNoVirtualTexture); +#endif +} +#endif +} diff --git a/Source/MonolithIndex/Private/Indexers/GenericAssetIndexer.cpp b/Source/MonolithIndex/Private/Indexers/GenericAssetIndexer.cpp index bcdba73d5..2640a4c75 100644 --- a/Source/MonolithIndex/Private/Indexers/GenericAssetIndexer.cpp +++ b/Source/MonolithIndex/Private/Indexers/GenericAssetIndexer.cpp @@ -1,8 +1,8 @@ #include "Indexers/GenericAssetIndexer.h" +#include "MonolithMaterialSamplerCompat.h" #include "Engine/StaticMesh.h" #include "Engine/SkeletalMesh.h" #include "Engine/Texture2D.h" -#include "Materials/MaterialExpressionTextureBase.h" #include "Sound/SoundWave.h" #include "Sound/SoundCue.h" #include "PhysicsEngine/PhysicsAsset.h" @@ -79,7 +79,7 @@ bool FGenericAssetIndexer::IndexAsset(const FAssetData& AssetData, UObject* Load Props->SetBoolField(TEXT("compression_no_alpha"), Tex->CompressionNoAlpha != 0); #endif // Recommended sampler type for material use - EMaterialSamplerType SamplerType = UMaterialExpressionTextureBase::GetSamplerTypeForTexture(Tex); + const EMaterialSamplerType SamplerType = MonolithMaterialSamplerCompat::GetSamplerTypeForTexture(Tex); UEnum* SamplerEnum = StaticEnum(); if (SamplerEnum) { diff --git a/Source/MonolithMaterial/Private/MonolithMaterialActions.cpp b/Source/MonolithMaterial/Private/MonolithMaterialActions.cpp index 9aa2f0114..33be38c4b 100644 --- a/Source/MonolithMaterial/Private/MonolithMaterialActions.cpp +++ b/Source/MonolithMaterial/Private/MonolithMaterialActions.cpp @@ -1,4 +1,5 @@ #include "MonolithMaterialActions.h" +#include "MonolithMaterialSamplerCompat.h" #include "MonolithToolRegistry.h" #include "MonolithParamSchema.h" #include "MonolithPackagePathValidator.h" @@ -9344,7 +9345,7 @@ static void PopulateTextureMetadata(UTexture* Tex, const TSharedPtr #endif // Recommended sampler type for material usage - EMaterialSamplerType SamplerType = UMaterialExpressionTextureBase::GetSamplerTypeForTexture(Tex); + const EMaterialSamplerType SamplerType = MonolithMaterialSamplerCompat::GetSamplerTypeForTexture(Tex); UEnum* SamplerEnum = StaticEnum(); if (SamplerEnum) {