private void Awake()
 {
     if (m_Painter == null)
     {
         m_Painter = FindObjectOfType <FoliagePainter>();
     }
 }
 /**
  * Extracts the given detail layers from the terrains.
  *
  * @param terrains
  *          The terrains from which we extract the data from. All must have the exact same terrain detail layers
  * @param mapping
  *          The mapping of the items that we want to extract. The key is the terrain detail layer that we want to extract from and the value is the foliage type hash
  *          that we are going to instantiate for that detail type's layer
  */
 public static void ExtractDetailsFromTerrains(FoliagePainter painter, IEnumerable <Terrain> terrains, List <FoliageDetailExtracterMapping> mappings, bool disable, bool delete)
 {
     foreach (Terrain t in terrains)
     {
         ExtractDetailsFromTerrain(painter, t, mappings, disable, delete);
     }
 }
        public void Init(FoliagePainter painter, OnExtractDetailsPressed extract)
        {
            m_Painter  = painter;
            m_Callback = extract;

            m_TypesRuntime = m_Painter.GetFoliageTypesRuntime();
        }
        /**
         * Prepare the renderer for the rendering.
         */
        public void InitRenderer(FoliagePainter painter, FoliageDataRuntime dataToRender, List <FoliageType> foliageTypes)
        {
            m_FoliageData = dataToRender;

            foreach (FoliageType type in foliageTypes)
            {
                FoliageTypeUtilities.BuildDataRuntime(painter, type, m_Settings.m_WindTransform);
            }

            UpdateFoliageTypes(foliageTypes);
        }
        public static void ExtractFoliage(FoliagePainter painter, IEnumerable <GameObject> objects, bool autoExtract, bool disable, bool delete)
        {
            foreach (GameObject obj in objects)
            {
                if (obj.GetComponent <Terrain>() != null)
                {
                    ExtractFromTerrain(painter, painter.GetEditTime, obj.GetComponent <Terrain>(), autoExtract, disable, delete);
                }
                else
                {
                    ExtractFromRootObject(painter.GetEditTime, obj, disable, delete);
                }
            }

            // Re-generate the billboards
            painter.GetEditTime.GenerateBillboards();
        }
Beispiel #6
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;
        }
Beispiel #7
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");
                }
            }
        }
Beispiel #8
0
 public FoliagePainterRuntime(FoliagePainter painter)
 {
     m_Painter = painter;
 }
        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);
        }
Beispiel #11
0
 public void Init(FoliagePainter painter, System.Action <int[]> extract)
 {
     m_Painter  = painter;
     m_Callback = extract;
 }
 public FoliagePainterEditTime(FoliagePainter painter)
 {
     m_Painter = painter;
 }