namespace Environment.Instancing { using UnityEngine; using System.Collections.Generic; using Environment.Utilities; /// /// Instances behaviour to generate on surfaces of meshes. /// [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] public class MeshInstancesBehaviour : InstancesBehaviour { [Header("Sub Mesh Details")] public bool UseSubMesh = false; public int SubMeshIndex = 0; [Header("Instance Settings")] public float Density = 1f; public InstancingSettings[] InstancingSettings; /// /// Calculates the bounds for the instances used for culling by Unity. /// public override Bounds CalculateInstancesBounds() { var rendererBounds = this.GetComponent().bounds; var diff = this.transform.position - rendererBounds.center; var absDiff = new Vector3(Mathf.Abs(diff.x), Mathf.Abs(diff.y), Mathf.Abs(diff.z)); return new Bounds(this.transform.position, (rendererBounds.extents + absDiff) * 2f); } /// /// Implementation of instance data logic for instances behaviour. /// public override Dictionary> GetInstanceData() { if (this.InstancingSettings == null || this.InstancingSettings.Length == 0) { Debug.LogWarning("Instancing settings should be defined the Unity Editor."); return null; } foreach (var settings in this.InstancingSettings) { if (settings.Scale <= 0f || settings.Material == null || settings.Mesh == null) { Debug.LogError("The given Instance Configurations should have a material, mesh and a scale larger than 0"); return null; } } var mesh = this.GetComponent().mesh; if (this.UseSubMesh) { if (!mesh.isReadable) { Debug.LogWarning("Mesh is not readable. Provide read permissions for it to work in builds!" ); } if(this.SubMeshIndex >= mesh.subMeshCount) { Debug.LogWarning("The sub mesh index does not exist. Using default; using the whole mesh."); } else { var subMesh = new Mesh { vertices = mesh.vertices, triangles = mesh.GetTriangles(this.SubMeshIndex) }; mesh = subMesh; } } var instanceData = RandomMeshInstanceData(mesh, this.Density, this.InstancingSettings); return DivideInstanceData(instanceData, this.InstancingSettings); } /// /// Gets random mesh instance data on a mesh. /// public static InstanceData[] RandomMeshInstanceData(Mesh mesh, float density, InstancingSettings[] configurations) { if (mesh == null || density <= 0 || configurations == null || configurations.Length == 0) { return null; } var instanceAmount = Mathf.CeilToInt(MeshUtilities.GetMeshArea(mesh) / density); var samples = MeshUtilities.RandomMeshPoints(mesh, instanceAmount); var data = new List(); for (var i = 0; i < samples.Length; i++) { var (vertex, normal) = samples[i]; var instance = new InstanceData { TRS = Matrix4x4.TRS( vertex, Quaternion.identity, Vector3.one ), Normal = normal }; data.Add(instance); } return data.ToArray(); } } }