[{"id":"MI1","fact":"Every property edit in the Material Instance Editor rewrites the instance's parameter overrides and then unconditionally adds a scalar `RefractionDepthBias` entry, so an instance saved after any edit carries that override, valued at what the editor read when it set the instance up (the instance's own entry, else the nearest parent's, down to `UMaterial::RefractionDepthBias`, default 0) unless the user changed that field. `PostEditChangeProperty` calls `CopyToSourceInstance`, which opens a parameter update context with `EMaterialInstanceClearParameterFlag::All` (empties the numeric arrays), re-adds only the overridden parameters, then calls `SetScalarParameterValueEditorOnly` for `RefractionDepthBias`, which appends an entry when none exists. From then on the instance's own entry wins in `GetRefractionSettings`, so the instance stops following its parent's value. The entry has an effect only in translucent or SingleLayerWater materials, the only ones whose translator compiles `RefractionDepthBias` as a scalar parameter. A report of what an author overrode should count this entry as engine-written.","source":"5.3 `Editor/UnrealEd/Private/PreviewMaterial.cpp:471`, 5.3 `Editor/UnrealEd/Private/PreviewMaterial.cpp:523`, 5.3 `Editor/UnrealEd/Private/PreviewMaterial.cpp:755`, 5.3 `Editor/UnrealEd/Private/PreviewMaterial.cpp:769`, 5.3 `Editor/UnrealEd/Private/PreviewMaterial.cpp:811-812`, 5.3 `Editor/UnrealEd/Private/PreviewMaterial.cpp:976` (5.3.2 source carries a local edit here; stock 5.3.2: `:745`, `:759`, `:801-802`, `:966`), 5.3 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:1049-1073`, 5.3 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:3435`, 5.3 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:3614-3627`, 5.3 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:3848`, 5.3 `Runtime/Engine/Private/Materials/Material.cpp:1034`, 5.3 `Runtime/Engine/Private/Materials/Material.cpp:2148` (stock 5.3.2: `:1021`, `:2135`), 5.3 `Runtime/Engine/Private/Materials/HLSLMaterialTranslator.cpp:1105-1111`, 5.8 `Editor/UnrealEd/Private/PreviewMaterial.cpp:527`, 5.8 `Editor/UnrealEd/Private/PreviewMaterial.cpp:592`, 5.8 `Editor/UnrealEd/Private/PreviewMaterial.cpp:903`, 5.8 `Editor/UnrealEd/Private/PreviewMaterial.cpp:917`, 5.8 `Editor/UnrealEd/Private/PreviewMaterial.cpp:962-963`, 5.8 `Editor/UnrealEd/Private/PreviewMaterial.cpp:1206`, 5.8 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:1246-1275`, 5.8 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:4054`, 5.8 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:4240-4254`, 5.8 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:4556`, 5.8 `Runtime/Engine/Private/Materials/Material.cpp:1216`, 5.8 `Runtime/Engine/Private/Materials/Material.cpp:2346-2350`, 5.8 `Runtime/Engine/Private/Materials/HLSLMaterialTranslator.cpp:1647-1655`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE materials and FBX import - what the Material Instance Editor writes, and what a default FBX import builds"},{"id":"MI2","fact":"On 5.5-5.8 an FBX imported in the editor goes to Interchange (`Interchange.FeatureFlags.Import.FBX` defaults to true, and the legacy FBX factory drops `fbx` from its formats when it is on) and the generic mesh pipeline's `bBuildNanite` defaults to true, so every imported static mesh gets Nanite unless the pipeline is told otherwise; the legacy importer's `UFbxStaticMeshImportData::bBuildNanite` defaults to false. On 5.3 and 5.4 the Interchange FBX flag and the pipeline's `bBuildNanite` both default to false, so a default FBX import is legacy and Nanite-off. From 5.7 the pipeline also has `NaniteTriangleThreshold` (default 0 = no threshold; above 0 a mesh with fewer LOD0 triangles is built without Nanite). The pipeline turns Nanite off for alternate-skinning and custom morph-target imports. The legacy value is a `config` property (`EditorPerProjectUserSettings`) and Interchange pipelines reload the user's last-used settings from the per-project editor ini, so a saved user choice overrides either default. The stock pipeline assets store no `bBuildNanite` value (read offline in their name maps on all six engines), so the C++ default applies. Skeletal meshes were not checked.","source":"5.3 `Plugins/Interchange/Runtime/Source/Import/Private/Fbx/InterchangeFbxTranslator.cpp:27-30`, 5.3 `Plugins/Interchange/Runtime/Source/Pipelines/Public/InterchangeGenericMeshPipeline.h:81`, 5.3 `Editor/UnrealEd/Private/Fbx/FbxFactory.cpp:1168-1178`, 5.3 `Editor/UnrealEd/Private/Fbx/FbxStaticMeshImportData.cpp:12`, 5.3 `Editor/UnrealEd/Classes/Factories/FbxStaticMeshImportData.h:16`, 5.3 `Editor/UnrealEd/Classes/Factories/FbxStaticMeshImportData.h:41-42`, 5.4 `Plugins/Interchange/Runtime/Source/Import/Private/Fbx/InterchangeFbxTranslator.cpp:29` (false), 5.4 `Plugins/Interchange/Runtime/Source/Pipelines/Public/InterchangeGenericMeshPipeline.h:82` (false), 5.5 `Plugins/Interchange/Runtime/Source/Import/Private/Fbx/InterchangeFbxTranslator.cpp:29`, 5.5 `Plugins/Interchange/Runtime/Source/Pipelines/Public/InterchangeGenericMeshPipeline.h:100`, 5.6 `Plugins/Interchange/Runtime/Source/Import/Private/Fbx/InterchangeFbxTranslator.cpp:29`, 5.6 `Plugins/Interchange/Runtime/Source/Pipelines/Public/InterchangeGenericMeshPipeline.h:107`, 5.7 `Plugins/Interchange/Runtime/Source/Import/Private/Fbx/InterchangeFbxTranslator.cpp:34`, 5.7 `Plugins/Interchange/Runtime/Source/Pipelines/Public/InterchangeGenericMeshPipeline.h:110`, 5.7 `Plugins/Interchange/Runtime/Source/Pipelines/Public/InterchangeGenericMeshPipeline.h:117`, 5.8 `Plugins/Interchange/Runtime/Source/Import/Private/Fbx/InterchangeFbxTranslator.cpp:34-39`, 5.8 `Plugins/Interchange/Runtime/Source/Import/Private/Fbx/InterchangeFbxTranslator.cpp:260-270`, 5.8 `Editor/UnrealEd/Private/Fbx/FbxFactory.cpp:1204-1215`, 5.8 `Plugins/Interchange/Runtime/Source/Pipelines/Public/InterchangeGenericMeshPipeline.h:133-141`, 5.8 `Plugins/Interchange/Runtime/Source/Pipelines/Private/InterchangeGenericMeshPipeline.cpp:120`, 5.8 `Plugins/Interchange/Runtime/Source/Pipelines/Private/InterchangeGenericMeshPipeline.cpp:143`, 5.8 `Plugins/Interchange/Runtime/Source/Pipelines/Private/InterchangeGenericStaticMeshPipeline.cpp:200-234`, 5.8 `Plugins/Interchange/Runtime/Source/Pipelines/Private/InterchangeGenericStaticMeshPipeline.cpp:483`, 5.8 `Runtime/Interchange/Core/Private/InterchangePipelineBase.cpp:72`, 5.8 `Runtime/Interchange/Core/Private/InterchangePipelineBase.cpp:101-109`, 5.8 `Runtime/Interchange/Engine/Private/InterchangeManager.cpp:60-65`, 5.8 `Editor/UnrealEd/Private/Fbx/FbxStaticMeshImportData.cpp:14`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE materials and FBX import - what the Material Instance Editor writes, and what a default FBX import builds"},{"id":"MI3","fact":"**A material or instance loaded from a COOKED package in the editor has a valid, EMPTY `EditorOnlyData` - never a null one.** `UMaterialInterface::PostInitProperties` creates a new editor-only object for every material the editor constructs, and `FObjectInitializer::PostConstructInit` calls `PostInitProperties` for a loading export as for any other; a cook for a platform without editor data writes neither that object nor a reference to it (the cooked instance's name table holds no `<Name>EditorOnlyData`), so the load never overwrites the new one. Measured after a Windows cook: `get_property EditorOnlyData` returned `/Game/MifMaskFixture/MI_Mask.MI_Mask:MI_MaskEditorOnlyData` on the instance and `...:M_MaskEditorOnlyData` on its material, both packages cooked, filterEditorOnly and cookedForEditor, and the instance's `StaticParameters.StaticComponentMaskParameters` read `[]` where its uncooked save held one entry. So a crash that needs a null pointer does not come from a cooked load, and `list_material_parameters` never crashed on cooked content; what a cooked load lacks is the editor-only DATA (see the next row).","source":"5.3 `Runtime/Engine/Private/Materials/MaterialInterface.cpp:721`, 5.4 `Runtime/Engine/Private/Materials/MaterialInterface.cpp:724`, 5.5 `Runtime/Engine/Private/Materials/MaterialInterface.cpp:737`, 5.6 `Runtime/Engine/Private/Materials/MaterialInterface.cpp:741`, 5.7 `Runtime/Engine/Private/Materials/MaterialInterface.cpp:820`, 5.8 `Runtime/Engine/Private/Materials/MaterialInterface.cpp:1074`, 5.3 `Runtime/CoreUObject/Private/UObject/UObjectGlobals.cpp:3919`, 5.8 `Runtime/CoreUObject/Private/UObject/UObjectGlobals.cpp:4320`; live: `get_property` and `describe_package` on a scratch project (5.8.2) and a scratch project (5.3.2), 2026-09-26","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE material instances on cooked content - the editor-only object and static component masks"},{"id":"MI4","fact":"**Static component masks do not survive cook, and reading them on cooked content answers wrongly rather than failing.** `StaticComponentMask` is the one editor-only material parameter type (it sits at `NumRuntime`): an instance's mask overrides live in its editor-only `StaticParameters`, and a material's mask defaults in the editor-only part of its cached parameters, so a cook strips both. Measured with an instance saved with mask G over its material's R: cooked over its uncooked material, `list_material_parameters` (before the 2026-09-26 guard) listed the mask as R with `overriddenOnThisInstance` false; with the material cooked too the mask parameter was absent from both listings, while the instance kept `bHasStaticPermutationResource` true - its cook compiled a shader map of its own. Since that change the listing and `tree:true` leave masks out on any cooked level, with `componentMasksRead` false and `componentMasksNote` naming the level (test_material_params T126).","source":"5.3 `Runtime/Engine/Public/MaterialTypes.h:197-200`, 5.4 `Runtime/Engine/Public/MaterialTypes.h:197-200`, 5.5 `Runtime/Engine/Public/MaterialTypes.h:196-199`, 5.6 `Runtime/Engine/Public/MaterialTypes.h:197-200`, 5.7 `Runtime/Engine/Public/Materials/MaterialParameters.h:199-202`, 5.8 `Runtime/Engine/Public/Materials/MaterialParameters.h:200-203`, 5.3 `Runtime/Engine/Private/Materials/MaterialCachedData.cpp:220`, 5.4 `Runtime/Engine/Private/Materials/MaterialCachedData.cpp:222`, 5.5 `Runtime/Engine/Private/Materials/MaterialCachedData.cpp:231`, 5.6 `Runtime/Engine/Private/Materials/MaterialCachedData.cpp:268`, 5.7 `Runtime/Engine/Private/Materials/MaterialCachedData.cpp:281`, 5.8 `Runtime/Engine/Private/Materials/MaterialCachedData.cpp:388`; live: `list_material_parameters` on a scratch project (5.8.2, the build before the guard) and `get_property` of the instance's editor-only masks on 5.8.2 and 5.3.2, 2026-09-26","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE material instances on cooked content - the editor-only object and static component masks"},{"id":"MI5","fact":"**A null instance `EditorOnlyData` terminates the editor at the engine's mask read, and one `set_property` makes one.** `UMaterialInstance::GetAllParametersOfType(StaticComponentMask)` dereferences `GetEditorOnlyData()` for every instance in the chain, and `GetParameterOverrideValue` for the instance it is called on, with no null check. `set_property {EditorOnlyData: \"None\"}` on an uncooked instance succeeds and reads back None - the engine's own editing reads check the pointer (`UMaterialInstance::PostLoad`, `HasStaticParameters`, `GetStaticParameters`) - and the next unfiltered `list_material_parameters` on a build without the 2026-09-26 guard terminated the 5.8.2 editor: EXCEPTION_ACCESS_VIOLATION reading 0x60 in `GameThread_ApplyParameterOverrides<FStaticComponentMaskParameter>`, at the loop's `Parameters.Num()`, called from MaterialInstance.cpp:2343. A `types` filter without staticComponentMask never reaches it, and setting the pointer back to the old object's path restores the listing.","source":"5.3 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:1998`, 5.4 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:1971`, 5.5 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:2205`, 5.6 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:2223`, 5.7 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:2318`, 5.8 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:2342`, 5.3 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:996`, 5.8 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:1176`, 5.3 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:2979`, 5.3 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:4617`, 5.3 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:4631`, 5.8 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:3520`, 5.8 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:5462`, 5.8 `Runtime/Engine/Private/Materials/MaterialInstance.cpp:5476`, 5.8 `Runtime/Engine/Private/Materials/MaterialInstanceSupport.h:554`; live: crash report UECC-Windows-721E48224828701560676296A0CDA025_0000 on a scratch project, 2026-09-26; test_material_tree MT114 runs the same case against the guard on 5.8.2 and 5.3.2","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE material instances on cooked content - the editor-only object and static component masks"},{"id":"MI6","fact":"**An automated FBX import takes the options the FBX import dialog last SAVED in that project, not the class defaults.** `UFbxImportUI`'s constructor loads section `FBX_Import_UI_Option_<Class>` of the per-project editor ini into its static-mesh, skeletal, animation and texture data, and `UFbxFactory` loads the same into its own options object; only the interactive dialog writes that section, and an automated import saves nothing. So a scripted import - a `UAssetImportTask` without Options, and import_asset before 2026-09-27 - gets Nanite, collision, vertex colors and combined meshes as the last person to click the dialog left them, per project. The class defaults, the same on 5.3 and 5.8: vertex colors IGNORED, Nanite off, meshes not combined, auto collision on (an 18-DOP round the whole mesh when the file has no UCX_ hull), lightmap UVs generated, normals imported. import_asset's meshOptions resets every `CPF_Config` property of the static-mesh and texture import data to its class default, applies the keys given and reads them back before importing.","source":"FbxFactory.cpp 5.8:1236-1261 -> `LoadOptions` :1894-1911, and :70-71 (the factory's own object); FbxMainImport.cpp 5.8:298-320 (the dialog saves) and :337-351 (an automated import does not); FbxStaticMeshImport.cpp 5.8:2433-2443 (the 18-DOP); BaseEditorPerProjectUserSettings.ini (the defaults, read on 5.3 and 5.8). Measured: tools/test_import_mesh_options.py 38/38 on 5.8.2 and 5.3.2, 2026-09-27 (probes a scratch project and a scratch project, fixture tools/fixtures/SM_ProbeTwoParts.fbx)","engine":"5.8 (source); 5.3 and 5.8 (defaults, live)","ue":["5.3","5.8"],"bl":[],"section":"UE material instances on cooked content - the editor-only object and static component masks"},{"id":"MI7","fact":"**The Alembic importer's Maya preset - `FAbcConversionSettings()`, the conversion an Alembic import starts from - has Scale (1, -1, 1), and Rotation (0, 0, 0) on 5.3 but (90, 0, 0) from 5.4.** The same .abc imported with the default preset lies down on 5.3 and stands up on 5.4+. import_asset's `alembic` answer reports the value each engine holds; alembicScale and alembicRotation replace the preset (Preset=Custom) when a result must match across engines.","source":"AbcImportSettings.h (Plugins/Importers/AlembicImporter/Source/AlembicLibrary/Public), installed engines: 5.3:221 `Rotation(FVector::ZeroVector)`; 5.4, 5.5 and 5.6:221 and 5.7 and 5.8:223 `Rotation(FVector(90.0f, 0.0f, 0.0f))`; Scale (1, -1, 1) at 5.3:220 and 5.8:222. Measured: import_asset alembicAs geometryCache on tools/fixtures/mif_wave_12f.abc answered rotation [0, 0, 0] on 5.3.2 (a scratch project) and [90, 0, 0] on 5.8.2 (a scratch project), 2026-09-27 (tools/test_import_alembic.py T3400, 16/16 on both)","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE material instances on cooked content - the editor-only object and static component masks"},{"id":"MI8","fact":"**An Unreal material's VertexColor reads the number in the FBX file, quantized to 8 bits, with no color-space change on the way.** The FBX static-mesh importer turns each FBX color straight into a byte (`uint8(255.f * value)`, clamped from 5.4) and stores it through `FLinearColor(FColor)`; the mesh build re-encodes with `ToFColor(true)`, so the render bytes are the importer's; the local vertex factory fetches them with no decode. So 0.5 in the FBX is byte 127 and reads 0.498 in a material.","source":"FbxStaticMeshImport.cpp 5.3:944-952 and 5.8:943-952; StaticMeshBuilder.cpp 5.3:998 and 5.8:1706-1708; LocalVertexFactory.ush 5.3:896-898 and 5.8:959-961; the same three lines found on 5.4, 5.5, 5.6 and 5.7 (installed engine source, 2026-09-27). Read in source; and READ BACK LIVE on 5.3.2 and 5.8.2, 2026-09-27 (probes a scratch project and a scratch project): a 3 m build_tree tree with its hull, export_mesh -> import_asset (meshOptions combineMeshes, vertexColors replace) -> export_asset ASCII with vertex colors held every one of 71,556 bark corners at byte 128 for a value written as 0.5 (color_srgb), none at 188; G 0 to 1","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE material instances on cooked content - the editor-only object and static component masks"},{"id":"MI14","fact":"**On 5.3.2, set_property on a static mesh's NaniteSettings changes the flag and not the mesh.** Setting NaniteSettings.bPreserveArea true, and NaniteSettings.bEnabled false, on an imported static mesh answered ok, changed and verified, and nothing was rebuilt: no Nanite build was logged, and neither the placed actor nor a freshly spawned actor of the asset drew any differently. On 5.8.2 the same call rebuilds in its own frame. UStaticMesh::PostEditChangeProperty always calls Build and both flags are in the build's cache key, so the gap is between the bridge's notification and the build (open).","source":"Runtime/Engine/Private/StaticMesh.cpp 5.3:3970-4073 (PostEditChangeProperty, Build at 4052) and 2543-2560 (SerializeNaniteSettingsForDDC); NaniteBuilder.cpp 5.3:780 logs every build; measured live on 5.3.2, 2026-09-28, probe a scratch project, through set_property, spawn_actor_in_level and capture_viewport","engine":"5.3","ue":["5.3"],"bl":[],"section":"UE material instances on cooked content - the editor-only object and static component masks"},{"id":"M58ce4f","fact":"get_perf_stats reports EDITOR frame time and RHI draw calls - the editor drawing its own viewport, gizmos and selection outlines included - and is not the game's FPS; the dashboard therefore reports a static content census (LOD0 triangles, primitive components, material slots, and a [BP TICK] flag) with bar length relative to the heaviest actor rather than to any invented budget. [BP TICK] is explicitly not a measurement: a cheap tick and an expensive one look identical.","source":"docs/16_DASHBOARD.md § PERFORMANCE (:127-151), including the reason bar length is relative ('there is no universal triangle budget, and inventing one would be a number pretending to be a threshold')","engine":"","ue":[],"bl":[],"section":"MifBridge dashboard"},{"id":"M57d8de","fact":"find_assets pathPrefix matches whole PATH SEGMENTS, not string prefixes - {\"pathPrefix\": \"/Game/_Mif\"} returns nothing ever, because the scratch folders are named _MifPure, _MifPin, _MifLayout, _MifAnim and a partial segment name matches none of them. Matching is always recursive.","source":"docs/02_GOTCHAS.md § find_assets pathPrefix matches whole PATH SEGMENTS (:2674-2682), measured against the running editor 2026-08-31. The always-recursive property is stated in the endpoint's own rejection note, MifBridgeCooked.cpp:267.","engine":"","ue":[],"bl":[],"section":"UE asset registry","value":"`/Game/_MifPure -> 6 (complete segment, matches); /Game/_Mif -> 0`"},{"id":"Medf34c","fact":"UE splits a collection three ways and the read/write asymmetry produced a FALSE NEGATIVE on a durable write: GetAssetsInCollection skips every path whose package starts /Script/, GetClassesInCollection keeps exactly those, GetObjectsInCollection returns the whole set - while AddToCollection has no such filter, so a class path is accepted, persisted to the .collection file, and then invisible to every filtered read.","source":"docs/01_POSTMORTEMS.md 2026-09-09 (:88). Engine 5.3.2 source  Developer/CollectionManager/Private/Collection.cpp:467 verified verbatim (`if (!ObjectName.GetLongPackageName().StartsWith(TEXT(\"/Script/\")))` inside GetAssetsInCollection) and :481 (GetClassesInCollection with the inverted test). Doc records reading all five ins","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE collections","value":"`MifBridgeCooked.cpp:2018 decided success from Satisfied != Paths.Num() against the filtered reader -> ok:false \"0 of 1 path(s) are in the collection afterwards\" for a write the engine performed`"},{"id":"M5d2d80","fact":"UVerseClass arrives in UE 5.5 and is the only class in the Runtime tree declaring IsAsset() true while living in a /Script/ package, which is why the collection defect above was exposed on 5.5/5.6/5.7 and not 5.4 - GetAllAssets enumerates in-memory objects first, so its CDO lands at row 0 of find_assets.","source":"Engine - verified during this pass: VVMVerseClass.h:135 on UE 5.5 is exactly that line, and the file is ABSENT from the 5.4 install. docs/01_POSTMORTEMS.md 2026-09-09; measured with one read-only find_assets {\"limit\":20} per probe editor, whose recorded outputs show 5.4 returning /Script/Engine.Texture2D and 5.5/5.6 re","engine":"","ue":[],"bl":[],"section":"UE collections","value":"`COREUOBJECT_API virtual bool IsAsset() const override { return true; }`"},{"id":"M7b2cd7","fact":"USimpleConstructionScript::CreateNode silently renames on collision via GenerateNewComponentName (Turret -> Turret1) and reported ok:true for a component the caller never asked for - a setup script replayed twice quietly grows Turret, Turret1, Turret2. add_component now asks GenerateNewComponentName what it would pick and refuses when that differs, before creating anything. An omitted name still means 'engine, pick one'.","source":"docs/02_GOTCHAS.md §13 (the SCS->CreateNode bullet, lines 1615-1629); noted there that its three siblings add_variable, create_function and add_event_dispatcher all already refuse a taken name. Covered by tools/test_idempotence.py.","engine":"","ue":[],"bl":[],"section":"UE component naming"},{"id":"Maa9a2b","fact":"A UStringTable's data lives in a private, non-UPROPERTY FStringTablePtr, so string-table edits are outside the transaction system — an endpoint must report undoable:false and restore by re-setting the previous value. UCompositeDataTable rejects row mutation by design (EmptyTable/RemoveRow/AddRow are overridden with \"Composite data tables don't currently add or remove rows\" and ParentTables is protected); use AppendParentTables for append.","source":"Engine/Source/Runtime/Engine/Public/Internationalization/StringTable.h:36-38 — CORRECTION: the candidate's Classes/Internationalization/StringTable.h does not exist. Runtime/Engine/Classes/Engine/CompositeDataTable.h:43-46 (the overrides + comment), :58 (AppendParentTables), :76-77 (ParentTables under protecte","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE data assets","value":"`private: /** Internal string table. Will never be null, but is a TSharedPtr rather than a TSharedRef so we can use a forward declaration */ FStringTablePtr StringTable;`"},{"id":"M443fa7","fact":"A mesh exported to FBX and re-imported loses materials, slot order, collision and LODs silently: the import creates placeholder Material assets with no cooked shader (gray in game), slot arrays do not line up by index (measured 10->6, 9->3, 10->5 - map by slot NAME), a merged WorldGridMaterial slot must take the material the ACTOR overrides to, setting StaticMesh TRUNCATES OverrideMaterials keeping old values, one generated hull replaced vanilla's 29/67/101, and 5 LODs dropped to 1.","source":"Eddie_v2/docs/29_VILLA_WINDOW_UPGRADES.md:163-212 (\"The four traps in the FBX round trip\" + \"Collision and LODs do not survive the round trip\"), measured 2026-09-03 on the three Villa04 wall meshes","engine":"","ue":[],"bl":[],"section":"UE FBX round trip","value":"`Map slots by name, not by index`"},{"id":"M760a84","fact":"UDynamicMeshPool's MaxPoolSize failsafe is a leak, not a backstop: on trip it does AllCreatedMeshes.Reset() + ForceGarbageCollection, after which ReturnMesh fails its Contains ensure for every pre-trip mesh — handles held by a rooted map survive GC but can never be returned. Enforce your own live-handle cap rather than relying on the CVar.","source":"Engine/Source/Runtime/GeometryFramework/Private/UDynamicMesh.cpp:546-549 (CVar declaration + default 1000), :563-568 (ensure trip → Reset + ForceGarbageCollection), :578 (ReturnMesh's ensure(AllCreatedMeshes.Contains(Mesh)))","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE geometry scripting","value":"`CVar geometry.DynamicMesh.MaxPoolSize, default 1000`"},{"id":"M23f331","fact":"SetConvexDecompositionCollisionsWithNotification blocks the game thread in a WaitFor(33ms) loop pumping an FScopedSlowTask dialog until V-HACD completes, and also reopens any static-mesh editor tab it closed — so an input-size cap is mandatory.","source":"Engine/Source/Editor/StaticMeshEditor/Private/StaticMeshEditorSubsystem.cpp:1383-1391 (FScopedSlowTask :1383, MakeDialog :1384, wait loop :1386-1391) and :1408-1411 (OpenEditorForAsset over EditedStaticMeshes)","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE mesh editing hazards","value":"`while (!Result.WaitFor(FTimespan::FromMilliseconds(33.0)))`"},{"id":"Mbd9e77","fact":"Writing a property whose meta editcondition flag is false is silently ignored: UStaticMeshComponent::MinLOD carries meta=(editcondition=\"bOverrideMinLOD\") and needs that flag set; ALevelStreamingVolume::StreamingLevelNames is VisibleAnywhere/BlueprintReadOnly so the inverse ULevelStreaming::EditorStreamingVolumes must be driven instead.","source":"Engine/Source/Runtime/Engine/Classes/Components/StaticMeshComponent.h:114-115 (MinLOD + meta), flag at :226; Runtime/Engine/Classes/Engine/LevelStreamingVolume.h:33-34 (VisibleAnywhere/BlueprintReadOnly StreamingLevelNames); inverse array at Runtime/Engine/Classes/Engine/LevelStreaming.h:318","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE property writes","value":"`UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadOnly, Category=LOD, meta=(editcondition = \"bOverrideMinLOD\"))`"},{"id":"M6d6381","fact":"A node's FMemberReference keeps the name it was created with and repoints by GUID, so after a rename GetMemberName()/GetPropertyName() still answers the OLD name - remove_event_dispatcher counted 1 orphan for a fresh dispatcher and 0 for a renamed one whose node was correctly wired and compiled clean. Use GetProperty(), which resolves the member, and check both because the resolved property is null once the member is gone.","source":"docs/02_GOTCHAS.md §13 § A node's stored member NAME goes stale after a rename (lines 1673-1690). The doc notes the count 'looked plausible in isolation; only running both cases side by side showed it.'","engine":"","ue":[],"bl":[],"section":"UE reflection","value":"`UK2Node_BaseMCDelegate::GetPropertyName() returns the stored name; DelegateReference.ResolveMember<>(...) returns the live one`"},{"id":"Mfe3c4f","fact":"A Blueprint struct member's real VarName carries an engine-appended GUID suffix and the editor shows the separate FriendlyName; members are addressed internally by FGuid, which is the only stable handle across the recompile every edit triggers. A UUserDefinedEnum entry's FName is engine-generated too (NewEnumeratorN) and the text you pass becomes the display name. Removing a non-final enum entry shifts every later index down, so anything storing the enum by index silently re-points.","source":"docs/02_GOTCHAS.md §5c (:547-573). Also recorded there: both types must keep at least one member/entry or they will not compile, so removing the last one is refused, and native C++ structs and enums are refused outright with that reason.","engine":"","ue":[],"bl":[],"section":"UE user-defined types"},{"id":"M0776e0","fact":"A light's AttenuationRadius falloff is a quartic mask reaching zero at R, so R = 1.05x the drop distance leaves ~3.1% of the light at that distance while staying near 1.0 close to the fixture.","source":"Engine/Shaders/Private/DeferredLightingCommon.ush:475-476","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE lighting/shaders","value":"`LightRadiusMask = Square( saturate( 1 - Square( DistanceSqr * Square(LightData.InvRadius) ) ) ) i.e. (1 - (d/R)^4)^2`"},{"id":"U35","fact":"**`material_statistics` refuses to answer without `compile:true`**, because `GetStatistics` calls `FinishCompilation` - a synchronous wait with no progress and no cancel. A refusal to MEASURE is not an incomplete shader map, and reading it as one is how a working material pass gets reported as broken. **[Reviewed 2026-09-21 against the code - STALE in part: material_statistics refuses only when the map is not already complete (MifBridgeMaterials.cpp:2426-2451)]**","source":"measured 2026-09-18: without the flag, `ok:false` and `shaderMapComplete:false` with a message naming the stall; with it, **12/12 materials complete**.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE","status":"superseded"},{"id":"U40","fact":"**A PARTIAL path still returns a plausible `pathLength`.** `nav_find_path` answers `ok:true, valid:true, partial:true, reachable:false` with a length that is the distance to the closest reachable point - so checking `ok` alone reports every unreachable destination as a successful route.","source":"measured 2026-09-18: PlayerStart to `Station_BreakerPanel` returned `pathLength 1454.2` while `reachable:false`. `tools/nav.py` has refused partials since Milestone 1; the audit written on top of it did not, until it was corrected.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U42","fact":"**A test that does not wait long enough is a false failure.** Not an engine fact, but it belongs next to U41 because the two were mistaken for each other.","source":"measured 2026-09-18: the same harness held the fix key for 6 seconds against a fault whose `FixSeconds` is **25** (others are 40, 90, 120 and 240) and reported \"held F at all six stations, nothing cleared\". Nothing was wrong with the game. A harness should read the duration out of the data rather than choosing one.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U45","fact":"**A single-input material expression's first pin has NO NAME.** `connect_material_expressions` refuses `toInput:\"Input\"` on Abs, Sine, Frac, OneMinus, ComponentMask and Clamp; the empty string is the pin. `MaterialExpressionWorldPosition`'s OUTPUTS are named, however - `XYZ`, `XY`, `Z`.","source":"measured 2026-09-18, and the refusal prints the pin list: `MaterialExpressionAbs_1 inputs: [None]`, `MaterialExpressionClamp_0 inputs: [None, Min, Max]`, `MaterialExpressionWorldPosition_0 outputs: [XYZ, XY, Z]`. Two-input expressions (Multiply, Add, Subtract, Divide) use `A`/`B`, LinearInterpolate `A`/`B`/`Alpha`, Power `Base`/`Exp` - those all connected first time.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"B97","fact":"**`gen_image` is prompt -> image; `gen_texture` is mesh + reference image -> PBR baked onto that mesh.** They are not two ways of making a surface texture - `gen_texture` needs the Hunyuan3D wrapper nodes and an existing mesh, so a flat tiling material comes from `gen_image`.","source":"read from each op's `describe_op` 2026-09-18: `gen_texture :: Existing mesh + reference image -> PBR textures baked on`, refusing without `image`/`imagePath` because \"the painter conditions on it\"; `gen_image :: Prompt -> reference image, left in ComfyUI's output folder`, taking `variant` schnell or dev. `gen_status` on this machine: 885 nodes, all Hy3D nodes present, 2 checkpoints, RTX 4080 SUPER 16 GB.","engine":"5.0.1","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"B98","fact":"**Flux fp8 does NOT stay resident between `gen_image` calls on a 16 GB card - it re-stages every prompt**, so throughput is roughly **7 minutes per 1024x1024 image at 4 steps**, not the ~90 s a warm model would give. Budget batches accordingly.","source":"measured 2026-09-18, RTX 4080 SUPER 16 GB, flux1-schnell-fp8: the first image reported `Prompt executed in 437.08 seconds`, and the ComfyUI log prints `Model Flux prepared for dynamic VRAM loading. 11340MB Staged.` at the START OF EACH prompt, followed by `0/4 [00:00<?, ?it/s, Model Initializing ...]`. The model is 11.3 GB against 16 GB of VRAM, so it is being swapped rather than kept.","engine":"5.0.1","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"B101","fact":"**`gen_image` appends a product-shot `PROMPT_SUFFIX` to every prompt**, so it cannot produce a tiling texture however the caller words the prompt. The suffix is right for the op's real job - a reference image for the Hunyuan3D mesh stage - and directly contradicts a tiling surface, which has no object, no background and no framing.","source":"read from `ops_gen.py:64` 2026-09-18: `\", full object centred in frame, orthographic side profile, plain flat background, even diffuse studio lighting, no dramatic shadows, no motion blur, sharp focus, single object, entire object visible\"`, appended at `ops_gen.py:295`. A prompt asking for \"seamless tileable\" was therefore also asking for \"single object, entire object visible\" in the same breath. The generated timber came back as clean modern lap siding on a flat background rather than a weathered tiling surface.","engine":"5.0.1","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"B106","fact":"**Do NOT put the word \"tiling\" or \"tileable\" in a texture prompt for a model with strong prompt adherence - it draws literal TILES.** \"A seamless tiling texture of X\" made flux **dev** return a grid of panels in four of nine surfaces. Seamlessness is a property of the FRAMING, so describe the framing (overhead, parallel, one continuous unbroken surface, no seams or borders) and leave the word out. The trap is invisible on schnell, which ignores the word along with most of the prompt - so **this gets WORSE as prompt adherence improves**, and looks like a regression when it is really the model finally obeying.","source":"measured 2026-09-18, flux1-dev-fp8 at 20 steps, guidance 3.5, identical prompt structure across nine surfaces: the PVC tarpaulin came back as a 3x3 grid of seamed panels, the rusted drum as riveted plates in a regular grid, the painted handrail as glazed square tiles with grouted joins, and the concrete apron as a grid of expansion joints around a centred drain. The same nine prompts on schnell at 4 steps produced no grid on any of them.","engine":"5.0.1","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U53","fact":"**A material CANNOT compensate for an actor's scale, so an engine-cube blockout scaled to level size is unfixable by any tiling number - it needs WORLD-SPACE coordinates.** A27 measures every `/Engine/BasicShapes` mesh at 100 uu across; its UVs stay 0..1 per face however far the actor is scaled up. `BO_Ground` is that cube at scale (70, 50, 0.4), so **70 x 50 metres carrying a single 0..1 UV square**. Sampled through `TextureCoordinate` at 0.25 tiles, one 1024 px texture is stretched over 70/0.25 = **280 m, which is 3.7 pixels per metre** - the blur. Feeding the sample from `WorldPosition` (centimetres, output pin `XY`) multiplied by tiles/100 gives real texel density regardless of actor scale: at 0.45 tiles per metre the same texture repeats every 2.2 m, **461 pixels per metre, a 126x improvement**.","source":"measured 2026-09-18: `get_property RootComponent.RelativeScale3D` on `BO_Ground` read `{X: 70, Y: 50, Z: 0.4}`; the arithmetic above follows from that and A27. The project's own Blender meshes need no such treatment - B108 measures them at one UV unit per metre, where `TextureCoordinate` is already correct.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"B111","fact":"**Isotropic noise cannot read as wood - grain needs ANISOTROPIC noise, high frequency across the board and very low along it.** A timber texture built from ordinary fbm looks like stone however the colours are set, because the eye reads wood from its directionality before its colour.","source":"measured 2026-09-18: the first procedural timber used `fbm` and read as grey concrete boards; replacing it with three octaves of `pnoise2` at (fx, fy) of (6, 220), (3, 90) and (12, 420) - roughly 35:1 anisotropy - read as weathered timber immediately, with the same palette and the same board layout. A small isotropic term (0.18 of a low-frequency fbm) is added to make the grain WANDER, or it comes out as perfectly straight stripes.","engine":"5.0.1","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"B113","fact":"**A SMART unwrap on a FLAT sheet is effectively planar, so a page can carry printed content without re-unwrapping it.** Smart UV Project cuts by angle; a leaf has one dominant face orientation, so it comes out as a single clean island rather than the scattered fragments the reported UV bounds suggest. What looks like scrambled UVs on a printed page is almost always the TILING, not the unwrap.","source":"measured 2026-09-18: `Book_Leaf_Turn` is x 0.000..0.200, y -0.142..0.142 with `uv_unwrap method:\"SMART\"` giving bounds u 0.197..0.947, v 0.011..0.862 - which reads like packed islands. A ruled form sampled at the wrong tiling rendered as magnified fragments; at U 3.521 / V 5.0 the same UVs rendered the form once, square-celled and legible. A planar rewrite was started and reverted as unnecessary.","engine":"5.0.1","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"B114","fact":"**A page's long axis is its Y, so a portrait form drawn into a square texture lands a quarter-turn wrong.** The journal leaf is 0.200 gutter-to-fore-edge and 0.284 along the spine, and the material's U runs along the LONG one - so the heading band appears down the left edge and the column headings read bottom-to-top. Rotate the IMAGE and swap the tilings; do not try to fix it by scaling.","source":"measured 2026-09-18: at U 5.0 / V 3.509 the log sheet rendered rotated with \"TIME\" reading bottom-to-top. `Image.transpose(ROTATE_90)` plus U 3.521 (1/0.284) / V 5.0 (1/0.200) put the columns down the page with square cells.","engine":"5.0.1","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"B116","fact":"**Both faces of a leaf must NOT share a projection: the underside needs its U mirrored, or the right-hand page reads backwards.** Projecting both sides identically is what a real sheet of paper does and is NOT what an open book looks like - the left page is the BACK of the leaf just turned and the right page the FRONT of the next, and both read forwards.","source":"measured 2026-09-18: with one projection the left page rendered correctly and the right page rendered mirrored, headings reversed. Flipping u to `1 - x/W` on faces with `poly.normal.z < 0` made both pages read forwards in the same capture.","engine":"5.0.1","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"B117","fact":"**Every page must go through ONE finishing step or half of them come out a half-turn from the other half.** `PAGE_ROTATE` was applied inside `t_paper` only, so the ledger was turned and the section pages were not - the contents page rendered upside down on the left of the opening spread while the ledger beside it read correctly.","source":"measured 2026-09-18: moving the rotate into the shared `_finish()` made all five section pages agree with the ledger in the same capture. The general shape of this is that a per-asset transform belongs in the shared exit path, not in whichever function happened to need it first.","engine":"5.0.1","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U61","fact":"**Fault timers accumulate in REAL DeltaSeconds while the night clock runs at TimeScale, so any time-scaled test misreads grace and fix times by exactly that factor.** Not a bug in the mechanic - a property of testing it fast, and worth knowing before reporting a working effect as broken.","source":"measured 2026-09-18: at `TimeScale 20` an in-game hour is 9 real seconds, so F8's 30 s grace spans 3.3 in-game HOURS. Armed at 01:00, the grace expired about 04:40 in-game, and the opening duly landed at 05:00 - which is what the log shows. `ApplyFaultEffects` is correct (`Live.ModulationPct = 0.0f` while biting); the hourly line simply never sampled a biting fault because the fault was still inside its grace for four of the eight hours.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U63","fact":"**Gameplay timers that must stay in step with a scaled clock have to be scaled at the SAME place the clock is advanced.** Grace, fix progress and dead air accumulated in real `DeltaSeconds` while the night ran at `TimeScale`, so a fast test behaved nothing like the shipping night.","source":"measured 2026-09-18: at `TimeScale 20` F8's 30 s grace spanned 3.3 in-game hours and the opening landed at 05:00 on a fault that arrived at 01:00. Multiplying the delta handed to the fix and dead-air paths by `TimeScale` put the opening at 01:00, immediately after the fault, at every scale. At `TimeScale 1` nothing changes.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U64","fact":"**`get_property` on a TArray returns UE's own TEXT form in `value` and the real JSON list in `typed`.** Comparing `value` to a python list never matches, and the mismatch looks exactly like a write that was refused.","source":"measured 2026-09-18: `get_property {objectPath, propertyPath:'Tags'}` on a tagged PointLight returned `\"value\": \"(\\\"HallLight\\\")\"` and `\"typed\": [\"HallLight\"]` in the same response. A tagging script that read `value` reported all seven lights as refusing a tag they had in fact already taken, and printed `FAILED (read back None)` for every one.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U69","fact":"**`call_function` returns the result in `returnValue`.** Not `result`, which is absent - and absent reads as a function that ran and returned nothing.","source":"measured 2026-09-18: `DebugHoldFix` read through `r.get('result')` printed `None` for a call that had advanced a fault; the same call read through `r.get('returnValue')` printed `1`. The wrong key turned a passing F4 station test into a silent failure.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U76","fact":"**MifBridge has no Nanite endpoint.** The registry carries no `*nanite*` name, so enabling Nanite has to go through `set_property` on the static mesh. The material and texture surface is: `import_texture`, `set_texture_settings`, `create_material`, `create_material_instance`, `create_material_function`, `add_material_expression`, `connect_material_expressions`, `connect_material_property`, `delete_material_expression`, `layout_material_expressions`, `list_material_expressions`, `list_material_parameters`, `set_material_parameter`, `set_material_layers`, `material_statistics`, `recompile_material`, `write_thumbnail_texture`, `generate_lods`, `remove_lods`, `reimport_asset`. **[Reviewed 2026-09-21 against the code - STALE in part: set_nanite_settings and set_landscape_nanite exist (1e8f7463, c1e16bcc) (MifBridgeCommon.cpp:182, 334; server.py:3349); set_nanite_settings and set_landscape_nanite exist (server.py; MifBridgeCollision.cpp:1224); set_nanite_settings and set_landscape_nanite exist (server.py:3348, 2177)]**","source":"read 2026-09-18 from `self_audit` on 127.0.0.1:8860 (458 endpoints registered), filtering the endpoint list for 'material', 'texture', 'nanite', 'lod', 'lightmap' and 'import'. No name matched 'nanite'.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE","status":"superseded"},{"id":"U81","fact":"**`import_texture` performs no suffix or naming inspection.** There is no `_N` / `_ORM` / `_D` convention; colour space and compression are set explicitly per texture through `srgb`, `compressionSettings`, `lodGroup`/`textureGroup`, `mipGenSettings`, `neverStream` and `filter`. For a baked PBR set that means BaseColor with `srgb:true`, Normal as `TC_Normalmap` with `srgb:false`, and a packed ORM linear. There is no `create_material_from_textures` helper, so per-map `add_material_expression` wiring is the expected route.","source":"supplied 2026-09-18 by the MifBridge session from the endpoint tables and add-on source, NOT from a live call - recorded as their reading rather than as a measurement. The complementary Blender half is measured: `bake_texture` sets a non-colour colourspace for data maps on save (B122).","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"B124","fact":"**AgX lifts and desaturates the bottom of the range, so an albedo chosen in the abstract renders about two stops lighter than intended.** Judge a material colour in the render under a PINNED view transform and exposure, or do not judge it.","source":"measured 2026-09-18 while reviewing props in Blender 5.0.1 / EEVEE: a Principled base colour of (0.10, 0.11, 0.12) on an engine block rendered as mid grey and (0.13, 0.22, 0.17) rendered as pastel mint, under `view_transform='AgX'`, `look='None'`, `exposure=0.0`. The values that render as intended are (0.022, 0.025, 0.028) and (0.045, 0.085, 0.062) - roughly a quarter. Separately and first, the review rig's area lights were about four stops too hot, which a 0.20 grey backdrop rendering pure WHITE made obvious; the lighting error has to be excluded before any albedo judgement, or it gets baked into every material.","engine":"5.0.1","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U86","fact":"**Do NOT trim a StaticMesh's `StaticMaterials` array.** `edit_container` will do it - its operations are add, insert, remove, clear, swap, resize, setKey, and MifBridge has no StaticMesh-specific guard - and that is the danger rather than the permission. Removing an element silently shifts every later section's material by one, which is the same corruption as a stale slot name except undetectable by comparing names afterwards. Report a stale tail instead; an unused slot past the last section costs nothing at runtime.","source":"supplied 2026-09-18 by the MifBridge-owning session from UE 5.8 source, with citations: `FStaticMeshSection::MaterialIndex` is an INDEX into StaticMaterials (`Public/StaticMeshResources.h:196`); `UStaticMesh::StaticMaterials` is PRIVATE and carries `UE_DEPRECATED(5.0, \"This must be protected for async build, always use the accessors even internally.\")` (`Classes/Engine/StaticMesh.h:1096`); and every accessor calls `WaitUntilAsyncPropertyReleased(EStaticMeshAsyncProperties::StaticMaterials)` (same header, 1129/1138/1147) which a reflection write through `edit_container` does not, so a trim can land while the async build holds the array. `SetStaticMaterials` via `call_function` is the BlueprintSetter and does take the async path - flagged by them as the next thing to probe rather than asserted to work.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U88","fact":"**`connect_material_property` accepts `fromOutput`**, so a TextureSample can drive a material property directly off a named output port (`RGB`, `R`, `G`, `B`, `A`) without an intermediate arithmetic node. Its full surface is `path (material, materialPath), from (fromExpression), fromOutput (fromOutputName), property (materialProperty)`.","source":"read 2026-09-18 from `describe_endpoint {endpoint:'connect_material_property'}` on 127.0.0.1:8860 (`acceptedSummary`), and exercised: baked-texture materials in this project wire BaseColor from `RGB`, Roughness and Metallic from `R`, and Normal from `RGB`, with all 28 materials reporting a complete shader map afterwards via `material_statistics {compile:true}`. Its `commonMistakes` also names the two wrong keys - `to`/`toExpression` belong to `connect_material_expressions`.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"B129","fact":"**`vertex_info` returns 200 vertices by default however many the mesh has, and its cap is 20,000.** It reports the truncation in fields BESIDE the data - `vertexCount` (the mesh's real total), `verticesReturned`, `verticesTruncated` (bool) and `truncationNote` - so a caller that reads only the `vertices` key gets a short list with no error and no exception. Any pass that writes back per-vertex must check `verticesTruncated` or assert `len(vertices) == vertexCount`; a mesh above 20,000 verts has to be read in `indices` batches and cannot be done in one call at all.","source":"measured 2026-09-18 on Blender 5.0.1 through MifBlender on 127.0.0.1:8796. `vertex_info {object:'TERRAIN_base'}` on a 120x120 grid returned `vertexCount: 14641, verticesReturned: 200, verticesTruncated: true, truncationNote: \"14641 vertices and 200 returned - pass indices, or raise limit (at most 20000).\"`; the same call with `limit: 20000` returned all 14,641 with `verticesTruncated: false`. **Consequence measured in the same session:** a terrain build read the default listing and moved 200 of 14,641 vertices, so 98.6% of the ground stayed flat and the printed height range (76..456 m of an expected 96..980) looked like a plausible answer rather than a partial read. A truncation that still reports a number is worse than an error, because the number passes inspection.","engine":"5.0.1","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U94","fact":"**`TextureCompressionSettings` has 17 entries and the editor will name them all if you ask wrong.** They are `TC_Default, TC_Normalmap, TC_Masks, TC_Grayscale, TC_Displacementmap, TC_VectorDisplacementmap, TC_HDR, TC_EditorIcon, TC_Alpha, TC_DistanceFieldFont, TC_HDR_Compressed, TC_BC7, TC_HalfFloat, TC_LQ, TC_EncodedReflectionCapture, TC_SingleFloat, TC_HDR_F32`. **The refusal also warns that an unrecognised entry name imports as 0 - the FIRST entry - which looks like a deliberate value**, so a typo silently sets `TC_Default` rather than failing.","source":"measured 2026-09-19 on UE 5.8 against the live editor on 8860: `set_property {propertyPath:'CompressionSettings', value:'TC_NotAThing'}` on a Texture2D returned the full enumerator list verbatim in its error, which is how the set was obtained rather than by guessing. `TC_BC7` was then written to 33 textures and read back on all 33.","engine":"","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U96","fact":"**A baked PBR set imports with the right colour space and compression per map type, and this was verified rather than assumed.** Across 33 materials: base colour `TC_Default`/`TC_BC7` with **sRGB true**, normal `TC_Normalmap` with sRGB false, roughness `TC_Grayscale` with sRGB false, metallic `TC_Grayscale` with sRGB false. **`TC_Grayscale` with sRGB off is NOT a fault for a roughness map** - it is uncompressed 8-bit single channel, which is higher quality than `TC_Masks`, at 1 byte per texel against BC4's 0.5.","source":"audited 2026-09-19 on UE 5.8 by reading `CompressionSettings` and `SRGB` off all 104 baked textures through `get_property`. Recorded because an audit that asserted `TC_Masks` was the right answer for roughness flagged 33 correct textures as wrong; the \"expected\" value was the auditor's assumption and not a measured requirement.","engine":"","ue":[],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U495","fact":"**A MetaHuman Character builds (assembles) only when it is Rigged AND has high-resolution textures; both come from Epic's online service (auto-rig, Download Texture Sources).** `CanBuildMetaHuman` refuses with 'Character is not rigged.' or 'The Character is missing textures, use Download Texture Sources ...' - the gates behind U134.","source":"5.8 `MetaHuman/MetaHumanCharacter/Source/MetaHumanCharacterEditor/Private/MetaHumanCharacterEditorSubsystem.cpp:2260` (rig) and 5.8 `MetaHuman/MetaHumanCharacter/Source/MetaHumanCharacterEditor/Private/MetaHumanCharacterEditorSubsystem.cpp:2268` (textures)","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U497","fact":"**MetaHuman face texture synthesis is a LINEAR model, not a neural network: a skin-tone PCA and a low-frequency colour matrix built from 153 scanned characters, combined with one chosen character's high-frequency detail; normal and cavity maps are SELECTED from that character, not synthesized.** The cloud texture request sends only that character index and a resolution. Data: Content/Optional/TextureSynthesis/TS-1.3-F_UE_res-1024_nchr-153 (ts_params.json n_characters 153).","source":"5.8 `MetaHuman/MetaHumanCoreTechLib/Source/MetaHumanCoreTechLib/Private/texture_synthesis/include/ts/sl_model.h:44` (PCA m_skin_tones_pca) and 5.8 `MetaHuman/MetaHumanCoreTechLib/Source/MetaHumanCoreTechLib/Private/texture_synthesis/include/ts/sl_model.h:45` (m_LF_model); 5.8 `MetaHuman/MetaHumanCoreTechLib/Source/MetaHumanCoreTechLib/Public/MetaHumanFaceTextureSynthesizer.h:138` (SelectNormal) and 5.8 `MetaHuman/MetaHumanCoreTechLib/Source/MetaHumanCoreTechLib/Public/MetaHumanFaceTextureSynthesizer.h:144` (SelectCavity); 5.8 `MetaHuman/MetaHumanSDK/Source/MetaHumanSDKEditor/Public/Cloud/MetaHumanTextureSynthesisServiceRequest.h:53` (HighFrequency index)","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U511","fact":"**Substrate is NOT on by default in 5.6, 5.7 or 5.8: the r.Substrate console variable defaults to 0 in all three. From 5.7 the AEC, manufacturing and photo-studio project templates (six of them) set r.Substrate=True in their DefaultEngine.ini; no 5.6 template does, and the Blank and Third Person templates on 5.8 do not.** So a Substrate material graph (a BSDF wired to FrontMaterial) appears only in projects made from those templates or ones that enable it. Measured 2026-10-04 by reading the installed engines' source and grepping every Templates/*/Config/DefaultEngine.ini for r.Substrate.","source":"5.6 `Engine/Source/Runtime/RenderCore/Private/RenderUtils.cpp:1843-1846`, 5.7 `Engine/Source/Runtime/RenderCore/Private/RenderUtils.cpp:1940-1943`, 5.8 `Engine/Source/Runtime/RenderCore/Private/RenderUtils.cpp:2068-2071`, 5.7 `Templates/TP_PhotoStudioBP/Config/DefaultEngine.ini:63`, 5.8 `Templates/TP_AEC_BlankBP/Config/DefaultEngine.ini:42`, 5.8 `Templates/TP_PhotoStudioBP/Config/DefaultEngine.ini:70`","engine":"5.6-5.8","ue":["5.6","5.7","5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U513","fact":"**A material's Substrate cost figures (closures, bytes per pixel, the budget and whether it was simplified) are the shader map's as LAST COMPILED; wiring the graph through the bridge does not recompile it, so they must be read after recompile_material {wait}. Under the blendable GBuffer the figures are real, not 0.** Measured on 5.8.2, blendable GBuffer, platform budget 20 bytes and 1 closure per pixel: one Slab BSDF requests 8 bytes (2 uints) and 1 closure; three Slabs in two Vertical Layers request 68 bytes and 3 closures and are simplified to 8 (bMaterialOutOfBudgetHasBeenSimplified); bUseParameterBlending on the top layer brings it back to 8, not simplified. CORRECTED 2026-10-04: this row first said a blendable GBuffer records 0 bytes - that 0 was the default material's shader map, read before a recompile.","source":"5.8 `Runtime/Engine/Public/MaterialShared.h:1845`; 5.8 `Editor/MaterialEditor/Private/SMaterialEditorSubstrateWidget.cpp:547`; tools/test_ue_spec_extensions_h.py SX4504-SX4505. Measured 2026-10-04","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U514","fact":"**Under GIsRunningUnattendedScript, FMessageDialog::Open shows nothing and returns the dialog's default (No, Cancel) for every type but Ok, and URendererSettings acts on that answer: ray tracing turned on with skin-cache shaders off is set back and r.RayTracing=False written into DefaultEngine.ini; on 5.7 and 5.8 bEnableSubstrate turned on is set back on Cancel. 5.3 and 5.4 raise only an Ok notice for Substrate (Strata on 5.3).** Answered Yes / Ok through FCoreDelegates::ModalMessageDialog with the flag lifted, both writes read back true on 5.8.2 and 5.3.2 and the engine turned the skin cache on itself.","source":"5.3 `Runtime/Core/Private/Misc/MessageDialog.cpp:128`; 5.8 `Runtime/Core/Private/Misc/MessageDialog.cpp:157`; 5.3 `Runtime/Engine/Private/RendererSettings.cpp:188`; 5.3 `Runtime/Engine/Private/RendererSettings.cpp:276`; 5.8 `Runtime/Engine/Private/RendererSettings.cpp:267`; 5.8 `Runtime/Engine/Private/RendererSettings.cpp:356`; tools/test_ue_spec_extensions_h.py SX4200-SX4204. Measured 2026-10-04","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U529","fact":"**A UMaterialExpressionComponentMask created from Python (MaterialEditingLibrary.create_material_expression) does not start with every channel off: setting only one channel True still produced a two-channel output, and two such masks fed into an Add failed the material compile (\"Arithmetic between types float2 and float3\"). Set all four of r, g, b, a explicitly on every mask.** The failed material compiles silently in the editor and falls back to the default material in game; the error only shows in the log.","source":"measured 2026-10-12 on 5.8.2 (material compile log after building masks with only one channel set; fixed by setting all four)","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U530","fact":"**EditorAssetLibrary.save_loaded_asset / save_asset called while a PIE session runs does not save: it logs \"The Editor is currently in a play mode.\" (LogUtils, error) and the package stays dirty. Editing an asset (a material graph) during PIE works and recompiles live; only the save is refused.** Stop PIE, then save.","source":"measured 2026-10-12 on 5.8.2 (pie session log lines at the save calls; the same saves returned true after PIE stopped)","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U532","fact":"**A material whose TextureSampleParameter2D nodes have no default texture (created from Python with only a parameter name) does not compile, and every instance of it renders the engine's default material - flat light grey/white - even though each instance's own texture parameters are set and read back correctly.** Giving each parameter a default texture of the right kind (a colour map for SAMPLERTYPE_COLOR, a TC_Masks texture for SAMPLERTYPE_MASKS, a TC_Normalmap texture for SAMPLERTYPE_NORMAL) and recompiling made the same instances render their textures.","source":"measured 2026-10-12 on 5.8.2 (capture_camera of the same placed actor before and after giving M_PropBaked's three parameters defaults; MI parameter values and component materials read back identical both times)","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U533","fact":"**Removing a Blueprint's SCS static mesh component and adding a new one with the SAME variable name does not update actors already placed in a level: after compile and save, a placed instance's component of that name still had the old component's mesh (an engine Cube), relative scale and override materials, while the Blueprint's template read back the new mesh, scale 1 and no overrides. A freshly spawned actor of the same class came out right.** Swapping a component under a reused name therefore needs the placed actors re-placed (or their instance values reset); a new name, or changing the existing template's mesh in place without removing it, avoids it - an ISM template whose mesh was changed in place updated every placed instance.","source":"measured 2026-10-12 on 5.8.2 (remove_component + add_component under the same names, set template mesh; get_components_by_class on the placed actor vs a spawn_actor_from_class of the same class)","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U553","fact":"**A material that feeds the engine function WorldAlignedNormal's \"XYZ Texture\" output straight into Normal, with Tangent Space Normal off, shades faces whose normal points along -X, -Y or -Z as if their normal pointed into the surface: they get no direct light and render black.** Faces along +X/+Y/+Z are fine. A fix that keeps the detail: n' = normalize(n - 2 * min(dot(n, VertexNormalWS), 0) * VertexNormalWS).","source":"measured 2026-10-16 on 5.8.3 in a project level: one downward ceiling face rendered black with the material (and with a white instance of it), lit with /Engine/BasicShapes/BasicShapeMaterial, and lit with the material once the reflection above was added before the Normal pin. North- and west-facing walls changed the same way. Function asset /Engine/Functions/Engine_MaterialFunctions01/Texturing/WorldAlignedNormal (graph not read)","engine":"5.8 (measured on 5.8.3 only)","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U559","fact":"**Beyond U553: on vertical faces the engine function WorldAlignedNormal's \"XYZ Texture\" output, fed to Normal with Tangent Space Normal off, also leans strongly toward +Z, so a wall is lit only by lights above each point of it (a sharp dark band from the light's height up to the ceiling indoors). U553's reflection does not fix this. Using VertexNormalWS for the normal lit the same walls correctly.**","source":"measured 2026-10-16 on 5.8.3 in a project level: inside walls of a world-aligned material under point lights at z 380 and then 340 (ceiling 420) went dark exactly above the light height; turning off shadows on the fixtures above the lights changed nothing; connecting VertexNormalWS to Normal removed the band. Function graph not read","engine":"5.8 (measured on 5.8.3 only)","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U576","fact":"**A material ScalarParameter with `use_custom_primitive_data` on does not fall back to its own default when the primitive has no custom data at that index: it reads 0.** A masked material clipping at `WorldPosition.z < ClipZ` (ClipZ from custom primitive data 0, default 100000) hid its whole mesh on three placed actors whose construction script had called `SetCustomPrimitiveDataFloat(0, ...)`; the component's serialized `CustomPrimitiveData.Data` read back empty. With the same parameter switched to a plain scalar (use_custom_primitive_data off, default 100000) and set through `SetScalarParameterValueOnMaterials`, the meshes showed and clipped where expected. So a CPD-driven parameter needs its data set on every primitive that uses it, and the editor-side construction call did not leave it set.","source":"Live on UE 5.8.3, 2026-10-17: two captures of the same three actors before and after the switch (capture_camera); `custom_primitive_data.data` read through editor Python. Engine source not read.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U581","fact":"**A MaterialInstance uses its own `PhysMaterial` only when `bOverridePhysMaterial` (Python `override_phys_material`) is true; otherwise `GetPhysicalMaterial()` returns the parent's (here the engine DefaultPhysicalMaterial) even with `phys_material` set.**","source":"Live on UE 5.8.3, 2026-10-17: Python `mi.get_physical_material()` returned DefaultPhysicalMaterial after `set_editor_property(\"phys_material\", pm)`; after `set_editor_property(\"override_phys_material\", True)` it returned pm. In PIE, `GetWheelState(i)` on a Chaos car on that surface then reported pm.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U583","fact":"**In the editor world, `SystemLibrary.line_trace_single` (return physical material on) reports no physical material for bodies on freshly spawned actors: a StaticMeshActor with a cube and `set_phys_material_override(pm)` hit, but PhysMat came back None.** Checking a surface's physical material needs a play world (e.g. a vehicle's wheel state).","source":"Live on UE 5.8.3, 2026-10-17: Python spawn + trace + destroy probe; the hit was blocking, the PhysMat slot None.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U624","fact":"**A static mesh component's mesh-paint overrides are what the engine reads: bOverrideMeshPaintTextureResolution with OverriddenMeshPaintTextureResolution 256 makes GetMeshPaintTextureResolution answer 256 (aligned), and bOverrideMeshPaintTextureCoordinateIndex with index 1 makes the paint UV channel 1; no paint texture exists until the first paint** (RS1900 on 5.5.4, 5.7.4 and 5.8.3, 2026-10-18; on 5.5.4 the project's mesh paint texture support is off by default, so no paint ran there).","source":"5.8 `StaticMeshComponent.cpp:3228`","engine":"5.5-5.8","ue":["5.5","5.6","5.7","5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U635","fact":"**Editing a just-imported static mesh's materials / section info while its async distance-field and mesh-card build runs can crash the editor: EXCEPTION_ACCESS_VIOLATION in `FMeshSectionInfoMap::GetSectionNumber` (StaticMesh.cpp:6505) called from `BuildSignedDistanceFieldBuildSectionData` (DistanceFieldAtlas.cpp:191) <- `CreateCardRepresentationTask` (MeshCardRepresentation.cpp:345) <- `FDistanceFieldAsyncQueue::Build` (DistanceFieldAtlas.cpp:1058), preceded by the ensure \"Container has changed during ranged-for iteration\" (SparseArray.h:1190) on the same stack.**","source":"Live crash, 2026-10-19: UE 5.8.3 CrashContext.runtime-xml call stack and the editor log's handled ensure; the editing script ran in the same Python call as a 37-file FBX import (~85 meshes imported in the session). Waiting for the editor log to go quiet before editing avoided it on the next 3 runs (126 meshes each).","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U643","fact":"**The engine's `WorldAlignedTexture` function (Engine_MaterialFunctions01/Texturing) samples `p = WorldPosition / (-abs(TextureSize))`: X-facing faces use `p.yz`, Y-facing `p.xz`, top/bottom `p.xy`, with no per-side mirroring; result = `lerp(lerp(Y, X, cc(abs(n.x))), Z, cc(abs(n.z)))` where cc = CheapContrast with ProjectionTransitionContrast (default 1), i.e. `saturate(3*abs(n) - 1)`.** A Custom-node triplanar written to this exact mapping reproduced a world-aligned MI's look pixel for pixel; a different (sign-mirrored) mapping visibly moved the pattern.","source":"Read 2026-10-20, UE 5.8.3: MifBridge `list_material_expressions` on /Engine/Functions/Engine_MaterialFunctions01/Texturing/WorldAlignedTexture (include_connections): Divide_0 = FunctionInput WorldPosition / Multiply_13(Abs(TextureSize), -1); masks GB -> Sample_0 (X lerp B), RB -> Sample_3 (lerp A), RG -> Sample_5 (Z); alphas CheapContrast(Abs(normal.r)) and CheapContrast(Abs(normal.b)). Re-run: same call.","engine":"","ue":[],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U644","fact":"**A `MaterialExpressionTextureSampleParameter2D` with sampler type Masks fails the whole material's compile (\"Sampler type is Masks, should be Color\") when its default texture is sRGB - and the engine's stock `/Engine/EngineResources/WhiteSquareTexture` and `/Engine/EngineResources/DefaultTexture` are both sRGB. Give a Masks parameter a linear (sRGB off) default texture.**","source":"Live, 2026-10-20, UE 5.8.3: MifBridge `material_statistics compile:true` on a rebuilt master returned compileErrors naming each of those two textures in turn; with a project TC_Masks/sRGB-off texture as the default the shader map completed. Re-run: create a Masks sampler parameter defaulting to either engine texture, compile.","engine":"","ue":[],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U659","fact":"**A cook writes stable shader keys (`Metadata/PipelineCaches/ShaderStableInfo-<Project>-<ShaderFormat>.shk`) only with `[DevOptions.Shaders] NeedsShaderStableKeys=true` (or bNeedsShaderStableKeys) in the engine ini; `r.ShaderPipelineCache.Enabled=1` alone writes none.**","source":"5.8: Engine/Source/Editor/UnrealEd/Private/Cooker/ShaderLibraryCooking.cpp:262-263, :278-279; live 2026-10-25, stock 5.8.3: two package_project stages [cook] runs of a small probe, the first (Enabled=1 only) with no .shk, the second (plus NeedsShaderStableKeys=true) with ShaderStableInfo-Global-PCD3D_SM5.shk and ShaderStableInfo-MifProbe-PCD3D_SM5.shk.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U660","fact":"**The ShaderPipelineCacheTools commandlet's Dump reads only a `.upipelinecache` or a `.shk`; any other file (a `.spc` included) falls through to the usage text and the commandlet returns 0 - as it also does when Expand's output name has the wrong extension. Its exit code cannot say a cache was made; its log can (\"Loaded N PSOs total\", \"Generated N stable PSOs total\", one \"--- Entry\" per dumped PSO).**","source":"5.8: Engine/Source/Editor/UnrealEd/Private/Commandlets/ShaderPipelineCacheToolsCommandlet.cpp:2918-2935 (Dump), :2963-2972 (usage, return 0), :1112-1116 (Expand output check returns 0), :1213, :1451; live 2026-10-25 stock 5.8.3: a 45 s -logPSO session of a packaged probe recorded 35 PSOs, Expand generated 79 stable PSOs, Dump listed 35 entries (PSOR101-PSOR103).","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U682","fact":"**Weathered, baked PNGs through the textures form hold their channels: Unreal's source pixels are the file's, and the settings are what a PBR material needs.**","source":"measured 2026-11-01, tools/test_handoff_materials14.py BK100-BK102","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)","value":"BC sRGB TC_Default (DXT1), N not sRGB TC_Normalmap (BC5), ORM not sRGB TC_Masks (DXT1); mean, min and max per channel within 0.0002 of the file; read from the source, not the compressed mips"}]