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++;
                        }
                    }
                }
            }
        }
        public static bool CanChangeType(FoliageType type, EFoliageType newType)
        {
            GameObject prefab = type.m_Prefab;
            // PrefabType prefabType = PrefabUtility.GetPrefabType(prefab);
            string path = AssetDatabase.GetAssetPath(prefab);

            if (IsSpeedTree(newType))
            {
                // We want to change to a SpeedTree type

                if (path.EndsWith(".spm") == false)
                {
                    FoliageLog.e("Can't change to SpeedTree foliage: " + type.m_Name + " since it's path does not end with '.spm'! It means that it's not a SpeedTree!");
                    return(false);
                }

                LODGroup group = prefab.GetComponent <LODGroup>();

                if (group == null)
                {
                    FoliageLog.e("Can't change to SpeedTree foliage: " + type.m_Name + " since it doesn't contain a 'LODGroup' component!");
                    return(false);
                }

                if (newType == EFoliageType.SPEEDTREE_TREE_BILLBOARD)
                {
                    bool containsBillboardRenderer = false;

                    LOD[] lods = group.GetLODs();

                    foreach (LOD lod in lods)
                    {
                        if (lod.renderers[0].GetComponent <BillboardRenderer>() != null)
                        {
                            containsBillboardRenderer = true;
                            break;
                        }
                    }

                    if (containsBillboardRenderer == false)
                    {
                        FoliageLog.e("Can't change to SpeedTree with billboard the foliage: " + type.m_Name + " since it doesn't contain a billboard renderer!");
                        return(false);
                    }
                }
            }

            // We're cool no error
            return(true);
        }
        public static void DestroyBillboards(GameObject owner, int cellHash, FoliageType type)
        {
            string name = string.Format("MeshCell[{0}_{1}]", cellHash, type.m_Prefab.name);

            Transform existing = owner.transform.Find(name);

            if (existing != null)
            {
#if UNITY_EDITOR
                if (UnityEditor.EditorApplication.isPlaying == false)
                {
                    Object.DestroyImmediate(existing.gameObject);
                }
                else
                {
                    Object.Destroy(existing.gameObject);
                }
#else
                Object.Destroy(existing.gameObject);
#endif
                existing = null;
            }
        }
        public static void GenerateBillboards(Bounds bounds, FoliageCell cell, GameObject owner, List <FoliageInstance> trees, FoliageType type, bool addLodGroup, float screenFadeSize, bool animatedCrossFade)
        {
            int[] originalTriangles = m_SystemQuadTriangles;

            GameObject meshObj = new GameObject();

            // Mark object as static
#if UNITY_EDITOR
            GameObjectUtility.SetStaticEditorFlags(meshObj, StaticEditorFlags.OccludeeStatic | StaticEditorFlags.ReflectionProbeStatic);
#endif

            string name = string.Format("MeshCell[{0}_{1}]", cell.GetHashCode(), type.m_Prefab.name);

            Transform existing = owner.transform.Find(name);
            if (existing != null)
            {
#if UNITY_EDITOR
                if (UnityEditor.EditorApplication.isPlaying == false)
                {
                    Object.DestroyImmediate(existing.gameObject);
                }
                else
                {
                    Object.Destroy(existing.gameObject);
                }
#else
                Object.Destroy(existing.gameObject);
#endif
                existing = null;
            }

            meshObj.transform.SetParent(owner.transform);
            meshObj.name = name;

            var data = type.m_RuntimeData.m_SpeedTreeData;

            Vector3 worldScale = new Vector3(data.m_Size.x, data.m_Size.y, data.m_Size.x);

            // Set material
            MeshRenderer rend = meshObj.AddComponent <MeshRenderer>();
            rend.sharedMaterial = GenerateBillboardMaterial(type.m_RuntimeData.m_SpeedTreeData);

            MeshFilter filter = meshObj.AddComponent <MeshFilter>();

            Mesh treeMesh = new Mesh();
            treeMesh.name = meshObj.name;

            List <Vector4> m_TempWorldPositions = new List <Vector4>();
            List <Vector3> m_TempWorldScales    = new List <Vector3>();
            List <Vector3> m_TempQuadVertices   = new List <Vector3>();
            List <Vector4> m_TempQuadTangents   = new List <Vector4>();
            List <Vector3> m_TempQuadNormals    = new List <Vector3>();
            List <int>     m_TempQuadIndices    = new List <int>();

            for (int treeIndex = 0; treeIndex < trees.Count; treeIndex++)
            {
                Vector3 position = trees[treeIndex].m_Position;
                Vector3 scale    = trees[treeIndex].m_Scale;
                float   rot      = trees[treeIndex].m_Rotation.eulerAngles.y * Mathf.Deg2Rad;

                // Offset world position, by the grounding factor
                Vector3 instancePos = position;

                // Don't use this, but offset in shader, so that we can have that correct hue
                // instancePos.y += data.m_Size.z;

                // Scale by the world scale too so that we don't have to do an extra multip
                Vector3 instanceScale = scale;
                instanceScale.Scale(worldScale);

                // Add the world and scale data
                for (int index = 0; index < 4; index++)
                {
                    Vector4 posAndRot = instancePos;
                    posAndRot.w = rot;

                    m_TempWorldPositions.Add(posAndRot);
                    m_TempWorldScales.Add(instanceScale);
                }

                // Add stanard quad data
                m_TempQuadVertices.AddRange(m_SystemQuadVertices);
                m_TempQuadTangents.AddRange(m_SystemQuadTangents);
                m_TempQuadNormals.AddRange(m_SystemQuadNormals);

                // Calculate triangle indixes
                m_TempQuadIndices.AddRange(originalTriangles);
                for (int triIndex = 0; triIndex < 6; triIndex++)
                {
                    // Just add to the triangles the existing triangles + the new indices
                    m_TempQuadIndices[triIndex + 6 * treeIndex] = originalTriangles[triIndex] + 4 * treeIndex;
                }
            }

            treeMesh.Clear();

            // Set standard data
            treeMesh.SetVertices(m_TempQuadVertices);
            treeMesh.SetNormals(m_TempQuadNormals);
            treeMesh.SetTangents(m_TempQuadTangents);

            // Set the custom data
            treeMesh.SetUVs(1, m_TempWorldPositions);
            treeMesh.SetUVs(2, m_TempWorldScales);

            // Set triangles and do not calculate bounds
            treeMesh.SetTriangles(m_TempQuadIndices, 0, false);

            // Set the manually calculated bounds
            treeMesh.bounds = bounds;

            treeMesh.UploadMeshData(true);

            // Set the mesh
            filter.mesh = treeMesh;

            if (addLodGroup)
            {
                // Add the mesh' lod group
                LODGroup group = meshObj.AddComponent <LODGroup>();
                group.animateCrossFading = false;

                if (animatedCrossFade)
                {
                    group.fadeMode           = LODFadeMode.CrossFade;
                    group.animateCrossFading = true;
                }
                else
                {
                    group.fadeMode           = LODFadeMode.None;
                    group.animateCrossFading = false;
                }

                group.SetLODs(new LOD[] { new LOD(screenFadeSize, new Renderer[] { rend }) });
                group.RecalculateBounds();
            }

#if UNITY_EDITOR
            UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(meshObj.scene);
#endif
        }
Esempio n. 5
0
        /** To be used at runtime. Will create all we need for SpeedTree wind and other data */
        public static void BuildDataRuntime(FoliagePainter painter, FoliageType type, Transform attachmentPoint)
        {
            FoliageLog.Assert(type.IsRuntimeInitialized == false, "Runtime data already initialized!");

            // Everyone has MPB's
            type.m_RuntimeData.m_TypeMPB = new MaterialPropertyBlock();

            if (type.IsSpeedTreeType)
            {
                // Create the glued mesh
                var speedData = type.m_RuntimeData.m_SpeedTreeData;
                FoliageLog.Assert(speedData != null, "Speed data must already be partly generated if we have a SpeedTree!");

                // Get the lod and instnatiade the one with the least instructions
                LOD[] lods = type.m_Prefab.GetComponent <LODGroup>().GetLODs();

                for (int i = lods.Length - 1; i >= 0; i--)
                {
                    if (lods[i].renderers[0].GetComponent <BillboardRenderer>() != null)
                    {
                        continue;
                    }
                    else
                    {
                        // Init the object with the lowest possible LOD
                        speedData.m_SpeedTreeWindObject = GameObject.Instantiate(lods[i].renderers[0].gameObject, attachmentPoint);
                        break;
                    }
                }

                // Set the data
                speedData.m_SpeedTreeWindObjectMesh = speedData.m_SpeedTreeWindObject.GetComponentInChildren <MeshRenderer>();

                // Set the NULL invisible shader
                Shader nullShader = painter.GetShaderNull();
                FoliageLog.Assert(nullShader, "Null shader not found! Make sure it exists and that it compiled!");

                // Set the invisible null shader, we only need the wind
                Material[] mats = speedData.m_SpeedTreeWindObjectMesh.materials;
                for (int i = 0; i < mats.Length; i++)
                {
                    mats[i].shader = nullShader;
                }

                // Attach the wind object. Ensures that we are enabled.
                speedData.m_SpeedTreeWindObject.AddComponent <FoliageWindTreeWind>();

                speedData.m_SpeedTreeWindObjectMesh.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;

                speedData.m_SpeedTreeWindObject.transform.SetParent(attachmentPoint, false);
                speedData.m_SpeedTreeWindObject.transform.localPosition = new Vector3(0, 0, 0);

                MeshFilter m = speedData.m_SpeedTreeWindObject.GetComponentInChildren <MeshFilter>();

                Bounds b = m.mesh.bounds;
                b.Expand(4.5f);

                m.mesh.bounds = b;
                speedData.m_SpeedTreeWindObject.GetComponentInChildren <MeshFilter>().mesh = m.mesh;
            }

            // Not used here, since at edit time we already created the materials

            /*
             * if (type.Type == EFoliageType.SPEEDTREE_GRASS)
             * {
             *  Shader shader = painter.GetShaderGrass();
             *  FoliageLog.Assert(shader, "Could not find shader: Critias/SpeedTree_Grass! Make sure that it is added to the project and that it compiled!");
             *
             *  FoliageTypeLODGrass lodGrass = type.m_RuntimeData.m_LODDataGrass;
             *
             *  // Override the material at runtime
             *  // lodGrass.m_Material = new Material(lodGrass.m_Material);
             *  // lodGrass.m_Material.shader = shader;
             *
             *  // Enable it for instancing
             *  // lodGrass.m_Material.enableInstancing = true;
             * }
             * else if(type.Type == EFoliageType.SPEEDTREE_TREE || type.Type == EFoliageType.SPEEDTREE_TREE_BILLBOARD)
             * {
             *  Shader shader = painter.GetShaderTreeMaster();
             *  FoliageLog.Assert(shader, "Could not find shader: Critias/SpeedTree_Master! Make sure that it is added to the project and that it compiled!");
             *
             *  FoliageTypeLODTree[] lodTree = type.m_RuntimeData.m_LODDataTree;
             *
             *  for(int i = 0; i < lodTree.Length; i++)
             *  {
             *      FoliageTypeLODTree tree = lodTree[i];
             *
             *      Material[] mats = tree.m_Materials;
             *
             *      for(int m = 0; m < mats.Length; m++)
             *      {
             *          // Set the new material
             *          //mats[m] = new Material(mats[m]);
             *          //mats[m].shader = shader;
             *
             *          // Enable instancing
             *          //mats[m].enableInstancing = true;
             *      }
             *
             *      tree.m_Materials = mats;
             *  }
             * }
             */

            // We did initialize
            type.IsRuntimeInitialized = true;
        }
Esempio n. 6
0
        /** To be used at edit-time */
        public static void BuildDataPartialEditTime(FoliagePainter painter, FoliageType type)
        {
            GameObject prefab = type.m_Prefab;

            // Update the type
            type.Type = type.Type;

            FoliageLog.Assert(prefab != null, "Null foliage prefab!");

            if (type.m_RuntimeData == null)
            {
                type.m_RuntimeData = new FoliageTypeRuntimeData();
            }

            FoliageTypeRuntimeData runtime = type.m_RuntimeData;

            List <Material> checkMaterials = new List <Material>();

            // Init the SpeedTree data
            if (type.IsSpeedTreeType)
            {
                if (runtime.m_SpeedTreeData == null)
                {
                    runtime.m_SpeedTreeData = new FoliageTypeSpeedTreeData();
                }
            }

            if (type.IsGrassType)
            {
                // Build the data universally for all grass types
                if (runtime.m_LODDataGrass == null)
                {
                    runtime.m_LODDataGrass = new FoliageTypeLODGrass();
                }

                runtime.m_LODDataGrass.m_Mesh     = prefab.GetComponentInChildren <MeshFilter>().sharedMesh;
                runtime.m_LODDataGrass.m_Material = prefab.GetComponentInChildren <MeshRenderer>().sharedMaterial;

                checkMaterials.Add(runtime.m_LODDataGrass.m_Material);

                FoliageLog.Assert(runtime.m_LODDataGrass.m_Mesh != null, "Could not find mesh for type: " + prefab.name + ". Make sure that is has at least one mesh and one material");
                FoliageLog.Assert(runtime.m_LODDataGrass.m_Material != null, "Could not find material for type: " + prefab.name + ". Make sure that is has at least one mesh and one material");
            }
            else
            {
                LODGroup group = prefab.GetComponent <LODGroup>();

                if (group == null)
                {
                    FoliageLog.w("Detected tree: " + prefab.name + " without a lod group. Are you sure that you require a tree for this?");

                    if (runtime.m_LODDataTree == null || runtime.m_LODDataTree.Length == 0)
                    {
                        runtime.m_LODDataTree = new FoliageTypeLODTree[1];
                    }

                    runtime.m_LODDataTree[0]               = new FoliageTypeLODTree();
                    runtime.m_LODDataTree[0].m_Mesh        = prefab.GetComponentInChildren <MeshFilter>().sharedMesh;
                    runtime.m_LODDataTree[0].m_Materials   = prefab.GetComponentInChildren <MeshRenderer>().sharedMaterials;
                    runtime.m_LODDataTree[0].m_EndDistance = type.m_RenderInfo.m_MaxDistance;

                    checkMaterials.AddRange(runtime.m_LODDataTree[0].m_Materials);
                }
                else
                {
                    List <FoliageTypeLODTree> treeLods = new List <FoliageTypeLODTree>(group.lodCount);
                    LOD[] lods = group.GetLODs();

                    for (int i = 0; i < group.lodCount; i++)
                    {
                        if (lods[i].renderers[0].gameObject.GetComponent <BillboardRenderer>() != null)
                        {
                            // Extract the billboard data
                            var speedData = runtime.m_SpeedTreeData;
                            FoliageWindTreeUtilities.ExtractBillboardData(lods[i].renderers[0].gameObject.GetComponent <BillboardRenderer>(), speedData);

                            continue;
                        }

                        FoliageTypeLODTree treeLod = new FoliageTypeLODTree();

                        MeshRenderer rend   = lods[i].renderers[0].gameObject.GetComponent <MeshRenderer>();
                        MeshFilter   filter = lods[i].renderers[0].gameObject.GetComponent <MeshFilter>();

                        treeLod.m_Mesh      = filter.sharedMesh;
                        treeLod.m_Materials = rend.sharedMaterials;

                        checkMaterials.AddRange(rend.sharedMaterials);

                        treeLods.Add(treeLod);
                    }

                    runtime.m_LODDataTree = treeLods.ToArray();

                    // Update the LOD distances
                    UpdateDistancesLOD(runtime.m_LODDataTree, lods, type.m_RenderInfo.m_MaxDistance, type.IsSpeedTreeType);
                }
            }

            if (checkMaterials.Count > 0)
            {
                if (type.IsSpeedTreeType)
                {
                    if (type.m_RenderInfo.m_Hue == new Color(0, 0, 0, 0))
                    {
                        type.m_RenderInfo.m_Hue = checkMaterials[0].GetColor("_HueVariation");
                    }

                    if (type.m_RenderInfo.m_Color == new Color(0, 0, 0, 0))
                    {
                        type.m_RenderInfo.m_Color = checkMaterials[0].GetColor("_Color");
                    }
                }

                for (int i = 0; i < checkMaterials.Count; i++)
                {
                    if (checkMaterials[i].enableInstancing == false)
                    {
                        checkMaterials[i].enableInstancing = true;
                        FoliageLog.w("Material: [" + checkMaterials[i].name + "] did not had instancing enabled! We enabled it!");
                    }
                }
            }

            // Moved the build at partial edit-time
            if (type.Type == EFoliageType.SPEEDTREE_GRASS)
            {
                Shader shader = painter.GetShaderGrass();
                FoliageLog.Assert(shader, "Could not find shader: Critias/SpeedTree_Grass! Make sure that it is added to the project and that it compiled!");

                FoliageTypeLODGrass lodGrass = type.m_RuntimeData.m_LODDataGrass;

                // Override the material at runtime
                lodGrass.m_Material        = new Material(lodGrass.m_Material);
                lodGrass.m_Material.shader = shader;

                // Enable it for instancing
                lodGrass.m_Material.enableInstancing = true;
            }
            else if (type.Type == EFoliageType.SPEEDTREE_TREE || type.Type == EFoliageType.SPEEDTREE_TREE_BILLBOARD)
            {
                Shader shader = painter.GetShaderTreeMaster();
                FoliageLog.Assert(shader, "Could not find shader: Critias/SpeedTree_Master! Make sure that it is added to the project and that it compiled!");

                FoliageTypeLODTree[] lodTree = type.m_RuntimeData.m_LODDataTree;

                for (int i = 0; i < lodTree.Length; i++)
                {
                    FoliageTypeLODTree tree = lodTree[i];

                    Material[] mats = tree.m_Materials;

                    for (int m = 0; m < mats.Length; m++)
                    {
                        // Set the new material
                        mats[m]        = new Material(mats[m]);
                        mats[m].shader = shader;

                        // Enable instancing
                        mats[m].enableInstancing = true;
                    }

                    tree.m_Materials = mats;
                }
            }

            // Set the materials the values for enabling the bend stuff if we have it
            if (type.IsGrassType)
            {
                if (type.m_EnableBend)
                {
                    type.m_RuntimeData.m_LODDataGrass.m_Material.EnableKeyword("CRITIAS_DISTANCE_BEND");
                }
                else
                {
                    type.m_RuntimeData.m_LODDataGrass.m_Material.DisableKeyword("CRITIAS_DISTANCE_BEND");
                }
            }
        }
        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;
            }
        }
        private GameObject GetColliderForPrototype(int hash)
        {
            FoliageType data = m_FoliageTypes[hash];

            if (data.m_EnableCollision == false)
            {
                return(null);
            }

            if (m_Cache.ContainsKey(hash) == false)
            {
                // If we don't contain the key create and add it

                // If we don't have a tree with a collider, like a bush or something, just add a null mapping
                if (data.m_Prefab.GetComponentInChildren <Collider>() == null)
                {
                    m_Cache.Add(hash, null);
                    return(null);
                }
                else
                {
                    // Create the collider prototype and remove all it's mesh renderers and stuff
                    GameObject colliderPrototype = Instantiate(data.m_Prefab, m_ColliderHolder.transform);
                    colliderPrototype.name = "ColliderPrototype_" + data.m_Prefab.name;

                    // Clear the lod group
                    LODGroup lod = colliderPrototype.GetComponent <LODGroup>();
                    if (lod)
                    {
                        DestroyImmediate(lod);
                    }

                    // Clear any owned GObjects that don't have colliders
                    for (int i = colliderPrototype.transform.childCount - 1; i >= 0; i--)
                    {
                        GameObject owned = colliderPrototype.transform.GetChild(i).gameObject;

                        if (owned.GetComponent <Collider>() == null)
                        {
                            DestroyImmediate(owned);
                        }
                    }

                    Component[] components = colliderPrototype.GetComponentsInChildren <Component>();

                    // Delete all non-colliders
                    for (int i = 0; i < components.Length; i++)
                    {
                        if ((components[i] is Collider) == false && (components[i] is Transform) == false)
                        {
                            DestroyImmediate(components[i]);
                        }
                    }

                    // Deactivate it
                    colliderPrototype.SetActive(false);

                    // Create the cache entry
                    CollisionCache cache = new CollisionCache(colliderPrototype, m_ColliderHolder);

                    // Add the collision cache to our dictionary
                    m_Cache.Add(hash, cache);
                    return(cache.RetrieveInstance());
                }
            }
            else
            {
                var cache = m_Cache[hash];

                // We contain the cache, just retrieve an object
                if (cache != null)
                {
                    return(m_Cache[hash].RetrieveInstance());
                }
                else
                {
                    return(null);
                }
            }
        }
        private static void ExtractDetailsFromTerrain(FoliagePainter painter, Terrain terrain, List <FoliageDetailExtracterMapping> mappings, bool disable, bool delete)
        {
            string label = FoliageGlobals.LABEL_TERRAIN_DETAILS_EXTRACTED + terrain.name;

            FoliagePainterEditTime edit = painter.GetEditTime;

            int detailMapSizeW = terrain.terrainData.detailWidth;
            int detailMapSizeH = terrain.terrainData.detailHeight;

            float patchSizeW = terrain.terrainData.size.x / detailMapSizeW;
            float patchSizeH = terrain.terrainData.size.z / detailMapSizeH;

            int extracted = 0;

            // Extract the types for all the mapping
            for (int m = 0; m < mappings.Count; m++)
            {
                int   layer   = mappings[m].m_DetailLayer;
                int   mapping = mappings[m].m_FoliageTypeHash;
                float density = mappings[m].m_ExtractedDensity;

                FoliageLog.Assert(painter.HasFoliageType(mapping), "Must have foliage hash!!");

                // Get the foliage type
                FoliageType     type  = painter.GetFoliageTypeByHash(mapping);
                DetailPrototype proto = terrain.terrainData.detailPrototypes[layer];

                // If we should align to the surface
                bool    followTerrainNormal = type.m_PaintInfo.m_SurfaceAlign;
                Vector2 alignPercentage     = type.m_PaintInfo.m_SurfaceAlignInfluence;

                // Get the terrain data
                int[,] data = terrain.terrainData.GetDetailLayer(0, 0, detailMapSizeW, detailMapSizeH, layer);

                // Iterate data
                for (int i = 0; i < detailMapSizeH; i++)
                {
                    for (int j = 0; j < detailMapSizeW; j++)
                    {
                        // j,i not i,j
                        int count = data[j, i];

                        if (count > 0)
                        {
                            // Minimum 1 never 0
                            count = Mathf.Clamp(Mathf.CeilToInt(count * density), 1, count + 1);

                            // Map from local space cell space to local terrain space
                            Vector2 cellMin = new Vector2(patchSizeW * i, patchSizeH * j);
                            Vector2 cellMax = cellMin + new Vector2(patchSizeW, patchSizeH);

                            for (int d = 0; d < count; d++)
                            {
                                Vector3 randomInCell;
                                randomInCell.x = Random.Range(cellMin.x, cellMax.x);
                                randomInCell.z = Random.Range(cellMin.y, cellMax.y);
                                randomInCell.y = 0;

                                randomInCell = FoliageTerrainUtilities.TerrainLocalToTerrainNormalizedPos(randomInCell, terrain);
                                float y = FoliageTerrainUtilities.TerrainHeight(randomInCell, terrain);

                                // Build the rotation
                                Quaternion rotation = Quaternion.identity;

                                if (followTerrainNormal)
                                {
                                    Quaternion slopeOrientation = Quaternion.LookRotation(FoliageTerrainUtilities.TerrainNormal(randomInCell, terrain)) * Quaternion.Euler(90, 0, 0);

                                    // How much we orient towards the slope
                                    rotation = Quaternion.Slerp(rotation, slopeOrientation,
                                                                Random.Range(alignPercentage.x, alignPercentage.y));
                                }

                                // Rotate around the Y axis
                                rotation *= Quaternion.Euler(0, Random.Range(0, 360), 0);

                                // Random in cell in world position
                                randomInCell   = FoliageTerrainUtilities.TerrainNormalizedToWorldPos(randomInCell, terrain);
                                randomInCell.y = y;

                                Vector3 scale;
                                float   x = Random.Range(proto.minWidth, proto.maxWidth);
                                scale.x = x;
                                scale.z = x;
                                scale.y = Random.Range(proto.minHeight, proto.maxHeight);

                                // Construct a foliage instance based on the foliage type data
                                FoliageInstance instance = new FoliageInstance();

                                // Build the foliage data based on the type
                                instance.m_Position = randomInCell;
                                instance.m_Rotation = rotation;
                                instance.m_Scale    = scale;

                                // Instantiate at a random pos in that cell
                                edit.AddFoliageInstance(mapping, instance, label);
                                extracted++;
                            }
                        }

                        // If we should delete
                        if (delete)
                        {
                            data[j, i] = 0;
                        }
                    }
                } // End detail array iteration

                // If we deleted set the new detail layer data
                if (delete)
                {
                    terrain.terrainData.SetDetailLayer(0, 0, layer, data);
                }
            } // End types iteration

            // If we should disable the trees and foliage draw
            if (disable)
            {
                terrain.drawTreesAndFoliage = false;
            }

            // If we deleted anything we need to save
            if (delete)
            {
                UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(terrain.gameObject.scene);
            }

            FoliageLog.i("Extracted details: " + extracted + " from: " + terrain.name);
        }
        private static void ExtractFromTerrain(FoliagePainter painterRaw, FoliagePainterEditTime painter, Terrain terrain, bool autoExtract, bool disable, bool delete)
        {
            string label = FoliageGlobals.LABEL_TERRAIN_EXTRACTED + terrain.name;

            // Ensure that we have all the required foliage types
            List <TreeInstance> terrainTreeInstances = new List <TreeInstance>(terrain.terrainData.treeInstances);

            TreePrototype[] terrainTreePrototypes = terrain.terrainData.treePrototypes;

            // Attempt to build the prefab's that we don't have
            if (autoExtract)
            {
                AutoExtractTypes(painter, terrain);
            }

            int extracted = 0;

            for (int i = terrainTreeInstances.Count - 1; i >= 0; i--)
            {
                GameObject proto = terrainTreePrototypes[terrainTreeInstances[i].prototypeIndex].prefab;

                if (painter.HasFoliageType(proto) == false)
                {
                    continue;
                }

                int         hash = painter.GetFoliageTypeHash(proto);
                FoliageType type = painterRaw.GetFoliageTypeByHash(hash);

                FoliageInstance instance = new FoliageInstance();

                // Populate the data

                // Get the world data
                float   YOffset       = Random.Range(type.m_PaintInfo.m_YOffset.x, type.m_PaintInfo.m_YOffset.y);
                Vector3 worldPosition = FoliageTerrainUtilities.TerrainNormalizedToWorldPos(terrainTreeInstances[i].position, terrain) + new Vector3(0, YOffset, 0); // YOffset too
                Vector3 worldScale    = new Vector3(terrainTreeInstances[i].widthScale, terrainTreeInstances[i].heightScale, terrainTreeInstances[i].widthScale);
                Vector3 worldRotation = new Vector3(0, terrainTreeInstances[i].rotation * Mathf.Rad2Deg, 0);

                instance.m_Position = worldPosition;
                instance.m_Scale    = worldScale;
                instance.m_Rotation = Quaternion.Euler(worldRotation.x, worldRotation.y, worldRotation.z);

                // Add the foliage instance
                painter.AddFoliageInstance(hash, instance, label);
                extracted++;

                // Delete the instance from the terrain if we have to
                if (delete)
                {
                    terrainTreeInstances.RemoveAt(i);
                }
            }

            if (disable)
            {
                terrain.drawTreesAndFoliage = false;
            }

            // If we should delete then delete the instance
            if (delete)
            {
                terrain.terrainData.treeInstances = terrainTreeInstances.ToArray();
                UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(terrain.gameObject.scene);
            }

            FoliageLog.i("Extracted objects: " + extracted + " from: " + terrain.name);
        }