/// <summary> /// Adds exterior ground tiles to the batch. /// </summary> /// <param name="blockData">Block data.</param> private void AddRMBGroundTiles(ref DFBlock blockData) { // Make ground slightly lower to minimise depth-fighting on ground aligned polygons const float groundHeight = 0f; // Corner positions Vector3 topLeftPos, topRightPos, bottomLeftPos, bottomRightPos; Vector2 topLeftUV, topRightUV, bottomLeftUV, bottomRightUV; // Create vertices. These will be updated for each tile based on position and UV orientation. VertexPositionNormalTextureBump[] vertices = new VertexPositionNormalTextureBump[4]; // Create indices. These are the same for every tile. int[] indices = new int[] { 0, 1, 2, 1, 3, 2 }; // Loop through tiles int tileCount = 16; float tileDimension = 256.0f * ModelManager.GlobalScale; for (int y = 0; y < tileCount; y++) { for (int x = 0; x < tileCount; x++) { // Get source tile data DFBlock.RmbGroundTiles tile = blockData.RmbBlock.FldHeader.GroundData.GroundTiles[x, y]; // Set random terrain marker back to grass int textureRecord = (tile.TextureRecord > 55) ? 2 : tile.TextureRecord; // Create material BaseMaterialEffect material = core.ModelManager.CreateModelMaterial( (int)DFLocation.ClimateTextureSet.Exterior_Terrain, textureRecord); material.SamplerState0 = SamplerState.AnisotropicClamp; // Create vertices for this quad topLeftPos = new Vector3(x * tileDimension, groundHeight, y * tileDimension); topRightPos = new Vector3(topLeftPos.X + tileDimension, groundHeight, topLeftPos.Z); bottomLeftPos = new Vector3(topLeftPos.X, groundHeight, topLeftPos.Z + tileDimension); bottomRightPos = new Vector3(topLeftPos.X + tileDimension, groundHeight, topLeftPos.Z + tileDimension); // Set UVs if (tile.IsRotated && !tile.IsFlipped) { // Rotate only topLeftUV = new Vector2(1, 0); topRightUV = new Vector2(1, 1); bottomLeftUV = new Vector2(0, 0); bottomRightUV = new Vector2(0, 1); } else if (tile.IsFlipped && !tile.IsRotated) { // Flip only topLeftUV = new Vector2(1, 1); topRightUV = new Vector2(0, 1); bottomLeftUV = new Vector2(1, 0); bottomRightUV = new Vector2(0, 0); } else if (tile.IsRotated && tile.IsRotated) { // Rotate and flip topLeftUV = new Vector2(0, 1); topRightUV = new Vector2(0, 0); bottomLeftUV = new Vector2(1, 1); bottomRightUV = new Vector2(1, 0); } else { // No rotate or flip topLeftUV = new Vector2(0, 0); topRightUV = new Vector2(1, 0); bottomLeftUV = new Vector2(0, 1); bottomRightUV = new Vector2(1, 1); } // Set vertices vertices[0] = new VertexPositionNormalTextureBump(topLeftPos, Vector3.Up, topLeftUV, Vector3.Zero, Vector3.Zero); vertices[1] = new VertexPositionNormalTextureBump(topRightPos, Vector3.Up, topRightUV, Vector3.Zero, Vector3.Zero); vertices[2] = new VertexPositionNormalTextureBump(bottomLeftPos, Vector3.Up, bottomLeftUV, Vector3.Zero, Vector3.Zero); vertices[3] = new VertexPositionNormalTextureBump(bottomRightPos, Vector3.Up, bottomRightUV, Vector3.Zero, Vector3.Zero); // Add to builder staticGeometry.AddToBuilder(material.ID, vertices, indices, Matrix.Identity); } } }
// 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; }
/// <summary> /// Gets a simple ground plane mesh. /// This is only used for RMB block layouts, not for terrain system. /// </summary> /// <param name="blockData">BlockData for tiles layout.</param> /// <param name="tileMap">Tilemap Color32 array for shader.</param> /// <returns>Mesh.</returns> public Mesh GetSimpleGroundPlaneMesh( ref DFBlock blockData, out Color32[] tileMap, bool solveTangents = false, bool lightmapUVs = false) { const int tileDim = 16; tileMap = new Color32[tileDim * tileDim]; // Make ground slightly lower to minimise depth-fighting on ground aligned polygons // But not too low, or shadows can be seen under buildings float groundHeight = DaggerfallGroundPlane.GroundOffset * MeshReader.GlobalScale; // Create tilemap for (int y = 0; y < tileDim; y++) { for (int x = 0; x < tileDim; x++) { // Get source tile data DFBlock.RmbGroundTiles tile = blockData.RmbBlock.FldHeader.GroundData.GroundTiles[x, (tileDim - 1) - y]; // Calculate tile index byte record = (byte)(tile.TextureRecord * 4); if (tile.IsRotated && !tile.IsFlipped) { record += 1; } if (!tile.IsRotated && tile.IsFlipped) { record += 2; } if (tile.IsRotated && tile.IsFlipped) { record += 3; } // Assign tile index, setting random marker back to grass int offset = (y * tileDim) + x; if (tile.TextureRecord < 56) { tileMap[offset] = new Color32(record, 0, 0, 0); } else { tileMap[offset] = new Color32(8, 0, 0, 0); // Index 8 is grass } } } // Create a basic quad float tileSize = DaggerfallGroundPlane.TileSize * GlobalScale; float quadSize = tileSize * tileDim; // Vertices Vector3[] verts = new Vector3[4]; verts[0] = new Vector3(0, groundHeight, 0); verts[1] = new Vector3(0, groundHeight, quadSize); verts[2] = new Vector3(quadSize, groundHeight, quadSize); verts[3] = new Vector3(quadSize, groundHeight, 0); // Normals Vector3[] norms = new Vector3[4]; norms[0] = Vector3.up; norms[1] = Vector3.up; norms[2] = Vector3.up; norms[3] = Vector3.up; // UVs Vector2[] uvs = new Vector2[4]; uvs[0] = new Vector2(0, 0); uvs[1] = new Vector2(0, 1); uvs[2] = new Vector2(1, 1); uvs[3] = new Vector2(1, 0); // Indices int[] indices = new int[6]; indices[0] = 0; indices[1] = 1; indices[2] = 2; indices[3] = 0; indices[4] = 2; indices[5] = 3; // Create mesh Mesh mesh = new Mesh(); mesh.name = "SimpleGroundPlaneMesh"; mesh.vertices = verts; mesh.normals = norms; mesh.uv = uvs; mesh.triangles = indices; // Finalise mesh if (solveTangents) { TangentSolver(mesh); } if (lightmapUVs) { AddLightmapUVs(mesh); } mesh.RecalculateBounds(); return(mesh); }
// Set texture and height data for city tiles public static void SetLocationTiles(ContentReader contentReader, ref MapPixelData mapPixel) { const int tileDim = 16; const int chunkDim = 8; // Get location DFLocation location = contentReader.MapFileReader.GetLocation(mapPixel.mapRegionIndex, mapPixel.mapLocationIndex); // Centre location tiles inside terrain area int startX = ((chunkDim * tileDim) - location.Exterior.ExteriorData.Width * tileDim) / 2; int startY = ((chunkDim * tileDim) - location.Exterior.ExteriorData.Height * tileDim) / 2; // 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 = terrainSampleDim, ymin = terrainSampleDim; 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 = contentReader.MapFileReader.GetRmbBlockName(ref location, blockX, blockY); if (!contentReader.GetBlock(blockName, out block)) { continue; } // Copy ground tile info for (int tileY = 0; tileY < tileDim; tileY++) { for (int tileX = 0; tileX < tileDim; tileX++) { DFBlock.RmbGroundTiles tile = block.RmbBlock.FldHeader.GroundData.GroundTiles[tileX, (tileDim - 1) - tileY]; int xpos = startX + blockX * tileDim + tileX; int ypos = startY + blockY * tileDim + tileY; int offset = (ypos * terrainSampleDim) + xpos; int record = tile.TextureRecord; 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.samples[offset].record = record; mapPixel.samples[offset].flip = tile.IsFlipped; mapPixel.samples[offset].rotate = tile.IsRotated; mapPixel.samples[offset].location = true; } } } } } // Update location rect with extra clearance const int extraClearance = 2; Rect locationRect = new Rect(); locationRect.xMin = xmin - extraClearance; locationRect.xMax = xmax + extraClearance; locationRect.yMin = ymin - extraClearance; locationRect.yMax = ymax + extraClearance; mapPixel.locationRect = locationRect; }
// Set location tilemap data public static void SetLocationTiles(ref MapPixelData mapPixel) { //const int tileDim = 16; //const int chunkDim = 8; DaggerfallUnity dfUnity = DaggerfallUnity.Instance; // Get location DFLocation location = dfUnity.ContentReader.MapFileReader.GetLocation(mapPixel.mapRegionIndex, mapPixel.mapLocationIndex); // Centre location tiles inside terrain area //int startX = ((chunkDim * tileDim) - location.Exterior.ExteriorData.Width * tileDim) / 2; //int startY = ((chunkDim * tileDim) - location.Exterior.ExteriorData.Height * tileDim) / 2; // Position tiles inside terrain area int width = location.Exterior.ExteriorData.Width; int height = location.Exterior.ExteriorData.Height; DFPosition tilePos = TerrainHelper.GetLocationTerrainTileOrigin(width, height); // 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; int record = tile.TextureRecord; 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.tilemapSamples[xpos, ypos].record = record; mapPixel.tilemapSamples[xpos, ypos].flip = tile.IsFlipped; mapPixel.tilemapSamples[xpos, ypos].rotate = tile.IsRotated; mapPixel.tilemapSamples[xpos, ypos].location = true; } } } } } // Update location rect with extra clearance const int extraClearance = 2; Rect locationRect = new Rect(); locationRect.xMin = xmin - extraClearance; locationRect.xMax = xmax + extraClearance; locationRect.yMin = ymin - extraClearance; locationRect.yMax = ymax + extraClearance; mapPixel.locationRect = locationRect; }