Пример #1
0
        // Used for debug rendering BG's. Renders the source BG, does not scroll etc
        public void DebugRenderScanlineAffine(int scanline, int scanlineWidth, DirectBitmap drawBuffer)
        {
            Color[] palette        = gba.LcdController.Palettes.Palette0;
            bool    eightBitColour = CntRegister.PaletteMode == BgPaletteMode.PaletteMode256x1;

            // Which line within the current tile are we rendering?
            int tileRow = scanline % 8;

            for (int x = 0; x < scanlineWidth; x++)
            {
                // Coords (measured in tiles) of the tile we want to render
                int bgRow    = scanline / 8;
                int bgColumn = x / 8;

                // Which row / column within the tile we are rendering?
                int tileColumn = x % 8;

                // Affine BG's have one byte screen data (the tile index). Also all tiles are 8bpp
                // Affine BG's are also all square (they have their own size table which is different to regular tiled bg's)
                int tileInfoOffset = (bgRow * bgWidthInTiles) + bgColumn;

                int tileNumber = gba.Memory.VRam[(CntRegister.ScreenBlockBaseAddress * 2048) + tileInfoOffset];

                int tileVramOffset = (int)(tileDataVramOffset + (tileNumber * tileSize));

                // Sometimes Bg's can be set up with invalid data which won't be drawn
                if (tileVramOffset >= gba.Memory.VRam.Length)
                {
                    continue;
                }

                int paletteIndex = TileHelpers.GetTilePixel(tileColumn, tileRow, true, gba.Memory.VRam, tileVramOffset, false, false);

                // Pal 0 == Transparent
                if (paletteIndex == 0)
                {
                    continue;
                }

                drawBuffer.SetPixel(x, scanline, palette[paletteIndex]);
            }
        }
Пример #2
0
        // Used for debug rendering BG's. Renders the source BG, does not scroll etc
        public void RenderScanlineAffine(int scanline, int scanlineWidth, DirectBitmap drawBuffer)
        {
            Color[] palette       = gba.LcdController.Palettes.Palette0;
            int     paletteOffset = 0;

            bool eightBitColour = true;

            // Which line within the current tile are we rendering?
            int tileRow = scanline % 8;

            for (int x = 0; x < scanlineWidth; x++)
            {
                // Which column within the current tile are we rendering?
                int tileColumn = x % 8;

                var tileMetaData = TileMap.TileMapItemFromBgXY(x, scanline);

                // If we are in 4 bpp mode the tilemap contains which 16 colour palette to use. 16 entries per palette
                if (eightBitColour == false)
                {
                    paletteOffset = tileMetaData.Palette * 16;
                }

                int tileSize = (eightBitColour ? LcdController.Tile_Size_8bit : LcdController.Tile_Size_4bit);

                // 4 bytes represent one row of pixel data for a single tile
                int tileVramOffset = (int)(tileDataVramOffset + ((tileMetaData.TileNumber) * tileSize));

                int paletteIndex = TileHelpers.GetTilePixel(tileColumn, tileRow, eightBitColour, gba.Memory.VRam, tileVramOffset, tileMetaData.FlipHorizontal, tileMetaData.FlipVertical);

                // Pal 0 == Transparent
                if (paletteIndex == 0)
                {
                    continue;
                }

                drawBuffer.SetPixel(x, scanline, palette[paletteOffset + paletteIndex]);
            }
        }
Пример #3
0
        bool RenderSpritePixel(int screenX, int screenY, int priority, bool windowing, int windowRegion, ref int bgVisibleOverride)
        {
            int paletteIndex;
        
            // If a sprite has the same priority as a bg, the sprite is drawn on top, therefore we check sprites first 
            foreach (var obj in priorityObjList[priority])
            {
                if (obj.Attributes.RotationAndScaling)
                {
                    // Clip against the bounding box which can be DoubleSize. This is the only time doublesize is actually checed
                    if (obj.BoundingBoxScreenSpace.ContainsPoint(screenX, screenY) == false)
                    {
                        continue;
                    }

                    int sourceWidth = obj.Attributes.Dimensions.Width;
                    int sourceHeight = obj.Attributes.Dimensions.Height;

                    // The game will have set up the matrix to be the inverse texture mapping matrix. I.E it maps from screen space to texture space. Just what we need!                    
                    OamAffineMatrix rotScaleMatrix = obj.Attributes.AffineMatrix();

                    // NB: Order of operations counts here!
                    // Transform with the origin set to the centre of the sprite (that's what the - width/height /2 below is for)
                    int originX = screenX - obj.Attributes.XPosition - (sourceWidth / 2);
                    int originY = screenY - obj.Attributes.YPosition - (sourceHeight / 2);
                    // Not well documented anywhere but when double size is enabled we render offset by half the original source width / height
                    if(obj.Attributes.DoubleSize)
                    {
                        originX -= sourceWidth / 2;
                        originY -= sourceHeight / 2;
                    }

                    int transformedX, transformedY;
                    rotScaleMatrix.Multiply(originX , originY, out transformedX, out transformedY);

                    // Transform back from centre of sprite
                    transformedX += (sourceWidth / 2);
                    transformedY += (sourceHeight / 2);

                    paletteIndex = obj.PixelValue(transformedX, transformedY);
                }
                else
                {
                    if (obj.BoundingBoxScreenSpace.ContainsPoint(screenX, screenY) == false)
                    {
                        continue;
                    }

                    //paletteIndex = obj.PixelScreenValue(x, scanline);
                    paletteIndex = obj.PixelValue(screenX - obj.Attributes.XPositionAdjusted(), screenY - obj.Attributes.YPositionAdjusted());
                }

                // Pal 0 == Transparent 
                if (paletteIndex == 0)
                {
                    continue;
                }


                // TODO: I *think* this will render the Obj window correctly but i cannot test it yet
                // This pixel belongs to a sprite in the Obj Window and Win 0 & 1 are not enclosing this pixel
                if (windowing &&
                    DisplayControlRegister.DisplayObjWin &&
                    obj.Attributes.Mode == ObjAttributes.ObjMode.ObjWindow &&
                    ((windowRegion & (int)TileHelpers.WindowRegion.WindowIn) == 0))
                {
                    bgVisibleOverride = Windows[(int)Window.WindowName.WindowObj].DisplayBg0 |
                                            Windows[(int)Window.WindowName.WindowObj].DisplayBg1 |
                                            Windows[(int)Window.WindowName.WindowObj].DisplayBg2 |
                                            Windows[(int)Window.WindowName.WindowObj].DisplayBg3;

                    return false;
                }

                drawBuffer.SetPixel(screenX, screenY, Palettes.Palette1[paletteIndex]);
                return true;
                   
            }                        
            return false;
        }
Пример #4
0
        // DENNIS: the way I did threaded rendering was by caching the LCD IO registers, and updating them for the PPU whenever it started rendering the next scanline

        private void RenderScanlineTextMode()
        {
            /*
            if(frameNumber % 30 != 0)
            {
                return;
            }
            */

            /*
            if (DisplayControlRegister.BgVisible(0)) Bg[0].CacheScanline();
            if (DisplayControlRegister.BgVisible(1)) Bg[1].CacheScanline();
            if (DisplayControlRegister.BgVisible(2)) Bg[2].CacheScanline();
            if (DisplayControlRegister.BgVisible(3)) Bg[3].CacheScanline();
            */

            ObjController.ObjPrioritySort();
            int scanline = CurrentScanline;
            bool windowing = (DisplayControlRegister.DisplayWin0 || DisplayControlRegister.DisplayWin1 || DisplayControlRegister.DisplayWin1 || DisplayControlRegister.DisplayObjWin);


            //Bg[0].WaitForScanline();
            //Bg[1].WaitForScanline();
            //Bg[2].WaitForScanline();
            //Bg[3].WaitForScanline();

            // We render front to back. Once a pixel is drawn we stop going through the layers.
            // TODO: In order to do blending we may need to go through all the layers for each pixel
#if ParallelizeScanline
            var paritioner = Partitioner.Create(0, LcdController.Screen_X_Resolution, 80);
            var result = Parallel.ForEach(paritioner, (range) =>
            {
            for (int x = range.Item1; x < range.Item2; x++)
#else
            for (int x = 0; x < Screen_X_Resolution; x++)
#endif
            {
                int paletteIndex;
                bool pixelDrawn = false;

                // Windowing can disable obj's and bg's
                bool objVisibleOverride = false;

                int windowRegion = 0;

                int bgVisibleOverride = 0; // bitmask for bg visible (from window)
                if (windowing)
                {
                    windowRegion = TileHelpers.PixelWindowRegion(x, CurrentScanline, gba);

                    // 0 is outside of all windows
                    if (windowRegion == 0)
                    {
                        objVisibleOverride = Windows[(int) Window.WindowName.WindowOut].DisplayObjs;

                        bgVisibleOverride = Windows[(int)Window.WindowName.WindowOut].DisplayBg0 |
                                            Windows[(int)Window.WindowName.WindowOut].DisplayBg1 |
                                            Windows[(int)Window.WindowName.WindowOut].DisplayBg2 |
                                            Windows[(int)Window.WindowName.WindowOut].DisplayBg3;
                    }
                    // Window 0 takes priority over window 1. We don't need to check if the point is within both areas as it is done in the function call above
                    else if((windowRegion & (int)TileHelpers.WindowRegion.Window0) != 0)
                    {
                        objVisibleOverride = Windows[(int)Window.WindowName.Window0].DisplayObjs;

                        bgVisibleOverride = Windows[(int)Window.WindowName.Window0].DisplayBg0 |
                                            Windows[(int)Window.WindowName.Window0].DisplayBg1 |
                                            Windows[(int)Window.WindowName.Window0].DisplayBg2 |
                                            Windows[(int)Window.WindowName.Window0].DisplayBg3;

                    }
                    else if ((windowRegion & (int)TileHelpers.WindowRegion.Window1) != 0)
                    {
                        objVisibleOverride = Windows[(int)Window.WindowName.Window1].DisplayObjs;

                        bgVisibleOverride = Windows[(int)Window.WindowName.Window1].DisplayBg0 |
                                            Windows[(int)Window.WindowName.Window1].DisplayBg1 |
                                            Windows[(int)Window.WindowName.Window1].DisplayBg2 |
                                            Windows[(int)Window.WindowName.Window1].DisplayBg3;
                    }


                }
                else
                {
                    objVisibleOverride = true;
                    bgVisibleOverride = 0;
                }

                // Start at the top priority, if something draws to the pixel, we can early out and stop processing this pixel 
                // Highest priority (0) is drawn at the front. Lower priorities can be obscured 
                for (int priority = 0; priority < 4; priority++)
                {
                    // Sprite rendering
                    // If a sprite has the same priority as a bg, the sprite is drawn on top, therefore we check sprites first 
                    if (DisplayControlRegister.DisplayObj &&
                        (!windowing || (windowing && objVisibleOverride)))
                    {
                        pixelDrawn = ObjController.RenderSpritePixel(drawBuffer, x, scanline, priority, windowing, windowRegion, ref bgVisibleOverride);
                        if (pixelDrawn)
                        {
                            break;
                        }                        
                    }

                    // No Sprite occupied this pixel, move on to backgrounds

                    // Bg Rendering
                    // Find the backgrounds with this priority                        
                    // In case of same priority, BG0 has highest (drawn at front)
                    for (int bgSelect = 0; bgSelect < 4; bgSelect++)
                    {                      
                        if (Bg[bgSelect].CntRegister.Priority != priority ||
                            DisplayControlRegister.BgVisible(Bg[bgSelect].BgNumber) == false ||
                            (windowing && ((bgVisibleOverride & (1 << bgSelect)) == 0)))
                        {
                            continue;
                        }

                        /*
                        if(Bg[2].AffineMode && bgSelect == 1)
                        {
                            continue;
                        }
                        */

                        
                        if (Bg[bgSelect].AffineMode)
                        {
                            paletteIndex = Bg[bgSelect].PixelValueAffine(x, scanline);
                        }
                        else
                        {
                            paletteIndex = Bg[bgSelect].PixelValue(x, scanline);
                        }
                        
                        // TODO: If we need to blend then get the pixels we need to blend here

                        /*
                        lock (Bg[bgSelect].ScanlineData)
                        {
                            paletteIndex = Bg[bgSelect].ScanlineData[x];
                        }
                        */

                        // Pal 0 == Transparent 
                        if (paletteIndex == 0)
                        {
                            continue;
                        }

                        drawBuffer.SetPixel(x, scanline, Palettes.Palette0[paletteIndex]);
                        pixelDrawn = true;

                        // Once a pixel has been drawn, no need to check other BG's
                        break;
                    }                                        
                    if(pixelDrawn)
                    {
                        break;
                    }
                }

                // If nothing is drawn then default to backdrop colour
                if (pixelDrawn == false)
                {
                    drawBuffer.SetPixel(x, scanline, Palettes.Palette0[0]);
                }
            }
#if ParallelizeScanline
            }); // Parallel.For
            //while (result.IsCompleted == false) ;
#endif
        }