module Utils where import Control.Applicative import Control.Monad import Control.Monad.Trans import Data.IORef import FRP.Elerea.Param import Graphics.UI.GLFW as GLFW import Graphics.LambdaCube as LC -- Reactive helper functions integral :: (Real p, Fractional t) => t -> Signal t -> SignalGen p (Signal t) integral v0 s = transfer v0 (\dt v v0 -> v0+v*realToFrac dt) s driveNetwork :: (MonadIO m) => (p -> IO (m a)) -> IO (Maybe p) -> m () driveNetwork network driver = do dt <- liftIO driver case dt of Just dt -> do join . liftIO $ network dt driveNetwork network driver Nothing -> return () -- OpenGL/GLFW boilerplate initCommon :: String -> IO (Signal (Int, Int)) initCommon title = do initialize openWindow defaultDisplayOptions { displayOptions_numRedBits = 8 , displayOptions_numGreenBits = 8 , displayOptions_numBlueBits = 8 , displayOptions_numDepthBits = 24 , displayOptions_width = 1280 , displayOptions_height = 720 -- , displayOptions_displayMode = Fullscreen } setWindowTitle title (windowSize,windowSizeSink) <- external (0,0) setWindowSizeCallback $ \w h -> windowSizeSink (fromIntegral w, fromIntegral h) return windowSize -- FPS tracking data State = State { frames :: IORef Int, t0 :: IORef Double } fpsState :: IO State fpsState = State <$> newIORef 0 <*> newIORef 0 updateFPS :: State -> Double -> IO () updateFPS state t1 = do let t = 1000*t1 fR = frames state tR = t0 state modifyIORef fR (+1) t0' <- readIORef tR writeIORef tR $ t0' + t when (t + t0' >= 5000) $ do f <- readIORef fR let seconds = (t + t0') / 1000 fps = fromIntegral f / seconds putStrLn (show f ++ " frames in " ++ show seconds ++ " seconds = "++ show fps ++ " FPS") writeIORef tR 0 writeIORef fR 0 -- Continuous camera state (rotated with mouse, moved with arrows) userCamera :: Real p => Vec3 -> Signal (FloatType, FloatType) -> Signal (Bool, Bool, Bool, Bool, Bool) -> SignalGen p (Signal (Vec3, Vec3, Vec3, (FloatType, FloatType))) userCamera p mposs keyss = transfer2 (p,zero,zero,(0,0)) calcCam mposs keyss where d0 = Vec4 0 0 (-1) 1 u0 = Vec4 0 1 0 1 calcCam dt (dmx,dmy) (ka,kw,ks,kd,turbo) (p0,_,_,(mx,my)) = (p',d,u,(mx',my')) where f0 c n = if c then (&+ n) else id p' = foldr1 (.) [f0 ka (v &* (-t)),f0 kw (d &* t),f0 ks (d &* (-t)),f0 kd (v &* t)] p0 k = if turbo then 100 else 30 t = k * realToFrac dt mx' = dmx + mx my' = dmy + my rm = fromProjective $ rotationEuler $ Vec3 (mx' / 100) (my' / 100) 0 d = trim $ rm *. d0 :: Vec3 u = trim $ rm *. u0 :: Vec3 v = LC.normalize $ d &^ u followCamera :: FloatType -> FloatType -> FloatType -> Signal Proj4 -> SignalGen p (Signal (Vec3, Vec3)) followCamera height minDist maxDist target = transfer (Vec3 (-maxDist) height 0, Vec3 1 0 0) follow target where follow _dt tproj (pos,_dir) = (pos',tpos &- pos') where Mat4 _ _ _ tpos4 = fromProjective (tproj .*. translation (Vec3 0 height 0)) tpos = trim tpos4 tdir = tpos &- pos dist = len tdir pos' | dist < minDist = pos &+ (normalize tdir &* (dist-minDist)) | dist > maxDist = pos &+ (normalize tdir &* (dist-maxDist)) | otherwise = pos