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++;
                        }
                    }
                }
            }
        }
Пример #2
0
        /**
         * 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;
            }
        }
Пример #3
0
        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++;
                }
            }
        }
Пример #4
0
        /** 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;
        }
Пример #5
0
        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++;
                        }
                    }
                }
            }
        }
Пример #6
0
        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);
        }