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; }
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 <Terrain>(); 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); setMaterialFogParameters(); terrainMaterial.SetInt("_FogFromSkyTex", 0); //terrainMaterial.SetFloat("_BlendFactor", blendFactor); terrainMaterial.SetFloat("_BlendStart", blendStart); terrainMaterial.SetFloat("_BlendEnd", blendEnd); #if REFLECTIONSMOD_CODE_AVAILABLE if (isActiveReflectionsMod) { RenderTexture reflectionSeaTexture = GameObject.Find("ReflectionsMod").GetComponent <ReflectionsMod.UpdateReflectionTextures>().getSeaReflectionRenderTexture(); if (reflectionSeaTexture != null) { terrainMaterial.EnableKeyword("ENABLE_WATER_REFLECTIONS"); terrainMaterial.SetTexture("_SeaReflectionTex", reflectionSeaTexture); terrainMaterial.SetInt("_UseSeaReflectionTex", 1); } else { terrainMaterial.SetInt("_UseSeaReflectionTex", 0); } } #else terrainMaterial.SetInt("_UseSeaReflectionTex", 0); #endif // Promote material terrain.materialTemplate = terrainMaterial; terrain.materialType = Terrain.MaterialType.Custom; terrainGameObject.SetActive(true); return(terrainGameObject); }