#if GRIFFIN using System.Collections.Generic; using UnityEngine; using Unity.Collections; using Pinwheel.Griffin.Rendering; using Pinwheel.Griffin.Compression; using System; using UnityEngine.Serialization; namespace Pinwheel.Griffin { [System.Serializable] public class GGrassPatch { [SerializeField] private GFoliage foliage; public GFoliage Foliage { get { return foliage; } private set { foliage = value; } } [SerializeField] private Vector2 index; public Vector2 Index { get { return index; } set { index = value; } } #pragma warning disable 0649 //Old container that get serialized as raw values and should be retired for now //However, it should still be here to prevent data lost when upgrading from lower version //This container will be empty after the manual upgrade [SerializeField] private List instances; #pragma warning restore 0649 internal int InstanceCount { get { if (instances_NonSerialized == null) return 0; else return instances_NonSerialized.Count; } } [NonSerialized] private List instances_NonSerialized; internal List Instances { get { if (instances_NonSerialized == null) { instances_NonSerialized = new List(); } return instances_NonSerialized; } set { instances_NonSerialized = value; } } private bool requireFullUpdate; internal bool RequireFullUpdate { get { return requireFullUpdate; } set { requireFullUpdate = value; } } [SerializeField] internal Bounds bounds; public Bounds Bounds { get { return bounds; } private set { bounds = value; } } [SerializeField] private byte[] prototypeIndexSerializeData; [SerializeField] private byte[] positionSerializeData; [SerializeField] private byte[] rotationSerializeData; [SerializeField] private byte[] scaleSerializeData; internal GGrassPatch(GFoliage owner, int indexX, int indexY) { foliage = owner; Index = new Vector2(indexX, indexY); } public void Changed() { if (Foliage != null && Foliage.TerrainData != null) { Foliage.TerrainData.InvokeGrassChange(Index); } } public void AddInstances(IEnumerable newInstances) { Instances.AddRange(newInstances); RecalculateBounds(); Changed(); } public int RemoveInstances(Predicate match) { int count = Instances.RemoveAll(match); if (count > 0) { RecalculateBounds(); Changed(); } return count; } public void ClearInstances() { Instances.Clear(); RecalculateBounds(); Changed(); } public Rect GetUvRange() { return GCommon.GetUvRange(foliage.PatchGridSize, (int)Index.x, (int)Index.y); } public void RecalculateBounds() { if (Instances.Count == 0) { Rect r = GetUvRange(); Vector3 center = new Vector3(r.x, 0, r.y); Vector3 size = Vector3.zero; Bounds b = new Bounds(); b.center = center; b.size = size; Bounds = b; } else { float minX = float.MaxValue; float maxX = float.MinValue; float minY = float.MaxValue; float maxY = float.MinValue; float minZ = float.MaxValue; float maxZ = float.MinValue; int instanceCount = Instances.Count; for (int i = 0; i < instanceCount; ++i) { Vector3 pos = Instances[i].Position; minX = Mathf.Min(minX, pos.x); maxX = Mathf.Max(maxX, pos.x); minY = Mathf.Min(minY, pos.y); maxY = Mathf.Max(maxY, pos.y); minZ = Mathf.Min(minZ, pos.z); maxZ = Mathf.Max(maxZ, pos.z); } Vector3 p = Vector3.Lerp( new Vector3(minX, minY, minZ), new Vector3(maxX, maxY, maxZ), 0.5f); Vector3 s = new Vector3( maxX - minX, Mathf.Max(0.001f, maxY - minY), maxZ - minZ); Bounds = new Bounds(p, s); } } internal void Serialize() { int[] protoIndices = new int[Instances.Count]; float[] positions = new float[Instances.Count * 3]; float[] rotations = new float[Instances.Count * 4]; float[] scales = new float[Instances.Count * 3]; for (int i = 0; i < Instances.Count; ++i) { GGrassInstance grass = Instances[i]; protoIndices[i] = grass.PrototypeIndex; positions[i * 3 + 0] = grass.Position.x; positions[i * 3 + 1] = grass.Position.y; positions[i * 3 + 2] = grass.Position.z; rotations[i * 4 + 0] = grass.Rotation.x; rotations[i * 4 + 1] = grass.Rotation.y; rotations[i * 4 + 2] = grass.Rotation.z; rotations[i * 4 + 3] = grass.Rotation.w; scales[i * 3 + 0] = grass.Scale.x; scales[i * 3 + 1] = grass.Scale.y; scales[i * 3 + 2] = grass.Scale.z; } prototypeIndexSerializeData = new byte[Buffer.ByteLength(protoIndices)]; Buffer.BlockCopy(protoIndices, 0, prototypeIndexSerializeData, 0, prototypeIndexSerializeData.Length); prototypeIndexSerializeData = GCompressor.Compress(prototypeIndexSerializeData); positionSerializeData = new byte[Buffer.ByteLength(positions)]; Buffer.BlockCopy(positions, 0, positionSerializeData, 0, positionSerializeData.Length); positionSerializeData = GCompressor.Compress(positionSerializeData); rotationSerializeData = new byte[Buffer.ByteLength(rotations)]; Buffer.BlockCopy(rotations, 0, rotationSerializeData, 0, rotationSerializeData.Length); rotationSerializeData = GCompressor.Compress(rotationSerializeData); scaleSerializeData = new byte[Buffer.ByteLength(scales)]; Buffer.BlockCopy(scales, 0, scaleSerializeData, 0, scaleSerializeData.Length); scaleSerializeData = GCompressor.Compress(scaleSerializeData); GCompressor.CleanUp(); } internal void Deserialize() { if (prototypeIndexSerializeData != null && positionSerializeData != null && rotationSerializeData != null && scaleSerializeData != null) { prototypeIndexSerializeData = GCompressor.Decompress(prototypeIndexSerializeData); positionSerializeData = GCompressor.Decompress(positionSerializeData); rotationSerializeData = GCompressor.Decompress(rotationSerializeData); scaleSerializeData = GCompressor.Decompress(scaleSerializeData); int[] indices = new int[prototypeIndexSerializeData.Length / sizeof(int)]; float[] positions = new float[positionSerializeData.Length / sizeof(float)]; float[] rotations = new float[rotationSerializeData.Length / sizeof(float)]; float[] scales = new float[scaleSerializeData.Length / sizeof(float)]; Buffer.BlockCopy(prototypeIndexSerializeData, 0, indices, 0, prototypeIndexSerializeData.Length); Buffer.BlockCopy(positionSerializeData, 0, positions, 0, positionSerializeData.Length); Buffer.BlockCopy(rotationSerializeData, 0, rotations, 0, rotationSerializeData.Length); Buffer.BlockCopy(scaleSerializeData, 0, scales, 0, scaleSerializeData.Length); Instances.Clear(); for (int i = 0; i < indices.Length; ++i) { GGrassInstance grass = GGrassInstance.Create(indices[i]); grass.Position = new Vector3( positions[i * 3 + 0], positions[i * 3 + 1], positions[i * 3 + 2]); grass.Rotation = new Quaternion( rotations[i * 4 + 0], rotations[i * 4 + 1], rotations[i * 4 + 2], rotations[i * 4 + 3]); grass.Scale = new Vector3( scales[i * 3 + 0], scales[i * 3 + 1], scales[i * 3 + 2]); Instances.Add(grass); } GCompressor.CleanUp(); } } internal void UpgradeSerializeVersion() { //v250: Compressed serialization if (instances != null && instances.Count > 0) { if (instances_NonSerialized != null) { instances_NonSerialized.AddRange(instances); RecalculateBounds(); Changed(); } } } public int GetMemStats() { int mem = 0; if (prototypeIndexSerializeData != null) { mem += prototypeIndexSerializeData.Length; mem += positionSerializeData.Length; mem += rotationSerializeData.Length; mem += scaleSerializeData.Length; } return mem; } public NativeArray GetGrassPositionArray(Allocator allocator = Allocator.TempJob) { int instanceCount = InstanceCount; List instances = Instances; NativeArray positions = new NativeArray(instanceCount, allocator, NativeArrayOptions.UninitializedMemory); Vector2 pos = Vector2.zero; for (int i = 0; i < instanceCount; ++i) { GGrassInstance g = instances[i]; pos.Set(g.position.x, g.position.z); positions[i] = pos; } return positions; } } } #endif