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:
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);
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;
}
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};
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"
}
All code can be found in my github