コード例 #1
0
        // Update terrain nature
        private void UpdateTerrainNature(TerrainDesc terrainDesc)
        {
            //System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew();
            //long startTime = stopwatch.ElapsedMilliseconds;

            // Setup billboards
            DaggerfallTerrain        dfTerrain        = terrainDesc.terrainObject.GetComponent <DaggerfallTerrain>();
            DaggerfallBillboardBatch dfBillboardBatch = terrainDesc.billboardBatchObject.GetComponent <DaggerfallBillboardBatch>();

            if (dfTerrain && dfBillboardBatch)
            {
                // Get current climate and nature archive
                DFLocation.ClimateSettings climate = MapsFile.GetWorldClimateSettings(dfTerrain.MapData.worldClimate);
                int natureArchive = climate.NatureArchive;
                if (dfUnity.WorldTime.SeasonValue == WorldTime.Seasons.Winter)
                {
                    // Offset to snow textures
                    natureArchive++;
                }

                dfBillboardBatch.SetMaterial(natureArchive);
                TerrainHelper.LayoutNatureBillboards(dfTerrain, dfBillboardBatch, TerrainScale);
            }

            // Only set active again once complete
            terrainDesc.billboardBatchObject.SetActive(true);

            //long totalTime = stopwatch.ElapsedMilliseconds - startTime;
            //DaggerfallUnity.LogMessage(string.Format("Time to update terrain nature: {0}ms", totalTime), true);
        }
コード例 #2
0
        // Update terrain nature
        private void UpdateTerrainNature(TerrainDesc terrainDesc)
        {
            // Setup billboards
            DaggerfallTerrain        dfTerrain        = terrainDesc.terrainObject.GetComponent <DaggerfallTerrain>();
            DaggerfallBillboardBatch dfBillboardBatch = terrainDesc.billboardBatchObject.GetComponent <DaggerfallBillboardBatch>();

            if (dfTerrain && dfBillboardBatch)
            {
                // Get current climate and nature archive
                int natureArchive = ClimateSwaps.GetNatureArchive(LocalPlayerGPS.ClimateSettings.NatureSet, dfUnity.WorldTime.Now.SeasonValue);
                dfBillboardBatch.SetMaterial(natureArchive);
                TerrainHelper.LayoutNatureBillboards(dfTerrain, dfBillboardBatch, TerrainScale);
            }

            // Only set active again once complete
            terrainDesc.billboardBatchObject.SetActive(true);
        }
コード例 #3
0
        // Update terrain nature
        private void UpdateTerrainNature(TerrainDesc terrainDesc)
        {
            //System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew();
            //long startTime = stopwatch.ElapsedMilliseconds;

            // Setup billboards
            DaggerfallTerrain        dfTerrain        = terrainDesc.terrainObject.GetComponent <DaggerfallTerrain>();
            DaggerfallBillboardBatch dfBillboardBatch = terrainDesc.billboardBatchObject.GetComponent <DaggerfallBillboardBatch>();

            if (dfTerrain && dfBillboardBatch)
            {
                // Get current climate and nature archive
                int natureArchive = ClimateSwaps.GetNatureArchive(LocalPlayerGPS.ClimateSettings.NatureSet, dfUnity.WorldTime.Now.SeasonValue);
                dfBillboardBatch.SetMaterial(natureArchive);
                TerrainHelper.LayoutNatureBillboards(dfTerrain, dfBillboardBatch, TerrainScale);
            }

            // Only set active again once complete
            terrainDesc.billboardBatchObject.SetActive(true);

            //long totalTime = stopwatch.ElapsedMilliseconds - startTime;
            //DaggerfallUnity.LogMessage(string.Format("Time to update terrain nature: {0}ms", totalTime), true);
        }
コード例 #4
0
        // Update terrain data
        // Spreads loading across several frames to reduce gameplay stalls
        // This can also be done using true multi-threading, but at much greater
        // complexity for only minor visible gains.
        // Only yields after initial init complete
        private IEnumerator UpdateTerrains()
        {
            // First stage updates terrain heightmaps
            for (int i = 0; i < terrainArray.Length; i++)
            {
                if (terrainArray[i].active && terrainArray[i].updateHeights)
                {
                    UpdateTerrainHeights(terrainArray[i]);
                    terrainArray[i].updateHeights = false;
                    if (!init)
                    {
                        yield return(new WaitForEndOfFrame());
                    }
                }
            }

            // Wait for physics update when streaming
            if (!init)
            {
                yield return(new WaitForFixedUpdate());
            }

            // Second stage updates terrain nature
            for (int i = 0; i < terrainArray.Length; i++)
            {
                if (terrainArray[i].active && terrainArray[i].updateNature)
                {
                    UpdateTerrainNature(terrainArray[i]);
                    terrainArray[i].updateNature = false;
                    if (!init)
                    {
                        yield return(new WaitForEndOfFrame());
                    }
                }
            }

            // Get key for central player terrain
            int playerKey = TerrainHelper.MakeTerrainKey(MapPixelX, MapPixelY);

            // Third stage updates location if present
            // Vast majority of terrains will not have a location
            // Locations are not optimised as yet and are quite heavy on drawcalls
            for (int i = 0; i < terrainArray.Length; i++)
            {
                // Get key for this terrain
                int key = TerrainHelper.MakeTerrainKey(terrainArray[i].mapPixelX, terrainArray[i].mapPixelY);

                if (terrainArray[i].active && terrainArray[i].hasLocation)
                {
                    // Create location if not present
                    if (!locationDict.ContainsKey(key))
                    {
                        // Create location object
                        DFLocation location;
                        GameObject locationObject = CreateLocationGameObject(i, out location);
                        if (!locationObject)
                        {
                            continue;
                        }

                        // Add location object to dictionary
                        locationDict.Add(key, locationObject);

                        // Create location beacon
                        // This is parented to location and shares its lifetime
                        if (AddLocationBeacon)
                        {
                            const float beaconHeight   = 900f;
                            const float beaconOffset   = (MapsFile.WorldMapTerrainDim * MeshReader.GlobalScale) / 2f;
                            GameObject  locationMarker = (GameObject)GameObject.Instantiate(Resources.Load <GameObject>("LocationBeacon"));
                            locationMarker.hideFlags               = HideFlags.HideAndDontSave;
                            locationMarker.transform.parent        = locationObject.transform;
                            locationMarker.transform.localPosition = new Vector3(beaconOffset, beaconHeight, beaconOffset);
                        }

                        // Add one nature batch for entire location
                        // This is parented to location and shares its lifetime
                        GameObject natureBatchObject = new GameObject("NatureBatch");
                        natureBatchObject.hideFlags               = HideFlags.HideAndDontSave;
                        natureBatchObject.transform.parent        = locationObject.transform;
                        natureBatchObject.transform.localPosition = Vector3.zero;
                        DaggerfallBillboardBatch natureBatch = natureBatchObject.AddComponent <DaggerfallBillboardBatch>();
                        int natureArchive = ClimateSwaps.GetNatureArchive(LocalPlayerGPS.ClimateSettings.NatureSet, dfUnity.WorldTime.Now.SeasonValue);
                        natureBatch.SetMaterial(natureArchive);

                        // RMB blocks are laid out in centre of terrain to align with ground
                        int     width   = location.Exterior.ExteriorData.Width;
                        int     height  = location.Exterior.ExteriorData.Height;
                        float   offsetX = ((8 * RMBLayout.RMBSide) - (width * RMBLayout.RMBSide)) / 2;
                        float   offsetZ = ((8 * RMBLayout.RMBSide) - (height * RMBLayout.RMBSide)) / 2;
                        Vector3 origin  = new Vector3(offsetX, 2.0f * MeshReader.GlobalScale, offsetZ);

                        // Perform layout and yield after each block is placed
                        DaggerfallLocation dfLocation = locationObject.GetComponent <DaggerfallLocation>();
                        for (int y = 0; y < height; y++)
                        {
                            for (int x = 0; x < width; x++)
                            {
                                // Set origin for billboard batch add
                                // This causes next additions to be offset by this position
                                Vector3 blockOrigin = origin + new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide));
                                natureBatch.origin = blockOrigin;

                                // Add block and yield
                                string     blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y));
                                GameObject go        = RMBLayout.CreateGameObject(blockName, true, natureBatch);
                                go.hideFlags               = HideFlags.HideAndDontSave;
                                go.transform.parent        = locationObject.transform;
                                go.transform.localPosition = blockOrigin;
                                dfLocation.ApplyClimateSettings();
                                if (!init)
                                {
                                    yield return(new WaitForEndOfFrame());
                                }
                            }
                        }

                        // If this is the player terrain we may need to reposition player
                        if (playerKey == key && repositionPlayer)
                        {
                            // Position to location and use start marker for large cities
                            bool useStartMarker = (dfLocation.Summary.LocationType == DFRegion.LocationTypes.TownCity);
                            PositionPlayerToLocation(MapPixelX, MapPixelY, dfLocation, origin, width, height, useStartMarker);
                            repositionPlayer = false;
                        }

                        // Apply nature batch
                        natureBatch.Apply();
                    }
                }
                else if (terrainArray[i].active)
                {
                    if (playerKey == key && repositionPlayer)
                    {
                        PositionPlayerToTerrain(MapPixelX, MapPixelY, Vector3.zero);
                        repositionPlayer = false;
                    }
                }
            }

            // If this is an init we can use the load time to unload unused assets
            // Keeps memory usage much lower over time
            if (init)
            {
                Resources.UnloadUnusedAssets();
            }

            // Finish by collecting stale data and setting neighbours
            CollectTerrains();
            CollectLocations();
            UpdateNeighbours();
        }
コード例 #5
0
        /// <summary>
        /// Performs a fully standalone in-place location layout.
        /// This method is used only by editor layouts, not by streaming world.
        /// </summary>
        private void LayoutLocation(ref DFLocation location)
        {
#if SHOW_LAYOUT_TIMES
            // Start timing
            System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew();
            long startTime = stopwatch.ElapsedMilliseconds;
#endif

            // Get city dimensions
            int width  = location.Exterior.ExteriorData.Width;
            int height = location.Exterior.ExteriorData.Height;

            // Create billboard batch game objects for this location
            //TextureAtlasBuilder miscBillboardAtlas = null;
            summary.NatureBillboardBatch = null;
            DaggerfallBillboardBatch lightsBillboardBatch  = null;
            DaggerfallBillboardBatch animalsBillboardBatch = null;
            //DaggerfallBillboardBatch miscBillboardBatch = null;
            if (dfUnity.Option_BatchBillboards)
            {
                //miscBillboardAtlas = dfUnity.MaterialReader.MiscBillboardAtlas;
                int natureArchive = ClimateSwaps.GetNatureArchive(CurrentNatureSet, CurrentSeason);
                summary.NatureBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(natureArchive, transform);
                lightsBillboardBatch         = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.LightsTextureArchive, transform);
                animalsBillboardBatch        = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.AnimalsTextureArchive, transform);
                //miscBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(miscBillboardAtlas.AtlasMaterial, transform);
            }

            // Import blocks
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if (dfUnity.Option_BatchBillboards)
                    {
                        // Set block origin for billboard batches
                        // This causes next additions to be offset by this position
                        Vector3 blockOrigin = new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide));
                        summary.NatureBillboardBatch.BlockOrigin = blockOrigin;
                        lightsBillboardBatch.BlockOrigin         = blockOrigin;
                        animalsBillboardBatch.BlockOrigin        = blockOrigin;
                        //miscBillboardBatch.BlockOrigin = blockOrigin;
                    }

                    string     blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y));
                    GameObject go        = GameObjectHelper.CreateRMBBlockGameObject(
                        blockName,
                        x,
                        y,
                        dfUnity.Option_RMBGroundPlane,
                        dfUnity.Option_CityBlockPrefab,
                        summary.NatureBillboardBatch,
                        lightsBillboardBatch,
                        animalsBillboardBatch,
                        null, //miscBillboardAtlas,
                        null, //miscBillboardBatch,
                        CurrentNatureSet,
                        CurrentSeason);
                    go.transform.parent   = this.transform;
                    go.transform.position = new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide));
                }
            }

            // Apply batches
            if (summary.NatureBillboardBatch)
            {
                summary.NatureBillboardBatch.Apply();
            }
            if (lightsBillboardBatch)
            {
                lightsBillboardBatch.Apply();
            }
            if (animalsBillboardBatch)
            {
                animalsBillboardBatch.Apply();
            }
            //if (miscBillboardBatch) miscBillboardBatch.Apply();

            // Enumerate start marker game objects
            EnumerateStartMarkers();

#if SHOW_LAYOUT_TIMES
            // Show timer
            long totalTime = stopwatch.ElapsedMilliseconds - startTime;
            DaggerfallUnity.LogMessage(string.Format("Time to layout location: {0}ms", totalTime), true);
#endif
        }
コード例 #6
0
        // Drops nature flats based on random chance scaled by simple rules
        public static void LayoutNatureBillboards(DaggerfallTerrain dfTerrain, DaggerfallBillboardBatch dfBillboardBatch, float terrainScale, int terrainDist)
        {
            const float maxSteepness      = 50f;        // 50
            const float slopeSinkRatio    = 70f;        // Sink flats slightly into ground as slope increases to prevent floaty trees.
            const float baseChanceOnDirt  = 0.2f;       // 0.2
            const float baseChanceOnGrass = 0.9f;       // 0.4
            const float baseChanceOnStone = 0.05f;      // 0.05

            // Location Rect is expanded slightly to give extra clearance around locations
            const int natureClearance = 4;
            Rect      rect            = dfTerrain.MapData.locationRect;

            if (rect.x > 0 && rect.y > 0)
            {
                rect.xMin -= natureClearance;
                rect.xMax += natureClearance;
                rect.yMin -= natureClearance;
                rect.yMax += natureClearance;
            }
            // Chance scaled based on map pixel height
            // This tends to produce sparser lowlands and denser highlands
            // Adjust or remove clamp range to influence nature generation
            float elevationScale = (dfTerrain.MapData.worldHeight / 128f);

            elevationScale = Mathf.Clamp(elevationScale, 0.4f, 1.0f);

            // Chance scaled by base climate type
            float climateScale = 1.0f;

            DFLocation.ClimateSettings climate = MapsFile.GetWorldClimateSettings(dfTerrain.MapData.worldClimate);
            switch (climate.ClimateType)
            {
            case DFLocation.ClimateBaseType.Desert:             // Just lower desert for now
                climateScale = 0.25f;
                break;
            }
            float chanceOnDirt  = baseChanceOnDirt * elevationScale * climateScale;
            float chanceOnGrass = baseChanceOnGrass * elevationScale * climateScale;
            float chanceOnStone = baseChanceOnStone * elevationScale * climateScale;

            // Get terrain
            Terrain terrain = dfTerrain.gameObject.GetComponent <Terrain>();

            if (!terrain)
            {
                return;
            }

            // Get terrain data
            TerrainData terrainData = terrain.terrainData;

            if (!terrainData)
            {
                return;
            }

            // Remove exiting billboards
            dfBillboardBatch.Clear();
            MeshReplacement.ClearNatureGameObjects(terrain);

            // Seed random with terrain key
            Random.InitState(MakeTerrainKey(dfTerrain.MapPixelX, dfTerrain.MapPixelY));

            // Just layout some random flats spread evenly across entire map pixel area
            // Flats are aligned with tiles, max 16129 billboards per batch
            Vector2 tilePos          = Vector2.zero;
            int     tDim             = MapsFile.WorldMapTileDim;
            int     hDim             = DaggerfallUnity.Instance.TerrainSampler.HeightmapDimension;
            float   scale            = terrainData.heightmapScale.x * (float)hDim / (float)tDim;
            float   maxTerrainHeight = DaggerfallUnity.Instance.TerrainSampler.MaxTerrainHeight;
            float   beachLine        = DaggerfallUnity.Instance.TerrainSampler.BeachElevation;

            for (int y = 0; y < tDim; y++)
            {
                for (int x = 0; x < tDim; x++)
                {
                    // Reject based on steepness
                    float steepness = terrainData.GetSteepness((float)x / tDim, (float)y / tDim);
                    if (steepness > maxSteepness)
                    {
                        continue;
                    }

                    // Reject if inside location rect (expanded slightly to give extra clearance around locations)
                    tilePos.x = x;
                    tilePos.y = y;
                    if (rect.x > 0 && rect.y > 0 && rect.Contains(tilePos))
                    {
                        continue;
                    }

                    // Chance also determined by tile type
                    int tile = dfTerrain.MapData.tilemapSamples[x, y] & 0x3F;
                    if (tile == 1)
                    {   // Dirt
                        if (Random.Range(0f, 1f) > chanceOnDirt)
                        {
                            continue;
                        }
                    }
                    else if (tile == 2)
                    {   // Grass
                        if (Random.Range(0f, 1f) > chanceOnGrass)
                        {
                            continue;
                        }
                    }
                    else if (tile == 3)
                    {   // Stone
                        if (Random.Range(0f, 1f) > chanceOnStone)
                        {
                            continue;
                        }
                    }
                    else
                    {   // Anything else
                        continue;
                    }

                    int   hx     = (int)Mathf.Clamp(hDim * ((float)x / (float)tDim), 0, hDim - 1);
                    int   hy     = (int)Mathf.Clamp(hDim * ((float)y / (float)tDim), 0, hDim - 1);
                    float height = dfTerrain.MapData.heightmapSamples[hy, hx] * maxTerrainHeight;  // x & y swapped in heightmap for TerrainData.SetHeights()

                    // Reject if too close to water
                    if (height < beachLine)
                    {
                        continue;
                    }

                    // Sample height and position billboard
                    Vector3 pos     = new Vector3(x * scale, 0, y * scale);
                    float   height2 = terrain.SampleHeight(pos + terrain.transform.position);
                    pos.y = height2 - (steepness / slopeSinkRatio);

                    // Add to batch unless a mesh replacement is found
                    int record = Random.Range(1, 32);
                    if (terrainDist > 1 || !MeshReplacement.ImportNatureGameObject(dfBillboardBatch.TextureArchive, record, terrain, x, y))
                    {
                        dfBillboardBatch.AddItem(record, pos);
                    }
                    else if (!NatureMeshUsed)
                    {
                        NatureMeshUsed = true;  // Signal that nature mesh has been used to initiate extra terrain updates
                    }
                }
            }

            // Apply new batch
            dfBillboardBatch.Apply();
        }
コード例 #7
0
        // Drops nature flats based on random chance scaled by simple rules
        public static void LayoutNatureBillboards(DaggerfallTerrain dfTerrain, DaggerfallBillboardBatch dfBillboardBatch, float terrainScale)
        {
            const float maxSteepness  = 50f;        // 50
            const float chanceOnDirt  = 0.2f;       // 0.2
            const float chanceOnGrass = 0.9f;       // 0.4
            const float chanceOnStone = 0.05f;      // 0.05

            // Get terrain
            Terrain terrain = dfTerrain.gameObject.GetComponent <Terrain>();

            if (!terrain)
            {
                return;
            }

            // Get terrain data
            TerrainData terrainData = terrain.terrainData;

            if (!terrainData)
            {
                return;
            }

            // Remove exiting billboards
            dfBillboardBatch.Clear();

            // Seed random with terrain key
            UnityEngine.Random.seed = MakeTerrainKey(dfTerrain.MapPixelX, dfTerrain.MapPixelY);

            // Just layout some random flats spread evenly across entire map pixel area
            // Flats are aligned with tiles, max 127x127 in billboard batch
            Vector2 tilePos = Vector2.zero;
            float   scale   = terrainData.heightmapScale.x;
            int     dim     = TerrainHelper.terrainTileDim - 1;

            for (int y = 0; y < dim; y++)
            {
                for (int x = 0; x < dim; x++)
                {
                    // Reject based on steepness
                    float steepness = terrainData.GetSteepness((float)x / dim, (float)y / dim);
                    if (steepness > maxSteepness)
                    {
                        continue;
                    }

                    // Reject if inside location rect
                    // Rect is expanded slightly to give extra clearance around locations
                    tilePos.x = x;
                    tilePos.y = y;
                    const int natureClearance = 4;
                    Rect      rect            = dfTerrain.MapData.locationRect;
                    if (rect.x > 0 && rect.y > 0)
                    {
                        rect.xMin -= natureClearance;
                        rect.xMin += natureClearance;
                        rect.yMin -= natureClearance;
                        rect.yMax += natureClearance;
                        if (rect.Contains(tilePos))
                        {
                            continue;
                        }
                    }

                    // Chance scaled based on map pixel height
                    // This tends to produce sparser lowlands and denser highlands
                    // Adjust or remove clamp range to influence nature generation
                    float elevationScale = (dfTerrain.MapData.worldHeight / 128f);
                    elevationScale = Mathf.Clamp(elevationScale, 0.4f, 1.0f);

                    // Chance scaled by base climate type
                    float climateScale = 1.0f;
                    DFLocation.ClimateSettings climate = MapsFile.GetWorldClimateSettings(dfTerrain.MapData.worldClimate);
                    switch (climate.ClimateType)
                    {
                    case DFLocation.ClimateBaseType.Desert:             // Just lower desert for now
                        climateScale = 0.25f;
                        break;
                    }

                    // Chance also determined by tile type
                    WorldSample sample = TerrainHelper.GetSample(ref dfTerrain.MapData.samples, x, y);
                    if (sample.record == 1)
                    {
                        // Dirt
                        if (UnityEngine.Random.Range(0f, 1f) > chanceOnDirt * elevationScale * climateScale)
                        {
                            continue;
                        }
                    }
                    else if (sample.record == 2)
                    {
                        // Grass
                        if (UnityEngine.Random.Range(0f, 1f) > chanceOnGrass * elevationScale * climateScale)
                        {
                            continue;
                        }
                    }
                    else if (sample.record == 3)
                    {
                        // Stone
                        if (UnityEngine.Random.Range(0f, 1f) > chanceOnStone * elevationScale * climateScale)
                        {
                            continue;
                        }
                    }
                    else
                    {
                        // Anything else
                        continue;
                    }

                    // Sample height and position billboard
                    Vector3 pos    = new Vector3(x * scale, 0, y * scale);
                    float   height = terrain.SampleHeight(pos + terrain.transform.position);
                    pos.y = height;

                    // Reject if too close to water
                    float beachLine = DaggerfallUnity.Instance.TerrainSampler.BeachElevation * terrainScale;
                    if (height < beachLine)
                    {
                        continue;
                    }

                    // Add to batch
                    int record = UnityEngine.Random.Range(1, 32);
                    dfBillboardBatch.AddItem(record, pos);
                }
            }

            // Apply new batch
            dfBillboardBatch.Apply();
        }
コード例 #8
0
        // Drops nature flats based on random chance scaled by simple rules
        public static void LayoutNatureBillboards(DaggerfallTerrain dfTerrain, DaggerfallBillboardBatch dfBillboardBatch, float terrainScale)
        {
            const float maxSteepness = 50f;         // 50
            const float chanceOnDirt = 0.2f;        // 0.2
            const float chanceOnGrass = 0.9f;       // 0.4
            const float chanceOnStone = 0.05f;      // 0.05

            // Get terrain
            Terrain terrain = dfTerrain.gameObject.GetComponent<Terrain>();
            if (!terrain)
                return;

            // Get terrain data
            TerrainData terrainData = terrain.terrainData;
            if (!terrainData)
                return;

            // Remove exiting billboards
            dfBillboardBatch.Clear();

            // Seed random with terrain key
            UnityEngine.Random.seed = MakeTerrainKey(dfTerrain.MapPixelX, dfTerrain.MapPixelY);

            // Just layout some random flats spread evenly across entire map pixel area
            // Flats are aligned with tiles, max 127x127 in billboard batch
            Vector2 tilePos = Vector2.zero;
            float scale = terrainData.heightmapScale.x;
            int dim = TerrainHelper.terrainTileDim - 1;
            for (int y = 0; y < dim; y++)
            {
                for (int x = 0; x < dim; x++)
                {
                    // Reject based on steepness
                    float steepness = terrainData.GetSteepness((float)x / dim, (float)y / dim);
                    if (steepness > maxSteepness)
                        continue;

                    // Reject if inside location rect
                    // Rect is expanded slightly to give extra clearance around locations
                    tilePos.x = x;
                    tilePos.y = y;
                    const int natureClearance = 4;
                    Rect rect = dfTerrain.MapData.locationRect;
                    if (rect.x > 0 && rect.y > 0)
                    {
                        rect.xMin -= natureClearance;
                        rect.xMin += natureClearance;
                        rect.yMin -= natureClearance;
                        rect.yMax += natureClearance;
                        if (rect.Contains(tilePos))
                            continue;
                    }

                    // Chance scaled based on map pixel height
                    // This tends to produce sparser lowlands and denser highlands
                    // Adjust or remove clamp range to influence nature generation
                    float elevationScale = (dfTerrain.MapData.worldHeight / 128f);
                    elevationScale = Mathf.Clamp(elevationScale, 0.4f, 1.0f);

                    // Chance scaled by base climate type
                    float climateScale = 1.0f;
                    DFLocation.ClimateSettings climate = MapsFile.GetWorldClimateSettings(dfTerrain.MapData.worldClimate);
                    switch (climate.ClimateType)
                    {
                        case DFLocation.ClimateBaseType.Desert:         // Just lower desert for now
                            climateScale = 0.25f;
                            break;
                    }

                    // Chance also determined by tile type
                    WorldSample sample = TerrainHelper.GetSample(ref dfTerrain.MapData.samples, x, y);
                    if (sample.record == 1)
                    {
                        // Dirt
                        if (UnityEngine.Random.Range(0f, 1f) > chanceOnDirt * elevationScale * climateScale)
                            continue;
                    }
                    else if (sample.record == 2)
                    {
                        // Grass
                        if (UnityEngine.Random.Range(0f, 1f) > chanceOnGrass * elevationScale * climateScale)
                            continue;
                    }
                    else if (sample.record == 3)
                    {
                        // Stone
                        if (UnityEngine.Random.Range(0f, 1f) > chanceOnStone * elevationScale * climateScale)
                            continue;
                    }
                    else
                    {
                        // Anything else
                        continue;
                    }

                    // Sample height and position billboard
                    Vector3 pos = new Vector3(x * scale, 0, y * scale);
                    float height = terrain.SampleHeight(pos + terrain.transform.position);
                    pos.y = height;

                    // Reject if too close to water
                    float beachLine = TerrainHelper.scaledBeachElevation * terrainScale;
                    if (height < beachLine - beachLine * 0.1f)
                        continue;

                    // Add to batch
                    int record = UnityEngine.Random.Range(1, 32);
                    dfBillboardBatch.AddItem(record, pos);
                }
            }

            // Apply new batch
            dfBillboardBatch.Apply();
        }
コード例 #9
0
        private IEnumerator UpdateLocation(int index, bool allowYield)
        {
            int  key             = TerrainHelper.MakeTerrainKey(terrainArray[index].mapPixelX, terrainArray[index].mapPixelY);
            int  playerKey       = TerrainHelper.MakeTerrainKey(MapPixelX, MapPixelY);
            bool isPlayerTerrain = (key == playerKey);

            if (terrainArray[index].active && terrainArray[index].hasLocation && terrainArray[index].updateLocation)
            {
                // Disable update flag
                terrainArray[index].updateLocation = false;

                // Create location object
                DFLocation location;
                GameObject locationObject = CreateLocationGameObject(index, out location);
                if (locationObject)
                {
                    // Add location object to dictionary
                    LocationDesc locationDesc = new LocationDesc();
                    locationDesc.locationObject = locationObject;
                    locationDesc.mapPixelX      = terrainArray[index].mapPixelX;
                    locationDesc.mapPixelY      = terrainArray[index].mapPixelY;
                    locationList.Add(locationDesc);

                    // Create billboard batch game objects for this location
                    // Streaming world always batches for performance, regardless of options
                    int natureArchive = ClimateSwaps.GetNatureArchive(LocalPlayerGPS.ClimateSettings.NatureSet, dfUnity.WorldTime.Now.SeasonValue);
                    TextureAtlasBuilder      miscBillboardAtlas    = dfUnity.MaterialReader.MiscBillboardAtlas;
                    DaggerfallBillboardBatch natureBillboardBatch  = GameObjectHelper.CreateBillboardBatchGameObject(natureArchive, locationObject.transform);
                    DaggerfallBillboardBatch lightsBillboardBatch  = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.LightsTextureArchive, locationObject.transform);
                    DaggerfallBillboardBatch animalsBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.AnimalsTextureArchive, locationObject.transform);
                    DaggerfallBillboardBatch miscBillboardBatch    = GameObjectHelper.CreateBillboardBatchGameObject(miscBillboardAtlas.AtlasMaterial, locationObject.transform);

                    // Set hide flags
                    natureBillboardBatch.hideFlags  = HideFlags.HideAndDontSave;
                    lightsBillboardBatch.hideFlags  = HideFlags.HideAndDontSave;
                    animalsBillboardBatch.hideFlags = HideFlags.HideAndDontSave;
                    miscBillboardBatch.hideFlags    = HideFlags.HideAndDontSave;

                    // RMB blocks are laid out in centre of terrain to align with ground
                    int     width   = location.Exterior.ExteriorData.Width;
                    int     height  = location.Exterior.ExteriorData.Height;
                    float   offsetX = ((8 * RMBLayout.RMBSide) - (width * RMBLayout.RMBSide)) / 2;
                    float   offsetZ = ((8 * RMBLayout.RMBSide) - (height * RMBLayout.RMBSide)) / 2;
                    Vector3 origin  = new Vector3(offsetX, 2.0f * MeshReader.GlobalScale, offsetZ);

                    // Get location data
                    DaggerfallLocation dfLocation = locationObject.GetComponent <DaggerfallLocation>();

                    // Perform layout and yield after each block is placed
                    for (int y = 0; y < height; y++)
                    {
                        for (int x = 0; x < width; x++)
                        {
                            // Set block origin for billboard batches
                            // This causes next additions to be offset by this position
                            Vector3 blockOrigin = origin + new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide));
                            natureBillboardBatch.BlockOrigin  = blockOrigin;
                            lightsBillboardBatch.BlockOrigin  = blockOrigin;
                            animalsBillboardBatch.BlockOrigin = blockOrigin;
                            miscBillboardBatch.BlockOrigin    = blockOrigin;

                            // Add block and yield
                            string     blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y));
                            GameObject go        = GameObjectHelper.CreateRMBBlockGameObject(
                                blockName,
                                false,
                                dfUnity.Option_CityBlockPrefab,
                                natureBillboardBatch,
                                lightsBillboardBatch,
                                animalsBillboardBatch,
                                miscBillboardAtlas,
                                miscBillboardBatch);
                            go.hideFlags               = HideFlags.HideAndDontSave;
                            go.transform.parent        = locationObject.transform;
                            go.transform.localPosition = blockOrigin;
                            dfLocation.ApplyClimateSettings();
                            if (allowYield)
                            {
                                yield return(new WaitForEndOfFrame());
                            }
                        }
                    }

                    // If this is the player terrain we may need to reposition player
                    if (isPlayerTerrain && repositionPlayer)
                    {
                        // Position to location and use start marker for large cities
                        bool useStartMarker = (dfLocation.Summary.LocationType == DFRegion.LocationTypes.TownCity);
                        PositionPlayerToLocation(MapPixelX, MapPixelY, dfLocation, origin, width, height, useStartMarker);
                        repositionPlayer = false;
                    }

                    // Apply billboard batches
                    natureBillboardBatch.Apply();
                    lightsBillboardBatch.Apply();
                    animalsBillboardBatch.Apply();
                    miscBillboardBatch.Apply();
                }
            }
            else if (terrainArray[index].active)
            {
                if (playerKey == key && repositionPlayer)
                {
                    PositionPlayerToTerrain(MapPixelX, MapPixelY, Vector3.zero);
                    repositionPlayer = false;
                }
            }
        }
コード例 #10
0
        /// <summary>
        /// Performs a fully standalone in-place location layout.
        /// This method is used only by editor layouts, not by streaming world.
        /// </summary>
        private void LayoutLocation(ref DFLocation location)
        {
            // Get city dimensions
            int width  = location.Exterior.ExteriorData.Width;
            int height = location.Exterior.ExteriorData.Height;

            // Create billboard batch game objects for this location
            TextureAtlasBuilder miscBillboardAtlas = null;

            summary.NatureBillboardBatch = null;
            DaggerfallBillboardBatch lightsBillboardBatch  = null;
            DaggerfallBillboardBatch animalsBillboardBatch = null;
            DaggerfallBillboardBatch miscBillboardBatch    = null;

            if (dfUnity.Option_BatchBillboards)
            {
                miscBillboardAtlas = dfUnity.MaterialReader.MiscBillboardAtlas;
                int natureArchive = ClimateSwaps.GetNatureArchive(CurrentNatureSet, CurrentSeason);
                summary.NatureBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(natureArchive, transform);
                lightsBillboardBatch         = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.LightsTextureArchive, transform);
                animalsBillboardBatch        = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.AnimalsTextureArchive, transform);
                miscBillboardBatch           = GameObjectHelper.CreateBillboardBatchGameObject(miscBillboardAtlas.AtlasMaterial, transform);
            }

            // Import blocks
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if (dfUnity.Option_BatchBillboards)
                    {
                        // Set block origin for billboard batches
                        // This causes next additions to be offset by this position
                        Vector3 blockOrigin = new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide));
                        summary.NatureBillboardBatch.BlockOrigin = blockOrigin;
                        lightsBillboardBatch.BlockOrigin         = blockOrigin;
                        animalsBillboardBatch.BlockOrigin        = blockOrigin;
                        miscBillboardBatch.BlockOrigin           = blockOrigin;
                    }

                    string     blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y));
                    GameObject go        = GameObjectHelper.CreateRMBBlockGameObject(
                        blockName,
                        dfUnity.Option_RMBGroundPlane,
                        dfUnity.Option_CityBlockPrefab,
                        summary.NatureBillboardBatch,
                        lightsBillboardBatch,
                        animalsBillboardBatch,
                        miscBillboardAtlas,
                        miscBillboardBatch,
                        CurrentNatureSet,
                        CurrentSeason);
                    go.transform.parent   = this.transform;
                    go.transform.position = new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide));
                }
            }

            // Apply batches
            if (summary.NatureBillboardBatch)
            {
                summary.NatureBillboardBatch.Apply();
            }
            if (lightsBillboardBatch)
            {
                lightsBillboardBatch.Apply();
            }
            if (animalsBillboardBatch)
            {
                animalsBillboardBatch.Apply();
            }
            if (miscBillboardBatch)
            {
                miscBillboardBatch.Apply();
            }

            // Enumerate start marker game objects
            EnumerateStartMarkers();
        }