[MOD] Interesting Terrains [WIP]

Show off your mod creations or just a work in progress.
User avatar
Freak2121
Posts: 39
Joined: Fri Sep 08, 2017 6:14 am

Re: [MOD] Interesting Terrains [WIP]

Post by Freak2121 »

I had modified StreamingWorld.cs to allow for higher render distances.

Like this:
const int maxTerrainArray = 1024; // Maximum terrains in memory at any time

// Local player GPS for tracking player virtual position
public PlayerGPS LocalPlayerGPS;

// Number of terrains tiles to load around central terrain tile
// Each terrain is equivalent to one full city area of 8x8 RMB blocks
// 1 : ( 2 * 1 + 1 ) * ( 2 * 1 + 1 ) = 9 tiles
// 2 : ( 2 * 2 + 1 ) * ( 2 * 2 + 1 ) = 25 tiles
// 3 : ( 2 * 3 + 1 ) * ( 2 * 3 + 1 ) = 49 tiles
// 4 : ( 2 * 4 + 1 ) * ( 2 * 4 + 1 ) = 81 tiles
[Range(1, 7)]
public int TerrainDistance = 7;
except all the way to 11. 11 has proved to be very unstable (crashes after a few loads) but 5-7 seem fairly stable and performant from what I've seen.

User avatar
Freak2121
Posts: 39
Joined: Fri Sep 08, 2017 6:14 am

Re: [MOD] Interesting Terrains [WIP]

Post by Freak2121 »

I released it on Nexus. I hope you guys enjoy!

I modified the dune/desert generator slightly to match the deserts in my home country a bit more (larger and taller dunes.)
Spoiler!
Image

Image

Image
While I was testing it, I noticed that the Basic Roads mod turns all oceans into land, for some reason. I have no idea what might be causing this, simply disabling the Basic Roads mod fixes it.

Image

Anyone have any idea what might be the issue?

User avatar
carademono
Posts: 112
Joined: Sat Jul 11, 2020 3:20 pm

Re: [MOD] Interesting Terrains [WIP]

Post by carademono »

Very exciting! Downloaded and will play around with it tomorrow. I think Daniel87 has a fix for the ocean bug in conjunction with his Wilderness Overhaul mod.

User avatar
Hazelnut
Posts: 2837
Joined: Sat Aug 26, 2017 2:46 pm
Contact:

Re: [MOD] Interesting Terrains [WIP]

Post by Hazelnut »

Where is the code located? I can take a look and see what may be breaking basic roads.
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

Jarlyjarljarl
Posts: 59
Joined: Sat Aug 01, 2020 6:37 am

Re: [MOD] Interesting Terrains [WIP]

Post by Jarlyjarljarl »

Hazelnut wrote: Wed May 12, 2021 11:06 am Where is the code located? I can take a look and see what may be breaking basic roads.
The code is over here: http://forums.dfworkshop.net/viewtopic. ... 1d333efa90

I'll place it here too:

ITTerrainTexturing.cs

Code: Select all

// Project:         Daggerfall Tools For Unity
// Copyright:       Copyright (C) 2009-2021 Daggerfall Workshop
// Web Site:        http://www.dfworkshop.net
// License:         MIT License (http://www.opensource.org/licenses/mit-license.php)
// Source Code:     https://github.com/Interkarma/daggerfall-unity
// Original Author: Gavin Clayton (interkarma@dfworkshop.net)
// Contributors:    Hazelnut

using UnityEngine;
using System;
using DaggerfallConnect.Arena2;
using Unity.Jobs;
using Unity.Collections;

namespace DaggerfallWorkshop
{
    /// <summary>
    /// Generates texture tiles for terrains and uses marching squares for tile transitions.
    /// These features are very much in early stages of development.
    /// </summary>
    public class ITTerrainTexturing : ITerrainTexturing
    {
        // Use same seed to ensure continuous tiles
        const int seed = 417028;

        const byte water = 0;
        const byte dirt = 1;
        const byte grass = 2;
        const byte stone = 3;

        protected static readonly int tileDataDim = MapsFile.WorldMapTileDim + 1;

        protected static readonly int assignTilesDim = MapsFile.WorldMapTileDim;

        protected byte[] lookupTable;

        public ITTerrainTexturing()
        {
            CreateLookupTable();
        }

        public virtual JobHandle ScheduleAssignTilesJob(ITerrainSampler terrainSampler, ref MapPixelData mapData, JobHandle dependencies, bool march = true)
        {
            // Cache tile data to minimise noise sampling during march.
            NativeArray<byte> tileData = new NativeArray<byte>(tileDataDim * tileDataDim, Allocator.TempJob);
            GenerateTileDataJob tileDataJob = new GenerateTileDataJob
            {
                heightmapData = mapData.heightmapData,
                tileData = tileData,
                tdDim = tileDataDim,
                hDim = terrainSampler.HeightmapDimension,
                maxTerrainHeight = terrainSampler.MaxTerrainHeight,
                oceanElevation = terrainSampler.OceanElevation,
                beachElevation = terrainSampler.BeachElevation,
                mapPixelX = mapData.mapPixelX,
                mapPixelY = mapData.mapPixelY,
            };
            JobHandle tileDataHandle = tileDataJob.Schedule(tileDataDim * tileDataDim, 64, dependencies);

            // Assign tile data to terrain
            NativeArray<byte> lookupData = new NativeArray<byte>(lookupTable, Allocator.TempJob);
            AssignTilesJob assignTilesJob = new AssignTilesJob
            {
                lookupTable = lookupData,
                tileData = tileData,
                tilemapData = mapData.tilemapData,
                tdDim = tileDataDim,
                tDim = assignTilesDim,
                march = march,
                locationRect = mapData.locationRect,
            };
            JobHandle assignTilesHandle = assignTilesJob.Schedule(assignTilesDim * assignTilesDim, 64, tileDataHandle);

            // Add both working native arrays to disposal list.
            mapData.nativeArrayList.Add(tileData);
            mapData.nativeArrayList.Add(lookupData);

            return assignTilesHandle;
        }

        #region Marching Squares - WIP

        // Very basic marching squares for water > dirt > grass > stone transitions.
        // Cannot handle water > grass or water > stone, etc.
        // Will improve this at later date to use a wider range of transitions.
        protected struct AssignTilesJob : IJobParallelFor
        {
            [ReadOnly]
            public NativeArray<byte> tileData;
            [ReadOnly]
            public NativeArray<byte> lookupTable;

            public NativeArray<byte> tilemapData;

            public int tdDim;
            public int tDim;
            public bool march;
            public Rect locationRect;

            public void Execute(int index)
            {
                int x = JobA.Row(index, tDim);
                int y = JobA.Col(index, tDim);

                // Do nothing if in location rect as texture already set, to 0xFF if zero
                if (tilemapData[index] != 0)
                    return;

                // Assign tile texture
                if (march)
                {
                    // Get sample points
                    int tdIdx = JobA.Idx(x, y, tdDim);
                    int b0 = tileData[tdIdx];               // tileData[x, y]
                    int b1 = tileData[tdIdx + 1];           // tileData[x + 1, y]
                    int b2 = tileData[tdIdx + tdDim];       // tileData[x, y + 1]
                    int b3 = tileData[tdIdx + tdDim + 1];   // tileData[x + 1, y + 1]

                    int shape = (b0 & 1) | (b1 & 1) << 1 | (b2 & 1) << 2 | (b3 & 1) << 3;
                    int ring = (b0 + b1 + b2 + b3) >> 2;
                    int tileID = shape | ring << 4;

                    tilemapData[index] = lookupTable[tileID];
                }
                else
                {
                    tilemapData[index] = tileData[JobA.Idx(x, y, tdDim)];
                }
            }
        }

        protected struct GenerateTileDataJob : IJobParallelFor
        {
            [ReadOnly]
            public NativeArray<float> heightmapData;

            public NativeArray<byte> tileData;

            public int hDim;
            public int tdDim;
            public float maxTerrainHeight;
            public float oceanElevation;
            public float beachElevation;
            public int mapPixelX;
            public int mapPixelY;

            // Gets noise value
            private float NoiseWeight(float worldX, float worldY)
            {
                return GetNoise(worldX, worldY, 0.05f, 0.9f, 0.4f, 3, seed);
            }

            // Sets texture by range
            private byte GetWeightedRecord(float weight, float lowerGrassSpread = 0.5f, float upperGrassSpread = 0.95f)
            {
                if (weight < lowerGrassSpread)
                    return dirt;
                else if (weight > upperGrassSpread)
                    return stone;
                else
                    return grass;
            }

            // Noise function
            private float GetNoise(
                float x,
                float y,
                float frequency,
                float amplitude,
                float persistance,
                int octaves,
                int seed = 0)
            {
                float finalValue = 0f;
                for (int i = 0; i < octaves; ++i)
                {
                    finalValue += Mathf.PerlinNoise(seed + (x * frequency), seed + (y * frequency)) * amplitude;
                    frequency *= 2.0f;
                    amplitude *= persistance;
                }

                return Mathf.Clamp(finalValue, -1, 1);
            }

            public void Execute(int index)
            {
                int x = JobA.Row(index, tdDim);
                int y = JobA.Col(index, tdDim);

                // Height sample for ocean and beach tiles
                int hx = (int)Mathf.Clamp(hDim * ((float)x / (float)tdDim), 0, hDim - 1);
                int hy = (int)Mathf.Clamp(hDim * ((float)y / (float)tdDim), 0, hDim - 1);
                float height = heightmapData[JobA.Idx(hy, hx, hDim)] * maxTerrainHeight;  // x & y swapped in heightmap for TerrainData.SetHeights()
                
                // Ocean
                if (height/1.05f <= oceanElevation) {
                        tileData[index] = water;
                        return;
                }
                // Beach line
                // Adds a little +/- randomness to threshold so beach line isn't too regular
                if (height/1.05f <= beachElevation + (JobRand.Next(-50000000, -25000000) / 10000000f)) {
                        tileData[index] = dirt;
                        return;
                }

                // Get latitude and longitude of this tile
                int latitude = (int)(mapPixelX * MapsFile.WorldMapTileDim + x);
                int longitude = (int)(MapsFile.MaxWorldTileCoordZ - mapPixelY * MapsFile.WorldMapTileDim + y);

                // Set texture tile using weighted noise
                float weight = 0;
                weight += NoiseWeight(latitude, longitude);
                // TODO: Add other weights to influence texture tile generation
                tileData[index] = GetWeightedRecord(weight);
            }
        }

        // Creates lookup table
        void CreateLookupTable()
        {
            lookupTable = new byte[64];
            AddLookupRange(0, 1, 5, 48, false, 0);
            AddLookupRange(2, 1, 10, 51, true, 16);
            AddLookupRange(2, 3, 15, 53, false, 32);
            AddLookupRange(3, 3, 15, 53, true, 48);
        }

        // Adds range of 16 values to lookup table
        void AddLookupRange(int baseStart, int baseEnd, int shapeStart, int saddleIndex, bool reverse, int offset)
        {
            if (reverse)
            {
                // high > low
                lookupTable[offset] = MakeLookup(baseStart, false, false);
                lookupTable[offset + 1] = MakeLookup(shapeStart + 2, true, true);
                lookupTable[offset + 2] = MakeLookup(shapeStart + 2, false, false);
                lookupTable[offset + 3] = MakeLookup(shapeStart + 1, true, true);
                lookupTable[offset + 4] = MakeLookup(shapeStart + 2, false, true);
                lookupTable[offset + 5] = MakeLookup(shapeStart + 1, false, true);
                lookupTable[offset + 6] = MakeLookup(saddleIndex, true, false); //d
                lookupTable[offset + 7] = MakeLookup(shapeStart, true, true);
                lookupTable[offset + 8] = MakeLookup(shapeStart + 2, true, false);
                lookupTable[offset + 9] = MakeLookup(saddleIndex, false, false); //d
                lookupTable[offset + 10] = MakeLookup(shapeStart + 1, false, false);
                lookupTable[offset + 11] = MakeLookup(shapeStart, false, false);
                lookupTable[offset + 12] = MakeLookup(shapeStart + 1, true, false);
                lookupTable[offset + 13] = MakeLookup(shapeStart, false, true);
                lookupTable[offset + 14] = MakeLookup(shapeStart, true, false);
                lookupTable[offset + 15] = MakeLookup(baseEnd, false, false);
            }
            else
            {
                // low > high
                lookupTable[offset] = MakeLookup(baseStart, false, false);
                lookupTable[offset + 1] = MakeLookup(shapeStart, true, false);
                lookupTable[offset + 2] = MakeLookup(shapeStart, false, true);
                lookupTable[offset + 3] = MakeLookup(shapeStart + 1, true, false);
                lookupTable[offset + 4] = MakeLookup(shapeStart, false, false);
                lookupTable[offset + 5] = MakeLookup(shapeStart + 1, false, false);
                lookupTable[offset + 6] = MakeLookup(saddleIndex, false, false); //d
                lookupTable[offset + 7] = MakeLookup(shapeStart + 2, true, false);
                lookupTable[offset + 8] = MakeLookup(shapeStart, true, true);
                lookupTable[offset + 9] = MakeLookup(saddleIndex, true, false); //d
                lookupTable[offset + 10] = MakeLookup(shapeStart + 1, false, true);
                lookupTable[offset + 11] = MakeLookup(shapeStart + 2, false, true);
                lookupTable[offset + 12] = MakeLookup(shapeStart + 1, true, true);
                lookupTable[offset + 13] = MakeLookup(shapeStart + 2, false, false);
                lookupTable[offset + 14] = MakeLookup(shapeStart + 2, true, true);
                lookupTable[offset + 15] = MakeLookup(baseEnd, false, false);
            }
        }

        // Encodes a byte with Daggerfall tile lookup
        byte MakeLookup(int index, bool rotate, bool flip)
        {
            if (index > 55)
                throw new IndexOutOfRangeException("Index out of range. Valid range 0-55");
            if (rotate) index += 64;
            if (flip) index += 128;

            return (byte)index;
        }

        #endregion
    }
}
InterestingTerrainFixMod.cs

Code: Select all

using UnityEngine;
using DaggerfallWorkshop;
using DaggerfallWorkshop.Game;
using DaggerfallWorkshop.Game.Utility.ModSupport;
using DaggerfallWorkshop.Game.Utility.ModSupport.ModSettings;

namespace InterestingTerrainFix
{
	public class InterestingTerrainFixMod : MonoBehaviour
	{
		static Mod mod;
		static ITTerrainTexturing itTexturing;

        	static Mod InterestingTerrainMod;
        	static bool InterestingTerrainModEnabled;

		[Invoke(StateManager.StateTypes.Start , 0)]
		public static void Init(InitParams initParams)
		{
			mod = initParams.Mod;
			var modGameObject = new GameObject(mod.Title);
			modGameObject.AddComponent<InterestingTerrainFixMod>();
			
                         if (ModManager.Instance.GetModFromGUID("d08bb628-ff2e-4e2f-ae57-1d4981e61843") != null)
			{
				InterestingTerrainMod = ModManager.Instance.GetModFromGUID("d08bb628-ff2e-4e2f-ae57-1d4981e61843");
				if (InterestingTerrainMod != null && InterestingTerrainMod.Enabled) {
					InterestingTerrainModEnabled = true;
                                        Debug.Log("Interesting Terrain Fix: Interesting Terrain Mod is active");
                                }
			}
		}

		void Start()
		{
			itTexturing = new ITTerrainTexturing();
			DaggerfallUnity.Instance.TerrainTexturing = itTexturing;

			mod.IsReady = true;
			Debug.Log("Interesting Terrain Fix: Mod Initiated");
		}
	}
}
I've already compiled the fix but never got around to uploading it to the nexus or asking Daniel if he was okay with that. The only issue with it is that it gets rid of the rocky tiling texture IT adds to very steep slopes. I believe Daniel has solved this issue with one of his bits of code for his wilderness mod?

Anyways to fix the issue just compile a new mod with those scripts and place the mod below IT.

User avatar
Daniel87
Posts: 337
Joined: Thu Nov 28, 2019 6:25 pm

Re: [MOD] Interesting Terrains [WIP]

Post by Daniel87 »

Hazelnut wrote: Wed May 12, 2021 11:06 am Where is the code located? I can take a look and see what may be breaking basic roads.
If I remember it right, I fixed that ocean bug from IT in my mod by changing the texturing height for ocean. Had to increase it by ~100.

Code: Select all

// Ocean
                if (height/1.05f <= oceanElevation) {
                        tileData[index] = water;
                        return;
                }
                // Beach line
                // Adds a little +/- randomness to threshold so beach line isn't too regular
                if (height/1.05f <= beachElevation + (JobRand.Next(-50000000, -25000000) / 10000000f)) {
                        tileData[index] = dirt;
                        return;
                }
The height value divided by 1.05f might need a different number to divide by or substract something. You need to play around a bit with the values to find the sweet spot.

User avatar
Hazelnut
Posts: 2837
Joined: Sat Aug 26, 2017 2:46 pm
Contact:

Re: [MOD] Interesting Terrains [WIP]

Post by Hazelnut »

I was hoping for a github repo that I could clone but that will do I guess. Not going to look tonight though, not feeling great. Probably due to covid vaccine earlier.
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

User avatar
Freak2121
Posts: 39
Joined: Fri Sep 08, 2017 6:14 am

Re: [MOD] Interesting Terrains [WIP]

Post by Freak2121 »

Thank for the help guys! Since you guys mentioned it's an issue of ocean elevation, I looked into InterestingTerrainSampler.cs and found the line "OceanElevation = 98f;"

It merely needs to be set to 100.01f or higher to bring the ocean back. I think you guys may have overthought the solution for it, or perhaps I'm missing some issue. :)

I uploaded the fixed version to Github and Nexus.

Image

Edit: It does remove rocky cliff faces like you guys mentioned, now that I've looked a bit more. I think that's a less noticeable issue than missing oceans though. Baby steps.

Edit 2: Actually, the Basic Roads mod seems to almost completely override the terrain texturing in Interesting Terrain with Daggerfall Unity's vanilla cow-pattern dirt-grass/dirt-sand textures.

Jarlyjarljarl
Posts: 59
Joined: Sat Aug 01, 2020 6:37 am

Re: [MOD] Interesting Terrains [WIP]

Post by Jarlyjarljarl »

Freak2121 wrote: Wed May 12, 2021 10:29 pm Thank for the help guys! Since you guys mentioned it's an issue of ocean elevation, I looked into InterestingTerrainSampler.cs and found the line "OceanElevation = 98f;"

It merely needs to be set to 100.01f or higher to bring the ocean back. I think you guys may have overthought the solution for it, or perhaps I'm missing some issue. :)

I uploaded the fixed version to Github and Nexus.

Image

Edit: It does remove rocky cliff faces like you guys mentioned, now that I've looked a bit more. I think that's a less noticeable issue than missing oceans though. Baby steps.

Edit 2: Actually, the Basic Roads mod seems to almost completely override the terrain texturing in Interesting Terrain with Daggerfall Unity's vanilla cow-pattern dirt-grass/dirt-sand textures.
I agree. Not having water is essentially a deal breaker - not having a rocky shader on slopes is a minor thing that was never in game originally in the first place.

User avatar
Daniel87
Posts: 337
Joined: Thu Nov 28, 2019 6:25 pm

Re: [MOD] Interesting Terrains [WIP]

Post by Daniel87 »

Freak2121 wrote: Wed May 12, 2021 10:29 pm Thank for the help guys! Since you guys mentioned it's an issue of ocean elevation, I looked into InterestingTerrainSampler.cs and found the line "OceanElevation = 98f;"

It merely needs to be set to 100.01f or higher to bring the ocean back. I think you guys may have overthought the solution for it, or perhaps I'm missing some issue. :)

I uploaded the fixed version to Github and Nexus.

Image

Edit: It does remove rocky cliff faces like you guys mentioned, now that I've looked a bit more. I think that's a less noticeable issue than missing oceans though. Baby steps.

Edit 2: Actually, the Basic Roads mod seems to almost completely override the terrain texturing in Interesting Terrain with Daggerfall Unity's vanilla cow-pattern dirt-grass/dirt-sand textures.
Yeah, Basic Roads has its own TerrainTexturing.cs. Thats why it overrides the cliff faces. I have the same issue with my Wilderness Overhaul + Basic Roads. We will try n figure out a fix later on when I finished my mod. Best conjunction would be the heightmaps from Interesting Terrain, the roads from Basic Raods and the TerrainTexturing from my mod. I constructed a decent noise to texture the terrain depending on height + variations in landscapes. Would be cool if basic roads could just paint the roads on top of my textured terrain, rather than retexturing the whole thing.
We'll figure something out :)

Post Reply