Merge branch 'Swiggies-stochastic-sampling-node'

This commit is contained in:
Wojciech Figat
2023-01-13 10:40:39 +01:00
2 changed files with 230 additions and 0 deletions

View File

@@ -86,6 +86,71 @@ namespace FlaxEditor.Surface.Archetypes
}
}
// TODO merge the above and below function into one?
internal class ProceduralSampleNode : SurfaceNode
{
private ComboBox _textureGroupPicker;
public ProceduralSampleNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
: base(id, context, nodeArch, groupArch)
{
}
public override void OnValuesChanged()
{
base.OnValuesChanged();
UpdateUI();
}
public override void OnLoaded()
{
base.OnLoaded();
UpdateUI();
}
private void UpdateUI()
{
if ((int)Values[1] == (int)CommonSamplerType.TextureGroup)
{
if (_textureGroupPicker == null)
{
_textureGroupPicker = new ComboBox
{
Location = new Float2(FlaxEditor.Surface.Constants.NodeMarginX + 50, FlaxEditor.Surface.Constants.NodeMarginY + FlaxEditor.Surface.Constants.NodeHeaderSize + FlaxEditor.Surface.Constants.LayoutOffsetY * 3),
Width = 100,
Parent = this,
};
_textureGroupPicker.SelectedIndexChanged += OnSelectedTextureGroupChanged;
var groups = GameSettings.Load<StreamingSettings>();
if (groups?.TextureGroups != null)
{
for (int i = 0; i < groups.TextureGroups.Length; i++)
_textureGroupPicker.AddItem(groups.TextureGroups[i].Name);
}
}
else
{
_textureGroupPicker.Visible = true;
}
_textureGroupPicker.SelectedIndexChanged -= OnSelectedTextureGroupChanged;
_textureGroupPicker.SelectedIndex = (int)Values[2];
_textureGroupPicker.SelectedIndexChanged += OnSelectedTextureGroupChanged;
}
else if (_textureGroupPicker != null)
{
_textureGroupPicker.Visible = false;
}
ResizeAuto();
}
private void OnSelectedTextureGroupChanged(ComboBox comboBox)
{
SetValue(2, _textureGroupPicker.SelectedIndex);
}
}
/// <summary>
/// The nodes for that group.
/// </summary>
@@ -405,6 +470,29 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Output(0, "Color", typeof(Float3), 3)
}
},
new NodeArchetype
{
TypeID = 17,
Create = (id, context, arch, groupArch) => new ProceduralSampleNode(id, context, arch, groupArch),
Title = "Procedural Sample Texture",
Description = "Samples a texture to create a more natural look with less obvious tiling.",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(240, 100),
DefaultValues = new object[]
{
new Float2(1.0f, 1.0f),
2,
0,
},
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Texture", true, typeof(FlaxEngine.Object), 0),
NodeElementArchetype.Factory.Input(1, "UV", true, typeof(Float2), 1, 0),
NodeElementArchetype.Factory.Output(0, "Color", typeof(Float4), 2),
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 2, "Sampler"),
NodeElementArchetype.Factory.ComboBox(50, Surface.Constants.LayoutOffsetY * 2, 100, 1, typeof(CommonSamplerType))
}
},
};
}
}

View File

@@ -669,6 +669,148 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value)
_writer.Write(*triplanarTexture);
value = result;
}
// Procedural Texture Sample
case 17:
{
enum CommonSamplerType
{
LinearClamp = 0,
PointClamp = 1,
LinearWrap = 2,
PointWrap = 3,
TextureGroup = 4,
};
const Char* SamplerNames[]
{
TEXT("SamplerLinearClamp"),
TEXT("SamplerLinearWrap"),
};
auto textureBox = node->GetBox(0);
auto uvsBox = node->GetBox(1);
Value uvs;
const bool useCustomUVs = uvsBox->HasConnection();
if (useCustomUVs)
{
// Get custom UVs
uvs = eatBox(uvsBox->GetParent<Node>(), uvsBox->FirstConnection());
}
else
{
// Use default UVs
uvs = getUVs;
}
if (!textureBox->HasConnection())
{
// No texture to sample
value = Value::Zero;
break;
}
if (!CanUseSample(_treeType))
{
// Must sample texture in pixel shader
value = Value::Zero;
break;
}
const auto texture = eatBox(textureBox->GetParent<Node>(), textureBox->FirstConnection());
const auto textureParam = findParam(texture.Value);
const bool isNormalMap = textureParam->Type == MaterialParameterType::NormalMap;
const Char* samplerName;
const int32 samplerIndex = node->Values[1].AsInt;
if (samplerIndex == TextureGroup)
{
auto& textureGroupSampler = findOrAddTextureGroupSampler(node->Values[2].AsInt);
samplerName = *textureGroupSampler.ShaderName;
}
else if (samplerIndex >= 0 && samplerIndex < ARRAY_COUNT(SamplerNames))
{
samplerName = SamplerNames[samplerIndex];
}
else
{
OnError(node, box, TEXT("Invalid texture sampler."));
return;
}
auto result = writeLocal(Value::InitForZero(ValueType::Float4), node);
createGradients(node);
auto proceduralSample = String::Format(TEXT(
" {{\n"
" float W1, W2, W3;\n"
" float2 vertex1, vertex2, vertex3;\n"
" float2 uv = {0} * 3.464; // 2 * sqrt (3);\n"
" float2 UV1, UV2, UV3;\n"
" const float2x2 gridToSkewedGrid = float2x2( 1.0, 0.0, -0.57735027, 1.15470054 );\n"
" float2 skewedCoord = mul( gridToSkewedGrid, uv );\n"
" int2 baseId = int2( floor( skewedCoord ) );\n"
" float3 temp = float3( frac( skewedCoord ), 0 );\n"
" temp.z = 1.0 - temp.x - temp.y;\n"
" if ( temp.z > 0.0 )\n"
" {{\n"
" W1 = temp.z;\n"
" W2 = temp.y;\n"
" W3 = temp.x;\n"
" vertex1 = baseId;\n"
" vertex2 = baseId + int2( 0, 1 );\n"
" vertex3 = baseId + int2( 1, 0 );\n"
" }}\n"
" else\n"
" {{\n"
" W1 = -temp.z;\n"
" W2 = 1.0 - temp.y;\n"
" W3 = 1.0 - temp.x;\n"
" vertex1 = baseId + int2( 1, 1 );\n"
" vertex2 = baseId + int2( 1, 0 );\n"
" vertex3 = baseId + int2( 0, 1 );\n"
" }}\n"
" UV1 = {0} + frac( sin( mul( float2x2( 127.1, 311.7, 269.5, 183.3 ), vertex1 ) ) * 43758.5453 );\n"
" UV2 = {0} + frac( sin( mul( float2x2( 127.1, 311.7, 269.5, 183.3 ), vertex2 ) ) * 43758.5453 );\n"
" UV3 = {0} + frac( sin( mul( float2x2( 127.1, 311.7, 269.5, 183.3 ), vertex3 ) ) * 43758.5453 );\n"
" float4 tex1 = {1}.SampleGrad({4}, UV1, {2}, {3});\n"
" float4 tex2 = {1}.SampleGrad({4}, UV2, {2}, {3});\n"
" float4 tex3 = {1}.SampleGrad({4}, UV3, {2}, {3});\n"
),
uvs.Value, // {0}
texture.Value, // {1}
_ddx.Value, // {2}
_ddy.Value, // {3}
samplerName // {4}
);
// Decode normal map vector
if (isNormalMap)
{
proceduralSample += String::Format(TEXT(
" tex1.xy = tex1.xy * 2.0 - 1.0;\n"
" tex1.z = sqrt(saturate(1.0 - dot(tex1.xy, tex1.xy))); \n"
" tex2.xy = tex2.xy * 2.0 - 1.0;\n"
" tex2.z = sqrt(saturate(1.0 - dot(tex2.xy, tex2.xy))); \n"
" tex3.xy = tex3.xy * 2.0 - 1.0;\n"
" tex3.z = sqrt(saturate(1.0 - dot(tex3.xy, tex3.xy))); \n"
));
}
proceduralSample += String::Format(TEXT(
" tex1 *= W1;\n"
" tex2 *= W2;\n"
" tex3 *= W3;\n"
" {0} = tex1 + tex2 + tex3;\n"
" }}\n"
),
result.Value // {0}
);
_writer.Write(*proceduralSample);
value = result;
break;
}
default:
break;
}