3D scrolling: implementation
Terminology
A small dictionary used in a hundred
tye terms.
Textureortexture layer- a certain bit─
map, which is displayed on the screen as on─
clonal plane using perspe─
active-correct overlay algorithm
textures.
Texture byte- any byte, composition─
lying textures. Usually in these bytes it is used─
no more than 4-5 texture pixels are used
(or bits). This trick is necessary in order to
to limit use to a reasonable level
memory use by tables (see below).
For example, consider a texture layer from
New Wave demos(Fig. 8).On the left is how he is on
is actually stored in memory, on the right - with
subtracted constant. It can be noticed that
each character is 5 or less wide
pixels and is located in bits 4..0 each─
one texture byte.
Screen, screen byte- everything that relates
to the ZX screen 6912 bytes.
Mapping- display of texture on the screen─
wound
Horizontal, vertical table-the way tables are arranged in memory. If
in register H - the number (pointer) of the table, and
in L - index in the table, then such a table
Let's call it horizontal. Conversely, if L -
table number, and H is the index in it, then tab─
faces vertical.
Similarly, we can classify
buffers with graphic data: if for
shift right by 1 byte we do INC L, and
to shift down INC H or add con─
stantu to HL is a horizontal buffer.
If INC L shifts HL down a byte (or
up) and INC H - one byte to the right, then this
vertical buffer.
Key Ideas
The main idea is, in general, obvious -
Not every line of the screen has been redrawn─
each frame is displayed. In the upper part you can
see some noise and aliasing (like in the game
Doom, if you move away from the wall). There
usually all lines are redrawn every time
frame. From below, however, the situation is completely
another, and for scrolling by 1 texture peak─
it is necessary to carry out several phases
screen updates(Fig. 9). The whole process
scrolling consists of sequential rendering
each phase and shifting the texture buffer by
1 pixel after all phases have been completed─
not. Usually this shift is made simply
changing the pointer in the texture buffer.
Since at every step at the bottom
Not all lines of the screen are updated,
and at the topparts - stitches relative
narrow, usually it turns out to let the whole effect go
at 50 FPS without double buffering (i.e.
one screen!), managing to complete all the re─
drawing the beam forward. Moreover, an attempt
using two screens will obviously lead to
to the fact that at the bottom of the screens will double─
Xia number of lines that require updating!
The next most important idea is
in the mapping process itself: how exactly bytes
textures are mapped to screen bytes. Oche─
Apparently, this process occurs with the help
multiple tables - updating for each one─
That's how many screen bytes are required─
blitz, how many texture bytes are there?
give (in whole or in part), while co─
The relationship between screen bytes and tabs─
faces fixed. At the top of the screen
multiple texture bytes mapped to 1
screen byte, at the bottom - on the contrary, one
texture byte is mapped to multiple
screen(Fig. 10).
I generate all these various tables─
are formed during the preheating process, and usually
it turns out (or I’m trying to achieve this on purpose─
are known) that the number of different tables is not
exceeds 256. Texture bytes, like uka─
called above, usually have 4 or 5 values─
common bits, therefore, all mapping tables
conveniently placed vertically in
memory block of size 4 or
8 kilobytes. Index (loaded into old─
the biggest partregister pair) in such tables─
tsakh serves as a texture byte, to which
earlier (when drawing to the texture layer, on─
example), you can add an offset to the block
tables. The table number is loaded into young─
the largest part of a register pair.
So, all of the above is enough,
to understand the essence of the effect.
Implementation
I certainly won't clutter
this article is full of endless foot wraps
Z80-code effect, as well as pre─
rolling - I'll leave this as an exercise
for a reasonably interested reader.
However, below are the key frags─
Z80 code cops.
If there is too much memory (for example, you have
128 KB ZX), then you can just sge─
don't waste one big footcloth of code for each─
doy phase, which is each screen byte about─
works accordingly: load─
presses the necessary table numbers, walks along the text─
round, combines values from tables, etc.
The total volume of such code (for all phases)
turns out to be in the region of several dozen
kilobyte
Let the texture buffer be horizontal, we
we read it using POP , and on the screen
indicates DE . Then the code might look like
as follows:
;Step to next texture line
LD HL,const
ADDHL,SP
RES 4,H; we do not allow going beyond
;buffer limits
LD SP,HL
;one byte of texture is distributed over
;a few screen bytes
POP BC; take 2 texture bytes at once
LD L,table_number1
LD H,C;texture byte - index
;in the table
LDI;from the table directly to the screen
LD L,table_number2
LDI
LD H,B;next texture byte
;several texture bytes end up in
;one screen
POP BC
LD L,table_numberЗ
LD H,C
LD A,(HL);piece - from one table
LD L,table_number
LD H,B
OR (HL);piece - from another
;and combine
LD (DE),A;result on screen
INC E;next screen byte
Most likely, this code is almost optimal.
However, if you set yourself the task
realize the same effect within
48 kilobytes, then the main problem will be
unacceptable size of this code. For radik─
I used the following code abbreviation:
cool trick suggested byAlone Coder.
Instead of generating in high heat
a whole sheet that is entirely rentable─
rits one phase, you can search naturally─
sti and similar pieces in the code - for example,
you can highlight a piece that promotes
texture pointer, piece that maps─
feeds several texture bytes into one ek─
early, etc.
There are several such pieces in total
kilobyte To control the sequence─
the task of summoning such pieces and supplying them
data (for example, table numbers) using─
It's obviously a stack. Move to next
piece -RET , reading the necessary data -
POP .
The following is an example from48K New Wave demos.
In this exampleHL points to the vertical─
texture buffer,BC to the screen.
POP BC;loading a new position
;on screen
LD A,L
POP HL
ADD A,L
LD L,A; skip 1 or more lines L
;in the texture buffer
;(it has 256 lines in total)
;at the same time we will load a new one
;horizontal position of H in it
RET; let's go to the next piece
POP DE;take the table number in E
LD D,(HL); substitute the index in this
;table from texture buffer
LD A,(DE); lookup in this table
LD (BC),A;and to the screen
INC C;next byte on screen
INC H;track. bytes in texture buffer
RET;the next piece of code
POP DE; everything is the same, but we combine
LD D,(HL);in one screen byte
INC H ;several textured,
LD A,(DE);passed through several
POP DE ;different tables
LD D,(HL)
INC H
EX DE,HL
OR (HL)
EX DE,HL
LD (BC),A
INC C
RET
Such code, of course, is not so fast and
so elegant (for exampleEX DE,HL twice
- this is awesome!), but it fits tolerably─
It works and works in 48K.
Precalk
Precalc has already been mentioned several times
and not in vain: because this is exactly whatturns
some random Z80 code with lookups via
incomprehensible tables in a working visual
effect.
This procedure is quite expensive
by Z80 standards: presumably it lasted
would be many tens of seconds (if not minutes)
on ZX. My precalc was written and working
on pc.
A rough outline of what happens in
preheating:
We set the initial data: slope
texture plane, position, width,
number of phases, etc.
Using raycasting we determine the map─
ping texture pixels to screen pixels. Immediately
you can check, for example, that visible tech─
line lines less than 256 minus height
font - so as not to see the printing process
new characters are somewhere far away on the screen.
We combine pixels into bytes and compose
mapping tables, throw out duplicate ones.
We check that there are no more than 256 tables in total.
Finally, we generate the phase output code, or
(for the case48K) code blocks and control
table.
Conclusion
I believe that all of the above in─
formation is quite enough to
reproduce the described effect. Also who─
The following improvements are possible:
- the textured surface can be ox─
low, curved vertically and horizon─
whether. Curvature may lead to the need─
the ability to remove the invisible parts of this plot─
rxness.
- all precalculation can be done on the Z80,for example, cramming the entire effect into 1 kB.
- you can color each line of text
in your color.
- well, and everything else that wanders into
head :)
Finally, thanks:
-Alone Coderfor the trick with code fragments
and control using data on the stack.
Well, and for the constant work on the Info Guide.
-Bolek,once againAlone CoderandEllvisfor
valuable historical information regarding poho─
live 3D scrolls.
-Stein(to the author of star wars scroll in C64
demos Trick and Treat and RGB C64 demo) - for
inspiration.
Author :lvd^mhm, lvd.mhm@gmail.com
Share your thoughts about the article