2014-01-05 19:09:01 +01:00
|
|
|
{-# LANGUAGE BangPatterns #-}
|
2014-01-20 14:12:02 +01:00
|
|
|
module Main where
|
2014-01-03 03:01:54 +01:00
|
|
|
|
2014-01-20 14:12:02 +01:00
|
|
|
-- Monad-foo
|
|
|
|
import Control.Applicative
|
|
|
|
import Control.Monad (unless, void, when)
|
|
|
|
import Control.Monad.Trans.Maybe (MaybeT (..), runMaybeT)
|
|
|
|
-- data consistency
|
2014-01-05 20:23:22 +01:00
|
|
|
import Control.Concurrent.STM (TQueue, atomically,
|
|
|
|
newTQueueIO,
|
|
|
|
tryReadTQueue,
|
|
|
|
writeTQueue)
|
|
|
|
import Control.Monad.RWS.Strict (RWST, ask, asks,
|
|
|
|
evalRWST, get, liftIO,
|
|
|
|
modify, put)
|
2014-01-20 14:12:02 +01:00
|
|
|
-- FFI
|
2014-01-05 20:23:22 +01:00
|
|
|
import Foreign (Ptr, castPtr, with)
|
|
|
|
import Foreign.C (CFloat)
|
2014-01-20 14:12:02 +01:00
|
|
|
|
|
|
|
-- Math
|
|
|
|
import Control.Lens (transposeOf, (^.))
|
2014-01-05 20:23:22 +01:00
|
|
|
import Linear as L
|
|
|
|
|
2014-01-20 14:12:02 +01:00
|
|
|
-- GUI
|
2014-01-20 16:11:34 +01:00
|
|
|
import Graphics.UI.SDL as SDL
|
2014-01-20 14:12:02 +01:00
|
|
|
|
|
|
|
-- Render
|
2014-01-05 20:23:22 +01:00
|
|
|
import qualified Graphics.Rendering.OpenGL.GL as GL
|
|
|
|
import Graphics.Rendering.OpenGL.Raw.Core31
|
|
|
|
|
2014-01-20 14:12:02 +01:00
|
|
|
-- Our modules
|
2014-01-05 20:23:22 +01:00
|
|
|
import Map.Map
|
|
|
|
import Render.Misc (checkError,
|
|
|
|
createFrustum, getCam,
|
|
|
|
lookAt, up)
|
|
|
|
import Render.Render (initRendering,
|
|
|
|
initShader)
|
2014-01-03 03:01:54 +01:00
|
|
|
|
2014-01-20 16:11:34 +01:00
|
|
|
--Static Read-Only-State
|
|
|
|
data Env = Env
|
|
|
|
{ envEventsChan :: TQueue Event
|
|
|
|
, envWindow :: !Window
|
|
|
|
, envZDistClosest :: !Double
|
|
|
|
, envZDistFarthest :: !Double
|
|
|
|
}
|
2013-12-29 18:18:18 +01:00
|
|
|
|
2014-01-20 16:11:34 +01:00
|
|
|
--Mutable State
|
|
|
|
data State = State
|
|
|
|
{ stateWindowWidth :: !Int
|
|
|
|
, stateWindowHeight :: !Int
|
|
|
|
, stateWinClose :: !Bool
|
|
|
|
--- IO
|
|
|
|
, stateXAngle :: !Double
|
|
|
|
, stateYAngle :: !Double
|
|
|
|
, stateZDist :: !Double
|
|
|
|
, stateMouseDown :: !Bool
|
|
|
|
, stateDragging :: !Bool
|
|
|
|
, stateDragStartX :: !Double
|
|
|
|
, stateDragStartY :: !Double
|
|
|
|
, stateDragStartXAngle :: !Double
|
|
|
|
, stateDragStartYAngle :: !Double
|
|
|
|
, statePositionX :: !Double
|
|
|
|
, statePositionY :: !Double
|
|
|
|
, stateFrustum :: !(M44 CFloat)
|
|
|
|
--- pointer to bindings for locations inside the compiled shader
|
|
|
|
--- mutable because shaders may be changed in the future.
|
|
|
|
, shdrVertexIndex :: !GL.AttribLocation
|
|
|
|
, shdrColorIndex :: !GL.AttribLocation
|
|
|
|
, shdrNormalIndex :: !GL.AttribLocation
|
|
|
|
, shdrProjMatIndex :: !GL.UniformLocation
|
|
|
|
, shdrViewMatIndex :: !GL.UniformLocation
|
|
|
|
, shdrModelMatIndex :: !GL.UniformLocation
|
|
|
|
, shdrNormalMatIndex :: !GL.UniformLocation
|
|
|
|
--- the map
|
|
|
|
, stateMap :: !GL.BufferObject
|
|
|
|
, mapVert :: !GL.NumArrayIndices
|
|
|
|
}
|
|
|
|
|
|
|
|
type Pioneers = RWST Env () State IO
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
2013-12-22 23:29:11 +01:00
|
|
|
main :: IO ()
|
2014-01-20 16:11:34 +01:00
|
|
|
main = do
|
|
|
|
SDL.withInit [InitEverything] $ do --also: InitNoParachute -> faster, without parachute!
|
|
|
|
window <- SDL.createWindow "Pioneers" (Position 100 100) (Size 1024 768) [WindowOpengl -- we want openGL
|
|
|
|
,WindowShown -- window should be visible
|
|
|
|
,WindowResizable -- and resizable
|
|
|
|
,WindowInputFocus -- focused (=> active)
|
|
|
|
,WindowMouseFocus -- Mouse into it
|
|
|
|
--,WindowInputGrabbed-- never let go of input (KB/Mouse)
|
|
|
|
]
|
|
|
|
|
|
|
|
(Size fbWidth fbHeight) <- glGetDrawableSize window
|
|
|
|
initRendering
|
|
|
|
--generate map vertices
|
|
|
|
(mapBuffer, vert) <- getMapBufferObject
|
|
|
|
(ci, ni, vi, pri, vii, mi, nmi) <- initShader
|
|
|
|
eventQueue <- newTQueueIO :: IO (TQueue Event)
|
|
|
|
|
|
|
|
let zDistClosest = 10
|
|
|
|
zDistFarthest = zDistClosest + 20
|
|
|
|
fov = 90 --field of view
|
|
|
|
near = 1 --near plane
|
|
|
|
far = 100 --far plane
|
|
|
|
ratio = fromIntegral fbWidth / fromIntegral fbHeight
|
|
|
|
frust = createFrustum fov near far ratio
|
|
|
|
env = Env
|
|
|
|
{ envEventsChan = eventQueue
|
|
|
|
, envWindow = window
|
|
|
|
, envZDistClosest = zDistClosest
|
|
|
|
, envZDistFarthest = zDistFarthest
|
|
|
|
}
|
|
|
|
state = State
|
|
|
|
{ stateWindowWidth = fbWidth
|
|
|
|
, stateWindowHeight = fbHeight
|
|
|
|
, stateXAngle = pi/6
|
|
|
|
, stateYAngle = pi/2
|
|
|
|
, stateZDist = 10
|
|
|
|
, statePositionX = 5
|
|
|
|
, statePositionY = 5
|
|
|
|
, stateMouseDown = False
|
|
|
|
, stateDragging = False
|
|
|
|
, stateDragStartX = 0
|
|
|
|
, stateDragStartY = 0
|
|
|
|
, stateDragStartXAngle = 0
|
|
|
|
, stateDragStartYAngle = 0
|
|
|
|
, shdrVertexIndex = vi
|
|
|
|
, shdrNormalIndex = ni
|
|
|
|
, shdrColorIndex = ci
|
|
|
|
, shdrProjMatIndex = pri
|
|
|
|
, shdrViewMatIndex = vii
|
|
|
|
, shdrModelMatIndex = mi
|
|
|
|
, shdrNormalMatIndex = nmi
|
|
|
|
, stateMap = mapBuffer
|
|
|
|
, mapVert = vert
|
|
|
|
, stateFrustum = frust
|
|
|
|
, stateWinClose = False
|
|
|
|
}
|
|
|
|
void $ evalRWST (adjustWindow >> run) env state
|
|
|
|
|
|
|
|
destroyWindow window
|
|
|
|
|
|
|
|
-- Main game loop
|
|
|
|
|
|
|
|
run :: Pioneers ()
|
|
|
|
run = do
|
|
|
|
win <- asks envWindow
|
|
|
|
events <- asks envEventsChan
|
|
|
|
|
|
|
|
-- draw Scene
|
|
|
|
--draw
|
|
|
|
liftIO $ do
|
|
|
|
glSwapWindow win
|
|
|
|
submitEvents events
|
|
|
|
-- getEvents & process
|
|
|
|
processEvents
|
|
|
|
|
|
|
|
-- update State
|
|
|
|
|
|
|
|
state <- get
|
|
|
|
-- change in camera-angle
|
|
|
|
{- if stateDragging state
|
|
|
|
then do
|
|
|
|
let sodx = stateDragStartX state
|
|
|
|
sody = stateDragStartY state
|
|
|
|
sodxa = stateDragStartXAngle state
|
|
|
|
sodya = stateDragStartYAngle state
|
|
|
|
(x, y) <- liftIO $ GLFW.getCursorPos win
|
|
|
|
let myrot = (x - sodx) / 2
|
|
|
|
mxrot = (y - sody) / 2
|
|
|
|
newXAngle = curb (pi/12) (0.45*pi) newXAngle'
|
|
|
|
newXAngle' = sodxa + mxrot/100
|
|
|
|
newYAngle
|
|
|
|
| newYAngle' > pi = newYAngle' - 2 * pi
|
|
|
|
| newYAngle' < (-pi) = newYAngle' + 2 * pi
|
|
|
|
| otherwise = newYAngle'
|
|
|
|
newYAngle' = sodya + myrot/100
|
|
|
|
put $ state
|
|
|
|
{ stateXAngle = newXAngle
|
|
|
|
, stateYAngle = newYAngle
|
|
|
|
}
|
|
|
|
-- liftIO $ putStrLn $ unwords $ map show $ [newXAngle, newYAngle]
|
|
|
|
else do
|
|
|
|
(jxrot, jyrot) <- liftIO $ getJoystickDirections GLFW.Joystick'1
|
|
|
|
put $ state
|
|
|
|
{ stateXAngle = stateXAngle state + (2 * jxrot)
|
|
|
|
, stateYAngle = stateYAngle state + (2 * jyrot)
|
|
|
|
}
|
|
|
|
|
|
|
|
-- get cursor-keys - if pressed
|
|
|
|
--TODO: Add sin/cos from stateYAngle
|
|
|
|
(kxrot, kyrot) <- liftIO $ getCursorKeyDirections win
|
|
|
|
modify $ \s ->
|
|
|
|
let
|
|
|
|
multc = cos $ stateYAngle s
|
|
|
|
mults = sin $ stateYAngle s
|
|
|
|
in
|
|
|
|
s {
|
|
|
|
statePositionX = statePositionX s - 0.2 * kxrot * multc
|
|
|
|
- 0.2 * kyrot * mults
|
|
|
|
, statePositionY = statePositionY s + 0.2 * kxrot * mults
|
|
|
|
- 0.2 * kyrot * multc
|
|
|
|
}
|
|
|
|
-}
|
|
|
|
{-
|
|
|
|
--modify the state with all that happened in mt time.
|
|
|
|
mt <- liftIO GLFW.getTime
|
|
|
|
modify $ \s -> s
|
|
|
|
{
|
|
|
|
}
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
|
|
unless (stateWinClose state) run
|
|
|
|
|
|
|
|
adjustWindow :: Pioneers ()
|
|
|
|
adjustWindow = do
|
|
|
|
state <- get
|
|
|
|
let fbWidth = stateWindowWidth state
|
|
|
|
fbHeight = stateWindowHeight state
|
|
|
|
fov = 90 --field of view
|
|
|
|
near = 1 --near plane
|
|
|
|
far = 100 --far plane
|
|
|
|
ratio = fromIntegral fbWidth / fromIntegral fbHeight
|
|
|
|
frust = createFrustum fov near far ratio
|
|
|
|
liftIO $ glViewport 0 0 (fromIntegral fbWidth) (fromIntegral fbHeight)
|
|
|
|
put $ state {
|
|
|
|
stateFrustum = frust
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-- | Writes all Events atomically to global Queue for further processing.
|
|
|
|
submitEvents :: TQueue Event -> IO ()
|
|
|
|
submitEvents q = do
|
|
|
|
event <- pollEvent
|
|
|
|
case event of
|
|
|
|
Nothing -> return ()
|
|
|
|
Just e -> do
|
|
|
|
atomically $ writeTQueue q e
|
|
|
|
submitEvents q
|
2014-01-03 03:01:54 +01:00
|
|
|
|
2014-01-20 16:11:34 +01:00
|
|
|
processEvents :: Pioneers ()
|
|
|
|
processEvents = do
|
|
|
|
return ()
|