Reflection / 2D Raytracing

Monkey Forums/Monkey Code/Reflection / 2D Raytracing

Markus(Posted 2013) [#1]
line-line intersect with ray reflection


'Reflection / 2D Raytracing

'M.Rauch 15.08.2013

'(Monkey Pro 73b)

Strict

Import mojo

Class Vector

 Field x:Float
 Field y:Float
 Field z:Float
 
 Function Create:Vector(x:Float,y:Float,z:Float)
 
 	Local v:Vector=New Vector
 	
 	v.x=x
 	v.y=y
 	v.z=z
 	
 	Return v
 
 End
 
 Method Length:Float()

	Return Sqrt(x*x + y*y + z*z)

 End
 
 Method Length1:Vector()

	Local v:Vector = Vector.Create(x,y,z)
		
		Local l:Float=v.Length()
		If l>0
		 v.x=v.x / l
		 v.y=v.y / l
		 v.z=v.z / l
		Endif
		
		Return v

 End
 
 Function Sub:Vector(a:Vector , b:Vector)

	'Delta
	
	Local v:Vector=New Vector

	v.x = a.x - b.x
	v.y = a.y - b.y
	v.z = a.z - b.z

	Return v 

 End

 Method Sub:Vector(b:Vector)

	'Delta
	
	Local v:Vector=New Vector

	v.x = x - b.x
	v.y = y - b.y
	v.z = z - b.z

	Return v 

 End

 Method Add:Vector(b:Vector)

	'Plus
	
	Local v:Vector=New Vector

	v.x = x + b.x
	v.y = y + b.y
	v.z = z + b.z

	Return v 

 End

 Method Mul:Vector(b:Vector)

	'*
	
	Local v:Vector=New Vector

	v.x = x * b.x
	v.y = y * b.y
	v.z = z * b.z

	Return v 

 End

 Function Middle:Vector(a:Vector , b:Vector)

	'Mitte
	
	Local m:Vector=New Vector

	m.x = (a.x + b.x) / 2.0
	m.y = (a.y + b.y) / 2.0
	m.z = (a.z + b.z) / 2.0

	Return m 

 End
 
 Method Draw:Void()
 
 	DrawOval(x-4,y-4,8,8)
 
 End

End

Class MyApp Extends App

	Field walllist:List<Line> = New List<Line>

	Method OnCreate:Int()

		Print "Device Screen Size "+DeviceWidth() + " x " + DeviceHeight()
	
		'wenn das bei HTML5 fehlt kein Bild nur weiß
				
		SetUpdateRate 30 'Render & Update identisch
		
		MakeWalls()
		
		Return 1
		
	End
	
	Method MakeWalls:void()

	  	Local wall:Line
	  	wall=New Line
	  	wall.a=Vector.Create(10,DeviceHeight()-10,0)
		wall.b=Vector.Create(10,10,0)
		walllist.AddLast(wall)

	  	wall=New Line
	  	wall.a=Vector.Create(10,10,0)
		wall.b=Vector.Create(DeviceWidth()-10,10,0)
		walllist.AddLast(wall)

	  	wall=New Line
		wall.a=Vector.Create(DeviceWidth()-10,10,0)
	  	wall.b=Vector.Create(DeviceWidth()-10,DeviceHeight()-10,0)
		walllist.AddLast(wall)

	  	wall=New Line
	  	wall.a=Vector.Create(DeviceWidth()-10,DeviceHeight()-10,0)
		wall.b=Vector.Create(10,DeviceHeight()-10,0)
		walllist.AddLast(wall)

		const s:Float=10.0
		For Local w:Float=0 To 360.0-s Step s
			Local p1:Vector=Vector.Create(100.0+Sin(w  )*40,100.0+Cos(w  )*40,0)
			Local p2:Vector=Vector.Create(100.0+Sin(w+s)*40,100.0+Cos(w+s)*40,0)
			wall=Line.Create(p1,p2)
			walllist.AddLast(wall)
		Next

		For Local w:Float=0 To 360.0-s Step s
			Local p1:Vector=Vector.Create(100.0+Sin(w  )*40,300.0+Cos(w  )*40,0)
			Local p2:Vector=Vector.Create(100.0+Sin(w+s)*40,300.0+Cos(w+s)*40,0)
			wall=Line.Create(p1,p2)
			walllist.AddLast(wall)
		Next

	End
	
	Method RenderWalls:void()

	 	'-------------------- paint walls

	 	SetColor 128,128,128
	 	For Local wall:Line=Eachin walllist
		 	wall.DrawNormal(5.0)
		Next
	 	SetColor 255,255,255
	 	For Local wall:Line=Eachin walllist
		 	wall.Draw()	  	  
		Next

	End
	
	Method OnUpdate:Int()

		Return 1

	End

	Method OnRender:Int()

	  	Cls 0,0,0

	  	PushMatrix
	 
	 	RenderWalls()
	 
	 	'-------------------- start ray
			
	 	Local ray:Line=New Line
	 	ray.a=Vector.Create(DeviceWidth()/2,DeviceHeight()/2,0)
	 	ray.b=Vector.Create(TouchX(),TouchY(),0).Sub(ray.a).Length1().Mul(Vector.Create(1000.0,1000.0,1000.0)).Add(ray.a)
	 	
	 	'-------------------- ray trace / Raytracing
	 	 
	 	Ray.Trace(ray,walllist,10) 'with trace depth limit	
	 		
		PopMatrix
	 
	 Return 1

	End

End

Class Ray

	Function Trace:Void(ray:Line,lines:List<Line>,depth:int)
	
		depth=depth-1
		If depth=0 Then Return
		 	
	 	Local l:Float=1000000.0
		Local newray:Line=null
	 	
	 	For Local wall:Line=Eachin lines
	 		
			Local hit:Vector = LineLine.Intersection(ray,wall)
			If hit
				'SetColor 255,0,0
				'hit.Draw()
	
				'surface Normal
				Local n:Vector=wall.Normal()
				
				'Direction
				Local dir:Vector=ray.Direction()
				
				'Reflect Direction
				Local o:Vector=New Vector
				Local op:Float =2.0 * (dir.x*n.x + dir.y*n.y + dir.z*n.z)
				o.x=dir.x - (op * n.x)
				o.y=dir.y - (op * n.y)
				o.z=dir.z - (op * n.z)
				
				If hit.Sub(ray.a).Length() <l Then 'find the nearest
					l=hit.Sub(ray.a).Length()
				
					ray.b=hit 'shorten the ray
				
					'Reflected Line
					'Start from End To New Point
					newray=New Line
					newray.a=Vector.Create(hit.x+n.x*0.5,hit.y+n.y*0.5,hit.z+n.z*0.5) 'else we hit direct the same wall, we start a little bit up ( sonst trift man die gleiche Mauer wieder)
					newray.b=Vector.Create(hit.x+o.x*1000.0,hit.y+o.y*1000.0,hit.z+o.z*1000.0) 'länge 1000 weiter
					
				Endif
											
			 Endif
		 
		 Next
		 
		 SetColor 255,255,0
	 	 ray.Draw()

		 'do the same again
		 If newray<>Null
						newray.a.Draw()
		 				Ray.Trace(newray,lines,depth)
		 Endif		 
	
	End
	
End

Class Line

	Field a:Vector
	Field b:Vector
	
	Function Create:Line(a:Vector,b:Vector)
	
		Local l:Line= New Line
		l.a=a
		l.b=b
	
		Return l 
	
	end
	
	Method Draw:Void()
		DrawLine(a.x , a.y , b.x , b.y )
	End	
	
	Method DrawNormal:Void(l:Float)

		Local m:Vector=Vector.Middle(a,b)
		Local n:Vector=Normal()
		
		DrawLine(m.x , m.y , m.x + n.x*l , m.y + n.y*l )

	End

	Method Normal:Vector()
	
		'Normale der Linie!
	
		'define dx=x2-x1 And dy=y2-y1		
		'the normals are (-dy, dx) And (dy, -dx)
		
		Local d:Vector=b.Sub(a).Length1()
		
		Return Vector.Create(-d.y,d.x,0)
	
	End
	
	Method Direction:Vector()
				
		Return b.Sub(a).Length1()

	End
	
	Method Length:Float()
	
		'Länge Linie

		Return b.Sub(a).Length()

	End
	
End

Class LineLine

	Function Intersection:Vector(line1:Line,line2:Line)     
		
        Local p:Vector=Null

		Local x1:Float=line1.a.x
		Local y1:Float=line1.a.y
		Local x2:Float=line1.b.x
		Local y2:Float=line1.b.y

		Local x3:Float=line2.a.x
		Local y3:Float=line2.a.y
		Local x4:Float=line2.b.x
		Local y4:Float=line2.b.y

		Local bx:Float = x2 - x1
		Local by:Float = y2 - y1

		Local dx:Float = x4 - x3
		Local dy:Float = y4 - y3
		
		Local b_dot_d_perp:Float = bx * dy - by * dx
		If b_dot_d_perp = 0.0 Then Return Null
  
		Local cx:Float = x3 - x1
	  	Local cy:Float = y3 - y1
	  	
	  	Local t:Float = (cx * dy - cy * dx) / b_dot_d_perp
	  	If (t < 0.0) Or (t > 1.0) Then	Return Null
	  	
	  	Local u:Float = (cx * by - cy * bx) / b_dot_d_perp
	  	If (u < 0.0) Or (u > 1.0) Then Return Null
	  	
  		p=Vector.Create(x1+t*bx, y1+t*by,0)
  
		Return p
		
	End

End

Function Main:Int()

	Local myApp:MyApp = New MyApp
	
	Return 1
	
End	




DruggedBunny(Posted 2013) [#2]
Nice!


grovey(Posted 2013) [#3]
Can you explain exactly what this does?

Local b_dot_d_perp:Float = bx * dy - by * dx


Dot product is in the form (a.x * b.x) + (a.y * b.y) so I am trying to understand the equation above and how it relates to the intersection function.

Thanks.


Jesse(Posted 2013) [#4]
its the perpendicular dot product. it is the vector multiplied by the left normal of another vector. I normally use it to calculate the distance from a point to a vector. or the distance of the end of one vector to the other vector. the original format is (bx * dy) + (by *- dx). the dy,-dx is the perpendicular vector to the vector composed of dx,dy


ElectricBoogaloo(Posted 2013) [#5]
.... And therein lies one of the many reasons I've never truly tackled anything beyond basic 2D stuff!.. I can understand about 3% of your reply!!


.. Vector's the crocodile from Knuckles Chaotix, right?


grovey(Posted 2013) [#6]
Thanks Jesse, I have been reading up on this now, and will study it more.


Jesse(Posted 2013) [#7]
Ha ha ha EB!

What gets most people is the terminology. makes you think it's something really complicated but it actually is not. it took me a couple of weeks to understand it and figure out the foundation for my pool game:

http://www.monkeycoder.co.nz/Community/topics.php?forum=1081&app_id=81

what helped me the most is this page:

http://www.tonypa.pri.ee/vectors/start.html

it is not 100% accurate but most of what the author talks about is true and exact.


grovey(Posted 2013) [#8]
Yes I saw that article too,

This one has been helping me as well.

http://hub.tutsplus.com/tutorials/predicting-collision-points-with-math-in-as3--active-11218


zoqfotpik(Posted 2013) [#9]
This is great work.

You could use this to write a raycasting engine. Doom-like engines are probably played out but Comanche-like pseudovoxel terrain flying engines can work with basically the same principle and might look pretty good on a tablet.