using System.Collections.Generic; using UnityEngine; namespace SDFr { public abstract class AVolumeBaker : MonoBehaviour where T : AVolume, 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 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 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; } } /// /// changes bound size automatically /// public virtual void Encapsulate() { List tempRenderers = new List(); AVolumeSettings settings = new AVolumeSettings(bounds, dimensions); GetChildRenderersAndEncapsulate(ref settings, ref tempRenderers, transform); bounds = settings.BoundsLocal; } /// /// Gets all Renderers parented to the VolumeBaker & Encapsulates them with the bounds (unless manual bounds is enabled) /// /// /// /// /// public bool GetChildRenderersAndEncapsulate( ref AVolumeSettings settings, ref List renderers, Transform target ) { if (renderers == null) return false; //get renderers in children Renderer[] mrs = target.GetComponentsInChildren(); 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(); 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 renderers ) { if (renderers == null) return false; Renderer[] mrs = FindObjectsOfType(); 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 _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 } }