Skip to content

Commit 05f704a

Browse files
committed
Implement StaticSwitchParameter and CollectionParameter support in DreamShader
- Added functionality to parse and evaluate StaticSwitchParameter calls, including handling of True and False inputs. - Introduced CollectionParameter support, allowing for dynamic parameter retrieval from MaterialParameterCollections. - Enhanced error handling for missing required inputs and default arguments in shader functions. - Updated metadata application for material expressions to include descriptions, groups, and sort priorities. - Improved asset reference extraction for parameters and added validation for parameter names and types. - Refactored code for better readability and maintainability, including the introduction of helper functions for expression metadata and parameter handling.
1 parent 1897522 commit 05f704a

12 files changed

Lines changed: 1441 additions & 39 deletions

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
# DreamShader ChangeLog
22

3+
## 1.2.3 - 2026-04-29
4+
5+
### Parameters
6+
7+
- Added declaration metadata `[Group="...", SortPriority=32, Description="..."]` for material `Properties` and function input/output declarations.
8+
- Added explicit Parameter node declarations including `ScalarParameter`, `VectorParameter`, `TextureObjectParameter`, texture sample parameter nodes, `StaticBoolParameter`, and `StaticSwitchParameter`.
9+
- Added `StaticSwitchParameter` graph calls, for example `UseDetail(True=detailColor, False=baseColor)`.
10+
- Added `UE.CollectionParam(Collection=Path(...), Parameter="...")` for Material Parameter Collection reads.
11+
12+
### Function Defaults
13+
14+
- Added `opt` inputs for `ShaderFunction` and `VirtualFunction`.
15+
- Added `default` call arguments for optional material function inputs, preserving Unreal FunctionInput preview defaults.
16+
- Generated `ShaderFunction` assets now write input/output descriptions and sort priorities to FunctionInput / FunctionOutput nodes.
17+
- VirtualFunction copy/create/sync now emits optional inputs, preview defaults, and pin metadata when available.
18+
319
## 1.2.2 - 2026-04-29
420

521
### VirtualFunction Workflow

Docs/LanguageReference.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ DreamShaderLang 是 DreamShader 插件使用的文本语言。它用 `.dsm` / `.
44

55
| 项目 | 内容 |
66
| --- | --- |
7-
| 插件版本 | `1.2.2` |
7+
| 插件版本 | `1.2.3` |
88
| 源文件 | `.dsm` / `.dsh` |
99
| 主要产物 | `UMaterial` / `UMaterialFunction` |
1010
| 开发者 | TypeDreamMoon |
@@ -210,6 +210,23 @@ Properties = {
210210
float Strength = 1.0;
211211
vec3 Tint = vec3(1.0, 1.0, 1.0);
212212
Texture2D MainTex = Path(Game, "/Textures/T_Main");
213+
StaticSwitchParameter UseDetail = true [Group="Switches", SortPriority=30, Description="Use detail branch"];
214+
}
215+
```
216+
217+
`float` / `vec3` / `Texture2D` 简写外,`Properties` 也支持常见显式 Parameter 节点类型,例如 `ScalarParameter``VectorParameter``DoubleVectorParameter``TextureObjectParameter``TextureSampleParameter2D``StaticBoolParameter``StaticSwitchParameter` 等。
218+
219+
声明尾部可以加元数据:
220+
221+
```c
222+
ScalarParameter Roughness = 0.35 [Group="Surface", SortPriority=10, Description="Material roughness"];
223+
```
224+
225+
`StaticSwitchParameter``Graph` 中以同名函数形式使用:
226+
227+
```c
228+
Graph = {
229+
float3 finalColor = UseDetail(True=detailColor, False=baseColor);
213230
}
214231
```
215232

@@ -220,10 +237,18 @@ Properties = {
220237
```c
221238
Inputs = {
222239
vec3 InColor;
223-
float Strength = 1.0;
240+
opt float Strength = 1.0 [Description="Preview default strength"];
224241
}
225242
```
226243

244+
`opt` 表示该输入可选,并使用 Unreal Function Input 的预览值作为默认值。调用 `ShaderFunction` / `VirtualFunction` 时可以传 `default`,也可以省略尾部可选参数:
245+
246+
```c
247+
float3 color = MyFunction(InColor, default, Output="Result");
248+
```
249+
250+
`ShaderFunction` / `VirtualFunction``Inputs` / `Outputs` 同样支持 `[SortPriority=..., Description="..."]` 元数据;`Group` 会被解析并保留在语法层,但 Unreal Function Input / Output 本身没有分组字段。
251+
227252
### 3.3 `Outputs`
228253

229254
`Shader` 中既能声明输出变量,也能绑定 Unreal 材质属性。
@@ -285,6 +310,8 @@ Options = {
285310
- 标量、向量构造。
286311
- Brace initializer。
287312
- `UE.*` builtin 调用。
313+
- `UE.CollectionParam(Collection=Path(...), Parameter="Name")` 读取 Material Parameter Collection。
314+
- `UE.StaticSwitchParameter(...)``StaticSwitchParameter` 属性调用。
288315
- `Function(...)` / `Namespace::Function(...)` 独立调用。
289316
- `ShaderFunction(...)` / `VirtualFunction(...)` 值调用。
290317
- 基础 `if` / `else` 图分支。
@@ -375,7 +402,7 @@ Properties = {
375402
规则:
376403

377404
- 单参数形式必须使用 `/Game/...``/Engine/...`
378-
- 双参数形式的根名支持 `Game` / `Engine`
405+
- 双参数形式的根名支持 `Game` / `Engine` / `Plugin.PluginName` / `Plugins.PluginName`
379406
- 如果未显式写 `.AssetName`,会自动补成合法 Unreal object path。
380407
- 会校验声明类型和实际资产类型是否一致。
381408

DreamShader.uplugin

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"FileVersion": 3,
3-
"Version": 122,
4-
"VersionName": "1.2.2",
3+
"Version": 123,
4+
"VersionName": "1.2.3",
55
"FriendlyName": "DreamShader",
66
"Description": "Author Unreal materials and material functions with DreamShaderLang .dsm/.dsh source files that auto-generate standard UMaterial and UMaterialFunction assets.",
77
"Category": "DreamPlugin",

README.md

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
DreamShader 是一个 Unreal Engine 材质生成插件。它提供 `DreamShaderLang` 文本语言,让你用 `.dsm` / `.dsh` 源文件描述材质图、共享函数和材质函数,并自动生成标准 Unreal `UMaterial` / `UMaterialFunction` 资产。
44

5-
> 当前版本:`1.2.2`。插件仍在持续开发中,核心工作流已经可用,建议在项目中逐步接入并保留源文件版本管理。
5+
> 当前版本:`1.2.3`。插件仍在持续开发中,核心工作流已经可用,建议在项目中逐步接入并保留源文件版本管理。
66
77
## 核心能力
88

99
- 使用文本源文件维护材质逻辑,减少手动连材质节点的重复工作。
1010
-`Shader(Name="...", Root="Game")` 生成 `UMaterial`,从 `ShaderFunction(Name="...", Root="Game")` 生成 `UMaterialFunction`
1111
- 使用 `VirtualFunction(Name="...")` 声明现有 Unreal `UMaterialFunction` 资产,并在 `Graph` 中直接调用,不会生成或覆盖资产。
12+
- `Properties` 支持显式 Parameter 节点、`StaticSwitchParameter``UE.CollectionParam(...)``[Group="...", SortPriority=32, Description="..."]` 元数据。
13+
- `ShaderFunction` / `VirtualFunction` 输入支持 `opt` 和调用侧 `default`,用于复用 Unreal FunctionInput 的预览默认值。
1214
-`Graph = { ... }` 中声明变量、调用 UE 材质节点、调用共享函数,并绑定材质输出。
1315
-`Function` / `Namespace` 中编写可复用 HLSL 风格 helper。
1416
- 支持 `Inline` / `SelfContained` 函数,把依赖代码嵌入材质 Custom 节点,便于在未安装 DreamShader 的项目中继续使用生成资产。
@@ -98,6 +100,52 @@ Shader(Name="DreamMaterials/M_Minimal", Root="Plugin.MyPlugin")
98100
这会使用 `/MyPlugin/DreamMaterials/M_Minimal.M_Minimal` 作为 Unreal object path,并物理保存到 `[Project]/Plugins/MyPlugin/Content/DreamMaterials/M_Minimal.uasset`
99101
`Plugins.MyPlugin` / `Plugins/MyPlugin` 也作为兼容写法支持。
100102

103+
## Parameter 与默认输入
104+
105+
`Properties` 可以继续使用 `float` / `float3` / `Texture2D` 简写,也可以显式声明 Unreal Parameter 节点,并在声明尾部加元数据:
106+
107+
```c
108+
Properties = {
109+
ScalarParameter Roughness = 0.35 [Group="Surface", SortPriority=10, Description="Material roughness"];
110+
VectorParameter Tint = float4(1.0, 0.9, 0.8, 1.0) [Group="Surface", SortPriority=20];
111+
StaticSwitchParameter UseDetail = true [Group="Switches", SortPriority=30];
112+
}
113+
114+
Graph = {
115+
float3 baseCol = Tint.rgb;
116+
float3 detailCol = baseCol * 2.0;
117+
float3 finalCol = UseDetail(True=detailCol, False=baseCol);
118+
}
119+
```
120+
121+
Material Parameter Collection 可以在 Graph 中直接读取:
122+
123+
```c
124+
float wind = UE.CollectionParam(
125+
Collection=Path(Game, "MaterialParameterCollections/MPC_Global"),
126+
Parameter="WindStrength");
127+
```
128+
129+
`ShaderFunction` / `VirtualFunction` 的输入可以标记 `opt`。调用时传 `default` 或省略尾部可选参数,会使用 Unreal FunctionInput 的预览默认值:
130+
131+
```c
132+
ShaderFunction(Name="Functions/DebugValue")
133+
{
134+
Inputs = {
135+
float2 UV;
136+
opt float4 ColorA = float4(0.3, 0.3, 0.7, 0.7) [Description="Debug color A"];
137+
}
138+
139+
Outputs = {
140+
float4 Result;
141+
}
142+
143+
Graph = {
144+
Result = ColorA;
145+
}
146+
}
147+
```
148+
101149
## 共享函数示例
102150
103151
`.dsh` 文件适合放共享函数:

Source/DreamShader/Private/DreamShaderParserScanner.cpp

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ namespace UE::DreamShader::Private
389389
TArray<FString> Statements;
390390
FString Current;
391391
int32 ParenthesisDepth = 0;
392+
int32 BracketDepth = 0;
392393
bool bInString = false;
393394

394395
for (int32 Index = 0; Index < BlockContent.Len(); ++Index)
@@ -430,7 +431,21 @@ namespace UE::DreamShader::Private
430431
continue;
431432
}
432433

433-
if (Char == TCHAR(';') && ParenthesisDepth == 0)
434+
if (Char == TCHAR('['))
435+
{
436+
++BracketDepth;
437+
Current.AppendChar(Char);
438+
continue;
439+
}
440+
441+
if (Char == TCHAR(']'))
442+
{
443+
BracketDepth = FMath::Max(0, BracketDepth - 1);
444+
Current.AppendChar(Char);
445+
continue;
446+
}
447+
448+
if (Char == TCHAR(';') && ParenthesisDepth == 0 && BracketDepth == 0)
434449
{
435450
Current.TrimStartAndEndInline();
436451
if (!Current.IsEmpty())
@@ -458,6 +473,7 @@ namespace UE::DreamShader::Private
458473
TArray<FString> Parts;
459474
FString Current;
460475
int32 ParenthesisDepth = 0;
476+
int32 BracketDepth = 0;
461477
bool bInString = false;
462478

463479
for (int32 Index = 0; Index < Input.Len(); ++Index)
@@ -499,7 +515,21 @@ namespace UE::DreamShader::Private
499515
continue;
500516
}
501517

502-
if (Char == Delimiter && ParenthesisDepth == 0)
518+
if (Char == TCHAR('['))
519+
{
520+
++BracketDepth;
521+
Current.AppendChar(Char);
522+
continue;
523+
}
524+
525+
if (Char == TCHAR(']'))
526+
{
527+
BracketDepth = FMath::Max(0, BracketDepth - 1);
528+
Current.AppendChar(Char);
529+
continue;
530+
}
531+
532+
if (Char == Delimiter && ParenthesisDepth == 0 && BracketDepth == 0)
503533
{
504534
Current.TrimStartAndEndInline();
505535
if (!Current.IsEmpty())
@@ -525,6 +555,7 @@ namespace UE::DreamShader::Private
525555
bool SplitTopLevelAssignment(const FString& InText, FString& OutLeft, FString& OutRight)
526556
{
527557
int32 ParenthesisDepth = 0;
558+
int32 BracketDepth = 0;
528559
bool bInString = false;
529560

530561
for (int32 Index = 0; Index < InText.Len(); ++Index)
@@ -562,7 +593,19 @@ namespace UE::DreamShader::Private
562593
continue;
563594
}
564595

565-
if (Char == TCHAR('=') && ParenthesisDepth == 0)
596+
if (Char == TCHAR('['))
597+
{
598+
++BracketDepth;
599+
continue;
600+
}
601+
602+
if (Char == TCHAR(']'))
603+
{
604+
BracketDepth = FMath::Max(0, BracketDepth - 1);
605+
continue;
606+
}
607+
608+
if (Char == TCHAR('=') && ParenthesisDepth == 0 && BracketDepth == 0)
566609
{
567610
OutLeft = InText.Left(Index).TrimStartAndEnd();
568611
OutRight = InText.Mid(Index + 1).TrimStartAndEnd();
@@ -577,6 +620,7 @@ namespace UE::DreamShader::Private
577620
{
578621
const FString Trimmed = InText.TrimStartAndEnd();
579622
int32 ParenthesisDepth = 0;
623+
int32 BracketDepth = 0;
580624
bool bInString = false;
581625
int32 LastWhitespaceIndex = INDEX_NONE;
582626

@@ -615,7 +659,19 @@ namespace UE::DreamShader::Private
615659
continue;
616660
}
617661

618-
if (ParenthesisDepth == 0 && FChar::IsWhitespace(Char))
662+
if (Char == TCHAR('['))
663+
{
664+
++BracketDepth;
665+
continue;
666+
}
667+
668+
if (Char == TCHAR(']'))
669+
{
670+
BracketDepth = FMath::Max(0, BracketDepth - 1);
671+
continue;
672+
}
673+
674+
if (ParenthesisDepth == 0 && BracketDepth == 0 && FChar::IsWhitespace(Char))
619675
{
620676
LastWhitespaceIndex = Index;
621677
}

0 commit comments

Comments
 (0)