Created
May 8, 2018 17:07
-
-
Save svnt/64b2d8e5eee84e9494523a18380a14d5 to your computer and use it in GitHub Desktop.
Water FX, tutorial and C64 ASM code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Water | |
--------------------------------------------------------------------------- | |
Introduction | |
Water is a really nice effect, one of the better tricks around. Its also | |
pretty simple to code, should take no more than an afternoons work to get a | |
good water routine. | |
--------------------------------------------------------------------------- | |
Basics | |
First thing you'll need is 2 buffers, for the water. This needs to be an | |
array of ints, same size as destination buffer. Arrange these in a | |
2-element array, to ease the flipping. Clear these to zero, and you're | |
ready to start. | |
--------------------------------------------------------------------------- | |
Calculate The New Water | |
Calculating the new water is pretty simple. You'll need a loop that execute | |
from 1 to height-1, then 1 to width - 1. At each element for the water, | |
you'll need to sum the North, South, East, West points from the current | |
water, and divide by 2 Not 4, 2. Then subtract the new water for [y][x] | |
from this. This is your basic smooth function. Now you need to add the | |
'harmonic motion' to it. Take the new-water[y][x], shift it right x places | |
(x could be 4) and subtract it from the new-water[y][x]. Pseudo-code for | |
this might look like: | |
for y := 1 to height - 1 | |
for x := 1 to width - 1 | |
new-water[y][x] = ((old-water[y-1][x] + | |
old-water[y+1][x] + | |
old-water[y][x-1] + | |
old-water[y][x+1]) / 2) - | |
new-water[y][x]) | |
new-water[y][x] -= new-water[y][x] shr x | |
end | |
end | |
Not too hard to make into working code. Because there are 2 pages, and we | |
are flipping between them, the references to what seem like uncalculated | |
water ( - new-water[y][x]) are ok, because they come around and feed back. | |
This is all it takes to calculate the water! You may also want a part where | |
you set the 1 pixel wide frame around the water to 0, just to be safe. | |
--------------------------------------------------------------------------- | |
Paint The Water | |
Water is also pretty straightforward to paint. In fact some of the | |
techniques here, you will see later on in the bump-mapping. Back to the | |
task in hand. We will again need a loop for every pixel on the screen. What | |
we need to do is calculate a kind-of normal at each pixel. This is simply: | |
offsetx = water[y][x] - water[y+1][x] | |
offsety = water[y][x] - water[y][x+1] | |
Which measures changes in X and Y. 'Colour' is then calculated by 128 - | |
offsetx. Clip colour to the 0..255 range. Divide both offsetx and offsety | |
by 8, and add them to x and y. Now for the tricky bit. You'll need a | |
background image (or perhaps you can do without ...). You need to light the | |
background image by the water. This is done by: | |
offsetx /= 3; | |
offsety /= 8; | |
indexu = offsetx + x; | |
indexv = offsety + y; | |
MulTable[backdrop[indexv*256+indexu]*256+colour]; | |
MulTable is another handy lookup table. It simply takes the value of | |
(row*col) >> 8. | |
Pseudo code for this would be : | |
For y := 1 to height - 1 | |
For x := 1 to width - 1 | |
offsetx = water[y][x] - water[y+1][x] | |
offsety = water[y][x] - water[y][x+1] | |
colour = 128 - offsetx | |
trim colour to 0..255 | |
divide offsetx and offsety by 8 | |
add offsetx to x giving indexu | |
add offsety to y giving indexv | |
Plot (backdrop*colour) >> 8, lookup in table | |
End | |
End | |
What we are effectively doing here is applying fake lighting to the water, | |
then mixing the colours. There are plenty of variations on calculating the | |
normals. Plenty of room for exploration there. | |
--------------------------------------------------------------------------- | |
The Water Loop | |
Note it makes a difference what order you do calculations in. Its pretty | |
simple though. You need to: | |
1. Draw to the water | |
2. Paint it | |
3. Calculate new water | |
4. Page flip the water | |
If you stick to that, you can't go wrong. If you really want to be smart, | |
you'll use the texturemap lighting info on this page to do make logos and | |
so on ripple. I've even seen it combined with bump-mapping. Water is a very | |
rewarding effect, well worth coding. | |
Tom Hammersley,tomh@globalnet.co.uk |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;--------------------------------------------- | |
;------------------------------------------------ | |
;-------------------------------------------------- | |
; "lame 2D water" | |
; | |
; | |
; (c) by tecM0/Plush/+H | |
; | |
; 02.09.2003 02:18 faster version buffer=screen | |
; 25.07.2003 03:41 enuff for today. | |
; 16.04.2006 18:00 get rid of the damn copy routine | |
;-------------------------------------------------- | |
!to "water.prg" | |
;memmap-------------------------------------------- | |
; | |
;$ 0123456789abcdef | |
; | |
;0 ................ | |
;1 c............... code | |
;2 ................ | |
;3 ................ | |
;4v1111222233334444 4 "screens" 0/4-screenbuffer | |
;5vffffffff........ 1 font 8/c-waterbuffer | |
;6v................ | |
;7v................ | |
;8 ................ | |
;9 ................ | |
;a ................ | |
;b ................ | |
;c ................ | |
;d ................ | |
;e ................ | |
;f ................ | |
; | |
; v vicbank | |
; | |
; | |
;0page--------------------------------------------- | |
arg = $6a ;floating point acc2 & rnd seed | |
seed = $8b ;used by rnd#generator 32 bit | |
;not complete! | |
;-------------------------------------------------- | |
*=$0800 ;basicstart | |
!byte $00,$0b,$08,$00,$00,$9e | |
!convtab pet | |
!text "4096" | |
;*=$1000 | |
;!bin "../../../#incoming/the fourth .prg",,2 | |
;*=$5000 | |
;!bin "../../#/data/bumpfont4x4mul.bin",,2 | |
*= $1000 | |
jsr rndinit ;initalize radom number genarator | |
lda #$0a ;set vicbase to $4000 | |
sta $dd00 | |
lda #$09 | |
sta $d020 | |
sta $d021 | |
sta 646 | |
jsr $e544 | |
lda #0 | |
sta $d020 | |
lda #0 | |
sta $d021 | |
lda #$06 | |
sta $d022 | |
lda #$0e | |
sta $d023 | |
;lda $d011 | |
;ora #%00000000 | |
;sta $d011 | |
lda #%00011000 ;set multicolormode | |
sta $d016 | |
lda #%00010101 ;set font to $5000 | |
sta $d018 | |
;---------- | |
ldx #0 ;clear all... | |
cccc | |
lda #$00 | |
sta $5000,x | |
sta $5100,x | |
sta $5200,x | |
sta $5300,x | |
sta $5400,x | |
sta $5500,x | |
sta $5600,x | |
sta $5700,x | |
sta $4000,x | |
sta $4100,x | |
sta $4200,x | |
sta $4300,x | |
sta $4400,x | |
sta $4500,x | |
sta $4600,x | |
sta $4700,x | |
txa | |
sta $4400,x | |
dex | |
bne cccc | |
;---------- | |
jsr createfont ;dither a font | |
;---------- | |
lda #$ef ;wait for spacekey | |
cmp $dc01 | |
bne *-3 | |
;---------- | |
;--------------------------------------------------eof | |
;......... | |
;...***... | |
;...***... | |
;...***... | |
;.*******. | |
;..*****.. | |
;...***... | |
;....*.... | |
;......... | |
;-------------------------------------------------- | |
;----- paragraph @the bull starts here....init irq@ ----- | |
;-------------------------------------------------- | |
bull | |
lda #$00 ;clear all 4 screens/buffers | |
ldx #$00 | |
clears sta $4000,x ;scr1 | |
sta $4100,x | |
sta $4200,x | |
sta $4300,x | |
sta $4400,x ;scr2 | |
sta $4500,x | |
sta $4600,x | |
sta $4700,x | |
sta $4800,x ;buf1 | |
sta $4900,x | |
sta $4a00,x | |
sta $4b00,x | |
sta $4c00,x ;buf2 | |
sta $4d00,x | |
sta $4e00,x | |
sta $4f00,x | |
dex | |
bne clears | |
;lda #$00 | |
;jsr $1000 | |
jsr install | |
jsr main | |
jsr deinstall | |
;jmp * | |
;jmp bull | |
jmp $1000 | |
;--------------------------------------------------eof | |
;-------------------------------------------------- | |
;----- paragraph @the main...outside irq_stuff@ ----- | |
;-------------------------------------------------- | |
!zone main | |
;------- | |
main | |
lda fxstate ;...time for a new fx? | |
beq main ;no if 0...and back... | |
;yes...start run if 1 | |
;------- | |
lda fxtoggl ;toggle toggl (2buffering.) | |
eor #$01 | |
sta fxtoggl | |
jsr plot4025 ;plot new 'drop' | |
jsr plot4025 ;plot new 'drop' | |
jsr water | |
dec fxstate ;set fxstate to 0 | |
lda $dc01 ;space pressed? | |
cmp #$ef | |
beq end | |
jmp main ;no, do it again... | |
end rts | |
fxstate !byte $00 ;fx state: 01->running 00->ready | |
fxtoggl !byte $00 ;0/1 toggles every fx (2buffer...) | |
;--------------------------------------------------eof | |
;-------------------------------------------------- | |
;----- paragraph @sub: calculate water loops @ ----- | |
;-------------------------------------------------- | |
;mx = 1(or 2,3,4) | |
;for y = 1 to height - 1 | |
; for x = 1 to width - 1 | |
; new-water[y][x] = ((old-water[y-1][x] + | |
; old-water[y+1][x] + | |
; old-water[y][x-1] + | |
; old-water[y][x+1]) / 2) - | |
; new-water[y][x]) | |
; new-water[y][x] = new-water[y][x] - (new-water[y][x] shr mx) | |
; end | |
;end | |
; | |
; | |
!zone water | |
water | |
;$4800/$4c00 | |
lda #$00 ;nn00 | |
sta $50 | |
lda fxtoggl ;0->48 1->4c | |
asl | |
asl | |
ora #%01001000 | |
sta $51 | |
;$4828/$4c28 | |
lda #$28 ;nn28 | |
sta $52 | |
lda fxtoggl ;0->48 1->4c | |
asl | |
asl | |
ora #%01001000 | |
sta $53 | |
;$4850/$4c50 | |
lda #$50 ;nn50 | |
sta $54 | |
lda fxtoggl ;0->48 1->4c | |
asl | |
asl | |
ora #%01001000 | |
sta $55 | |
;$4c28/$4828 | |
lda #$28 ;nn28 | |
sta $56 | |
lda fxtoggl ;0->4c 1->48 | |
eor #%00000001 | |
asl | |
asl | |
ora #%01001000 | |
sta $57 | |
; + 50 | |
;+ + 52 -> 56 | |
; + 54 | |
ldx #23 ;y-max | |
wlop2 | |
ldy #39 ;x_max | |
wlop1 | |
!set m=0 | |
!do { | |
lda ($52),y ;get1 | |
dey | |
clc | |
adc ($50),y ;add2 | |
adc ($54),y ;add3 | |
dey | |
adc ($52),y ;add4 | |
lsr | |
iny | |
sec | |
sbc ($56),y | |
sta ($56),y | |
lda ($56),y | |
lsr | |
lsr | |
lsr | |
lsr | |
;lsr | |
;lsr | |
sta $ff | |
lda ($56),y | |
sec | |
sbc $ff | |
bpl *+4 | |
lda #$00 | |
sta ($56),y | |
!set m=m+1 | |
} until m=39 | |
lda $50 | |
clc | |
adc #$28 | |
sta $50 | |
bcc *+4 | |
inc $51 | |
clc | |
lda $56 | |
adc #$28 | |
sta $56 | |
lda $52 | |
clc | |
adc #$28 | |
sta $52 | |
bcc *+6 | |
inc $53 | |
inc $57 | |
clc | |
lda $54 | |
adc #$28 | |
sta $54 | |
bcc *+4 | |
inc $55 | |
dex | |
beq elop | |
jmp wlop2 | |
elop | |
rts | |
;--------------------------------------------------eof | |
;-------------------------------------------------- | |
;----- paragraph @irqtable: irqs defined here@ ----- | |
;-------------------------------------------------- | |
irqtable | |
; low high rasterline | |
!byte <irq1,>irq1,$00,$00 | |
;!byte <irq2,>irq2,$80,$00 | |
;!byte <irq3,>irq3,$88,$00 | |
nirq !byte 0 | |
nirqmax = ((nirq-irqtable)/4)-1 | |
;-------------------------------------------------- | |
;----- paragraph @the irq routines@ ----- | |
;-------------------------------------------------- | |
!zone irqroutines | |
!subzone irq1 { | |
irq1 | |
jsr nosharp | |
;------- fx_ready | |
lda fxstate | |
bne nofx | |
inc fxstate | |
lda fxtoggl ;prepare screenpart | |
eor #1 | |
ora #%00000010 | |
asl | |
asl | |
asl | |
asl | |
ora #%00000101 ;add (low) charpart | |
sta $d018 ;set new screen | |
nofx | |
;-------- | |
jmp nextirq | |
lda $d020 | |
sta oldBGcol1+1 | |
lda fxtoggl | |
asl | |
asl | |
sta $d020 | |
;sta $d021 | |
ldx #$c2 | |
dex | |
bne *-1 | |
;jsr $1003 | |
oldBGcol1 lda #$00 | |
;sta $d020 | |
sta $d020 | |
jmp nextirq | |
} | |
;------------------- | |
!subzone irq2{ | |
irq2 | |
jsr sharp | |
lda #$04 | |
sta $d020 | |
sta $d021 | |
ldx #$01 | |
dex | |
bne *-1 | |
lda #$00 | |
sta $d020 | |
sta $d021 | |
jmp nextirq | |
} | |
;------------------- | |
!subzone irq3{ | |
irq3 | |
jsr nosharp | |
lda #$05 | |
sta $d020 | |
sta $d021 | |
ldx #$01 | |
dex | |
bne *-1 | |
lda #$00 | |
sta $d020 | |
sta $d021 | |
jmp nextirq | |
} | |
;------------------- | |
;--------------------------------------------------eof | |
;----- paragraph @sub: irq_core@ ----- | |
;--------------------------------------- | |
;-------- | |
;-------- - -- - | |
;-------- - - - - - | |
;-------- - -- --- | |
;-------- - - - -- | |
;-------- | |
;--------------------------------------- | |
;------------------------ multi_ir(a)q's | |
;--------------------------------------- | |
;-------------------------------------- | |
!zone irqcore | |
;------- install | |
install | |
sei lda #$00 | |
sta nirq | |
lda #$35 | |
sta $01 | |
lda #<nmi | |
ldx #>nmi | |
sta $fffa | |
sta $fffc | |
stx $fffb | |
stx $fffd | |
lda irqtable | |
ldx irqtable+1 | |
sta $fffe | |
stx $ffff | |
lda irqtable+2 | |
sta $d012 | |
lda $d011 | |
and #$7f | |
ora irqtable+3 | |
sta $d011 | |
sta $dc0d ; disable 1 | |
sta $dd0d ; disable 2 | |
lda #$01 | |
sta $d01a | |
inc $d019 | |
cli | |
rts | |
;------- deinstall | |
deinstall | |
sei | |
lda #$37 | |
sta $01 | |
lda #$81 | |
sta $dc0d | |
lda #$00 | |
sta $d01a | |
lda #$1b | |
sta $d011 | |
inc $d019 | |
lda #$37 | |
sta $01 | |
cli | |
rts | |
;------- sharp: called from irq | |
sharp | |
inc $d012 | |
inc $d019 | |
sta sa+1 | |
lda #<sharpirq | |
sta $fffe | |
lda #>sharpirq | |
sta $ffff | |
cli | |
nop | |
nop | |
nop | |
nop | |
nop | |
nop | |
nop | |
nop | |
nop | |
; <- nosharp starts here | |
nop ; one extra nop | |
jmp *-10 | |
;--- nosharp: called instead of sharp by code that | |
; doesn't require timing | |
nosharp | |
sta sa+1 ; restore a | |
stx sx+1 ; restore x | |
rts | |
;------- sharpirq: irq generated by sharp | |
sharpirq | |
pla ; remove sharpirq | |
pla ; from stack | |
pla ; | |
stx sx+1 | |
ldx #5 | |
dex | |
bne *-1 | |
nop | |
nop | |
lda $d012 | |
cmp $d012 | |
beq *+2 | |
rts ; return to caller of sharp | |
;------- nmi: handle nmi irq's (restore button) | |
nmi | |
rti ; disable restore button | |
;pla ; jump back to i.e. tass | |
;pla | |
;pla | |
;jsr deinstall | |
;ldx #$ff | |
;txs | |
;jmp $9000 ;back2tass | |
;------- nextirq: end current irq and setup next one | |
nextirq | |
ldx nirq | |
txa | |
asl | |
asl | |
tax | |
lda irqtable,x | |
sta $fffe | |
lda irqtable+1,x | |
sta $ffff | |
lda irqtable+2,x | |
sta $d012 | |
lda $d011 | |
and #$7f | |
ora irqtable+3,x | |
sta $d011 | |
ldx nirq | |
inx | |
cpx #nirqmax+1 | |
bne *+4 | |
ldx #0 | |
stx nirq | |
inc $d019 | |
sa lda #0 ;restore a | |
sx ldx #0 ;restore x | |
rti ;return from irq | |
;--------------------------------------------------eof | |
;-------------------------------------------------- | |
;----- paragraph @sub:plot40x25@ ----- | |
;-------------------------------------------------- | |
!zone plot4026 | |
plot4025 | |
jsr rnd | |
lda seed+2 | |
and #%00011111 | |
adc #3 | |
tay | |
lda seed+3 | |
and #%0001111 | |
clc | |
adc #3 | |
tax | |
lda seed+3 | |
and #%00011111 | |
sta pl4025chr+1 | |
jsr doplot4025 | |
lda seed+3 | |
and #%00011111 | |
lsr | |
sta pl4025chr+1 | |
inx | |
jsr doplot4025 | |
dex | |
dex | |
jsr doplot4025 | |
inx | |
iny | |
jsr doplot4025 | |
dey | |
dey | |
jsr doplot4025 | |
lda seed+3 | |
and #%00011111 | |
lsr | |
lsr | |
lsr | |
sta pl4025chr+1 | |
dex | |
jsr doplot4025 | |
iny | |
iny | |
jsr doplot4025 | |
inx | |
inx | |
jsr doplot4025 | |
dey | |
dey | |
jsr doplot4025 | |
rts | |
;---------- | |
doplot4025 | |
lda lop4025,x | |
sta $fe | |
sta $fc | |
lda fxtoggl ;0->48 1->4c | |
asl | |
asl | |
ora #%01001000 | |
clc | |
adc hip4025,x | |
sta $fd | |
sta $ff | |
pl4025chr lda #$00 | |
;lda ($fe),y | |
;ora bmp4025,x | |
sta ($fc),y | |
sta ($fe),y | |
rts | |
hip4025 !byte $00,$00,$00,$00,$00,$00 | |
!byte $00,$01,$01,$01,$01,$01 | |
!byte $01,$02,$02,$02,$02,$02 | |
!byte $02,$02,$03,$03,$03,$03 | |
!byte $03 | |
lop4025 !byte $00,$28,$50,$78,$a0,$c8 | |
!byte $f0,$18,$40,$68,$90,$b8 | |
!byte $e0,$08,$30,$58,$80,$a8 | |
!byte $d0,$f8,$20,$48,$70,$98 | |
!byte $c0 | |
bmp4025 | |
;--------------------------------------------------eof | |
;-------------------------------------------------- | |
;----- Paragraph @sub: 32bitRandomizer@ ----- | |
;-------------------------------------------------- | |
!zone 32bit_randomize | |
!macro add .mactar { | |
lda $6a+.mactar | |
adc $8b+.mactar | |
sta $6a+.mactar | |
} | |
!macro rot .target { | |
lda $8b+.target | |
rol | |
sta $6a+.target | |
} | |
; initalize the seed with 'random' data | |
rndinit | |
adc $d012 | |
sta seed+3 | |
eor $dc04 | |
sta seed+2 | |
sbc $dc05 | |
sta seed+1 | |
eor #23 | |
sta seed+0 | |
rts | |
; random value held in seed (32 bits), | |
; arg used as temp. | |
; preserves x, y | |
; returns msb in a. | |
rnd lda seed+4 | |
asl | |
sta arg+4 | |
+rot 3 | |
+rot 2 | |
+rot 1 | |
sec | |
rol arg+4 | |
rol arg+3 | |
rol arg+2 | |
rol arg+1 | |
clc | |
+add 4 | |
pha | |
+add 3 | |
pha | |
+add 2 | |
+add 1 | |
clc | |
lda arg+2 | |
adc seed+4 | |
sta seed+2 | |
lda arg+1 | |
adc seed+3 | |
sta seed+1 | |
pla | |
sta seed+3 | |
pla | |
sta seed+4 | |
lda seed+1 ;most signif byte | |
rts | |
;--------------------------------------------------eof | |
;-------------------------------------------------- | |
;----- Paragraph @sub:create (dithered) font@ ----- | |
;-------------------------------------------------- | |
!zone ditherchar | |
createfont | |
lda #$08 | |
sta $50 | |
lda #$50 | |
sta $51 | |
lda #$01 | |
sta ditlop+1 | |
;screenscrambler ;) | |
;ditlop | |
;didilo | |
; jsr rnd | |
; lda seed+3 | |
; and #%00000110 | |
; tay | |
; lda seed+1 | |
; sta ($50),y | |
; | |
; lda seed+2 | |
; lsr | |
; asl | |
; adc $50 | |
; sta $50 | |
; | |
; sta $50 | |
; bcc *+4 | |
; inc $51 | |
; lda $51 | |
; cmp #$58 | |
; bne ditlop | |
ditlop ldx #$00 | |
inc ditlop+1 | |
inc ditlop+1 | |
inc ditlop+1 | |
inc ditlop+1 | |
didilo stx xpuf1+1 | |
jsr rnd | |
lda seed+1 | |
and #%00000111 | |
tax | |
lda seed+3 | |
and #%00000110 ;doublescanOff ;) | |
;and #%00000111 | |
tay | |
lda ($50),y | |
ora dbmp,x | |
sta ($50),y | |
xpuf1 ldx #$00 | |
dex | |
bne didilo | |
lda $50 | |
clc | |
adc #$08 | |
sta $50 | |
bcc *+4 | |
inc $51 | |
lda $51 | |
cmp #$51 | |
bne ditlop | |
rts | |
dbmp !byte 1,2,4,8,16,32,64,128 ;lookup table for charDither | |
;---------------------------------------eof | |
;-------------------------------------------------- | |
;------------------------------------------------ | |
;--------------------------------------------- | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment