Type TPick

	' EntityPickMode in TEntity

	Const EPSILON=.0001
	
	Global ent_list:TList=New TList ' list containing pickable entities

	Global picked_x:Float,picked_y:Float,picked_z:Float
	Global picked_nx:Float,picked_ny:Float,picked_nz:Float
	Global picked_time:Float
	Global picked_ent:TEntity
	Global picked_surface:TSurface
	Global picked_triangle

	Function CameraPick:TEntity(cam:TCamera,vx:Float,vy:Float)

		vy:Float=TGlobal.height-vy

		Local px:Double
		Local py:Double
		Local pz:Double

        gluUnProject(vx,vy,0.0,cam.mod_mat,cam.proj_mat,cam.viewport,Varptr px,Varptr py,Varptr pz)

		Local x:Float=Float(px)
		Local y:Float=Float(py)
		Local z:Float=-Float(pz)
		
        gluUnProject(vx,vy,1.0,cam.mod_mat,cam.proj_mat,cam.viewport,Varptr px,Varptr py,Varptr pz)

		Local x2:Float=Float(px)
		Local y2:Float=Float(py)
		Local z2:Float=-Float(pz)

		Return Pick(x:Float,y:Float,z:Float,x2:Float,y2:Float,z2:Float)
	
	End Function

	Function EntityPick:TEntity(ent:TEntity,range:Float)

		TEntity.TFormPoint(0.0,0.0,0.0,ent,Null)
		Local x:Float=TEntity.TFormedX()
		Local y:Float=TEntity.TFormedY()
		Local z:Float=TEntity.TFormedZ()
		
		TEntity.TFormPoint(0.0,0.0,range:Float,ent,Null)
		Local x2:Float=TEntity.TFormedX()
		Local y2:Float=TEntity.TFormedY()
		Local z2:Float=TEntity.TFormedZ()
		
		Return Pick(x,y,z,x2,y2,z2)

	End Function

	Function LinePick:TEntity(x:Float,y:Float,z:Float,dx:Float,dy:Float,dz:Float,radius:Float=0.0)

		Return Pick(x,y,z,x+dx,y+dy,z+dz,radius)

	End Function

	Function EntityVisible(src_ent:TEntity,dest_ent:TEntity)

		' get pick values
		
		Local px:Float=picked_x
		Local py:Float=picked_y
		Local pz:Float=picked_z
		Local pnx:Float=picked_nx
		Local pny:Float=picked_ny
		Local pnz:Float=picked_nz
		Local ptime:Float=picked_time
		Local pent:TEntity=picked_ent
		Local psurf:TSurface=picked_surface
		Local ptri=picked_triangle

		' perform line pick

		Local ax:Float=src_ent.EntityX(True)
		Local ay:Float=src_ent.EntityY(True)
		Local az:Float=src_ent.EntityZ(True)
		
		Local bx:Float=dest_ent.EntityX(True)
		Local by:Float=dest_ent.EntityY(True)
		Local bz:Float=dest_ent.EntityZ(True)

		Local pick:TEntity=Pick(ax,ay,az,bx,by,bz)
		
		' restore pick values
		
		picked_x=px
		picked_y=py
		picked_z=pz
		picked_nx=pnx
		picked_ny=pny
		picked_nz=pnz
		picked_time=ptime
		picked_ent=pent
		picked_surface=psurf
		picked_triangle=ptri
		
		If pick<>Null
		
			Return False
			
		EndIf
		
		Return True
		
	End Function

	Function PickedX:Float()
		Return picked_x
	End Function
	
	Function PickedY:Float()
		Return picked_y
	End Function
	
	Function PickedZ:Float()
		Return picked_z
	End Function
	
	Function PickedNX:Float()
		Return picked_nx
	End Function
	
	Function PickedNY:Float()
		Return picked_ny
	End Function
	
	Function PickedNZ:Float()
		Return picked_nz
	End Function
	
	Function PickedTime:Float()
		Return picked_time
	End Function
	
	Function PickedEntity:TEntity()
		Return picked_ent
	End Function
	
	Function PickedSurface:TSurface()
		Return picked_surface
	End Function
	
	Function PickedTriangle()
		Return picked_triangle
	End Function

	' requires two absolute positional values
	Function Pick:TEntity(ax:Float,ay:Float,az:Float,bx:Float,by:Float,bz:Float,radius:Float=0.0)

		picked_ent=Null
		picked_time=1.0
	
		Local t_min:Float=1.0
		
		For Local ent:TEntity=EachIn ent_list
		
			If ent.pick_mode=0 Or ent.parent_hidden=True Or ent.hidden=True Then Continue
			
			If ent.pick_mode=1 ' sphere
				PickSphere(ent,ax,ay,az,bx,by,bz,radius,t_min)
			EndIf

			If ent.pick_mode=2
				PickMesh(ent,ax,ay,az,bx,by,bz,radius,t_min)
			EndIf

			If ent.pick_mode=3
				PickBox(ent,ax,ay,az,bx,by,bz,radius,t_min)
			EndIf

		Next
		
		Return picked_ent

	End Function
	
	Function PickSphere(ent:TEntity,ax:Float,ay:Float,az:Float,bx:Float,by:Float,bz:Float,radius:Float=0.0,t_min:Float Var)
	
		Local t:Float=0.0
		Local px:Float,py:Float,pz:Float
	
		Local sx:Float=ent.EntityX:Float(True)
		Local sy:Float=ent.EntityY:Float(True)
		Local sz:Float=ent.EntityZ:Float(True)
		Local sr:Float=ent.radius_x:Float
	
		Local pick=IntersectSegmentSphere(ax,ay,az,bx,by,bz,sx,sy,sz,sr,t,px,py,pz)
	
		If pick And t<=t_min
		
			t_min=t
		
			picked_x=px
			picked_y=py
			picked_z=pz
			
			Local x:Float=picked_x-sx
			Local y:Float=picked_y-sy
			Local z:Float=picked_z+sz
			Local uv:Float=Sqr(x*x+y*y+z*z)
			
			picked_nx=x/uv
			picked_ny=y/uv
			picked_nz=z/uv
			
			picked_time=t
			
			picked_ent=ent
			'picked_surface=Null ' don't overwrite picked_surface, same as B3D
			'picked_triangle=0 ' don't overwrite picked_triangle, same as B3D
	
		EndIf
						
	End Function
	
	Function PickMesh(ent:TEntity,ax:Float,ay:Float,az:Float,bx:Float,by:Float,bz:Float,radius:Float=0.0,t_min:Float Var)
	
		If TMesh(ent)<>Null
		
			TMesh(ent).TreeCheck() ' create collision tree for mesh if necessary
			
			' transform ray from world space to object space
			
			TEntity.TFormPoint ax,ay,az,Null,ent
			ax=TEntity.tformed_x
			ay=TEntity.tformed_y
			az=TEntity.tformed_z
			
			TEntity.TFormPoint bx,by,bz,Null,ent
			bx=TEntity.tformed_x
			by=TEntity.tformed_y
			bz=TEntity.tformed_z

			'Local t_min2:Float=1.0
				
			Local polys:TPolygon[]=TMesh(ent).col_tree.TreePolysLine(ax,ay,az,bx,by,bz) ' get polys from collision tree that are potentially pickable
	
			Local t:Float=0.0
			Local px:Float,py:Float,pz:Float

			Local cx1:Float,cy1:Float,cz1:Float
			Local cx2:Float,cy2:Float,cz2:Float
			Local cx3:Float,cy3:Float,cz3:Float
					
			Local u:Float,v:Float,w:Float
		
			' check tri-ray collisions
			
			Local poly_picked=False
			
			For Local poly:TPolygon=EachIn polys
		
				If poly.no_verts=3
		
					poly.GetVertexNew(0,cx1,cy1,cz1)
					poly.GetVertexNew(1,cx2,cy2,cz2)
					poly.GetVertexNew(2,cx3,cy3,cz3)
					
					Local pick
					If USE_C
						pick=C_IntersectSegmentTriangle(ax,ay,az,bx,by,bz,cx1,cy1,cz1,cx2,cy2,cz2,cx3,cy3,cz3,u,v,w,t)
					Else
						pick=IntersectSegmentTriangle(ax,ay,az,bx,by,bz,cx1,cy1,cz1,cx2,cy2,cz2,cx3,cy3,cz3,u,v,w,t)
					EndIf
		
					If pick And t<=t_min
					
						poly_picked=True
					
						t_min=t
					
						' convert barycentric coordinates to world coordinates
						px=(cx1*u)+(cx2*v)+(cx3*w)
						py=(cy1*u)+(cy2*v)+(cy3*w)
						pz=(cz1*u)+(cz2*v)+(cz3*w)
		
						picked_x=px
						picked_y=py
						picked_z=pz
						
						picked_nx=poly.surf.TriangleNX(poly.tri)
						picked_ny=poly.surf.TriangleNY(poly.tri)
						picked_nz=poly.surf.TriangleNZ(poly.tri)
						
						TEntity.TFormPoint(picked_x,picked_y,picked_z,ent,Null)
						picked_x=TEntity.tformed_x
						picked_y=TEntity.tformed_y
						picked_z=TEntity.tformed_z
						
						TEntity.TFormNormal(picked_nx,picked_ny,picked_nz,ent,Null)
						picked_nx=TEntity.tformed_x
						picked_ny=TEntity.tformed_y
						picked_nz=TEntity.tformed_z
						
						picked_time=t
						
						picked_ent=ent
						picked_surface=poly.surf
						picked_triangle=poly.tri
		
					EndIf
				
				EndIf
			
			Next
		
		EndIf
		
	End Function
	
	Function PickBox(ent:TEntity,ax:Float,ay:Float,az:Float,bx:Float,by:Float,bz:Float,radius:Float=0.0,t_min:Float Var)

		Local t:Float=0.0
		Local px:Float,py:Float,pz:Float
	
		Local x:Float=ent.box_x + ent.EntityX(True)
		Local y:Float=ent.box_y + ent.EntityY(True)
		Local z:Float=ent.box_z + ent.EntityZ(True)
		Local w:Float=ent.box_w
		Local h:Float=ent.box_h
		Local d:Float=ent.box_d 
	
		Local pick=IntersectSegmentAABB(ax,ay,az,bx,by,bz,x,y,z,w,h,d,t,px,py,pz)
		
		If pick And t<=t_min
		
			t_min=t
		
			picked_x=px
			picked_y=py
			picked_z=pz
			
			Local nx:Float,ny:Float,nz:Float
			
			Local a:Float[6]
			
			a[0]=Abs(x-picked_x)
			a[1]=Abs(x+w-picked_x)
			a[2]=Abs(y-picked_y)
			a[3]=Abs(y+h-picked_y)
			a[4]=Abs(z-picked_z)
			a[5]=Abs(z+d-picked_z)

			If a[0]<a[1] And a[0]<a[2] And a[0]<a[3] And a[0]<a[4] And a[0]<a[5]
				nx=-1.0
			Else
				If a[1]<a[2] And a[1]<a[3] And a[1]<a[4] And a[1]<a[5]
					nx=1.0
				Else
					If a[2]<a[3] And a[2]<a[4] And a[2]<a[5]
						ny=-1.0
					Else
						If a[3]<a[4] And a[3]<a[5]
							ny=1.0
						Else
							If a[4]<a[5]
								nz=-1.0
							Else
								nz=1.0
							EndIf
						EndIf
					EndIf
				EndIf		
			EndIf
			
			picked_nx=nx
			picked_ny=ny
			picked_nz=nz
			
			picked_time=t
			
			picked_ent=ent
			'picked_surface=Null ' don't overwrite picked_surface, same as B3D
			'picked_triangle=0 ' don't overwrite picked_triangle, same as B3D
			
		EndIf
	
	End Function

	Function IntersectSegmentSphere(px:Float,py:Float,pz:Float,qx:Float,qy:Float,qz:Float,sx:Float,sy:Float,sz:Float,sr:Float,t:Float Var,qx2:Float Var,qy2:Float Var,qz2:Float Var)
	
		Local p:TVector=TVector.Create(px,py,pz)
		Local q:TVector=TVector.Create(qx,qy,qz)
		Local sc:TVector=TVector.Create(sx,sy,sz)
		Local q2:TVector
	
		Local pq:TVector=New TVector
		pq=q.Subtract(p)
	
		Local d:TVector=TVector.Create(pq.x,pq.y,pq.z)
		d.Normalize()
	
		Local m:TVector=New TVector
		m=p.Subtract(sc)
		Local b:Float=m.Dot(d)
		Local c:Float=m.Dot(m) - (sr*sr)
		
		If c:Float>0.0 And b>0.0 Return 0
		Local discr:Float=(b*b)-c
		
		If discr:Float<0.0 Return 0
	
		t=-b-Sqr(discr)
		
		If(t<0.0) Then t=0.0
		q2=p.Add(d.Multiply(t))
		
		qx2=q2.x;qy2=q2.y;qz2=q2.z
		
		' segment check
		If t>pq.Length()
			Return 0
		Else
			t=t/pq.Length()
		EndIf
		
		Return 1
	
	End Function

	Function IntersectSegmentTriangle(	px:Float,py:Float,pz:Float,qx:Float,qy:Float,qz:Float,..
										ax:Float,ay:Float,az:Float,bx:Float,by:Float,bz:Float,cx:Float,cy:Float,cz:Float,..
										u:Float Var,v:Float Var,w:Float Var,t:Float Var)

		Local p:TVector=TVector.Create(px,py,pz)
		Local q:TVector=TVector.Create(qx,qy,qz)
		Local a:TVector=TVector.Create(ax,ay,az)
		Local b:TVector=TVector.Create(bx,by,bz)
		Local c:TVector=TVector.Create(cx,cy,cz)

		Local ab:TVector=New TVector
		Local ac:TVector=New TVector
		Local qp:TVector=New TVector
		
		ab=b.Subtract(a)
		ac=c.Subtract(a)
		qp=p.Subtract(q)
		
		Local n:TVector=New TVector
		n=ab.Cross(ac)
		
		Local d:Float=qp.Dot(n)
		If d:Float<=0.0 Return 0
		
		Local ap:TVector=New TVector
		ap=p.Subtract(a)
		t:Float=ap.Dot(n)
		If t:Float<0.0 Return 0
		If t:Float>d:Float Return 0
		
		Local e:TVector=New TVector
		e=qp.Cross(ap)
		v:Float=ac.Dot(e)
		If (v:Float<0.0 Or v:Float>d:Float) Return 0
		w:Float=-ab.Dot(e)
		If (w:Float<0.0 Or v:Float+w:Float>d:Float) Return 0
	
		Local ood:Float=1.0/d:Float
		t:Float:*ood
		v:Float:*ood
		w:Float:*ood
		u:Float=1.0-v:Float-w:Float
			
		Return 1		
	
	End Function

	Function IntersectSegmentAABB(px:Float,py:Float,pz:Float,qx:Float,qy:Float,qz:Float,x:Float,y:Float,z:Float,w:Float,h:Float,d:Float,tmin:Float Var,qx2:Float Var,qy2:Float Var,qz2:Float Var)

		Local p:TVector=TVector.Create(px,py,pz)
		Local q:TVector=TVector.Create(qx,qy,qz)
		Local q2:TVector

		tmin=0.0

		Local pq:TVector=New TVector
		pq=q.Subtract(p)
		Local tmax:Float=pq.Length() ' segment distance

		Local d1:TVector=TVector.Create(pq.x,pq.y,pq.z)
		d1.Normalize()
		
		Local amin:Float[]=[x,y,z]
		Local amax:Float[]=[x+w,y+h,z+d]
		
		Local p2:Float[]=[p.x,p.y,p.z]
		Local d2:Float[]=[d1.x,d1.y,d1.z]
		
		For Local i=0 To 2
		
			If Abs(d2[i])<EPSILON
			
				If p2[i]<amin[i] Or p2[i]>amax[i] Then Return 0
			
			Else
			
				Local ood:Float=1.0/d2[i]
				Local t1:Float=(amin[i]-p2[i])*ood
				Local t2:Float=(amax[i]-p2[i])*ood
				
				If t1>t2
					Local swap:Float=t1
					t1=t2
					t2=swap
				EndIf
				
				If t1>tmin Then tmin=t1
				If t2<tmax Then tmax=t2
				
				If tmin>tmax Then Return 0
			
			EndIf 
		
		Next

		q2=p.Add(d1.Multiply(tmin))
	
		qx2=q2.x;qy2=q2.y;qz2=q2.z
	
		tmin=tmin/pq.Length()
		
		Return 1
	
	End Function
	
End Type
