Beispiel #1
0
        /// <summary>
        /// Determines the overall geometry of a single <see cref="Tileset"/> visual layer. This involves
        /// tile boundaries in source and target data, as well as texture sizes and similar.
        /// </summary>
        /// <param name="renderInput"></param>
        /// <param name="layerData"></param>
        private LayerGeometry CalculateLayerGeometry(TilesetRenderInput renderInput, PixelData layerData)
        {
            LayerGeometry geometry;

            // What's the space requirement for each tile?
            geometry.SourceTileAdvance = renderInput.SourceTileAdvance;
            geometry.TargetTileAdvance = renderInput.TargetTileAdvance;

            // How many tiles will we have?
            Point2 tileCount = renderInput.GetSourceTileCount(layerData.Width, layerData.Height);

            geometry.SourceTilesPerRow    = tileCount.X;
            geometry.SourceTilesPerColumn = tileCount.Y;
            geometry.SourceTileCount      = geometry.SourceTilesPerRow * geometry.SourceTilesPerColumn;
            geometry.TargetTileCount      = geometry.SourceTileCount + this.generateTileSchedule.Count;

            // What's the optimal texture size to include them all?
            int minTilesPerLine = MathF.Max(1, (int)MathF.Sqrt(geometry.TargetTileCount));

            geometry.TargetTextureSize.X = MathF.NextPowerOfTwo(geometry.TargetTileAdvance.X * minTilesPerLine);

            int actualTilesPerLine = geometry.TargetTextureSize.X / geometry.TargetTileAdvance.X;
            int requiredLineCount  = 1 + (geometry.TargetTileCount / actualTilesPerLine);

            geometry.TargetTextureSize.Y = MathF.NextPowerOfTwo(geometry.TargetTileAdvance.Y * requiredLineCount);

            return(geometry);
        }
Beispiel #2
0
        /// <summary>
        /// Initializes local working data for processing the specified <see cref="TilesetCompilerInput"/>.
        /// </summary>
        /// <param name="input"></param>
        private void InitWorkingData(TilesetCompilerInput input)
        {
            // Determine how many source tiles we have. This will be the smallest overlap
            // of available rendering input layers
            this.inputTileCount = 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);
                this.inputTileCount = Math.Min(this.inputTileCount, layerGeometry.SourceTileCount);
            }
            if (input.RenderConfig.Count == 0)
            {
                this.inputTileCount = 0;
            }

            // Initialize intermediate tile data to be big enough for the source data
            this.outputTileCount = this.inputTileCount;
            this.tiles.Count     = this.inputTileCount;
            TileInfo[] data = this.tiles.Data;
            for (int i = 0; i < this.inputTileCount; i++)
            {
                data[i].IsVisuallyEmpty = true;
            }

            // Copy input data to our intermediate data structures
            TileInput[] inputData = input.TileInput.Data;
            for (int i = MathF.Min(this.inputTileCount, input.TileInput.Count); i >= 0; i--)
            {
                data[i].DepthOffset = inputData[i].DepthOffset;
                data[i].IsVertical  = inputData[i].IsVertical;
                data[i].Collision   = inputData[i].Collision;
            }
        }
Beispiel #3
0
        /// <summary>
        /// Looks up the source pixel coordinates for the specified input layer and tile.
        /// </summary>
        /// <param name="renderConfigIndex"></param>
        /// <param name="tileIndex"></param>
        /// <param name="pos"></param>
        /// <param name="size"></param>
        public void LookupTileSourceRect(int renderConfigIndex, int tileIndex, out Point2 pos, out Point2 size)
        {
            TilesetRenderInput input = this.renderConfig[renderConfigIndex];

            Point2 advance   = input.SourceTileAdvance;
            Point2 tileCount = input.GetSourceTileCount(
                input.SourceData.Res.Width,
                input.SourceData.Res.Height);
            Point2 tileCoords = new Point2(
                tileIndex % tileCount.X,
                tileIndex / tileCount.X);

            size = input.SourceTileSize;
            pos  = new Point2(
                tileCoords.X * advance.X,
                tileCoords.Y * advance.Y);
        }
Beispiel #4
0
        /// <summary>
        /// Generates pixel and atlas data for a single <see cref="Tileset"/> visual layer.
        /// </summary>
        /// <param name="renderInput"></param>
        /// <param name="sourceData"></param>
        /// <param name="geometry"></param>
        /// <param name="tileData"></param>
        /// <returns></returns>
        private LayerPixelData GenerateLayerPixelData(TilesetRenderInput renderInput, PixelData sourceData, LayerGeometry geometry, RawList<TileInfo> tileData)
        {
            // Create a buffer for writing target pixel data
            LayerPixelData target;
            target.PixelData = new PixelData(geometry.TargetTextureSize.X, geometry.TargetTextureSize.Y);
            target.Atlas = new List<Rect>();

            // Iterate over tiles and move each tile from source to target
            Point2 targetTilePos = new Point2(0, 0);
            for (int tileIndex = 0; tileIndex < geometry.SourceTileCount; tileIndex++)
            {
                // Initialize a new tile info when necessary
                if (tileIndex >= tileData.Count)
                {
                    tileData.Count++;
                    tileData.Data[tileIndex].IsVisuallyEmpty = true;
                }

                // Determine where on the source buffer the tile is located
                Point2 sourceTilePos = new Point2(
                    geometry.SourceTileAdvance.X * (tileIndex % geometry.SourceTilesPerRow),
                    geometry.SourceTileAdvance.Y * (tileIndex / geometry.SourceTilesPerRow));

                // Draw the source tile onto the target buffer, including its spacing / border
                Point2 targetContentPos = new Point2(
                    targetTilePos.X + renderInput.TargetTileMargin,
                    targetTilePos.Y + renderInput.TargetTileMargin);
                sourceData.DrawOnto(target.PixelData,
                    BlendMode.Solid,
                    targetContentPos.X,
                    targetContentPos.Y,
                    renderInput.SourceTileSize.X,
                    renderInput.SourceTileSize.Y,
                    sourceTilePos.X,
                    sourceTilePos.Y);

                // Fill up the target spacing area with similar pixels
                if (renderInput.TargetTileMargin > 0)
                {
                    FillTileSpacing(target.PixelData, renderInput.TargetTileMargin, targetContentPos, renderInput.SourceTileSize);
                }

                // Update whether the tile is considered visually empty
                if (tileData.Data[tileIndex].IsVisuallyEmpty)
                {
                    bool isLayerVisuallyEmpty = IsCompletelyTransparent(
                        sourceData,
                        sourceTilePos,
                        renderInput.SourceTileSize);
                    if (!isLayerVisuallyEmpty)
                        tileData.Data[tileIndex].IsVisuallyEmpty = false;
                }

                // Add an entry to the generated atlas
                Rect atlasRect = new Rect(
                    targetTilePos.X + renderInput.TargetTileMargin,
                    targetTilePos.Y + renderInput.TargetTileMargin,
                    geometry.TargetTileAdvance.X - renderInput.TargetTileMargin * 2,
                    geometry.TargetTileAdvance.Y - renderInput.TargetTileMargin * 2);
                target.Atlas.Add(atlasRect);

                // Advance the target tile position
                targetTilePos.X += geometry.TargetTileAdvance.X;
                if (targetTilePos.X + geometry.TargetTileAdvance.X > target.PixelData.Width)
                {
                    targetTilePos.X = 0;
                    targetTilePos.Y += geometry.TargetTileAdvance.Y;
                }
            }

            return target;
        }
Beispiel #5
0
        /// <summary>
        /// Determines the overall geometry of a single <see cref="Tileset"/> visual layer. This involves
        /// tile boundaries in source and target data, as well as texture sizes and similar.
        /// </summary>
        /// <param name="renderInput"></param>
        /// <param name="layerData"></param>
        /// <returns></returns>
        private LayerGeometry CalculateLayerGeometry(TilesetRenderInput renderInput, PixelData layerData)
        {
            LayerGeometry geometry;

            // What's the space requirement for each tile?
            geometry.SourceTileAdvance = renderInput.SourceTileAdvance;
            geometry.TargetTileAdvance = renderInput.TargetTileAdvance;

            // How many tiles will we have?
            Point2 tileCount = renderInput.GetSourceTileCount(layerData.Width, layerData.Height);
            geometry.SourceTilesPerRow = tileCount.X;
            geometry.SourceTilesPerColumn = tileCount.Y;
            geometry.SourceTileCount = geometry.SourceTilesPerRow * geometry.SourceTilesPerColumn;
            geometry.TargetTileCount = geometry.SourceTileCount; // ToDo: Account for expanded AutoTiles

            // What's the optimal texture size to include them all?
            int minTilesPerLine = MathF.Max(1, (int)MathF.Sqrt(geometry.TargetTileCount));
            geometry.TargetTextureSize.X = MathF.NextPowerOfTwo(geometry.TargetTileAdvance.X * minTilesPerLine);

            int actualTilesPerLine = geometry.TargetTextureSize.X / geometry.TargetTileAdvance.X;
            int requiredLineCount = 1 + (geometry.TargetTileCount / actualTilesPerLine);
            geometry.TargetTextureSize.Y = MathF.NextPowerOfTwo(geometry.TargetTileAdvance.Y * requiredLineCount);

            return geometry;
        }
        /// <inheritdoc />
        public override void AddLayer()
        {
            base.AddLayer();
            Tileset tileset = this.SelectedTileset.Res;
            if (tileset == null) return;

            // Determine which texture IDs are already present, so we can
            // derive which one we'll pick as a default for creating a new one.
            bool hasMainTex = false;
            int highestCustomTex = -1;
            for (int i = 0; i < tileset.RenderConfig.Count; i++)
            {
                if (tileset.RenderConfig[i].Id == TilesetRenderInput.MainTexId)
                {
                    hasMainTex = true;
                }
                else if (tileset.RenderConfig[i].Id.StartsWith(TilesetRenderInput.CustomTexId))
                {
                    string customTexIndexString = tileset.RenderConfig[i].Id.Substring(
                        TilesetRenderInput.CustomTexId.Length,
                        tileset.RenderConfig[i].Id.Length - TilesetRenderInput.CustomTexId.Length);

                    int customTexIndex;
                    if (!int.TryParse(customTexIndexString, out customTexIndex))
                        customTexIndex = 0;

                    highestCustomTex = Math.Max(highestCustomTex, customTexIndex);
                }
            }

            // Decide upon an id for our new layer
            string layerId;
            string layerName;
            if (!hasMainTex)
            {
                layerId = TilesetRenderInput.MainTexId;
                layerName = TilesetRenderInput.MainTexName;
            }
            else
            {
                layerId = TilesetRenderInput.CustomTexId + (highestCustomTex + 1).ToString();
                layerName = TilesetRenderInput.CustomTexName;
            }

            // Create a new layer using an UndoRedo action
            TilesetRenderInput newLayer = new TilesetRenderInput
            {
                Id = layerId,
                Name = layerName
            };
            UndoRedoManager.Do(new AddTilesetConfigLayerAction<TilesetRenderInput>(
                tileset,
                TilemapsReflectionInfo.Property_Tileset_RenderConfig,
                newLayer));

            // Select the newly created visual layer
            VisualLayerNode modelNode = this.treeModel
                .Nodes
                .OfType<VisualLayerNode>()
                .FirstOrDefault(n => n.VisualLayer == newLayer);
            this.SelectLayer(modelNode);
        }
 public VisualLayerNode(TilesetRenderInput layer)
     : base()
 {
     this.layer = layer;
     this.Image = Properties.TilemapsResCache.IconTilesetSingleVisualLayer;
 }
Beispiel #8
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)
        {
            // Clear private working data before beginning a new operation
            this.ClearWorkingData();

            // Initialize private working data with the required space for our new input
            this.InitWorkingData(input);

            // Gather data on AutoTiles that are partially transformed from the input tileset,
            // and partially generated from existing tiles as part of the compilation.
            this.GatherAutoTileData(input.AutoTileConfig);

            // Generate metadata for scheduled tiles
            this.GenerateTileMetadata();

            // Draw 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 overall 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.ComposeLayerPixelData(
                    renderInput,
                    sourceLayerData,
                    layerGeometry);

                // 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);

                    this.renderData.Add(targetTexture);
                }
            }

            // Retrieve texture atlas data for quick lookup during rendering
            if (this.renderData.Count > 0)
            {
                for (int i = 0; i < this.tiles.Count; i++)
                {
                    this.renderData[0].LookupAtlas(i, out this.tiles.Data[i].TexCoord0);
                }
            }

            // Prepare output, keeping existing structures to avoid allocations
            TilesetCompilerOutput output = input.ExistingOutput;

            output.TileCount = this.outputTileCount;

            output.TileData = output.TileData ?? new RawList <TileInfo>(input.TileInput.Count);
            output.TileData.Clear();
            this.tiles.CopyTo(output.TileData, 0, this.tiles.Count);

            output.RenderData = output.RenderData ?? new List <Texture>(this.renderData);
            output.RenderData.Clear();
            output.RenderData.AddRange(this.renderData);

            output.AutoTileData = output.AutoTileData ?? new List <TilesetAutoTileInfo>();
            output.AutoTileData.Clear();
            this.TransformAutoTileData(output.AutoTileData);

            output.EmptyTileIndex = this.GetEmptyTileIndex();

            this.ClearWorkingData();
            return(output);
        }
Beispiel #9
0
        /// <summary>
        /// Composes pixel and atlas data for a single <see cref="Tileset"/> visual layer.
        /// </summary>
        private LayerPixelData ComposeLayerPixelData(TilesetRenderInput renderInput, PixelData sourceData, LayerGeometry geometry)
        {
            // Create a buffer for writing target pixel data
            LayerPixelData target;

            target.PixelData = new PixelData(geometry.TargetTextureSize.X, geometry.TargetTextureSize.Y);
            target.Atlas     = new List <Rect>();

            // Iterate over source tiles and copy each tile from source to target
            Point2 targetTilePos = new Point2(0, 0);

            for (int tileIndex = 0; tileIndex < geometry.SourceTileCount; tileIndex++)
            {
                // Determine source and target positions on pixel data / buffer
                Point2 sourceTilePos    = geometry.GetSourceTilePos(tileIndex);
                Point2 targetContentPos = new Point2(
                    targetTilePos.X + renderInput.TargetTileMargin,
                    targetTilePos.Y + renderInput.TargetTileMargin);

                // Draw the source tile onto the target buffer, including its spacing / border
                sourceData.DrawOnto(target.PixelData,
                                    BlendMode.Solid,
                                    targetContentPos.X,
                                    targetContentPos.Y,
                                    renderInput.SourceTileSize.X,
                                    renderInput.SourceTileSize.Y,
                                    sourceTilePos.X,
                                    sourceTilePos.Y);

                // Fill up the target spacing area with similar pixels
                if (renderInput.TargetTileMargin > 0)
                {
                    FillTileSpacing(target.PixelData, renderInput.TargetTileMargin, targetContentPos, renderInput.SourceTileSize);
                }

                // Update whether the tile is considered visually empty
                if (this.tiles.Data[tileIndex].IsVisuallyEmpty)
                {
                    bool isLayerVisuallyEmpty = IsCompletelyTransparent(
                        sourceData,
                        sourceTilePos,
                        renderInput.SourceTileSize);
                    if (!isLayerVisuallyEmpty)
                    {
                        this.tiles.Data[tileIndex].IsVisuallyEmpty = false;
                    }
                }

                // Add an entry to the generated atlas
                Rect atlasRect = new Rect(
                    targetTilePos.X + renderInput.TargetTileMargin,
                    targetTilePos.Y + renderInput.TargetTileMargin,
                    geometry.TargetTileAdvance.X - renderInput.TargetTileMargin * 2,
                    geometry.TargetTileAdvance.Y - renderInput.TargetTileMargin * 2);
                target.Atlas.Add(atlasRect);

                // Advance the target tile position
                targetTilePos.X += geometry.TargetTileAdvance.X;
                if (targetTilePos.X + geometry.TargetTileAdvance.X > target.PixelData.Width)
                {
                    targetTilePos.X  = 0;
                    targetTilePos.Y += geometry.TargetTileAdvance.Y;
                }
            }

            // Generate additional target tiles as scheduled
            foreach (GeneratedQuadTile tile in this.generateTileSchedule)
            {
                // Determine source and target positions on pixel data / buffer
                Point2 sourceTilePosTopLeft     = geometry.GetSourceTilePos(tile.SourceTopLeftIndex);
                Point2 sourceTilePosTopRight    = geometry.GetSourceTilePos(tile.SourceTopRightIndex);
                Point2 sourceTilePosBottomRight = geometry.GetSourceTilePos(tile.SourceBottomRightIndex);
                Point2 sourceTilePosBottomLeft  = geometry.GetSourceTilePos(tile.SourceBottomLeftIndex);
                Point2 targetContentPos         = new Point2(
                    targetTilePos.X + renderInput.TargetTileMargin,
                    targetTilePos.Y + renderInput.TargetTileMargin);

                // Draw the source tile onto the target buffer, including its spacing / border
                Point2 quadSize = renderInput.SourceTileSize / 2;
                sourceData.DrawOnto(target.PixelData, BlendMode.Solid,
                                    targetContentPos.X,
                                    targetContentPos.Y,
                                    quadSize.X,
                                    quadSize.Y,
                                    sourceTilePosTopLeft.X,
                                    sourceTilePosTopLeft.Y);
                sourceData.DrawOnto(target.PixelData, BlendMode.Solid,
                                    targetContentPos.X + quadSize.X,
                                    targetContentPos.Y,
                                    quadSize.X,
                                    quadSize.Y,
                                    sourceTilePosTopRight.X + quadSize.X,
                                    sourceTilePosTopRight.Y);
                sourceData.DrawOnto(target.PixelData, BlendMode.Solid,
                                    targetContentPos.X + quadSize.X,
                                    targetContentPos.Y + quadSize.Y,
                                    quadSize.X,
                                    quadSize.Y,
                                    sourceTilePosBottomRight.X + quadSize.X,
                                    sourceTilePosBottomRight.Y + quadSize.Y);
                sourceData.DrawOnto(target.PixelData, BlendMode.Solid,
                                    targetContentPos.X,
                                    targetContentPos.Y + quadSize.Y,
                                    quadSize.X,
                                    quadSize.Y,
                                    sourceTilePosBottomLeft.X,
                                    sourceTilePosBottomLeft.Y + quadSize.Y);

                // Fill up the target spacing area with similar pixels
                if (renderInput.TargetTileMargin > 0)
                {
                    FillTileSpacing(target.PixelData, renderInput.TargetTileMargin, targetContentPos, renderInput.SourceTileSize);
                }

                // Add an entry to the generated atlas
                Rect atlasRect = new Rect(
                    targetTilePos.X + renderInput.TargetTileMargin,
                    targetTilePos.Y + renderInput.TargetTileMargin,
                    geometry.TargetTileAdvance.X - renderInput.TargetTileMargin * 2,
                    geometry.TargetTileAdvance.Y - renderInput.TargetTileMargin * 2);
                target.Atlas.Add(atlasRect);

                // Advance the target tile position
                targetTilePos.X += geometry.TargetTileAdvance.X;
                if (targetTilePos.X + geometry.TargetTileAdvance.X > target.PixelData.Width)
                {
                    targetTilePos.X  = 0;
                    targetTilePos.Y += geometry.TargetTileAdvance.Y;
                }
            }

            // Update which tiles are considered visually empty
            for (int tileIndex = 0; tileIndex < this.outputTileCount; tileIndex++)
            {
                // Determine target positions on pixel data / buffer
                Rect targetRect = target.Atlas[tileIndex];

                // Update whether the tile is considered visually empty
                if (this.tiles.Data[tileIndex].IsVisuallyEmpty)
                {
                    bool isLayerVisuallyEmpty = IsCompletelyTransparent(
                        target.PixelData,
                        (Point2)targetRect.Pos,
                        (Point2)targetRect.Size);
                    if (!isLayerVisuallyEmpty)
                    {
                        this.tiles.Data[tileIndex].IsVisuallyEmpty = false;
                    }
                }
            }

            return(target);
        }
Beispiel #10
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);
        }
Beispiel #11
0
        /// <summary>
        /// Generates pixel and atlas data for a single <see cref="Tileset"/> visual layer.
        /// </summary>
        /// <param name="renderInput"></param>
        /// <param name="sourceData"></param>
        /// <param name="geometry"></param>
        /// <param name="tileData"></param>
        /// <returns></returns>
        private LayerPixelData GenerateLayerPixelData(TilesetRenderInput renderInput, PixelData sourceData, LayerGeometry geometry, RawList <TileInfo> tileData)
        {
            // Create a buffer for writing target pixel data
            LayerPixelData target;

            target.PixelData = new PixelData(geometry.TargetTextureSize.X, geometry.TargetTextureSize.Y);
            target.Atlas     = new List <Rect>();

            // Iterate over tiles and move each tile from source to target
            Point2 targetTilePos = new Point2(0, 0);

            for (int tileIndex = 0; tileIndex < geometry.SourceTileCount; tileIndex++)
            {
                // Initialize a new tile info when necessary
                if (tileIndex >= tileData.Count)
                {
                    tileData.Count++;
                    tileData.Data[tileIndex].IsVisuallyEmpty = true;
                }

                // Determine where on the source buffer the tile is located
                Point2 sourceTilePos = new Point2(
                    geometry.SourceTileAdvance.X * (tileIndex % geometry.SourceTilesPerRow),
                    geometry.SourceTileAdvance.Y * (tileIndex / geometry.SourceTilesPerRow));

                // Draw the source tile onto the target buffer, including its spacing / border
                Point2 targetContentPos = new Point2(
                    targetTilePos.X + renderInput.TargetTileMargin,
                    targetTilePos.Y + renderInput.TargetTileMargin);
                sourceData.DrawOnto(target.PixelData,
                                    BlendMode.Solid,
                                    targetContentPos.X,
                                    targetContentPos.Y,
                                    renderInput.SourceTileSize.X,
                                    renderInput.SourceTileSize.Y,
                                    sourceTilePos.X,
                                    sourceTilePos.Y);

                // Fill up the target spacing area with similar pixels
                if (renderInput.TargetTileMargin > 0)
                {
                    FillTileSpacing(target.PixelData, renderInput.TargetTileMargin, targetContentPos, renderInput.SourceTileSize);
                }

                // Update whether the tile is considered visually empty
                if (tileData.Data[tileIndex].IsVisuallyEmpty)
                {
                    bool isLayerVisuallyEmpty = IsCompletelyTransparent(
                        sourceData,
                        sourceTilePos,
                        renderInput.SourceTileSize);
                    if (!isLayerVisuallyEmpty)
                    {
                        tileData.Data[tileIndex].IsVisuallyEmpty = false;
                    }
                }

                // Add an entry to the generated atlas
                Rect atlasRect = new Rect(
                    targetTilePos.X + renderInput.TargetTileMargin,
                    targetTilePos.Y + renderInput.TargetTileMargin,
                    geometry.TargetTileAdvance.X - renderInput.TargetTileMargin * 2,
                    geometry.TargetTileAdvance.Y - renderInput.TargetTileMargin * 2);
                target.Atlas.Add(atlasRect);

                // Advance the target tile position
                targetTilePos.X += geometry.TargetTileAdvance.X;
                if (targetTilePos.X + geometry.TargetTileAdvance.X > target.PixelData.Width)
                {
                    targetTilePos.X  = 0;
                    targetTilePos.Y += geometry.TargetTileAdvance.Y;
                }
            }

            return(target);
        }