|
@@ -1,14 +1,51 @@
|
|
|
using System;
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
using System.Collections.Generic;
|
|
|
|
|
+using System.Globalization;
|
|
|
using System.Linq;
|
|
using System.Linq;
|
|
|
using System.Numerics;
|
|
using System.Numerics;
|
|
|
using System.Text;
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
+using IMAGEFILE = SharpGLTF.Memory.MemoryImage;
|
|
|
|
|
+
|
|
|
namespace SharpGLTF.Materials
|
|
namespace SharpGLTF.Materials
|
|
|
{
|
|
{
|
|
|
[System.Diagnostics.DebuggerDisplay("{Name} {ShaderStyle}")]
|
|
[System.Diagnostics.DebuggerDisplay("{Name} {ShaderStyle}")]
|
|
|
public class MaterialBuilder
|
|
public class MaterialBuilder
|
|
|
{
|
|
{
|
|
|
|
|
+ #region constants
|
|
|
|
|
+
|
|
|
|
|
+ public const string SHADERUNLIT = "Unlit";
|
|
|
|
|
+ public const string SHADERPBRMETALLICROUGHNESS = "PBRMetallicRoughness";
|
|
|
|
|
+ public const string SHADERPBRSPECULARGLOSSINESS = "PBRSpecularGlossiness";
|
|
|
|
|
+
|
|
|
|
|
+ private static readonly KnownChannel[] _UnlitChannels = new[] { KnownChannel.BaseColor };
|
|
|
|
|
+
|
|
|
|
|
+ private static readonly KnownChannel[] _MetRouChannels = new[]
|
|
|
|
|
+ {
|
|
|
|
|
+ KnownChannel.BaseColor,
|
|
|
|
|
+ KnownChannel.MetallicRoughness,
|
|
|
|
|
+ KnownChannel.Normal,
|
|
|
|
|
+ KnownChannel.Occlusion,
|
|
|
|
|
+ KnownChannel.Emissive,
|
|
|
|
|
+ KnownChannel.ClearCoat,
|
|
|
|
|
+ KnownChannel.ClearCoatNormal,
|
|
|
|
|
+ KnownChannel.ClearCoatRoughness
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ private static readonly KnownChannel[] _SpeGloChannels = new[]
|
|
|
|
|
+ {
|
|
|
|
|
+ KnownChannel.Diffuse,
|
|
|
|
|
+ KnownChannel.SpecularGlossiness,
|
|
|
|
|
+ KnownChannel.Normal,
|
|
|
|
|
+ KnownChannel.Occlusion,
|
|
|
|
|
+ KnownChannel.Emissive,
|
|
|
|
|
+ // KnownChannel.ClearCoat,
|
|
|
|
|
+ // KnownChannel.ClearCoatNormal,
|
|
|
|
|
+ // KnownChannel.ClearCoatRoughness
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
#region lifecycle
|
|
#region lifecycle
|
|
|
|
|
|
|
|
public MaterialBuilder(string name = null)
|
|
public MaterialBuilder(string name = null)
|
|
@@ -49,14 +86,15 @@ namespace SharpGLTF.Materials
|
|
|
|
|
|
|
|
#region data
|
|
#region data
|
|
|
|
|
|
|
|
- public const string SHADERUNLIT = "Unlit";
|
|
|
|
|
- public const string SHADERPBRMETALLICROUGHNESS = "PBRMetallicRoughness";
|
|
|
|
|
- public const string SHADERPBRSPECULARGLOSSINESS = "PBRSpecularGlossiness";
|
|
|
|
|
-
|
|
|
|
|
|
|
+ [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
|
|
|
private readonly List<ChannelBuilder> _Channels = new List<ChannelBuilder>();
|
|
private readonly List<ChannelBuilder> _Channels = new List<ChannelBuilder>();
|
|
|
|
|
|
|
|
|
|
+ [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
|
|
|
private MaterialBuilder _CompatibilityFallbackMaterial;
|
|
private MaterialBuilder _CompatibilityFallbackMaterial;
|
|
|
|
|
|
|
|
|
|
+ [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
|
|
|
|
|
+ private string _ShaderStyle = SHADERPBRMETALLICROUGHNESS;
|
|
|
|
|
+
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Gets or sets the name of this <see cref="MaterialBuilder"/> instance.
|
|
/// Gets or sets the name of this <see cref="MaterialBuilder"/> instance.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
@@ -71,7 +109,11 @@ namespace SharpGLTF.Materials
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
public Boolean DoubleSided { get; set; } = false;
|
|
public Boolean DoubleSided { get; set; } = false;
|
|
|
|
|
|
|
|
- public String ShaderStyle { get; set; } = SHADERPBRMETALLICROUGHNESS;
|
|
|
|
|
|
|
+ public String ShaderStyle
|
|
|
|
|
+ {
|
|
|
|
|
+ get => _ShaderStyle;
|
|
|
|
|
+ set => _SetShader(value);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
public static bool AreEqualByContent(MaterialBuilder x, MaterialBuilder y)
|
|
public static bool AreEqualByContent(MaterialBuilder x, MaterialBuilder y)
|
|
|
{
|
|
{
|
|
@@ -91,7 +133,7 @@ namespace SharpGLTF.Materials
|
|
|
if (x.AlphaMode != y.AlphaMode) return false;
|
|
if (x.AlphaMode != y.AlphaMode) return false;
|
|
|
if (x.AlphaCutoff != y.AlphaCutoff) return false;
|
|
if (x.AlphaCutoff != y.AlphaCutoff) return false;
|
|
|
if (x.DoubleSided != y.DoubleSided) return false;
|
|
if (x.DoubleSided != y.DoubleSided) return false;
|
|
|
- if (x.ShaderStyle != y.ShaderStyle) return false;
|
|
|
|
|
|
|
+ if (x._ShaderStyle != y._ShaderStyle) return false;
|
|
|
|
|
|
|
|
if (!AreEqualByContent(x._CompatibilityFallbackMaterial, y._CompatibilityFallbackMaterial)) return false;
|
|
if (!AreEqualByContent(x._CompatibilityFallbackMaterial, y._CompatibilityFallbackMaterial)) return false;
|
|
|
|
|
|
|
@@ -107,7 +149,7 @@ namespace SharpGLTF.Materials
|
|
|
var xc = x.GetChannel(ckey);
|
|
var xc = x.GetChannel(ckey);
|
|
|
var yc = y.GetChannel(ckey);
|
|
var yc = y.GetChannel(ckey);
|
|
|
|
|
|
|
|
- if (!ChannelBuilder.AreEqual(xc, yc)) return false;
|
|
|
|
|
|
|
+ if (!ChannelBuilder.AreEqualByContent(xc, yc)) return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
return true;
|
|
@@ -155,72 +197,146 @@ namespace SharpGLTF.Materials
|
|
|
|
|
|
|
|
#region API
|
|
#region API
|
|
|
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Sets <see cref="MaterialBuilder.ShaderStyle"/> to use <see cref="SHADERUNLIT"/>.
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- /// <returns>This <see cref="MaterialBuilder"/>.</returns>
|
|
|
|
|
- public MaterialBuilder WithUnlitShader() { return WithShader(SHADERUNLIT); }
|
|
|
|
|
|
|
+ private void _SetShader(string shader)
|
|
|
|
|
+ {
|
|
|
|
|
+ Guard.NotNullOrEmpty(shader, nameof(shader));
|
|
|
|
|
+ Guard.IsTrue(shader == SHADERUNLIT || shader == SHADERPBRMETALLICROUGHNESS || shader == SHADERPBRSPECULARGLOSSINESS, nameof(shader));
|
|
|
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Sets <see cref="MaterialBuilder.ShaderStyle"/> to use <see cref="SHADERPBRMETALLICROUGHNESS"/>.
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- /// <returns>This <see cref="MaterialBuilder"/>.</returns>
|
|
|
|
|
- public MaterialBuilder WithMetallicRoughnessShader() { return WithShader(SHADERPBRMETALLICROUGHNESS); }
|
|
|
|
|
|
|
+ _ShaderStyle = shader;
|
|
|
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Sets <see cref="MaterialBuilder.ShaderStyle"/> to use <see cref="SHADERPBRSPECULARGLOSSINESS"/>.
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- /// <returns>This <see cref="MaterialBuilder"/>.</returns>
|
|
|
|
|
- public MaterialBuilder WithSpecularGlossinessShader() { return WithShader(SHADERPBRSPECULARGLOSSINESS); }
|
|
|
|
|
|
|
+ var validChannels = _GetValidChannels();
|
|
|
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Sets <see cref="MaterialBuilder.ShaderStyle"/>.
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- /// <param name="shader">
|
|
|
|
|
- /// A valid shader style, which can be one of these values:
|
|
|
|
|
- /// <see cref="SHADERUNLIT"/>,
|
|
|
|
|
- /// <see cref="SHADERPBRMETALLICROUGHNESS"/>,
|
|
|
|
|
- /// <see cref="SHADERPBRSPECULARGLOSSINESS"/>
|
|
|
|
|
- /// </param>
|
|
|
|
|
- /// <returns>This <see cref="MaterialBuilder"/>.</returns>
|
|
|
|
|
- public MaterialBuilder WithShader(string shader)
|
|
|
|
|
|
|
+ // remove incompatible channels.
|
|
|
|
|
+ for (int i = _Channels.Count - 1; i >= 0; --i)
|
|
|
|
|
+ {
|
|
|
|
|
+ var c = _Channels[i];
|
|
|
|
|
+ if (!validChannels.Contains(c.Key)) _Channels.RemoveAt(i);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private IReadOnlyList<KnownChannel> _GetValidChannels()
|
|
|
{
|
|
{
|
|
|
- Guard.NotNullOrEmpty(shader, nameof(shader));
|
|
|
|
|
- Guard.IsTrue(shader == SHADERUNLIT || shader == SHADERPBRMETALLICROUGHNESS || shader == SHADERPBRSPECULARGLOSSINESS, nameof(shader));
|
|
|
|
|
- ShaderStyle = shader;
|
|
|
|
|
- return this;
|
|
|
|
|
|
|
+ switch (ShaderStyle)
|
|
|
|
|
+ {
|
|
|
|
|
+ case SHADERUNLIT: return _UnlitChannels;
|
|
|
|
|
+ case SHADERPBRMETALLICROUGHNESS: return _MetRouChannels;
|
|
|
|
|
+ case SHADERPBRSPECULARGLOSSINESS: return _SpeGloChannels;
|
|
|
|
|
+ default: throw new NotImplementedException();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public ChannelBuilder GetChannel(KnownChannel channelKey)
|
|
public ChannelBuilder GetChannel(KnownChannel channelKey)
|
|
|
{
|
|
{
|
|
|
- return GetChannel(channelKey.ToString());
|
|
|
|
|
|
|
+ return _Channels.FirstOrDefault(item => item.Key == channelKey);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public ChannelBuilder UseChannel(KnownChannel channelKey)
|
|
|
|
|
+ {
|
|
|
|
|
+ Guard.IsTrue(_GetValidChannels().Contains(channelKey), nameof(channelKey));
|
|
|
|
|
+
|
|
|
|
|
+ var ch = GetChannel(channelKey);
|
|
|
|
|
+ if (ch != null) return ch;
|
|
|
|
|
+
|
|
|
|
|
+ ch = new ChannelBuilder(this, channelKey);
|
|
|
|
|
+ _Channels.Add(ch);
|
|
|
|
|
+
|
|
|
|
|
+ return ch;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public ChannelBuilder GetChannel(string channelKey)
|
|
public ChannelBuilder GetChannel(string channelKey)
|
|
|
{
|
|
{
|
|
|
Guard.NotNullOrEmpty(channelKey, nameof(channelKey));
|
|
Guard.NotNullOrEmpty(channelKey, nameof(channelKey));
|
|
|
|
|
+ var key = (KnownChannel)Enum.Parse(typeof(KnownChannel), channelKey, true);
|
|
|
|
|
+
|
|
|
|
|
+ return GetChannel(key);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- channelKey = channelKey.ToLowerInvariant();
|
|
|
|
|
|
|
+ public ChannelBuilder UseChannel(string channelKey)
|
|
|
|
|
+ {
|
|
|
|
|
+ Guard.NotNullOrEmpty(channelKey, nameof(channelKey));
|
|
|
|
|
+ var key = (KnownChannel)Enum.Parse(typeof(KnownChannel), channelKey, true);
|
|
|
|
|
|
|
|
- return _Channels.FirstOrDefault(item => string.Equals(channelKey, item.Key, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
|
+ return UseChannel(key);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public ChannelBuilder UseChannel(KnownChannel channelKey)
|
|
|
|
|
|
|
+ public void RemoveChannel(KnownChannel key)
|
|
|
{
|
|
{
|
|
|
- return UseChannel(channelKey.ToString());
|
|
|
|
|
|
|
+ var idx = _Channels.IndexOf(item => item.Key == key);
|
|
|
|
|
+ if (idx < 0) return;
|
|
|
|
|
+ _Channels.RemoveAt(idx);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public ChannelBuilder UseChannel(string channelKey)
|
|
|
|
|
|
|
+ internal void ValidateForSchema2()
|
|
|
{
|
|
{
|
|
|
- var ch = GetChannel(channelKey);
|
|
|
|
|
- if (ch != null) return ch;
|
|
|
|
|
|
|
+ var hasClearCoat = this.GetChannel("ClearCoat") != null
|
|
|
|
|
+ || this.GetChannel("ClearCoatRoughness") != null
|
|
|
|
|
+ || this.GetChannel("ClearCoatNormal") != null;
|
|
|
|
|
|
|
|
- ch = new ChannelBuilder(this, channelKey);
|
|
|
|
|
- _Channels.Add(ch);
|
|
|
|
|
|
|
+ if (this.ShaderStyle == SHADERPBRSPECULARGLOSSINESS)
|
|
|
|
|
+ {
|
|
|
|
|
+ Guard.IsFalse(hasClearCoat, KnownChannel.ClearCoat.ToString(), "Clear Coat not supported for Specular Glossiness materials.");
|
|
|
|
|
|
|
|
- return ch;
|
|
|
|
|
|
|
+ if (this.CompatibilityFallback != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ Guard.MustBeNull(this.CompatibilityFallback.CompatibilityFallback, nameof(CompatibilityFallback));
|
|
|
|
|
+
|
|
|
|
|
+ Guard.MustBeEqualTo(this.Name, this.CompatibilityFallback.Name, nameof(Name));
|
|
|
|
|
+
|
|
|
|
|
+ Guard.IsTrue(this.CompatibilityFallback.ShaderStyle == SHADERPBRMETALLICROUGHNESS, nameof(ShaderStyle));
|
|
|
|
|
+
|
|
|
|
|
+ Guard.IsTrue(this.AlphaMode == this.CompatibilityFallback.AlphaMode, nameof(AlphaMode));
|
|
|
|
|
+ Guard.MustBeEqualTo(this.AlphaCutoff, this.CompatibilityFallback.AlphaCutoff, nameof(AlphaCutoff));
|
|
|
|
|
+ Guard.MustBeEqualTo(this.DoubleSided, this.CompatibilityFallback.DoubleSided, nameof(DoubleSided));
|
|
|
|
|
+
|
|
|
|
|
+ foreach (var chKey in new[] { KnownChannel.Normal, KnownChannel.Occlusion, KnownChannel.Emissive })
|
|
|
|
|
+ {
|
|
|
|
|
+ var primary = this.GetChannel(chKey);
|
|
|
|
|
+ var fallbck = this.CompatibilityFallback.GetChannel(chKey);
|
|
|
|
|
+
|
|
|
|
|
+ Guard.IsTrue(ChannelBuilder.AreEqualByContent(primary, fallbck), chKey.ToString(), "Primary and fallback materials must have the same channel properties");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ Guard.MustBeNull(this.CompatibilityFallback, nameof(CompatibilityFallback));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region API * With
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Sets <see cref="MaterialBuilder.ShaderStyle"/>.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="shader">
|
|
|
|
|
+ /// A valid shader style, which can be one of these values:
|
|
|
|
|
+ /// <see cref="SHADERUNLIT"/>,
|
|
|
|
|
+ /// <see cref="SHADERPBRMETALLICROUGHNESS"/>,
|
|
|
|
|
+ /// <see cref="SHADERPBRSPECULARGLOSSINESS"/>
|
|
|
|
|
+ /// </param>
|
|
|
|
|
+ /// <returns>This <see cref="MaterialBuilder"/>.</returns>
|
|
|
|
|
+ public MaterialBuilder WithShader(string shader) { _SetShader(shader); return this; }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Sets <see cref="MaterialBuilder.ShaderStyle"/> to use <see cref="SHADERUNLIT"/>.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <returns>This <see cref="MaterialBuilder"/>.</returns>
|
|
|
|
|
+ public MaterialBuilder WithUnlitShader() { _SetShader(SHADERUNLIT); return this; }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Sets <see cref="MaterialBuilder.ShaderStyle"/> to use <see cref="SHADERPBRMETALLICROUGHNESS"/>.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <returns>This <see cref="MaterialBuilder"/>.</returns>
|
|
|
|
|
+ public MaterialBuilder WithMetallicRoughnessShader() { _SetShader(SHADERPBRMETALLICROUGHNESS); return this; }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Sets <see cref="MaterialBuilder.ShaderStyle"/> to use <see cref="SHADERPBRSPECULARGLOSSINESS"/>.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <returns>This <see cref="MaterialBuilder"/>.</returns>
|
|
|
|
|
+ public MaterialBuilder WithSpecularGlossinessShader() { _SetShader(SHADERPBRSPECULARGLOSSINESS); return this; }
|
|
|
|
|
+
|
|
|
public MaterialBuilder WithAlpha(AlphaMode alphaMode = AlphaMode.OPAQUE, Single alphaCutoff = 0.5f)
|
|
public MaterialBuilder WithAlpha(AlphaMode alphaMode = AlphaMode.OPAQUE, Single alphaCutoff = 0.5f)
|
|
|
{
|
|
{
|
|
|
this.AlphaMode = alphaMode;
|
|
this.AlphaMode = alphaMode;
|
|
@@ -243,27 +359,32 @@ namespace SharpGLTF.Materials
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public MaterialBuilder WithChannelParam(string channelKey, Vector4 parameter)
|
|
|
|
|
|
|
+ public MaterialBuilder WithChannelImage(KnownChannel channelKey, IMAGEFILE primaryImage)
|
|
|
{
|
|
{
|
|
|
- this.UseChannel(channelKey).Parameter = parameter;
|
|
|
|
|
|
|
+ if (primaryImage.IsEmpty)
|
|
|
|
|
+ {
|
|
|
|
|
+ this.GetChannel(channelKey)?.RemoveTexture();
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.UseChannel(channelKey)
|
|
|
|
|
+ .UseTexture()
|
|
|
|
|
+ .WithPrimaryImage(primaryImage);
|
|
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public MaterialBuilder WithChannelImage(KnownChannel channelKey, string primaryImagePath)
|
|
|
|
|
|
|
+ public MaterialBuilder WithChannelParam(string channelKey, Vector4 parameter)
|
|
|
{
|
|
{
|
|
|
- this.UseChannel(channelKey)
|
|
|
|
|
- .UseTexture()
|
|
|
|
|
- .WithPrimaryImage(primaryImagePath);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ this.UseChannel(channelKey).Parameter = parameter;
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public MaterialBuilder WithChannelImage(string channelKey, string primaryImagePath)
|
|
|
|
|
|
|
+ public MaterialBuilder WithChannelImage(string channelKey, IMAGEFILE primaryImage)
|
|
|
{
|
|
{
|
|
|
this.UseChannel(channelKey)
|
|
this.UseChannel(channelKey)
|
|
|
.UseTexture()
|
|
.UseTexture()
|
|
|
- .WithPrimaryImage(primaryImagePath);
|
|
|
|
|
|
|
+ .WithPrimaryImage(primaryImage);
|
|
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
@@ -283,50 +404,129 @@ namespace SharpGLTF.Materials
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- internal void ValidateForSchema2()
|
|
|
|
|
|
|
+ public MaterialBuilder WithMetallicRoughnessFallback(IMAGEFILE baseColor, Vector4? rgba, IMAGEFILE metallicRoughness, float? metallic, float? roughness)
|
|
|
{
|
|
{
|
|
|
- if (this.ShaderStyle == SHADERPBRSPECULARGLOSSINESS)
|
|
|
|
|
- {
|
|
|
|
|
- if (this.CompatibilityFallback != null)
|
|
|
|
|
- {
|
|
|
|
|
- Guard.MustBeNull(this.CompatibilityFallback.CompatibilityFallback, nameof(this.CompatibilityFallback.CompatibilityFallback));
|
|
|
|
|
|
|
+ var fallback = this
|
|
|
|
|
+ .Clone()
|
|
|
|
|
+ .WithMetallicRoughnessShader()
|
|
|
|
|
+ .WithBaseColor(baseColor, rgba)
|
|
|
|
|
+ .WithMetallicRoughness(metallicRoughness, metallic, roughness);
|
|
|
|
|
|
|
|
- Guard.IsTrue(this.CompatibilityFallback.ShaderStyle == SHADERPBRMETALLICROUGHNESS, nameof(CompatibilityFallback.ShaderStyle));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- Guard.MustBeNull(this.CompatibilityFallback, nameof(CompatibilityFallback));
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return this.WithFallback(fallback);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region API * With for specific channels
|
|
|
|
|
+
|
|
|
|
|
+ public MaterialBuilder WithNormal(IMAGEFILE imageFile, float scale = 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ WithChannelImage(KnownChannel.Normal, imageFile);
|
|
|
|
|
+ WithChannelParam(KnownChannel.Normal, new Vector4(scale, 0, 0, 0));
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public MaterialBuilder WithOcclusion(IMAGEFILE imageFile, float strength = 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ WithChannelImage(KnownChannel.Occlusion, imageFile);
|
|
|
|
|
+ WithChannelParam(KnownChannel.Occlusion, new Vector4(strength, 0, 0, 0));
|
|
|
|
|
+ return this;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public MaterialBuilder WithNormal(string imageFilePath, float scale = 1)
|
|
|
|
|
|
|
+ public MaterialBuilder WithEmissive(Vector3 rgb) { return WithChannelParam(KnownChannel.Emissive, new Vector4(rgb, 1)); }
|
|
|
|
|
+
|
|
|
|
|
+ public MaterialBuilder WithEmissive(IMAGEFILE imageFile, Vector3? rgb = null)
|
|
|
{
|
|
{
|
|
|
- WithChannelImage("Normal", imageFilePath);
|
|
|
|
|
- WithChannelParam("Normal", new Vector4(scale, 0, 0, 0));
|
|
|
|
|
|
|
+ WithChannelImage(KnownChannel.Emissive, imageFile);
|
|
|
|
|
+ if (rgb.HasValue) WithEmissive(rgb.Value);
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ public MaterialBuilder WithBaseColor(Vector4 rgba) { return WithChannelParam(KnownChannel.BaseColor, rgba); }
|
|
|
|
|
+
|
|
|
|
|
+ public MaterialBuilder WithBaseColor(IMAGEFILE imageFile, Vector4? rgba = null)
|
|
|
|
|
+ {
|
|
|
|
|
+ WithChannelImage(KnownChannel.BaseColor, imageFile);
|
|
|
|
|
+ if (rgba.HasValue) WithBaseColor(rgba.Value);
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public MaterialBuilder WithOcclusion(string imageFilePath, float strength = 1)
|
|
|
|
|
|
|
+ public MaterialBuilder WithMetallicRoughness(float? metallic = null, float? roughness = null)
|
|
|
{
|
|
{
|
|
|
- WithChannelImage("Occlusion", imageFilePath);
|
|
|
|
|
- WithChannelParam("Occlusion", new Vector4(strength, 0, 0, 0));
|
|
|
|
|
|
|
+ if (!metallic.HasValue && !roughness.HasValue) return this;
|
|
|
|
|
+
|
|
|
|
|
+ var channel = UseChannel(KnownChannel.MetallicRoughness);
|
|
|
|
|
+ var val = channel.Parameter;
|
|
|
|
|
+ if (metallic.HasValue) val.X = metallic.Value;
|
|
|
|
|
+ if (roughness.HasValue) val.Y = roughness.Value;
|
|
|
|
|
+ channel.Parameter = val;
|
|
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public MaterialBuilder WithEmissive(string imageFilePath, Vector3 emissiveFactor)
|
|
|
|
|
|
|
+ public MaterialBuilder WithMetallicRoughness(IMAGEFILE imageFile, float? metallic = null, float? roughness = null)
|
|
|
{
|
|
{
|
|
|
- WithChannelImage("Emissive", imageFilePath);
|
|
|
|
|
- WithChannelParam("Emissive", new Vector4(emissiveFactor, 0));
|
|
|
|
|
|
|
+ WithChannelImage(KnownChannel.MetallicRoughness, imageFile);
|
|
|
|
|
+ WithMetallicRoughness(metallic, roughness);
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public MaterialBuilder WithDiffuse(Vector4 rgba) { return WithChannelParam(KnownChannel.Diffuse, rgba); }
|
|
|
|
|
|
|
|
|
|
+ public MaterialBuilder WithDiffuse(IMAGEFILE imageFile, Vector4? rgba = null)
|
|
|
|
|
+ {
|
|
|
|
|
+ WithChannelImage(KnownChannel.Diffuse, imageFile);
|
|
|
|
|
+ if (rgba.HasValue) WithDiffuse(rgba.Value);
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public MaterialBuilder WithEmissive(string imageFilePath) { return WithChannelImage("Emissive", imageFilePath); }
|
|
|
|
|
|
|
+ public MaterialBuilder WithSpecularGlossiness(Vector3? specular = null, float? glossiness = null)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!specular.HasValue && !glossiness.HasValue) return this;
|
|
|
|
|
+
|
|
|
|
|
+ var channel = UseChannel(KnownChannel.SpecularGlossiness);
|
|
|
|
|
|
|
|
- public MaterialBuilder WithEmissive(Vector3 emissiveFactor) { return WithChannelParam("Emissive", new Vector4(emissiveFactor, 0)); }
|
|
|
|
|
|
|
+ var val = channel.Parameter;
|
|
|
|
|
+ if (specular.HasValue)
|
|
|
|
|
+ {
|
|
|
|
|
+ val.X = specular.Value.X;
|
|
|
|
|
+ val.Y = specular.Value.Y;
|
|
|
|
|
+ val.Z = specular.Value.Z;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (glossiness.HasValue) val.W = glossiness.Value;
|
|
|
|
|
+
|
|
|
|
|
+ channel.Parameter = val;
|
|
|
|
|
+
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public MaterialBuilder WithSpecularGlossiness(IMAGEFILE imageFile, Vector3? specular = null, float? glossiness = null)
|
|
|
|
|
+ {
|
|
|
|
|
+ WithChannelImage(KnownChannel.SpecularGlossiness, imageFile);
|
|
|
|
|
+ WithSpecularGlossiness(specular, glossiness);
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public MaterialBuilder WithClearCoatNormal(IMAGEFILE imageFile)
|
|
|
|
|
+ {
|
|
|
|
|
+ WithChannelImage(KnownChannel.ClearCoatNormal, imageFile);
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public MaterialBuilder WithClearCoat(IMAGEFILE imageFile, float intensity)
|
|
|
|
|
+ {
|
|
|
|
|
+ WithChannelImage(KnownChannel.ClearCoat, imageFile);
|
|
|
|
|
+ WithChannelParam(KnownChannel.ClearCoat, new Vector4(intensity, 0, 0, 0));
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public MaterialBuilder WithClearCoatRoughness(IMAGEFILE imageFile, float roughness)
|
|
|
|
|
+ {
|
|
|
|
|
+ WithChannelImage(KnownChannel.ClearCoatRoughness, imageFile);
|
|
|
|
|
+ WithChannelParam(KnownChannel.ClearCoatRoughness, new Vector4(roughness, 0, 0, 0));
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
#endregion
|
|
#endregion
|
|
|
|
|
|
|
@@ -338,12 +538,12 @@ namespace SharpGLTF.Materials
|
|
|
|
|
|
|
|
public bool Equals(MaterialBuilder x, MaterialBuilder y)
|
|
public bool Equals(MaterialBuilder x, MaterialBuilder y)
|
|
|
{
|
|
{
|
|
|
- return MaterialBuilder.AreEqualByContent(x, y);
|
|
|
|
|
|
|
+ return AreEqualByContent(x, y);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public int GetHashCode(MaterialBuilder obj)
|
|
public int GetHashCode(MaterialBuilder obj)
|
|
|
{
|
|
{
|
|
|
- return MaterialBuilder.GetContentHashCode(obj);
|
|
|
|
|
|
|
+ return GetContentHashCode(obj);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|