public void Execute(int index) { int x = JobA.Row(index, tDim); int y = JobA.Col(index, tDim); // Assign tile data to tilemap Color32 tileColor = new Color32(0, 0, 0, 0); // Get sample tile data byte tile = tilemapData[JobA.Idx(x, y, tDim)]; // Convert from [flip,rotate,6bit-record] => [6bit-record,flip,rotate] int record; if (tile == byte.MaxValue) { // Zeros are converted to FF so assign tiles doesn't overwrite location tiles, convert back. record = 0; } else { record = tile * 4; if ((tile & rotBit) != 0) { record += 1; } if ((tile & flipBit) != 0) { record += 2; } } // Assign to tileMap tileColor.r = tileColor.a = (byte)record; tileMap[y * tDim + x] = tileColor; }
/// <summary> /// Complete terrain data update using jobs system. (second of a two stage process) /// </summary> /// <param name="terrainTexturing">Instance of ITerrainTexturing implementation class to use.</param> public void CompleteMapPixelDataUpdate(ITerrainTexturing terrainTexturing = null) { // Convert heightmap data back to standard managed 2d array. MapData.heightmapSamples = new float[heightmapDim, heightmapDim]; for (int i = 0; i < MapData.heightmapData.Length; i++) { MapData.heightmapSamples[JobA.Row(i, heightmapDim), JobA.Col(i, heightmapDim)] = MapData.heightmapData[i]; } // Convert tilemap data back to standard managed 2d array. // (Still needed for nature layout so it can be called again without requiring terrain data generation) MapData.tilemapSamples = new byte[tilemapDim, tilemapDim]; for (int i = 0; i < MapData.tilemapData.Length; i++) { byte tile = MapData.tilemapData[i]; if (tile == byte.MaxValue) { tile = 0; } MapData.tilemapSamples[JobA.Row(i, tilemapDim), JobA.Col(i, tilemapDim)] = tile; } // Create tileMap array or resize if needed and copy native array. if (TileMap == null || TileMap.Length != MapData.tileMap.Length) { TileMap = new Color32[MapData.tileMap.Length]; } MapData.tileMap.CopyTo(TileMap); // Copy max and avg heights. (TODO: Are these needed? Seem to not be used anywhere) MapData.averageHeight = MapData.avgMaxHeight[TerrainHelper.avgHeightIdx]; MapData.maxHeight = MapData.avgMaxHeight[TerrainHelper.maxHeightIdx]; DisposeNativeMemory(); }
public void Execute(int index) { int x = JobA.Row(index, tdDim); int y = JobA.Col(index, tdDim); // Height sample for ocean and beach tiles int hx = (int)Mathf.Clamp(hDim * ((float)x / (float)tdDim), 0, hDim - 1); int hy = (int)Mathf.Clamp(hDim * ((float)y / (float)tdDim), 0, hDim - 1); float height = heightmapData[JobA.Idx(hy, hx, hDim)] * maxTerrainHeight; // x & y swapped in heightmap for TerrainData.SetHeights() // Ocean texture if (height <= oceanElevation) { tileData[index] = water; return; } // Beach texture // Adds a little +/- randomness to threshold so beach line isn't too regular if (height <= beachElevation + (JobRand.Next(-15000000, 15000000) / 10000000f)) { tileData[index] = dirt; return; } // Get latitude and longitude of this tile int latitude = (int)(mapPixelX * MapsFile.WorldMapTileDim + x); int longitude = (int)(MapsFile.MaxWorldTileCoordZ - mapPixelY * MapsFile.WorldMapTileDim + y); // Set texture tile using weighted noise float weight = 0; weight += NoiseWeight(latitude, longitude); // TODO: Add other weights to influence texture tile generation tileData[index] = GetWeightedRecord(weight); }
public void Execute(int index) { int x = JobA.Row(index, tDim); int y = JobA.Col(index, tDim); // Do nothing if in location rect as texture already set, to 0xFF if zero if (tilemapData[index] != 0) { return; } // Assign tile texture if (march) { // Get sample points int tdIdx = JobA.Idx(x, y, tdDim); int b0 = tileData[tdIdx]; // tileData[x, y] int b1 = tileData[tdIdx + 1]; // tileData[x + 1, y] int b2 = tileData[tdIdx + tdDim]; // tileData[x, y + 1] int b3 = tileData[tdIdx + tdDim + 1]; // tileData[x + 1, y + 1] int shape = (b0 & 1) | (b1 & 1) << 1 | (b2 & 1) << 2 | (b3 & 1) << 3; int ring = (b0 + b1 + b2 + b3) >> 2; int tileID = shape | ring << 4; tilemapData[index] = lookupTable[tileID]; } else { tilemapData[index] = tileData[JobA.Idx(x, y, tdDim)]; } }
public void Execute(int index) { // Use cols=x and rows=y for height data int x = JobA.Col(index, hDim); int y = JobA.Row(index, hDim); 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)(hDim - 1); float sfracy = (float)y / (float)(hDim - 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[JobA.Idx(0, 3, sd)], shm[JobA.Idx(1, 3, sd)], shm[JobA.Idx(2, 3, sd)], shm[JobA.Idx(3, 3, sd)], sfracx); x2 = TerrainHelper.CubicInterpolator(shm[JobA.Idx(0, 2, sd)], shm[JobA.Idx(1, 2, sd)], shm[JobA.Idx(2, 2, sd)], shm[JobA.Idx(3, 2, sd)], sfracx); x3 = TerrainHelper.CubicInterpolator(shm[JobA.Idx(0, 1, sd)], shm[JobA.Idx(1, 1, sd)], shm[JobA.Idx(2, 1, sd)], shm[JobA.Idx(3, 1, sd)], sfracx); x4 = TerrainHelper.CubicInterpolator(shm[JobA.Idx(0, 0, sd)], shm[JobA.Idx(1, 0, sd)], shm[JobA.Idx(2, 0, sd)], shm[JobA.Idx(3, 0, sd)], 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[JobA.Idx(ix, iy + 0, ld)], lhm[JobA.Idx(ix + 1, iy + 0, ld)], lhm[JobA.Idx(ix + 2, iy + 0, ld)], lhm[JobA.Idx(ix + 3, iy + 0, ld)], fracx); x2 = TerrainHelper.CubicInterpolator(lhm[JobA.Idx(ix, iy + 1, ld)], lhm[JobA.Idx(ix + 1, iy + 1, ld)], lhm[JobA.Idx(ix + 2, iy + 1, ld)], lhm[JobA.Idx(ix + 3, iy + 1, ld)], fracx); x3 = TerrainHelper.CubicInterpolator(lhm[JobA.Idx(ix, iy + 2, ld)], lhm[JobA.Idx(ix + 1, iy + 2, ld)], lhm[JobA.Idx(ix + 2, iy + 2, ld)], lhm[JobA.Idx(ix + 3, iy + 2, ld)], fracx); x4 = TerrainHelper.CubicInterpolator(lhm[JobA.Idx(ix, iy + 3, ld)], lhm[JobA.Idx(ix + 1, iy + 3, ld)], lhm[JobA.Idx(ix + 2, iy + 3, ld)], lhm[JobA.Idx(ix + 3, iy + 3, ld)], fracx); noiseHeight = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, fracy); scaledHeight += noiseHeight * noiseMapScale; // Additional noise mask for small terrain features at ground level int noisex = mapPixelX * (hDim - 1) + x; int noisey = (MapsFile.MaxMapPixelY - mapPixelY) * (hDim - 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; } // Set sample float height = Mathf.Clamp01(scaledHeight / maxTerrainHeight); heightmapData[index] = height; }
// Makes terrain sampler implementations backwards compatible with jobs system terrain data generation. public virtual JobHandle ScheduleGenerateSamplesJob(ref MapPixelData mapPixel) { GenerateSamples(ref mapPixel); // Convert generated samples to the flattened native array used by jobs. int hDim = HeightmapDimension; for (int y = 0; y < hDim; y++) { for (int x = 0; x < hDim; x++) { mapPixel.heightmapData[JobA.Idx(y, x, hDim)] = mapPixel.heightmapSamples[y, x]; } } return(new JobHandle()); }
public void Execute() { // Convert from rect in tilemap space to interior corners in 0-1 range float xMin = locationRect.xMin / MapsFile.WorldMapTileDim; float xMax = locationRect.xMax / MapsFile.WorldMapTileDim; float yMin = locationRect.yMin / MapsFile.WorldMapTileDim; float yMax = locationRect.yMax / MapsFile.WorldMapTileDim; // Scale values for converting blend space into 0-1 range float leftScale = 1 / xMin; float rightScale = 1 / (1 - xMax); float topScale = 1 / yMin; float bottomScale = 1 / (1 - yMax); // Flatten location area and blend with surrounding heights float strength = 0; float targetHeight = avgMaxHeight[avgHeightIdx]; for (int y = 0; y < hDim; y++) { float v = (float)y / (float)(hDim - 1); bool insideY = (v >= yMin && v <= yMax); for (int x = 0; x < hDim; x++) { float u = (float)x / (float)(hDim - 1); bool insideX = (u >= xMin && u <= xMax); if (insideX || insideY) { if (insideY && u <= xMin) { strength = u * leftScale; } else if (insideY && u >= xMax) { strength = (1 - u) * rightScale; } else if (insideX && v <= yMin) { strength = v * topScale; } else if (insideX && v >= yMax) { strength = (1 - v) * bottomScale; } } else { float xs = 0, ys = 0; if (u <= xMin) { xs = u * leftScale; } else if (u >= xMax) { xs = (1 - u) * rightScale; } if (v <= yMin) { ys = v * topScale; } else if (v >= yMax) { ys = (1 - v) * bottomScale; } strength = TerrainHelper.BilinearInterpolator(0, 0, 0, 1, xs, ys); } int idx = JobA.Idx(y, x, hDim); float height = heightmapData[idx]; if (insideX && insideY) { height = targetHeight; } else { height = Mathf.Lerp(height, targetHeight, strength); } heightmapData[idx] = height; } } }
// Set location tilemap data public static void SetLocationTiles(ref MapPixelData mapPixel) { // Get location DaggerfallUnity dfUnity = DaggerfallUnity.Instance; DFLocation location = dfUnity.ContentReader.MapFileReader.GetLocation(mapPixel.mapRegionIndex, mapPixel.mapLocationIndex); // Position tiles inside terrain area DFPosition tilePos = TerrainHelper.GetLocationTerrainTileOrigin(location); // Full 8x8 locations have "terrain blend space" around walls to smooth down random terrain towards flat area. // This is indicated by texture index > 55 (ground texture range is 0-55), larger values indicate blend space. // We need to know rect of actual city area so we can use blend space outside walls. int xmin = int.MaxValue, ymin = int.MaxValue; int xmax = 0, ymax = 0; // Iterate blocks of this location for (int blockY = 0; blockY < location.Exterior.ExteriorData.Height; blockY++) { for (int blockX = 0; blockX < location.Exterior.ExteriorData.Width; blockX++) { // Get block data DFBlock block; string blockName = dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, blockX, blockY); if (!dfUnity.ContentReader.GetBlock(blockName, out block)) { continue; } // Copy ground tile info for (int tileY = 0; tileY < RMBLayout.RMBTilesPerBlock; tileY++) { for (int tileX = 0; tileX < RMBLayout.RMBTilesPerBlock; tileX++) { DFBlock.RmbGroundTiles tile = block.RmbBlock.FldHeader.GroundData.GroundTiles[tileX, (RMBLayout.RMBTilesPerBlock - 1) - tileY]; int xpos = tilePos.X + blockX * RMBLayout.RMBTilesPerBlock + tileX; int ypos = tilePos.Y + blockY * RMBLayout.RMBTilesPerBlock + tileY; if (tile.TextureRecord < 56) { // Track interior bounds of location tiled area if (xpos < xmin) { xmin = xpos; } if (xpos > xmax) { xmax = xpos; } if (ypos < ymin) { ymin = ypos; } if (ypos > ymax) { ymax = ypos; } // Store texture data from block mapPixel.tilemapData[JobA.Idx(xpos, ypos, MapsFile.WorldMapTileDim)] = tile.TileBitfield == 0 ? byte.MaxValue : tile.TileBitfield; } } } } } // Update location rect with extra clearance int extraClearance = location.MapTableData.LocationType == DFRegion.LocationTypes.TownCity ? 3 : 2; Rect locationRect = new Rect(); locationRect.xMin = xmin - extraClearance; locationRect.xMax = xmax + extraClearance; locationRect.yMin = ymin - extraClearance; locationRect.yMax = ymax + extraClearance; mapPixel.locationRect = locationRect; }