LZ water ripple in s2

Discussion in 'Engineering & Reverse Engineering' started by MoDule, Jun 21, 2008.

Thread Status:
Not open for further replies.
  1. MoDule

    MoDule

    Tech Member
    323
    5
    18
    Procrastinating from writing bug-fix guides
    I will now show you how to add the water ripple effect from LZ to theoretically any zone in s2.
    Don't get too excited yet, though. If you've played the betas you might remember the effect being there. So why was it taken out of the final? Because things can get really slow, that's why.

    First, let me begin by explaining how the ripple effect is achieved. All you really need to know is that horizontal scrolling is handeled by an array of longwords beginning at $FFFFE000. Each entry holds the scroll factor of a single horizontal line of pixels going from top to bottom whereas the high word is for the foreground and the low word for the background. This is explained in more detail by qiuu in the S2:AE thread (thanks, btw). The water ripple basically just manipulates the array so that some lines are shifted slightly to one side or the other. How far each line is shifted is stored in a table. For the foreground it's stored in 'Deform_LZ_Data1' and the background uses the wobble data from the bubble object.

    So let's begin, shall we?
    First, just so you know, im using Xenowhirl's 2007 disassembly.
    Now, we'll be adding to the software scrolling engine, so it would make sense to put our code in there. Since CPZ and ARZ are the only two zones that use water in s2 it would be best to put it somewhere inbetween 'SwScrl_CPZ' and 'SwScrl_ARZ'.

    [codebox]SwScrl_Water:
    ; this adds the LZ water ripple effect to any level
    lea (Deform_LZ_Data1).l,a3
    lea (Obj0A_WobbleData).l,a2
    move.b ($FFFFF7D8).w,d2
    move.b d2,d3
    addi.w #$80,($FFFFF7D8).w ; '€'
    add.w (Camera_Bg_Y_pos).w,d2
    andi.w #$FF,d2
    add.w (Camera_Y_pos).w,d3
    andi.w #$FF,d3
    lea (Horiz_Scroll_Buf).w,a1
    move.w #$DF,d1 ; 'ß'
    move.w (Water_Level_1).w,d4
    move.w (Camera_Y_pos).w,d5
    [/codebox]
    I'll stop here real quick because I need to tell you that I added an equate to the disassembly to enhance readability. More specifically, it's 'Camera_Bg_Y_pos' which is located at $FFFFEE0C. You'l need to add that equate for it to work. best put it right underneath 'Camera_Y_pos' so it looks something like this:
    [codebox]Camera_RAM = ramaddr( $FFFFEE00 )
    Camera_X_pos = ramaddr( $FFFFEE00 )
    Camera_Y_pos = ramaddr( $FFFFEE04 )
    Camera_Bg_Y_pos = ramaddr( $FFFFEE0C )
    [/codebox]
    So, what does this first segment do? We load the two tables containing the deformation data into a3 and a2. $FFFFF7D8 stores the 'phase' of the the ripple. Without this there'd still be a ripple, but it wouldn't move. We add $80 to it every frame which means the phase increases every second frame (level coordinates are stored as words and $80 is 1/2 of $100). The phase is copied once to d2 which is for the background and once to d3 which is for the foreground. These two registers will be used later for vertical position of the ripple plus it's phase. Both our tables of ripple data are 256 bytes long, so to avoid flowing into other data we need to use something like x mod 256. Since 256 is a power of 2 we can just and with $FF. We store the Hscroll buffer in a1. If we didn't do that all would be for nothing as we couldn't even make the ripple show without it. We move $DF (223) to d1 which is how many lines there are minus 1 (consider it a do-while statement, where the code is always executed at least once even when the condition isn't true).

    Moving on
    [codebox]- ; as long as the camera is above the water
    cmp.w d4,d5 ; is camera below water?
    bge.s SwScrl_Water_doRipple ; if yes, branch
    addq.w #4,a1 ; increment pointer
    addq.w #1,d5 ; increment camera y pos
    addq.b #1,d2
    addq.b #1,d3
    dbf d1,-
    rts
    [/codebox]
    Here we start checking all 224 lines from to to bottom. As long as the line we're checking isn't under water we just increment the the pointer in a1 (to the Hscroll buffer. This basically means we're not changing the horizontal position of the current line and moving on to the next). d4 and d5 are the current water level and the camera y position, respectively. We increment all of our y position counters so that in the next loop we'll be looking at the line below the current one. If there's no water in sight we've basically done nothing (except waste valuable processor time). Once we reach a line that's below the water surface we branch to the next part of the program.

    [codebox] ; does the LZ water ripple effect once the camera is below the water
    SwScrl_Water_doRipple:
    move.b (a3,d3.w),d4 ; FG ripple effect
    ext.w d4
    add.w d4,(a1)+

    move.b (a2,d2.w),d4 ; BG ripple effect
    ext.w d4
    add.w d4,(a1)+
    addq.b #1,d2
    addq.b #1,d3
    dbf d1,SwScrl_Water_doRipple
    rts[/codebox]
    Here we finally make the water ripple happen. Using a3 and a2 as indexes for our tables we retrieve the appropriate values for the current line (remember how we added 1/2 of $100 to the phase earlier? That becomes significant here, because we're just using the high byte of the y position, which only increases every second frame). These values are then added to the current scroll factor of the line we're focusing on. We then increment our counters like before and repeat until we reach the last line.

    [codebox]Deform_LZ_Data1:
    dc.b 1, 1, 2, 2, 3, 3, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0; 0
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 16
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 32
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 48
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 64
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 80
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 96
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 112
    dc.b $FF,$FF,$FE,$FE,$FD,$FD,$FD,$FD,$FE,$FE,$FF,$FF, 0, 0, 0, 0; 128
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 144
    dc.b 1, 1, 2, 2, 3, 3, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0; 160
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 176
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 192
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 208
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 224
    dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 240[/codebox]
    Here's the ripple data so you don't have to look for it in s1.

    There, now that you understand how the water ripple effect works you might be wondering how to use it. Simple. All you have to do is locate the code for the zone you want to add the ripple to (they all start with 'SwScrl') and replace all the rts with branches to our code (bra, not bsr, jmp if it's too far away). Done.
    At least, I think that's all you have to do. There might be some zones that branch to somewhere outside the main code and don't return.
    Note that CPZ only has water in act 2, so you'll need to add a short test for that.

    I'd like to warn you again that this will slow down your hack noticably if you have lots of objects on screen. As long as you are playing alone it should be okay in s2, but if you're playing as Sonic and Tails the slowdown will be pretty bad.
    I have no idea how s3 manages to do that with collapsing ledges, brakable floors, animated tiles, Sonic and Tails and generally lots of sprites on screen without any slowdown at all.

    Btw, does anyone have the AIZ ripple data? I tried to find it but came up empty handed.

    If this is satisfactory, I'd like to ask one of the wiki guys to add this to the how-tos section. That is, if there aren't any objections.

    Here's some images for demonstrational purposes:
    [​IMG] CPZ
    [​IMG] ARZ

    Download
     
  2. Trunks

    Trunks

    AGAIN TRY Oldbie
    Wicked stuff, man. I'm a bit retarded when it comes to ASM, but is there any chance we could see an example in ROM form? I'm excited by this.
     
  3. MoDule

    MoDule

    Tech Member
    323
    5
    18
    Procrastinating from writing bug-fix guides
    I guess I could compile a rom real quick with just the effect in. I'd just need a place to upload it to.

    Edit: yay, level up.
     
  4. Hitaxas

    Hitaxas

    Retro 80's themed Twich streamer Member
    Module, this is awesome. :thumbsup:

    I was going to port this for my hack, but haven't got to it. This will help a lot. <3


    Edit: Just a heads up: Not all of the code that Module has posted is labeled correctly, but if you know what you are doing, you can grab the necessary chunks from the S1 asm.
     
  5. Tets

    Tets

    one rude dude Oldbie
    827
    9
    18
    Great timing, I was just thinking the other day about how awesome it would be if I could put Labyrinth Zone's water ripple effects in Sonic 2. I did a quick test of this on my own hack (in which I've already added water to CPZ act 1) and it looks really good, albeit slow like you said. I'd like to have a look at how Sonic 3 does this without any apparent slowdown, but I don't even know where to start.
     
  6. qiuu

    qiuu

    Tech Member
    144
    9
    18
    Blue Ball & Blocks
    Just added it to ARZ, looks nice.

    I think the data of Deform_LZ_Data1 is only included in the rev01 of Sonic 1, of which I haven't got a disassembly, however in an older topic jman2050 has documented that BG deformation changes (also for the other Sonic 1 zones) including ASM code: http://forums.sonicretro.org/index.php?showtopic=7340
    bgdeform.txt includes the foreground deformation data, labeled LZ_Wave_Data.

    btw, this is my post MoDule was referring to in the beginning of his post, though I guess his explanation is better than mine.

    I guess that part for the calculation above water level can be optimized as no loop is necessary for this:
    <!--g1--><div class='geshitop'>Syntax Highlighted Code: ASM</div><div class='geshimain'><!--eg1--><pre class="asm"> [color= #00bfff;]sub[/color].[color= #00bfff;]w[/color] <span style="font-weight:bold;">d5</span>,<span style="font-weight:bold;">d4</span>
    [color= #00bfff;]blt[/color].[color= #00bfff;]s[/color] SwScrl_Water_doRipple
    [color= #00bfff;]add[/color].[color= #00bfff;]b[/color] <span style="font-weight:bold;">d4</span>,<span style="font-weight:bold;">d2</span>
    [color= #00bfff;]add[/color].[color= #00bfff;]b[/color] <span style="font-weight:bold;">d4</span>,<span style="font-weight:bold;">d3</span>
    [color= #00bfff;]asl[/color].[color= #00bfff;]w[/color] [color= #ff0000;]#[/color][color= #ff0000;]2[/color],<span style="font-weight:bold;">d4</span>
    [color= #00bfff;]adda[/color].[color= #00bfff;]w[/color] <span style="font-weight:bold;">d4</span>,<span style="font-weight:bold;">a1</span>
    [color= #00bfff;]cmpi[/color].[color= #00bfff;]w[/color] [color= #ff0000;]#[/color][color= #ff0000;]$[/color][color= #ff0000;][color= #ff0000;]380[/color][/color],<span style="font-weight:bold;">d4</span>
    [color= #00bfff;]blt[/color].[color= #00bfff;]s[/color] SwScrl_Water_doRipple
    [color= #00bfff;]rts[/color]</pre><!--gc2--><!--CXN1Yi53CWQ1LGQ0CglibHQucwlTd1NjcmxfV2F0ZXJfZG9SaXBwbGUKCWFkZC5iCWQ0LGQyCglhZGQu
    YglkNCxkMwoJYXNsLncJIzIsZDQKCWFkZGEudwlkNCxhMQoJY21waS53CSMmIzAzNjszODAsZDQKCWJsd
    C5zCVN3U2NybF9XYXRlcl9kb1JpcHBsZQoJcnRz--><!--egc2--><!--g2--></div><!--eg2-->
    This should at least improve performance a little if there is no or only little water on the screen.
    I guess further performance improvement can be made if the water effect is applied and adapted to each zone's deformation separately.

    On a less related note, I noticed that in EHZ, the lowest two layers don't scroll as they are not affected by the deformation code. This can be fixed by appending something like <!--g1--><div class='geshitop'>Syntax Highlighted Code: ASM</div><div class='codemain'><!--eg1--><pre class="asm"> [color= #00bfff;]move[/color].[color= #00bfff;]w[/color] <span style="font-weight:bold;">d4</span>,<span style="">(</span><span style="font-weight:bold;">a1</span><span style="">)</span>+
    [color= #00bfff;]move[/color].[color= #00bfff;]w[/color] <span style="font-weight:bold;">d3</span>,<span style="">(</span><span style="font-weight:bold;">a1</span><span style="">)</span>+</pre><!--gc2--><!--CW1vdmUudwlkNCwoYTEpKwoJbW92ZS53CWQzLChhMSkr--><!--egc2--><!--g2--></div><!--eg2--> twice to the EHZ deformation routine.
     
  7. MoDule

    MoDule

    Tech Member
    323
    5
    18
    Procrastinating from writing bug-fix guides
    Whoops. I can't believe I forgot about that. I had it right next to me. I'll just have to add it later when I get home.
    Edit: It's been added.
    I felt like mentioning you, because your post is what got me interested in the first place.

    I'll definitely try that out when I get home.
    And yes, applying the effect directly instead of looping through the array a second time should improve performance considerably.
    Edit: I just tried it and there was no noticable speed increase. Too bad, really, it seemed like a good idea and there's got to be something we can do.

    Actually, I think that's the first thing I did with deformation after your post. Such an easy fix. I wonder how an error like that could make it into the final.
     
  8. Alriightyman

    Alriightyman

    I am back... from the dead! Tech Member
    354
    3
    18
    Largo, FL
    0101001101101111011011100110100101100011 00000010: 0101001100000011 01000101011001000110100101110100011010010110111101101110
    Sorry about the bump. But I thought I'd add something to this. This will add that S3K type of ripple effect.
    Code (Text):
    1. Deform_LZ_Data1:   
    2.         dc.w 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0
    3.         dc.w 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0
    4.         dc.w 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0
    5.         dc.w 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0
    6.         dc.w 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0
    7.         dc.w 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0
    8.         dc.w 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0
    9.         dc.w 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0
    10.         dc.w 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0
    11.         dc.w 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0
    12.         dc.w 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0
    13.         dc.w 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0
    14.         dc.w 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0
    15.         dc.w 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0
    16.         dc.w 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0
    17.         dc.w 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0
    18.         dc.w $FFFE, 1, 2, 2, $FFFF, 2, 2, 1, 2, $FFFF, $FFFE
    19.         dc.w $FFFE, $FFFE, 1, $FFFF, $FFFF, $FFFF, 0, $FFFE, 0, 0
    20.         dc.w 0, $FFFE, 0, $FFFE, 2, 0, $FFFE, 2, 2, $FFFF, $FFFE
    21.         dc.w $FFFE, 1, 2, 2, $FFFF, 2, 2, 1, 2, $FFFF, $FFFE
    22.         dc.w $FFFE, $FFFE, 1, $FFFF, $FFFF, $FFFF, 0, $FFFE, 0, 0, 0
    23.         dc.w $FFFE, 0, $FFFE, 2, 0, $FFFE, 2, 2, $FFFF, $FFFE, $FFFE
    24.         dc.w 1, 2, 2, $FFFF, 2, 2, 1, 2, $FFFF, $FFFE, $FFFE
    25.         dc.w $FFFE, 1, $FFFF, $FFFF, $FFFF, 0, $FFFE, 0, 0, 0
    26.         dc.w $FFFE, 0, $FFFE, 2, 0, $FFFE, 2, 2, $FFFF, $FFFE
    27.         dc.w $FFFE, 1, 2, 2, $FFFF, 2, 2, 1, 2, $FFFF, $FFFE
    28.         dc.w $FFFE, $FFFE, 1, $FFFF, $FFFF, $FFFF, 0, $FFFE, 0, 0
    29.         dc.w 0, $FFFE, 0, $FFFE, 2, 0, $FFFE, 2, 2, $FFFF, $FFFE
    30.         dc.w $FFFE, 1, 2, 2, $FFFF, 2, 2, 1, 2, $FFFF, $FFFE
    31.         dc.w $FFFE, $FFFE, 1, $FFFF, $FFFF, $FFFF, 0, $FFFE, 0, 0
    32.         dc.w 0, $FFFE, 0, $FFFE, 2, 0, $FFFE, 2, 2, $FFFF, $FFFE
    33.         dc.w $FFFE, 1, 2, 2, $FFFF, 2, 2, 1, 2, $FFFF, $FFFE
    34.         dc.w $FFFE, $FFFE, 1, $FFFF, $FFFF, $FFFF, 0, $FFFE, 0, 0
    35.         dc.w 0, $FFFE, 0, $FFFE, 2, 0, $FFFE, 2, 2, $FFFF, $FFFE
    36.         dc.w $FFFE, 1, 2, 2, $FFFF, 2, 2, 1, 2, $FFFF, $FFFE
    37.         dc.w $FFFE, $FFFE, 1, $FFFF, $FFFF, $FFFF, 0, $FFFE, 0, 0
    38.         dc.w 0, $FFFE, 0, $FFFE, 2, 0, $FFFE, 2, 2, $FFFF, $FFFE
    39.         dc.w $FFFE, 1, 2, 2, $FFFF, 2, 2, 1, 2, $FFFF, $FFFE
    40.         dc.w $FFFE, $FFFE, 1, $FFFF, $FFFF, $FFFF, 0, $FFFE, 0, 0
    41.         dc.w 0, $FFFE, 0, $FFFE, 2, 0, $FFFE, 2, 2, $FFFF, $FFFE
    Also, at least in my hack, it seems to not have as much lag as the other ripple effect does. :words:
     
  9. jman2050

    jman2050

    Teh Sonik Haker Tech Member
    633
    2
    16
    There are several, but one of the big reasons is the method by which it loads objects. I plan to explain that in tutorial form sometime in the near future, as it was applied to Megamix to eliminate most all lag.
     
  10. ICEknight

    ICEknight

    Researcher Researcher
    Please.

    Even if it runs slower than it should, I'd really like to see by myself why that Sonic 1 effect was scrapped.

    EDIT: Also, it looks like the effect in Sonic 3 (only used in Launch Base?) is somewhat different. Perhaps this one could be ported to Sonic 2... and Sonic 1?
     
  11. MathUser

    MathUser

    3rd top wiki contributor Researcher
    2,083
    1
    18
    I'd like to see it, with slowdown removed, but I wouldnt mind seeing it with slowdown either.
     
  12. jman2050

    jman2050

    Teh Sonik Haker Tech Member
    633
    2
    16
    I'm not sure if "removing" slowdown is such a simple matter...
     
  13. Tets

    Tets

    one rude dude Oldbie
    827
    9
    18
    I'm looking forward to this.

    I think the S3K ripple effect looks better in ARZ, although the LZ effect still works well with CPZ.
     
  14. Rika Chou

    Rika Chou

    Tech Member
    5,269
    161
    43
    I'm interested in this. Would probably help my hack out quite a bit.
     
  15. MoDule

    MoDule

    Tech Member
    323
    5
    18
    Procrastinating from writing bug-fix guides
    Ah, thanks. I had the darndest trouble finding it in the rom. I'll need to compare it to the guess I made based on screenshots later.


    Do tell. I was going to ask in the Megamix topic, but your post was already two pages back.


    I'll see to it when I get home.

    Edit: Done.
     
  16. ICEknight

    ICEknight

    Researcher Researcher
    Thanks a lot for uploading it. Now I see what you meant about the slowdown...
     
  17. MoDule

    MoDule

    Tech Member
    323
    5
    18
    Procrastinating from writing bug-fix guides
    Told ja so. It's not as bad when you play as Sonic or Tails alone, but when the game has to run two player objects it gets really bad.
     
  18. Hitaxas

    Hitaxas

    Retro 80's themed Twich streamer Member
    Well, I added this effect to SSD a while back, and I've had no slowdown.


    Yeah, it is. They have a different foreground ripple effect, S3K's being smaller yet more ripples.
     
  19. Overlord

    Overlord

    Now playable in Smash Bros Ultimate Moderator
    18,221
    349
    63
    Berkshire, England
    Learning Cymraeg
    Aw man. You're right, that IS awful. You can see why they went a different route for Sonic 3 & Knuckles.
     
  20. roxahris

    roxahris

    Everyone's a hypocrite. Take my word for it. Member
    1,224
    0
    0
    Doing anything at all
    Considering there is little to no lag in the Nick Arcade version (which has the ripple effect), why not port it from there?
     
Thread Status:
Not open for further replies.