예제 #1
0
        // ===================================================================================================
        //                                      Affine objects
        // ===================================================================================================

        private ushort GetAffineOBJPixel(uint TileID, OBJSize OBJsz,
                                         byte px, byte py, bool ColorMode, byte PaletteBank)
        {
            /*
             * Though this is really similar to regular objects, we cannot just do it in a straight line, so we have to calculate the address
             * over and over. For regular sprites / backgrounds it is faster to just do it all in a row
             */
            uint PixelAddress = 0x10000;   // OBJ vram starts at 0x10000 within VRAM

            // base address is the same for 4bpp and 8bpp sprites
            // Tonc about Sprite tile memory offsets: Always per 4bpp tile size: start = base + id * 32
            PixelAddress += (uint)(TileID * 0x20);
            // removed shifting for less arithmetic, like in regular objects
            PixelAddress += (uint)(this.OAM2DMap ? (OBJsz.Width * (py >> 3) * 4) : (32 * 0x20 * (py >> 3)));

            if (!ColorMode)     // 4bpp
            {
                PixelAddress += (uint)(4 * (py & 0x07));
                PixelAddress += (uint)(0x20 * (px >> 3));

                byte PaletteNibble = this.gba.mem.VRAM[PixelAddress + ((px & 0x07) >> 1)];
                if ((px & 1) == 1)
                {
                    PaletteNibble >>= 4;
                }

                PaletteNibble &= 0x0f;
                if (PaletteNibble == 0)
                {
                    return(Transparent);
                }

                return(this.GetPaletteEntry(0x200 + (uint)PaletteBank * 0x20 + (uint)(2 * PaletteNibble)));
            }
            else                // 8bpp
            {
                PixelAddress += (uint)(8 * (py & 0x07));
                PixelAddress += (uint)(0x40 * (px >> 3));

                byte VRAMEntry = this.gba.mem.VRAM[PixelAddress + (px & 0x07)];
                if (VRAMEntry == 0)
                {
                    return(Transparent);
                }

                return(this.GetPaletteEntry(0x200 + 2 * (uint)VRAMEntry));
            }
        }
예제 #2
0
        private void RenderAffineOBJ(short OBJy, OBJSize OBJsz, bool ColorMode, bool Mosaic,
                                     ushort OBJ_ATTR1, ushort OBJ_ATTR2, bool DoubleRendering, bool EnableBlending, bool UseOBJWindowMask = false)
        {
            int StartX = OBJ_ATTR1 & 0x01ff;

            if ((OBJ_ATTR1 & 0x0100) > 0)
            {
                StartX = (int)(StartX | 0xffff_ff00);                            // sign extend
            }
            ushort TileID      = (ushort)(OBJ_ATTR2 & 0x03ff);
            byte   Priority    = (byte)((OBJ_ATTR2 & 0x0c00) >> 10);
            byte   PaletteBank = (byte)((OBJ_ATTR2 & 0xf000) >> 12);

            byte   AffineIndex   = (byte)((OBJ_ATTR1 & 0x3e00) >> 9);
            ushort RotScaleIndex = (ushort)(32 * AffineIndex + 6);

            // PA, PB, PC, PD:
            short[] RotateScaleParams = new short[4];
            for (int di = 0; di < 4; di++, RotScaleIndex += 8)
            {
                RotateScaleParams[di] = (short)(this.gba.mem.OAM[RotScaleIndex] | (this.gba.mem.OAM[RotScaleIndex + 1] << 8));
            }

            uint px, py;
            uint px0 = (uint)(OBJsz.Width >> 1);
            uint py0 = (uint)(OBJsz.Height >> 1);

            // distance with the midpoint of the sprite
            short dy, dx;

            if (DoubleRendering)
            {
                dy = (short)(scanline - OBJy - OBJsz.Height);
                dx = (short)(-OBJsz.Width);
            }
            else
            {
                dy = (short)(scanline - OBJy - (OBJsz.Height >> 1));
                dx = (short)-(OBJsz.Width >> 1);
            }

            // What the object width is to be interpreted as for looping over x coordinates
            byte FictionalOBJWidth = (byte)(DoubleRendering ? 2 * OBJsz.Width : OBJsz.Width);

            for (int ix = 0; ix < FictionalOBJWidth; ix++, dx++)
            {
                if ((StartX + ix < 0))
                {
                    continue;
                }
                else if ((StartX + ix) >= width)
                {
                    break;
                }

                if (this.OBJLayers[Priority][StartX + ix] != Transparent)
                {
                    continue;
                }

                if (!OBJWindow[StartX + ix])
                {
                    continue;
                }

                // transform
                px = (uint)(((RotateScaleParams[0] * dx + RotateScaleParams[1] * dy) >> 8) + px0);
                py = (uint)(((RotateScaleParams[2] * dx + RotateScaleParams[3] * dy) >> 8) + py0);

                // use actual width of sprite, even for double rendering
                if (px >= OBJsz.Width || py >= OBJsz.Height)
                {
                    continue;
                }

                if (UseOBJWindowMask)
                {
                    this.OBJWindowMask      [StartX + ix] = this.GetAffineOBJPixel(TileID, OBJsz, (byte)px, (byte)py, ColorMode, PaletteBank);
                }
                else
                {
                    this.OBJLayers[Priority][StartX + ix] = this.GetAffineOBJPixel(TileID, OBJsz, (byte)px, (byte)py, ColorMode, PaletteBank);

                    // update sprite blending mode override
                    // comparison operators are always false comparing to null
                    if (!(Priority >= OBJMaxPriority[StartX + ix]))
                    {
                        if (this.OBJLayers[Priority][StartX + ix] != Transparent)
                        {
                            this.OBJMaxPriority [StartX + ix] = Priority;
                            this.OBJBlendingMask[StartX + ix] = EnableBlending;
                        }
                    }
                }
            }
        }
예제 #3
0
        private void RenderRegularOBJ(short OBJy, OBJSize OBJsz, bool ColorMode, bool Mosaic,
                                      ushort OBJ_ATTR1, ushort OBJ_ATTR2, bool EnableBlending, bool UseOBJWindowMask = false)
        {
            int StartX = OBJ_ATTR1 & 0x01ff;

            if ((OBJ_ATTR1 & 0x0100) > 0)
            {
                StartX = (int)(StartX | 0xffff_ff00);                            // sign extend
            }
            int  XSign = 1;
            bool VFlip = (OBJ_ATTR1 & 0x2000) > 0;
            bool HFlip = (OBJ_ATTR1 & 0x1000) > 0;

            ushort TileID      = (ushort)(OBJ_ATTR2 & 0x03ff);
            byte   Priority    = (byte)((OBJ_ATTR2 & 0x0c00) >> 10);
            byte   PaletteBank = (byte)((OBJ_ATTR2 & 0xf000) >> 12);

            byte dy = (byte)(scanline - OBJy);   // between 0 and OBJsz.Height (8, 16, 32, 64)

            if (Mosaic)
            {
                dy -= (byte)(dy % this.IO.MOSAIC.OBJMosaicVStretch);
            }

            if (VFlip)
            {
                dy = (byte)(OBJsz.Height - dy - 1);
            }

            uint SliverBaseAddress;  // base address for horizontal sprite sliver

            if (HFlip)
            {
                XSign = -1;
                // tiles are also in a different order when we flip horizontally
                StartX += OBJsz.Width - 1;
            }

            if (!ColorMode)     // ========================= 4bpp =============================
            {
                SliverBaseAddress = (uint)(TileID * 0x20);
                // removed shifting for less arithmetic, logically OBJsz.Width should be OBJsz.Width >> 3 for the width in tiles, and
                // 4 should be 0x20. This way we wrap around with the number of tiles, but since OBJsz.Width is a power of 2,
                // this is ever so slightly faster, at least I think.
                SliverBaseAddress += (uint)(this.OAM2DMap ? (OBJsz.Width * (dy >> 3) * 4) : (32 * 0x20 * (dy >> 3)));
                SliverBaseAddress += (uint)(4 * (dy & 0x07));   // offset within tile

                // prevent overflow, not sure what is supposed to happen
                if (SliverBaseAddress + ((OBJsz.Width >> 3) - 1) * 0x20 > 0x8000)
                {
                    SliverBaseAddress = 0;
                }

                // base address for sprites is 0x10000 in OAM
                SliverBaseAddress = (SliverBaseAddress & 0x7fff) | 0x10000;

                for (int dTileX = 0; dTileX < (OBJsz.Width >> 3); dTileX++, StartX += 8 * XSign)
                {
                    // foreground palette starts at 0x0500_0200
                    // we can use our same rendering method as for background, as we simply render a tile

                    if (UseOBJWindowMask)
                    {
                        this.Render4bpp(
                            ref this.OBJWindowMask,
                            null,
                            StartX,
                            XSign,
                            (uint)(SliverBaseAddress + (0x20 * dTileX)),
                            (uint)(0x200 + PaletteBank * 0x20),
                            Mosaic,
                            this.IO.MOSAIC.OBJMosaicHStretch);
                    }
                    else
                    {
                        this.Render4bpp(
                            ref this.OBJLayers[Priority],
                            this.OBJWindow,
                            StartX,
                            XSign,
                            (uint)(SliverBaseAddress + (0x20 * dTileX)),
                            (uint)(0x200 + PaletteBank * 0x20),
                            Mosaic,
                            this.IO.MOSAIC.OBJMosaicHStretch);

                        // update sprite blending mode override
                        this.UpdateOBJMask(StartX, Priority, EnableBlending);
                    }
                }
            }
            else                // ========================= 8bpp =============================
            {
                // Tonc about Sprite tile memory offsets: Always per 4bpp tile size: start = base + id * 32
                SliverBaseAddress = (uint)(TileID * 0x20);
                // removed shifting for less arithmetic, like in 4bpp
                SliverBaseAddress += (uint)(this.OAM2DMap ? (OBJsz.Width * (dy >> 3) * 8) : (32 * 0x20 * (dy >> 3)));
                SliverBaseAddress += (uint)(8 * (dy & 0x07));   // offset within tile

                // prevent overflow, not sure what is supposed to happen
                if (SliverBaseAddress + (OBJsz.Width >> 3) * 0x20 > Transparent)
                {
                    SliverBaseAddress = 0;
                }

                SliverBaseAddress = (SliverBaseAddress & 0x7fff) | 0x10000;

                for (int dTileX = 0; dTileX < (OBJsz.Width >> 3); dTileX++, StartX += 8 * XSign)
                {
                    // we can use our same rendering method as for background, as we simply render a tile
                    if (UseOBJWindowMask)
                    {
                        this.Render8bpp(
                            ref this.OBJWindowMask,
                            null,
                            StartX,
                            XSign,
                            (uint)(SliverBaseAddress + (0x40 * dTileX)),
                            Mosaic,
                            this.IO.MOSAIC.OBJMosaicHStretch,
                            PaletteOffset: 0x200
                            );
                    }
                    else
                    {
                        this.Render8bpp(
                            ref this.OBJLayers[Priority],
                            this.OBJWindow,
                            StartX,
                            XSign,
                            (uint)(SliverBaseAddress + (0x40 * dTileX)),
                            Mosaic,
                            this.IO.MOSAIC.OBJMosaicHStretch,
                            PaletteOffset: 0x200
                            );

                        // update sprite blending mode override
                        this.UpdateOBJMask(StartX, Priority, EnableBlending);
                    }
                }
            }
        }