nes-emu
Version:
A NES emulator
111 lines (100 loc) • 3.73 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = renderSprites;
var _constants = _interopRequireDefault(require("../../constants"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/** Renders the sprites from OAM. */
function renderSprites(context) {
const sprites = evaluateSprites(context);
const buffer = drawSpritesIntoBuffer(context, sprites);
drawSprites(context, buffer);
}
/** Evaluates which sprites should be rendered in the current scanline. */
const evaluateSprites = _ref => {
let {
ppu
} = _ref;
const sprites = [];
for (let spriteId = 0; spriteId < _constants.default.MAX_SPRITES; spriteId++) {
const sprite = ppu.oam.createSprite(spriteId);
if (sprite.shouldRenderInScanline(ppu.scanline) && sprites.length < _constants.default.MAX_SPRITES_PER_SCANLINE + 1) {
if (sprites.length < _constants.default.MAX_SPRITES_PER_SCANLINE) {
sprites.push(sprite);
} else {
ppu.registers.ppuStatus.spriteOverflow = 1;
break;
}
}
}
// (sprites on lower addresses have greater priority)
return sprites.reverse();
};
/** Draws a list of `sprites` into a buffer. */
const drawSpritesIntoBuffer = (context, sprites) => {
const {
ppu
} = context;
const {
ppuMask,
ppuStatus
} = ppu.registers;
const finalY = ppu.scanline;
const buffer = {
colors: [],
priorities: [],
xs: []
};
for (let sprite of sprites) {
const insideY = sprite.diffY(finalY);
for (let insideX = 0; insideX < _constants.default.TILE_LENGTH; insideX++) {
const finalX = sprite.x + insideX;
if (!ppuMask.showSpritesInLeftmost8PixelsOfScreen && finalX < 8) continue;
// color fetch
const paletteId = sprite.paletteId;
const paletteIndex = getSpritePixelPaletteIndex(context, sprite, insideX, insideY);
const isSpritePixelOpaque = paletteIndex !== _constants.default.COLOR_TRANSPARENT;
const isBackgroundPixelOpaque = ppu.paletteIndexOf(finalX, finalY) !== _constants.default.COLOR_TRANSPARENT;
// sprite 0 hit
if (sprite.id === 0 && isSpritePixelOpaque && isBackgroundPixelOpaque && ppuMask.showBackground && ppuMask.showSprites) ppuStatus.sprite0Hit = 1;
// add to drawing buffer
if (isSpritePixelOpaque) {
const color = ppu.framePalette.getColorOf(paletteId, paletteIndex);
buffer.colors[finalX] = color;
buffer.priorities[finalX] = sprite.isInFrontOfBackground;
buffer.xs[finalX] = finalX;
}
}
}
return buffer;
};
/** Draws a `buffer` using the PPU, in the right layer. */
const drawSprites = (_ref2, _ref3) => {
let {
ppu
} = _ref2;
let {
colors,
priorities,
xs
} = _ref3;
const finalY = ppu.scanline;
for (let finalX of xs) {
const isInFrontOfBackground = priorities[finalX];
const color = colors[finalX];
// sprite/background priority
const isBackgroundPixelOpaque = ppu.paletteIndexOf(finalX, finalY) !== _constants.default.COLOR_TRANSPARENT;
const shouldDraw = isInFrontOfBackground || !isBackgroundPixelOpaque;
// actual drawing
if (shouldDraw) ppu.plot(finalX, finalY, color);
}
};
/** Returns the (`insideX`, `insideY`) `sprite`'s pixel palette index. */
const getSpritePixelPaletteIndex = (_ref4, sprite, insideX, insideY) => {
let {
ppu
} = _ref4;
const tileInsideY = insideY % _constants.default.TILE_LENGTH;
return ppu.patternTable.getPaletteIndexOf(sprite.patternTableId, sprite.tileIdFor(insideY), sprite.flipX ? _constants.default.TILE_LENGTH - 1 - insideX : insideX, sprite.flipY ? _constants.default.TILE_LENGTH - 1 - tileInsideY : tileInsideY);
};