Need professional advice on a tile texturing problem

Discuss modding questions and implementation details.
User avatar
Daniel87
Posts: 391
Joined: Thu Nov 28, 2019 6:25 pm

Need professional advice on a tile texturing problem

Post by Daniel87 »

Hi everyone and sorry for the unimaginative title.

I am struggling since more than 6 hours on a bug I am experiencing but which I cannot track down. I hope somebody familiar with the DF and DFU system to store tile texture information in byte format, can help me out here, as I am going nuts.

THE BUG:
Image

DESCRIPTION OF WHATS GOING ON:
As you can see, there is one tile textured as water, which should be a blend of stone, dirt and grass. The same texture with different rotation poses no problem and is displayed and used correctly. ONLY this orientation causes the bug.
This problem occurs, because the texture that SHOULD be used is texture index number 63.
It is saved into the Lookup Table via:

Code: Select all

lookupTable[102] = MakeLookup(63, true, true);
When we look at the native MakeLookup() function from DFU I am using to create my custom lookup table, we see that for this particular texture and flip/rotation, we would have a byte index of 11111111 or 255 in decimal. (63 + 64 + 128)

Code: Select all

static byte MakeLookup(int index, bool rotate, bool flip)
        {
            if (index > 63)
                throw new IndexOutOfRangeException("Index out of range. Valid range 0-63");
            if (rotate) index += 64;
            if (flip) index += 128;

            return (byte)index;
        }
The water tile we can see being used instead has an index of 0 with no rotation and no flip, so its binary index is 00000000.
I created Debug.Log() at several places to make sure that the texture needed indeed has the index of 255. It definitely is 255 that is returned and the code is looking at the lookupTable[102]. So basically this bug should not be there, but it is and I have no clue as to why.
It is true, it's not a long way from 11111111 to 00000000. Only one bit too much is needed, but where should this bit come from?

The following is the code with which I create my custom LookupTable as well as a LookupRegistry, which is the rosetta stone for my code to find the right index for a given tile. The program knows only "Bottom left = dirt, bottom right = rock, top right = grass and top left = stone" and looks for such a tile in the LookupRegistry. If found, it will return the index at which it was found back, so the program can look into the actual LookupTable at this index and find the right texture (which it actually does for the buggy tile as well -> 255). If the corresponding texture is not found, a error message is thrown.

Example code:

Code: Select all

lookupTable[100] = MakeLookup(63, true, false);
lookupTable[101] = MakeLookup(63, false, true);
lookupTable[102] = MakeLookup(63, true, true);
lookupTable[103] = MakeLookup(63, false, false);
lookupRegistry[100] = new int[4]{2,3,1,3};
lookupRegistry[101] = new int[4]{3,2,3,1};
lookupRegistry[102] = new int[4]{1,3,2,3};
lookupRegistry[103] = new int[4]{3,1,3,2};
(The above code is only a small part of the whole list creation. Indexes range from 0 to 112)

It crossed my mind, that it could be a problem of the Shader. I am using the original TileTextureArray shader of DFU. There is one line, where the MaxIndex is defined as 255. This should mean that the texture at index 255 (the buggy texture) is supported and should be picked properly, rather than the first texture (water) at index 0.

Shader code for reference:

Code: Select all

// Project:         Daggerfall Tools For Unity
// Copyright:       Copyright (C) 2009-2016 Gavin Clayton
// License:         MIT License (http://www.opensource.org/licenses/mit-license.php)
// Web Site:        http://www.dfworkshop.net
// Contact:         Gavin Clayton (interkarma@dfworkshop.net)
// Project Page:    https://github.com/Interkarma/daggerfall-unity
// Contributor:		Nystul

Shader "WildernessOverhaul/TilemapTextureArray" {
	Properties {
		// These params are required to stop terrain system throwing errors
		// However we won't be using them as Unity likes to init these textures
		// and will overwrite any assignments we already made
		// TODO: Combine splat painting with tilemapping
		[HideInInspector] _MainTex("BaseMap (RGB)", 2D) = "white" {}
		[HideInInspector] _Control ("Control (RGBA)", 2D) = "red" {}
		[HideInInspector] _SplatTex3("Layer 3 (A)", 2D) = "white" {}
		[HideInInspector] _SplatTex2("Layer 2 (B)", 2D) = "white" {}
		[HideInInspector] _SplatTex1("Layer 1 (G)", 2D) = "white" {}
		[HideInInspector] _SplatTex0("Layer 0 (R)", 2D) = "white" {}

		// These params are used for our shader
		_TileTexArr("Tile Texture Array", 2DArray) = "" {}
		_TileNormalMapTexArr("Tileset NormalMap Texture Array (RGBA)", 2DArray) = "" {}
		_TileMetallicGlossMapTexArr ("Tileset MetallicGlossMap Texture Array (RGBA)", 2DArray) = "" {}
		_TilemapTex("Tilemap (R)", 2D) = "red" {}
		_TilemapDim("Tilemap Dimension (in tiles)", Int) = 128
		_MaxIndex("Max Tileset Index", Int) = 255
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200

		CGPROGRAM

		#pragma target 3.5
		#pragma surface surf Standard
		#pragma glsl

		//#pragma shader_feature _NORMALMAP
		#pragma multi_compile __ _NORMALMAP

		UNITY_DECLARE_TEX2DARRAY(_TileTexArr);

		#ifdef _NORMALMAP
			UNITY_DECLARE_TEX2DARRAY(_TileNormalMapTexArr);
		#endif

		sampler2D _TilemapTex;
		float4 _TileTexArr_TexelSize;
		int _MaxIndex;
		int _TilemapDim;

		struct Input
		{
			float2 uv_MainTex : TEXCOORD0;
			//float2 uv_BumpMap : TEXCOORD1;
		};

		// compute all 4 posible configurations of terrain tiles (normal, rotated, flipped, rotated and flipped)
		// rotations and translations not conflated together, because OpenGL ES 2.0 only supports square matrices
		static float2x2 rotations[4] = {
			float2x2(1.0, 0.0, 0.0, 1.0),
			float2x2(0.0, 1.0, -1.0, 0.0),
			float2x2(-1.0, 0.0, 0.0, -1.0),
			float2x2(0.0, -1.0, 1.0, 0.0)
		};
		static float2 translations[4] = {
			float2(0.0, 0.0),
			float2(0.0, 1.0),
			float2(1.0, 1.0),
			float2(1.0, 0.0)
		};

		#define MIPMAP_BIAS (-0.5)

		inline float GetMipLevel(float2 iUV, float4 iTextureSize)
		{
			float2 dx = ddx(iUV * iTextureSize.z);
			float2 dy = ddy(iUV * iTextureSize.w);
			float d = max(dot(dx, dx), dot(dy,dy));
			return 0.5 * log2(d) + MIPMAP_BIAS;
		}

		void surf (Input IN, inout SurfaceOutputStandard o)
		{
			// Get offset to tile in atlas
			uint index = tex2D(_TilemapTex, IN.uv_MainTex).a * _MaxIndex + 0.5;

			// Offset to fragment position inside tile
			float2 unwrappedUV = IN.uv_MainTex * _TilemapDim;
			float2 uv = mul(rotations[index % 4], frac(unwrappedUV)) + translations[index % 4];

			// Sample based on gradient and set output
			float3 uv3 = float3(uv, index / 4); // compute correct texture array index from index

			float mipMapLevel = GetMipLevel(unwrappedUV, _TileTexArr_TexelSize);
			half4 c = UNITY_SAMPLE_TEX2DARRAY_SAMPLER_LOD(_TileTexArr, _TileTexArr, uv3, mipMapLevel);

			o.Albedo = c.rgb;
			o.Alpha = c.a;

			#ifdef _NORMALMAP
				o.Normal = UnpackNormal(UNITY_SAMPLE_TEX2DARRAY_SAMPLER_LOD(_TileNormalMapTexArr, _TileTexArr, uv3, mipMapLevel));
			#endif
		}
		ENDCG
	}
	FallBack "Standard"
}

Please, anyone. Help! I have no idea, why it doesn't do, what it is supposed to do.
All code can be found in my github
In Julianos we Trust.

User avatar
pango
Posts: 3347
Joined: Wed Jul 18, 2018 6:14 pm
Location: France
Contact:

Re: Need professional advice on a tile texturing problem

Post by pango »

Code: Select all

			uint index = tex2D(_TilemapTex, IN.uv_MainTex).a * _MaxIndex + 0.5;
Adding 0.5 here helped getting index right on some old card/integrated graphics that didn't get float -> int truncation right (and put water puddles all over the place...)
https://github.com/Interkarma/daggerfal ... /pull/1484
viewtopic.php?f=5&t=2519
viewtopic.php?t=2081

Looks like you're hitting a similar issue with an overflow somewhere? Hard to say, the previous problem was hard enough to track down, and was also hardware/driver dependant...
Mastodon: @pango@fosstodon.org
When a measure becomes a target, it ceases to be a good measure.
-- Charles Goodhart

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

Re: Need professional advice on a tile texturing problem

Post by Hazelnut »

I know what is causing this, I was going to look into it a bit more and then answer your query in the other thread as I had a suspicion as soon as you said what's happening. Sorry I was bit too slow I guess. :(

One of the things that the terrain tiling code does is rely on the fact that any tile that has not yet been allocated is set to 0. This causes an issue when allocating a water tile since it's value is 0 so while the tiles are being assigned, 0xFF is used instead and when the tile data is processed out of the native array later then these values are converted back to 0 or water tiles. This is mainly so that water tiles in towns etc are not overwritten by the standard terrain tiling, but it's also used so rivers and streams are not overwritten in my basic roads mod. (they're disabled since I've not done any pathing data) The same code also puts the rotate and flip bits at the other end of the byte since that's what the shader needs to work.

The easiest way to fix this would be to avoid using the 64th tile completely. How essential is it? The other value I'd be concerned about is 56 (I think, from memory) which is used for the exterior of city walls, BadLuckBurt figured out what it did, but I can't remember sorry. Maybe he will recall and post here. If you're not seeing any issues around city walls then it's probably okay.. in fact it may be something DFU is not using, but did something important in classic. Wish I had a better memory! :?

Any other solution will need a change to core DFU code allowing you to disable this conversion. You would then need to do that 0xFF->0 conversion in your mods code. I can take a look at doing this tomorrow if you definitely need that last tile value. I'll probably add another method to the terrain texturing interface that informs DFU whether the water conversion needs doing or not. Should be pretty simple but I think it may miss the next build.
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

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

Re: Need professional advice on a tile texturing problem

Post by Hazelnut »

I've updated to your latest mod code and unfortunately it's simply not working in my Unity editor. No idea what's wrong, but it wont even open the settings for the mod, throws a null exception. As yet I have no way to work on the fix, sorry.

When running your test build with DFU release 0.11.3 I get these errors:

Code: Select all

WARNING: Shader Unsupported: 'Hidden/Nature/Terrain/Utilities' - All passes removed
WARNING: Shader Did you use #pragma only_renderers and omit this platform?
ERROR: Shader Hidden/Nature/Terrain/Utilities shader is not supported on this GPU (none of subshaders/fallbacks are suitable)
302-TexArray: expected depth 302-TexArray but got 56.
I have an AMD RX580.


I will try to work on the water fix based on the older code that works, but that does mean I wont be able to test that it actually fixes the issue my end since I can't run the latest code. You will need to check that.
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

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

Re: Need professional advice on a tile texturing problem

Post by Hazelnut »

Okay I've successfully made the changes and about to submit a PR for your mod on github. This will only work with the latest DFU code as it needs the PR I just merged, and means that you will need to wait for DFU 0.11.4 to be released for your mod to work.

Regarding the shader issues, I don't know much about shaders at all so I am at a loss. My guess is you have an Nvidia card.. and probably no access to an AMD card to allow you to work on fixing the incompatibilities. I'm hoping someone else out there will offer to help out, but if not then I can run changes on my PC and give you the error messages if it helps.
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

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

Re: Need professional advice on a tile texturing problem

Post by Daniel87 »

Hazelnut wrote: Mon May 31, 2021 10:35 am Okay I've successfully made the changes and about to submit a PR for your mod on github. This will only work with the latest DFU code as it needs the PR I just merged, and means that you will need to wait for DFU 0.11.4 to be released for your mod to work.

Regarding the shader issues, I don't know much about shaders at all so I am at a loss. My guess is you have an Nvidia card.. and probably no access to an AMD card to allow you to work on fixing the incompatibilities. I'm hoping someone else out there will offer to help out, but if not then I can run changes on my PC and give you the error messages if it helps.
Hi Hazel and thank you for the PR and your help on the issue!
Sorry I was away for a while due to getting swamped in IRL work. Yes indeed I use a NVidia and have no AMD available to figure out that weird shader bug.
All I actually did was using the TilemapTextureArray shader from DFU. As far as I remember, I changed nothing on that shader, so what you could try is in WOTerrainMaterialProvider.cs line 32 change the shader to the original one from DFU:

Code: Select all

private readonly Shader shader = Shader.Find("Daggerfall/TilemapTextureArray");
This COULD do the trick, but I am not sure at all. This should works, since I didn't really change anything about the original shader besides the name. I created my own version of the same shader just to be able to experiment around the water tile bug.

Also inside of the DFU advanced settings, you need to have TextureArrays activated when running my mod. I hope this will help.

EDIT: I added your PR, but weirdly getting the error:

Code: Select all

Error (535): `Monobelisk.InterestingTerrainTexturer' does not implement interface member `DaggerfallWorkshop.ITerrainTexturing.ConvertWaterTiles()'
It seems that InterestingTerrainTexturer is not compatible with the recent changes. When I deactivate that mod, the game starts properly without exception. Makes me feel a bit guilty that because of my mod, other people also have to update their mods. :cry:
So I guess at the least, Interesting Terrain and Interesting Eroded Terrain will now not start anymore.
In Julianos we Trust.

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

Re: Need professional advice on a tile texturing problem

Post by Daniel87 »

pango wrote: Sun May 30, 2021 9:04 pm

Code: Select all

			uint index = tex2D(_TilemapTex, IN.uv_MainTex).a * _MaxIndex + 0.5;
Adding 0.5 here helped getting index right on some old card/integrated graphics that didn't get float -> int truncation right (and put water puddles all over the place...)
https://github.com/Interkarma/daggerfal ... /pull/1484
viewtopic.php?f=5&t=2519
viewtopic.php?t=2081

Looks like you're hitting a similar issue with an overflow somewhere? Hard to say, the previous problem was hard enough to track down, and was also hardware/driver dependant...
Thank you for the info!
Yeah, the tech behind DF is just too old I guess :/
Looks like a compatibility issue rather than a bug in the DFU or mod logic.
In Julianos we Trust.

User avatar
pango
Posts: 3347
Joined: Wed Jul 18, 2018 6:14 pm
Location: France
Contact:

Re: Need professional advice on a tile texturing problem

Post by pango »

Well, accelerated 3D display, allowing applications to take advantage of very different hardware to display 3d scenes with high efficiency, is a difficult engineering problem, and implementation is a wide and relatively tall stack of interfaces. Bugs and leaky abstractions are par for the course, even if it works okay for the most part, thanks to iterative fixes by hardware and software engineers around the world.
The more you use standard features implemented by libraries, the more likely such issues have been someone else's problem. Each time you implement something non-standard, you take the risk of finding compatibility issues; No matter whether you're trying to implement old school out of fashion ideas or ground breaking ones...

Talking of pending display issues, they're those: seams between ground tiles on AMD/ATI and integrated GPUs, missing pixels in 3D models...
viewtopic.php?f=5&t=1464

(and to be clear, I'm not a professional game developer, DFU is my first experience with shaders, it has been fascinating but I'm still an amateur in this domain).
Mastodon: @pango@fosstodon.org
When a measure becomes a target, it ceases to be a good measure.
-- Charles Goodhart

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

Re: Need professional advice on a tile texturing problem

Post by Hazelnut »

Daniel87 wrote: Sat Jun 05, 2021 8:28 am
Hazelnut wrote: Mon May 31, 2021 10:35 am Okay I've successfully made the changes and about to submit a PR for your mod on github. This will only work with the latest DFU code as it needs the PR I just merged, and means that you will need to wait for DFU 0.11.4 to be released for your mod to work.

Regarding the shader issues, I don't know much about shaders at all so I am at a loss. My guess is you have an Nvidia card.. and probably no access to an AMD card to allow you to work on fixing the incompatibilities. I'm hoping someone else out there will offer to help out, but if not then I can run changes on my PC and give you the error messages if it helps.
Hi Hazel and thank you for the PR and your help on the issue!
Sorry I was away for a while due to getting swamped in IRL work. Yes indeed I use a NVidia and have no AMD available to figure out that weird shader bug.
All I actually did was using the TilemapTextureArray shader from DFU. As far as I remember, I changed nothing on that shader, so what you could try is in WOTerrainMaterialProvider.cs line 32 change the shader to the original one from DFU:

Code: Select all

private readonly Shader shader = Shader.Find("Daggerfall/TilemapTextureArray");
This COULD do the trick, but I am not sure at all. This should works, since I didn't really change anything about the original shader besides the name. I created my own version of the same shader just to be able to experiment around the water tile bug.

Also inside of the DFU advanced settings, you need to have TextureArrays activated when running my mod. I hope this will help.

EDIT: I added your PR, but weirdly getting the error:

Code: Select all

Error (535): `Monobelisk.InterestingTerrainTexturer' does not implement interface member `DaggerfallWorkshop.ITerrainTexturing.ConvertWaterTiles()'
It seems that InterestingTerrainTexturer is not compatible with the recent changes. When I deactivate that mod, the game starts properly without exception. Makes me feel a bit guilty that because of my mod, other people also have to update their mods. :cry:
So I guess at the least, Interesting Terrain and Interesting Eroded Terrain will now not start anymore.
Hi, glad to help. I guessed you were busy IRL and hoped you'd respond at the w/e. Been out all day so only just seen this.

Interesting terrains mod will work just fine once an updated version is released with my road comparability changes plus built using the latest core code for DFU. It doesn't need any code changes, just a rebuild. In fact all terrain texturing mods will, but I think that's only yours, my roads, and interesting terrain mod.

So if the shader was just for your attempts to get rid of the water tile issue, then I assume you'll be removing it again now? Have you confirmed the water tile issue is fixed by my changes?
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

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

Re: Need professional advice on a tile texturing problem

Post by Hazelnut »

Shader issues turned out to be an issue with a path on my machine, so if you have a reason to keep the shader it works fine on AMD. That was a bad conclusion of mine. :oops:

I found another issue that can occur if the first run of layout nature finds a tile allowing nature in the first run though because vegetationChance is not initialised. I'll submit a PR with a fix for you.

Dirt tracks pained by the roads mod sometimes conflict with your dirt patches, not sure why this is since the integration of roads I did should ensure you leave all the tiles painted by roads mod as they are, and the painting assigns tiles from the plain ones for most seamless look. (tho it's not perfect) I'm not familiar with your code so I am just guessing here, but I think maybe it's manipulating adjacent tiles or something? Here's an example screenshot so you can see what I'm talking about. The blue areas are caused by my mod but the yellow parts are not what I expected. It's not a critical issue, so it can be left but thought I should mention it in case you can easily correct it.
Attachments
WO-Track.JPG
WO-Track.JPG (376.08 KiB) Viewed 2042 times
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

Post Reply