private void ProcessCell(FoliageCellDataRuntime cell, float colliderDistSqr) { for (int foliageType = 0; foliageType < cell.m_TypeHashLocationsRuntime.Length; foliageType++) { int foliageTypeKey = cell.m_TypeHashLocationsRuntime[foliageType].Key; FoliageType type = m_FoliageTypes[foliageTypeKey]; if (type.m_EnableCollision == false) { continue; } var batches = cell.m_TypeHashLocationsRuntime[foliageType].Value.m_EditTime; int hash = type.m_Hash; float x, y, z; float dist; for (int i = 0; i < batches.Length; i++) { Vector3 pos = batches[i].m_Position; x = pos.x - m_LastPosition.x; y = pos.y - m_LastPosition.y; z = pos.z - m_LastPosition.z; dist = x * x + y * y + z * z; if (dist <= colliderDistSqr) { GameObject collider = GetColliderForPrototype(hash); // Set the layer collider collider.layer = m_Layer; if (collider != null) { FoliageColliderData data = collider.GetComponent <FoliageColliderData>(); if (data == null) { data = collider.AddComponent <FoliageColliderData>(); } // Append the collision data for query at the runtime data.m_FoliageType = foliageTypeKey; data.m_FoliageInstance = batches[i]; // Update it's transform values collider.transform.position = batches[i].m_Position; collider.transform.rotation = batches[i].m_Rotation; collider.transform.localScale = batches[i].m_Scale; // Increment the active collider count m_DataIssuedActiveColliders++; } } } } }
/** * Removes the instance from the list. Costly operation that will re-build the array. Do not use often! */ private void RemoveFoliageInstanceCell(int typeHash, System.Guid guid, FoliageCellDataRuntime data, bool ignoreDifferentHash = true) { for (int type = 0; type < data.m_TypeHashLocationsRuntime.Length; type++) { if (ignoreDifferentHash && data.m_TypeHashLocationsRuntime[type].Key != typeHash) { continue; } var keyValuePair = data.m_TypeHashLocationsRuntime[type]; // Build new arrays FoliageTuple <FoliageInstance[]> tuple = keyValuePair.Value; FoliageInstance[] newInstancesEdit = null; FoliageInstance[] newInstancesRuntime = null; if (tuple.m_EditTime != null) { newInstancesEdit = System.Array.FindAll(tuple.m_EditTime, (x) => x.m_UniqueId != guid); } if (tuple.m_RuntimeAppended != null) { newInstancesRuntime = System.Array.FindAll(tuple.m_RuntimeAppended, (x) => x.m_UniqueId != guid); } // Set the new value data.m_TypeHashLocationsRuntime[type].Value.m_EditTime = newInstancesEdit; data.m_TypeHashLocationsRuntime[type].Value.m_RuntimeAppended = newInstancesRuntime; } }
private void ProcessCellGrass(FoliageCellDataRuntime runtimeCell) { for (int i = 0, len = runtimeCell.m_FoliageDataSubdivided.Length; i < len; i++) { FoliageCellSubdividedDataRuntime runtimeCellSubdivided = runtimeCell.m_FoliageDataSubdivided[i].Value; float subdivDistance = runtimeCellSubdivided.m_Bounds.SqrDistance(m_CurrentFrameCameraPosition); if (subdivDistance <= m_MaxDistanceGrassSqr && GeometryUtility.TestPlanesAABB(m_CameraPlanes, runtimeCellSubdivided.m_Bounds)) { ProcessSubdividedCell(runtimeCell, runtimeCellSubdivided, subdivDistance); m_DrawStats.m_ProcessedCellsSubdiv++; } } }
/** Add foliage instance. Only works for trees at runtime. */ public void AddFoliageInstance(int typeHash, FoliageInstance instance) { int keyHash = FoliageCell.MakeHash(instance.m_Position); FoliageCellDataRuntime cell; if (m_FoliageData.ContainsKey(keyHash) == false) { cell = new FoliageCellDataRuntime(); FoliageCell fCell = new FoliageCell(instance.m_Position, false); // Set the standard data cell.m_Bounds = fCell.GetBounds(); cell.m_Position = fCell; // Empty subdivided data cell.m_FoliageDataSubdivided = new FoliageKeyValuePair <int, FoliageCellSubdividedDataRuntime> [0]; // Empty type data cell.m_TypeHashLocationsRuntime = new FoliageKeyValuePair <int, FoliageTuple <FoliageInstance[]> > [0]; // Add the data m_FoliageData.Add(keyHash, cell); } cell = m_FoliageData[keyHash]; // Check if we have all the data int idx = System.Array.FindIndex(cell.m_TypeHashLocationsRuntime, (x) => x.Key == keyHash); if (idx < 0) { System.Array.Resize(ref cell.m_TypeHashLocationsRuntime, cell.m_TypeHashLocationsRuntime.Length + 1); idx = cell.m_TypeHashLocationsRuntime.Length - 1; // Add the type cell.m_TypeHashLocationsRuntime[idx] = new FoliageKeyValuePair <int, FoliageTuple <FoliageInstance[]> >(typeHash, new FoliageTuple <FoliageInstance[]>(new FoliageInstance[0])); } // We have the stuff System.Array.Resize(ref cell.m_TypeHashLocationsRuntime[idx].Value.m_EditTime, cell.m_TypeHashLocationsRuntime[idx].Value.m_EditTime.Length + 1); cell.m_TypeHashLocationsRuntime[idx].Value.m_EditTime[cell.m_TypeHashLocationsRuntime[idx].Value.m_EditTime.Length - 1] = instance; }
private void ProcessSubdividedCell(FoliageCellDataRuntime cell, FoliageCellSubdividedDataRuntime cellSubdivided, float distance) { for (int foliageType = 0, foliageTypeCount = cellSubdivided.m_TypeHashLocationsRuntime.Length; foliageType < foliageTypeCount; foliageType++) { FoliageType type = m_FoliageTypes[cellSubdivided.m_TypeHashLocationsRuntime[foliageType].Key]; float maxTypeDist = type.m_RenderInfo.m_MaxDistance * type.m_RenderInfo.m_MaxDistance; if (distance <= maxTypeDist) { var batches = cellSubdivided.m_TypeHashLocationsRuntime[foliageType].Value.m_EditTime; // Set the MPB values that are universal for all grass types MaterialPropertyBlock mpb = type.m_RuntimeData.m_TypeMPB; mpb.SetFloat(m_ShaderIDCritiasFoliageDistance, type.m_RenderInfo.m_MaxDistance); mpb.SetFloat(m_ShaderIDCritiasFoliageDistanceSqr, maxTypeDist); // TODO: Set bend data if we have it for this type if (type.m_EnableBend) { mpb.SetFloat(m_ShaderIDCritiasBendDistance, type.m_BendDistance); mpb.SetFloat(m_ShaderIDCritiasBendScale, type.m_BendPower); mpb.SetVector(m_ShaderIDCritiasBendPosition, m_CurrentFrameBendPosition); } // Get data from the type Mesh mesh = type.m_RuntimeData.m_LODDataGrass.m_Mesh; Material mat = type.m_RuntimeData.m_LODDataGrass.m_Material; // Only if we have the type for rendering indirect if (type.RenderIndirect && m_CurrentFrameAllowIndirect) { long indirectCachedDataKey = ((((long)cell.m_Position.GetHashCode()) << 32) | ((long)cellSubdivided.m_Position.GetHashCode())) + type.m_Hash; // * 0xF01226E02D41B if (m_CachedGPUBufferData.ContainsKey(indirectCachedDataKey) == false) { GPUBufferCellCachedData data = new GPUBufferCellCachedData(); // Merge the buffers Matrix4x4[] allInstances; // Build all the data if (batches.Length > 1) { int totalCount = 0; for (int batchIdx = 0; batchIdx < batches.Length; batchIdx++) { totalCount += batches[batchIdx].Length; } List <Matrix4x4> concat = new List <Matrix4x4>(totalCount); for (int batchIdx = 0; batchIdx < batches.Length; batchIdx++) { concat.AddRange(batches[batchIdx]); } allInstances = concat.ToArray(); } else { allInstances = batches[0]; } // Set the position data data.m_BufferPositions = new ComputeBuffer(allInstances.Length, 64); data.m_BufferPositions.SetData(allInstances); // Set the arguments data.m_BufferArguments = new ComputeBuffer(1, m_TempDrawArgs.Length * sizeof(uint), ComputeBufferType.IndirectArguments); data.m_IndexCount = mesh.GetIndexCount(0); // Set the instance count data.m_InstanceCount = (uint)allInstances.Length; // Add the data. The cache will take care of clearing the data that is in excess m_CachedGPUBufferData.Add(indirectCachedDataKey, data); } GPUBufferCellCachedData indirectData = m_CachedGPUBufferData[indirectCachedDataKey]; // Set the buffer positions mpb.SetBuffer(m_ShaderIDCritiasInstanceBuffer, indirectData.m_BufferPositions); // Set the draw count and send it m_TempDrawArgs[0] = indirectData.m_IndexCount; m_TempDrawArgs[1] = (uint)(indirectData.m_InstanceCount * m_Settings.m_GrassDensity); indirectData.m_BufferArguments.SetData(m_TempDrawArgs); // Draw without shadows Graphics.DrawMeshInstancedIndirect(mesh, 0, mat, cellSubdivided.m_Bounds, indirectData.m_BufferArguments, 0, mpb, ShadowCastingMode.Off, true, m_CurrentFrameLayer, m_CurrentFrameCameraDraw); } else { // Cast shadow only if the type allows ShadowCastingMode castShadow = type.m_RenderInfo.m_CastShadow ? ShadowCastingMode.On : ShadowCastingMode.Off; // Pass with the MPB the data related to per-type distance for (int i = 0, batchCount = batches.Length; i < batchCount; i++) { Graphics.DrawMeshInstanced(mesh, 0, mat, batches[i], (int)(batches[i].Length * m_Settings.m_GrassDensity), mpb, castShadow, true, m_CurrentFrameLayer, m_CurrentFrameCameraDraw); m_DrawStats.m_ProcessedDrawCalls++; } } } } }
private void ProcessCellTree(FoliageCellDataRuntime cell, float distanceSqr, bool shadowCorrection, float shadowCorrectionDistanceSqr, bool shadowOnly) { // Process tree cell content with types for (int foliageType = 0, foliageTypeCount = cell.m_TypeHashLocationsRuntime.Length; foliageType < foliageTypeCount; foliageType++) { FoliageType type = m_FoliageTypes[cell.m_TypeHashLocationsRuntime[foliageType].Key]; var batches = cell.m_TypeHashLocationsRuntime[foliageType].Value.m_EditTime; float maxDistance = type.m_RenderInfo.m_MaxDistance; float maxDistanceSqr = maxDistance * maxDistance; MaterialPropertyBlock mpb = type.m_RuntimeData.m_TypeMPB; // Set the global per-type data mpb.SetFloat(m_ShaderIDCritiasFoliageDistance, maxDistance); mpb.SetFloat(m_ShaderIDCritiasFoliageDistanceSqr, maxDistanceSqr); // Set the per-lod data FoliageTypeLODTree[] treeLods = type.m_RuntimeData.m_LODDataTree; bool castAnyShadow = type.m_RenderInfo.m_CastShadow; ShadowCastingMode shadow = castAnyShadow ? ShadowCastingMode.On : ShadowCastingMode.Off; // Reset the temp count for (int i = 0; i < m_MtxLODTempCount.Length; i++) { m_MtxLODTempCount[i] = 0; m_MtxLODTempShadowCount[i] = 0; } float x, y, z; float dist; for (int treeIndex = 0, treeIndexCount = batches.Length; treeIndex < treeIndexCount; treeIndex++) { // Test the distance and the frustum cull Vector3 pos = batches[treeIndex].m_Position; x = pos.x - m_CurrentFrameCameraPosition.x; y = pos.y - m_CurrentFrameCameraPosition.y; z = pos.z - m_CurrentFrameCameraPosition.z; dist = x * x + y * y + z * z; if (dist <= maxDistanceSqr && GeometryUtility.TestPlanesAABB(m_CameraPlanes, batches[treeIndex].m_Bounds) && shadowOnly == false) { // Get the current LOD int currentLOD = GetCurrentLOD(ref treeLods, Mathf.Sqrt(dist)); int currentIdx = m_MtxLODTempCount[currentLOD]; // Add it to the LOD matrix m_MtxLODTemp[currentLOD][currentIdx].m00 = batches[treeIndex].m_Matrix.m00; m_MtxLODTemp[currentLOD][currentIdx].m01 = batches[treeIndex].m_Matrix.m01; m_MtxLODTemp[currentLOD][currentIdx].m02 = batches[treeIndex].m_Matrix.m02; m_MtxLODTemp[currentLOD][currentIdx].m03 = batches[treeIndex].m_Matrix.m03; m_MtxLODTemp[currentLOD][currentIdx].m10 = batches[treeIndex].m_Matrix.m10; m_MtxLODTemp[currentLOD][currentIdx].m11 = batches[treeIndex].m_Matrix.m11; m_MtxLODTemp[currentLOD][currentIdx].m12 = batches[treeIndex].m_Matrix.m12; m_MtxLODTemp[currentLOD][currentIdx].m13 = batches[treeIndex].m_Matrix.m13; m_MtxLODTemp[currentLOD][currentIdx].m20 = batches[treeIndex].m_Matrix.m20; m_MtxLODTemp[currentLOD][currentIdx].m21 = batches[treeIndex].m_Matrix.m21; m_MtxLODTemp[currentLOD][currentIdx].m22 = batches[treeIndex].m_Matrix.m22; m_MtxLODTemp[currentLOD][currentIdx].m23 = batches[treeIndex].m_Matrix.m23; // Increment the LOD count m_MtxLODTempCount[currentLOD]++; // If we reached 1000 elements, submit the batch if (m_MtxLODTempCount[currentLOD] >= FoliageGlobals.RENDER_BATCH_SIZE) { // Issue the draw and reset the count IssueBatchLOD(m_MtxLODTemp[currentLOD], m_MtxLODTempCount[currentLOD], treeLods[currentLOD], mpb, shadow); m_MtxLODTempCount[currentLOD] = 0; } } else if (castAnyShadow && shadowCorrection && dist <= shadowCorrectionDistanceSqr) { int currentLOD = GetCurrentLOD(ref treeLods, Mathf.Sqrt(dist)); int currentIdx = m_MtxLODTempShadowCount[currentLOD]; // Add it to the shadow matrix m_MtxLODTempShadow[currentLOD][currentIdx].m00 = batches[treeIndex].m_Matrix.m00; m_MtxLODTempShadow[currentLOD][currentIdx].m01 = batches[treeIndex].m_Matrix.m01; m_MtxLODTempShadow[currentLOD][currentIdx].m02 = batches[treeIndex].m_Matrix.m02; m_MtxLODTempShadow[currentLOD][currentIdx].m03 = batches[treeIndex].m_Matrix.m03; m_MtxLODTempShadow[currentLOD][currentIdx].m10 = batches[treeIndex].m_Matrix.m10; m_MtxLODTempShadow[currentLOD][currentIdx].m11 = batches[treeIndex].m_Matrix.m11; m_MtxLODTempShadow[currentLOD][currentIdx].m12 = batches[treeIndex].m_Matrix.m12; m_MtxLODTempShadow[currentLOD][currentIdx].m13 = batches[treeIndex].m_Matrix.m13; m_MtxLODTempShadow[currentLOD][currentIdx].m20 = batches[treeIndex].m_Matrix.m20; m_MtxLODTempShadow[currentLOD][currentIdx].m21 = batches[treeIndex].m_Matrix.m21; m_MtxLODTempShadow[currentLOD][currentIdx].m22 = batches[treeIndex].m_Matrix.m22; m_MtxLODTempShadow[currentLOD][currentIdx].m23 = batches[treeIndex].m_Matrix.m23; // Increment count m_MtxLODTempShadowCount[currentLOD]++; if (m_MtxLODTempShadowCount[currentLOD] >= FoliageGlobals.RENDER_BATCH_SIZE) { IssueBatchLOD(m_MtxLODTempShadow[currentLOD], m_MtxLODTempShadowCount[currentLOD], treeLods[currentLOD], mpb, ShadowCastingMode.ShadowsOnly); m_MtxLODTempShadowCount[currentLOD] = 0; } } } // If we have any leftovers for (int i = 0; i < treeLods.Length; i++) { if (m_MtxLODTempCount[i] > 0) { // Issue the draw and reset the count IssueBatchLOD(m_MtxLODTemp[i], m_MtxLODTempCount[i], treeLods[i], mpb, shadow); m_MtxLODTempCount[i] = 0; } if (m_MtxLODTempShadowCount[i] > 0) { IssueBatchLOD(m_MtxLODTempShadow[i], m_MtxLODTempShadowCount[i], treeLods[i], mpb, ShadowCastingMode.ShadowsOnly); m_MtxLODTempShadowCount[i] = 0; } } m_DrawStats.m_ProcessedInstances += batches.Length; } }
/** * Load from file the runtime version of the data */ public static FoliageDataRuntime LoadFromFileRuntime(string filename) { FoliageData data = LoadFromFileEditTime(filename); // Build the runtime data from the edit time data FoliageDataRuntime runtime = new FoliageDataRuntime(); foreach (var hashedCell in data.m_FoliageData) { FoliageCellData editCell = hashedCell.Value; FoliageCellDataRuntime runtimeCell = new FoliageCellDataRuntime(); // Set the data. Note that we only need the extended bounds runtimeCell.m_Bounds = editCell.m_BoundsExtended; runtimeCell.m_Position = editCell.m_Position; // Build the tree instance data int idx = -1; runtimeCell.m_TypeHashLocationsRuntime = new FoliageKeyValuePair <int, FoliageTuple <FoliageInstance[]> > [editCell.m_TypeHashLocationsEditor.Count]; foreach (var instances in editCell.m_TypeHashLocationsEditor) { idx++; List <FoliageInstance> allTreeInstances = new List <FoliageInstance>(); var labeledInstances = instances.Value; // Build all the data from the labeled data foreach (List <FoliageInstance> inst in labeledInstances.Values) { allTreeInstances.AddRange(inst); } // We will build the world matrix for trees for (int i = 0; i < allTreeInstances.Count; i++) { FoliageInstance inst = allTreeInstances[i]; inst.BuildWorldMatrix(); allTreeInstances[i] = inst; } // Don't forget to trim all excess instances! allTreeInstances.TrimExcess(); #if UNITY_EDITOR if (allTreeInstances.Count == 0) { Debug.Assert(false, "Count 0!"); } #endif runtimeCell.m_TypeHashLocationsRuntime[idx] = new FoliageKeyValuePair <int, FoliageTuple <FoliageInstance[]> >(instances.Key, new FoliageTuple <FoliageInstance[]>(allTreeInstances.ToArray())); } // Build the grass instance data from the subdivided cells List <FoliageKeyValuePair <int, FoliageCellSubdividedDataRuntime> > foliageCellDataSubdivided = new List <FoliageKeyValuePair <int, FoliageCellSubdividedDataRuntime> >(editCell.m_FoliageDataSubdivided.Count); foreach (var hashedSubdividedCell in editCell.m_FoliageDataSubdivided) { FoliageCellSubdividedData editSubdividedCell = hashedSubdividedCell.Value; FoliageCellSubdividedDataRuntime runtimeSubdividedCell = new FoliageCellSubdividedDataRuntime(); // Set the data runtimeSubdividedCell.m_Bounds = editSubdividedCell.m_Bounds; runtimeSubdividedCell.m_Position = editSubdividedCell.m_Position; idx = -1; runtimeSubdividedCell.m_TypeHashLocationsRuntime = new FoliageKeyValuePair <int, FoliageTuple <Matrix4x4[][]> > [editSubdividedCell.m_TypeHashLocationsEditor.Count]; foreach (var instances in editSubdividedCell.m_TypeHashLocationsEditor) { idx++; List <FoliageInstance> allGrassInstances = new List <FoliageInstance>(); var labeledInstances = instances.Value; foreach (List <FoliageInstance> inst in labeledInstances.Values) { allGrassInstances.AddRange(inst); } #if UNITY_EDITOR if (allGrassInstances.Count == 0) { Debug.Assert(false, "Count 0!"); } #endif // Build the multi-array data int ranges = Mathf.CeilToInt(allGrassInstances.Count / (float)FoliageGlobals.RENDER_BATCH_SIZE); Matrix4x4[][] batches = new Matrix4x4[ranges][]; for (int i = 0; i < ranges; i++) { List <FoliageInstance> range = allGrassInstances.GetRange(i * FoliageGlobals.RENDER_BATCH_SIZE, i * FoliageGlobals.RENDER_BATCH_SIZE + FoliageGlobals.RENDER_BATCH_SIZE > allGrassInstances.Count ? allGrassInstances.Count - i * FoliageGlobals.RENDER_BATCH_SIZE : FoliageGlobals.RENDER_BATCH_SIZE); batches[i] = range.ConvertAll <Matrix4x4>((x) => x.GetWorldTransform()).ToArray(); } // Set the data runtimeSubdividedCell.m_TypeHashLocationsRuntime[idx] = new FoliageKeyValuePair <int, FoliageTuple <Matrix4x4[][]> >(instances.Key, new FoliageTuple <Matrix4x4[][]>(batches)); } // Add the subdivided runtime cell foliageCellDataSubdivided.Add(new FoliageKeyValuePair <int, FoliageCellSubdividedDataRuntime>(hashedSubdividedCell.Key, runtimeSubdividedCell)); } // Build the subdivided data runtimeCell.m_FoliageDataSubdivided = foliageCellDataSubdivided.ToArray(); // Add the runtime cell runtime.m_FoliageData.Add(hashedCell.Key, runtimeCell); } // Good for GC data = null; return(runtime); }