Legend of Zelda: LTTP Effect?

BlitzMax Forums/BlitzMax Programming/Legend of Zelda: LTTP Effect?

Ked(Posted 2009) [#1]
Would it be possible to do the "wavy" full screen effect in BlitzMax that the Legend of Zelda: LTTP executed when the magic mirror was used? If so, does anyone have any code samples?

Thanks!


Jesse(Posted 2009) [#2]
I have some code. I don't know if it's exactly what you need. it manipulates a pixmap. you can download it from my signature link look for it under demos. it's called pixmap distortion. you would need to figure out how to addopt it to your own screen size.


jkrankie(Posted 2009) [#3]
Function makeShip:TImage[] (inImage:timage,arr:timage[])
	
        SetImageHandle inimage, 0, 0
	DrawImage inImage, 0, 0
	For Local i:Int = 0 To arr.count()-1
		arr[i] = CreateImage(ImageWidth(inImage) , 1, 1, DYNAMICIMAGE | MASKEDIMAGE | FILTEREDIMAGE)
		GrabImage (arr[i] , 0, i) 
	Next
	Return arr 
End Function


I use this code in one of my WIP games to make a particular enemy, however theres no reason you couldn't use it to make a wavy background. Send the function an image and an image array. You'll probably want to set the length of the array to the images height by the looks of things.

You could use grabimage to get an image of the screen. you can also use setscale etc. before the grabimage if you don't need it to be as detailed looking.

Cheers
Charlie


Ked(Posted 2009) [#4]
@Jesse: Thanks, but that's not the effect I'm looking for. :)
@jkrankie: Do you have an example for that piece of code? I have no idea how to use it.

IMO, it would be pretty sad if BlitzMax cannot duplicate this effect. I mean, the SNES did it, shouldn't BlitzMax be able to?


Gabriel(Posted 2009) [#5]
IMO, it would be pretty sad if BlitzMax cannot duplicate this effect. I mean, the SNES did it, shouldn't BlitzMax be able to?

Well that's kind of hard to say for those of us (I'm guessing the majority) who don't even know the effect you refer to. And I played that game a lot. I do know that the SNES had some funky video hardware that almost certainly did things pretty differently than a modern PC vidcard, so it might take some creative thinking.

A Youtube link to the effect would be most handy though.


Ked(Posted 2009) [#6]
Well that's kind of hard to say for those of us (I'm guessing the majority) who don't even know the effect you refer to. And I played that game a lot.

?!

A Youtube link to the effect would be most handy though.

YouTube. It starts at 8:08.


Jesse(Posted 2009) [#7]
it looks like it might be possible with a light blend effect and continually drawing two complementary images on top of each other and moving away from each other without erasing them but that's just a guess.


Gabriel(Posted 2009) [#8]
Oh the sine wave distortion? You probably don't want to hear this but this sort of effect is ridiculously easy (and cheap) when done in a shader.


sswift(Posted 2009) [#9]
You can duplicate this effect in Max, but depending on how you do it, it might not be super fast. The SNES had this as a hardware effect. Max is a 3D language, and 3D cards aren't designed to do effects like that. At least, they weren't. As someone else mentioned they have shader effects now that can do that stuff, but I don't know how to write shaders or use them in BlitzMax and such effects won't work on all cards out there.

So here's how to do it as a software effect that is cross platform and will work on any hardware. But for very high resolutions, it might be a bit slow:

Copy the screen to an image before displaying the result of your last set of draw operations. Blit this image, one horizontal line at a time, back to the screen. Offset each of these lines to the left or right as you blit it, using SIN to calculate their position.

The math to do this is kinda complicated. But it's something like this:

X# = MaxOffset# * Sin(Y#/MaxY# * 360.0*Waves# + Scroll#*360.0)

Where MaxOffset# is how big the waves should be, Waves# is the number of waves on screen, Y# is the Y coordinate of the scanline you're blitting, X# is the new start position to blit it at, MaxY# is the height of the screen in pixels-1, and Scroll# is an offset which you can increment to scroll the waves down the screen, with 1 being the height of one wave on the screen.

To make the effect look nice, you will want to not only incremenet Scroll# by a little bit each frame, (a little bit being something like 0.05) but also start MaxOffset# at 0 and increment it over time to say, 16, and then decrement it back to 0 again to complete the effect. This will make the waves grow and then shrink again, while also scrolling down the screen. And you'll want something like 2 waves on the screen to get the effect I'm seeing in that video. You can also adjust the number of waves over time too to get an interesting effect.


Armitage 1982(Posted 2009) [#10]
@Gabriel
Oh the sine wave distortion? You probably don't want to hear this but this sort of effect is ridiculously easy (and cheap) when done in a shader.

Using a shader is probably a good idea.
I'm wondering how to add a shader effect in the blitzMax OpenGl driver environment.
Would be very nice to add heat effect or fast screen effect in 2D platform game.


ImaginaryHuman(Posted 2009) [#11]
SSwift that's how I was going to suggest doing it - it should be fast enough to be smooth on a low-mid-range gpu.


nawi(Posted 2009) [#12]
SuperStrict
Graphics 800,600

Const MaxOffSet# = 7.0,Waves# = 4.0
Local Scroll# = 0.0

'load a 800x600 pic, which is the "screen"
Local screenshot:TImage = LoadImage("pic.jpg") 
DrawImage screenshot,0,0
screenshot = Null

'create 600 images, each 1 pixel high and 800 pixels wide
Local screenarray:TImage[GraphicsHeight()]
For Local y:Int = 0 To GraphicsHeight()-1
	screenarray[y] = CreateImage(GraphicsWidth(),1)
	GrabImage(screenarray[y],0,y)
Next

Repeat
	'DrawImage screenshot,0,0
	Scroll# = Scroll# + 0.01
	For Local y:Int = 0 To GraphicsHeight()-1
		If y Mod 2 = 0 Then
			DrawImage screenarray[y],MaxOffset# * Sin(Float(y)/(GraphicsHeight()-1) * 360.0*Waves# + Scroll#*360.0),y
		Else
			DrawImage screenarray[y],-MaxOffset# * Sin(Float(y)/(GraphicsHeight()-1) * 360.0*Waves# + Scroll#*360.0),y
		EndIf
	Next

	Flip
	Cls
Until KeyHit(KEY_ESCAPE)

This is to get started, based on sswift's ideas.


sswift(Posted 2009) [#13]
That looks great. I like the scanline effect. :-)

I wanted to see how it would look with a straight alpha blit though:

SuperStrict
Graphics 800,600

SetBlend(ALPHABLEND)

Const MaxOffSet# = 7.0,Waves# = 4.0
Local Scroll# = 0.0

'load a 800x600 pic, which is the "screen"
Local screenshot:TImage = LoadImage("pic.jpg") 
DrawImage screenshot,0,0
screenshot = Null

'create 600 images, each 1 pixel high and 800 pixels wide
Local screenarray:TImage[GraphicsHeight()]
For Local y:Int = 0 To GraphicsHeight()-1
	screenarray[y] = CreateImage(GraphicsWidth(),1)
	GrabImage(screenarray[y],0,y)
Next

Repeat
	'DrawImage screenshot,0,0
	Scroll# = Scroll# + 0.01
	For Local y:Int = 0 To GraphicsHeight()-1
		SetAlpha(1)
		DrawImage screenarray[y],MaxOffset# * Sin(Float(y)/(GraphicsHeight()-1) * 360.0*Waves# + Scroll#*360.0),y
		SetAlpha(0.5)
		DrawImage screenarray[y],-MaxOffset# * Sin(Float(y)/(GraphicsHeight()-1) * 360.0*Waves# + Scroll#*360.0),y
	Next

	Flip
	Cls
Until KeyHit(KEY_ESCAPE)


I like the scanlines better, though I think if I was using this in a modern game I'd go with the alpha cause scanlines are retro.


sswift(Posted 2009) [#14]
Note that Nawi's code doesn't grab the image on the screen and split it into scanlines. It splits a loaded image before it starts. That means the effect may be a bit slower in practice, because the slow bit is grabbing the image off the screen which 3D cards tend to suck at.


jkrankie(Posted 2009) [#15]
It's not that confusing. send the function a Timage (this is any TImage), and an array of type Timage. The size of the array is the number of slices you want to make, presumable in your case it will the the same as the graphics height.

anyway, this code should demonstrate how to use the function.

Cheers
Charlie

SuperStrict

Graphics 800,600,0,60

'create an array of images
Local imgArray:timage[600]

'image to send to the function
Local backgroundImage:timage

'draw checkerboard for background
For Local i:Int=0 To 7
	For Local o:Int=0 To 5
		If i Mod 2=0
			If o Mod 2=0
				SetColor 255,255,255;	 
			Else
				SetColor 0,0,0; 
			EndIf
		Else
			If o Mod 2=0
				SetColor 0,0,0
			Else
				SetColor 255,255,255
			EndIf
		EndIf
		DrawRect i*100,o*100,100,100
	Next
Next

'copy the checkerboard to a Timage
backgroundImage=CreateImage(800,600,1,DYNAMICIMAGE | MASKEDIMAGE | FILTEREDIMAGE)
GrabImage backgroundImage,0,0

Flip
Cls


'cut the image up into 600 lines
imgArray=makeship(backgroundimage,imgarray)


'wobble the lines around :)
Local angle:Int=0
While Not KeyHit(key_escape)
	angle:+1
	For Local i:Int=0 To 599
		Local r:Float=10*Sin(angle+i)
		DrawImage imgArray[i],r,i
	Next 
	Flip
	Cls
Wend



Function makeShip:TImage[] (inImage:timage,arr:timage[])
	
        SetImageHandle inimage, 0, 0
	DrawImage inImage, 0, 0
	For Local i:Int = 0 To arr.length-1
		arr[i] = CreateImage(ImageWidth(inImage) , 1, 1, DYNAMICIMAGE | MASKEDIMAGE | FILTEREDIMAGE)
		GrabImage (arr[i] , 0, i) 
	Next
	Return arr 
End Function




Ked(Posted 2009) [#16]
@sswift & @nawi:
Both of those examples do not work for me. Here is what I'm getting:

EDIT: Nevermind! I was using a modified D3D7Max2DDriver that I modded with a "tweak" I guess doesn't work anymore! :)

Effect is fantastic! Exactly what I was looking for! Thanks!


Guy Fawkes(Posted 2009) [#17]
can someone plz fix this so it works with blitz3d?


Guy Fawkes(Posted 2009) [#18]
yea.

heres the blitz3d version

fix it up how u wish.

code:

;SuperStrict

Graphics 800,600,0,2

;'create an array of images
;Local imgArray:timage[600]

Dim imgArray(600)

;'image To send To the Function
;Local backgroundImage:timage

Global backgroundImage

;'draw checkerboard For background
For i = 0 To 7
;For Local i:Int=0 To 7
 For o = 0 To 5
;	For Local o:Int=0 To 5
		If i Mod 2=0
			If o Mod 2=0
				Color 255,255,255
			Else
				Color 0,0,0
			EndIf
		Else
			If o Mod 2=0
				Color 0,0,0
			Else
				Color 255,255,255
			EndIf
		EndIf
		Rect i*100,o*100,100,100
	Next
Next

;'copy the checkerboard To a Timage
backgroundImage=CreateImage(800,600,1)
GrabImage backgroundImage,0,0

Flip
Cls


;'cut the image up into 600 lines
imgArray1=makeship(backgroundimage,imgarray1)

;'wobble the lines around :)
;Local angle:Int=0
Global angle=0
While Not KeyHit(key_escape)
	angle=angle+1
For i = 0 To 599
;	For Local i:Int=0 To 599
		r#=(10*Sin(angle)+i)
;		Local r:Float=10*Sin(angle+i)
		DrawImage imgArray1,r,i
Next 
	Flip
	Cls
Wend



Function makeShip(inImage,arr)
	
        HandleImage inimage, 0, 0
	DrawImage inImage, 0, 0
	For i=0 To arr Step -1
;	For Local i:Int = 0 To arr.length-1
         arr = CreateImage(ImageWidth(inImage), 1, 1)
;		arr[i] = CreateImage(ImageWidth(inImage) , 1, 1, DYNAMICIMAGE | MASKEDIMAGE | FILTEREDIMAGE)
		GrabImage (arr, 0, i) 
	Next
	Return arr 
End Function