;=======================================================================================================
; GeoWall Stereo Library for Blitz3D
; (C) 2004 Jason Leigh, Electronic Visualization Laboratory
; Created: 5/15/2004
; Version: 8/15/2004
;
; This is a set of APIs to enable GeoWall-style (ie twinview side-by-side) stereo for Blitz3D.
; Note that in this implementation the LEFT eye is assumed to be the lead camera. The RIGHT eye view
; is created by offseting from the lead camera.
 
; If you have comments, questions, improvements please post a message on: www.evl.uic.edu/cavern/forum
; For further information about the GeoWall see www.geowall.org
; This code is fully freeware. By using this you take sole responsibility for any damages
; that may result from its use, misuse Or abuse.

; Version: 7/3/2004
; Added SL_SwitchToMono and SL_SwitchToStereo to allow an application to switch on the fly.
;
; Version: 8/15/2004
; Added eye separation as a parameter that the library stores for itself. Also added accessor functions and
; eliminated the need to specify eye separation in SL_Update()
; ======================================================================================================

Global g_slCameraL, g_slCameraR, g_slPivot
Global g_screenXSize = 1024
Global g_screenYSize = 768

Global minimode = False

Function SL_InitDisplayMode()

	Print "Choose Display Mode:"
	Print "1 - Standard GeoWall Stereo (2048x768)"
	Print "2 - Desktop Stereo (1280x1024)"
	Print "3 - Desktop Stereo (1024x768)"
	Print "4 - Desktop Stereo (640x480x16)"
	Select WaitKey()
	Case 49		; '1'
		g_screenXSize=2048
		g_screenYSize=768
	Case 50		; '2'
		g_screenXSize=1280
		g_screenYSize=1024
	Case 51		; '3'
		g_screenXSize=1024
		g_screenYSize=768
	Case 52		; '4'
		g_screenXSize=640
		g_screenYSize=480
		colordepth=16
	Case 53		; '5'
		g_screenXSize=1024
		g_screenYSize=768
		minimode = True	
	Default
		End		; quit
	End Select
	Graphics3D g_screenXSize, g_screenYSize, colordepth ;, 1;=fullscreen
	SetBuffer BackBuffer()

End Function

; ***********************************************************************

; Use this to automatically create 2 cameras.
; If you already create your own cameras you should call:
;		SL_SetLeftCamera(); SL_RightCamera() and then SL_SetUpCamera()
Function SL_CreateStereoCameras()
	g_slPivot = CreatePivot()
	SL_CreateLeftCamera(g_slPivot)
	SL_CreateRightCamera(g_slPivot)
	
	PositionEntity g_slCameraL,0,0,0
	PositionEntity g_slCameraR,0,0,0
	
	;PositionEntity g_slCameraL,-g_slEyeSeparation/2.0,0,0
	;PositionEntity g_slCameraR, g_slEyeSeparation/2.0,0,0
	
	Return g_slPivot
End Function

; Accessor Functions --------------------
Function SL_SetLeftCamera(camera,pivot=0)
	g_slCameraL=camera
	EntityParent camera,pivot
End Function

Function SL_SetRightCamera(camera, pivot=0)
	g_slCameraR=camera
	EntityParent camera,pivot
End Function

Function SL_CreateLeftCamera(pivot=0)
	g_slCameraL=CreateCamera()
	EntityParent g_slCameraL,pivot
	Return g_slCameraL
End Function

Function SL_CreateRightCamera(pivot=0)
	g_slCameraR=CreateCamera()
	EntityParent g_slCameraR,pivot
	Return g_slCameraR
End Function

Function SL_GetRightCamera()
	Return g_slCameraR
End Function

Function SL_GetLeftCamera()
	Return g_slCameraL
End Function

Function SL_SetEyeSeparation(eye#)
	g_slEyeSeparation# = eye#
End Function

Function SL_GetEyeSeparation()
	Return g_slEyeSeparation#
End Function

Function SL_SetCameraFOV(FOV#)
   CameraZoom g_slCameraL, 1.0 / Tan(FOV#/2.0)
   CameraZoom g_slCameraR, 1.0 / Tan(FOV#/2.0)
End Function

;----------------------------------------

Function WhereIs$(entity)
	;x# = EntityX(entity, True) : y# = EntityY(entity, True) : z# = EntityZ(entity, True)
	TFormPoint 0,0,0,entity,0
	;Return "(" + x + ", " + y + ", " + z + ")"
	Return "(" + TFormedX() + ", " + TFormedY() + ", " + TFormedZ() + ")"
End Function

Function DebugCameras()
	DebugLog "L cam at " + WhereIs$(g_slCameraL) + " R cam at " + WhereIs$(g_slCameraR)
End Function

; Set up stereo cameras (ie their viewports)
Function SL_SetUpCameras()
	CameraViewport g_slCameraR,GraphicsWidth()/2,0,GraphicsWidth()/2,GraphicsHeight()
	CameraViewport g_slCameraL,0,0,GraphicsWidth()/2,GraphicsHeight()
	ViewX=GraphicsWidth()/2 : ViewY=GraphicsHeight()
	ViewAspect# = Float(viewx)/Float(viewy)
End Function

; Switch to monoscopic display mode
Function SL_SwitchToMono()
	CameraViewport g_slCameraL,0,0,GraphicsWidth(), GraphicsHeight()
	CameraViewport g_slCameraR,0,0,0,0
	ViewX=GraphicsWidth() : ViewY=GraphicsHeight()
	ViewAspect# = Float(viewx)/Float(viewy)
End Function

; Switch to stereoscopic display mode
Function SL_SwitchToStereo()
	SL_SetUpCameras()
	ViewX=GraphicsWidth()/2 : ViewY=GraphicsHeight()
	ViewAspect# = Float(viewx)/Float(viewy)
End Function

; Switch to orthogonal projection for 2D rendering of 3D objects
Function SL_OrtProj()
	CameraProjMode g_slCameraL,2
	CameraProjMode g_slCameraR,2
	SL_SetCameraFOV(0.1)
	eye# = SL_GetEyeSeparation()
	SL_Update(needtoknowthetweenvalue#)
End Function

; Switch back to perspective camera mode
Function SL_RestoreProj()
	CameraProjMode g_slCameraL,1
	CameraProjMode g_slCameraR,1
	SL_SetCameraFOV(1.0)
End Function

; Update stereo cameras.
; Stereo eye separation is +ve For transverse viewing, And -ve For crosseye viewing.
Function SL_Update(tween#)
	realsep# = g_slEyeSeparation
	Select archer\state
		Case AT_SURFACE
			realsep# = (archer\loc\radius * plrscl) / 100.0
		Case AT_ARROW
			realsep# = (archer\loc\radius * plrscl) / 100.0
		Case AT_CORE
			realsep# = (archer\loc\radius * plrscl) / 100.0
		Case AT_ORBIT
			realsep# = (archer\loc\radius) / 100.0
	End Select

	pitch#=EntityPitch#(g_slCameraL,True)	; must use global='True' to account for parenting
	roll#=EntityRoll#(g_slCameraL,True)		; see above
	yaw#=EntityYaw#(g_slCameraL,True)		; see above
	x#=EntityX#(g_slCameraL,True)
	y#=EntityY#(g_slCameraL,True)
	z#=EntityZ#(g_slCameraL,True)
	PositionEntity g_slCameraR,x#,y#,z#,True
	RotateEntity g_slCameraR,pitch#,yaw#,roll#,True
	TurnEntity g_slCameraR,0,-90,0
	MoveEntity g_slCameraR,0,0,realsep
	TurnEntity g_slCameraR,0,90,0
	
	realsep# = g_slEyeSeparation
	Select archer\state
		Case AT_SURFACE
			realsep# = (archer\loc\radius * plrscl) / 200.0
		Case AT_ARROW
			realsep# = (archer\loc\radius * plrscl) / 200.0
		Case AT_CORE
			realsep# = (archer\loc\radius * plrscl) / 200.0
		Case AT_ORBIT
			realsep# = (archer\loc\radius) / 200.0
		Case AT_SPACE
			realsep# = 1.0
	End Select

	realsep# = g_slEyeSeparation
	DebugLog "Eyesep: " + g_slEyeSeparation
	
	;PositionEntity g_slCameraL,0,0,0 : PositionEntity g_slCameraR,0,0,0
	;PositionEntity g_slCameraL,-realsep#/2.0,0,0
	;PositionEntity g_slCameraR, realsep#/2.0,0,0
	
	;CameraProjMode g_slCameraR,0	; disable right camera
	RenderWorld tween#				; render for left camera
	;Text 20,40,"Triangles Rendered: "+TrisRendered()
	;CameraProjMode g_slCameraL,0	; disable left camera
	;CameraProjMode g_slCameraR,1	; enable right camera in perspective projection mode
	;RenderWorld tween#				; render for right camera
	;Text GraphicsWidth()/2 + 20,40,"Triangles Rendered: "+TrisRendered()
	;CameraProjMode g_slCameraL,1	; enable left camera in perspective projection mode
End Function

; Set up the field of view to produce "accurate" stereo given the physical screen dimensions
; and the distance of the viewer to the screen.
; 
; This function is really only useful if all your objects in the world conform to a scaling
; convention that is the same as the real world- ie. 1 unit of distance = 1 foot (or 1 meter).
; If you do follow this convention then a 1 foot square will measure to be 1 foot when projected
; at the plane of the screen, and all you need to do is to use the actual eye separation of the
; viewer in the SL_Update() function.
; As I said this is only really useful if you conform to a uniform scaling convention. If you
; are trying to put stereo into an existing game, I recommend you ignore this function and just
; play with different eye separation values until you get a comfortable display.
;
; Final Note for the stereo techies: this is NOT perfectly accurate stereo because
; I cannot create an off-axis projected view frustrum with Blitz3D. But this is pretty close.
Function SL_SetUpCorrectScaleStereo(screenWidth#, screenHeight#, distanceToScreen#)
	stereofov# = 2.0*ATan(screenWidth#/(2.0*distanceToScreen#))
	
	; Set camera fov
	CameraZoom g_slCameraL,1.0/Tan(stereofov#/2.0)
	CameraZoom g_slCameraR,1.0/Tan(stereofov#/2.0)
	
	; Ignore the code below. They belong to the DarkBASIC version of this code.
	
;	set camera fov leftCam,stereofov#*2.0
;	set camera fov rightCam, stereofov#*2.0
	
;	` Set the screen's correct aspect ratio so that a square looks like a square.
;	sx#=screenMaxX
;	sy#=screenMaxY
;	stereoaspect# = (sx#/2.0)/sy#
	
;	set camera aspect leftCam, stereoaspect#
;set camera aspect rightCam, stereoaspect#
End Function