' Updates:
' 01/02/07 - Pick added
' 01/02/07 - CameraPick, EntityPick, LinePick, EntityVisible

Type TPick

	' EntityPickMode in TEntity

	Const EPSILON=.0001

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

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

		vy#=TGlobal.height-vy#

		Local px!
		Local py!
		Local pz!

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

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

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

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

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

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

	End Function

	Function LinePick:TEntity(x#,y#,z#,dx#,dy#,dz#,radius#=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#=picked_x#
		Local py#=picked_y#
		Local pz#=picked_z#
		Local pnx#=picked_nx#
		Local pny#=picked_ny#
		Local pnz#=picked_nz#
		Local ptime#=picked_time#
		Local pent:TEntity=picked_ent
		Local psurf:TSurface=picked_surface
		Local ptri=picked_triangle

		' perform line pick

		Local a:TVector=TVector.Create(src_ent.EntityX#(True),src_ent.EntityY#(True),src_ent.EntityZ#(True))
		Local b:TVector=TVector.Create(dest_ent.EntityX#(True),dest_ent.EntityY#(True),dest_ent.EntityZ#(True))

		Local pick:TEntity=Pick(a.x,a.y,a.z,b.x,b.y,b.z)
		
		' 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#()
		Return picked_x#
	End Function
	
	Function PickedY#()
		Return picked_y#
	End Function
	
	Function PickedZ#()
		Return picked_z#
	End Function
	
	Function PickedNX#()
		Return picked_nx#
	End Function
	
	Function PickedNY#()
		Return picked_ny#
	End Function
	
	Function PickedNZ#()
		Return picked_nz#
	End Function
	
	Function PickedTime#()
		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

	' MiniB3D functions
	
	' Internal - not recommended for general use
	
	' requires two absolute positional values
	Function Pick:TEntity(x#,y#,z#,x2#,y2#,z2#,radius#=0.0)

		picked_ent=Null
		picked_time#=1.0

		Local ent:TEntity

		Local a:TVector=TVector.Create(x#,y#,z#)
		Local b:TVector=TVector.Create(x2#,y2#,z2#)
		Local sp:TVector=TVector.Create(0.0,0.0,0.0)
		Local sr#=0.0
		Local t#=0.0
		Local p:TVector=TVector.Create(0.0,0.0,0.0)
		
		Local c1:TVector=TVector.Create(0.0,0.0,0.0)
		Local c2:TVector=TVector.Create(0.0,0.0,0.0)
		Local c3:TVector=TVector.Create(0.0,0.0,0.0)
		Local u#
		Local v#
		Local w#
		
		Local t_min#=1.0
		
		For ent:TEntity=EachIn TEntity.entity_list
		
			If ent.pick_mode<>0 And ent.EntityHidden()=False
			
				If ent.pick_mode=1
				
					sp.x#=ent.EntityX#(True)
					sp.y#=ent.EntityY#(True)
					sp.z#=ent.EntityZ#(True)
					sr#=ent.radius_x#
				
					Local pick=IntersectSegmentSphere(a,b,sp,sr#,t#,p)

					If pick And t<=t_min
					
						t_min=t
					
						picked_x#=p.x#
						picked_y#=p.y#
						picked_z#=p.z#
						
						Local x#=picked_x#-sp.x#
						Local y#=picked_y#-sp.y#
						Local z#=picked_z#+sp.z#
						Local uv#=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
				
				EndIf
				
				If ent.pick_mode=2
	
					If TMesh(ent)<>Null
					
						' mesh has octree
						
						If TMesh(ent).octree<>Null
						
							' transform ray from world space to object space
							
							Local at:TVector=TVector.Create(0,0,0)
							Local bt:TVector=TVector.Create(0,0,0)
	
							TFormPoint a.x,a.y,a.z,Null,ent
							at.x=TFormedX()
							at.y=TFormedY()
							at.z=TFormedZ()
							
							TFormPoint b.x,b.y,b.z,Null,ent
							bt.x=TFormedX()
							bt.y=TFormedY()
							bt.z=TFormedZ()

							Local t_min2#=1.0
							
							TMesh(ent).octree.OctPick(at,bt,TMesh(ent),t_min#,t_min2#)
						
						' mesh doesn't have octree
						
						Else
						
							' perform pick test with bounding sphere before picking tris
							
							Local s:TSphere=ent.BoundingSphere()

							Local pick=IntersectSegmentSphere(a,b,s.c,s.r,t#,p)
							t=0;p.x=0;p.y=0;p.z=0

							If pick
					
								' transform ray from world space to object space
								
								Local at:TVector=TVector.Create(0,0,0)
								Local bt:TVector=TVector.Create(0,0,0)
		
								TFormPoint a.x,a.y,a.z,Null,ent
								at.x=TFormedX()
								at.y=TFormedY()
								at.z=TFormedZ()
								
								TFormPoint b.x,b.y,b.z,Null,ent
								bt.x=TFormedX()
								bt.y=TFormedY()
								bt.z=TFormedZ()
				
								' check tri-ray collisions
								
								For Local s=1 To TMesh(ent).CountSurfaces()
								
									Local surf:TSurface=TMesh(ent).GetSurface(s)
									
									For Local tri=0 Until surf.CountTriangles()
									
										Local v0=surf.TriangleVertex(tri,0)
										Local v1=surf.TriangleVertex(tri,1)
										Local v2=surf.TriangleVertex(tri,2)
										
										c1.x=surf.VertexX#(v0)
										c1.y=surf.VertexY#(v0)
										c1.z=surf.VertexZ#(v0)
										
										c2.x=surf.VertexX#(v1)
										c2.y=surf.VertexY#(v1)
										c2.z=surf.VertexZ#(v1)
										
										c3.x=surf.VertexX#(v2)
										c3.y=surf.VertexY#(v2)
										c3.z=surf.VertexZ#(v2)
							
										Local pick=IntersectSegmentTriangle(at,bt,c1,c2,c3,u#,v#,w#,t#)
									
										If pick And t<=t_min
										
											t_min=t
											
											' convert barycentric coordinates to world coordinates
											p=c1.Multiply(u#).Add(c2.Multiply(v#)).Add(c3.Multiply(w#))
			
											picked_x#=p.x#
											picked_y#=p.y#
											picked_z#=p.z#
											
											picked_nx#=surf.TriangleNX(tri)
											picked_ny#=surf.TriangleNY(tri)
											picked_nz#=surf.TriangleNZ(tri)
											
											TEntity.TFormPoint(picked_x#,picked_y#,picked_z#,ent,Null)
											picked_x#=TEntity.TFormedX#()
											picked_y#=TEntity.TFormedY#()
											picked_z#=TEntity.TFormedZ#()
											
											TEntity.TFormNormal(picked_nx#,picked_ny#,picked_nz#,ent,Null)
											picked_nx#=TEntity.TFormedX#()
											picked_ny#=TEntity.TFormedY#()
											picked_nz#=TEntity.TFormedZ#()
											
											picked_time#=t#
											
											picked_ent=ent
											picked_surface=surf
											picked_triangle=tri
							
										EndIf
									
									Next
								
								Next
								
							EndIf
						
						EndIf
					
					EndIf
					
				EndIf
					
				If ent.pick_mode=3

					Local x#=ent.box_x + ent.EntityX#(True)
					Local y#=ent.box_y + ent.EntityY#(True)
					Local z#=ent.box_z + ent.EntityZ#(True)
					Local w#=ent.box_w
					Local h#=ent.box_h
					Local d#=ent.box_d 
				
					Local pick=IntersectSegmentAABB(a,b,x#,y#,z#,w#,h#,d#,t#,p)
					
					If pick And t<=t_min
					
						t_min=t
					
						picked_x#=p.x#
						picked_y#=p.y#
						picked_z#=p.z#
						
						Local nx#,ny#,nz#
						
						Local a#[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
			
				EndIf
				
			EndIf
		
		Next
		
		Return picked_ent

	End Function

	' called by TOctree->OctPick
	Function PickPolys(a:TVector,b:TVector,polys:TPolygon[],mesh:TMesh,t_min# Var)
	
		Local t#=0.0
		Local p:TVector=TVector.Create(0.0,0.0,0.0)
		
		Local c1:TVector=TVector.Create(0.0,0.0,0.0)
		Local c2:TVector=TVector.Create(0.0,0.0,0.0)
		Local c3:TVector=TVector.Create(0.0,0.0,0.0)
		Local u#
		Local v#
		Local w#
			
		' check tri-ray collisions
		
		Local poly_picked=False
		
		For Local poly:TPolygon=EachIn polys

			If poly.no_verts=3

				c1:TVector=poly.GetVertex(0)
				c2:TVector=poly.GetVertex(1)
				c3:TVector=poly.GetVertex(2)

				Local pick=IntersectSegmentTriangle(a,b,c1,c2,c3,u#,v#,w#,t#)

				If pick And t<=t_min
				
					poly_picked=True
				
					t_min=t
					
					' convert barycentric coordinates to world coordinates
					p=c1.Multiply(u#).Add(c2.Multiply(v#)).Add(c3.Multiply(w#))

					picked_x#=p.x#
					picked_y#=p.y#
					picked_z#=p.z#
					
					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#,mesh,Null)
					picked_x#=TEntity.TFormedX#()
					picked_y#=TEntity.TFormedY#()
					picked_z#=TEntity.TFormedZ#()
					
					TEntity.TFormNormal(picked_nx#,picked_ny#,picked_nz#,mesh,Null)
					picked_nx#=TEntity.TFormedX#()
					picked_ny#=TEntity.TFormedY#()
					picked_nz#=TEntity.TFormedZ#()
					
					picked_time#=t#
					
					picked_ent=mesh
					picked_surface=poly.surf
					picked_triangle=poly.tri
	
				EndIf
			
			EndIf
		
		Next
		
		Return poly_picked
		
	End Function
	
	Function IntersectSegmentSphere(p:TVector,q:TVector,sc:TVector,sr#,t# Var,q2:TVector Var)
	
		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#=m.Dot(d)
		Local c#=m.Dot(m) - (sr*sr)
		
		If c#>0.0 And b>0.0 Return 0
		Local discr#=(b*b)-c
		
		If discr#<0.0 Return 0
	
		t=-b-Sqr(discr)
		
		If(t<0.0) Then t=0.0
		q2=p.Add(d.Multiply(t))
		
		' segment check
		If t>pq.Length()
			Return 0
		Else
			t=t/pq.Length()
		EndIf
		
		Return 1
	
	End Function
	
	Function IntersectSegmentTriangle(p:TVector,q:TVector,a:TVector,b:TVector,c:TVector,u# Var,v# Var,w# Var,t# Var)

		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#=qp.Dot(n)
		If d#<=0.0 Return 0
		
		Local ap:TVector=New TVector
		ap=p.Subtract(a)
		t#=ap.Dot(n)
		If t#<0.0 Return 0
		If t#>d# Return 0
		
		Local e:TVector=New TVector
		e=qp.Cross(ap)
		v#=ac.Dot(e)
		If (v#<0.0 Or v#>d#) Return 0
		w#=-ab.Dot(e)
		If (w#<0.0 Or v#+w#>d#) Return 0
	
		Local ood#=1.0/d#
		t#:*ood
		v#:*ood
		w#:*ood
		u#=1.0-v#-w#
			
		Return 1		
	
	End Function
	
	Function IntersectSegmentAABB(p:TVector,q:TVector,x#,y#,z#,w#,h#,d#,tmin# Var,q2:TVector Var)

		tmin=0.0

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

		Local d1:TVector=TVector.Create(pq.x,pq.y,pq.z)
		d1.Normalize()
		
		Local amin#[]=[x,y,z]
		Local amax#[]=[x+w,y+h,z+d]
		
		Local p2#[]=[p.x,p.y,p.z]
		Local d2#[]=[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#=1.0/d2[i]
				Local t1#=(amin[i]-p2[i])*ood
				Local t2#=(amax[i]-p2[i])*ood
				
				If t1>t2
					Local swap#=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))
	
		tmin=tmin/pq.Length()
		
		Return 1
	
	End Function
	
End Type
