示例#1
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;
            }
        }
示例#2
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;
        }
示例#3
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);
        }
示例#4
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);
        }
示例#5
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);
        }
示例#6
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);
        }
示例#7
0
 public LayerFeature(string type, LayerProperties properties, LayerGeometry geometry)
 {
     Type       = type;
     Properties = properties;
     Geometry   = geometry;
 }