Files
UShaderLab/Source/UShaderLabEditor/Private/MaterialExpressionShaderLabParameterAnchor.h
Eragon-Brisingr a861b4680b Init repo
2026-06-30 15:36:02 +08:00

64 lines
3.2 KiB
C++

// Copyright FlecsProj. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "MaterialValueType.h"
#include "Materials/MaterialExpressionCustomOutput.h"
#include "MaterialExpressionShaderLabParameterAnchor.generated.h"
/**
* Internal ShaderLab node with two jobs, both keeping workaround machinery off the user's body nodes:
*
* 1. Anchors parameters so the Material Instance editor lists them. StaticBool parameters are consumed
* only inside HLSL `#if`, so they would otherwise be unconnected; the instance editor's visibility
* walk only reaches parameters connected to a material output or a CustomOutput. This is a
* CustomOutput, so it is always traversed; each anchored parameter reaches one of its inputs (via
* its static-switch selector).
*
* 2. Injects per-permutation static-switch `#define`s with ZERO engine modifications. Each input is a
* StaticSwitch over two `#define <Switch> 1` / `#define <Switch> 0` Custom nodes. This node returns
* ShouldCompileBeforeAttributes()==true, so FHLSLMaterialTranslator compiles it BEFORE the material
* attributes (FrontMaterial / body). Its Compile() compiles each input, so the selected `#define`
* (only the taken StaticSwitch branch is compiled) is emitted ahead of every body's `#if`. The
* `#define` leaks forward (preprocessor is global) and the body's `#if <Switch>` sees the current
* permutation's value — including a Material Instance's static override.
*
* It only ever lives in the editor graph (never serialized into a cooked package).
*/
UCLASS(MinimalAPI, collapsecategories, hidecategories = Object)
class UMaterialExpressionShaderLabParameterAnchor : public UMaterialExpressionCustomOutput
{
GENERATED_UCLASS_BODY()
/** One input per static-switch selector (a StaticSwitch over two `#define`-emitting Custom nodes). */
UPROPERTY()
TArray<FExpressionInput> Inputs;
//~ Begin UMaterialExpressionCustomOutput Interface
// One output so the translator actually compiles us (a zero-output CustomOutput is skipped). The
// output value is unused; compiling us is purely to emit our inputs' `#define`s before attributes.
virtual int32 GetNumOutputs() const override { return 1; }
virtual FString GetFunctionName() const override { return TEXT("ShaderLabParameterAnchor"); }
#if WITH_EDITOR
virtual bool NeedsCustomOutputDefines() override { return false; } // don't emit NUM_MATERIAL_OUTPUTS_*
virtual bool ShouldCompileBeforeAttributes() override { return true; } // emit #defines ahead of body #if
#endif
//~ End UMaterialExpressionCustomOutput Interface
#if WITH_EDITOR
//~ Begin UMaterialExpression Interface
virtual TArrayView<FExpressionInput*> GetInputsView() override;
virtual FExpressionInput* GetInput(int32 InputIndex) override;
virtual FName GetInputName(int32 InputIndex) const override;
virtual EMaterialValueType GetInputValueType(int32 InputIndex) override { return MCT_Float1; }
virtual int32 Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) override;
virtual void GetCaption(TArray<FString>& OutCaptions) const override;
//~ End UMaterialExpression Interface
#endif
private:
/** Scratch pointer view rebuilt by GetInputsView(). */
TArray<FExpressionInput*> CachedInputs;
};