/// <summary> /// Reconstructs the sprite info table from OAM. /// </summary> public void ReconstructOAMTable() { for (int i = 0; i < SpriteInfoTable.Length; i++) { int off = i << 2; SpriteInfoTable[i].OAMIndex = off; SpriteInfoTable[i].YOffset = OAM[off + 0] - 16; SpriteInfoTable[i].XOffset = OAM[off + 1] - 8; SpriteInfoTable[i].TileOffset = OAM[off + 2] << 4;//index * 16 = offset in VRAM of tile SpriteInfoTable[i].SpriteProperties = OAM[off + 3]; // Sprites are sorted by their X position. In the case that their Xs are equal, // the one with the lower OAM position takes priority. Because they are already // 'sorted by OAM offset', only Xs need to be compared. int x = i; while (x > 0 && (SpriteInfoTable[x].XOffset < SpriteInfoTable[x - 1].XOffset)) { SpriteInfo temp = SpriteInfoTable[x - 1]; SpriteInfoTable[x - 1] = SpriteInfoTable[x]; SpriteInfoTable[x] = temp; x--; } } for (int i = 0; i < SpritesOnScanline.Length; i++) { SpritesOnScanline[i] = new SpriteInfo() { OAMIndex = int.MaxValue, XOffset = int.MaxValue, YOffset = int.MaxValue }; } }
/// <summary> /// Draws the sprites on the current scanline. /// </summary> private void DrawSpriteScanline() { int LineSpriteCount = 0; for (int i = 0; i < SpriteInfoTable.Length; i++) { SpriteInfo currentSprite = SpriteInfoTable[i]; // Only draw if the sprite is on the scanline if (LY >= currentSprite.YOffset && LY < currentSprite.YOffset + SpriteHeight) { LineSpriteCount++; if (!currentSprite.IsOnScreen) { continue; } int SpritePixelY = LY - currentSprite.YOffset; int SpriteTileOffset = 0; if (Sprite8By16Mode) { if (SpritePixelY < 8) { SpriteTileOffset = currentSprite.UpperTileOffset; } else { SpritePixelY -= 8; SpriteTileOffset = currentSprite.LowerTileOffset; } } else { SpriteTileOffset = currentSprite.TileOffset; } for (int LCD_X = currentSprite.XOffset, SpritePixelX = 0; LCD_X < currentSprite.XOffset + 8; LCD_X++, SpritePixelX++) { if (LCD_X >= LCDWidth) { break; } int SpriteColorNum = GetPixelPaletteNumberFromTile(SpriteTileOffset, SpritePixelX, SpritePixelY, currentSprite.XFlip, currentSprite.YFlip); // Color 0 is never drawn (transparent). // If PriorityOverBG, sprite pixel is drawn over BG, except in the case of sprite color 0. // If !PriorityOverBG, sprite pixel isn't drawn over BG, except when BG color is 0. if (SpriteColorNum == 0) { continue; } bool PriorityOverExistingSprite = (currentSprite.XOffset < SpritesOnScanline[LCD_X].XOffset) || (currentSprite.OAMIndex < SpritesOnScanline[LCD_X].OAMIndex); if (PriorityOverExistingSprite && (currentSprite.PriorityOverBG || BackgroundColorNumOnScanline[LCD_X] == 0)) { SetPixel(LCD_X, LY, DMGObjectPalettes[currentSprite.DMGObjectPaletteNum][SpriteColorNum]); SpritesOnScanline[LCD_X] = currentSprite; } } } if (LineSpriteCount >= 10) { break; } } }