Cubic Reveal

I just though I'd develop this a little further. Written a while ago on a whim!

square_reveal_1_1.gif?dl=1
«134

Comments

  • edited November 2017
    It was part of an investigation into using a plasma based table for displaying the picture. We've all been wowed by Demos that do fancy plasma effects, so I though I would do something more useable with it. You can load a screen, then reveal it before you load the next block for example. Many other possibilities I'm sure too. If of interest to anyone, I'll tidy up the source and post it. Its 151 bytes for the routine and 192 for the Plasma.

    It actually only does 1 frame per call, so you can use it along side something else, like a scroller or music I suppose

    I'm sure it could be written more efficiently and shorter though.
    Post edited by Arkannoyed on
  • Obviously, different Plasma maps can be created to give very different results to your liking.

    square_reveal_1_1a.gif?dl=1
  • Thanks.

    Using the same plasma field, you can do it a lot easier and quicker using just the ATTRs, which is still nice. I have an example ....somewhere.
  • Wonderful. A little extravagant, so should be used sparingly, but entirely impressive.
    Every man should plant a tree, build a house, and write a ZX Spectrum game.

    Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
    and zasm Z80 Assembler syntax highlighter.
    Member of the team that discovered, analyzed, and detailed the floating bus behavior on the ZX Spectrum +2A/+3.

    A few Spectrum game fixes.
  • Yep that's very lovely indeed :)
  • Wonderful. A little extravagant, so should be used sparingly, but entirely impressive.
    Yes maybe, but extravagance when done in not too many bytes just gives a nice finishing touch to games etc.
    I'm rather appalled at how horribly I wrote it, so I'm just doing a re-write to its less embarassing!
    :))
  • Arkannoyed wrote: »
    I'm rather appalled at how horribly I wrote it, so I'm just doing a re-write so it’s less embarrassing!
    Preach, brother! I just knocked five bytes off of my fade-to-black routine and sped it up quite a bit. Feel so silly about not seeing how unnecessary those five bytes were to begin with. But that’s (good) coding practice for you—do a rough proof-of-concept draft, then tune it to perfection.
    Every man should plant a tree, build a house, and write a ZX Spectrum game.

    Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
    and zasm Z80 Assembler syntax highlighter.
    Member of the team that discovered, analyzed, and detailed the floating bus behavior on the ZX Spectrum +2A/+3.

    A few Spectrum game fixes.
  • Indeed. So many times you come back to an old project, and after re-figuring out how it works because you commented the original source so sparsely that even the Speccys struggling to keep up, you see things that just didn't occur to you at the time. I've re-written stuff I though was might clever and really short and ended up saving massive chunks.
  • Viewing it from a PC it seems to run a little fast, almost too fast to really appreciate the way it reveals. On a real or emulated Spectrum perhaps the pace is slower?
  • It is quite fast, but as I mentioned, it runs on repeated CALLs. So if you CALL the routine it only updates by one frame before RETurning. A full cycle to reveal the screen can be anywhere from 7-255, so you can either pause between frames a little to slow it down, or alter the plasma to make the reveal happen over a longer period.

    basically the way plasma works, well in this case anyway, is that 1 frame decreases each byte of the plasma table by 1 Then it checks if that byte falls into the area in which it can start to reveal what occupies that 2x2 character square. Have I lost anyone yet?

    There are 7 square masks from full 16 x 16 to only 2x2 pixels. Once a plasma byte is within the 00-07 range then it prints the corresponding square masked portion of the screen, 07 being the smallest, 00 the full monty!

    The plasma can be quite difficult to code to make it flow nicely. There is a Sinus calc to do this, which I found somewhere years ago. Once you have your plasma table, its cyclically updated all relative to one another, so it doesn't get destroyed, but might need positioning correctly for displaying the screen again. You could of course just have multiple tables to display it differently each time with some simple random number generation to select the plasma pattern. Or if the table calc could be coded into Z80 simply enough then while the screen is displayed, it could make a new plasma table, then fade out, which of course is also possible too with a little alteration.

    I've found a few neat tricks to speed it up and make it smaller, so I'm re-writing it at the moment.

    I'll try and make it easier to use and understand, so it might be a useful addition to a game or loader for someone.
    Thanked by 1colonel32
  • edited November 2017
    Very nice and impressive indeed.

    These are the type of things that I wrote before attempting games.
    They helped with learning the way the screen and attributes were layered and tricks that could be used.
    They have been lost to time, were my first attempts at real code and very nasty in the way they were written.
    Post edited by MatGubbins on
  • edited November 2017
    I've been playing around with lots of multicolour effects (NIRVANA etc) recently. I fancy looking at a multicolour version of plasma! One that leaves the pixels alone but does something interesting with the 8x2 attributes.
    Post edited by colonel32 on
    Robin Verhagen-Guest
    SevenFFF / Threetwosevensixseven / colonel32
    NXtel NXTP ESP Update ESP Reset CSpect Plugins
  • edited November 2017
    I slowed your gif down to give a better idea of the mechanics of the effect. Hope you don't mind!

    giphy.gif
    Post edited by colonel32 on
    Robin Verhagen-Guest
    SevenFFF / Threetwosevensixseven / colonel32
    NXtel NXTP ESP Update ESP Reset CSpect Plugins
  • edited November 2017
    Arkannoyed wrote: »
    It is quite fast, but as I mentioned, it runs on repeated CALLs. So if you CALL the routine it only updates by one frame before RETurning. A full cycle to reveal the screen can be anywhere from 7-255, so you can either pause between frames a little to slow it down, or alter the plasma to make the reveal happen over a longer period.

    I love this way of working. You can do a bunch of highly extravagant things, all apparently at once, just by spreading it all out in the time domain!

    Post edited by colonel32 on
    Robin Verhagen-Guest
    SevenFFF / Threetwosevensixseven / colonel32
    NXtel NXTP ESP Update ESP Reset CSpect Plugins
  • Looks really nice. Would definitely be interested in seeing the code behind it (though I think I get what you mean from your description).
  • Nice work Arkannoyed!!!... Im likewise looking forward to seeing the sources for this one... Could be used for some interesting full screen explosion type effects, perhaps... ?... :)
  • Looks less impressive slowed down doesn't it!? That does illustrate the mechanics of the effect well though.

    Re-writing is going ok, though I've hit a wall where something isn't right and I can't figure out what. I need that moment of inspiration, the lightbulb above the head, to get it fixed. Hopefully now though under 130 bytes, and moderately more efficiently coded!
  • Could someone explain how plasmas work in really really really simple terms, as I'm not sure I'll ever understand it...
    Joefish
    - IONIAN-GAMES.com -
  • OK, I found this Python example:
    http://www.pygame.org/pcr/numpy_plasma/index.php

    I think his 'Save' comments actually mean 'Restore', but I get the gist.
    He's got two parameters that he increases at different rates as he steps across the X-direction, and then resets them and steps two other parameters with each step in the Y-direction. And he uses those parameters to index into a Cosine table and adds up the four results, translating them into a colour.
    Then after each frame he goes back to the initial values and tweaks them a bit, then starts again.

    So really he's got two Sine / Cosine wave functions running in each of the X and Y directions, and combining them to make a colour contour map.

    I thought there were more shortcuts to it than that though. I guess you could do the X-lookups first then the Y-lookups to make two 1D lists, then combine them to do the 2D X/Y plot..? That sounds too easy to me, but I can't see why it wouldn't work..?
    Joefish
    - IONIAN-GAMES.com -
  • edited November 2017
    Ok, basically plasma in this case works like this;

    we have a routine that treats the screen as if it were 16 wide x 12 high squares. That gives 192 bytes with values of 00-ff (0-255). What we do is make things happen when those corresponding bytes fall within a particular range, in this instance, 00-07, which correspond to the 8 square masks.

    On each call to the routine we increase each of the 192 bytes by 1, checking each in turn for its value and whether that indicates that we need to 'do' something. If it falls outside the 'active' range, then we just go to the next and repeat until all 192 are done.

    So as you can see, with all the bytes in that 192 starting with different values, we get patterns emerging. A little demonstration below shows the plasma as a simple increasing number pattern with 00 as the first byte all the way up to 191 as the last (192nd). The resulting reveal pattern is obvious! Actually, we have to reverse the order of the numbers and they need to Descend, but you get the idea hopefully.

    plasma3.gif?dl=1

    Post edited by Arkannoyed on
  • This is actually the fresh n' newly re-written version in action too, currently at 130 bytes, but that might slim down by a few more as usually happens. Just cleaning up the source too white I'm at it.
  • edited November 2017
    That's clear - I meant, how you generate and animate plasmas in the first place! But I think I get it now. You're just using one static frame of a plasma screen to give you the starting values for each cell of your effect.

    You could simply do X+Y+N-28 for each cell and as N increases the cells go through 0..7 in a diagonal sweep. But with your method you get to make more interesting patterns. Although with only 192 squares I'd probably just program the starting numbers in by hand.

    What would be cool is if you keep running the plasma effect so it's moving in waves AND add on a gradual offset, so the squares actually pulse in and out a bit before settling down. Have you tried that??? :!!
    Post edited by joefish on
    Joefish
    - IONIAN-GAMES.com -
  • Ok, here it is then, at 130 bytes for now along with some example Plasma to get going with;
    ;square fade in
    ;using a plasma table
    ;fade in the screen display file
    ;
    ;plasma map at 0f940h but can be altered or moved elsewhere
    ;stored picture must reside at 0c000h - 49152
    ;
    ;CALL the set_up routine first
    ;Thereafter repeatedly CALL the entry point further in
    ;set_up: FA10h - RAND USR 64016
    ;entry : FA1Ch - RAND USR 64028
    ;
    ;JES Unknown - Nov 2017
    ;
    org 0f940h
              db 14h,14h,16h,19h,1dh,20h,23h,24h,24h,22h,1eh,19h,12h,0ah,09h,08h
              db 1ch,1dh,1fh,22h,25h,29h,2bh,2dh,2dh,2bh,27h,21h,1ah,13h,0ch,09h
              db 24h,24h,26h,29h,2dh,30h,33h,35h,35h,33h,2fh,29h,22h,1bh,13h,0dh
              db 2ah,2bh,2dh,30h,34h,37h,3ah,3bh,3bh,39h,35h,30h,29h,21h,1ah,13h
              db 30h,30h,32h,35h,39h,3ch,3fh,40h,40h,3eh,3ah,35h,2eh,26h,1fh,19h
              db 33h,33h,35h,38h,3ch,3fh,42h,43h,43h,41h,3dh,38h,31h,2ah,22h,1ch
              db 33h,34h,36h,39h,3dh,40h,43h,44h,44h,42h,3eh,39h,32h,2ah,23h,1dh
              db 32h,33h,35h,38h,3bh,3fh,41h,43h,43h,41h,3dh,37h,31h,29h,22h,1bh
              db 2fh,2fh,31h,34h,38h,3bh,3eh,3fh,3fh,3dh,39h,34h,2dh,26h,1eh,18h
              db 29h,2ah,2ch,2fh,32h,36h,38h,3ah,3ah,38h,34h,2eh,28h,20h,19h,12h
              db 22h,23h,25h,28h,2bh,2fh,31h,33h,33h,31h,2dh,27h,21h,19h,12h,0bh
              db 1ah,1bh,1dh,20h,23h,27h,29h,2bh,2bh,29h,25h,1fh,19h,11h,0ah,08h
    org 0fa00h
              
    data_squares:
              db 01h,80h,03h,0c0h,07h,0e0h,0fh,0f0h
              db 1fh,0f8h,3fh,0fch,7fh,0feh,0ffh,0ffh
    
    set_up:
              ld hl,5affh     ;ATTRs end
              xor a           ;Black PAPER, Black INK
    slp0:
              ld (hl),a       ;
              dec hl          ;
              bit 3,h         ;
              jr nz,slp0      ;
              out (0feh),a    ;BORDER 0
    
    entry:
              ld hl,0f940h     ;plasma table 16 x 12 blocks = 192 bytes
              ld de,0d820h     ;
    mlp0:
              inc (hl)
              ld a,(hl)
              and 0f8h        ;only the 0-7 portion on the plasma byte controls the fade-in
              jr nz,nxt_block ;if not 0-7 then skip to next
    
              push hl         ;save plasma table position
    
              xor (hl)        ;retain just bits 0-3 (00-07)
              add a,a         ;x2
              ld l,a          ;
              inc h           ;HL = address of mask bytes 0FAxx
              ld b,(hl)       ;B=left mask
              inc l           ;
              ld c,(hl)       ;C=right mask
              ld h,b          ;
              ld l,c          ;HL=map counter
              
    a2s_addr:
              push de         ;pushed DE = stored ATTRs address
    
              ld a,d          ;hi-byte of ATTRs addr
              and 0bh         ;mask
              add a,a         ;x2
              add a,a         ;x4
              add a,a         ;x8
              or 87h          ;point to stored screen address
              ld d,a          ;DE = stored screen address
    mlp1:
              add hl,hl       ;uses mask bytes as a map of which lines to mask or ignore
              jr nc,nlu       ;next line up
    
              ld a,(de)       ;get byte
              and b           ;mask LHS
              ex af,af'       ;store result
    
              inc e           ;next byte
              ld a,(de)       ;get byte
              and c           ;mask RHS
              res 7,d         ;point DE to screen
    
              ld (de),a       ;put byte
              dec e           ;next
              ex af,af'       ;restore stored
              ld (de),a       ;put byte
              set 7,d         ;point DE back to stored scr
    nlu:
              ld a,d          ;next line up.
              dec d           ;I'm not going to explain this
              and 07h         ;as if you aren't familiar with
              jr nz,mlpe      ;this by now, then you should
              ld a,e          ;be ashamed!
              add a,0e0h      ;
              ld e,a          ;
              sbc a,a         ;
              and 08h         ;
              add a,d         ;
              ld d,a          ;
    mlpe
              ld a,h          ;check if counter is zero
              or l            ;
              jr nz,mlp1      ;
    
    mlp2:                     ;place ATTRs
              pop hl          ;ATTRs address now into HL
    
              ld d,h          ;
              res 7,d         ;
              ld e,l          ;point DE to screen equivalent ATTRs address
              ldi             ;copy byte
              ldd             ;copy byte
              res 5,e         ;next ATTRs line up
              ld l,e          ;
              ldi             ;copy byte
              ldd             ;copy byte
              set 5,l         ;back down 1 ATTRs line
    
              ex de,hl        ;DE now holds ATTRs addr of stored scr
              pop hl          ;plasma address into HL
    nxt_block:
              inc e           ;next right
              inc de          ;next right and take care of D if it needs to INC
              bit 5,e         ;ensure that we only address odd lines (1,3,5,7)
              jr nz,mlp3      ;
              ld a,e          ;
              add a,20h       ;
              ld e,a          ;
    mlp3:
              inc l           ;next plasma byte
              jr nz,mlp0      ;repeat
              ret             ;
    

    Don't forget to plonk a screen at 49152! Enjoy :)
    Thanked by 1Ast_A_Moore
  • colonel32 wrote: »
    I've been playing around with lots of multicolour effects (NIRVANA etc) recently. I fancy looking at a multicolour version of plasma! One that leaves the pixels alone but does something interesting with the 8x2 attributes.
    If you could get it to run in regular time slices you could do it in 4x4 cells on a 128K, simply switching between display screens at the right moment to give you 8x4 attributes. Page the alternate screen into top RAM so you can write to both screens at once, and only flip the bit that chooses which screen to actually display.

    Joefish
    - IONIAN-GAMES.com -
  • Indeed you could!

    4x4 cells would require a fair bit more programming, but a plasma table of only 48 bytes. I'm sure I've seen it done in Demos before.

    If one was really creative, it could continue loading whilst revealing the screen maybe? Now that would look nice. Probably a little tricky though!

    I'm just tidying up the basic listing for the Plasma creator that I've been using mostly. I've some recollection that it came from Russian demo site perhaps.
  • edited November 2017
    I knew I'd forget something!

    saved 3 bytes on the Next line up calc, as it doesn't cross a screen 1/3
    nlu:
              ld a,d          ;
              dec d           ;
              and 07h         ;
              jr nz,mlpe      ;
              ld a,08         ;
              add a,d         ;
              ld d,a          ;
              res 5,e         ;
    

    Now 127 bytes
    Post edited by Arkannoyed on
  • Actually, I suppose the 12 bytes that set the ATTRs and BORDER to black aren't strictly needed, so that would save a bit more making it 115 bytes.
  • I generally do the next line up/down calculation as follows (accounts for all screen positions):

    16 bytes
    	ld a,h		
    	and 7		
    	jr nz,done
    	ld a,l		
    	sub 32		
    	ld l,a		
    	jr c,done
    		
    	ld a,h
    	add a,8		
    	ld h,a		
    
    done	dec h		
    
    Every man should plant a tree, build a house, and write a ZX Spectrum game.

    Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
    and zasm Z80 Assembler syntax highlighter.
    Member of the team that discovered, analyzed, and detailed the floating bus behavior on the ZX Spectrum +2A/+3.

    A few Spectrum game fixes.
  • Generally thats fine, but in this instance, we don't need to cross the screen third boundary, so ld a,h,add a,8 ld h,a
    isn't needed. We can optimise for just the parameters that it'll need to work within.

    I've just saved another chunk by removing the bitmap masks for the squares. It's slowed ever so slightly, but now creates the masks as it goes.
    ;square fade in
    ;using a plasma table
    ;fade in the screen display file
    ;
    ;plasma map at 0f940h but can be altered or moved elsewhere
    ;stored picture must reside at 0c000h - 49152
    ;
    ;CALL the set_up routine first
    ;Thereafter repeatedly CALL the entry point further in
    ;set_up: FA00h - RAND USR 64000
    ;entry : FA0Ch - RAND USR 64012
    ;
    ;JES Unknown - Nov 2017
    ;v1.2
    
    org 0fa00h
    
    set_up:
              ld hl,5affh     ;ATTRs end
              xor a           ;Black PAPER, Black INK
    slp0:
              ld (hl),a       ;
              dec hl          ;
              bit 3,h         ;
              jr nz,slp0      ;
              out (0feh),a    ;BORDER 0
    
    entry:
              ld hl,0f940h     ;plasma table 16 x 12 blocks = 192 bytes
              ld de,0d820h     ;
    mlp0:
              inc (hl)
              ld a,(hl)
              and 0f8h        ;only the 0-7 portion on the plasma byte controls the fade-in
              jr nz,nxt_block ;if not 0-7 then skip to next
    
              push hl         ;save plasma table position
    
              xor (hl)        ;retain just bits 0-3 (00-07)
    
              ld b,a
              xor a
              inc b
    tst_l0:
              sll a
              djnz tst_l0
              ld h,a
    tst_l1:
              rlca
              jr nc,tst_l1
              rrca
              ld l,a
              ld b,h
              ld c,l
              
    a2s_addr:
              push de         ;pushed DE = stored ATTRs address
    
              ld a,d          ;hi-byte of ATTRs addr
              and 0bh         ;mask
              add a,a         ;x2
              add a,a         ;x4
              add a,a         ;x8
              or 87h          ;point to stored screen address
              ld d,a          ;DE = stored screen address
    mlp1:
              add hl,hl       ;uses mask bytes as a map of which lines to mask or ignore
              jr nc,nlu       ;next line up
    
              ld a,(de)       ;get byte
              and b           ;mask LHS
              ex af,af'       ;store result
    
              inc e           ;next byte
              ld a,(de)       ;get byte
              and c           ;mask RHS
              res 7,d         ;point DE to screen
    
              ld (de),a       ;put byte
              dec e           ;next
              ex af,af'       ;restore stored
              ld (de),a       ;put byte
              set 7,d         ;point DE back to stored scr
    nlu:
              ld a,d          ;
              dec d           ;
              and 07h         ;
              jr nz,mlpe      ;
              ld a,08         ;
              add a,d         ;
              ld d,a          ;
              res 5,e         ;
    mlpe
              ld a,h          ;check if counter is zero
              or l            ;
              jr nz,mlp1      ;
    
    mlp2:                     ;place ATTRs
              pop hl          ;ATTRs address now into HL
    
              ld d,h          ;
              res 7,d         ;
              ld e,l          ;point DE to screen equivalent ATTRs address
              ldi             ;copy byte
              ldd             ;copy byte
              res 5,e         ;next ATTRs line up
              ld l,e          ;
              ldi             ;copy byte
              ldd             ;copy byte
              set 5,l         ;back down 1 ATTRs line
    
              ex de,hl        ;DE now holds ATTRs addr of stored scr
              pop hl          ;plasma address into HL
    nxt_block:
              inc e           ;next right
              inc de          ;next right and take care of D if it needs to INC
              bit 5,e         ;ensure that we only address odd lines (1,3,5,7)
              jr nz,mlp3      ;
              ld a,e          ;
              add a,20h       ;
              ld e,a          ;
    mlp3:
              inc l           ;next plasma byte
              jr nz,mlp0      ;repeat
              ret             ;
    

    Now 118 or 106 bytes.
Sign In or Register to comment.