Example #1
0
        /// <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;
        }
Example #4
0
        /// <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);
        }
Example #5
0
        /// <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);
            }
        }
Example #6
0
        /// <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;
 }