288 lines
10 KiB
C#
288 lines
10 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace SDFr
|
|
{
|
|
public abstract class AVolumeBaker<T,D> : MonoBehaviour
|
|
where T : AVolume<T>, new()
|
|
where D : AVolumeData
|
|
{
|
|
[SerializeField]
|
|
protected Bounds bounds;
|
|
[SerializeField]
|
|
protected Vector3Int dimensions = new Vector3Int(32,32,32);
|
|
[SerializeField,Tooltip("Encapsulate Bounds using Vertices (exact)")]
|
|
protected bool fitToVertices = true;
|
|
[SerializeField]
|
|
protected List<Renderer> bakedRenderers;
|
|
[SerializeField,Tooltip("Voxel size in Meters")]
|
|
float targetVoxelSize;
|
|
[SerializeField,Tooltip("Use a specified voxel size instead of dimensions")]
|
|
protected bool useTargetVoxelSize;
|
|
[SerializeField,Tooltip("Use manually defined bounds, do not encapsulate")]
|
|
protected bool useManualBounds;
|
|
|
|
public static bool showAllPreviews = false;
|
|
|
|
public abstract int MaxDimension { get; }
|
|
|
|
#if UNITY_EDITOR
|
|
public abstract AVolumePreview<D> CreatePreview();
|
|
|
|
|
|
public void TogglePreview()
|
|
{
|
|
if ( !RenderersEnabled ) EnableRenderers( true );
|
|
|
|
if (_aPreview == null)
|
|
{
|
|
_aPreview = CreatePreview();
|
|
EnableRenderers( false );
|
|
}
|
|
else
|
|
{
|
|
_aPreview?.Dispose();
|
|
_aPreview = null;
|
|
|
|
EnableRenderers( true );
|
|
}
|
|
}
|
|
|
|
protected void EnableRenderers( bool visible )
|
|
{
|
|
if (bakedRenderers == null || bakedRenderers.Count == 0) return;
|
|
RenderersEnabled = visible;
|
|
foreach (var r in bakedRenderers)
|
|
{
|
|
if (r == null) continue;
|
|
r.enabled = RenderersEnabled;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// changes bound size automatically
|
|
/// </summary>
|
|
public virtual void Encapsulate()
|
|
{
|
|
List<Renderer> tempRenderers = new List<Renderer>();
|
|
AVolumeSettings settings = new AVolumeSettings(bounds, dimensions);
|
|
GetChildRenderersAndEncapsulate(ref settings, ref tempRenderers, transform);
|
|
bounds = settings.BoundsLocal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all Renderers parented to the VolumeBaker & Encapsulates them with the bounds (unless manual bounds is enabled)
|
|
/// </summary>
|
|
/// <param name="settings"></param>
|
|
/// <param name="renderers"></param>
|
|
/// <param name="target"></param>
|
|
/// <returns></returns>
|
|
public bool GetChildRenderersAndEncapsulate( ref AVolumeSettings settings, ref List<Renderer> renderers, Transform target )
|
|
{
|
|
if (renderers == null) return false;
|
|
|
|
//get renderers in children
|
|
Renderer[] mrs = target.GetComponentsInChildren<Renderer>();
|
|
if (mrs == null || mrs.Length == 0) return false;
|
|
|
|
Bounds newBounds = useManualBounds ? bounds : new Bounds(target.position,Vector3.zero);
|
|
bool first = true;
|
|
foreach (var r in mrs)
|
|
{
|
|
if (!(r is MeshRenderer) && !(r is SkinnedMeshRenderer)) continue;
|
|
//skip inactive renderers
|
|
if (!r.gameObject.activeSelf) continue;
|
|
if (!r.enabled) continue;
|
|
|
|
if (!useManualBounds) //do not change bounds if manual bounds specified
|
|
{
|
|
if (!fitToVertices)
|
|
{
|
|
newBounds.Encapsulate(r.bounds);
|
|
}
|
|
else
|
|
{
|
|
//iterate all vertices and encapsulate
|
|
Mesh mesh = null;
|
|
if (r is MeshRenderer)
|
|
{
|
|
MeshFilter mf = r.GetComponent<MeshFilter>();
|
|
if (mf != null && mf.sharedMesh != null)
|
|
{
|
|
mesh = mf.sharedMesh;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mesh = (r as SkinnedMeshRenderer).sharedMesh;
|
|
}
|
|
|
|
if (mesh != null)
|
|
{
|
|
Bounds b = EncapsulateVertices(mesh, r.transform.localToWorldMatrix);
|
|
|
|
if (first)
|
|
{
|
|
newBounds = b;
|
|
}
|
|
else
|
|
{
|
|
newBounds.Encapsulate(b);
|
|
}
|
|
|
|
first = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
renderers.Add(r);
|
|
}
|
|
|
|
if (renderers.Count == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Debug.Log( $"GetMeshRenderersInChildren newBounds: {newBounds.center} Size: {newBounds.size} Dimensions: {settings.Dimensions}");
|
|
|
|
//assign new bounds
|
|
//remove the world offset
|
|
if (!useManualBounds) newBounds = new Bounds(newBounds.center - target.position, newBounds.size);
|
|
|
|
settings = new AVolumeSettings(newBounds, settings.Dimensions)
|
|
{
|
|
UsePadding = !useManualBounds //no padding with manual bounds, sorry!
|
|
};
|
|
AVolumeSettings.AddBoundsBorder(ref settings);
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool GetMeshRenderersIntersectingVolume( AVolumeSettings settings, Transform transform, ref List<Renderer> renderers )
|
|
{
|
|
if (renderers == null) return false;
|
|
|
|
Renderer[] mrs = FindObjectsOfType<Renderer>();
|
|
if (mrs == null || mrs.Length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//shift bounds local to world space position
|
|
Bounds ws = settings.BoundsLocal;
|
|
ws.center += transform.position;
|
|
|
|
foreach (var r in mrs)
|
|
{
|
|
if (!(r is MeshRenderer) && !(r is SkinnedMeshRenderer)) continue;
|
|
//skip inactive renderers
|
|
if (!r.gameObject.activeSelf) continue;
|
|
if (!r.enabled) continue;
|
|
if (!ws.Intersects(r.bounds)) continue;
|
|
renderers.Add(r);
|
|
}
|
|
|
|
if (renderers.Count == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static Bounds EncapsulateVertices( Mesh mesh, Matrix4x4 localToWorld )
|
|
{
|
|
Vector3[] vertices = new Vector3[mesh.vertexCount];
|
|
mesh.vertices.CopyTo(vertices,0);
|
|
|
|
Vector3 vert = localToWorld.MultiplyPoint3x4(vertices[0]);
|
|
Bounds b = new Bounds(vert,Vector3.zero);
|
|
for ( int i=1; i<vertices.Length; i++)
|
|
{
|
|
b.Encapsulate(localToWorld.MultiplyPoint3x4(vertices[i]));
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
private static Color colorPreviewBounds = new Color(0.5f,1f,0.5f,0.5f);
|
|
|
|
protected AVolumePreview<D> _aPreview;
|
|
public bool IsPreviewing => _aPreview != null;
|
|
public bool RenderersEnabled { get; protected set; }
|
|
|
|
private void OnValidate()
|
|
{
|
|
//need to ensure that bounds are not zero, and dimensions are at least something reasonable
|
|
if (bounds.extents.x < float.Epsilon) bounds.extents = new Vector3(float.Epsilon,bounds.extents.y,bounds.extents.z);
|
|
if (bounds.extents.y < float.Epsilon) bounds.extents = new Vector3(bounds.extents.x,float.Epsilon,bounds.extents.z);
|
|
if (bounds.extents.z < float.Epsilon) bounds.extents = new Vector3(bounds.extents.x,bounds.extents.y,float.Epsilon);;
|
|
|
|
//TODO target voxel size needs work to be useful
|
|
/*if (useTargetVoxelSize)
|
|
{
|
|
targetVoxelSize = Mathf.Max(float.Epsilon, targetVoxelSize);
|
|
|
|
dimensions.x = Mathf.RoundToInt(bounds.size.x / targetVoxelSize);
|
|
dimensions.y = Mathf.RoundToInt(bounds.size.y / targetVoxelSize);
|
|
dimensions.z = Mathf.RoundToInt(bounds.size.z / targetVoxelSize);
|
|
}*/
|
|
|
|
dimensions.x = Mathf.Clamp(dimensions.x, 1, MaxDimension);
|
|
dimensions.y = Mathf.Clamp(dimensions.y, 1, MaxDimension);
|
|
dimensions.z = Mathf.Clamp(dimensions.z, 1, MaxDimension);
|
|
}
|
|
|
|
public abstract void Bake();
|
|
|
|
protected virtual void OnDrawGizmos()
|
|
{
|
|
if ( showAllPreviews ) DrawGizmos( true, false );
|
|
/*
|
|
//draw bounds to indicate preview
|
|
//ignore scale of transform
|
|
var transform1 = transform;
|
|
Gizmos.matrix = Matrix4x4.TRS(transform1.position, transform1.rotation, Vector3.one);
|
|
Gizmos.color = colorPreviewBounds;
|
|
Gizmos.DrawWireCube(bounds.center, bounds.size);
|
|
*/
|
|
}
|
|
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
DrawGizmos( !showAllPreviews, true );
|
|
/*
|
|
//draw voxel size
|
|
var transform1 = transform;
|
|
Gizmos.matrix = Matrix4x4.TRS(transform1.position, transform1.rotation, Vector3.one);
|
|
Gizmos.color = Color.red;
|
|
Vector3 voxelSize = new Vector3( bounds.size.x/dimensions.x, bounds.size.y/dimensions.y, bounds.size.z/dimensions.z);
|
|
Bounds voxelBounds = new Bounds(bounds.center - bounds.extents + (voxelSize*0.5f), voxelSize);
|
|
Gizmos.DrawWireCube(voxelBounds.center, voxelBounds.size);
|
|
*/
|
|
}
|
|
|
|
void DrawGizmos( bool showPreview, bool showVoxel )
|
|
{
|
|
//ignore scale of transform
|
|
var transform1 = transform;
|
|
Gizmos.matrix = Matrix4x4.TRS(transform1.position, transform1.rotation, Vector3.one);
|
|
|
|
if ( showPreview )
|
|
{
|
|
Gizmos.color = colorPreviewBounds;
|
|
Gizmos.DrawWireCube( bounds.center, bounds.size );
|
|
}
|
|
|
|
if ( showVoxel )
|
|
{
|
|
Gizmos.color = Color.red;
|
|
Vector3 voxelSize = new Vector3( bounds.size.x/dimensions.x, bounds.size.y/dimensions.y, bounds.size.z/dimensions.z);
|
|
Bounds voxelBounds = new Bounds(bounds.center - bounds.extents + (voxelSize*0.5f), voxelSize);
|
|
Gizmos.DrawWireCube(voxelBounds.center, voxelBounds.size);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
} |