/// <summary> /// Transforms regular AutoTile input data into an output format that is optimized for /// efficient reading and updating operations. /// </summary> /// <param name="autoTileIndex"></param> /// <param name="autoTileInput"></param> /// <param name="tileData"></param> /// <param name="sourceTileCount"></param> /// <returns></returns> private TilesetAutoTileInfo TransformAutoTileData(int autoTileIndex, TilesetAutoTileInput autoTileInput, RawList <TileInfo> tileData, int sourceTileCount) { int[] stateToTileMap = new int[(int)TileConnection.All + 1]; TilesetAutoTileItem[] autoTileInfo = new TilesetAutoTileItem[sourceTileCount]; int baseTile = MathF.Clamp(autoTileInput.BaseTileIndex, 0, sourceTileCount - 1); // Initialize the tile mapping for all potential connection states with the base tile for (int conIndex = 0; conIndex < stateToTileMap.Length; conIndex++) { stateToTileMap[conIndex] = baseTile; } // Use the directly applicable tile mapping as-is int autoTileSourceTileCount = MathF.Min(autoTileInput.TileInput.Count, sourceTileCount); bool[] isStateAvailable = new bool[stateToTileMap.Length + 1]; for (int tileIndex = autoTileSourceTileCount - 1; tileIndex >= 0; tileIndex--) { TilesetAutoTileItem tileInput = autoTileInput.TileInput[tileIndex]; autoTileInfo[tileIndex] = tileInput; if (tileInput.IsAutoTile) { isStateAvailable[(int)tileInput.Neighbours] = true; stateToTileMap[(int)tileInput.Neighbours] = tileIndex; autoTileInfo[tileIndex].ConnectsToAutoTile = true; // Apply base tile information to the main tile dataset tileData.Count = Math.Max(tileData.Count, tileIndex + 1); tileData.Data[tileIndex].AutoTileLayer = autoTileIndex + 1; } } // Fill up unavailable state mappings with the closest available match for (int stateIndex = 0; stateIndex < isStateAvailable.Length; stateIndex++) { if (isStateAvailable[stateIndex]) { continue; } IReadOnlyList <TileConnection> fallbacks = AutoTileFallbackMap.GetFallback((TileConnection)stateIndex); for (int i = 0; i < fallbacks.Count; i++) { int fallbackStateIndex = (int)fallbacks[i]; if (isStateAvailable[fallbackStateIndex]) { stateToTileMap[stateIndex] = stateToTileMap[fallbackStateIndex]; break; } } } // Add the complete AutoTile info / mapping to the result data return(new TilesetAutoTileInfo( baseTile, stateToTileMap, autoTileInfo)); }
public EditTilesetAutoTileItemAction(Tileset tileset, TilesetAutoTileInput autoTile, RawList<TilesetAutoTileItem> tileInput, RawList<bool> tileInputMask) { if (tileset == null) throw new ArgumentNullException("tileset"); if (autoTile == null) throw new ArgumentNullException("autoTile"); if (tileInput == null) throw new ArgumentNullException("tileInput"); if (tileInputMask == null) throw new ArgumentNullException("tileInputMask"); if (tileInputMask.Count != tileInput.Count) throw new ArgumentException("Input Mask needs to be the same size as input.", "tileInputMask"); this.autoTile = autoTile; this.tileset = tileset; this.tileInput = tileInput; this.tileInputMask = tileInputMask; }
public EditTilesetAutoTileItemAction(Tileset tileset, TilesetAutoTileInput autoTile, int tileIndex, TilesetAutoTileItem tileInput) { if (tileset == null) throw new ArgumentNullException("tileset"); if (autoTile == null) throw new ArgumentNullException("autoTile"); this.tileset = tileset; this.autoTile = autoTile; this.tileInput = new RawList<TilesetAutoTileItem>(tileIndex + 1); this.tileInput.Count = tileIndex + 1; this.tileInput.Data[tileIndex] = tileInput; this.tileInputMask = new RawList<bool>(tileIndex + 1); this.tileInputMask.Count = tileIndex + 1; this.tileInputMask.Data[tileIndex] = true; }
/// <summary> /// Transforms regular AutoTile input data into an output format that is optimized for /// efficient reading and updating operations. /// </summary> /// <param name="autoTileIndex"></param> /// <param name="autoTileInput"></param> /// <param name="tileData"></param> /// <param name="sourceTileCount"></param> /// <returns></returns> private TilesetAutoTileInfo TransformAutoTileData(int autoTileIndex, TilesetAutoTileInput autoTileInput, RawList<TileInfo> tileData, int sourceTileCount) { int[] stateToTileMap = new int[(int)TileConnection.All + 1]; TilesetAutoTileItem[] autoTileInfo = new TilesetAutoTileItem[sourceTileCount]; int baseTile = MathF.Clamp(autoTileInput.BaseTileIndex, 0, sourceTileCount - 1); // Initialize the tile mapping for all potential connection states with the base tile for (int conIndex = 0; conIndex < stateToTileMap.Length; conIndex++) { stateToTileMap[conIndex] = baseTile; } // Use the directly applicable tile mapping as-is int autoTileSourceTileCount = MathF.Min(autoTileInput.TileInput.Count, sourceTileCount); bool[] isStateAvailable = new bool[stateToTileMap.Length + 1]; for (int tileIndex = autoTileSourceTileCount - 1; tileIndex >= 0; tileIndex--) { TilesetAutoTileItem tileInput = autoTileInput.TileInput[tileIndex]; autoTileInfo[tileIndex] = tileInput; if (tileInput.IsAutoTile) { isStateAvailable[(int)tileInput.Neighbours] = true; stateToTileMap[(int)tileInput.Neighbours] = tileIndex; autoTileInfo[tileIndex].ConnectsToAutoTile = true; // Apply base tile information to the main tile dataset tileData.Count = Math.Max(tileData.Count, tileIndex + 1); tileData.Data[tileIndex].AutoTileLayer = autoTileIndex + 1; } } // Fill up unavailable state mappings with the closest available match for (int stateIndex = 0; stateIndex < isStateAvailable.Length; stateIndex++) { if (isStateAvailable[stateIndex]) continue; IReadOnlyList<TileConnection> fallbacks = AutoTileFallbackMap.GetFallback((TileConnection)stateIndex); for (int i = 0; i < fallbacks.Count; i++) { int fallbackStateIndex = (int)fallbacks[i]; if (isStateAvailable[fallbackStateIndex]) { stateToTileMap[stateIndex] = stateToTileMap[fallbackStateIndex]; break; } } } // Add the complete AutoTile info / mapping to the result data return new TilesetAutoTileInfo( baseTile, stateToTileMap, autoTileInfo); }
/// <summary> /// Gathers and processes AutoTile input data into an easily modifyable intermediate format, /// while also collecting information on generated tiles and connectivity state mappings. /// </summary> /// <param name="autoTileConfig"></param> private void GatherAutoTileData(IReadOnlyList <TilesetAutoTileInput> autoTileConfig) { for (int autoTileIndex = 0; autoTileIndex < autoTileConfig.Count; autoTileIndex++) { TilesetAutoTileInput autoTileInput = autoTileConfig[autoTileIndex]; AutoTileData autoTile = new AutoTileData { BaseTile = MathF.Clamp(autoTileInput.BaseTileIndex, 0, this.inputTileCount - 1), TileInfo = this.autoTileItemPool.Rent(this.inputTileCount), StateToTile = new int[(int)TileConnection.All + 1], IsStateAvailable = new bool[(int)TileConnection.All + 1] }; autoTile.TileInfo.Count = this.inputTileCount; // Initialize the tile mapping for all potential connection states with the base tile for (int conIndex = 0; conIndex < autoTile.StateToTile.Length; conIndex++) { autoTile.StateToTile[conIndex] = autoTile.BaseTile; } // Use the directly applicable tile mapping as-is int autoTileSourceTileCount = MathF.Min(autoTileInput.TileInput.Count, this.inputTileCount); for (int tileIndex = autoTileSourceTileCount - 1; tileIndex >= 0; tileIndex--) { TilesetAutoTileItem tileInput = autoTileInput.TileInput[tileIndex]; autoTile.TileInfo[tileIndex] = tileInput; if (tileInput.IsAutoTile) { autoTile.IsStateAvailable[(int)tileInput.Neighbours] = true; autoTile.StateToTile[(int)tileInput.Neighbours] = tileIndex; autoTile.TileInfo.Data[tileIndex].ConnectsToAutoTile = true; // Apply base tile information to the main tile dataset this.tiles.Data[tileIndex].AutoTileLayer = autoTileIndex + 1; } } // Attempt to construct missing tiles of the minimum required base set from existing tiles // by using their sub-tile quadrants individually. Use a buffer for availability checks, so // we don't base generated tiles on previously generated tiles. autoTile.IsStateAvailable.CopyTo(this.autoTileStateBuffer, 0); for (int i = 0; i < AutoTileFallbackMap.BaseConnectivityTiles.Count; i++) { TileConnection connectivity = AutoTileFallbackMap.BaseConnectivityTiles[i]; if (this.autoTileStateBuffer[(int)connectivity]) { continue; } TileConnection topLeft = FindGeneratedAutoTileBase(TileQuadrant.TopLeft, connectivity, this.autoTileStateBuffer); TileConnection topRight = FindGeneratedAutoTileBase(TileQuadrant.TopRight, connectivity, this.autoTileStateBuffer); TileConnection bottomRight = FindGeneratedAutoTileBase(TileQuadrant.BottomRight, connectivity, this.autoTileStateBuffer); TileConnection bottomLeft = FindGeneratedAutoTileBase(TileQuadrant.BottomLeft, connectivity, this.autoTileStateBuffer); // Skip cases where we can't construct a full tile if (topLeft == TileConnection.None) { continue; } if (topRight == TileConnection.None) { continue; } if (bottomRight == TileConnection.None) { continue; } if (bottomLeft == TileConnection.None) { continue; } int generatedIndex = this.ScheduleGenerateTile( autoTile.BaseTile, autoTile.StateToTile[(int)topLeft], autoTile.StateToTile[(int)topRight], autoTile.StateToTile[(int)bottomRight], autoTile.StateToTile[(int)bottomLeft]); autoTile.IsStateAvailable[(int)connectivity] = true; autoTile.StateToTile[(int)connectivity] = generatedIndex; autoTile.TileInfo.Count = MathF.Max(autoTile.TileInfo.Count, generatedIndex + 1); autoTile.TileInfo.Data[generatedIndex] = autoTileInput.TileInput[autoTile.BaseTile]; autoTile.TileInfo[generatedIndex] = new TilesetAutoTileItem { IsAutoTile = true, ConnectsToAutoTile = true, Neighbours = connectivity }; } // Fill up unavailable state mappings with the closest available match for (int stateIndex = 0; stateIndex < autoTile.IsStateAvailable.Length; stateIndex++) { if (autoTile.IsStateAvailable[stateIndex]) { continue; } IReadOnlyList <TileConnection> fallbacks = AutoTileFallbackMap.GetFallback((TileConnection)stateIndex); for (int i = 0; i < fallbacks.Count; i++) { int fallbackStateIndex = (int)fallbacks[i]; if (autoTile.IsStateAvailable[fallbackStateIndex]) { autoTile.StateToTile[stateIndex] = autoTile.StateToTile[fallbackStateIndex]; break; } } } // Add the gathered info to our local working data this.autoTiles.Add(autoTile); } }
/// <summary> /// Compiles a <see cref="Tileset"/> using its specified source data, in order to /// generate optimized target data for rendering and collision detection. /// </summary> public TilesetCompilerOutput Compile(TilesetCompilerInput input) { TilesetCompilerOutput output = input.ExistingOutput; output.TileData = output.TileData ?? new RawList <TileInfo>(input.TileInput.Count); output.RenderData = output.RenderData ?? new List <Texture>(); output.AutoTileData = output.AutoTileData ?? new List <TilesetAutoTileInfo>(); // Clear existing data, but keep the sufficiently big data structures output.TileData.Clear(); output.RenderData.Clear(); output.AutoTileData.Clear(); // Determine how many source tiles we have int sourceTileCount = int.MaxValue; for (int renderInputIndex = 0; renderInputIndex < input.RenderConfig.Count; renderInputIndex++) { TilesetRenderInput renderInput = input.RenderConfig[renderInputIndex] ?? DefaultRenderInput; PixelData sourceLayerData = (renderInput.SourceData.Res ?? Pixmap.Checkerboard.Res).MainLayer; LayerGeometry layerGeometry = this.CalculateLayerGeometry(renderInput, sourceLayerData); sourceTileCount = Math.Min(sourceTileCount, layerGeometry.SourceTileCount); } if (input.RenderConfig.Count == 0) { sourceTileCount = 0; } // Transform AutoTile data for (int autoTileIndex = 0; autoTileIndex < input.AutoTileConfig.Count; autoTileIndex++) { TilesetAutoTileInput autoTileInput = input.AutoTileConfig[autoTileIndex]; TilesetAutoTileInfo autoTileInfo = this.TransformAutoTileData( autoTileIndex, autoTileInput, output.TileData, sourceTileCount); output.AutoTileData.Add(autoTileInfo); } // Initialize all tiles to being visually empty. They will be subtractively updated // during output pixel data generation in the next step. { int tileDataCount = output.TileData.Count; TileInfo[] tileData = output.TileData.Data; for (int i = 0; i < tileDataCount; i++) { tileData[i].IsVisuallyEmpty = true; } } // Generate output pixel data for (int renderInputIndex = 0; renderInputIndex < input.RenderConfig.Count; renderInputIndex++) { TilesetRenderInput renderInput = input.RenderConfig[renderInputIndex] ?? DefaultRenderInput; PixelData sourceLayerData = (renderInput.SourceData.Res ?? Pixmap.Checkerboard.Res).MainLayer; // Determine overal geometry values for this layer, such as tile bounds and texture sizes LayerGeometry layerGeometry = this.CalculateLayerGeometry(renderInput, sourceLayerData); // Generate pixel data and atlas values for this layer's texture LayerPixelData targetLayerData = this.GenerateLayerPixelData( renderInput, sourceLayerData, layerGeometry, output.TileData); // Create the texture to be used for this rendering input using (Pixmap targetPixmap = new Pixmap(targetLayerData.PixelData)) { targetPixmap.Atlas = targetLayerData.Atlas; Texture targetTexture = new Texture( targetPixmap, TextureSizeMode.Enlarge, renderInput.TargetMagFilter, renderInput.TargetMinFilter, TextureWrapMode.Clamp, TextureWrapMode.Clamp, renderInput.TargetFormat); output.RenderData.Add(targetTexture); } } // Generate additional per-tile data this.TransformTileData(input.TileInput, output.TileData, output.RenderData); // Apply global tileset stats output.TileCount = sourceTileCount; return(output); }
/// <inheritdoc /> public override void AddLayer() { base.AddLayer(); Tileset tileset = this.SelectedTileset.Res; if (tileset == null) return; // Decide upon an id for our new layer string autoTileId = TilesetAutoTileInput.DefaultId + (tileset.AutoTileConfig.Count).ToString(); string autoTileName = TilesetAutoTileInput.DefaultName; // Create a new AutoTile using an UndoRedo action TilesetAutoTileInput newAutoTile = new TilesetAutoTileInput { Id = autoTileId, Name = autoTileName }; UndoRedoManager.Do(new AddTilesetConfigLayerAction<TilesetAutoTileInput>( tileset, TilemapsReflectionInfo.Property_Tileset_AutoTileConfig, newAutoTile)); // Select the newly created AutoTile AutoTileInputNode modelNode = this.treeModel .Nodes .OfType<AutoTileInputNode>() .FirstOrDefault(n => n.AutoTileInput == newAutoTile); this.SelectLayer(modelNode); }
public AutoTileInputNode(TilesetAutoTileInput autoTile) : base() { this.autoTile = autoTile; this.Image = Properties.TilemapsResCache.IconTilesetAutoTileLayer; }