private static void AutoExtractTypes(FoliagePainterEditTime painter, Terrain terrain)
        {
            TreeInstance[]  terrainTreeInstances  = terrain.terrainData.treeInstances;
            TreePrototype[] terrainTreePrototypes = terrain.terrainData.treePrototypes;

            for (int i = 0; i < terrainTreePrototypes.Length; i++)
            {
                TreePrototype proto  = terrainTreePrototypes[i];
                GameObject    prefab = proto.prefab;

                // See if we already have it
                if (painter.HasFoliageType(prefab) == false)
                {
                    // Attempt to add it
                    FoliageTypeBuilder builder;
                    bool anyError;

                    FoliageUtilitiesEditor.ConfigurePrefab(prefab, out builder, out anyError);

                    if (anyError == false)
                    {
                        painter.AddFoliageType(builder);
                    }
                    else
                    {
                        FoliageLog.e("Could not add foliage type: " + prefab.name + " due to error. Check the log for more info.");
                    }
                }
            }
        }
        /**
         * Removes all the foliage instances of the type from the data.
         */
        public void RemoveType(int typeHash)
        {
            bool anyRemoved  = false;
            int  cellsPurged = 0;

            foreach (FoliageCellData data in m_FoliageData.Values)
            {
                if (data.m_TypeHashLocationsEditor.Remove(typeHash))
                {
                    anyRemoved = true;
                    cellsPurged++;
                }

                foreach (FoliageCellSubdividedData subdivData in data.m_FoliageDataSubdivided.Values)
                {
                    if (subdivData.m_TypeHashLocationsEditor.Remove(typeHash))
                    {
                        anyRemoved = true;
                        cellsPurged++;
                    }
                }
            }

            if (anyRemoved)
            {
                FoliageLog.i("Removed type instance from: " + cellsPurged + " cells.");
                RemoveEmptyData();

                // Recalc the bounds
                RecalculateBoundsAfterRemove();
            }
        }
Beispiel #3
0
        /**
         * Update the LOD min/max distances for an object.
         *
         * @param isSpeedTree
         *          If this is set to true, then we will check if we have a billboard renderer to an LOD
         *          and we will skip it in case that we have one
         */
        public static void UpdateDistancesLOD(FoliageTypeLODTree[] treeLods, LOD[] groupLods, float maxDistance, bool isSpeedTree)
        {
            if (groupLods != null && groupLods.Length > 0)
            {
                FoliageLog.Assert(groupLods.Length >= treeLods.Length, "Must have same or more lods than the tree lods!");

                for (int i = 0; i < treeLods.Length; i++)
                {
                    // If we are a speedtree check if we have a billboard renderer
                    if (isSpeedTree && groupLods[i].renderers[0].GetComponent <BillboardRenderer>() != null)
                    {
                        continue;
                    }

                    FoliageTypeLODTree lodTree = treeLods[i];
                    LOD lodGroupCurrent        = groupLods[i];

                    lodTree.m_EndDistance = ((1.0f - lodGroupCurrent.screenRelativeTransitionHeight) * maxDistance);
                }
            }
            else
            {
                treeLods[0].m_EndDistance = maxDistance;
            }
        }
        /**
         * Removes any empty data from the list. Called before disk writing so that we're sure that
         * we are not going to test for any empty cells at runtime.
         */
        public void RemoveEmptyData()
        {
            HashSet <int> emptyCells       = null;
            HashSet <int> emptyCellsSubdiv = null;

            // Iterate cells
            foreach (var cell in m_FoliageData)
            {
                RemoveEmptyTypeDataCell(cell.Value);
                if (IsCellEmpty(cell.Value))
                {
                    if (emptyCells == null)
                    {
                        emptyCells = new HashSet <int>();
                    }

                    emptyCells.Add(cell.Key);
                }

                if (emptyCellsSubdiv != null)
                {
                    emptyCellsSubdiv.Clear();
                }

                // Iterate sub-cells too
                foreach (var cellSubdiv in cell.Value.m_FoliageDataSubdivided)
                {
                    RemoveEmptyTypeDataCellSubdivided(cellSubdiv.Value);
                    if (IsSubCellEmpty(cellSubdiv.Value))
                    {
                        if (emptyCellsSubdiv == null)
                        {
                            emptyCellsSubdiv = new HashSet <int>();
                        }

                        emptyCellsSubdiv.Add(cellSubdiv.Key);
                    }
                }

                if (emptyCellsSubdiv != null && emptyCellsSubdiv.Count > 0)
                {
                    foreach (int key in emptyCellsSubdiv)
                    {
                        cell.Value.m_FoliageDataSubdivided.Remove(key);
                    }
                }
            }

            // Remove the empty cells
            if (emptyCells != null)
            {
                foreach (int key in emptyCells)
                {
                    m_FoliageData.Remove(key);
                }
            }

            FoliageLog.i("Removed: " + (emptyCells != null ? emptyCells.Count : 0) + " empty cells and: " + (emptyCellsSubdiv != null ? emptyCellsSubdiv.Count : 0) + " empty subdivided cells.");
        }
        private void Awake()
        {
            // Extract the planes methods
            MethodInfo info = typeof(GeometryUtility).GetMethod("Internal_ExtractPlanes", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Plane[]), typeof(Matrix4x4) }, null);

            ExtractPlanes = Delegate.CreateDelegate(typeof(Action <Plane[], Matrix4x4>), info) as Action <Plane[], Matrix4x4>;

            // Matrices init
            {
                m_MtxLODTemp       = new Matrix4x4[FoliageGlobals.RENDER_MAX_LOD_COUNT][];
                m_MtxLODTempShadow = new Matrix4x4[FoliageGlobals.RENDER_BATCH_SIZE][];

                // Fill the matrices with the identity
                for (int i = 0; i < FoliageGlobals.RENDER_MAX_LOD_COUNT; i++)
                {
                    m_MtxLODTemp[i]       = new Matrix4x4[FoliageGlobals.RENDER_BATCH_SIZE];
                    m_MtxLODTempShadow[i] = new Matrix4x4[FoliageGlobals.RENDER_BATCH_SIZE];

                    for (int mtx = 0; mtx < FoliageGlobals.RENDER_BATCH_SIZE; mtx++)
                    {
                        m_MtxLODTemp[i][mtx]       = Matrix4x4.identity;
                        m_MtxLODTempShadow[i][mtx] = Matrix4x4.identity;
                    }
                }
            }

            // Set the system data
            if (!m_Settings.m_WindTransform)
            {
                FoliageLog.i("Wind transform not found, defaulting to 'Camera.main.transform'!");
                m_Settings.m_WindTransform = Camera.main.transform;
            }

            if (!m_Settings.m_UsedCameraCulling)
            {
                FoliageLog.i("Culling camera not found, defaulting to 'Camera.main'!");
                m_Settings.m_UsedCameraCulling = Camera.main;
            }

            // Get shader data

            // Used for maximum distance
            m_ShaderIDCritiasFoliageDistance    = Shader.PropertyToID("CRITIAS_MaxFoliageTypeDistance");
            m_ShaderIDCritiasFoliageDistanceSqr = Shader.PropertyToID("CRITIAS_MaxFoliageTypeDistanceSqr");

            // Used for LOD distance
            m_ShaderIDCritiasFoliageLOD    = Shader.PropertyToID("CRITIAS_FoliageMaxDistanceLOD");
            m_ShaderIDCritiasFoliageLODSqr = Shader.PropertyToID("CRITIAS_FoliageMaxDistanceLODSqr");

            // Used for the indirect buffer
            m_ShaderIDCritiasInstanceBuffer = Shader.PropertyToID("CRITIAS_InstancePositionBuffer");

            // Used for the positions
            m_ShaderIDCritiasBendPosition = Shader.PropertyToID("CRITIAS_Bend_Position");
            m_ShaderIDCritiasBendDistance = Shader.PropertyToID("CRITIAS_Bend_Distance");
            m_ShaderIDCritiasBendScale    = Shader.PropertyToID("CRITIAS_Bend_Scale");
        }
        private static void ExtractFromRootObject(FoliagePainterEditTime painter, GameObject root, bool disable, bool delete)
        {
            FoliageLog.Assert(root.transform.parent == null, "Must have root object!");

            List <GameObject> foliageInstances = new List <GameObject>();

            RecursivelyExtract(root, foliageInstances);

            int extracted = 0;

            // Process them and add them
            for (int i = foliageInstances.Count - 1; i >= 0; i--)
            {
                GameObject proto = foliageInstances[i];

                GameObject protoPrefab = PrefabUtility.GetPrefabParent(proto) as GameObject;

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

                int             hash     = painter.GetFoliageTypeHash(protoPrefab);
                FoliageInstance instance = new FoliageInstance();

                // Populate the data

                // Get the world data
                Vector3    worldPosition = proto.transform.position;
                Vector3    worldScale    = proto.transform.localScale;
                Quaternion worldRotation = proto.transform.rotation;

                instance.m_Position = worldPosition;
                instance.m_Scale    = worldScale;
                instance.m_Rotation = worldRotation;

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

                // Auto disable
                if (disable)
                {
                    proto.SetActive(false);
                }

                // Auto delete
                if (delete)
                {
                    GameObject.DestroyImmediate(proto);
                }
            }

            FoliageLog.i("Extracted objects: " + extracted + " from: " + root.name);
        }
        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);
        }
Beispiel #8
0
        public static float GetMaxDistance(EFoliageType type)
        {
            switch (type)
            {
            case EFoliageType.OTHER_GRASS:
            case EFoliageType.SPEEDTREE_GRASS:
                return(FOLIAGE_MAX_GRASS_DISTANCE);

            case EFoliageType.OTHER_TREE:
            case EFoliageType.SPEEDTREE_TREE:
            case EFoliageType.SPEEDTREE_TREE_BILLBOARD:
                return(FOLIAGE_MAX_TREE_DISTANCE);

            default:
                FoliageLog.Assert(false, "Wrong type!");
                return(FOLIAGE_MAX_GRASS_DISTANCE);
            }
        }
Beispiel #9
0
        public static float ClampDistance(EFoliageType type, float maxViewDistance)
        {
            switch (type)
            {
            case EFoliageType.OTHER_GRASS:
            case EFoliageType.SPEEDTREE_GRASS:
                return(Mathf.Clamp(maxViewDistance, 0, FOLIAGE_MAX_GRASS_DISTANCE));

            case EFoliageType.OTHER_TREE:
            case EFoliageType.SPEEDTREE_TREE:
            case EFoliageType.SPEEDTREE_TREE_BILLBOARD:
                return(Mathf.Clamp(maxViewDistance, 0, FOLIAGE_MAX_TREE_DISTANCE));

            default:
                FoliageLog.Assert(false, "Wrong type!");
                return(Mathf.Clamp(maxViewDistance, 0, FOLIAGE_MAX_GRASS_DISTANCE));
            }
        }
        /**
         * Update the foliage types.
         */
        public void UpdateFoliageTypes(List <FoliageType> foliageTypes)
        {
            m_MaxDistanceGrass = 0;
            m_MaxDistanceTree  = 0;
            m_MaxDistanceAll   = 0;

            m_FoliageTypes.Clear();
            foreach (FoliageType type in foliageTypes)
            {
                if (type.IsGrassType == true && type.m_RenderInfo.m_MaxDistance > m_MaxDistanceGrass)
                {
                    m_MaxDistanceGrass = type.m_RenderInfo.m_MaxDistance;
                }
                else if (type.IsGrassType == false && type.m_RenderInfo.m_MaxDistance > m_MaxDistanceTree)
                {
                    m_MaxDistanceTree = type.m_RenderInfo.m_MaxDistance;
                }

                m_FoliageTypes.Add(type.m_Hash, type);
            }

            m_FoliageTypesArray = foliageTypes.ToArray();

            // Update the maximum grass distance
            m_MaxDistanceGrass    = Mathf.Clamp(m_MaxDistanceGrass, 0, FoliageGlobals.FOLIAGE_MAX_GRASS_DISTANCE);
            m_MaxDistanceGrassSqr = m_MaxDistanceGrass * m_MaxDistanceGrass;

            m_MaxDistanceTree    = Mathf.Clamp(m_MaxDistanceTree, 0, FoliageGlobals.FOLIAGE_MAX_TREE_DISTANCE);
            m_MaxDistanceTreeSqr = m_MaxDistanceTree * m_MaxDistanceTree;

            m_MaxDistanceAll    = Mathf.Max(m_MaxDistanceGrass, m_MaxDistanceTree);
            m_MaxDistanceAllSqr = m_MaxDistanceAll * m_MaxDistanceAll;

            // Cell recursion count based on the maximum value of all values
            m_CellNeighborCount = Mathf.CeilToInt(m_MaxDistanceAll / FoliageGlobals.CELL_SIZE);

            FoliageLog.i("Neighbor cell count: " + m_CellNeighborCount);
        }
Beispiel #11
0
        void OnWizardCreate()
        {
            FoliageLog.d("On wizard create!");

            List <int> hashes = new List <int>();

            for (int i = 0; i < m_Types.Count; i++)
            {
                int hash = m_Types[i].m_Hash;
                if (m_Extracting[i] && m_Painter.HasFoliageType(hash) && hashes.Contains(hash) == false)
                {
                    hashes.Add(hash);
                }
            }

            if (hashes.Count > 0)
            {
                m_Callback(hashes.ToArray());
            }
            else
            {
                FoliageLog.w("Could not extract anything from the list!");
            }
        }
        void OnWizardCreate()
        {
            FoliageLog.d("On wizard create!");

            // Build the unique stuff
            List <GameObject> uniq = new List <GameObject>();

            for (int i = 0; i < m_Extract.Count; i++)
            {
                if (m_Extract[i] != null && uniq.Contains(m_Extract[i]) == false)
                {
                    uniq.Add(m_Extract[i]);
                }
            }

            if (uniq.Count > 0)
            {
                m_Callback(uniq, m_AutoExtractPrototypes, m_DisableAfterExtraction, m_DeleteAfterExtraction);
            }
            else
            {
                FoliageLog.w("Could not extract anything from the list!");
            }
        }
Beispiel #13
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");
                }
            }
        }
        void Update()
        {
#if UNITY_EDITOR
            FoliageLog.Assert(m_FoliageData != null);
#endif
            // Camera used for culling
            m_CurrentFrameCameraCull = m_Settings.m_UsedCameraCulling;

            // Camera used for drawing
            m_CurrentFrameCameraDraw = m_Settings.m_UsedCameraDrawing;
            m_CurrentFrameLayer      = LayerMask.NameToLayer(m_Settings.m_UsedLayer);

            // Extract the planes
            ExtractPlanes(m_CameraPlanes, m_CurrentFrameCameraCull.projectionMatrix * m_CurrentFrameCameraCull.worldToCameraMatrix);

            // Set the position
            m_CurrentFrameCameraPosition = m_CurrentFrameCameraCull.transform.position;

            // Set the bend position
            m_CurrentFrameBendPosition = m_Settings.m_BendTransform != null ? m_Settings.m_BendTransform.position : m_CurrentFrameCameraPosition;

            // Current cell position
            currentCell.Set(m_CurrentFrameCameraPosition);

            m_CurrentFrameAllowIndirect = m_Settings.m_AllowDrawInstancedIndirect;

            m_DrawStats.Reset();

            // Copy the wind for SpeedTree types
            for (int i = 0; i < m_FoliageTypesArray.Length; i++)
            {
                if (m_FoliageTypesArray[i].IsSpeedTreeType)
                {
                    m_FoliageTypesArray[i].CopyBlock();
                }
            }

            bool  applyShadowCorrection       = m_Settings.m_ApplyShadowPoppingCorrection;
            float shadowCorrectionDistanceSqr = m_Settings.m_ShadowPoppingCorrection * m_Settings.m_ShadowPoppingCorrection;

            // We iterate only as many cells as we need
            FoliageCell.IterateNeighboring(currentCell, m_CellNeighborCount, (int hash) =>
            {
                FoliageCellDataRuntime data;

                if (m_FoliageData.m_FoliageData.TryGetValue(hash, out data))
                {
                    // If it is within distance and in the frustum
                    float distanceSqr = data.m_Bounds.SqrDistance(m_CurrentFrameCameraPosition);

                    // Check for the maximum distance
                    if (distanceSqr <= m_MaxDistanceAllSqr && GeometryUtility.TestPlanesAABB(m_CameraPlanes, data.m_Bounds))
                    {
                        // Process the big cells if we are withing the tree distance
                        if (distanceSqr <= m_MaxDistanceTreeSqr)
                        {
                            ProcessCellTree(data, distanceSqr, applyShadowCorrection, shadowCorrectionDistanceSqr, false);
                        }

                        // Process subdivided cells only if we have instancing enabled and only if it is within the distance proximity for grass
                        if (distanceSqr <= m_MaxDistanceGrassSqr && m_Settings.m_DrawInstanced)
                        {
                            ProcessCellGrass(data);
                        }

                        m_DrawStats.m_ProcessedCells++;
                    }
                    else if (distanceSqr <= shadowCorrectionDistanceSqr)
                    {
                        ProcessCellTree(data, distanceSqr, applyShadowCorrection, shadowCorrectionDistanceSqr, true);
                    }
                }
            });

#if UNITY_EDITOR
            if (Time.frameCount % 300 == 0)
            {
                FoliageLog.i(string.Format("Proc cells:{0} Proc subdiv cells: {1} Proc tree instances: {2} Proc draw calls: {3}",
                                           m_DrawStats.m_ProcessedCells,
                                           m_DrawStats.m_ProcessedCellsSubdiv,
                                           m_DrawStats.m_ProcessedInstances,
                                           m_DrawStats.m_ProcessedDrawCalls));
            }
#endif
        }
        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);
        }
        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);
        }
        /**
         * Rebuild the hierarchy that contains the type. Must be used when we make a type that was
         * a tree into a grass (therefore having to use the subdivided cells) or vice-versa.
         *
         * Will preserve labeling data.
         */
        public void RebuildType(int typeHash, bool subdivided)
        {
            Dictionary <string, List <FoliageInstance> > instances = new Dictionary <string, List <FoliageInstance> >();

            foreach (FoliageCellData data in m_FoliageData.Values)
            {
                if (data.m_TypeHashLocationsEditor.ContainsKey(typeHash))
                {
                    var labeledData = data.m_TypeHashLocationsEditor[typeHash];

                    // Get all the data from the cells
                    foreach (var labeled in labeledData)
                    {
                        string label = labeled.Key;

                        if (instances.ContainsKey(label) == false)
                        {
                            instances.Add(label, new List <FoliageInstance>());
                        }

                        instances[label].AddRange(labeled.Value);
                    }
                }

                foreach (FoliageCellSubdividedData subdivData in data.m_FoliageDataSubdivided.Values)
                {
                    if (subdivData.m_TypeHashLocationsEditor.ContainsKey(typeHash))
                    {
                        var labeledData = subdivData.m_TypeHashLocationsEditor[typeHash];

                        // Get all the data from the subdivided cells
                        foreach (var labeled in labeledData)
                        {
                            string label = labeled.Key;

                            if (instances.ContainsKey(label) == false)
                            {
                                instances.Add(label, new List <FoliageInstance>());
                            }

                            instances[label].AddRange(labeled.Value);
                        }
                    }
                }
            }

            if (instances.Count > 0)
            {
                // Clear the old added data
                RemoveType(typeHash);

                int count = 0;

                foreach (var labeledInstances in instances)
                {
                    string label = labeledInstances.Key;
                    List <FoliageInstance> inst = labeledInstances.Value;

                    count += inst.Count;

                    // Add back the instances
                    AddInstances(typeHash, inst, subdivided, label);
                }

                FoliageLog.i("Relocated: " + count + " instanced.");
            }
        }
        public static void ConfigurePrefab(GameObject foliage, out FoliageTypeBuilder outBuilder, out bool outAnyError)
        {
            PrefabType type = PrefabUtility.GetPrefabType(foliage);

            if (type != PrefabType.ModelPrefab && type != PrefabType.Prefab)
            {
                FoliageLog.e("The prefab type of: " + foliage.name + " is: '" + type + "'! Must be a 'ModelPrefab' or a 'Prefab'!");
                outAnyError = true;
            }

            string path = AssetDatabase.GetAssetPath(foliage);

            FoliageLog.i("Path of added foliage type: " + path);

            FoliageTypeBuilder builder = new FoliageTypeBuilder();

            // Build all it's required data
            builder.m_PaintInfo  = new FoliageTypePaintInfo();
            builder.m_RenderInfo = new FoliageTypeRenderInfo();

            bool anyWeirdGrassError = false;

            // Attempt to auto-configure the foliage for the easiest possible user interaction
            if (path.EndsWith(".spm"))
            {
                // We have a SpeedTree
                LODGroup lodGroup = foliage.GetComponent <LODGroup>();

                if (lodGroup != null)
                {
                    if (lodGroup.lodCount == 1)
                    {
                        FoliageLog.i("Detected a SpeedTree grass, since it has 1 LOD!");
                        builder.m_Type            = EFoliageType.SPEEDTREE_GRASS;
                        builder.m_EnableCollision = false;

                        if (foliage.GetComponentInChildren <MeshRenderer>() == null)
                        {
                            anyWeirdGrassError = true;
                            FoliageLog.e("SpeedTree grass without any MeshRenderer detected!");
                        }
                    }
                    else
                    {
                        FoliageLog.i("Detected a SpeedTree tree, since it has 1+ LODS!");
                        builder.m_Type            = EFoliageType.SPEEDTREE_TREE;
                        builder.m_EnableCollision = foliage.GetComponentInChildren <Collider>() != null ? true : false;

                        if (foliage.GetComponentInChildren <BillboardRenderer>() != null)
                        {
                            builder.m_Type = EFoliageType.SPEEDTREE_TREE_BILLBOARD;
                        }

                        if (foliage.GetComponentInChildren <MeshRenderer>() == null)
                        {
                            anyWeirdGrassError = true;
                            FoliageLog.e("SpeedTree tree without any MeshRenderer detected!");
                        }
                    }

                    // Set the bounds
                    builder.m_Bounds = lodGroup.GetLODs()[0].renderers[0].GetComponent <MeshFilter>().sharedMesh.bounds;
                }
                else
                {
                    FoliageLog.e("Weird, we have a SpeedTree without a lod group. Anything wrong here? Please fix.");

                    anyWeirdGrassError = true;
                    builder.m_Bounds   = foliage.GetComponentInChildren <MeshFilter>().sharedMesh.bounds;
                }
            }
            else
            {
                LODGroup lodGroup = foliage.GetComponent <LODGroup>();

                if (lodGroup != null && lodGroup.lodCount > 1)
                {
                    FoliageLog.i("Detected an object tree!");
                    builder.m_Type            = EFoliageType.OTHER_TREE;
                    builder.m_EnableCollision = foliage.GetComponentInChildren <Collider>() != null ? true : false;

                    if (foliage.GetComponentInChildren <MeshRenderer>() == null)
                    {
                        anyWeirdGrassError = true;
                        FoliageLog.e("Object tree without any MeshRenderer detected!");
                    }

                    // Set the bounds
                    builder.m_Bounds = lodGroup.GetLODs()[0].renderers[0].GetComponent <MeshFilter>().sharedMesh.bounds;
                }
                else
                {
                    FoliageLog.i("Detected an object grass!");
                    builder.m_Type            = EFoliageType.OTHER_GRASS;
                    builder.m_EnableCollision = false;

                    if (foliage.GetComponentInChildren <MeshRenderer>() == null)
                    {
                        anyWeirdGrassError = true;
                        FoliageLog.e("Object grass without any LOD group or MeshRenderer detected!");
                    }

                    // Set the bounds
                    builder.m_Bounds = foliage.GetComponentInChildren <MeshFilter>().sharedMesh.bounds;
                }
            }

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

            if (group != null && group.lodCount > 0)
            {
                LOD[] lods = group.GetLODs();

                for (int i = 0; i < lods.Length; i++)
                {
                    Renderer[] rends = lods[i].renderers;

                    if (rends == null || rends.Length != 1)
                    {
                        anyWeirdGrassError = true;
                        FoliageLog.e("Detected object with a lod without any renderers on it or with more than one renderer attached to it!");
                    }
                }
            }

            if (anyWeirdGrassError)
            {
                EditorUtility.DisplayDialog("Error", "Found error for foliage: " + foliage.name + "! Could not add to system! Check the log for more info!", "Ok");
                FoliageLog.e("Found error for foliage: " + foliage.name + "! Could not add to system!");
            }
            else
            {
                // Set the prefab
                builder.m_Prefab       = foliage;
                builder.m_PaintEnabled = true;

                switch (builder.m_Type)
                {
                case EFoliageType.OTHER_GRASS:
                case EFoliageType.SPEEDTREE_GRASS:
                    builder.m_RenderInfo.m_CastShadow  = false;
                    builder.m_RenderInfo.m_MaxDistance = 30;
                    break;

                case EFoliageType.OTHER_TREE:
                case EFoliageType.SPEEDTREE_TREE:
                case EFoliageType.SPEEDTREE_TREE_BILLBOARD:
                    builder.m_RenderInfo.m_CastShadow  = true;
                    builder.m_RenderInfo.m_MaxDistance = 100;
                    break;
                }
            }

            switch (builder.m_Type)
            {
            case EFoliageType.OTHER_GRASS:
            case EFoliageType.SPEEDTREE_GRASS:
                builder.m_PaintInfo.m_SurfaceAlign = true;
                break;

            case EFoliageType.OTHER_TREE:
            case EFoliageType.SPEEDTREE_TREE_BILLBOARD:
            case EFoliageType.SPEEDTREE_TREE:
                builder.m_PaintInfo.m_SurfaceAlign = false;
                break;
            }

            if (anyWeirdGrassError == false)
            {
                if (builder.m_Type == EFoliageType.SPEEDTREE_GRASS || builder.m_Type == EFoliageType.SPEEDTREE_TREE || builder.m_Type == EFoliageType.SPEEDTREE_TREE_BILLBOARD)
                {
                    // Get a hue
                    builder.m_RenderInfo.m_Hue   = foliage.GetComponentInChildren <MeshRenderer>().sharedMaterial.GetColor("_HueVariation");
                    builder.m_RenderInfo.m_Color = foliage.GetComponentInChildren <MeshRenderer>().sharedMaterial.GetColor("_Color");


                    if (builder.m_RenderInfo.m_Hue == new Color(0, 0, 0, 0))
                    {
                        builder.m_RenderInfo.m_Hue = Color.white;
                    }

                    if (builder.m_RenderInfo.m_Color == new Color(0, 0, 0, 0))
                    {
                        builder.m_RenderInfo.m_Color = Color.white;
                    }
                }
                else
                {
                    builder.m_RenderInfo.m_Hue   = Color.white;
                    builder.m_RenderInfo.m_Color = Color.white;
                }
            }

            outAnyError = anyWeirdGrassError;
            outBuilder  = builder;
        }
        protected override bool DrawWizardGUI()
        {
            EditorGUILayout.LabelField("Add terrains or objects here: ");

            using (new ScopedLayout(() => { EditorGUILayout.BeginVertical("Box"); }, EBeginMode.BEGIN_HORIZONTAL))
            {
                GameObject possibleTerrains = EditorGUILayout.ObjectField(null, typeof(GameObject), true) as GameObject;

                if (possibleTerrains != null)
                {
                    List <Terrain> terrains = new List <Terrain>();
                    RecursivelyExtractTerrains(possibleTerrains, terrains);

                    for (int i = 0; i < terrains.Count; i++)
                    {
                        Terrain extr = terrains[i];

                        if (m_TerrainsExtract.Contains(extr) == false)
                        {
                            if (extr.GetComponent <Terrain>() != null)
                            {
                                // If it's the first (main) terrain
                                if (m_TerrainsExtract.Count == 0)
                                {
                                    DetailPrototype[] protos = extr.GetComponent <Terrain>().terrainData.detailPrototypes;

                                    if (protos != null && protos.Length > 0)
                                    {
                                        m_TerrainsExtract.Add(extr);
                                    }
                                    else
                                    {
                                        EditorUtility.DisplayDialog("Warning!", "The first added (main) terrain does not have any terrain details!", "Ok");
                                    }
                                }
                                else
                                {
                                    // Check if the same details appear
                                    if (HasSameDetails(m_TerrainsExtract[0].GetComponent <Terrain>(), extr.GetComponent <Terrain>()))
                                    {
                                        m_TerrainsExtract.Add(extr);
                                    }
                                    else
                                    {
                                        EditorUtility.DisplayDialog("Warning!", "The added terrain does not have the same details as the first (main) terrain!", "Ok");
                                    }
                                }
                            }
                            else
                            {
                                EditorUtility.DisplayDialog("Warning!", "You can only extract details from terrains!", "Ok");
                            }
                        }
                    }
                }
            }

            // Display all the detail data
            if (m_TerrainsExtract.Count == 0)
            {
                return(false);
            }

            EditorGUILayout.Space();

            // Extracted terrains
            EditorGUILayout.LabelField("Terrain to extract details from: ");

            // Show the data
            using (new ScopedLayout(() => { EditorGUILayout.BeginVertical("Box"); }, EBeginMode.BEGIN_HORIZONTAL))
            {
                for (int i = 0; i < m_TerrainsExtract.Count; i++)
                {
                    EditorGUILayout.LabelField("(T) " + m_TerrainsExtract[i].name + (i == 0 ? " [Main Details]" : ""));
                }
            }

            EditorGUILayout.Space();

            EditorGUILayout.LabelField("Details mappings: ");

            if (m_Prototypes == null)
            {
                m_Prototypes = m_TerrainsExtract[0].GetComponent <Terrain>().terrainData.detailPrototypes;

                if (m_Prototypes == null || m_Prototypes.Length == 0)
                {
                    FoliageLog.e("No terrain details found!");
                    return(false);
                }

                // Generate the UI data
                m_PrototypesData = new DetailPrototypeData[m_Prototypes.Length];
                for (int i = 0; i < m_PrototypesData.Length; i++)
                {
                    m_PrototypesData[i] = new DetailPrototypeData();
                    m_PrototypesData[i].m_DetailLayer = i;

                    string protoName = m_Prototypes[i].prototype != null ? m_Prototypes[i].prototype.name : m_Prototypes[i].prototypeTexture.name;

                    // Attempt to search through the data to check for a name or something
                    int foundIdx = m_TypesRuntime.FindIndex((x) =>
                    {
                        return(x.m_Name.ToLowerInvariant().Replace(" ", "").Contains(protoName.ToLowerInvariant().Replace(" ", "")));
                    });

                    if (foundIdx >= 0)
                    {
                        m_PrototypesData[i].m_NoneMapping            = false;
                        m_PrototypesData[i].m_FoliageHashMapping     = m_TypesRuntime[foundIdx].m_Hash;
                        m_PrototypesData[i].m_FoliageTypeNameMapping = m_TypesRuntime[foundIdx].m_Name;
                    }
                }
            }

            // Set all the data related to that terrain and stuff
            DetailPrototype[] prototypes = m_Prototypes;

            using (new ScopedLayout(() => { EditorGUILayout.BeginVertical("Box"); }, EBeginMode.BEGIN_VERTICAL))
            {
                for (int i = 0; i < prototypes.Length; i++)
                {
                    DetailPrototype     proto     = prototypes[i];
                    DetailPrototypeData protoData = m_PrototypesData[i];

                    using (new ScopedLayout(() => { EditorGUILayout.BeginVertical(); }, EBeginMode.BEGIN_HORIZONTAL))
                    {
                        string name;

                        if (proto.prototype != null)
                        {
                            name = proto.prototype.name;
                        }
                        else
                        {
                            name = proto.prototypeTexture.name;
                        }

                        // Each prototype data
                        using (new ScopedLayout(() => { EditorGUILayout.BeginHorizontal(); }, EBeginMode.BEGIN_HORIZONTAL))
                        {
                            protoData.m_ShouldExtract = EditorGUILayout.Toggle(new GUIContent("Extract: [" + name + "]", "If we should extract that type"), protoData.m_ShouldExtract);

                            if (protoData.m_ShouldExtract)
                            {
                                EditorGUILayout.LabelField("as: ", GUILayout.Width(30));

                                bool dropdown = EditorGUILayout.DropdownButton(new GUIContent(protoData.m_NoneMapping ? "None" : protoData.m_FoliageTypeNameMapping,
                                                                                              "To what foliage type this detail will be changed transformed when extracting from the terrain"), FocusType.Passive);

                                if (dropdown)
                                {
                                    GenericMenu menu = new GenericMenu();

                                    menu.AddItem(new GUIContent("None"), protoData.m_NoneMapping, (object obj) =>
                                    {
                                        protoData.m_NoneMapping = true;
                                    }, null);

                                    menu.AddSeparator("");

                                    for (int r = 0; r < m_TypesRuntime.Count; r++)
                                    {
                                        FoliageTypeRuntime rt = m_TypesRuntime[r];
                                        bool on = protoData.m_NoneMapping == false && protoData.m_FoliageHashMapping == rt.m_Hash;

                                        menu.AddItem(new GUIContent(rt.m_Name), on, (object obj) =>
                                        {
                                            protoData.m_NoneMapping            = false;
                                            protoData.m_FoliageHashMapping     = ((FoliageTypeRuntime)obj).m_Hash;
                                            protoData.m_FoliageTypeNameMapping = ((FoliageTypeRuntime)obj).m_Name;
                                        }, rt);
                                    }

                                    menu.ShowAsContext();
                                }
                            }
                        }

                        if (protoData.m_ShouldExtract)
                        {
                            protoData.m_ExtractedDensity = EditorGUILayout.Slider(new GUIContent("[" + name + "] Density [0..1]",
                                                                                                 "A multiplier for the count of extracted details from the terrain. 1 means extract all instances, 0.5 means extract half of them, 0 extract none. " +
                                                                                                 "Use mostly for extracted grass, but not details like rocks or anything else like that. Since on the terrain we only have a lot of billboards as " +
                                                                                                 "grass and we might map them to some 3D mesh clumps it's higly likely that we will only need a half (0.5) or a third (0.3) of that existing terrain data. "),
                                                                                  protoData.m_ExtractedDensity, 0, 1);
                        }
                    }
                }
            }

            EditorGUILayout.Space();

            // Settings
            EditorGUILayout.LabelField("Settings: ");
            using (new ScopedLayout(() => { EditorGUILayout.BeginVertical("Box"); }, EBeginMode.BEGIN_VERTICAL))
            {
                m_DisableAfterExtraction = EditorGUILayout.Toggle(new GUIContent(
                                                                      "Disable After Extraction",
                                                                      "If we should disable the details after we extracted them from the terrain. This will will set 'Terrain.drawTreesAndFoliage' to false."),
                                                                  m_DisableAfterExtraction, GUILayout.ExpandWidth(true));

                bool deleteAfterExtraction = EditorGUILayout.Toggle(new GUIContent(
                                                                        "Delete After Extraction",
                                                                        "If this is checked it will delete all the details that were extracted. Will delete the extracted details. Not advisable!"),
                                                                    m_DeleteAfterExtraction, GUILayout.ExpandWidth(true));

                if (m_DeleteAfterExtraction != deleteAfterExtraction)
                {
                    if (deleteAfterExtraction)
                    {
                        bool sure = EditorUtility.DisplayDialog("Warning",
                                                                "Setting this to true will delete all the extracted details from the terrain! " +
                                                                "Not recomended if you want to try multiple iterations! Are you sure?",
                                                                "Yes", "No");

                        if (sure)
                        {
                            m_DeleteAfterExtraction = true;
                        }
                    }
                    else
                    {
                        m_DeleteAfterExtraction = deleteAfterExtraction;
                    }
                }
            }

            return(false);
        }
Beispiel #20
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;
        }
        protected override bool DrawWizardGUI()
        {
            // Have a size of 1 always
            if (m_Extract.Count == 0)
            {
                m_Extract.Add(null);
            }

            // Objects to extract
            EditorGUILayout.LabelField("Objects to extract trees from: ");
            using (new ScopedLayout(() => { EditorGUILayout.BeginVertical("Box"); }, EBeginMode.BEGIN_HORIZONTAL))
            {
                bool lastNonNull = false;

                for (int i = 0; i < m_Extract.Count; i++)
                {
                    GameObject extr = EditorGUILayout.ObjectField(m_Extract[i], typeof(GameObject), true) as GameObject;

                    if (extr != null)
                    {
                        if (extr.GetComponent <Terrain>() != null)
                        {
                            FoliageLog.i("Terrain detected!");
                            m_Extract[i] = extr;
                        }
                        else
                        {
                            if (extr.transform.parent == null)
                            {
                                FoliageLog.i("Tree holder detected!");
                                m_Extract[i] = extr;
                            }
                            else
                            {
                                EditorUtility.DisplayDialog("Warning!", "You can only extract foliage from terrains and objects that are at the root of the hierarchy!", "Ok");
                            }
                        }
                    }

                    if (i == m_Extract.Count - 1 && m_Extract[i] != null)
                    {
                        lastNonNull = true;
                    }
                }

                // If the last item is not null, then make it larger
                if (lastNonNull)
                {
                    m_Extract.Add(null);
                }
            }

            // Settings
            EditorGUILayout.LabelField("Settings: ");
            using (new ScopedLayout(() => { EditorGUILayout.BeginVertical("Box"); }, EBeginMode.BEGIN_VERTICAL))
            {
                using (new ScopedLayout(() => { EditorGUILayout.BeginHorizontal(); }, EBeginMode.BEGIN_HORIZONTAL))
                {
                    EditorGUILayout.LabelField(new GUIContent(
                                                   "Auto Extract Types (Terrain Only)",
                                                   "If this is checked and we are extracting foliage from a terrain then the system will atempt to create automatically " +
                                                   "the foliage types that it requires. It will not work for for objects that are not terrains!"),
                                               GUILayout.ExpandWidth(true));

                    m_AutoExtractPrototypes = EditorGUILayout.Toggle(m_AutoExtractPrototypes, GUILayout.ExpandWidth(false));
                }

                m_DisableAfterExtraction = EditorGUILayout.Toggle(new GUIContent(
                                                                      "Disable After Extraction",
                                                                      "If we should disable the trees after we extracted them from the terrain or object. This will disable the extracted objects " +
                                                                      "with 'SetActive(false)' and will set 'Terrain.drawTreesAndFoliage' to false."),
                                                                  m_DisableAfterExtraction, GUILayout.ExpandWidth(true));

                bool deleteAfterExtraction = EditorGUILayout.Toggle(new GUIContent(
                                                                        "Delete After Extraction",
                                                                        "If this is checked it will delete all the objects that were extracter. Will delete the extracted trees and all the extracted " +
                                                                        "tree instances from the terrains. not advisable!"),
                                                                    m_DeleteAfterExtraction, GUILayout.ExpandWidth(true));

                if (m_DeleteAfterExtraction != deleteAfterExtraction)
                {
                    if (deleteAfterExtraction)
                    {
                        bool sure = EditorUtility.DisplayDialog("Warning",
                                                                "Setting this to true will delete all the extracted trees and foliage! " +
                                                                "Not recomended if you want to try multiple iterations! Are you sure?",
                                                                "Yes", "No");

                        if (sure)
                        {
                            m_DeleteAfterExtraction = true;
                        }
                    }
                    else
                    {
                        m_DeleteAfterExtraction = deleteAfterExtraction;
                    }
                }
            }

            return(false);
        }
        private void Update()
        {
            if (!m_Settings.m_WatchedTransform)
            {
                return;
            }

            m_CameraPosTemp = m_Settings.m_WatchedTransform.position;

            float x = m_CameraPosTemp.x - m_LastPosition.x;
            float y = m_CameraPosTemp.y - m_LastPosition.y;
            float z = m_CameraPosTemp.z - m_LastPosition.z;

            float distWalked = x * x + y * y + z * z;

            // If we didn't walked enough, return
            if (distWalked > m_Settings.m_CollisionRefreshDistance * m_Settings.m_CollisionRefreshDistance)
            {
                // Update last position
                m_LastPosition = m_CameraPosTemp;

                m_Layer = LayerMask.NameToLayer(m_Settings.m_UsedLayer);

                // Reset counter
                m_DataIssuedActiveColliders = 0;

                // Reset all the cache's data
                foreach (CollisionCache cache in m_Cache.Values)
                {
                    if (cache != null)
                    {
                        cache.Reset();
                    }
                }

                // Set the current cell
                currentCell.Set(m_LastPosition);

                // Refresh eveything
                float collDistSqr = m_Settings.m_CollisionDistance * m_Settings.m_CollisionDistance;

                // Iterate cells
                FoliageCell.IterateNeighboring(currentCell, 1, (int hash) =>
                {
                    FoliageCellDataRuntime data;

                    if (m_FoliageData.m_FoliageData.TryGetValue(hash, out data))
                    {
                        // If it is within distance and in the frustum
                        float distanceSqr = data.m_Bounds.SqrDistance(m_LastPosition);

                        if (distanceSqr <= collDistSqr)
                        {
                            ProcessCell(data, collDistSqr);
                        }
                    }
                });
            }

#if UNITY_EDITOR
            if (Time.frameCount % 300 == 0)
            {
                FoliageLog.i("Issued colliders: " + m_DataIssuedActiveColliders);
            }
#endif
        }
        void OnWizardCreate()
        {
            if (m_TerrainsExtract.Count > 0)
            {
                if (m_TerrainsExtract.Count > 1)
                {
                    for (int i = 1; i < m_TerrainsExtract.Count; i++)
                    {
                        if (HasSameDetails(m_TerrainsExtract[0].GetComponent <Terrain>(), m_TerrainsExtract[i].GetComponent <Terrain>()) == false)
                        {
                            FoliageLog.e("Missing type when verified! Don't modify the types/details while extracting details!");
                            return;
                        }
                    }
                }

                Terrain           terrain = m_TerrainsExtract[0].GetComponent <Terrain>();
                DetailPrototype[] proto   = terrain.terrainData.detailPrototypes;

                List <FoliageDetailExtracterMapping> mappings = new List <FoliageDetailExtracterMapping>();

                // Build all the extracting data
                for (int i = 0; i < m_PrototypesData.Length; i++)
                {
                    var data = m_PrototypesData[i];

                    if (data.m_ShouldExtract == true && data.m_NoneMapping == false)
                    {
                        FoliageDetailExtracterMapping mapping = new FoliageDetailExtracterMapping();
                        mapping.m_DetailLayer      = data.m_DetailLayer;
                        mapping.m_FoliageTypeHash  = data.m_FoliageHashMapping;
                        mapping.m_ExtractedDensity = data.m_ExtractedDensity;

                        mappings.Add(mapping);
                    }
                }

                if (mappings.Count > 0)
                {
                    // Check that we have all the types and nothing has been tampered between the create
                    for (int i = 0; i < mappings.Count; i++)
                    {
                        if (m_Painter.HasFoliageType(mappings[i].m_FoliageTypeHash) == false)
                        {
                            FoliageLog.e("Missing type when created! Don't modify the types while extracting details!");
                            return;
                        }

                        if (mappings[i].m_DetailLayer < 0 || mappings[i].m_DetailLayer >= proto.Length)
                        {
                            FoliageLog.e("Missing type when created! Don't modify the types while extracting details!");
                            return;
                        }
                    }

                    // Proceed with the extraction
                    m_Callback(mappings, m_TerrainsExtract, m_DisableAfterExtraction, m_DeleteAfterExtraction);
                }
                else
                {
                    FoliageLog.i("Nothing to extract!");
                }
            }
            else
            {
                FoliageLog.i("Nothing to extract!");
            }
        }