예제 #1
0
        private void OnEnable()
        {
            spawner = (VegetationSpawner)target;

            if (spawner.terrains != null)
            {
                hasMissingTerrains = SpawnerBase.HasMissingTerrain(spawner.terrains);
            }

            seed     = serializedObject.FindProperty("seed");
            terrains = serializedObject.FindProperty("terrains");
            detailResolutionIndex = serializedObject.FindProperty("detailResolutionIndex");
            detailResolution      = serializedObject.FindProperty("detailResolution");
            grassPatchSize        = serializedObject.FindProperty("grassPatchSize");
            grassPatchSizeIndex   = serializedObject.FindProperty("grassPatchSizeIndex");
            terrainSettings       = serializedObject.FindProperty("terrainSettings");
            cellSize               = serializedObject.FindProperty("cellSize");
            cellDivisions          = serializedObject.FindProperty("cellDivisions");
            collisionLayerMask     = serializedObject.FindProperty("collisionLayerMask");
            highPrecisionCollision = serializedObject.FindProperty("highPrecisionCollision");
            tempColliders          = serializedObject.FindProperty("tempColliders");

            waterHeight      = serializedObject.FindProperty("waterHeight");
            autoRespawnTrees = serializedObject.FindProperty("autoRespawnTrees");

            VegetationSpawner.VisualizeCells = VisualizeCellsPersistent;

            Undo.undoRedoPerformed += OnUndoRedo;
        }
예제 #2
0
        private void SpawnTreeOnTerrain(Terrain terrain, TreeType item)
        {
            float height, worldHeight, normalizedHeight;

            List <TreeInstance> treeInstanceCollection = new List <TreeInstance>(terrain.terrainData.treeInstances);

            //Clear all existing instances first, setting the tree instances is additive
            for (int i = 0; i < treeInstanceCollection.Count; i++)
            {
                foreach (TreePrefab prefab in item.prefabs)
                {
                    treeInstanceCollection.RemoveAll(x => x.prototypeIndex == prefab.index);
                }
            }

            if (item.enabled)
            {
                InitializeSeed(item.seed);

                //Speed up, only recalculate spawn points if distance was altered, or if there are none at all
                if (item.prevDistance > item.distance || item.prevDistance < item.distance || item.spawnPoints.Count == 0)
                {
                    item.prevDistance = item.distance;

                    item.spawnPoints = PoissonDisc.GetSpawnpoints(terrain, item.distance, item.seed + seed);
                }

                foreach (Vector3 pos in item.spawnPoints)
                {
                    //InitializeSeed(item.seed + index);

                    //Relative position as 0-1 value
                    Vector2 normalizedPos = terrain.GetNormalizedPosition(pos);

                    InitializeSeed(item.seed + (int)pos.x * (int)pos.z);

                    //Skip if failing global probability check
                    if (((Random.value * 100f) <= item.probability) == false)
                    {
                        continue;
                    }

                    if (item.collisionCheck)
                    {
                        //Check for collision
                        if (InsideOccupiedCell(terrain, pos, normalizedPos))
                        {
                            continue;
                        }
                    }

                    TreePrefab prefab = SpawnerBase.GetProbableTree(item);

                    //Failed probability checks entirely
                    if (prefab == null)
                    {
                        continue;
                    }

                    terrain.SampleHeight(normalizedPos, out height, out worldHeight, out normalizedHeight);

                    //Reject if lower than chosen water level
                    if (item.rejectUnderwater && worldHeight < waterHeight)
                    {
                        continue;
                    }

                    //Check height
                    if (worldHeight < item.heightRange.x || worldHeight > item.heightRange.y)
                    {
                        continue;
                    }

                    if (item.slopeRange.x > 0 || item.slopeRange.y < 90f)
                    {
                        float slope = terrain.GetSlope(normalizedPos, false);

                        //Reject if slope check fails
                        if (!(slope >= (item.slopeRange.x) && slope <= (item.slopeRange.y)))
                        {
                            continue;
                        }
                    }

                    if (item.curvatureRange.x > 0 || item.curvatureRange.y < 1f)
                    {
                        float curvature = terrain.SampleConvexity(normalizedPos);
                        //0=concave, 0.5=flat, 1=convex
                        curvature = TerrainSampler.ConvexityToCurvature(curvature);
                        if (curvature < item.curvatureRange.x || curvature > item.curvatureRange.y)
                        {
                            continue;
                        }
                    }

                    float spawnChance = 0f;
                    if (item.layerMasks.Count == 0)
                    {
                        spawnChance = 100f;
                    }
                    else
                    {
                        //Reject based on layer masks
                        splatmapTexelIndex = terrain.SplatmapTexelIndex(normalizedPos);
                    }

                    foreach (TerrainLayerMask layer in item.layerMasks)
                    {
                        Texture2D splat = terrain.terrainData.GetAlphamapTexture(GetSplatmapID(layer.layerID));

                        Color color = splat.GetPixel(splatmapTexelIndex.x, splatmapTexelIndex.y);

                        int   channel = layer.layerID % 4;
                        float value   = SampleChannel(color, channel);

                        if (value > 0)
                        {
                            value = Mathf.Clamp01(value - layer.threshold);
                        }
                        value *= 100f;

                        spawnChance += value;
                    }
                    InitializeSeed((int)pos.x * (int)pos.z);
                    if ((Random.value <= spawnChance) == false)
                    {
                        continue;
                    }

                    //Passed all conditions, add instance
                    TreeInstance treeInstance = new TreeInstance();
                    treeInstance.prototypeIndex = prefab.index;

                    //Note: Sink amount should be converted to normalized 0-1 height
                    treeInstance.position = new Vector3(normalizedPos.x, normalizedHeight - (item.sinkAmount / (terrain.terrainData.size.y + 0.01f)), normalizedPos.y);
                    treeInstance.rotation = Random.Range(0f, 359f) * Mathf.Deg2Rad;

                    float scale = Random.Range(item.scaleRange.x, item.scaleRange.y);
                    treeInstance.heightScale = scale;
                    treeInstance.widthScale  = scale;

                    treeInstance.color         = Color.white;
                    treeInstance.lightmapColor = Color.white;

                    treeInstanceCollection.Add(treeInstance);

                    item.instanceCount++;
                }
            }

#if UNITY_2019_1_OR_NEWER
            terrain.terrainData.SetTreeInstances(treeInstanceCollection.ToArray(), false);
#else
            terrain.terrainData.treeInstances = treeInstanceCollection.ToArray();
#endif
        }