You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
8.6 KiB
C#

/******************************************************************************/
/*
Project - MudBun
Publisher - Long Bunny Labs
http://LongBunnyLabs.com
Author - Ming-Lun "Allen" Chou
http://AllenChou.net
*/
/******************************************************************************/
using UnityEngine;
using UnityEngine.Rendering;
namespace MudBun
{
[ExecuteInEditMode]
[RequireComponent(typeof(MudMaterial))]
public class MudSolid : MudBrush
{
public enum SymmetryMode
{
None,
FlipX,
MirrorX,
FlipMirrorX,
}
public static Aabb SymmetryBounds(SymmetryMode mode, Aabb bounds)
{
switch (mode)
{
case SymmetryMode.FlipX:
{
Vector3 center = bounds.Center;
Vector3 extent = bounds.Extent;
center.x = -center.x;
bounds = new Aabb(center - extent, center + extent);
break;
}
case SymmetryMode.MirrorX:
{
Vector3 newMin = bounds.Min;
newMin.x = -Mathf.Max(0.0f, bounds.Max.x);
bounds = new Aabb(newMin, bounds.Max);
break;
}
case SymmetryMode.FlipMirrorX:
{
Vector3 center = bounds.Center;
Vector3 extent = bounds.Extent;
center.x = -center.x;
bounds = new Aabb(center - extent, center + extent);
Vector3 newMin = bounds.Min;
newMin.x = -Mathf.Max(0.0f, bounds.Max.x);
bounds = new Aabb(newMin, bounds.Max);
break;
}
}
if (bounds.IsEmpty)
return Aabb.Empty;
return bounds;
}
public override Aabb BoundsRs => SymmetryBounds(m_symmetry, base.BoundsRs);
[SerializeField] private SdfBrush.OperatorEnum m_operator = SdfBrush.OperatorEnum.Union;
public SdfBrush.OperatorEnum Operator { get => m_operator; set { m_operator = value; MarkDirty(); } }
[SerializeField] [ConditionalField("m_operator", SdfBrush.OperatorEnum.Union, SdfBrush.OperatorEnum.Subtract, SdfBrush.OperatorEnum.Intersect, Label = " Type")]
private SdfBrush.BooleanOperatorTypeEnum m_booleanOperatorType = SdfBrush.BooleanOperatorTypeEnum.Cubic;
public SdfBrush.BooleanOperatorTypeEnum BooleanOperatorType { get => m_booleanOperatorType; set { m_booleanOperatorType = value; MarkDirty(); } }
[SerializeField] [ConditionalField("m_operator", SdfBrush.OperatorEnum.Dye, Label = " Blend Mode")]
private SdfBrush.DyeBlendModeEnum m_dyeBlendMode = SdfBrush.DyeBlendModeEnum.Overwrite;
public SdfBrush.DyeBlendModeEnum DyeBlendMode { get => m_dyeBlendMode; set { m_dyeBlendMode = value; MarkDirty(); } }
[SerializeField] private float m_blend;
public float Blend { get => m_blend; set { m_blend = value; MarkDirty(); } }
// TODO: not ready for auto-rigging yet
[SerializeField] private SymmetryMode m_symmetry = SymmetryMode.None;
public SymmetryMode Symmetry { get => m_symmetry; set { m_symmetry = value; MarkDirty(); } }
[Tooltip("If checked, this brush will be counted as bone during auto rigging.")]
[SerializeField] [ConditionalField("m_canCountAsBone", true)] public bool m_countAsBone = true;
[SerializeField] [HideInInspector] protected bool m_canCountAsBone = true;
public override bool CountAsBone => m_countAsBone || (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal); // Metal is weird
//[ConditionalField("m_mirrorX", true, Label = " Create Mirrored Bone")] public bool CreateMirroredBone = true;
public override float BoundsRsPadding => m_blend;
public override bool ShouldUseAccumulatedBounds
{
get
{
switch (m_operator)
{
case SdfBrush.OperatorEnum.Intersect:
case SdfBrush.OperatorEnum.Pipe:
case SdfBrush.OperatorEnum.CullOutside:
return true;
}
return false;
}
}
internal override bool UsesMaterial => true;
internal override int MaterialHash => GetComponent<MudMaterial>().MaterialHash;
internal MudMaterial m_material;
public override void SanitizeParameters()
{
base.SanitizeParameters();
Validate.NonNegative(ref m_blend);
}
public override void FillBrushData(ref SdfBrush brush, int iBrush)
{
base.FillBrushData(ref brush, iBrush);
brush.Operator = SdfBrush.GetShaderOperatorIntValue(m_operator, m_booleanOperatorType, m_dyeBlendMode);
brush.Blend = Blend;
if (!m_material)
m_material = GetComponent<MudMaterial>();
#if MUDBUN_DEV
Assert.True(m_material != null, "Mussing brush material. A solid brush must have a MudMaterial component.");
#endif
brush.Flags.AssignBit((int) SdfBrush.FlagBit.ContributeMaterial, m_material.ContributeMaterial);
switch (m_symmetry)
{
case SymmetryMode.FlipX:
brush.Flags.SetBit((int) SdfBrush.FlagBit.FlipX);
break;
case SymmetryMode.MirrorX:
brush.Flags.SetBit((int) SdfBrush.FlagBit.MirrorX);
break;
case SymmetryMode.FlipMirrorX:
brush.Flags.SetBit((int) SdfBrush.FlagBit.FlipX);
brush.Flags.SetBit((int) SdfBrush.FlagBit.MirrorX);
break;
}
brush.Flags.AssignBit((int) SdfBrush.FlagBit.CountAsBone, CountAsBone);
//brush.Flags.AssignBit((int) SdfBrush.FlagBit.CreateMirroredBone, CreateMirroredBone);
}
public override void FillBrushMaterialData(ref SdfBrushMaterial mat)
{
base.FillBrushMaterialData(ref mat);
if (!m_material)
m_material = GetComponent<MudMaterial>();
#if MUDBUN_DEV
Assert.True(m_material != null, "Missing brush material. A solid brush must have a MudMaterial component.");
#endif
mat.Color = m_material.Color;
mat.EmissionHash = m_material.Emission;
mat.MetallicSmoothnessSizeTightness.Set(m_material.Metallic, m_material.Smoothness, m_material.SplatSize, m_material.BlendTightness);
mat.TextureWeight.Set
(
(m_material.TextureIndex == 0 ? 1.0f : 0.0f),
(m_material.TextureIndex == 1 ? 1.0f : 0.0f),
(m_material.TextureIndex == 2 ? 1.0f : 0.0f),
(m_material.TextureIndex == 3 ? 1.0f : 0.0f)
);
}
public override void ValidateMaterial()
{
var material = GetComponent<MudMaterial>();
if (material != null)
return;
material = gameObject.AddComponent<MudMaterial>();
}
private bool OperatorShouldDrawOutline(SdfBrush.OperatorEnum op)
{
if (Renderer != null
&& Renderer.ClickSelection != MudRendererBase.ClickSelectionEnum.Gizmos)
return false;
switch (op)
{
case SdfBrush.OperatorEnum.Union:
{
if (Renderer == null)
break;
if (Renderer.RenderModeCategory != MudRendererBase.RenderModeCategoryEnum.Splats)
break;
var material = GetComponent<MudMaterial>();
if (material == null)
break;
return
Renderer.SplatSize * material.SplatSize < 0.1f
|| material.Color.a < 0.25f;
}
case SdfBrush.OperatorEnum.Subtract:
case SdfBrush.OperatorEnum.Intersect:
case SdfBrush.OperatorEnum.Pipe:
case SdfBrush.OperatorEnum.Engrave:
case SdfBrush.OperatorEnum.CullInside:
case SdfBrush.OperatorEnum.CullOutside:
case SdfBrush.OperatorEnum.NoOp:
return true;
default:
if (SdfBrush.IsDyeOperator(op))
return true;
break;
}
return false;
}
public override void DrawGizmosRs()
{
base.DrawGizmosRs();
bool selected = IsSelected();
bool shouldDrawOutlines =
selected
|| (Renderer != null
&& (Renderer.Enable2dMode
|| Renderer.AlwaysDrawGizmos));
if (!shouldDrawOutlines)
{
shouldDrawOutlines = OperatorShouldDrawOutline(m_operator);
}
if (!shouldDrawOutlines)
{
var parent = transform.parent;
while (parent != null)
{
var groupComp = parent.GetComponent<MudBrushGroup>();
if (groupComp != null)
{
shouldDrawOutlines = OperatorShouldDrawOutline(groupComp.Operator);
break;
}
if (parent == m_renderer)
break;
parent = parent.parent;
}
}
if (shouldDrawOutlines)
{
Color prevColor = Gizmos.color;
if (selected)
Gizmos.color = GizmosUtil.OutlineSelected;
DrawOutlineGizmosRs();
Gizmos.color = prevColor;
}
}
}
}