Sprites that scale, and a depth buffer to hide them behind
A target size in PIXELS rather than a multiplier, which is the whole of why this is usable here. A billboard at distance d wants to be k/d pixels tall, and that is a number a program has anyway - out of a lookup table, most likely. A multiplier would have to be a fixed point fraction arrived at by dividing, and this CPU cannot divide. Zero on an axis means the natural size, so every sprite written before scaling existed still means what it meant. The two axes are independent, and that shape - one tile wide at its own size, stretched to whatever height a distance says - is a wall column in a pseudo-3D game. Measured: a DDA step costs 85 cycles, so 80 columns of ray casting is about 85,000 cycles, or 12fps. Drawing those walls from the CPU instead would be 256,000 writes, fifteen frames of cycles for one frame of screen. The device doing the pixels is what makes such a game possible at all here, not merely faster. And a depth buffer, one byte a screen column at 0xD000, written by the program. A sprite with a depth draws only in the columns it is in front of. PER COLUMN, and that is the point: a billboard is nearer than the wall at one end of itself and further at the other, and no ordering of the table can say that. Table order settles sprites against each other; the buffer settles them against the scenery. Zero means no test at both ends, so a program that never writes it behaves as it did before it existed. The entry grew from 8 bytes to 16 - now, while two programs use the table, rather than once a game is written on it. Bytes 0 to 7 kept their meanings, so Sprite.asm needed no change. The pass is rewritten to walk where a sprite is GOING rather than where it came from, which is what makes a stretch and a squash one operation. It also made flipping fall out: turning the source coordinate round mirrors the tile order and the pixels inside each tile in one step, where drawing tile by tile had to be told to do both. All 111 checks passed unchanged at natural size, which is what says the rewrite changed nothing it should not. Clipping moved out of the inner loop and had to: a target size is sixteen bits, so a sprite asked to be 60,000 pixels tall would have been sixty thousand turns of a loop that drew eight rows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
f8c3db5d56
commit
cb898450b5
@@ -1210,6 +1210,10 @@ from page 0, which is, and a tile left in another page is invisible unless a map
|
||||
that page - and the map is given back or cleared. What a program puts in page 1 is its own
|
||||
and nobody is looking at it afterwards.
|
||||
|
||||
**The depth buffer is not touched at all.** It only matters to a sprite that has a depth, and
|
||||
the table is cleared, so there are none - a program that wants one writes it, and a program
|
||||
that leaves one behind has left something nobody is asking about.
|
||||
|
||||
**The sprite table is cleared at exit, not saved and restored.** It lives in the atlas at
|
||||
0xC000, and the pages the screen save walks run to the end of the map and pick up again at the
|
||||
palette, with the sprites in the gap between. That is deliberate: nothing the shell draws is a
|
||||
|
||||
@@ -5170,10 +5170,10 @@ screenSane:
|
||||
RSTA
|
||||
OUTA 0xE5
|
||||
OUTA 0xE2 ; Fill takes the byte it writes from SourceLow.
|
||||
INIA 0x08
|
||||
INIA 0x10
|
||||
OUTA 0xE6
|
||||
RSTA
|
||||
OUTA 0xE7 ; 0x0800, which is 256 entries of eight bytes.
|
||||
OUTA 0xE7 ; 0x1000, which is 256 entries of sixteen bytes.
|
||||
INIA 0x02
|
||||
OUTA 0xE8
|
||||
RET
|
||||
|
||||
Reference in New Issue
Block a user