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.

306 lines
8.6 KiB
C#

/******************************************************************************/
/*
Project - MudBun
Publisher - Long Bunny Labs
http://LongBunnyLabs.com
Author - Ming-Lun "Allen" Chou
http://AllenChou.net
*/
/******************************************************************************/
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using Unity.Collections;
using UnityEngine;
namespace MudBun
{
/// <summary>
/// The base class for all brushes. Users should extend one of the MudSolid, MudDistortion, or MudModifier classes to create custom brushes.
/// </summary>
[ExecuteInEditMode]
public abstract class MudBrushBase : MonoBehaviour
{
internal MudRendererBase m_renderer;
internal int m_iSdfBrush;
internal bool m_dirty = true;
#if UNITY_EDITOR
[HideInInspector] public bool Hidden = false;
#else
public bool Hidden => false;
#endif
public MudRendererBase Renderer => m_renderer;
public void MarkDirty() { m_dirty = true; }
/// <summary>
/// The actual bounds in renderer space used for spatial optimization. Normally this would be the same as BoundsRs, but for special situations like solid brushes with symmetry turned on, this would be a modified version of BoundsRs.
/// </summary>
public virtual Aabb BoundsRs
{
get
{
Aabb bounds = RawBoundsRs;
Transform t = transform.parent;
while (t != null)
{
MudBrushGroup group = t.GetComponent<MudBrushGroup>();
if (group != null)
{
bounds.Expand(group.Blend);
}
t = t.parent;
}
return bounds;
}
}
/// <summary>
/// The raw AABB bounds in renderer space. This should be always encompass the brush's maximum range of effect. Otherwise, parts of the brush's effect could be missing due to the renderer's spatial optimization.
/// </summary>
public virtual Aabb RawBoundsRs => Aabb.Empty;
public virtual float BoundsRsPadding => 0.0f;
public virtual bool IsSuccessorModifier => false;
public virtual bool ShouldUseAccumulatedBounds => false;
internal bool m_preChildrenFlag = false;
public virtual bool IsBrushGroup => false;
internal virtual bool UsesMaterial => false;
internal virtual int MaterialHash => 0;
internal int m_iProxy = AabbTree<MudBrushBase>.Null;
public virtual void UpdateProxies(AabbTree<MudBrushBase> tree, Aabb opBounds)
{
if (m_iProxy == AabbTree<MudBrushBase>.Null)
m_iProxy = tree.CreateProxy(opBounds, this);
tree.UpdateProxy(m_iProxy, opBounds, m_iSdfBrush);
}
public virtual void DestroyProxies(AabbTree<MudBrushBase> tree)
{
tree.DestroyProxy(m_iProxy);
m_iProxy = AabbTree<MudBrushBase>.Null;
}
public Aabb BoundaryShapeBounds(SdfBrush.BoundaryShapeEnum boundaryShape, float radius)
{
Vector3 size = transform.localScale;
Aabb bounds = Aabb.Empty;
switch (boundaryShape)
{
case SdfBrush.BoundaryShapeEnum.Box:
{
Vector3 r = 0.5f * size;
bounds = new Aabb(-r, r);
bounds.Rotate(RotationRs(transform.rotation));
break;
}
case SdfBrush.BoundaryShapeEnum.Sphere:
{
Vector3 r = radius * size;
bounds = new Aabb(-r, r);
bounds.Rotate(RotationRs(transform.rotation));
break;
}
case SdfBrush.BoundaryShapeEnum.Cylinder:
{
Vector3 r = new Vector3(radius + Mathf.Max(0.0f, size.x - 1.0f), 0.5f * size.y, radius + Mathf.Max(0.0f, size.z - 1.0f));
bounds = new Aabb(-r, r);
bounds.Rotate(RotationRs(transform.rotation));
break;
}
case SdfBrush.BoundaryShapeEnum.Torus:
{
Vector3 r = new Vector3(0.5f * size.x, 0.0f, 0.5f * size.z);
bounds = new Aabb(-r, r);
bounds.Rotate(RotationRs(transform.rotation));
Vector3 round = radius * Vector3.one;
bounds.Min -= round;
bounds.Max += round;
break;
}
case SdfBrush.BoundaryShapeEnum.SolidAngle:
{
Vector3 r = radius * VectorUtil.Abs(transform.localScale);
bounds = new Aabb(-r, r);
break;
}
}
return bounds;
}
protected virtual void ScanRenderer() { }
public virtual void OnEnable()
{
m_renderer = null;
m_iProxy = AabbTree<MudBrushBase>.Null;
m_iSdfBrush = -1;
MarkDirty();
ScanRenderer();
}
public virtual void OnDisable()
{
if (m_renderer != null)
m_renderer.OnBrushDisabled(this);
#if UNITY_EDITOR
SelectionManager.NotifyBrushDisabled(this);
#endif
}
private void OnValidate()
{
SanitizeParameters();
MarkDirty();
}
public virtual void SanitizeParameters() { }
// Ws: world space
// Rs: renderer space
// Bs: brush space
public Vector3 PointRs(Vector3 posWs)
{
return
m_renderer != null
? m_renderer.transform.InverseTransformPoint(posWs)
: posWs;
}
public Vector3 VectorRs(Vector3 vecWs)
{
return
m_renderer != null
? m_renderer.transform.InverseTransformVector(vecWs)
: vecWs;
}
public Quaternion RotationRs(Quaternion rotWs)
{
return
m_renderer != null
? Quaternion.Inverse(m_renderer.transform.rotation) * rotWs
: rotWs;
}
public Vector3 PointBs(Vector3 posWs) => transform.InverseTransformPoint(posWs);
public Vector3 VectorBs(Vector3 vecWs) => transform.InverseTransformVector(vecWs);
public Quaternion RotationBs(Quaternion rotWs) => Quaternion.Inverse(transform.rotation) * rotWs;
public virtual bool CountAsBone => false;
public virtual int FillComputeData(NativeArray<SdfBrush> aBrush, int iStart, List<Transform> aBone) { return 0; }
public virtual void FillBrushData(ref SdfBrush brush, int iBrush)
{
brush.Proxy = m_iProxy;
brush.Position = PointRs(transform.position);
brush.Rotation = RotationRs(transform.rotation);
brush.Size = transform.localScale;
brush.Flags.AssignBit((int) SdfBrush.FlagBit.Hidden, Hidden);
}
public virtual int FillComputeDataPostChildren(NativeArray<SdfBrush> aBrush, int iStart) { return 0; }
public virtual void FillBrushDataPostChildren(ref SdfBrush brush, int iBrush) { }
public virtual void FillBrushMaterialData(ref SdfBrushMaterial mat) { }
public virtual void ValidateMaterial() { }
private struct Locator
{
public Vector3 Position;
public Quaternion Rotation;
}
public IList<Transform> GetFlipXTransforms()
{
var aTransform = new List<Transform>();
CollectChildrenRecursive(transform, aTransform);
return aTransform;
}
public void FlipX()
{
var aTransform = new List<Transform>();
CollectChildrenRecursive(transform, aTransform);
var aMirroredLocator = new List<Locator>(aTransform.Count);
for (int i = 0; i < aTransform.Count; ++i)
{
var t = aTransform[i];
Locator loc = new Locator() { Position = t.position, Rotation = t.rotation };
loc.Position.x = -loc.Position.x;
loc.Rotation.y = -loc.Rotation.y;
loc.Rotation.z = -loc.Rotation.z;
aMirroredLocator.Add(loc);
}
for (int i = 0; i < aTransform.Count; ++i)
{
var t = aTransform[i];
var loc = aMirroredLocator[i];
t.position = loc.Position;
t.rotation = loc.Rotation;
}
}
private void CollectChildrenRecursive(Transform t, List<Transform> aTransform)
{
aTransform.Add(t);
for (int i = 0; i < t.childCount; ++i)
CollectChildrenRecursive(t.GetChild(i), aTransform);
}
public float GetFloatHash() => Mathf.Abs(Codec.Hash(GetHashCode()) % 0xFFFF) / ((float) 0xFFFF);
internal virtual bool IsSelected()
{
#if UNITY_EDITOR
bool selected = Selection.Contains(gameObject);
#else
bool selected = false;
#endif
return selected;
}
protected virtual void OnDrawGizmos()
{
if (Renderer == null)
return;
if (Renderer.ClickSelection != MudRendererBase.ClickSelectionEnum.Gizmos)
return;
Gizmos.matrix = Renderer.transform.localToWorldMatrix;
DrawSelectionGizmosRs();
Gizmos.matrix = Matrix4x4.identity;
}
public virtual void DrawGizmosRs() { }
public virtual void DrawSelectionGizmosRs() { }
public virtual void DrawOutlineGizmosRs() { }
}
}