diff --git a/Game/Addons/IncreasedTerrainDistanceMod.meta b/Game/Addons/IncreasedTerrainDistanceMod.meta new file mode 100644 index 0000000000..09e2ce79e3 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: d1bb22dc120f023418e479dd233189f8 +folderAsset: yes +timeCreated: 1445869505 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Prefabs.meta b/Game/Addons/IncreasedTerrainDistanceMod/Prefabs.meta new file mode 100644 index 0000000000..bb32f486a3 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Prefabs.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 7a111c9817a5b1d41b07a921a0a7356f +folderAsset: yes +timeCreated: 1445869505 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Prefabs/IncreasedTerrainDistanceMod.prefab b/Game/Addons/IncreasedTerrainDistanceMod/Prefabs/IncreasedTerrainDistanceMod.prefab new file mode 100644 index 0000000000..0a30ef5c9e Binary files /dev/null and b/Game/Addons/IncreasedTerrainDistanceMod/Prefabs/IncreasedTerrainDistanceMod.prefab differ diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Prefabs/IncreasedTerrainDistanceMod.prefab.meta b/Game/Addons/IncreasedTerrainDistanceMod/Prefabs/IncreasedTerrainDistanceMod.prefab.meta new file mode 100644 index 0000000000..b5b5fe4677 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Prefabs/IncreasedTerrainDistanceMod.prefab.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e3424d797238df4409df534a8905e097 +timeCreated: 1445869544 +licenseType: Free +NativeFormatImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Resources.meta b/Game/Addons/IncreasedTerrainDistanceMod/Resources.meta new file mode 100644 index 0000000000..286cde5eae --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Resources.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 04a1299935db67847b17a10d26d7f9c4 +folderAsset: yes +timeCreated: 1445869505 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeX.bin b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeX.bin new file mode 100644 index 0000000000..ca4d53b89d Binary files /dev/null and b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeX.bin differ diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeX.bin.meta b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeX.bin.meta new file mode 100644 index 0000000000..3fe772e3ff --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeX.bin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a535ea164aa7901479f9f1cd9e8185c6 +timeCreated: 1445869506 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeY.bin b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeY.bin new file mode 100644 index 0000000000..c10527d423 Binary files /dev/null and b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeY.bin differ diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeY.bin.meta b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeY.bin.meta new file mode 100644 index 0000000000..0245cfe0b7 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeY.bin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4d5ba253761494c4e8f9587c486f00ce +timeCreated: 1445869506 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_in.bin b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_in.bin new file mode 100644 index 0000000000..c21c217a38 Binary files /dev/null and b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_in.bin differ diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_in.bin.meta b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_in.bin.meta new file mode 100644 index 0000000000..5cf2f7e4c4 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_in.bin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 38ee638dec858ac4094a4de9166a6895 +timeCreated: 1445869505 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_out.bin b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_out.bin new file mode 100644 index 0000000000..2e8f437854 Binary files /dev/null and b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_out.bin differ diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_out.bin.meta b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_out.bin.meta new file mode 100644 index 0000000000..365652651c --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_out.bin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1c66fb9de5dc60645941b3472af7f012 +timeCreated: 1445869505 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts.meta b/Game/Addons/IncreasedTerrainDistanceMod/Scripts.meta new file mode 100644 index 0000000000..c43dfb6879 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 346bcd4eb2e136144a345eeaee454fce +folderAsset: yes +timeCreated: 1445869505 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts/CloneCameraPositionFromMainCamera.cs b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/CloneCameraPositionFromMainCamera.cs new file mode 100644 index 0000000000..7e3793aaf8 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/CloneCameraPositionFromMainCamera.cs @@ -0,0 +1,30 @@ +//Increased Terrain Distance Mod for Daggerfall Tools For Unity +//http://www.reddit.com/r/dftfu +//http://www.dfworkshop.net/ +//Author: Michael Rauter (a.k.a. Nystul) +//License: MIT License (http://www.opensource.org/licenses/mit-license.php) +//Version: 1.54 + +using UnityEngine; +//using System; +//using System.Collections; +//using System.Collections.Generic; +//using System.IO; +//using DaggerfallConnect; +//using DaggerfallConnect.Arena2; +//using DaggerfallConnect.Utility; +//using DaggerfallWorkshop; +using DaggerfallWorkshop.Game; +//using DaggerfallWorkshop.Utility; + +namespace ProjectIncreasedTerrainDistance +{ + public class CloneCameraPositionFromMainCamera : MonoBehaviour + { + void LateUpdate() + { + this.transform.position = Camera.main.transform.position; + } + + } +} \ No newline at end of file diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts/CloneCameraPositionFromMainCamera.cs.meta b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/CloneCameraPositionFromMainCamera.cs.meta new file mode 100644 index 0000000000..318f25d78d --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/CloneCameraPositionFromMainCamera.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a64bba0059451b44fb0617d7834a1106 +timeCreated: 1445869506 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts/CloneCameraRotationFromMainCamera.cs b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/CloneCameraRotationFromMainCamera.cs new file mode 100644 index 0000000000..15dd94d574 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/CloneCameraRotationFromMainCamera.cs @@ -0,0 +1,30 @@ +//Increased Terrain Distance Mod for Daggerfall Tools For Unity +//http://www.reddit.com/r/dftfu +//http://www.dfworkshop.net/ +//Author: Michael Rauter (a.k.a. Nystul) +//License: MIT License (http://www.opensource.org/licenses/mit-license.php) +//Version: 1.54 + +using UnityEngine; +//using System; +//using System.Collections; +//using System.Collections.Generic; +//using System.IO; +//using DaggerfallConnect; +//using DaggerfallConnect.Arena2; +//using DaggerfallConnect.Utility; +//using DaggerfallWorkshop; +using DaggerfallWorkshop.Game; +//using DaggerfallWorkshop.Utility; + +namespace ProjectIncreasedTerrainDistance +{ + public class CloneCameraRotationFromMainCamera : MonoBehaviour + { + void LateUpdate() + { + this.transform.rotation = Camera.main.transform.rotation; + } + + } +} \ No newline at end of file diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts/CloneCameraRotationFromMainCamera.cs.meta b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/CloneCameraRotationFromMainCamera.cs.meta new file mode 100644 index 0000000000..f4622e94f1 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/CloneCameraRotationFromMainCamera.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1c0005de01b61f1498f9d8a54abceb62 +timeCreated: 1445869506 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts/ImprovedTerrainSampler.cs b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/ImprovedTerrainSampler.cs new file mode 100644 index 0000000000..5a919616a5 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/ImprovedTerrainSampler.cs @@ -0,0 +1,138 @@ +//Increased Terrain Distance Mod for Daggerfall Tools For Unity +//http://www.reddit.com/r/dftfu +//http://www.dfworkshop.net/ +//Author: Michael Rauter (a.k.a. Nystul) +//License: MIT License (http://www.opensource.org/licenses/mit-license.php) +//Version: 1.54 + +using UnityEngine; +using System; +using System.Collections; +using DaggerfallConnect; +using DaggerfallConnect.Arena2; +using DaggerfallWorkshop; +using DaggerfallWorkshop.Utility; +using ProjectIncreasedTerrainDistance; + +//namespace DaggerfallWorkshop +namespace ProjectIncreasedTerrainDistance +{ + /// + /// Default TerrainSampler for StreamingWorld. + /// + public class ImprovedTerrainSampler : TerrainSampler + { + // Scale factors for this sampler implementation + public const float baseHeightScale = 8f; + public const float noiseMapScale = 4f; + public const float extraNoiseScale = 3f; + public const float scaledOceanElevation = 3.4f * baseHeightScale; + public const float scaledBeachElevation = 5.0f * baseHeightScale; + + // Max terrain height of this sampler implementation + public const float maxTerrainHeight = 26115f; + + public ImprovedTerrainSampler() + { + HeightmapDimension = defaultHeightmapDimension; + MaxTerrainHeight = maxTerrainHeight; + OceanElevation = scaledOceanElevation; + BeachElevation = scaledBeachElevation; + } + + public override void GenerateSamples(ref MapPixelData mapPixel) + { + DaggerfallUnity dfUnity = DaggerfallUnity.Instance; + + // Create samples arrays + mapPixel.tilemapSamples = new TilemapSample[MapsFile.WorldMapTileDim, MapsFile.WorldMapTileDim]; + mapPixel.heightmapSamples = new float[HeightmapDimension, HeightmapDimension]; + + // Divisor ensures continuous 0-1 range of tile samples + float div = (float)(HeightmapDimension - 1) / 3f; + + // Read neighbouring height samples for this map pixel + int mx = mapPixel.mapPixelX; + int my = mapPixel.mapPixelY; + byte[,] shm = dfUnity.ContentReader.WoodsFileReader.GetHeightMapValuesRange(mx - 2, my - 2, 4); + byte[,] lhm = dfUnity.ContentReader.WoodsFileReader.GetLargeHeightMapValuesRange(mx - 1, my, 3); + + float[,] multiplierValue = new float[4, 4]; + for (int y = 0; y < 4; y++) + { + for (int x = 0; x < 4; x++) + { + int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); + int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); + + multiplierValue[x, y] = ImprovedWorldTerrain.computeHeightMultiplier(mapPixelX, mapPixelY); + } + } + + // Extract height samples for all chunks + float averageHeight = 0; + float maxHeight = float.MinValue; + float baseHeight, noiseHeight; + float x1, x2, x3, x4; + int dim = HeightmapDimension; + //mapPixel.heightmapSamples = new float[dim, dim]; + for (int y = 0; y < dim; y++) + { + for (int x = 0; x < dim; x++) + { + float rx = (float)x / div; + float ry = (float)y / div; + int ix = Mathf.FloorToInt(rx); + int iy = Mathf.FloorToInt(ry); + float sfracx = (float)x / (float)(dim - 1); + float sfracy = (float)y / (float)(dim - 1); + float fracx = (float)(x - ix * div) / div; + float fracy = (float)(y - iy * div) / div; + float scaledHeight = 0; + + // Bicubic sample small height map for base terrain elevation + x1 = TerrainHelper.CubicInterpolator(shm[0, 3] * multiplierValue[0, 3], shm[1, 3] * multiplierValue[1, 3], shm[2, 3] * multiplierValue[2, 3], shm[3, 3] * multiplierValue[3, 3], sfracx); + x2 = TerrainHelper.CubicInterpolator(shm[0, 2] * multiplierValue[0, 2], shm[1, 2] * multiplierValue[1, 2], shm[2, 2] * multiplierValue[2, 2], shm[3, 2] * multiplierValue[3, 2], sfracx); + x3 = TerrainHelper.CubicInterpolator(shm[0, 1] * multiplierValue[0, 1], shm[1, 1] * multiplierValue[1, 1], shm[2, 1] * multiplierValue[2, 1], shm[3, 1] * multiplierValue[3, 1], sfracx); + x4 = TerrainHelper.CubicInterpolator(shm[0, 0] * multiplierValue[0, 0], shm[1, 0] * multiplierValue[1, 0], shm[2, 0] * multiplierValue[2, 0], shm[3, 0] * multiplierValue[3, 0], sfracx); + baseHeight = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, sfracy); + scaledHeight += baseHeight * baseHeightScale; + + // Bicubic sample large height map for noise mask over terrain features + x1 = TerrainHelper.CubicInterpolator(lhm[ix, iy + 0], lhm[ix + 1, iy + 0], lhm[ix + 2, iy + 0], lhm[ix + 3, iy + 0], fracx); + x2 = TerrainHelper.CubicInterpolator(lhm[ix, iy + 1], lhm[ix + 1, iy + 1], lhm[ix + 2, iy + 1], lhm[ix + 3, iy + 1], fracx); + x3 = TerrainHelper.CubicInterpolator(lhm[ix, iy + 2], lhm[ix + 1, iy + 2], lhm[ix + 2, iy + 2], lhm[ix + 3, iy + 2], fracx); + x4 = TerrainHelper.CubicInterpolator(lhm[ix, iy + 3], lhm[ix + 1, iy + 3], lhm[ix + 2, iy + 3], lhm[ix + 3, iy + 3], fracx); + noiseHeight = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, fracy); + scaledHeight += noiseHeight * noiseMapScale; + + // Additional noise mask for small terrain features at ground level + int noisex = mapPixel.mapPixelX * (HeightmapDimension - 1) + x; + int noisey = (MapsFile.MaxMapPixelY - mapPixel.mapPixelY) * (HeightmapDimension - 1) + y; + float lowFreq = TerrainHelper.GetNoise(noisex, noisey, 0.3f, 0.5f, 0.5f, 1); + float highFreq = TerrainHelper.GetNoise(noisex, noisey, 0.9f, 0.5f, 0.5f, 1); + scaledHeight += (lowFreq * highFreq) * extraNoiseScale; + + // Clamp lower values to ocean elevation + if (scaledHeight < scaledOceanElevation) + scaledHeight = scaledOceanElevation; + + // Accumulate average height + averageHeight += scaledHeight; + + // Get max height + if (scaledHeight > maxHeight) + maxHeight = scaledHeight; + + // Set sample + float height = Mathf.Clamp01(scaledHeight / MaxTerrainHeight); + mapPixel.heightmapSamples[y, x] = height; + } + } + + // Average and max heights are passed back for locations + mapPixel.averageHeight = (averageHeight /= (float)(dim * dim)) / MaxTerrainHeight; + mapPixel.maxHeight = maxHeight / MaxTerrainHeight; + } + } +} \ No newline at end of file diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts/ImprovedTerrainSampler.cs.meta b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/ImprovedTerrainSampler.cs.meta new file mode 100644 index 0000000000..e035575cf6 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/ImprovedTerrainSampler.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1da0c38b685540740a98f3aa69898f31 +timeCreated: 1445869506 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts/ImprovedWorldTerrain.cs b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/ImprovedWorldTerrain.cs new file mode 100644 index 0000000000..1f1af7215a --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/ImprovedWorldTerrain.cs @@ -0,0 +1,542 @@ +//Increased Terrain Distance Mod for Daggerfall Tools For Unity +//http://www.reddit.com/r/dftfu +//http://www.dfworkshop.net/ +//Author: Michael Rauter (a.k.a. Nystul) +//License: MIT License (http://www.opensource.org/licenses/mit-license.php) +//Version: 1.54 + +//#define CREATE_PERSISTENT_LOCATION_RANGE_MAPS +//#define CREATE_PERSISTENT_TREE_COVERAGE_MAP +#define LOAD_TREE_COVERAGE_MAP + +using UnityEngine; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; // for Encoding.UTF8 +using DaggerfallConnect; +using DaggerfallConnect.Arena2; +using DaggerfallConnect.Utility; +using DaggerfallWorkshop; +using DaggerfallWorkshop.Game; +using DaggerfallWorkshop.Utility; + +namespace ProjectIncreasedTerrainDistance +{ + /// + /// + public static class ImprovedWorldTerrain + { + const string filepathInTreeCoverageMap = "./Assets/daggerfall-unity/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_in.bin"; + + const string filepathOutTreeCoverageMap = "./Assets/daggerfall-unity/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapTreeCoverage_out.bin"; + + const string filepathMapLocationRangeX = "./Assets/daggerfall-unity/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeX.bin"; + const string filepathMapLocationRangeY = "./Assets/daggerfall-unity/Game/Addons/IncreasedTerrainDistanceMod/Resources/mapLocationRangeY.bin"; + + const float minDistanceFromWaterForExtraExaggeration = 3.0f; // when does exaggeration start in terms of how far does terrain have to be away from water + const float exaggerationFactorWaterDistance = 0.15f; // how strong is the distance from water incorporated into the multiplier + const float extraExaggerationFactorLocationDistance = 0.00075f; // how strong is the distance from locations incorporated into the multiplier + const float maxHeightsExaggerationMultiplier = 25.0f; // this directly affects maxTerrainHeight in TerrainHelper.cs: maxTerrainHeight should be maxHeightsExaggerationMultiplier * baseHeightScale * 128 + noiseMapScale * 128 + extraNoiseScale + + // 2D distance transform image - squared distance to water pixels of the world map + private static float[] mapDistanceSquaredFromWater = null; + + // 2D distance transform image - squared distance to world map pixels with location + private static float[] mapDistanceSquaredFromLocations = null; + + // map of multiplier values + private static float[] mapMultipliers = null; + + // map with location positions + private static byte[] mapLocations = null; + + // map with tree coverage + private static byte[] mapTreeCoverage = null; + + private static byte[] mapLocationRangeX = null; + private static byte[] mapLocationRangeY = null; + + // indicates if improved terrain is initialized (InitImprovedWorldTerrain() function was called) + private static bool init = false; + + public static bool IsInit { get { return init; } } + + public static void Unload() + { + mapDistanceSquaredFromWater = null; + mapDistanceSquaredFromLocations = null; + mapMultipliers = null; + mapLocations = null; + mapTreeCoverage = null; + mapLocationRangeX = null; + mapLocationRangeY = null; + + Resources.UnloadUnusedAssets(); + + System.GC.Collect(); + } + + /// + /// Gets or sets map with location positions + /// + public static byte[] MapLocations + { + get + { + return mapLocations; + } + set { mapLocations = value; } + } + + public static byte[] MapTreeCoverage + { + get + { + return mapTreeCoverage; + } + set { mapTreeCoverage = value; } + } + + public static byte[] MapLocationRangeX + { + get + { + return mapLocationRangeX; + } + set { mapLocationRangeX = value; } + } + + public static byte[] MapLocationRangeY + { + get + { + return mapLocationRangeY; + } + set { mapLocationRangeY = value; } + } + + /// + /// Gets or sets map with squared distance to water pixels. + /// + public static float[] MapDistanceSquaredFromWater + { + get { + return mapDistanceSquaredFromWater; + } + set { mapDistanceSquaredFromWater = value; } + } + + /// + /// gets the distance to water for a given world map pixel. + /// + public static float getDistanceFromWater(int mapPixelX, int mapPixelY) + { + if (init) + { + return ((float)Math.Sqrt(mapDistanceSquaredFromWater[mapPixelY * WoodsFile.mapWidthValue + mapPixelX])); + } + else + { + DaggerfallUnity.LogMessage("ImprovedWorldTerrain not initialized.", true); + return (1.0f); + } + } + + /// + /// computes the height multiplier for a given world map pixel. + /// + public static float computeHeightMultiplier(int mapPixelX, int mapPixelY) + { + if (init) + { + return (mapMultipliers[mapPixelY * WoodsFile.mapWidthValue + mapPixelX]); + } + else + { + return (1.0f); + } + } + + /// + /// initializes resources (mapDistanceSquaredFromWater, mapDistanceSquaredFromLocations, mapMultipliers) and smoothes small height map + /// + public static void InitImprovedWorldTerrain(ContentReader contentReader) + { + if (!init) + { + #if CREATE_PERSISTENT_LOCATION_RANGE_MAPS + { + int width = WoodsFile.mapWidthValue; + int height = WoodsFile.mapHeightValue; + + mapLocationRangeX = new byte[width * height]; + mapLocationRangeY = new byte[width * height]; + + //int y = 204; + //int x = 718; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + //MapPixelData MapData = TerrainHelper.GetMapPixelData(contentReader, x, y); + //if (MapData.hasLocation) + //{ + // int locationRangeX = (int)MapData.locationRect.xMax - (int)MapData.locationRect.xMin; + // int locationRangeY = (int)MapData.locationRect.yMax - (int)MapData.locationRect.yMin; + //} + + ContentReader.MapSummary mapSummary; + int regionIndex = -1, mapIndex = -1; + bool hasLocation = contentReader.HasLocation(x, y, out mapSummary); + if (hasLocation) + { + regionIndex = mapSummary.RegionIndex; + mapIndex = mapSummary.MapIndex; + DFLocation location = contentReader.MapFileReader.GetLocation(regionIndex, mapIndex); + byte locationRangeX = location.Exterior.ExteriorData.Width; + byte locationRangeY = location.Exterior.ExteriorData.Height; + + mapLocationRangeX[y * width + x] = locationRangeX; + mapLocationRangeY[y * width + x] = locationRangeY; + } + } + } + + // save to files + FileStream ostream; + ostream = new FileStream("./Assets/IncreasedTerrainDistance/Resources/mapLocationRangeX.bin", FileMode.Create, FileAccess.Write); + BinaryWriter writerMapLocationRangeX = new BinaryWriter(ostream, Encoding.UTF8); + writerMapLocationRangeX.Write(mapLocationRangeX, 0, width * height); + writerMapLocationRangeX.Close(); + ostream.Close(); + + ostream = new FileStream("./Assets/IncreasedTerrainDistance/Resources/mapLocationRangeY.bin", FileMode.Create, FileAccess.Write); + BinaryWriter writerMapLocationRangeY = new BinaryWriter(ostream, Encoding.UTF8); + writerMapLocationRangeY.Write(mapLocationRangeY, 0, width * height); + writerMapLocationRangeY.Close(); + ostream.Close(); + } +#else + { + int width = WoodsFile.mapWidthValue; + int height = WoodsFile.mapHeightValue; + + mapLocationRangeX = new byte[width * height]; + mapLocationRangeY = new byte[width * height]; + + FileStream istream; + istream = new FileStream(filepathMapLocationRangeX, FileMode.Open, FileAccess.Read); + BinaryReader readerMapLocationRangeX = new BinaryReader(istream, Encoding.UTF8); + readerMapLocationRangeX.Read(mapLocationRangeX, 0, width * height); + readerMapLocationRangeX.Close(); + istream.Close(); + + istream = new FileStream(filepathMapLocationRangeY, FileMode.Open, FileAccess.Read); + BinaryReader readerMapLocationRangeY = new BinaryReader(istream, Encoding.UTF8); + readerMapLocationRangeY.Read(mapLocationRangeY, 0, width * height); + readerMapLocationRangeY.Close(); + istream.Close(); + } + #endif + + if (mapDistanceSquaredFromWater == null) + { + byte[] heightMapArray = contentReader.WoodsFileReader.Buffer.Clone() as byte[]; + int width = WoodsFile.mapWidthValue; + int height = WoodsFile.mapHeightValue; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + if (heightMapArray[y * width + x] <= 2) + heightMapArray[y * width + x] = 1; + else + heightMapArray[y * width + x] = 0; + } + } + //now set image borders to "water" (this is a workaround to prevent mountains to become too high in north-east and south-east edge of map) + for (int y = 0; y < height; y++) + { + heightMapArray[y * width + 0] = 1; + heightMapArray[y * width + width - 1] = 1; + } + for (int x = 0; x < width; x++) + { + heightMapArray[0 * width + x] = 1; + heightMapArray[(height - 1) * width + x] = 1; + } + + mapDistanceSquaredFromWater = imageDistanceTransform(heightMapArray, width, height, 1); + + heightMapArray = null; + } + + if (mapDistanceSquaredFromLocations == null) + { + int width = WoodsFile.mapWidthValue; + int height = WoodsFile.mapHeightValue; + mapLocations = new byte[width * height]; + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + ContentReader.MapSummary summary; + if (contentReader.HasLocation(x + 1, height - 1 - y, out summary)) + mapLocations[y * width + x] = 1; + else + mapLocations[y * width + x] = 0; + } + } + mapDistanceSquaredFromLocations = imageDistanceTransform(mapLocations, width, height, 1); + } + + + + if (mapMultipliers == null) + { + int width = WoodsFile.mapWidthValue; + int height = WoodsFile.mapHeightValue; + mapMultipliers = new float[width * height]; + + // compute the multiplier and store it in mapMultipliers + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + float distanceFromWater = (float)Math.Sqrt(mapDistanceSquaredFromWater[y * WoodsFile.mapWidthValue + x]); + float distanceFromLocation = (float)Math.Sqrt(mapDistanceSquaredFromLocations[y * WoodsFile.mapWidthValue + x]); + float multiplierLocation = (distanceFromLocation * extraExaggerationFactorLocationDistance + 1.0f); // terrain distant from location gets extra exaggeration + if (distanceFromWater < minDistanceFromWaterForExtraExaggeration) // except if it is near water + multiplierLocation = 1.0f; + mapMultipliers[y * width + x] = (Math.Min(maxHeightsExaggerationMultiplier, multiplierLocation * Math.Max(1.0f, distanceFromWater * exaggerationFactorWaterDistance))); + } + } + + // multipliedMap gets smoothed + float[] newmapMultipliers = mapMultipliers.Clone() as float[]; + float[,] weights = { { 0.0625f, 0.125f, 0.0625f }, { 0.125f, 0.25f, 0.125f }, { 0.0625f, 0.125f, 0.0625f } }; + for (int y = 1; y < height - 1; y++) + { + for (int x = 1; x < width - 1; x++) + { + if (mapDistanceSquaredFromLocations[y * width + x] <= 2) // at and around locations ( <= 2 ... only map pixels in 8-connected neighborhood (distanceFromLocationMaps stores squared distances...)) + { + newmapMultipliers[y * width + x] = + weights[0, 0] * mapMultipliers[(y - 1) * width + (x - 1)] + weights[0, 1] * mapMultipliers[(y - 1) * width + (x)] + weights[0, 2] * mapMultipliers[(y - 1) * width + (x + 1)] + + weights[1, 0] * mapMultipliers[(y - 0) * width + (x - 1)] + weights[1, 1] * mapMultipliers[(y - 0) * width + (x)] + weights[1, 2] * mapMultipliers[(y - 0) * width + (x + 1)] + + weights[2, 0] * mapMultipliers[(y + 1) * width + (x - 1)] + weights[2, 1] * mapMultipliers[(y + 1) * width + (x)] + weights[2, 2] * mapMultipliers[(y + 1) * width + (x + 1)]; + } + } + } + mapMultipliers = newmapMultipliers; + + newmapMultipliers = null; + weights = null; + } + + //the height map gets smoothed as well + { + int width = WoodsFile.mapWidthValue; + int height = WoodsFile.mapHeightValue; + byte[] heightMapBuffer = contentReader.WoodsFileReader.Buffer.Clone() as byte[]; + int[,] intWeights = { { 1, 2, 1 }, { 2, 4, 2 }, { 1, 2, 1 } }; + for (int y = 1; y < height - 1; y++) + { + for (int x = 1; x < width - 1; x++) + { + if (mapDistanceSquaredFromWater[y * width + x] > 0) // check if squared distance from water is greater than zero -> if it is no water pixel + { + int value = + intWeights[0, 0] * (int)heightMapBuffer[(y - 1) * width + (x - 1)] + intWeights[0, 1] * (int)heightMapBuffer[(y - 1) * width + (x)] + intWeights[0, 2] * (int)heightMapBuffer[(y - 1) * width + (x + 1)] + + intWeights[1, 0] * (int)heightMapBuffer[(y - 0) * width + (x - 1)] + intWeights[1, 1] * (int)heightMapBuffer[(y - 0) * width + (x)] + intWeights[1, 2] * (int)heightMapBuffer[(y - 0) * width + (x + 1)] + + intWeights[2, 0] * (int)heightMapBuffer[(y + 1) * width + (x - 1)] + intWeights[2, 1] * (int)heightMapBuffer[(y + 1) * width + (x)] + intWeights[2, 2] * (int)heightMapBuffer[(y + 1) * width + (x + 1)]; + + heightMapBuffer[y * width + x] = (byte)(value / 16); + } + } + } + contentReader.WoodsFileReader.Buffer = heightMapBuffer; + + heightMapBuffer = null; + intWeights = null; + } + + // build tree coverage map + if (mapTreeCoverage == null) + { + int width = WoodsFile.mapWidthValue; + int height = WoodsFile.mapHeightValue; + mapTreeCoverage = new byte[width * height]; + + #if LOAD_TREE_COVERAGE_MAP + { + float startTreeCoverageAtElevation = ImprovedTerrainSampler.baseHeightScale * 2.0f; // ImprovedTerrainSampler.scaledBeachElevation; + float minTreeCoverageSaturated = ImprovedTerrainSampler.baseHeightScale * 6.0f; + float maxTreeCoverageSaturated = ImprovedTerrainSampler.baseHeightScale * 60.0f; + float endTreeCoverageAtElevation = ImprovedTerrainSampler.baseHeightScale * 80.0f; + //float maxElevation = 0.0f; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int readIndex = (height - 1 - y) * width + x; + float w = 0.0f; + + //float elevation = ((float)contentReader.WoodsFileReader.Buffer[(height - 1 - y) * width + x]) / 255.0f; // *mapMultipliers[index]; + float elevation = ((float)contentReader.WoodsFileReader.Buffer[readIndex]) * mapMultipliers[readIndex]; + + //maxElevation = Math.Max(maxElevation, elevation); + if ((elevation > minTreeCoverageSaturated) && (elevation < maxTreeCoverageSaturated)) + { + w = 1.0f; + } + else if ((elevation >= startTreeCoverageAtElevation) && (elevation <= minTreeCoverageSaturated)) + { + w = (elevation - startTreeCoverageAtElevation) / (minTreeCoverageSaturated - startTreeCoverageAtElevation); + } + else if ((elevation >= maxTreeCoverageSaturated) && (elevation <= endTreeCoverageAtElevation)) + { + w = 1.0f - ((elevation - maxTreeCoverageSaturated) / (endTreeCoverageAtElevation - maxTreeCoverageSaturated)); + } + + //w = 0.65f * w + 0.35f * Math.Min(6.0f, (float)Math.Sqrt(mapDistanceSquaredFromLocations[y * width + x])) / 6.0f; + + mapTreeCoverage[(y) * width + x] = Convert.ToByte(w * 255.0f); + + //if (elevation>0.05f) + // mapTreeCoverage[index] = Convert.ToByte(250); //w * 255.0f); + //else mapTreeCoverage[index] = Convert.ToByte(0); + + //if (elevation >= startTreeCoverageAtElevation) + //{ + // mapTreeCoverage[(y) * width + x] = Convert.ToByte(255.0f); + //} else{ + // mapTreeCoverage[(y) * width + x] = Convert.ToByte(0.0f); + //} + } + } + } + #else + { + FileStream istream; + istream = new FileStream(filepathInTreeCoverageMap, FileMode.Open, FileAccess.Read); + BinaryReader readerMapTreeCoverage = new BinaryReader(istream, Encoding.UTF8); + readerMapTreeCoverage.Read(mapTreeCoverage, 0, width * height); + readerMapTreeCoverage.Close(); + istream.Close(); + } + #endif + + #if CREATE_PERSISTENT_TREE_COVERAGE_MAP + { + FileStream ostream = new FileStream(filepathOutTreeCoverageMap, FileMode.Create, FileAccess.Write); + BinaryWriter writerMapTreeCoverage = new BinaryWriter(ostream, Encoding.UTF8); + writerMapTreeCoverage.Write(mapTreeCoverage, 0, width * height); + writerMapTreeCoverage.Close(); + ostream.Close(); + } + #endif + //Debug.Log(string.Format("max elevation: {0}", maxElevation)); + } + + init = true; + } + } + + /* distance transform of image (will get binarized) using squared distance */ + public static float[] imageDistanceTransform(byte[] imgIn, int width, int height, byte maskValue) + { + const float INF = 1E20f; + // allocate image and initialize + float[] imgOut = new float[width * height]; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + if (imgIn[y * width + x] == maskValue) + imgOut[y * width + x] = 0; // pixels with maskValue -> distance 0 + else + imgOut[y * width + x] = INF; // set to infinite + } + } + + distanceTransform2D(ref imgOut, width, height); + return imgOut; + } + + /* euklidean distance transform (based on an implementation of Pedro Felzenszwalb (from paper Fast distance transform in C++ by Felzenszwalb and Huttenlocher))*/ + + /* distance transform of 1d function using squared distance */ + private static float[] distanceTransform1D(ref float[] f, int n) + { + const float INF = 1E20f; + + float[] d = new float[n]; + int[] v = new int[n]; + float[] z = new float[n + 1]; + int k = 0; + v[0] = 0; + z[0] = -INF; + z[1] = +INF; + for (int q = 1; q <= n - 1; q++) + { + float s = ((f[q] + (q * q)) - (f[v[k]] + (v[k] * v[k]))) / (2 * q - 2 * v[k]); + while (s <= z[k]) + { + k--; + s = ((f[q] + (q * q)) - (f[v[k]] + (v[k] * v[k]))) / (2 * q - 2 * v[k]); + } + k++; + v[k] = q; + z[k] = s; + z[k + 1] = +INF; + } + + k = 0; + for (int q = 0; q <= n - 1; q++) + { + while (z[k + 1] < q) + k++; + d[q] = (q - v[k]) * (q - v[k]) + f[v[k]]; + } + + return d; + } + /* in-place 2D distance transform on float array using squared distance, float array must be initialized with 0 for maskValue-pixels and infinite otherwise*/ + private static void distanceTransform2D(ref float[] img, int width, int height) + { + float[] f = new float[Math.Max(width, height)]; + + // transform along columns + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + f[y] = img[y * width + x]; + } + float[] d = distanceTransform1D(ref f, height); + for (int y = 0; y < height; y++) + { + img[y * width + x] = d[y]; + } + } + + // transform along rows + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + f[x] = img[y * width + x]; + } + float[] d = distanceTransform1D(ref f, width); + for (int x = 0; x < width; x++) + { + img[y * width + x] = d[x]; + } + } + } + } +} \ No newline at end of file diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts/ImprovedWorldTerrain.cs.meta b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/ImprovedWorldTerrain.cs.meta new file mode 100644 index 0000000000..7b10b1572e --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/ImprovedWorldTerrain.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 8067275e055388547bd8736b7f7df08b +timeCreated: 1445869506 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts/IncreasedTerrainDistance.cs b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/IncreasedTerrainDistance.cs new file mode 100644 index 0000000000..ba9f32708f --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/IncreasedTerrainDistance.cs @@ -0,0 +1,994 @@ +//Increased Terrain Distance Mod for Daggerfall Tools For Unity +//http://www.reddit.com/r/dftfu +//http://www.dfworkshop.net/ +//Author: Michael Rauter (a.k.a. Nystul) +//License: MIT License (http://www.opensource.org/licenses/mit-license.php) +//Version: 1.54 +//Contributors: Lypyl + +// uncomment next line if enhanced sky mod by Lypyl is present +#define ENHANCED_SKY_AVAILABLE + +using UnityEngine; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using DaggerfallConnect; +using DaggerfallConnect.Arena2; +using DaggerfallConnect.Utility; +using DaggerfallWorkshop; +using DaggerfallWorkshop.Game; +using DaggerfallWorkshop.Utility; + +namespace ProjectIncreasedTerrainDistance +{ + /// + /// Manages a world terrain object built from the world height map for increased terrain/view distance + /// one main objective was to make everything work inside this script (and the shader for texturing) without a need for changes in any other files + /// this means as a consequence that initialization of resources of other scripts that are used inside the script needs to be finished before any other work can be done. + /// another objective was that render and loading performance should only be impacted as less as possible. I did some investigations and decided to use one big + /// unity terrain object for the whole world since it gave the best performance compared with splitting up into several terrains. The terrain has to be created only once + /// and only needs to be translated to match with the StreamingWorld component. Experiments were made in Unity 4.6, don't know if Unity 5.0 would make a difference. + /// furthermore when using unity terrain one can use unity's level of detail mechanism for geometry (if needed), and other benefits provided by unity’s terrain system. + /// a consequence is though that low-detailed terrain geometry would be also rendered at the position of the detailed terrain inside the distance from the player defined by TerrainDistance. + /// the IncreasedTerrainTilemap shader defined in file DaggerfallIncreasedTerrainTilemap.shader discards fragments inside the area containing the detailed terrain from StreamingWorld, + /// except the most outer ring of the detailed terrain blocks (TerrainDistance-1) - this is to prevent holes in the world, as a consequence in the most outer ring there can happen + /// intersections between detailed terrain and world terrain + /// to decrease the chance of intersecting geometry in the most outer ring the world map is translated down on the y-axis a bit + /// + public class IncreasedTerrainDistance : MonoBehaviour + { + #region Fields + + // Streaming World Component + public StreamingWorld streamingWorld; + + // Local player GPS for tracking player virtual position + public PlayerGPS playerGPS; + + // WeatherManager is used for seasonal textures + public WeatherManager weatherManager; + + //public RenderTexture renderTextureSky; + + //[Range(0.0001f, 0.000001f)] + //public float blendFactor = 0.000015f; + + [Range(0.0f, 300000.0f)] + public float blendStart = 120000.0f; + + [Range(0.0f, 300000.0f)] + public float blendEnd = 145000.0f; + + public float fogDensity = 0.000025f; + + + // is dfUnity ready? + bool isReady = false; + + // the height values of the world height map used as input for unity terrain function SetHeights() + float[,] worldHeights = null; + + int worldMapWidth = MapsFile.MaxMapPixelX - MapsFile.MinMapPixelX; + int worldMapHeight = MapsFile.MaxMapPixelY - MapsFile.MinMapPixelY; + + // used to track changes of playerGPS x- resp. y-position on the world map (-> a change results in an update of the terrain object's translation) + int MapPixelX = -1; + int MapPixelY = -1; + + // used to backup old center x- resp. y-position of sink-area to restore old values which are not sunk (sink area is used to decrease the chance of low-detail world height map geometry intersect detailed geometry in near distance defined by TerrainDistance) + int backupSinkAreaCenterPosX = -1; + int backupSinkAreaCenterPosY = -1; + + // unity terrain object which will hold the low-detail world map geometry, set to null initially for lazy creation + GameObject worldTerrainGameObject = null; + + // terrain material used for world texturing (will be changed when daggerFallLocation.currentSeason changes from previous setting) + Material terrainMaterial = null; + + // map holds info for tiles of terrain: r-channel... climate index, gb... currently unused, a-channel... discard-rendering flag for shader + Color32[] terrainInfoTileMap = null; + int terrainInfoTileMapDim; + + // texture for terrainInfoTileMap + Texture2D textureTerrainInfoTileMap = null; + + + ClimateSeason oldSeason = ClimateSeason.Summer; + + Texture2D textureAtlasDesertSummer = null; + Texture2D textureAtlasWoodlandSummer = null; + Texture2D textureAtlasMountainSummer = null; + Texture2D textureAtlasSwampSummer = null; + + Texture2D textureAtlasDesertWinter = null; + Texture2D textureAtlasWoodlandWinter = null; + Texture2D textureAtlasMountainWinter = null; + Texture2D textureAtlasSwampWinter = null; + + Texture2D textureAtlasDesertRain = null; + Texture2D textureAtlasWoodlandRain = null; + Texture2D textureAtlasMountainRain = null; + Texture2D textureAtlasSwampRain = null; + + // stacked near camera (used for near terrain from range 1000-15000) to prevent floating-point rendering precision problems for huge clipping ranges + Camera stackedNearCamera = null; + + // stacked camera (used for far terrain) to prevent floating-point rendering precision problems for huge clipping ranges + Camera stackedCamera = null; + + public Camera getFarTerrainCamera() { return stackedCamera; } + public Camera getStackedNearCamera() { return stackedNearCamera; } + + #if ENHANCED_SKY_AVAILABLE + EnhancedSky.SkyManager skyMan = null; + bool sampleFogColorFromSky = false; + #endif + + GameObject goRenderSkyboxToTexture = null; + Camera cameraRenderSkyboxToTexture = null; + + const int renderTextureSkyWidth = 256; + const int renderTextureSkyHeight = 256; + const int renderTextureSkyDepth = 16; + const RenderTextureFormat renderTextureSkyFormat = RenderTextureFormat.ARGB32; + RenderTexture renderTextureSky = null; + + + // instance of dfUnity + DaggerfallUnity dfUnity; + + #endregion + + #region Properties + + public bool IsReady { get { return ReadyCheck(); } } + + #endregion + + #region Unity + + static string GetGameObjectPath(GameObject obj) + { + string path = "/" + obj.name; + while (obj.transform.parent != null) + { + obj = obj.transform.parent.gameObject; + path = "/" + obj.name + path; + } + return path; + } + + void SetTerrainSampler() + { + DaggerfallUnity.Instance.TerrainSampler = new ImprovedTerrainSampler(); + } + + void Awake() + { + if (!DaggerfallUnity.Settings.Nystul_IncreasedTerrainDistance) + return; + + dfUnity = DaggerfallUnity.Instance; + + DaggerfallUnity.OnSetTerrainSampler += SetTerrainSampler; + + ImprovedTerrainSampler improvedTerrainSampler = DaggerfallUnity.Instance.TerrainSampler as ImprovedTerrainSampler; + if (improvedTerrainSampler == null) + { + DaggerfallUnity.LogMessage("IncreasedTerrainDistance: TerrainSampler instance is not of type ImprovedTerrainSampler (use ITerrainSampler terrainSampler = new ImprovedTerrainSampler() in DaggerfallUnity.cs)", true); + } + + if (!streamingWorld) + streamingWorld = GameObject.Find("StreamingWorld").GetComponent(); + if (!streamingWorld) + { + DaggerfallUnity.LogMessage("IncreasedTerrainDistance: Missing StreamingWorld reference.", true); + if (Application.isEditor) + Debug.Break(); + else + Application.Quit(); + } + + if (!playerGPS) + playerGPS = GameObject.FindGameObjectWithTag("Player").GetComponent(); + if (!playerGPS) + { + DaggerfallUnity.LogMessage("IncreasedTerrainDistance: Missing PlayerGPS reference.", true); + if (Application.isEditor) + Debug.Break(); + else + Application.Quit(); + } + + if (!weatherManager) + { + //weatherManager = GameObject.Find("WeatherManager").GetComponent(); + WeatherManager[] weatherManagers = Resources.FindObjectsOfTypeAll(); + foreach (WeatherManager currentWeatherManager in weatherManagers) + { + GameObject go = currentWeatherManager.gameObject; + string objectPathInHierarchy = GetGameObjectPath(go); + if (objectPathInHierarchy == "/Exterior/WeatherManager") + { + weatherManager = currentWeatherManager; + break; + } + } + } + if (!weatherManager) + { + DaggerfallUnity.LogMessage("IncreasedTerrainDistance: Missing WeatherManager reference.", true); + if (Application.isEditor) + Debug.Break(); + else + Application.Quit(); + } + } + + void OnEnable() + { + if (!DaggerfallUnity.Settings.Nystul_IncreasedTerrainDistance) + return; + + FloatingOrigin.OnPositionUpdate += WorldTerrainUpdatePosition; + + StreamingWorld.OnReady += UpdateTerrainInfoTilemap; // important to do actions after TerrainHelper.DilateCoastalClimate() was called in StreamingWorld.ReadyCheck() + + StreamingWorld.OnTeleportToCoordinates += UpdateWorldTerrain; + + PlayerEnterExit.OnTransitionExterior += TransitionToExterior; + PlayerEnterExit.OnTransitionDungeonExterior += TransitionToExterior; + } + + void OnDisable() + { + if (!DaggerfallUnity.Settings.Nystul_IncreasedTerrainDistance) + return; + + FloatingOrigin.OnPositionUpdate -= WorldTerrainUpdatePosition; + + StreamingWorld.OnReady -= UpdateTerrainInfoTilemap; + + StreamingWorld.OnTeleportToCoordinates -= UpdateWorldTerrain; + + PlayerEnterExit.OnTransitionExterior -= TransitionToExterior; + PlayerEnterExit.OnTransitionDungeonExterior -= TransitionToExterior; + } + + void InitImprovedWorldTerrain() + { + // preprocess heights + ImprovedWorldTerrain.InitImprovedWorldTerrain(DaggerfallUnity.Instance.ContentReader); + } + + void WorldTerrainUpdatePosition(Vector3 offset) + { + if (worldTerrainGameObject != null) + { + // do not forget to update shader parameters (needed for correct fragment discarding for terrain tiles of map pixels inside TerrainDistance-1 area (the detailed terrain)) + Terrain terrain = worldTerrainGameObject.GetComponent(); + terrain.materialTemplate.SetInt("_PlayerPosX", this.playerGPS.CurrentMapPixel.X); + terrain.materialTemplate.SetInt("_PlayerPosY", this.playerGPS.CurrentMapPixel.Y); + + //Debug.Log("update from floating origin event"); + updatePositionWorldTerrain(ref worldTerrainGameObject, offset); + } + } + + void UpdateTerrainInfoTilemap() + { + InitImprovedWorldTerrain(); + + worldTerrainGameObject = generateWorldTerrain(); + + GameObject goExterior = null; + + GameObject[] gameObjects = Resources.FindObjectsOfTypeAll(); + foreach (GameObject go in gameObjects) + { + string objectPathInHierarchy = GetGameObjectPath(go); + if (objectPathInHierarchy == "/Exterior") + { + goExterior = go; + } + } + + worldTerrainGameObject.transform.SetParent(goExterior.transform); + + SetUpCameras(); + + Terrain terrain = worldTerrainGameObject.GetComponent(); + + int worldMapResolution = Math.Max(worldMapWidth, worldMapHeight); + + int[] climateMap = new int[worldMapResolution * worldMapResolution]; + for (int y = 0; y < worldMapHeight; y++) + { + for (int x = 0; x < worldMapWidth; x++) + { + // get climate record for this map pixel + int worldClimate = dfUnity.ContentReader.MapFileReader.GetClimateIndex(x, y); + climateMap[(worldMapHeight - 1 - y) * worldMapResolution + x] = worldClimate; + } + } + + terrainInfoTileMapDim = terrain.terrainData.heightmapResolution - 1; + + terrainInfoTileMap = new Color32[terrainInfoTileMapDim * terrainInfoTileMapDim]; + + // Assign tile data to tilemap + Color32 tileColor = new Color32(0, 0, 0, 0); + for (int y = 0; y < worldMapHeight; y++) + { + for (int x = 0; x < worldMapWidth; x++) + { + // Get sample tile data + int climateIndex = climateMap[y * worldMapWidth + x]; + + // get location data + //byte hasLocation = ImprovedWorldTerrain.MapLocations[y * worldMapWidth + x]; + + byte locationMapRangeX = 0; + byte locationMapRangeY = 0; + if (x < worldMapWidth - 1) + { + locationMapRangeX = ImprovedWorldTerrain.MapLocationRangeX[(500 - 1 - y) * worldMapWidth + x + 1]; + locationMapRangeY = ImprovedWorldTerrain.MapLocationRangeY[(500 - 1 - y) * worldMapWidth + x + 1]; + } + + byte treeCoverage = ImprovedWorldTerrain.MapTreeCoverage[y * worldMapWidth + x]; + + // Assign to tileMap + tileColor.r = Convert.ToByte(climateIndex); + tileColor.g = treeCoverage; // hasLocation; + tileColor.b = locationMapRangeX; + tileColor.a = locationMapRangeY; + terrainInfoTileMap[y * terrainInfoTileMapDim + x] = tileColor; + } + } + + textureTerrainInfoTileMap = new Texture2D(terrainInfoTileMapDim, terrainInfoTileMapDim, TextureFormat.RGBA32, false); + textureTerrainInfoTileMap.filterMode = FilterMode.Point; + textureTerrainInfoTileMap.wrapMode = TextureWrapMode.Clamp; + + // Promote tileMap + textureTerrainInfoTileMap.SetPixels32(terrainInfoTileMap); + textureTerrainInfoTileMap.Apply(false); + + terrainMaterial.SetTexture("_MainTex", textureTerrainInfoTileMap); + terrainMaterial.SetTexture("_TilemapTex", textureTerrainInfoTileMap); + + terrainMaterial.SetInt("_TilemapDim", terrainInfoTileMapDim); + + terrainMaterial.mainTexture = textureTerrainInfoTileMap; + } + + void Start() + { + if (!DaggerfallUnity.Settings.Nystul_IncreasedTerrainDistance) + return; + + if (worldTerrainGameObject == null) // lazy creation + { + if (!ReadyCheck()) + return; + + if (!dfUnity.MaterialReader.IsReady) + return; + + int layerExtendedTerrain = LayerMask.NameToLayer("WorldTerrain"); + if (layerExtendedTerrain == -1) + { + DaggerfallUnity.LogMessage("Layer with name \"WorldTerrain\" missing! Set it in Unity Editor under \"Edit/Project Settings/Tags and Layers!\"", true); + if (Application.isEditor) + Debug.Break(); + else + Application.Quit(); + } + + Camera.main.farClipPlane = 1200.0f; + + if (!stackedNearCamera) + { + GameObject goStackedNearCamera = new GameObject("stackedNearCamera"); + stackedNearCamera = goStackedNearCamera.AddComponent(); + stackedNearCamera.cullingMask = Camera.main.cullingMask; + stackedNearCamera.nearClipPlane = 980.0f; + stackedNearCamera.farClipPlane = 15000.0f; + stackedNearCamera.fieldOfView = Camera.main.fieldOfView; + stackedNearCamera.renderingPath = Camera.main.renderingPath; + stackedNearCamera.gameObject.AddComponent(); + stackedNearCamera.gameObject.AddComponent(); + stackedNearCamera.transform.SetParent(this.transform); + } + + + + if (!stackedCamera) + { + GameObject goStackedCamera = new GameObject("stackedCamera"); + stackedCamera = goStackedCamera.AddComponent(); + stackedCamera.cullingMask = 1 << layerExtendedTerrain; + stackedCamera.nearClipPlane = 980.0f; + stackedCamera.farClipPlane = 300000.0f; + stackedCamera.fieldOfView = Camera.main.fieldOfView; + stackedCamera.renderingPath = Camera.main.renderingPath; + stackedCamera.gameObject.AddComponent(); + stackedCamera.gameObject.AddComponent(); + stackedCamera.transform.SetParent(this.transform); + } + + if (!renderTextureSky) + { + renderTextureSky = new RenderTexture(renderTextureSkyWidth, renderTextureSkyHeight, renderTextureSkyDepth, renderTextureSkyFormat); + } + + if (!goRenderSkyboxToTexture) + { + goRenderSkyboxToTexture = new GameObject("stackedCameraSkyboxRenderToTextureGeneric", typeof(Camera)); + goRenderSkyboxToTexture.transform.SetParent(this.transform); + } + + if (!cameraRenderSkyboxToTexture) + { + cameraRenderSkyboxToTexture = goRenderSkyboxToTexture.GetComponent(); + + goRenderSkyboxToTexture.AddComponent(); + goRenderSkyboxToTexture.AddComponent(); + + cameraRenderSkyboxToTexture.clearFlags = CameraClearFlags.Skybox; + cameraRenderSkyboxToTexture.cullingMask = 0; // nothing + cameraRenderSkyboxToTexture.nearClipPlane = stackedCamera.nearClipPlane; + cameraRenderSkyboxToTexture.farClipPlane = stackedCamera.farClipPlane; + cameraRenderSkyboxToTexture.fieldOfView = stackedCamera.fieldOfView; + } + } + } + + void OnDestroy() + { + ImprovedWorldTerrain.Unload(); + + worldHeights = null; + worldTerrainGameObject = null; + terrainMaterial = null; + terrainInfoTileMap = null; + textureTerrainInfoTileMap = null; + + textureAtlasDesertSummer = null; + textureAtlasWoodlandSummer = null; + textureAtlasMountainSummer = null; + textureAtlasSwampSummer = null; + + textureAtlasDesertWinter = null; + textureAtlasWoodlandWinter = null; + textureAtlasMountainWinter = null; + textureAtlasSwampWinter = null; + + textureAtlasDesertRain = null; + textureAtlasWoodlandRain = null; + textureAtlasMountainRain = null; + textureAtlasSwampRain = null; + + Resources.UnloadUnusedAssets(); + + System.GC.Collect(); + } + + void TransitionToExterior(PlayerEnterExit.TransitionEventArgs args) + { + SetUpCameras(); + } + + void SetUpCameras() + { + // set up camera stack - AFTER layer "WorldTerrain" has been assigned to worldTerrainGameObject (is done in function generateWorldTerrain()) + + Camera.main.clearFlags = CameraClearFlags.Depth; + stackedNearCamera.clearFlags = CameraClearFlags.Depth; + stackedCamera.clearFlags = CameraClearFlags.Depth; + stackedCamera.depth = 1; // rendered first + stackedNearCamera.depth = 2; + Camera.main.depth = 3; // renders over stacked camera + + cameraRenderSkyboxToTexture.depth = -10; // make sure to render first + cameraRenderSkyboxToTexture.renderingPath = stackedCamera.renderingPath; + + #if ENHANCED_SKY_AVAILABLE + //skyMan = GameObject.Find("EnhancedSkyController").GetComponent(); + EnhancedSky.SkyManager[] skyManagers = Resources.FindObjectsOfTypeAll(); + foreach (EnhancedSky.SkyManager currentSkyManager in skyManagers) + { + GameObject go = currentSkyManager.gameObject; + string objectPathInHierarchy = GetGameObjectPath(go); + if (objectPathInHierarchy == "/Exterior/EnhancedSkyController") + { + skyMan = currentSkyManager; + break; + } + } + + if (skyMan) + { + sampleFogColorFromSky = true; + } + #endif + + cameraRenderSkyboxToTexture.targetTexture = renderTextureSky; + } + + void Update() + { + if (worldTerrainGameObject != null) + { + // TODO: make sure this block is not executed when in floating origin mode (otherwise position update is done twice) + // Handle moving to new map pixel or first-time init + DFPosition curMapPixel = playerGPS.CurrentMapPixel; + if (curMapPixel.X != MapPixelX || + curMapPixel.Y != MapPixelY) + { + UpdateWorldTerrain(curMapPixel); + } + + Terrain terrain = worldTerrainGameObject.GetComponent(); + if (terrain) + { + /* + if (RenderSettings.fog == true) + { + if (RenderSettings.fogMode == FogMode.Linear) + terrainMaterial.SetInt("_FogMode", 1); + else if (RenderSettings.fogMode == FogMode.Exponential) + terrainMaterial.SetInt("_FogMode", 2); + else if (RenderSettings.fogMode == FogMode.ExponentialSquared) + terrainMaterial.SetInt("_FogMode", 3); + } + else + { + terrainMaterial.SetInt("_FogMode", 0); + } + */ + terrainMaterial.SetInt("_FogMode", 2); + terrainMaterial.SetFloat("_FogDensity", fogDensity); + + + #if ENHANCED_SKY_AVAILABLE + if ((sampleFogColorFromSky == true) && (!skyMan.IsOvercast)) + { + terrain.materialTemplate.SetFloat("_FogFromSkyTex", 1); + } + else + { + terrain.materialTemplate.SetFloat("_FogFromSkyTex", 0); + } + #endif + + //terrain.materialTemplate.SetFloat("_BlendFactor", blendFactor); + terrain.materialTemplate.SetFloat("_BlendStart", blendStart); + terrain.materialTemplate.SetFloat("_BlendEnd", blendEnd); + } + + updateSeasonalTextures(); // this is necessary since climate changes may occur after UpdateWorldTerrain() has been invoked, TODO: an event would be ideal to trigger updateSeasonalTextures() instead + } + } + + void UpdateWorldTerrain() + { + UpdateWorldTerrain(playerGPS.CurrentMapPixel); + } + + void UpdateWorldTerrain(DFPosition worldPos) + { + if (worldTerrainGameObject != null) // sometimes it can happen that this point is reached before worldTerrainGameObject was created, in such case we just skip + { + // do not forget to update shader parameters (needed for correct fragment discarding for terrain tiles of map pixels inside TerrainDistance-1 area (the detailed terrain)) + Terrain terrain = worldTerrainGameObject.GetComponent(); + terrain.materialTemplate.SetInt("_PlayerPosX", this.playerGPS.CurrentMapPixel.X); + terrain.materialTemplate.SetInt("_PlayerPosY", this.playerGPS.CurrentMapPixel.Y); + + Vector3 offset = new Vector3(0.0f, 0.0f, 0.0f); + updatePositionWorldTerrain(ref worldTerrainGameObject, offset); + + updateSeasonalTextures(); + + Resources.UnloadUnusedAssets(); + + //System.GC.Collect(); + } + } + + #endregion + + #region Private Methods + + private void updateSeasonalTextures() + { + if (!weatherManager) + return; + + ClimateSeason currentSeason; + + // Get season and weather + if (dfUnity.WorldTime.Now.SeasonValue == DaggerfallDateTime.Seasons.Winter) + { + currentSeason = ClimateSeason.Winter; + } + else + { + currentSeason = ClimateSeason.Summer; + + if (weatherManager.IsRaining) + { + currentSeason = ClimateSeason.Rain; + } + else if (weatherManager.IsSnowing) // should not happen (snow in summer would be weird...) + { + currentSeason = ClimateSeason.Winter; + } + } + + if (currentSeason != oldSeason) + { + switch (currentSeason) + { + case ClimateSeason.Summer: + terrainMaterial.SetTexture("_TileAtlasTexDesert", textureAtlasDesertSummer); + terrainMaterial.SetTexture("_TileAtlasTexWoodland", textureAtlasWoodlandSummer); + terrainMaterial.SetTexture("_TileAtlasTexMountain", textureAtlasMountainSummer); + terrainMaterial.SetTexture("_TileAtlasTexSwamp", textureAtlasSwampSummer); + terrainMaterial.SetInt("_TextureSetSeasonCode", 0); + break; + case ClimateSeason.Winter: + terrainMaterial.SetTexture("_TileAtlasTexDesert", textureAtlasDesertWinter); + terrainMaterial.SetTexture("_TileAtlasTexWoodland", textureAtlasWoodlandWinter); + terrainMaterial.SetTexture("_TileAtlasTexMountain", textureAtlasMountainWinter); + terrainMaterial.SetTexture("_TileAtlasTexSwamp", textureAtlasSwampWinter); + terrainMaterial.SetInt("_TextureSetSeasonCode", 1); + break; + case ClimateSeason.Rain: + terrainMaterial.SetTexture("_TileAtlasTexDesert", textureAtlasDesertRain); + terrainMaterial.SetTexture("_TileAtlasTexWoodland", textureAtlasWoodlandRain); + terrainMaterial.SetTexture("_TileAtlasTexMountain", textureAtlasMountainRain); + terrainMaterial.SetTexture("_TileAtlasTexSwamp", textureAtlasSwampRain); + terrainMaterial.SetInt("_TextureSetSeasonCode", 2); + break; + default: + terrainMaterial.SetTexture("_TileAtlasTexDesert", textureAtlasDesertSummer); + terrainMaterial.SetTexture("_TileAtlasTexWoodland", textureAtlasWoodlandSummer); + terrainMaterial.SetTexture("_TileAtlasTexMountain", textureAtlasMountainSummer); + terrainMaterial.SetTexture("_TileAtlasTexSwamp", textureAtlasSwampSummer); + terrainMaterial.SetInt("_TextureSetSeasonCode", 0); + break; + } + oldSeason = currentSeason; + } + } + + private void updatePositionWorldTerrain(ref GameObject terrainGameObject, Vector3 offset) + { + // reduce chance of geometry intersections of world terrain and the most outer ring of detailed terrain of the StreamingWorld component + float extraTranslationY = -10.0f; // -12.5f * streamingWorld.TerrainScale; + + // world scale computed as in StreamingWorld.cs and DaggerfallTerrain.cs scripts + float scale = MapsFile.WorldMapTerrainDim * MeshReader.GlobalScale; + + // get displacement in world map pixels + float xdif = + 1 - playerGPS.CurrentMapPixel.X; + float zdif = worldMapHeight - 1 - playerGPS.CurrentMapPixel.Y; + + // world map level transform (for whole world map pixels) + Vector3 worldMapLevelTransform; + worldMapLevelTransform.x = xdif * scale; + worldMapLevelTransform.y = extraTranslationY; + worldMapLevelTransform.z = -zdif * scale; + + // used location [693,225] and [573,27] for debugging the local translation + + // local world level transform (for inter- world map pixels) + float localTransformX = 0.0f; // old obsolete computation formula was: (float)Math.Floor((playerGPS.transform.position.x) / scale) * scale; // (float)Math.Floor((cameraPos.x) / scale) * scale; + float localTransformZ = 0.0f; // old obsolete computation formula was: (float)Math.Floor((playerGPS.transform.position.z) / scale) * scale; // (float)Math.Floor((cameraPos.z) / scale) * scale; + float localTransformY = 0.0f; + + localTransformX += streamingWorld.WorldCompensation.x; + localTransformZ += streamingWorld.WorldCompensation.z; + localTransformY += streamingWorld.WorldCompensation.y; + + float remainderX; + if (offset.x != 0) + { + remainderX = playerGPS.transform.position.x - (float)Math.Floor((playerGPS.transform.position.x) / Math.Abs(offset.x)) * Math.Abs(offset.x); + } + else + { + remainderX = playerGPS.transform.position.x; + } + + float remainderZ; + if (offset.z != 0) + { + remainderZ = playerGPS.transform.position.z - (float)Math.Floor((playerGPS.transform.position.z) / Math.Abs(offset.z)) * Math.Abs(offset.z); + } + else + { + remainderZ = playerGPS.transform.position.z; + } + + //Debug.Log(string.Format("remainderX, remainderZ: {0}, {1}; playerGPS x,z: {2},{3}", remainderX, remainderZ, playerGPS.transform.position.x, playerGPS.transform.position.z)); + + localTransformX += (float)Math.Floor((-streamingWorld.WorldCompensation.x + remainderX) / scale) * scale; + localTransformZ += (float)Math.Floor((-streamingWorld.WorldCompensation.z + remainderZ) / scale) * scale; + + // compute composite transform and apply it to terrain object + Vector3 finalTransform = new Vector3(worldMapLevelTransform.x + localTransformX, worldMapLevelTransform.y + localTransformY, worldMapLevelTransform.z + localTransformZ); + terrainGameObject.gameObject.transform.localPosition = finalTransform; + + + + int TerrainDistance = streamingWorld.TerrainDistance; + + // sinkHeight for world terrain height values inside TerrainDistance radius from player position + float sinkHeight = (100.0f * streamingWorld.TerrainScale) / DaggerfallUnity.Instance.TerrainSampler.MaxTerrainHeight; + + Terrain terrain = terrainGameObject.GetComponent(); + + // restore (previously) decreased terrain height values in sink area + float[,] heightValues = new float[TerrainDistance * 2, TerrainDistance * 2]; // only TerrainDistance * 2 height values are affected, not TerrainDistance * 2 + 1 values + + if ((backupSinkAreaCenterPosX != -1) && (backupSinkAreaCenterPosY != -1)) // no values needs to be restored on very first run (since nothing was decreased before...) + { + for (int y = -(TerrainDistance - 1); y <= TerrainDistance; y++) + { + for (int x = -(TerrainDistance - 1); x <= TerrainDistance; x++) + { + int xpos = backupSinkAreaCenterPosX + x + 1; + int ypos = (worldMapHeight - 1 - backupSinkAreaCenterPosY) + y + 1; + if ((xpos >= 0) && (xpos < worldMapWidth) && (ypos >= 0) && (ypos < worldMapHeight)) + { + heightValues[y + TerrainDistance - 1, x + TerrainDistance - 1] = worldHeights[ypos, xpos]; + } + } + } + terrain.terrainData.SetHeights(backupSinkAreaCenterPosX - TerrainDistance + 2, worldMapHeight - 1 - backupSinkAreaCenterPosY - TerrainDistance + 2, heightValues); + } + + backupSinkAreaCenterPosX = -2 + playerGPS.CurrentMapPixel.X; + backupSinkAreaCenterPosY = +1 + playerGPS.CurrentMapPixel.Y; + + // decrease terrain height values in sink area + for (int y = -(TerrainDistance - 1); y <= TerrainDistance; y++) + { + for (int x = -(TerrainDistance - 1); x <= TerrainDistance; x++) + { + int xpos = backupSinkAreaCenterPosX + x + 1; + int ypos = (worldMapHeight - 1 - backupSinkAreaCenterPosY) + y + 1; + if ((xpos >= 0) && (xpos < worldMapWidth) && (ypos >= 0) && (ypos < worldMapHeight)) + { + heightValues[y + TerrainDistance - 1, x + TerrainDistance - 1] = worldHeights[ypos, xpos] - sinkHeight; + } + } + } + terrain.terrainData.SetHeights(backupSinkAreaCenterPosX - TerrainDistance + 2, worldMapHeight - 1 - backupSinkAreaCenterPosY - TerrainDistance + 2, heightValues); + + heightValues = null; + + if (worldTerrainGameObject != null) // sometimes it can happen that this point is reached before worldTerrainGameObject was created, in such case we just skip + { + // update water height (thanks Lypyl!!!): + Vector3 vecWaterHeight = new Vector3(0.0f, (DaggerfallUnity.Instance.TerrainSampler.OceanElevation + 1.0f) * streamingWorld.TerrainScale, 0.0f); // water height level on y-axis (+1.0f some coastlines are incorrect otherwise) + Vector3 vecWaterHeightTransformed = worldTerrainGameObject.transform.TransformPoint(vecWaterHeight); // transform to world coordinates + terrainMaterial.SetFloat("_WaterHeightTransformed", vecWaterHeightTransformed.y); + } + + MapPixelX = playerGPS.CurrentMapPixel.X; + MapPixelY = playerGPS.CurrentMapPixel.Y; + } + + private GameObject generateWorldTerrain() + { + // Create Unity Terrain game object + GameObject terrainGameObject = Terrain.CreateTerrainGameObject(null); + terrainGameObject.name = string.Format("WorldTerrain"); + + terrainGameObject.gameObject.transform.localPosition = Vector3.zero; + + // assign terrainGameObject to layer "WorldTerrain" if available (used for rendering with secondary camera to prevent floating-point precision problems with huge clipping ranges) + int layerExtendedTerrain = LayerMask.NameToLayer("WorldTerrain"); + if (layerExtendedTerrain != -1) + terrainGameObject.layer = layerExtendedTerrain; + + int worldMapResolution = Math.Max(worldMapWidth, worldMapHeight); + + if (worldHeights == null) + { + worldHeights = new float[worldMapResolution, worldMapResolution]; + } + + for (int y = 0; y < worldMapHeight; y++) + { + for (int x = 0; x < worldMapWidth; x++) + { + // get height data for this map pixel from world map and scale it to approximately match StreamingWorld's terrain heights + float sampleHeight = Convert.ToSingle(dfUnity.ContentReader.WoodsFileReader.GetHeightMapValue(x, y)); + + sampleHeight *= (ImprovedWorldTerrain.computeHeightMultiplier(x, y) * ImprovedTerrainSampler.baseHeightScale + ImprovedTerrainSampler.noiseMapScale); + + // make ocean elevation the lower limit + if (sampleHeight < ImprovedTerrainSampler.scaledOceanElevation) + { + sampleHeight = ImprovedTerrainSampler.scaledOceanElevation; + } + + // normalize with TerrainHelper.maxTerrainHeight + worldHeights[worldMapHeight - 1 - y, x] = Mathf.Clamp01(sampleHeight / ImprovedTerrainSampler.maxTerrainHeight); + } + } + + // Basemap not used and is just pushed far away + const float basemapDistance = 1000000f; + + // Ensure TerrainData is created + Terrain terrain = terrainGameObject.GetComponent(); + if (terrain.terrainData == null) + { + // Setup terrain data + TerrainData terrainData = new TerrainData(); + terrainData.name = "TerrainData"; + + // this is not really an assignment! you tell unity terrain what resolution you want for your heightmap and it will allocate resources and take the next power of 2 increased by 1 as heightmapResolution... + terrainData.heightmapResolution = worldMapResolution; + + float heightmapResolution = terrainData.heightmapResolution; + // Calculate width and length of terrain in world units + float terrainSize = ((MapsFile.WorldMapTerrainDim * MeshReader.GlobalScale) * (heightmapResolution - 1.0f)); + + + terrainData.size = new Vector3(terrainSize, ImprovedTerrainSampler.maxTerrainHeight, terrainSize); + + //terrainData.size = new Vector3(terrainSize, TerrainHelper.maxTerrainHeight * TerrainScale * worldMapResolution, terrainSize); + terrainData.SetDetailResolution(worldMapResolution, 16); + terrainData.alphamapResolution = worldMapResolution; + terrainData.baseMapResolution = worldMapResolution; + + // Apply terrain data + terrain.terrainData = terrainData; + terrain.basemapDistance = basemapDistance; + } + + terrain.heightmapPixelError = 0; // 0 ... prevent unity terrain lod approach, set to higher values to enable it + //terrain.castShadows = true; + + // Promote heights + Vector3 size = terrain.terrainData.size; + terrain.terrainData.size = new Vector3(size.x, ImprovedTerrainSampler.maxTerrainHeight * streamingWorld.TerrainScale, size.z); + terrain.terrainData.SetHeights(0, 0, worldHeights); + + + // update world terrain position - do this before terrainGameObject.transform invocation, so that object2world matrix is updated with correct values + Vector3 offset = new Vector3(0.0f, 0.0f, 0.0f); + updatePositionWorldTerrain(ref terrainGameObject, offset); + + textureAtlasDesertSummer = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(2).albedoMap; + textureAtlasDesertSummer.filterMode = FilterMode.Point; + + textureAtlasWoodlandSummer = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(302).albedoMap; + textureAtlasWoodlandSummer.filterMode = FilterMode.Point; + + textureAtlasMountainSummer = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(102).albedoMap; + textureAtlasMountainSummer.filterMode = FilterMode.Point; + + textureAtlasSwampSummer = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(402).albedoMap; + textureAtlasSwampSummer.filterMode = FilterMode.Point; + + textureAtlasDesertWinter = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(3).albedoMap; + textureAtlasDesertWinter.filterMode = FilterMode.Point; + + textureAtlasWoodlandWinter = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(303).albedoMap; + textureAtlasWoodlandWinter.filterMode = FilterMode.Point; + + textureAtlasMountainWinter = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(103).albedoMap; + textureAtlasMountainWinter.filterMode = FilterMode.Point; + + textureAtlasSwampWinter = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(403).albedoMap; + textureAtlasSwampWinter.filterMode = FilterMode.Point; + + textureAtlasDesertRain = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(4).albedoMap; + textureAtlasDesertRain.filterMode = FilterMode.Point; + + textureAtlasWoodlandRain = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(304).albedoMap; + textureAtlasWoodlandRain.filterMode = FilterMode.Point; + + textureAtlasMountainRain = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(104).albedoMap; + textureAtlasMountainRain.filterMode = FilterMode.Point; + + textureAtlasSwampRain = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(404).albedoMap; + textureAtlasSwampRain.filterMode = FilterMode.Point; + + terrainMaterial = new Material(Shader.Find("Daggerfall/IncreasedTerrainTilemap")); + terrainMaterial.name = string.Format("world terrain material"); + + // Assign textures and parameters + terrainMaterial.SetTexture("_TileAtlasTexDesert", textureAtlasDesertSummer); + terrainMaterial.SetTexture("_TileAtlasTexWoodland", textureAtlasWoodlandSummer); + terrainMaterial.SetTexture("_TileAtlasTexMountain", textureAtlasMountainSummer); + terrainMaterial.SetTexture("_TileAtlasTexSwamp", textureAtlasSwampSummer); + //terrainMaterial.SetTexture("_TilemapTex", textureTerrainInfoTileMap); + + terrainMaterial.SetInt("_TextureSetSeasonCode", 0); + + updateSeasonalTextures(); // change seasonal textures if necessary + + terrainMaterial.SetInt("_PlayerPosX", this.playerGPS.CurrentMapPixel.X); + terrainMaterial.SetInt("_PlayerPosY", this.playerGPS.CurrentMapPixel.Y); + + terrainMaterial.SetInt("_TerrainDistance", streamingWorld.TerrainDistance - 1); // -1... allow the outer ring of of detailed terrain to intersect with far terrain (to prevent some holes) + + Vector3 vecWaterHeight = new Vector3(0.0f, (ImprovedTerrainSampler.scaledOceanElevation + 1.0f) * streamingWorld.TerrainScale, 0.0f); // water height level on y-axis (+1.0f some coastlines are incorrect otherwise) + Vector3 vecWaterHeightTransformed = terrainGameObject.transform.TransformPoint(vecWaterHeight); // transform to world coordinates + terrainMaterial.SetFloat("_WaterHeightTransformed", vecWaterHeightTransformed.y); + + terrainMaterial.SetTexture("_SkyTex", renderTextureSky); + + //Texture2D myTex = Resources.Load("test_pattern_texture") as Texture2D; + //terrainMaterial.SetTexture("_SkyTex", myTex); + + if (RenderSettings.fog == true) + { + if (RenderSettings.fogMode == FogMode.Linear) + terrainMaterial.SetInt("_FogMode", 1); + else if (RenderSettings.fogMode == FogMode.Exponential) + terrainMaterial.SetInt("_FogMode", 2); + else if (RenderSettings.fogMode == FogMode.ExponentialSquared) + terrainMaterial.SetInt("_FogMode", 3); + } + else + { + terrainMaterial.SetInt("_FogMode", 0); + } + + terrainMaterial.SetFloat("_FogFromSkyTex", 0); + + //terrainMaterial.SetFloat("_BlendFactor", blendFactor); + terrainMaterial.SetFloat("_BlendStart", blendStart); + terrainMaterial.SetFloat("_BlendEnd", blendEnd); + + // Promote material + terrain.materialTemplate = terrainMaterial; + terrain.materialType = Terrain.MaterialType.Custom; + + terrainGameObject.SetActive(true); + + return (terrainGameObject); + } + + #endregion + + #region Startup/Shutdown Methods + + private bool ReadyCheck() + { + if (isReady) + return true; + + if (dfUnity == null) + { + dfUnity = DaggerfallUnity.Instance; + } + + // Do nothing if DaggerfallUnity not ready + if (!dfUnity.IsReady) + { + DaggerfallUnity.LogMessage("ExtendedTerrainDistance: DaggerfallUnity component is not ready. Have you set your Arena2 path?"); + return false; + } + + // Raise ready flag + isReady = true; + + return true; + } + + #endregion + } +} \ No newline at end of file diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts/IncreasedTerrainDistance.cs.meta b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/IncreasedTerrainDistance.cs.meta new file mode 100644 index 0000000000..a982387f50 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/IncreasedTerrainDistance.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c73e2303837c8cb4e9bd0ee1c2fc291a +timeCreated: 1445869506 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts/RenderSkyboxWithoutSun.cs b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/RenderSkyboxWithoutSun.cs new file mode 100644 index 0000000000..05257fc528 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/RenderSkyboxWithoutSun.cs @@ -0,0 +1,44 @@ +//Increased Terrain Distance Mod for Daggerfall Tools For Unity +//http://www.reddit.com/r/dftfu +//http://www.dfworkshop.net/ +//Author: Michael Rauter (a.k.a. Nystul) +//License: MIT License (http://www.opensource.org/licenses/mit-license.php) +//Version: 1.54 + +using UnityEngine; +//using System; +//using System.Collections; +//using System.Collections.Generic; +//using System.IO; +//using DaggerfallConnect; +//using DaggerfallConnect.Arena2; +//using DaggerfallConnect.Utility; +//using DaggerfallWorkshop; +using DaggerfallWorkshop.Game; +//using DaggerfallWorkshop.Utility; + +namespace ProjectIncreasedTerrainDistance +{ + public class RenderSkyboxWithoutSun : MonoBehaviour + { + float revertSunSize; + + void OnPreRender() + { + if (RenderSettings.skybox) + { + revertSunSize = RenderSettings.skybox.GetFloat("_SunSize"); + RenderSettings.skybox.SetFloat("_SunSize", 0.0f); + } + } + + void OnPostRender() + { + if (RenderSettings.skybox) + { + RenderSettings.skybox.SetFloat("_SunSize", revertSunSize); + } + } + + } +} \ No newline at end of file diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Scripts/RenderSkyboxWithoutSun.cs.meta b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/RenderSkyboxWithoutSun.cs.meta new file mode 100644 index 0000000000..610c052fe5 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Scripts/RenderSkyboxWithoutSun.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 19364de74fc6bc949a025b4a396e0f62 +timeCreated: 1445869506 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Shaders.meta b/Game/Addons/IncreasedTerrainDistanceMod/Shaders.meta new file mode 100644 index 0000000000..f42a4e92a6 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Shaders.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: e42a24cb28ee68d48a7c3dce5187f95a +folderAsset: yes +timeCreated: 1445869505 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Shaders/DaggerfallIncreasedTerrainTilemap.shader b/Game/Addons/IncreasedTerrainDistanceMod/Shaders/DaggerfallIncreasedTerrainTilemap.shader new file mode 100644 index 0000000000..ca3eb67ab9 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Shaders/DaggerfallIncreasedTerrainTilemap.shader @@ -0,0 +1,321 @@ +//Increased Terrain Distance Mod for Daggerfall Tools For Unity +//http://www.reddit.com/r/dftfu +//http://www.dfworkshop.net/ +//Author: Michael Rauter (a.k.a. Nystul) +//License: MIT License (http://www.opensource.org/licenses/mit-license.php) +//Version: 1.54 + +Shader "Daggerfall/IncreasedTerrainTilemap" { + 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 + _TileAtlasTexDesert ("Tileset Atlas (RGB)", 2D) = "white" {} + _TileAtlasTexWoodland ("Tileset Atlas (RGB)", 2D) = "white" {} + _TileAtlasTexMountain ("Tileset Atlas (RGB)", 2D) = "white" {} + _TileAtlasTexSwamp ("Tileset Atlas (RGB)", 2D) = "white" {} + _SkyTex("Sky Texture", 2D) = "white" {} + _TilemapTex("Tilemap (R)", 2D) = "red" {} + _TilesetDim("Tileset Dimension (in tiles)", Int) = 16 + _TilemapDim("Tilemap Dimension (in tiles)", Int) = 1000 + _MaxIndex("Max Tileset Index", Int) = 255 + _AtlasSize("Atlas Size (in pixels)", Float) = 2048.0 + _GutterSize("Gutter Size (in pixels)", Float) = 32.0 + _PlayerPosX("Player Position in X-direction on world map", Int) = 0 + _PlayerPosY("Player Position in X-direction on world map", Int) = 0 + _TerrainDistance("Terrain Distance", Int) = 3 + _WaterHeightTransformed("water level on y-axis in world coordinates", Float) = 58.9 + _TextureSetSeasonCode("specifies which seasonal/weather texture set is used (0...Summer, 1...Winter/Snow, 2...Rain)", Int) = 0 + _BlendStart("blend start distance for blending distant terrain into skybox", Float) = 15000.0 + _BlendEnd("blend end distance for blending distant terrain into skybox", Float) = 200000.0 + _FogMode("Fog Mode", Int) = 1 + _FogDensity("Fog Density", Float) = 0.01 + _FogFromSkyTex("specifies if fog color should be derived from sky texture or not", Int) = 0 + } + SubShader { + Tags { "RenderType"="Opaque" } + LOD 200 + + // extra pass that renders to depth buffer only (world terrain is semi-transparent) - important for correct blending + Pass { + ZWrite On + ColorMask 0 + } + + CGPROGRAM + #pragma target 3.0 + #pragma surface surf Lambert alpha:fade keepalpha finalcolor:fcolor noforwardadd + #pragma glsl + + #define PI 3.1416f + + sampler2D _TileAtlasTexDesert; + sampler2D _TileAtlasTexWoodland; + sampler2D _TileAtlasTexMountain; + sampler2D _TileAtlasTexSwamp; + sampler2D _SkyTex; + sampler2D _TilemapTex; + int _TilesetDim; + int _TilemapDim; + int _MaxIndex; + float _AtlasSize; + float _GutterSize; + float _WaterHeightTransformed; + int _TerrainDistance; + int _PlayerPosX; + int _PlayerPosY; + int _TextureSetSeasonCode; + float _BlendStart; + float _BlendEnd; + + int _FogMode; + float _FogDensity; + int _FogFromSkyTex; + + struct Input + { + float2 uv_MainTex; + float3 worldPos; // interpolated vertex positions used for correct coast line texturing + float3 worldNormal; // interpolated vertex normals used for texturing terrain based on terrain slope + float4 screenPos; + }; + + void fcolor (Input IN, SurfaceOutput o, inout fixed4 color) + { + float dist = distance(IN.worldPos.xz, _WorldSpaceCameraPos.xz); //max(abs(IN.worldPos.x - _WorldSpaceCameraPos.x), abs(IN.worldPos.z - _WorldSpaceCameraPos.z)); + + float blendFacTerrain = 1.0f; + + /* + if (_FogMode == 1) // linear + { + blendFacTerrain = max(0.0f, min(1.0f, dist * unity_FogParams.z + unity_FogParams.w)); + } + if (_FogMode == 2) // exp + { + // factor = exp(-density*z) + float fogFac = 0.0; + fogFac = unity_FogParams.y * dist; + blendFacTerrain = exp2(-fogFac); + + } + if (_FogMode == 3) // exp2 + { + // factor = exp(-(density*z)^2) + float fogFac = 0.0; + fogFac = unity_FogParams.x * dist; + blendFacTerrain = exp2(-fogFac*fogFac); + } + */ + + // factor = exp(-density*z) + float fogFac = _FogDensity * dist; + blendFacTerrain = exp2(-fogFac); + + const float fadeRange = _BlendEnd - _BlendStart + 1.0f; + float alphaFadeAmount = max(0.0f, min(1.0f, (_BlendEnd - dist) / fadeRange)); + + if (_FogFromSkyTex == 1) + { + float2 screenUV = IN.screenPos.xy / IN.screenPos.w; + color.rgb = blendFacTerrain * color.rgb + (1.0f - blendFacTerrain) * tex2D(_SkyTex, screenUV).rgb; + } + else + { + color.rgb = blendFacTerrain * color.rgb + (1.0f - blendFacTerrain) * unity_FogColor.rgb; + } + color.a = alphaFadeAmount; + } + + half4 getColorByTextureAtlasIndex(Input IN, uniform sampler2D textureAtlas, uint index) + { + const float textureCrispness = 3.5f; // defines how crisp textures of extended terrain are (higher values result in more crispness) + const float textureCrispnessDiminishingFactor = 0.075f; // defines how fast crispness of textures diminishes with more distance from the player (the camera) + const float distanceAttenuation = 0.001; // used to attenuated computed distance + + float dist = max(abs(IN.worldPos.x - _WorldSpaceCameraPos.x), abs(IN.worldPos.z - _WorldSpaceCameraPos.z)); + dist = floor(dist*distanceAttenuation); + + uint xpos = index % _TilesetDim; + uint ypos = index / _TilesetDim; + float2 uv = float2(xpos, ypos) / _TilesetDim; + + // Offset to fragment position inside tile + float xoffset; + float yoffset; + // changed offset computation so that tile texture repeats over tile + xoffset = frac(IN.uv_MainTex.x * _TilemapDim * 1/(max(1,dist * textureCrispnessDiminishingFactor)) * textureCrispness ) / _GutterSize; + yoffset = frac(IN.uv_MainTex.y * _TilemapDim * 1/(max(1,dist * textureCrispnessDiminishingFactor)) * textureCrispness ) / _GutterSize; + + uv += float2(xoffset, yoffset) + _GutterSize / _AtlasSize; + + // Sample based on gradient and set output + float2 uvr = IN.uv_MainTex * ((float)_TilemapDim / _GutterSize); + half4 c = tex2Dgrad(textureAtlas, uv, ddx(uvr), ddy(uvr)); + return(c); + } + + void surf (Input IN, inout SurfaceOutput o) + { + const float limitAngleDirtTexture = 12.5f * PI / 180.0f; // tile will get dirt texture assigned if angles definied by surface normal and up-vector is larger than this value (and not larger than limitAngleStoneTexture) + const float limitAngleStoneTexture = 20.5f * PI / 180.0f; // tile will get stone texture assigned if angles definied by surface normal and up-vector is larger than this value + + half4 c; // output color value + + half4 c_g; // color value from grass texture + half4 c_d; // color value from dirt texture + half4 c_s; // color value from stone texture + + float weightGrass = 1.0f; + float weightDirt = 0.0f; + float weightStone = 0.0f; + + float4 terrainTileInfo = tex2D(_TilemapTex, IN.uv_MainTex).rgba; + + int mapPixelX = IN.uv_MainTex.x*_TilemapDim; + int mapPixelY = 499 - IN.uv_MainTex.y*_TilemapDim; + + // fragment discarding inside area spanned by _TerrainDistance + if ((abs(mapPixelX+1-_PlayerPosX)<=_TerrainDistance)&&(abs(mapPixelY+1-_PlayerPosY)<=_TerrainDistance)) + { + //float4 ret = float4(1.0f,0.0f,0.0f,1.0f); // for debugging fragment discard area use red color (also used to debug world terrain positioning with floating origin script) + //o.Albedo = ret.rgb; + //o.Alpha = ret.a; + //return; + discard; + } + + // fragments more distant than _BlendEnd will be discarded as well + const float fadeRange = _BlendEnd - _BlendStart + 1.0f; + float dist = distance(IN.worldPos.xz, _WorldSpaceCameraPos.xz); //max(abs(IN.worldPos.x - _WorldSpaceCameraPos.x), abs(IN.worldPos.z - _WorldSpaceCameraPos.z)); + if (dist>_BlendEnd) + { + discard; + } + + int index = terrainTileInfo.r * _MaxIndex; + + // there are several possibilities to get the tile surface normal... + // float3 surfaceNormal = normalize(cross(ddx(IN.worldPos.xyz), ddy(IN.worldPos.xyz))); // approximate it from worldPosition with derivations + // float3 surfaceNormal = normalize(o.Normal); // interpolated vertex normal + // float3 surfaceNormal = IN.worldNormal; // interpolated vertex normal (by input parameter) + // float3 surfaceNormal = WorldNormalVector(IN, o.Normal); // don't know what the difference is (googled it - was mentioned that it does not get interpolated but i can't confirm this) + // float3 surfaceNormal = 0.95f*(IN.worldNormal)+0.05f*normalize(cross(ddx(IN.worldPos.xyz), ddy(IN.worldPos.xyz))); // linear interpolation of interpolated vertex normal and approximated normal + + + const float3 upVec = float3(0.0f, 1.0f, 0.0f); + float dotResult = dot(normalize(IN.worldNormal), upVec); + + + if (acos(dotResult) < limitAngleDirtTexture) // between angles 0 to limitAngleDirtTexture interpolate between grass and dirt texture + { + weightGrass = 1.0f - acos(dotResult) / limitAngleDirtTexture; + weightDirt = acos(dotResult) / limitAngleDirtTexture; + weightStone = 0.0f; + } + else // between angles limitAngleDirtTexture to limitAngleStoneTexture interpolate between dirt and stone texture (limitAngleStoneTexture to 90 degrees -> also stone texture) + { + weightGrass = 0.0f; + weightDirt = 1.0f - min(1.0f, (acos(dotResult) - limitAngleDirtTexture) / limitAngleStoneTexture); + weightStone = min(1.0f, (acos(dotResult) - limitAngleDirtTexture) / limitAngleStoneTexture); + } + + if ((index==223)||(IN.worldPos.y < _WaterHeightTransformed)) // water (either by tile index or by tile world position) + { + c = getColorByTextureAtlasIndex(IN, _TileAtlasTexWoodland, 0); + //discard; + } + else if ((index==224)||(index==225)||(index==229)) // desert + { + c_g = getColorByTextureAtlasIndex(IN, _TileAtlasTexDesert, 8); + c_d = getColorByTextureAtlasIndex(IN, _TileAtlasTexDesert, 4); + c_s = getColorByTextureAtlasIndex(IN, _TileAtlasTexDesert, 12); + c = c_g * weightGrass + c_d * weightDirt + c_s * weightStone; + } + else if ((index==227)||(index==228)) // swamp + { + c_g = getColorByTextureAtlasIndex(IN, _TileAtlasTexSwamp, 8); + c_d = getColorByTextureAtlasIndex(IN, _TileAtlasTexSwamp, 4); + c_s = getColorByTextureAtlasIndex(IN, _TileAtlasTexSwamp, 12); + c = c_g * weightGrass + c_d * weightDirt + c_s * weightStone; + } + else if ((index==226)||(index==230)) // mountain + { + c_g = getColorByTextureAtlasIndex(IN, _TileAtlasTexMountain, 8); + c_d = getColorByTextureAtlasIndex(IN, _TileAtlasTexMountain, 4); + c_s = getColorByTextureAtlasIndex(IN, _TileAtlasTexMountain, 12); + c = c_g * weightGrass + c_d * weightDirt + c_s * weightStone; + } + else if ((index==231)||(index==232)||(index==233)) // moderate + { + c_g = getColorByTextureAtlasIndex(IN, _TileAtlasTexWoodland, 8); + c_d = getColorByTextureAtlasIndex(IN, _TileAtlasTexWoodland, 4); + c_s = getColorByTextureAtlasIndex(IN, _TileAtlasTexWoodland, 12); + c = c_g * weightGrass + c_d * weightDirt + c_s * weightStone; + } + else + { + c=half4(0.0f, 0.0f, 0.0f, 1.0f); + } + + float treeCoverage = terrainTileInfo.g; + + uint locationRangeX = terrainTileInfo.b * _MaxIndex; + uint locationRangeY = terrainTileInfo.a * _MaxIndex; + + half3 treeColor; + + // next line is the location placement I started with and which I understand... + // if ((locationXfract < (float)locationRangeX/8.0f) && (locationYfract < (float)locationRangeY / 8.0f)) // 8.0f is maximum location range (8 rmb blocks) - blocks are placed in the corner, so I played around with placement and came up with the following lines (no idea why it works but it does) + + // do not ask me what I am doing here, I simple don't know - just played around till it kind of fitted (locations are placed in the middle of its corresponding tile now...) + float extraX = (2.0f - (float)locationRangeX) * (1.0f/64.0f); // 64? yeah don't ask + float xDividor = (16.0f - (float)locationRangeX); // don't ask about the 16 as well :D + + float extraY = (2.0f - (float)locationRangeY) * (1.0f/64.0f); + float yDividor = (16.0f - (float)locationRangeY); + + float locationXfract = (IN.uv_MainTex.x*(float)_TilemapDim)-0.5f - (float)mapPixelX; + float locationYfract = (499 - IN.uv_MainTex.y*(float)_TilemapDim)-0.5f - (float)mapPixelY; + + // for debugging - red location markers + //if ((abs(locationXfract - extraX) < (float)locationRangeX/xDividor) && (abs(locationYfract - extraY) < (float)locationRangeY/yDividor)) + //{ + // c.rgb = 0.5f * c.rgb + 0.5f * half3(1.0f, 0.0f, 0.0f); + //} + + if (_TextureSetSeasonCode == 0) + treeColor = half3(0.125f, 0.165f, 0.061f); + else if (_TextureSetSeasonCode == 1) + treeColor = half3(0.96f, 0.98f, 0.94f); + else if (_TextureSetSeasonCode == 2) + treeColor = half3(0.10f, 0.14f, 0.04f); + + if ((abs(locationXfract - extraX) < (float)locationRangeX/xDividor) && (abs(locationYfract - extraY) < (float)locationRangeY/yDividor)) + { + c.rgb = min(1.0f, 0.4f * c.rgb + 0.6f * ((1.0f - treeCoverage) * c.rgb + treeCoverage * treeColor)); + c.rgb = 1.0f * c.rgb; + } + else + { + c.rgb = min(1.0f, 0.3f * c.rgb + 0.7f * ((1.0f - treeCoverage) * c.rgb + treeCoverage * treeColor)); + c.rgb = 0.9f * c.rgb; + } + + o.Albedo = c.rgb; + } + ENDCG + } + + + FallBack "Diffuse" +} diff --git a/Game/Addons/IncreasedTerrainDistanceMod/Shaders/DaggerfallIncreasedTerrainTilemap.shader.meta b/Game/Addons/IncreasedTerrainDistanceMod/Shaders/DaggerfallIncreasedTerrainTilemap.shader.meta new file mode 100644 index 0000000000..99344c6ab5 --- /dev/null +++ b/Game/Addons/IncreasedTerrainDistanceMod/Shaders/DaggerfallIncreasedTerrainTilemap.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6fcdb68e5c0026a4687f88caa539e703 +timeCreated: 1445869511 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod.meta b/Game/Addons/ReflectionsMod.meta new file mode 100644 index 0000000000..364fbcec20 --- /dev/null +++ b/Game/Addons/ReflectionsMod.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: ffd17ace2e9bf5849ad73aa2eced00a2 +folderAsset: yes +timeCreated: 1445868463 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Prefabs.meta b/Game/Addons/ReflectionsMod/Prefabs.meta new file mode 100644 index 0000000000..d11e0336e1 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Prefabs.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6989a7b20a96b1947a9fef7fdc59d64d +folderAsset: yes +timeCreated: 1445868463 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Prefabs/ReflectionsMod.prefab b/Game/Addons/ReflectionsMod/Prefabs/ReflectionsMod.prefab new file mode 100644 index 0000000000..a84df9effc Binary files /dev/null and b/Game/Addons/ReflectionsMod/Prefabs/ReflectionsMod.prefab differ diff --git a/Game/Addons/ReflectionsMod/Prefabs/ReflectionsMod.prefab.meta b/Game/Addons/ReflectionsMod/Prefabs/ReflectionsMod.prefab.meta new file mode 100644 index 0000000000..90b716ea61 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Prefabs/ReflectionsMod.prefab.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 12e13161da1e1bf4db32ff2f21dc35dc +timeCreated: 1445869025 +licenseType: Free +NativeFormatImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Resources.meta b/Game/Addons/ReflectionsMod/Resources.meta new file mode 100644 index 0000000000..e02c74b2cd --- /dev/null +++ b/Game/Addons/ReflectionsMod/Resources.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: d650caed9a98ed84f9629bc4e74a630c +folderAsset: yes +timeCreated: 1445868463 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Resources/configInjectionTextures.ini b/Game/Addons/ReflectionsMod/Resources/configInjectionTextures.ini new file mode 100644 index 0000000000..614ed03e7a --- /dev/null +++ b/Game/Addons/ReflectionsMod/Resources/configInjectionTextures.ini @@ -0,0 +1,446 @@ +; ReflectionsMod for Daggerfall Tools For Unity by Nystul +; http://www.reddit.com/r/dftfu +; http://www.dfworkshop.net/ +; Author: Michael Rauter (a.k.a. Nystul) +; License: MIT License (http://www.opensource.org/licenses/mit-license.php) +; Version: 0.32 + + +; +; ----------------------- +; formatting description: +; ----------------------- +; +; example for texture-global reflectivity and smoothness: +; +; [337_3 {Mages Guild Ground Texture (Woodland)}] +; textureArchive = 337 +; textureRecord = 3 +; textureFrame = 0 +; useMetallicGlossMap = false +; reflectivity = 0.6 +; smoothness = 0.9 +; +; +; example for reflectivity and smoothness from metallic gloss map: +; (metallic (reflectivity) is sampled from r-channel of texture, gloss (smoothness) is sampled from alpha channel of texture) +; filename of metallic gloss map has to be without file extension (e.g. don't put .png in there) and the file needs to be in the Resources folder +; +; [444_3 {House Ground Texture (Swamp)}] +; textureArchive = 444 +; textureRecord = 3 +; textureFrame = 0 +; useMetallicGlossMap = true +; filenameMetallicGlossMap = metallicGlossMap_Texture_444_3 +; +; you can inject albedo and normal maps also - ONLY WORKING FOR FLOOR TEXTURES CORRECTLY!!! - (e.g. for texture files in Resources folder and filenames custom_texture.png and custom_texture_normals.png): +; filenameAlbedoMap = custom_texture +; filenameNormalMap = custom_texture_normals +; + + +[337_3 {Mages Guild Ground Texture (Woodland)}] +textureArchive = 337 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.6 +smoothness = 0.9 + +[037_3 {Mages Guild Ground Texture (Desert)}] +textureArchive = 037 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.4 +smoothness = 0.8 + +[137_3 {Mages Guild Ground Texture (Mountain)}] +textureArchive = 137 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.5 +smoothness = 0.9 + +[437_3 {Mages Guild Ground Texture (Swamp)}] +textureArchive = 437 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.6 +smoothness = 0.9 + +[363_3 {Temple Ground Texture (Woodland)}] +textureArchive = 363 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.4 +smoothness = 0.96 + +[063_3 {Temple Ground Texture (Desert)}] +textureArchive = 063 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.4 +smoothness = 0.96 + +[463_3 {Temple Ground Texture (Swamp)}] +textureArchive = 463 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.4 +smoothness = 0.96 + +[360_3 {Kights Order Ground Texture (Woodland)}] +textureArchive = 360 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.1 +smoothness = 0.75 + +[060_3 {Kights Order Ground Texture (Desert)}] +textureArchive = 060 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.1 +smoothness = 0.75 + +[460_3 {Kights Order Ground Texture (Swamp)}] +textureArchive = 460 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.1 +smoothness = 0.55 + +[350_1 {Castle Daggerfall Ground Texture}] +textureArchive = 350 +textureRecord = 1 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.6 +smoothness = 0.875 + +[352_1 {Castle Wayrest Ground Texture}] +textureArchive = 352 +textureRecord = 1 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.5 +smoothness = 0.0925 + +[354_1 {Castle Sentinel Ground Texture}] +textureArchive = 354 +textureRecord = 1 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.3 +smoothness = 0.875 + +[150_1 {Shedugant Ground Texture}] +textureArchive = 150 +textureRecord = 1 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.6 +smoothness = 0.875 + +[152_1 {Woodborne Ground Texture}] +textureArchive = 152 +textureRecord = 1 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.6 +smoothness = 0.875 + +[153_1 {Lysandus Tomb Ground Texture}] +textureArchive = 153 +textureRecord = 1 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.07 +smoothness = 0.875 + +[016_3 {House Ground Texture (Desert)}] +textureArchive = 016 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.2 +smoothness = 0.95 + +[040_3 {House Ground Texture (Desert)}] +textureArchive = 040 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.18 +smoothness = 0.85 + +[044_3 {House and Palace Ground Texture (Desert)}] +textureArchive = 044 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.3 +smoothness = 0.95 + +[116_3 {House Ground Texture (Mountain)}] +textureArchive = 116 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.04 +smoothness = 0.67 + +[140_3 {House Ground Texture (Mountain)}] +textureArchive = 140 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.075 +smoothness = 0.71 + +[328_3 {House Ground Texture (Mountain)}] +textureArchive = 328 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.1 +smoothness = 0.7 + +[366_3 {House Ground Texture (Mountain)}] +textureArchive = 366 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.1 +smoothness = 0.7 + +[019_2 {Crypt Ground Texture (Desert)}] +textureArchive = 019 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.14 +smoothness = 0.5 + +[020_2 {Crypt Ground Texture (Desert)}] +textureArchive = 020 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.16 +smoothness = 0.5625 + +[022_2 {Dungeon Ground Texture (Desert)}] +textureArchive = 022 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.09 +smoothness = 0.35 + +[023_2 {Dungeon Ground Texture (Desert)}] +textureArchive = 023 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.12 +smoothness = 0.75 + +[024_2 {Dungeon Ground Texture (Desert)}] +textureArchive = 024 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.14 +smoothness = 0.9725 + +[119_2 {Crypt Ground Texture (Mountain)}] +textureArchive = 119 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.18 +smoothness = 0.71875 + +[120_2 {Crypt Ground Texture (Mountain)}] +textureArchive = 120 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.08 +smoothness = 0.3 + +[122_2 {Dungeon Ground Texture (Mountain)}] +textureArchive = 122 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.17 +smoothness = 0.8 + +[123_2 {Dungeon Ground Texture (Mountain)}] +textureArchive = 123 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.16 +smoothness = 0.7 + +[124_2 {Dungeon Ground Texture (Mountain)}] +textureArchive = 124 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.2 +smoothness = 0.8 + +[168_2 {Sewer Ground Texture (Mountain)}] +textureArchive = 168 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.14 +smoothness = 0.69375 + +[319_2 {Crypt Ground Texture (Woodland)}] +textureArchive = 319 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.18 +smoothness = 0.72 + +[320_2 {Crypt Ground Texture (Woodland)}] +textureArchive = 320 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.3 +smoothness = 0.62 + +[322_2 {Dungeon Ground Texture (Woodland)}] +textureArchive = 322 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.14 +smoothness = 0.8 + +[323_2 {Dungeon Ground Texture (Woodland)}] +textureArchive = 323 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.12 +smoothness = 0.75 + +[324_2 {Dungeon Ground Texture (Woodland)}] +textureArchive = 324 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.18 +smoothness = 0.85 + +[419_2 {Crypt Ground Texture (Swamp)}] +textureArchive = 419 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.18 +smoothness = 0.72 + +[420_2 {Crypt Ground Texture (Swamp)}] +textureArchive = 420 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.15 +smoothness = 0.69 + +[422_2 {Dungeon Ground Texture (Swamp)}] +textureArchive = 422 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.16 +smoothness = 0.725 + +[423_2 {Dungeon Ground Texture (Swamp)}] +textureArchive = 423 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.1 +smoothness = 0.5 + +[424_2 {Dungeon Ground Texture (Swamp)}] +textureArchive = 424 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.15 +smoothness = 0.6 + +[468_2 {Sewer Ground Texture (Swamp)}] +textureArchive = 468 +textureRecord = 2 +textureFrame = 0 +useMetallicGlossMap = false +reflectivity = 0.14 +smoothness = 0.65 + + + +[311_3 {House Ground Texture (Moderate)}] +textureArchive = 311 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = true +; similar to texture 444_3 - so use its metallglossmap texture +filenameMetallicGlossMap = metallicGlossMap_Texture_444_3 + +[316_3 {House Ground Texture (Moderate)}] +textureArchive = 316 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = true +filenameMetallicGlossMap = metallicGlossMap_Texture_316_3 + +[344_3 {House Ground Texture (Moderate)}] +textureArchive = 344 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = true +filenameMetallicGlossMap = metallicGlossMap_Texture_344_3 + +[411_3 {House Ground Texture (Swamp)}] +textureArchive = 411 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = true +; similar to texture 444_3 - so use its metallglossmap texture +filenameMetallicGlossMap = metallicGlossMap_Texture_444_3 + +[440_3 {House Ground Texture (Swamp)}] +textureArchive = 440 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = true +filenameMetallicGlossMap = metallicGlossMap_Texture_440_3 + +[444_3 {House Ground Texture (Swamp)}] +textureArchive = 444 +textureRecord = 3 +textureFrame = 0 +useMetallicGlossMap = true +filenameMetallicGlossMap = metallicGlossMap_Texture_444_3 diff --git a/Game/Addons/ReflectionsMod/Resources/configInjectionTextures.ini.meta b/Game/Addons/ReflectionsMod/Resources/configInjectionTextures.ini.meta new file mode 100644 index 0000000000..b20d2a2572 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Resources/configInjectionTextures.ini.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 961a48610a2aab34792b21065f46f7ef +timeCreated: 1445868463 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_316_3.png b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_316_3.png new file mode 100644 index 0000000000..99572f02b5 Binary files /dev/null and b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_316_3.png differ diff --git a/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_316_3.png.meta b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_316_3.png.meta new file mode 100644 index 0000000000..e14144be6b --- /dev/null +++ b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_316_3.png.meta @@ -0,0 +1,56 @@ +fileFormatVersion: 2 +guid: f7c0735cec551f44eab6574768112d77 +timeCreated: 1445868466 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: -1 + nPOTScale: 1 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + allowsAlphaSplitting: 0 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 0 + textureType: -1 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_344_3.png b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_344_3.png new file mode 100644 index 0000000000..ffabb74300 Binary files /dev/null and b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_344_3.png differ diff --git a/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_344_3.png.meta b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_344_3.png.meta new file mode 100644 index 0000000000..364850a6e0 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_344_3.png.meta @@ -0,0 +1,56 @@ +fileFormatVersion: 2 +guid: 96502d5816119cf41bede1a57426aa26 +timeCreated: 1445868465 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: -1 + nPOTScale: 1 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + allowsAlphaSplitting: 0 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 0 + textureType: -1 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_440_3.png b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_440_3.png new file mode 100644 index 0000000000..944067e764 Binary files /dev/null and b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_440_3.png differ diff --git a/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_440_3.png.meta b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_440_3.png.meta new file mode 100644 index 0000000000..ce72747ed5 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_440_3.png.meta @@ -0,0 +1,56 @@ +fileFormatVersion: 2 +guid: ef4098849a13418489076189a8df6b6f +timeCreated: 1445868466 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: -1 + nPOTScale: 1 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + allowsAlphaSplitting: 0 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 0 + textureType: -1 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_444_3.png b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_444_3.png new file mode 100644 index 0000000000..b7e0256914 Binary files /dev/null and b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_444_3.png differ diff --git a/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_444_3.png.meta b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_444_3.png.meta new file mode 100644 index 0000000000..4c470f61f9 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Resources/metallicGlossMap_Texture_444_3.png.meta @@ -0,0 +1,56 @@ +fileFormatVersion: 2 +guid: b4078bcfe826cc143a6d0658ec6d16dd +timeCreated: 1445868465 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: -1 + nPOTScale: 1 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + allowsAlphaSplitting: 0 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 0 + textureType: -1 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Resources/tileatlas_reflective.png b/Game/Addons/ReflectionsMod/Resources/tileatlas_reflective.png new file mode 100644 index 0000000000..4abe46378f Binary files /dev/null and b/Game/Addons/ReflectionsMod/Resources/tileatlas_reflective.png differ diff --git a/Game/Addons/ReflectionsMod/Resources/tileatlas_reflective.png.meta b/Game/Addons/ReflectionsMod/Resources/tileatlas_reflective.png.meta new file mode 100644 index 0000000000..5bd854a5e1 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Resources/tileatlas_reflective.png.meta @@ -0,0 +1,56 @@ +fileFormatVersion: 2 +guid: b6d44da64cbabea4fb0396aacfb802cf +timeCreated: 1445868466 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: -1 + nPOTScale: 1 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + allowsAlphaSplitting: 0 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 0 + textureType: -1 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Resources/tileatlas_reflective_raining.png b/Game/Addons/ReflectionsMod/Resources/tileatlas_reflective_raining.png new file mode 100644 index 0000000000..e573eb6748 Binary files /dev/null and b/Game/Addons/ReflectionsMod/Resources/tileatlas_reflective_raining.png differ diff --git a/Game/Addons/ReflectionsMod/Resources/tileatlas_reflective_raining.png.meta b/Game/Addons/ReflectionsMod/Resources/tileatlas_reflective_raining.png.meta new file mode 100644 index 0000000000..eef411aa89 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Resources/tileatlas_reflective_raining.png.meta @@ -0,0 +1,56 @@ +fileFormatVersion: 2 +guid: 03495c4617576b64f90179ac8bf60e89 +timeCreated: 1445868465 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: -1 + nPOTScale: 1 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + allowsAlphaSplitting: 0 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 0 + textureType: -1 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Scripts.meta b/Game/Addons/ReflectionsMod/Scripts.meta new file mode 100644 index 0000000000..766c92e2f2 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Scripts.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 913919706e8fa1f48a5f0bfa28eb55ff +folderAsset: yes +timeCreated: 1445868463 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Scripts/InjectReflectiveMaterialProperty.cs b/Game/Addons/ReflectionsMod/Scripts/InjectReflectiveMaterialProperty.cs new file mode 100644 index 0000000000..0c9e56a245 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Scripts/InjectReflectiveMaterialProperty.cs @@ -0,0 +1,365 @@ +//ReflectionsMod for Daggerfall Tools For Unity +//http://www.reddit.com/r/dftfu +//http://www.dfworkshop.net/ +//Author: Michael Rauter (a.k.a. Nystul) +//License: MIT License (http://www.opensource.org/licenses/mit-license.php) +//Version: 0.32 + +using UnityEngine; +using UnityEngine.Rendering; +using System.Collections.Generic; +using DaggerfallConnect; +using DaggerfallConnect.Arena2; +using DaggerfallConnect.Utility; +using DaggerfallWorkshop; +using DaggerfallWorkshop.Game; +using DaggerfallWorkshop.Utility; +using IniParser; + +namespace ReflectionsMod +{ + public class InjectReflectiveMaterialProperty : MonoBehaviour + { + const string filepathConfigInjectionTextures = "Assets/daggerfall-unity/Game/Addons/ReflectionsMod/Resources/configInjectionTextures.ini"; + // Streaming World Component + public StreamingWorld streamingWorld; + + DaggerfallUnity dfUnity; + + UpdateReflectionTextures reflectionTexturesScript = null; + + IniParser.FileIniDataParser iniParser; + IniParser.Model.IniData parsedIniData; + + void Start() + { + dfUnity = DaggerfallUnity.Instance; + + reflectionTexturesScript = GameObject.Find("ReflectionsMod").GetComponent(); + + if (!streamingWorld) + streamingWorld = GameObject.Find("StreamingWorld").GetComponent(); + if (!streamingWorld) + { + DaggerfallUnity.LogMessage("InjectReflectiveMaterialProperty: Missing StreamingWorld reference.", true); + if (Application.isEditor) + Debug.Break(); + else + Application.Quit(); + } + + iniParser = new FileIniDataParser(); + parsedIniData = iniParser.ReadFile(filepathConfigInjectionTextures); + } + + void Awake() + { + StreamingWorld.OnInitWorld += InjectMaterialProperties; + + StreamingWorld.OnTeleportToCoordinates += InjectMaterialProperties; + + FloatingOrigin.OnPositionUpdate += InjectMaterialProperties; + + PlayerEnterExit.OnTransitionInterior += InjectMaterialProperties; + PlayerEnterExit.OnTransitionExterior += InjectMaterialProperties; + PlayerEnterExit.OnTransitionDungeonInterior += InjectMaterialProperties; + PlayerEnterExit.OnTransitionDungeonExterior += InjectMaterialProperties; + + DaggerfallTerrain.OnInstantiateTerrain += InjectMaterialProperties; + } + + void OnDestroy() + { + StreamingWorld.OnInitWorld -= InjectMaterialProperties; + + StreamingWorld.OnTeleportToCoordinates -= InjectMaterialProperties; + + FloatingOrigin.OnPositionUpdate -= InjectMaterialProperties; + + PlayerEnterExit.OnTransitionInterior -= InjectMaterialProperties; + PlayerEnterExit.OnTransitionExterior -= InjectMaterialProperties; + PlayerEnterExit.OnTransitionDungeonInterior -= InjectMaterialProperties; + PlayerEnterExit.OnTransitionDungeonExterior -= InjectMaterialProperties; + + DaggerfallTerrain.OnInstantiateTerrain -= InjectMaterialProperties; + } + + public void OnWillRenderObject() + { + if (reflectionTexturesScript.isOutdoorEnvironment()) + { + GameObject goReflectionPlaneGroundLevel = GameObject.Find("ReflectionPlaneBottom"); + GameObject goReflectionPlaneSeaLevel = GameObject.Find("ReflectionPlaneSeaLevel"); + + GameObject go = GameObject.Find("StreamingTarget"); + foreach (Transform child in go.transform) + { + DaggerfallTerrain dfTerrain = child.GetComponent(); + if (!dfTerrain) + continue; + + Terrain terrain = child.GetComponent(); + if (terrain) + { + if (terrain.materialTemplate) + { + if (terrain.materialTemplate.shader.name == "Daggerfall/TilemapWithReflections") + { + terrain.materialTemplate.SetFloat("_GroundLevelHeight", goReflectionPlaneGroundLevel.transform.position.y); + terrain.materialTemplate.SetFloat("_SeaLevelHeight", goReflectionPlaneSeaLevel.transform.position.y); + } + } + } + } + } + + if (reflectionTexturesScript.isIndoorEnvironment()) + { + GameObject goReflectionPlaneGroundLevel = GameObject.Find("ReflectionPlaneBottom"); + + GameObject goReflectionPlaneLowerLevel = GameObject.Find("ReflectionPlaneSeaLevel"); + + Renderer[] renderers = null; + if (GameObject.Find("Interior")) + { + renderers = GameObject.Find("Interior").GetComponentsInChildren(); + } + else if (GameObject.Find("Dungeon")) + { + renderers = GameObject.Find("Dungeon").GetComponentsInChildren(); + } + + if (renderers != null) + { + foreach (Renderer r in renderers) + { + foreach (Material m in r.sharedMaterials) + { + if (m.shader.name == "Daggerfall/FloorMaterialWithReflections") + { + m.SetFloat("_GroundLevelHeight", goReflectionPlaneGroundLevel.transform.position.y); + m.SetFloat("_LowerLevelHeight", goReflectionPlaneLowerLevel.transform.position.y); + } + } + } + } + } + } + + //overloaded variant + void InjectMaterialProperties(DaggerfallTerrain sender) + { + InjectMaterialProperties(-1, -1); + } + + //overloaded variant + void InjectMaterialProperties(DaggerfallWorkshop.Game.PlayerEnterExit.TransitionEventArgs args) + { + InjectMaterialProperties(-1, -1); + } + + //overloaded variant + void InjectMaterialProperties(DFPosition worldPos) + { + InjectMaterialProperties(worldPos.X, worldPos.Y); + } + + //overloaded variant + void InjectMaterialProperties(Vector3 offset) + { + InjectMaterialProperties(); + } + + //overloaded variant + void InjectMaterialProperties() + { + InjectMaterialProperties(-1, -1); + } + + void updateMaterial(int archive, int record, int frame, Texture2D albedoMap, Texture2D normalMap, float reflectivity, float smoothness) + { + CachedMaterial cmat; + if (dfUnity.MaterialReader.GetCachedMaterial(archive, record, frame, out cmat)) + { + Material newMat = new Material(Shader.Find("Daggerfall/FloorMaterialWithReflections")); + newMat.CopyPropertiesFromMaterial(cmat.material); + newMat.name = cmat.material.name; + Texture tex = GameObject.Find("ReflectionPlaneBottom").GetComponent().m_ReflectionTexture; + if (tex) + { + newMat.SetTexture("_ReflectionGroundTex", tex); + } + tex = GameObject.Find("ReflectionPlaneSeaLevel").GetComponent().m_ReflectionTexture; + if (tex) + { + newMat.SetTexture("_ReflectionLowerLevelTex", tex); + } + newMat.SetFloat("_Metallic", reflectivity); + newMat.SetFloat("_Smoothness", smoothness); + + if (albedoMap != null) + { + newMat.SetTexture("_MainTex", albedoMap); + } + + if (normalMap != null) + { + newMat.SetTexture("_BumpMap", normalMap); + } + + cmat.material = newMat; + dfUnity.MaterialReader.SetCachedMaterial(archive, record, frame, cmat); + } + } + void updateMaterial(int archive, int record, int frame, Texture2D albedoMap, Texture2D normalMap, Texture2D metallicGlossMap) + { + CachedMaterial cmat; + if (dfUnity.MaterialReader.GetCachedMaterial(archive, record, frame, out cmat)) + { + Material newMat = new Material(Shader.Find("Daggerfall/FloorMaterialWithReflections")); + newMat.CopyPropertiesFromMaterial(cmat.material); + newMat.name = cmat.material.name; + Texture tex = GameObject.Find("ReflectionPlaneBottom").GetComponent().m_ReflectionTexture; + if (tex) + { + newMat.SetTexture("_ReflectionGroundTex", tex); + } + tex = GameObject.Find("ReflectionPlaneSeaLevel").GetComponent().m_ReflectionTexture; + if (tex) + { + newMat.SetTexture("_ReflectionLowerLevelTex", tex); + } + newMat.EnableKeyword("USE_METALLICGLOSSMAP"); + newMat.SetTexture("_MetallicGlossMap", metallicGlossMap); + + if (albedoMap != null) + { + newMat.SetTexture("_MainTex", albedoMap); + } + + if (normalMap != null) + { + newMat.SetTexture("_BumpMap", normalMap); + } + + cmat.material = newMat; + dfUnity.MaterialReader.SetCachedMaterial(archive, record, frame, cmat); + } + } + + void InjectMaterialProperties(int worldPosX, int worldPosY) + { + // mages guild 4 floors debuging worldpos: 704,337 + //if (reflectionTexturesScript.isIndoorEnvironment()) // fails for some reason when 2indoor transition happens - TODO: investigate + //{ + + IniParser.Model.IniData textureInjectionData = parsedIniData; + if (parsedIniData != null) + { + foreach (IniParser.Model.SectionData section in parsedIniData.Sections) + { + int textureArchive = int.Parse(textureInjectionData[section.SectionName]["textureArchive"]); + int textureRecord = int.Parse(textureInjectionData[section.SectionName]["textureRecord"]); + int textureFrame = int.Parse(textureInjectionData[section.SectionName]["textureFrame"]); + + Texture2D albedoTexture = null; + if (textureInjectionData[section.SectionName].ContainsKey("filenameAlbedoMap")) + { + string fileAlbedoMap = textureInjectionData[section.SectionName]["filenameAlbedoMap"]; + albedoTexture = Resources.Load(fileAlbedoMap) as Texture2D; + } + + Texture2D normalTexture = null; + if (textureInjectionData[section.SectionName].ContainsKey("filenameNormalMap")) + { + string fileNormalMap = textureInjectionData[section.SectionName]["filenameNormalMap"]; + normalTexture = Resources.Load(fileNormalMap) as Texture2D; + } + + bool useMetallicGlossMap = bool.Parse(textureInjectionData[section.SectionName]["useMetallicGlossMap"]); + + if (useMetallicGlossMap) + { + string fileNameMetallicGlossMap = textureInjectionData[section.SectionName]["filenameMetallicGlossMap"]; + Texture2D metallicGlossMapTexture = Resources.Load(fileNameMetallicGlossMap) as Texture2D; + updateMaterial(textureArchive, textureRecord, textureFrame, albedoTexture, normalTexture, metallicGlossMapTexture); + } + else + { + float reflectivity = float.Parse(textureInjectionData[section.SectionName]["reflectivity"]); + float smoothness = float.Parse(textureInjectionData[section.SectionName]["smoothness"]); + updateMaterial(textureArchive, textureRecord, textureFrame, albedoTexture, normalTexture, reflectivity, smoothness); + } + } + } + + //} + + if (reflectionTexturesScript.isOutdoorEnvironment()) // check position 474, 163, + { + GameObject go = GameObject.Find("StreamingTarget"); + foreach (Transform child in go.transform) + { + DaggerfallTerrain dfTerrain = child.GetComponent(); + if (!dfTerrain) + continue; + + PlayerGPS playerGPS = GameObject.Find("PlayerAdvanced").GetComponent(); + if (!playerGPS) + continue; + + //if ((dfTerrain.MapPixelX != playerGPS.CurrentMapPixel.X) || (dfTerrain.MapPixelY != playerGPS.CurrentMapPixel.Y)) + // continue; + + + Terrain terrain = child.GetComponent(); + if (terrain) + { + if ((terrain.materialTemplate)) //&&(terrain.materialTemplate.shader.name != "Daggerfall/TilemapWithReflections")) // uncommenting this makes initial location (after startup, not fast travelling) not receive correct shader - don't know why - so workaround is to force injecting materialshader even for unset material (not sure why it works, but it does) + { + Texture tileSetTexture = terrain.materialTemplate.GetTexture("_TileAtlasTex"); + + //Texture2D tileAtlas = dfUnity.MaterialReader.TextureReader.GetTerrainTilesetTexture(402).albedoMap; + //System.IO.File.WriteAllBytes("./Assets/ReflectionsMod/Resources/tileatlas_402.png", tileAtlas.EncodeToPNG()); + + Texture tileMapTexture = terrain.materialTemplate.GetTexture("_TilemapTex"); + int tileMapDim = terrain.materialTemplate.GetInt("_TilemapDim"); + + Material newMat = new Material(Shader.Find("Daggerfall/TilemapWithReflections")); + + newMat.SetTexture("_TileAtlasTex", tileSetTexture); + newMat.SetTexture("_TilemapTex", tileMapTexture); + newMat.SetInt("_TilemapDim", tileMapDim); + + GameObject goReflectionPlaneBottom = GameObject.Find("ReflectionPlaneBottom"); + Texture tex = goReflectionPlaneBottom.GetComponent().m_ReflectionTexture; + newMat.SetTexture("_ReflectionGroundTex", tex); + + //newMat.SetFloat("_GroundLevelHeight", goReflectionPlaneBottom.transform.position.y); + + GameObject goReflectionPlaneSeaLevel = GameObject.Find("ReflectionPlaneSeaLevel"); + Texture texSea = goReflectionPlaneSeaLevel.GetComponent().m_ReflectionTexture; + newMat.SetTexture("_ReflectionSeaTex", texSea); + + //newMat.SetFloat("_SeaLevelHeight", goReflectionPlaneSeaLevel.transform.position.y); + + WeatherManager weatherManager = GameObject.Find("WeatherManager").GetComponent(); + if (!weatherManager.IsRaining) + { + Texture2D tileAtlasReflectiveTexture = Resources.Load("tileatlas_reflective") as Texture2D; + newMat.SetTexture("_TileAtlasReflectiveTex", tileAtlasReflectiveTexture); + } + else + { + Texture2D tileAtlasReflectiveTexture = Resources.Load("tileatlas_reflective_raining") as Texture2D; + newMat.SetTexture("_TileAtlasReflectiveTex", tileAtlasReflectiveTexture); + } + + terrain.materialTemplate = newMat; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Game/Addons/ReflectionsMod/Scripts/InjectReflectiveMaterialProperty.cs.meta b/Game/Addons/ReflectionsMod/Scripts/InjectReflectiveMaterialProperty.cs.meta new file mode 100644 index 0000000000..034e820de7 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Scripts/InjectReflectiveMaterialProperty.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e92a92d365318ee47914ce61a27fb2c7 +timeCreated: 1445868463 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Scripts/MirrorReflection.cs b/Game/Addons/ReflectionsMod/Scripts/MirrorReflection.cs new file mode 100644 index 0000000000..79202e6927 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Scripts/MirrorReflection.cs @@ -0,0 +1,239 @@ +//ReflectionsMod for Daggerfall Tools For Unity +//http://www.reddit.com/r/dftfu +//http://www.dfworkshop.net/ +//Author: Michael Rauter (a.k.a. Nystul) +//License: MIT License (http://www.opensource.org/licenses/mit-license.php) +//Version: 0.32 + +// This script is derived from MirrorReflection4 script + +using UnityEngine; +using System.Collections; +using ProjectIncreasedTerrainDistance; + +namespace ReflectionsMod +{ + [ExecuteInEditMode] // Make mirror live-update even when not in play mode + public class MirrorReflection : MonoBehaviour + { + public bool m_DisablePixelLights = false; + public int m_TextureSize = 256; + public float m_ClipPlaneOffset = 0.05f; //0.0f; //0.07f; + + public LayerMask m_ReflectLayers = -1; + + private Hashtable m_ReflectionCameras = new Hashtable(); // Camera -> Camera table + + public RenderTexture m_ReflectionTexture = null; + private int m_OldReflectionTextureSize = 0; + + private static bool s_InsideRendering = false; + + // This is called when it's known that the object will be rendered by some + // camera. We render reflections and do other updates here. + // Because the script executes in edit mode, reflections for the scene view + // camera will just work! + public void OnWillRenderObject() + //void Update() + { + var rend = GetComponent(); + if (!enabled || !rend || !rend.sharedMaterial) // || !rend.enabled) + return; + + Camera cam = Camera.current; + if( !cam ) + return; + + if (cam != Camera.main) // skip everything that is not the main camera + return; + + // Safeguard from recursive reflections. + if( s_InsideRendering ) + return; + s_InsideRendering = true; + + Camera reflectionCamera; + CreateMirrorObjects( cam, out reflectionCamera ); + + // find out the reflection plane: position and normal in world space + Vector3 pos = transform.position; + Vector3 normal = transform.up; + + // Optionally disable pixel lights for reflection + int oldPixelLightCount = QualitySettings.pixelLightCount; + if( m_DisablePixelLights ) + QualitySettings.pixelLightCount = 0; + + UpdateCameraModes( cam, reflectionCamera ); + + // Render reflection + // Reflect camera around reflection plane + float d = -Vector3.Dot (normal, pos) - m_ClipPlaneOffset; + Vector4 reflectionPlane = new Vector4 (normal.x, normal.y, normal.z, d); + + Matrix4x4 reflection = Matrix4x4.zero; + CalculateReflectionMatrix (ref reflection, reflectionPlane); + Vector3 oldpos = cam.transform.position; + Vector3 newpos = reflection.MultiplyPoint( oldpos ); + reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection; + + // Setup oblique projection matrix so that near plane is our reflection + // plane. This way we clip everything below/above it for free. + Vector4 clipPlane = CameraSpacePlane( reflectionCamera, pos, normal, 1.0f ); + //Matrix4x4 projection = cam.projectionMatrix; + Matrix4x4 projection = cam.CalculateObliqueMatrix(clipPlane); + reflectionCamera.projectionMatrix = projection; + + reflectionCamera.cullingMask = ~(1<<4) & m_ReflectLayers.value; // never render water layer + reflectionCamera.targetTexture = m_ReflectionTexture; + + // next 2 lines are important for making shadows work correctly - otherwise shadows will be broken + rend.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; + rend.receiveShadows = false; + + GL.invertCulling = true; + reflectionCamera.transform.position = newpos; + Vector3 euler = cam.transform.eulerAngles; + reflectionCamera.transform.eulerAngles = new Vector3(0, euler.y, euler.z); + reflectionCamera.Render(); + reflectionCamera.transform.position = oldpos; + GL.invertCulling = false; + + // Restore pixel light count + if( m_DisablePixelLights ) + QualitySettings.pixelLightCount = oldPixelLightCount; + + s_InsideRendering = false; + } + + + // Cleanup all the objects we possibly have created + void OnDisable() + { + if( m_ReflectionTexture ) { + DestroyImmediate( m_ReflectionTexture ); + m_ReflectionTexture = null; + } + //foreach( DictionaryEntry kvp in m_ReflectionCameras ) + // DestroyImmediate( ((Camera)kvp.Value).gameObject ); + m_ReflectionCameras.Clear(); + } + + + private void UpdateCameraModes( Camera src, Camera dest ) + { + if( dest == null ) + return; + // set camera to clear the same way as current camera + CameraClearFlags clearFlags = CameraClearFlags.Skybox; + dest.clearFlags = clearFlags; + dest.backgroundColor = src.backgroundColor; + if( clearFlags == CameraClearFlags.Skybox ) + { + Skybox sky = src.GetComponent(typeof(Skybox)) as Skybox; + Skybox mysky = dest.GetComponent(typeof(Skybox)) as Skybox; + if( !sky || !sky.material ) + { + mysky.enabled = false; + } + else + { + mysky.enabled = true; + mysky.material = sky.material; + } + } + // update other values to match current camera. + // even if we are supplying custom camera&projection matrices, + // some of values are used elsewhere (e.g. skybox uses far plane) + dest.renderingPath = src.renderingPath; + dest.farClipPlane = src.farClipPlane; + dest.nearClipPlane = 0.03f; //src.nearClipPlane; + dest.orthographic = src.orthographic; + dest.fieldOfView = src.fieldOfView; + dest.aspect = src.aspect; + dest.orthographicSize = src.orthographicSize; + } + + // On-demand create any objects we need + private void CreateMirrorObjects( Camera currentCamera, out Camera reflectionCamera ) + { + reflectionCamera = null; + + // Reflection render texture + if( !m_ReflectionTexture || m_OldReflectionTextureSize != m_TextureSize ) + { + if( m_ReflectionTexture ) + DestroyImmediate( m_ReflectionTexture ); + m_ReflectionTexture = new RenderTexture(m_TextureSize, m_TextureSize, 16); //, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); + + m_ReflectionTexture.name = "__MirrorReflection" + GetInstanceID(); + m_ReflectionTexture.isPowerOfTwo = true; + + //m_ReflectionTexture.generateMips = true; + m_ReflectionTexture.useMipMap = true; + m_ReflectionTexture.wrapMode = TextureWrapMode.Clamp; + + //m_ReflectionTexture.hideFlags = HideFlags.DontSave; + m_OldReflectionTextureSize = m_TextureSize; + } + + // Camera for reflection + reflectionCamera = m_ReflectionCameras[currentCamera] as Camera; + if( !reflectionCamera ) // catch both not-in-dictionary and in-dictionary-but-deleted-GO + { + GameObject go = new GameObject( "Mirror Refl Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox) ); + reflectionCamera = go.GetComponent(); + reflectionCamera.enabled = false; + reflectionCamera.transform.position = transform.position; + reflectionCamera.transform.rotation = transform.rotation; + reflectionCamera.gameObject.AddComponent(); + //go.hideFlags = HideFlags.HideAndDontSave; + m_ReflectionCameras[currentCamera] = reflectionCamera; + + go.transform.SetParent(GameObject.Find("ReflectionsMod").transform); + } + } + + // Extended sign: returns -1, 0 or 1 based on sign of a + private static float sgn(float a) + { + if (a > 0.0f) return 1.0f; + if (a < 0.0f) return -1.0f; + return 0.0f; + } + + // Given position/normal of the plane, calculates plane in camera space. + private Vector4 CameraSpacePlane (Camera cam, Vector3 pos, Vector3 normal, float sideSign) + { + Vector3 offsetPos = pos + normal * m_ClipPlaneOffset; + Matrix4x4 m = cam.worldToCameraMatrix; + Vector3 cpos = m.MultiplyPoint( offsetPos ); + Vector3 cnormal = m.MultiplyVector( normal ).normalized * sideSign; + return new Vector4( cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos,cnormal) ); + } + + // Calculates reflection matrix around the given plane + private static void CalculateReflectionMatrix (ref Matrix4x4 reflectionMat, Vector4 plane) + { + reflectionMat.m00 = (1F - 2F*plane[0]*plane[0]); + reflectionMat.m01 = ( - 2F*plane[0]*plane[1]); + reflectionMat.m02 = ( - 2F*plane[0]*plane[2]); + reflectionMat.m03 = ( - 2F*plane[3]*plane[0]); + + reflectionMat.m10 = ( - 2F*plane[1]*plane[0]); + reflectionMat.m11 = (1F - 2F*plane[1]*plane[1]); + reflectionMat.m12 = ( - 2F*plane[1]*plane[2]); + reflectionMat.m13 = ( - 2F*plane[3]*plane[1]); + + reflectionMat.m20 = ( - 2F*plane[2]*plane[0]); + reflectionMat.m21 = ( - 2F*plane[2]*plane[1]); + reflectionMat.m22 = (1F - 2F*plane[2]*plane[2]); + reflectionMat.m23 = ( - 2F*plane[3]*plane[2]); + + reflectionMat.m30 = 0F; + reflectionMat.m31 = 0F; + reflectionMat.m32 = 0F; + reflectionMat.m33 = 1F; + } + } +} \ No newline at end of file diff --git a/Game/Addons/ReflectionsMod/Scripts/MirrorReflection.cs.meta b/Game/Addons/ReflectionsMod/Scripts/MirrorReflection.cs.meta new file mode 100644 index 0000000000..e1414bfa32 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Scripts/MirrorReflection.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 937a21676da9fbb46ae17a9843f8418c +timeCreated: 1445868463 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Scripts/UpdateReflectionTextures.cs b/Game/Addons/ReflectionsMod/Scripts/UpdateReflectionTextures.cs new file mode 100644 index 0000000000..030ace613e --- /dev/null +++ b/Game/Addons/ReflectionsMod/Scripts/UpdateReflectionTextures.cs @@ -0,0 +1,421 @@ +//ReflectionsMod for Daggerfall Tools For Unity +//http://www.reddit.com/r/dftfu +//http://www.dfworkshop.net/ +//Author: Michael Rauter (a.k.a. Nystul) +//License: MIT License (http://www.opensource.org/licenses/mit-license.php) +//Version: 0.32 + +using UnityEngine; +using System.Collections; +using UnityEngine.Rendering; +using DaggerfallConnect; +using DaggerfallConnect.Arena2; +using DaggerfallConnect.Utility; +using DaggerfallWorkshop; +using DaggerfallWorkshop.Game; +using DaggerfallWorkshop.Utility; + +namespace ReflectionsMod +{ + public class UpdateReflectionTextures : MonoBehaviour + { + private GameObject reflectionPlaneBottom = null; + private GameObject reflectionPlaneSeaLevel = null; + + public bool isOutdoorEnvironment() + { + if (GameObject.Find("Exterior")) + return(true); + else + return(false); + } + + public bool isIndoorEnvironment() + { + if ((GameObject.Find("Interior")) || (GameObject.Find("Dungeon"))) + return(true); + else + return(false); + } + + bool computeStepDownRaycast(Vector3 raycastStartPoint, Vector3 directionVec, float maxDiffMagnitude, out RaycastHit hit) + { + if (Physics.Raycast(raycastStartPoint, directionVec, out hit, 1000.0F)) + { + Vector3 hitPoint = hit.point; + hitPoint -= directionVec * 0.1f; // move away a bit from hitpoint in opposite direction of raycast - this is required since daggerfall interiors often have small gaps/holes where a walls meet the floor - so upcoming down-raycast will not miss floor... + + // and now raycast down + if (Physics.Raycast(hitPoint, Vector3.down, out hit, 1000.0F)) + { + Vector3 diffVec = raycastStartPoint - hit.point; + if (diffVec.sqrMagnitude <= maxDiffMagnitude) + { + return (true); + } + } + else + { + return (false); + } + } + return (false); + } + + float distanceToLowerLevel(Vector3 startPoint, Vector3 directionVec, out RaycastHit hit) + { + const int maxIterations = 20; + const float offset = 0.01f; + float maxDiffMagnitude = 3 * offset * offset * 1.1f; // ... once for every axis, *1.1f ... make it a bit bigger than the offset "boundingbox" + + // iterative raycast in forward direction + Vector3 raycastStartPoint = startPoint; // +Camera.main.transform.position; // +new Vector3(0.0f, 0.1f, 0.0f); + for (int i = 0; i < maxIterations; i++) + { + if (computeStepDownRaycast(raycastStartPoint, directionVec, maxDiffMagnitude, out hit)) + { + return (startPoint.y - hit.point.y); + } + + Vector3 offsetVec = -directionVec * offset; // move away a bit from hitpoint in opposite direction of raycast - this is required since daggerfall interiors often have small gaps/holes where a walls meet the floor - so upcoming down-raycast will not miss floor... + offsetVec.y = offset; // move a bit up as well - so that next raycast starts above ground a bit + raycastStartPoint = hit.point + offsetVec; + } + hit = new RaycastHit(); + return(float.MinValue); + } + + float majorityOf3FloatValues(float value1, float value2, float value3, float allowedDistance, float valueWhenTied) + { + if ((Mathf.Abs(value1 - value2) < allowedDistance) || (Mathf.Abs(value1 - value3) < allowedDistance)) + return (value1); + else if (Mathf.Abs(value2 - value3) < allowedDistance) + { + return (value2); + } + else + return valueWhenTied; + } + + float distanceToLowerLevelStartingFromWall(Vector3 startPoint, Vector3 directionVectorToWall, Vector3 directionVectorRaycast1, Vector3 directionVectorRaycast2) + { + const float offset = 0.1f; + RaycastHit hit, hit1, hit2, hit3; + float biasAmount = 0.5f; // vector bias of the 2 additional parallel raycasts + Vector3 biasVec = biasAmount * Vector3.Normalize(Vector3.Cross(directionVectorToWall, Vector3.up)); // get normalized normal vector to directionVectorToWall and up vector + Physics.Raycast(startPoint, directionVectorToWall, out hit1, 1000.0F); + // do 2 additional parallel raycast with small bias and do majority vote - workaround to reduce problems with holes in geometry... + Physics.Raycast(startPoint + biasVec, directionVectorToWall, out hit2, 1000.0F); + Physics.Raycast(startPoint - biasVec, directionVectorToWall, out hit3, 1000.0F); + + Vector3 wallPoint; + float distance = majorityOf3FloatValues(hit1.distance, hit2.distance, hit3.distance, 0.5f, float.MinValue); // 0.5f must be near to each other but allows for slanted walls (e.g. mages guild's spiral stair) + if (Mathf.Abs(hit1.distance - distance) < 0.5f) + { + wallPoint = hit1.point; + } + else if (Mathf.Abs(hit2.distance - distance) < 0.5f) + { + wallPoint = hit2.point; + } + else if (Mathf.Abs(hit3.distance - distance) < 0.5f) + { + wallPoint = hit3.point; + } + else + { + return (float.MinValue); + } + + + //Vector3 wallPoint = hit.point; + //wallPoint.y += 0.05f; + float distance1 = distanceToLowerLevel(wallPoint - directionVectorToWall * offset, directionVectorRaycast1, out hit); + float distance2 = distanceToLowerLevel(wallPoint - directionVectorToWall * offset, directionVectorRaycast2, out hit); + return Mathf.Max(distance1, distance2); + } + + float getDistanceToLowerLevel(GameObject goPlayerAdvanced) + { + float distanceToLowerLevelWhenGoingForward = float.MinValue; + float distanceToLowerLevelWhenGoingBack = float.MinValue; + float distanceToLowerLevelWhenGoingLeft = float.MinValue; + float distanceToLowerLevelWhenGoingRight = float.MinValue; + float distanceToLowerLevelWhenGoingForwardLeft = float.MinValue; + float distanceToLowerLevelWhenGoingForwardRight = float.MinValue; + float distanceToLowerLevelWhenGoingBackLeft = float.MinValue; + float distanceToLowerLevelWhenGoingBackRight = float.MinValue; + + float distanceToLowerLevelStartingFromLeftWall = float.MinValue; + float distanceToLowerLevelStartingFromRightWall = float.MinValue; + float distanceToLowerLevelStartingFromForwardWall = float.MinValue; + float distanceToLowerLevelStartingFromBackWall = float.MinValue; + + RaycastHit hit; + + Vector3 startPoint = goPlayerAdvanced.transform.position; + + // 2 additional raycasts parallel to the main raycast just next to it are performed - majority vote of result of these 3 raycasts is then used as distance to lower level in the direction of interest + float biasAmount = 0.5f; // vector bias of the 2 additional parallel raycasts + + distanceToLowerLevelWhenGoingForward = majorityOf3FloatValues(distanceToLowerLevel(startPoint, Vector3.forward, out hit), + distanceToLowerLevel(startPoint + new Vector3(-biasAmount, 0.0f, 0.0f), Vector3.forward, out hit), + distanceToLowerLevel(startPoint + new Vector3(+biasAmount, 0.0f, 0.0f), Vector3.forward, out hit), + 0.001f, + float.MinValue); + distanceToLowerLevelWhenGoingBack = majorityOf3FloatValues(distanceToLowerLevel(startPoint, Vector3.back, out hit), + distanceToLowerLevel(startPoint + new Vector3(-biasAmount, 0.0f, 0.0f), Vector3.back, out hit), + distanceToLowerLevel(startPoint + new Vector3(+biasAmount, 0.0f, 0.0f), Vector3.back, out hit), + 0.001f, + float.MinValue); + + + distanceToLowerLevelWhenGoingLeft = majorityOf3FloatValues(distanceToLowerLevel(startPoint, Vector3.left, out hit), + distanceToLowerLevel(startPoint + new Vector3(0.0f, 0.0f, -biasAmount), Vector3.left, out hit), + distanceToLowerLevel(startPoint + new Vector3(0.0f, 0.0f, +biasAmount), Vector3.left, out hit), + 0.001f, + float.MinValue); + + distanceToLowerLevelWhenGoingRight = majorityOf3FloatValues(distanceToLowerLevel(startPoint, Vector3.right, out hit), + distanceToLowerLevel(startPoint + new Vector3(0.0f, 0.0f, -biasAmount), Vector3.right, out hit), + distanceToLowerLevel(startPoint + new Vector3(0.0f, 0.0f, +biasAmount), Vector3.right, out hit), + 0.001f, + float.MinValue); + + + distanceToLowerLevelWhenGoingForwardLeft = majorityOf3FloatValues(distanceToLowerLevel(startPoint, new Vector3(-1.0f, 0.0f, 1.0f), out hit), + distanceToLowerLevel(startPoint + new Vector3(-biasAmount, 0.0f, -biasAmount), new Vector3(-1.0f, 0.0f, 1.0f), out hit), + distanceToLowerLevel(startPoint + new Vector3(+biasAmount, 0.0f, +biasAmount), new Vector3(-1.0f, 0.0f, 1.0f), out hit), + 0.001f, + float.MinValue); + + distanceToLowerLevelWhenGoingForwardRight = majorityOf3FloatValues(distanceToLowerLevel(startPoint, new Vector3(1.0f, 0.0f, 1.0f), out hit), + distanceToLowerLevel(startPoint + new Vector3(-biasAmount, 0.0f, +biasAmount), new Vector3(1.0f, 0.0f, 1.0f), out hit), + distanceToLowerLevel(startPoint + new Vector3(+biasAmount, 0.0f, -biasAmount), new Vector3(1.0f, 0.0f, 1.0f), out hit), + 0.001f, + float.MinValue); + + distanceToLowerLevelWhenGoingBackLeft = majorityOf3FloatValues(distanceToLowerLevel(startPoint, new Vector3(-1.0f, 0.0f, -1.0f), out hit), + distanceToLowerLevel(startPoint + new Vector3(-biasAmount, 0.0f, +biasAmount), new Vector3(-1.0f, 0.0f, -1.0f), out hit), + distanceToLowerLevel(startPoint + new Vector3(+biasAmount, 0.0f, -biasAmount), new Vector3(-1.0f, 0.0f, -1.0f), out hit), + 0.001f, + float.MinValue); + + distanceToLowerLevelWhenGoingBackRight = majorityOf3FloatValues(distanceToLowerLevel(startPoint, new Vector3(1.0f, 0.0f, -1.0f), out hit), + distanceToLowerLevel(startPoint + new Vector3(-biasAmount, 0.0f, -biasAmount), new Vector3(1.0f, 0.0f, -1.0f), out hit), + distanceToLowerLevel(startPoint + new Vector3(+biasAmount, 0.0f, +biasAmount), new Vector3(1.0f, 0.0f, -1.0f), out hit), + 0.001f, + float.MinValue); + + // now go to wall and start 2 perpendicular raycast (perpendicular to the wall) to determine distance to lower level (this is important when facing the edge of a wall but you can see to lower level beside the edge, otherwise you miss the lower level plane with the above raycast steps) + float extraY = Camera.main.transform.localPosition.y; // start from eye position to reduce unintentional "around the corner sampling" - can still happen but less likely + startPoint.y += extraY; + distanceToLowerLevelStartingFromLeftWall = -extraY + distanceToLowerLevelStartingFromWall(startPoint, Vector3.left, Vector3.forward, Vector3.back); + distanceToLowerLevelStartingFromRightWall = -extraY + distanceToLowerLevelStartingFromWall(startPoint, Vector3.right, Vector3.forward, Vector3.back); + distanceToLowerLevelStartingFromForwardWall = -extraY + distanceToLowerLevelStartingFromWall(startPoint, Vector3.forward, Vector3.left, Vector3.right); + distanceToLowerLevelStartingFromBackWall = -extraY + distanceToLowerLevelStartingFromWall(startPoint, Vector3.back, Vector3.left, Vector3.right); + + + return (Mathf.Max(distanceToLowerLevelWhenGoingForward, distanceToLowerLevelWhenGoingBack, distanceToLowerLevelWhenGoingLeft, distanceToLowerLevelWhenGoingRight, + distanceToLowerLevelWhenGoingForwardLeft, distanceToLowerLevelWhenGoingForwardRight, distanceToLowerLevelWhenGoingBackLeft, distanceToLowerLevelWhenGoingBackRight, + distanceToLowerLevelStartingFromLeftWall, distanceToLowerLevelStartingFromRightWall, distanceToLowerLevelStartingFromForwardWall, distanceToLowerLevelStartingFromBackWall)); + + } + + Mesh CreateMesh(float width, float height) + { + Mesh m = new Mesh(); + m.name = "ScriptedMesh"; + m.vertices = new Vector3[] { + new Vector3(-width, 0.01f, -height), + new Vector3(width, 0.01f, -height), + new Vector3(width, 0.01f, height), + new Vector3(-width, 0.01f, height), + new Vector3(-width, -10000.0f, -height), // create 2nd plane at level -10000, so that the stacked near camera (with near clip plane of around 1000) will trigger OnWillRenderObject() callback + new Vector3(width, -10000.0f, -height), // create 2nd plane at level -10000, so that the stacked near camera (with near clip plane of around 1000) will trigger OnWillRenderObject() callback + new Vector3(width, -10000.0f, height), // create 2nd plane at level -10000, so that the stacked near camera (with near clip plane of around 1000) will trigger OnWillRenderObject() callback + new Vector3(-width, -10000.0f, height) // create 2nd plane at level -10000, so that the stacked near camera (with near clip plane of around 1000) will trigger OnWillRenderObject() callback + }; + m.uv = new Vector2[] { + new Vector2 (0, 0), + new Vector2 (0, 1), + new Vector2(1, 1), + new Vector2 (1, 0), + new Vector2 (0, 0), // from 2nd plane + new Vector2 (0, 1), // from 2nd plane + new Vector2(1, 1), // from 2nd plane + new Vector2 (1, 0) // from 2nd plane + }; + m.triangles = new int[] { 0, 1, 2, 0, 2, 3, /* here starts 2nd plane */ 4, 5, 6, 4, 6, 7 }; + m.RecalculateNormals(); + + return m; + } + + void Awake() + { + if (!DaggerfallUnity.Settings.Nystul_RealtimeReflections) + return; + + reflectionPlaneBottom = new GameObject("ReflectionPlaneBottom"); + reflectionPlaneBottom.layer = LayerMask.NameToLayer("Water"); + MeshFilter meshFilter = (MeshFilter)reflectionPlaneBottom.AddComponent(typeof(MeshFilter)); + meshFilter.mesh = CreateMesh(100000.0f, 100000.0f); // create quad with normal facing into negative y-direction (so it is not visible but it will trigger OnWillRenderObject() in MirrorReflection.cs) - should be big enough to be "visible" even when looking parallel to the x/z-plane + MeshRenderer renderer = reflectionPlaneBottom.AddComponent(typeof(MeshRenderer)) as MeshRenderer; + + renderer.material.shader = Shader.Find("Standard"); + Texture2D tex = new Texture2D(1, 1); + tex.SetPixel(0, 0, Color.green); + tex.Apply(); + renderer.material.mainTexture = tex; + renderer.material.color = Color.green; + renderer.enabled = true; // if this is set to false OnWillRenderObject() in MirrorReflection.cs will not work (workaround would be to change OnWillRenderObject() to Update() + + MirrorReflection mirrorRefl = reflectionPlaneBottom.AddComponent(); + mirrorRefl.m_TextureSize = 512; + mirrorRefl.m_ReflectLayers = 1 << LayerMask.NameToLayer("Default"); + + reflectionPlaneBottom.transform.SetParent(this.transform); + + reflectionPlaneBottom.AddComponent(typeof(InjectReflectiveMaterialProperty)); // the inject script is parented to this plane so that the OnWillRenderObject() method of the inject script will work - this is important since update() function resulted in slightly delayed update which could be seen when ground level height changed + + + reflectionPlaneSeaLevel = new GameObject("ReflectionPlaneSeaLevel"); + reflectionPlaneSeaLevel.layer = LayerMask.NameToLayer("Water"); + MeshFilter meshFilterSeaLevel = (MeshFilter)reflectionPlaneSeaLevel.AddComponent(typeof(MeshFilter)); + meshFilterSeaLevel.mesh = CreateMesh(1000000.0f, 1000000.0f); // create quad facing into negative y-direction (so it is not visible but it will trigger OnWillRenderObject() in MirrorReflection.cs) - should be big enough to be "visible" even when looking parallel to the x/z-plane + MeshRenderer rendererSeaLevel = reflectionPlaneSeaLevel.AddComponent(typeof(MeshRenderer)) as MeshRenderer; + + + rendererSeaLevel.material.shader = Shader.Find("Standard"); + Texture2D texSeaLevel = new Texture2D(1, 1); + texSeaLevel.SetPixel(0, 0, Color.green); + texSeaLevel.Apply(); + rendererSeaLevel.material.mainTexture = texSeaLevel; + rendererSeaLevel.material.color = Color.green; + rendererSeaLevel.enabled = true; // if this is set to false OnWillRenderObject() in MirrorReflection.cs will not work (workaround would be to change OnWillRenderObject() to Update() + + MirrorReflection mirrorReflSeaLevel = reflectionPlaneSeaLevel.AddComponent(); + mirrorReflSeaLevel.m_TextureSize = 512; + mirrorReflSeaLevel.m_ReflectLayers = 1 << LayerMask.NameToLayer("Default"); + + reflectionPlaneSeaLevel.transform.SetParent(this.transform); + } + + void Update() + { + if (!DaggerfallUnity.Settings.Nystul_RealtimeReflections) + return; + + GameObject goPlayerAdvanced = GameObject.Find("PlayerAdvanced"); + + PlayerGPS playerGPS = GameObject.Find("PlayerAdvanced").GetComponent(); + if (!playerGPS) + return; + + if (isIndoorEnvironment()) + { + RaycastHit hit; + float distanceToGround = 0; + + if (Physics.Raycast(goPlayerAdvanced.transform.position, -Vector3.up, out hit, 100.0F)) + { + distanceToGround = hit.distance; + } + reflectionPlaneBottom.transform.position = goPlayerAdvanced.transform.position - new Vector3(0.0f, distanceToGround, 0.0f); + + float distanceLevelBelow = getDistanceToLowerLevel(goPlayerAdvanced); + //Debug.Log(string.Format("distance to lower level: {0}", distanceLevelBelow)); + reflectionPlaneSeaLevel.transform.position = goPlayerAdvanced.transform.position - new Vector3(0.0f, distanceLevelBelow, 0.0f); + } + + if (isOutdoorEnvironment()) + { + Terrain terrainInstancePlayerTerrain = null; + + int referenceLocationX = playerGPS.CurrentMapPixel.X; + int referenceLocationY = playerGPS.CurrentMapPixel.Y; + + ContentReader.MapSummary mapSummary; + // if there is no location at current player position... + if (!DaggerfallUnity.Instance.ContentReader.HasLocation(referenceLocationX, referenceLocationY, out mapSummary)) + { + // search for largest location in local 8-neighborhood and take this as reference location for location reflection plane + int maxLocationArea = -1; + for (int y = -1; y <= +1; y++) + { + for (int x = -1; x <= +1; x++) + { + if (DaggerfallUnity.Instance.ContentReader.HasLocation(playerGPS.CurrentMapPixel.X + x, playerGPS.CurrentMapPixel.Y + y, out mapSummary)) + { + DFLocation location = DaggerfallUnity.Instance.ContentReader.MapFileReader.GetLocation(mapSummary.RegionIndex, mapSummary.MapIndex); + byte locationRangeX = location.Exterior.ExteriorData.Width; + byte locationRangeY = location.Exterior.ExteriorData.Height; + int locationArea = locationRangeX * locationRangeY; + + if (locationArea > maxLocationArea) + { + referenceLocationX = playerGPS.CurrentMapPixel.X + x; + referenceLocationY = playerGPS.CurrentMapPixel.Y + y; + maxLocationArea = locationArea; + } + } + } + } + } + + GameObject go = GameObject.Find("StreamingTarget"); + foreach (Transform child in go.transform) + { + DaggerfallTerrain dfTerrain = child.GetComponent(); + if (!dfTerrain) + continue; + + if ((dfTerrain.MapPixelX != referenceLocationX) || (dfTerrain.MapPixelY != referenceLocationY)) + continue; + + + Terrain terrainInstance = child.GetComponent(); + terrainInstancePlayerTerrain = terrainInstance; + + if ((terrainInstance) && (terrainInstance.terrainData)) + { + float scale = terrainInstance.terrainData.heightmapScale.x; + float xSamplePos = DaggerfallUnity.Instance.TerrainSampler.HeightmapDimension * 0.55f; + float ySamplePos = DaggerfallUnity.Instance.TerrainSampler.HeightmapDimension * 0.55f; + Vector3 pos = new Vector3(xSamplePos * scale, 0, ySamplePos * scale); + float height = terrainInstance.SampleHeight(pos + terrainInstance.transform.position); + + float positionY = height + terrainInstance.transform.position.y; + reflectionPlaneBottom.transform.position = new Vector3(goPlayerAdvanced.transform.position.x + terrainInstance.transform.position.x, positionY, goPlayerAdvanced.transform.position.z + terrainInstance.transform.position.z); + } + } + + if (!terrainInstancePlayerTerrain) + return; + + //Debug.Log(string.Format("playerGPS: {0}, plane: {1}", goPlayerAdvanced.transform.position.y, reflectionPlaneBottom.transform.position.y)); + if (playerGPS.transform.position.y < reflectionPlaneBottom.transform.position.y) + { + RaycastHit hit; + float distanceToGround = 0; + + if (Physics.Raycast(goPlayerAdvanced.transform.position, -Vector3.up, out hit, 100.0F)) + { + distanceToGround = hit.distance; + } + + //Debug.Log(string.Format("distance to ground: {0}", distanceToGround)); + reflectionPlaneBottom.transform.position = goPlayerAdvanced.transform.position - new Vector3(0.0f, distanceToGround, 0.0f); + } + + StreamingWorld streamingWorld = GameObject.Find("StreamingWorld").GetComponent(); + Vector3 vecWaterHeight = new Vector3(0.0f, (DaggerfallUnity.Instance.TerrainSampler.OceanElevation) * streamingWorld.TerrainScale, 0.0f); // water height level on y-axis (+1.0f some coastlines are incorrect otherwise) + Vector3 vecWaterHeightTransformed = terrainInstancePlayerTerrain.transform.TransformPoint(vecWaterHeight); // transform to world coordinates + //Debug.Log(string.Format("x,y,z: {0}, {1}, {2}", vecWaterHeight.x, vecWaterHeight.y, vecWaterHeight.z)); + //Debug.Log(string.Format("transformed x,y,z: {0}, {1}, {2}", vecWaterHeightTransformed.x, vecWaterHeightTransformed.y, vecWaterHeightTransformed.z)); + reflectionPlaneSeaLevel.transform.position = new Vector3(goPlayerAdvanced.transform.position.x, vecWaterHeightTransformed.y, goPlayerAdvanced.transform.position.z); + } + } + } +} \ No newline at end of file diff --git a/Game/Addons/ReflectionsMod/Scripts/UpdateReflectionTextures.cs.meta b/Game/Addons/ReflectionsMod/Scripts/UpdateReflectionTextures.cs.meta new file mode 100644 index 0000000000..ccb787f824 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Scripts/UpdateReflectionTextures.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fc15d6fdd6e20fa44b11f780c026c6ec +timeCreated: 1445868465 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Shaders.meta b/Game/Addons/ReflectionsMod/Shaders.meta new file mode 100644 index 0000000000..26be9b6b55 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Shaders.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: ba843aa5933b964429e6c9122bdbae24 +folderAsset: yes +timeCreated: 1445868463 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Shaders/DaggerfallTilemapWithReflections.shader b/Game/Addons/ReflectionsMod/Shaders/DaggerfallTilemapWithReflections.shader new file mode 100644 index 0000000000..efda255a3c --- /dev/null +++ b/Game/Addons/ReflectionsMod/Shaders/DaggerfallTilemapWithReflections.shader @@ -0,0 +1,120 @@ +//ReflectionsMod for Daggerfall Tools For Unity +//http://www.reddit.com/r/dftfu +//http://www.dfworkshop.net/ +//Author: Michael Rauter (a.k.a. Nystul) +//License: MIT License (http://www.opensource.org/licenses/mit-license.php) +//Version: 0.32 + +Shader "Daggerfall/TilemapWithReflections" { + 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 + _TileAtlasTex ("Tileset Atlas (RGB)", 2D) = "white" {} + _TilemapTex("Tilemap (R)", 2D) = "red" {} + _ReflectionGroundTex("Reflection Texture Ground Reflection", 2D) = "black" {} + _ReflectionSeaTex("Reflection Texture Sea Reflection", 2D) = "black" {} + _TileAtlasReflectiveTex ("Tileset Reflection Atlas (RGB)", 2D) = "black" {} + _BumpMap("Normal Map", 2D) = "bump" {} + _TilesetDim("Tileset Dimension (in tiles)", Int) = 16 + _TilemapDim("Tilemap Dimension (in tiles)", Int) = 128 + _MaxIndex("Max Tileset Index", Int) = 255 + _AtlasSize("Atlas Size (in pixels)", Float) = 2048.0 + _GutterSize("Gutter Size (in pixels)", Float) = 32.0 + _SeaLevelHeight("sea level height", Float) = 0.0 + } + SubShader { + Tags { "RenderType"="Opaque" } + LOD 200 + + CGPROGRAM + #pragma target 3.0 + #pragma surface surf Standard + #pragma glsl + + sampler2D _TileAtlasTex; + sampler2D _TilemapTex; + sampler2D _BumpMap; + sampler2D _ReflectionGroundTex; + sampler2D _ReflectionSeaTex; + sampler2D _TileAtlasReflectiveTex; + int _TilesetDim; + int _TilemapDim; + int _MaxIndex; + float _AtlasSize; + float _GutterSize; + float _SeaLevelHeight; + + struct Input + { + float2 uv_MainTex; + float2 uv_BumpMap; + float3 worldPos; + float3 worldNormal; + float4 screenPos; + INTERNAL_DATA + }; + + void surf (Input IN, inout SurfaceOutputStandard o) + { + // Get offset to tile in atlas + uint index = tex2D(_TilemapTex, IN.uv_MainTex).x * _MaxIndex; + uint xpos = index % _TilesetDim; + uint ypos = index / _TilesetDim; + float2 uv = float2(xpos, ypos) / _TilesetDim; + + // Offset to fragment position inside tile + float xoffset = frac(IN.uv_MainTex.x * _TilemapDim) / _GutterSize; + float yoffset = frac(IN.uv_MainTex.y * _TilemapDim) / _GutterSize; + uv += float2(xoffset, yoffset) + _GutterSize / _AtlasSize; + + // Sample based on gradient and set output + float2 uvr = IN.uv_MainTex * ((float)_TilemapDim / _GutterSize); + half4 c = tex2Dgrad(_TileAtlasTex, uv, ddx(uvr), ddy(uvr)); + + float2 screenUV = IN.screenPos.xy / IN.screenPos.w; + + half3 refl; + if (IN.worldPos.y > _SeaLevelHeight + 0.01f) + { + refl = tex2Dlod(_ReflectionGroundTex, float4(screenUV, 0.0f, 1.0f)).rgb; // 4th component is blurring of reflection + } + else + { + refl = tex2Dlod(_ReflectionSeaTex, float4(screenUV, 0.0f, 1.5f)).rgb; + } + + o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); + float3 worldNormal = normalize(WorldNormalVector (IN, o.Normal)); + + float reflAmount; + const float3 upVec = float3(0.0f, 1.0f, 0.0f); + if (dot(worldNormal, upVec) > 0.99f) + { + //reflAmount = tex2D(_TileAtlasReflectiveTex, uv).r; //0.5f; + reflAmount = tex2Dgrad(_TileAtlasReflectiveTex, uv, ddx(uvr), ddy(uvr)); + } + else + { + reflAmount = 0.0f; + } + + c.rgb = c.rgb * (1.0f - reflAmount) + reflAmount * refl.rgb; + o.Albedo = c.rgb; + o.Alpha = c.a; + o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap)); + o.Metallic = 0.0f; + } + ENDCG + } + FallBack "Standard" +} diff --git a/Game/Addons/ReflectionsMod/Shaders/DaggerfallTilemapWithReflections.shader.meta b/Game/Addons/ReflectionsMod/Shaders/DaggerfallTilemapWithReflections.shader.meta new file mode 100644 index 0000000000..d43e3e62e6 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Shaders/DaggerfallTilemapWithReflections.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 8d06399a000651e47ad80dcd43cfcf53 +timeCreated: 1445868467 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Addons/ReflectionsMod/Shaders/FloorMaterialWithReflections.shader b/Game/Addons/ReflectionsMod/Shaders/FloorMaterialWithReflections.shader new file mode 100644 index 0000000000..14e8bc3e07 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Shaders/FloorMaterialWithReflections.shader @@ -0,0 +1,151 @@ +//ReflectionsMod for Daggerfall Tools For Unity +//http://www.reddit.com/r/dftfu +//http://www.dfworkshop.net/ +//Author: Michael Rauter (a.k.a. Nystul) +//License: MIT License (http://www.opensource.org/licenses/mit-license.php) +//Version: 0.32 + +Shader "Daggerfall/FloorMaterialWithReflections" { + Properties { + _Color("Color", Color) = (1,1,1,1) + _MainTex("Albedo Map", 2D) = "white" {} + _BumpMap("Normal Map", 2D) = "bump" {} + _EmissionMap("Emission Map", 2D) = "white" {} + [HideInInspector] _ReflectionGroundTex("Reflection Texture Ground Reflection", 2D) = "black" {} + [HideInInspector] _ReflectionLowerLevelTex("Reflection Texture for lower level Ground Reflection", 2D) = "black" {} + _EmissionColor("Emission Color", Color) = (0,0,0) + _MetallicGlossMap("Metallic Gloss Map", 2D) = "black" {} + _Metallic("metallic amount", Range(0.0, 1.0)) = 0 + _Smoothness("smoothness amount", Range(0.0, 1.0)) = 0 + [HideInInspector] _GroundLevelHeight("ground level height", Float) = 0.0 + [HideInInspector] _LowerLevelHeight("lower level height", Float) = 0.0 + } + SubShader { + Tags { "RenderType"="Opaque" } + LOD 200 + + CGPROGRAM + #pragma target 3.0 + #pragma surface surf Standard vertex:customvert + #pragma glsl + + #pragma multi_compile __ USE_METALLICGLOSSMAP + + half4 _Color; + sampler2D _MainTex; + sampler2D _BumpMap; + sampler2D _EmissionMap; + sampler2D _ReflectionGroundTex; + sampler2D _ReflectionLowerLevelTex; + half4 _EmissionColor; + + float _GroundLevelHeight; + float _LowerLevelHeight; + + #if defined (USE_METALLICGLOSSMAP) + sampler2D _MetallicGlossMap; + #else + half _Metallic; + half _Smoothness; + #endif + + struct Input + { + float2 uv_MainTex; + float2 uv_BumpMap; + float2 uv_EmissionMap; + float3 worldPos; + float4 screenPos; + float4 parallaxCorrectedScreenPos : TEXCOORD1; + }; + + void customvert(inout appdata_full v, out Input o) + { + UNITY_INITIALIZE_OUTPUT(Input, o); + + float4 posWorldSpace = mul(_Object2World, v.vertex); + if ((abs(posWorldSpace.y - _GroundLevelHeight) < 0.01f) || (abs(posWorldSpace.y - _LowerLevelHeight) < 0.01f)) + { + o.parallaxCorrectedScreenPos = ComputeScreenPos(mul(UNITY_MATRIX_VP, posWorldSpace)); + } + else + { + // parallax-correct reflection position + if (posWorldSpace.y > _GroundLevelHeight+0.01f) + o.parallaxCorrectedScreenPos = ComputeScreenPos(mul(UNITY_MATRIX_VP, posWorldSpace-float4(0.0f, posWorldSpace.y - _GroundLevelHeight + 0.25f, 0.0f, 0.0f))); + else if (posWorldSpace.y < _GroundLevelHeight-0.01f) + o.parallaxCorrectedScreenPos = ComputeScreenPos(mul(UNITY_MATRIX_VP, posWorldSpace-float4(0.0f, posWorldSpace.y - _GroundLevelHeight - 0.14f, 0.0f, 0.0f))); + else + o.parallaxCorrectedScreenPos = ComputeScreenPos(mul(UNITY_MATRIX_VP, posWorldSpace-float4(0.0f, posWorldSpace.y - _GroundLevelHeight, 0.0f, 0.0f))); + } + } + + float3 getReflectionColor(sampler2D tex, float2 screenUV, float smoothness) + { + half mipmapLevel1 = floor(smoothness); + half mipmapLevel2 = ceil(smoothness); + float w = smoothness - mipmapLevel1; + return((1.0f-w) * tex2Dlod(tex, float4(screenUV, 0.0f, mipmapLevel1)).rgb + w * tex2Dlod(tex, float4(screenUV, 0.0f, mipmapLevel2)).rgb); + } + + void surf (Input IN, inout SurfaceOutputStandard o) + { + half4 albedo = tex2D(_MainTex, IN.uv_MainTex) * _Color; + half3 emission = tex2D(_EmissionMap, IN.uv_EmissionMap).rgb * _EmissionColor.rgb; + + //float2 screenUV = IN.screenPos.xy / IN.screenPos.w; + float2 screenUV = IN.parallaxCorrectedScreenPos.xy / IN.parallaxCorrectedScreenPos.w; + + half3 refl; + + const half fadeWidth = 0.3f; + half fadeOutFactX = min(1.0f, max(0.0f, abs(0.5f - screenUV.x) - (0.5f-fadeWidth)) / fadeWidth); + half fadeOutFactY = min(1.0f, max(0.0f, abs(0.5f - screenUV.y) - (0.5f-fadeWidth)) / fadeWidth); + + half fadeOutFact = 0.0f; + if ((abs(IN.worldPos.y - _GroundLevelHeight) > 0.01f) && (abs(IN.worldPos.y - _LowerLevelHeight) > 0.01f)) + fadeOutFact = max(fadeOutFactX, fadeOutFactY); + + #if defined (USE_METALLICGLOSSMAP) + half4 metallicGloss = tex2D(_MetallicGlossMap, IN.uv_MainTex); + half metallic = metallicGloss.r * (1.0f-fadeOutFact); + half smoothness = (1.0f - metallicGloss.a) * 8.0f; + #else + half metallic = _Metallic * (1.0f-fadeOutFact); + half smoothness = (1.0f-_Smoothness) * 8.0f; + #endif + + //if ((screenUV.x>0.0f)&&(screenUV.x<1.0f)&&(screenUV.y>0.0f)&&(screenUV.y<1.0f)) + { + if (abs(IN.worldPos.y - _LowerLevelHeight) < 0.01f) + { + refl = getReflectionColor(_ReflectionLowerLevelTex, screenUV, smoothness); //refl = tex2Dlod(_ReflectionLowerLevelTex, float4(screenUV, 0.0f, _Smoothness)).rgb; + albedo.rgb += metallic * refl; // + (1.0f - metallic) * albedo.rgb; + albedo.rgb *= (1.0f/(1.0f + metallic)); + } + else if ( //(abs(IN.worldPos.y - _GroundLevelHeight) < 0.01f)|| // fragment belong to object on current ground level plane + (IN.worldPos.y < _GroundLevelHeight)|| // fragment is below (use parallax-corrected reflection) + (IN.worldPos.y - _GroundLevelHeight < 0.32f) // fragment is slightly above (use parallax-corrected reflection) - also valid for current ground level plane + ) + { + refl = getReflectionColor(_ReflectionGroundTex, screenUV, smoothness); //refl = tex2Dlod(_ReflectionGroundTex, float4(screenUV, 0.0f, _Smoothness)).rgb; + albedo.rgb += metallic * refl; // + (1.0f - metallic) * albedo.rgb; + albedo.rgb *= (1.0f/(1.0f + metallic)); + } + else + { + albedo.rgb += metallic * 0.2f; // correction of x/z planes without reflection texture applied - 0.2 seems to work ok for most situations + albedo.rgb *= (1.0f/(1.0f + metallic)); + } + } + + o.Albedo = albedo.rgb; // - emission; // Emission cancels out other lights + o.Alpha = albedo.a; + o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap)); + o.Emission = emission; + o.Metallic = 0.0f; //_Metallic; + } + ENDCG + } + FallBack "Standard" +} diff --git a/Game/Addons/ReflectionsMod/Shaders/FloorMaterialWithReflections.shader.meta b/Game/Addons/ReflectionsMod/Shaders/FloorMaterialWithReflections.shader.meta new file mode 100644 index 0000000000..9825c13a96 --- /dev/null +++ b/Game/Addons/ReflectionsMod/Shaders/FloorMaterialWithReflections.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 8cc87b30bef729249b31f1844ee57f6d +timeCreated: 1445868467 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Scenes/DaggerfallUnityGame_withMods.unity b/Game/Scenes/DaggerfallUnityGame_withMods.unity new file mode 100644 index 0000000000..6c66ca562d Binary files /dev/null and b/Game/Scenes/DaggerfallUnityGame_withMods.unity differ diff --git a/Game/Scenes/DaggerfallUnityGame_withMods.unity.meta b/Game/Scenes/DaggerfallUnityGame_withMods.unity.meta new file mode 100644 index 0000000000..4b4fc8a522 --- /dev/null +++ b/Game/Scenes/DaggerfallUnityGame_withMods.unity.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d8abae5a79531aa46a1e2a6f0f140603 +timeCreated: 1445867633 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/MaterialReader.cs b/Scripts/MaterialReader.cs index efdbcfe097..ff77680fd6 100644 --- a/Scripts/MaterialReader.cs +++ b/Scripts/MaterialReader.cs @@ -551,6 +551,29 @@ public bool GetCachedMaterial(int archive, int record, int frame, out CachedMate return true; } + /// + /// Sets CachedMaterial properties. + /// existing Material will be updated in cache with cachedMaterialIn. + /// + /// Archive index. + /// Record index. + /// Frame index. + /// the CachedMaterial used to update the cache. + /// True if CachedMaterial was found and updated successfully. + public bool SetCachedMaterial(int archive, int record, int frame, CachedMaterial cachedMaterialIn) + { + int key = MakeTextureKey((short)archive, (byte)record, (byte)frame); + if (materialDict.ContainsKey(key)) + { + materialDict[key] = cachedMaterialIn; + return true; + } + else + { + return false; + } + } + /// /// Gets CachedMaterial properties for an atlased material. /// Atlas material will not be loaded automatically if not found in cache.