module Main where
import System.Exit ( exitWith, ExitCode(ExitSuccess) )
import Graphics.UI.GLUT
import Data.IORef ( IORef, newIORef, modifyIORef, readIORef, writeIORef )
-- import Topkata.World.Build
-- import Topkata.World.Base (wallInFrontOfMe)
-- import Topkata.World.Render (drawWorld, inWall)
import Topkata.Topka.Topka
import Data.Array ((!))
import Data.Maybe (isJust, listToMaybe, mapMaybe, maybeToList)
import Labygen
import Labygen.Render
import ReadImage (readImageWithSize)
import Control.Monad( sequence_, when )
import qualified Sound.ALUT.Initialization as ALUT
import Sound.ALUT.Loaders (createBuffer, SoundDataSource(File))
import Sound.OpenAL (openDevice, closeDevice)
import Sound.OpenAL.AL.Listener
import Sound.OpenAL.AL.Source
import Sound.ALUT (ALfloat)
import qualified Vector as V
--import Graphics.Rendering.OpenGL.GL.Polygons
data ViewMode = ExploringMode
| EgoMode
| FollowMode
data State = State {
topka :: IORef TopkaState,
camX :: IORef GLdouble,
camY :: IORef GLdouble,
camZ :: IORef GLdouble,
camPhi :: IORef GLdouble,
vmode :: IORef ViewMode,
laby :: World Pos3,
worldDL :: DisplayList
}
mkstate bt eyeTex = do
ts <- newIORef initialTopkaState
cX <- newIORef 0.31
cY <- newIORef 0.3
cZ <- newIORef (-0.11)
phi <- newIORef 0.0
vm <- newIORef FollowMode
sh <- newIORef False
laby <- labygenIO (origin, target) origin target
worldDL <- compileWorldDL bt laby
return $ (State { topka = ts, laby = laby, vmode = vm,
camX = cX, camY = cY, camZ = cZ, camPhi = phi,
worldDL = worldDL
}, eyeTex)
where origin = Pos3 (1, 1, 1)
target = Pos3 (30, 1, 30)
topkaUpd state f x =
topka state $~ \ ts ->
f ts x
viewData state = do
vm <- get (vmode state)
case vm of
EgoMode -> do
ts <- get (topka state)
return $ topView ts
ExploringMode -> do
camX <- get (camX state)
camY <- get (camY state)
camZ <- get (camZ state)
phi <- get (camPhi state)
return (camX, camY, camZ, phi)
FollowMode -> do
(x, y, z, phi) <- fmap topView $ get $ topka state
return (x, 3, z-2, 0)
{-
togglePrimMode state = do
primMode state $~ \ a ->
case a of
LineStrip -> QuadStrip
QuadStrip -> LineStrip
postRedisplay Nothing -}
{-
xTopMove = move xTrans
yTopMove = move yTrans
zTopMove = move zTrans
xTopRot = move xRot
yTopRot = move yRot
zTopRot = move zRot
openMouth = move mouth
xrot = xTopRot
yrot = yTopRot
zrot = zTopRot
-}
moveD a b c = move a b c >> postRedisplay Nothing
xmove = move camX
ymove = move camY
zmove = move camZ
move r state inc = do
r state $~ (+ inc)
get $ r state
camMove state d = do
phi <- get (camPhi state)
let dx = sin phi * (-d)
dz = cos phi * d
zmove state dz
xmove state dx
return ()
showHit = putStrLn "HIT!"
safeMove move state d = do
move state d
hit <- testCamHit state
if hit then do
showHit
move state (-d)
else return ()
postRedisplay Nothing
camTurn state d = move camPhi state d >> return ()
myInit = do
clearColor $= Color4 0 0 0 0
polygonMode $= (Fill, Fill)
shadeModel $= Smooth
materialSpecular FrontAndBack $= Color4 1 1 1 1
materialShininess FrontAndBack $= 25
colorMaterial $= Just (FrontAndBack, Diffuse)
position (Light 1) $= Vertex4 0 0.5 (-0.5) 1
position (Light 0) $= Vertex4 1 2 (-8) 0
lighting $= Enabled
light (Light 0) $= Enabled
light (Light 1) $= Enabled
depthFunc $= Just Less
ambient (Light 0) $= Color4 0.2 0.2 0.2 1.0
diffuse (Light 0) $= Color4 1 1 1 1
diffuse (Light 1) $= Color4 1 1 1 1
specular (Light 1) $= Color4 1 1 1 1
--position (Light 0) $= Vertex4 0 3 2 0
lightModelAmbient $= Color4 0.4 0.4 0.4 1
lightModelLocalViewer $= Enabled
lightModelTwoSide $= Enabled
-- blend $= Enabled
-- blendFunc $= (SrcAlpha, OneMinusSrcAlpha)
-- hint PolygonSmooth $= Nicest
--polygonSmooth $= Enabled
-- hint LineSmooth $= Nicest
-- lineSmooth $= Enabled
normalize $= Enabled
let brickSize = TextureSize2D 512 256
brickTex <- fmap listToMaybe $ genObjectNames 1
textureBinding Texture2D $= brickTex
textureWrapMode Texture2D S $= (Mirrored, Repeat)
textureWrapMode Texture2D T $= (Mirrored, Repeat)
textureFilter Texture2D $= ((Nearest, Just Nearest), Nearest)
sequence_ $ zipWith (mkTex "brick") [0..9] (reverse $ take 10 $ iterate (* 2) 1)
let eyeSize = TextureSize2D 256 256
eyeTex <- fmap listToMaybe $ genObjectNames 1
textureBinding Texture2D $= eyeTex
--textureFilter Texture2D $= ((Nearest, Just Nearest), Nearest)
textureWrapMode Texture2D S $= (Repeated, Clamp)
textureWrapMode Texture2D T $= (Repeated, Clamp)
textureFilter Texture2D $= ((Nearest, Nothing), Nearest)
--textureFilter Texture2D $= ((Nearest, Just Nearest), Nearest)
putStrLn $ show $ isJust eyeTex
mkTex "eyes" 0 256
mkstate brickTex eyeTex
mkTex prefix lvl xsize = do
let fname = prefix ++ show xsize ++ ".rgb"
let ysize = max 1 (xsize `div` 2)
let size = TextureSize2D xsize ysize
--putStrLn fname
(_, bricksData) <- readImageWithSize fname xsize ysize
texImage2D Nothing NoProxy lvl RGBA' size 0 bricksData
-- ring = mrect
mrect = [Vertex3 0.25 0.25 0.0,
Vertex3 0.25 0.75 (0.0 :: GLfloat),
Vertex3 0.75 0.25 0.0,
Vertex3 0.75 0.75 0.0]
processHits :: Maybe [HitRecord] -> IO ()
processHits Nothing = putStrLn "selection buffer overflow"
processHits (Just hitRecords) = do
putStrLn ("hits = " ++ show (length hitRecords))
mapM_ (\(HitRecord z1 z2 names) -> do
putStrLn (" number of names for hit = " ++ show (length names))
putStr (" z1 is " ++ show z1)
putStrLn ("; z2 is " ++ show z2)
putStr " the name is"
sequence_ [ putStr (" " ++ show n) | Name n <- names ]
putChar '\n')
hitRecords
testCamHit state = do
frustum (-0.2) 0.5 (-0.2) (0.5) (0.5) (20)
matrixMode $= Projection
loadIdentity
--ortho (-2) (2) (-2) 2 (-2) 2
frustum (-0.2) 0.5 (-0.2) (0.5) (0.49) (0.5)
--perspective 60 (fromIntegral w/fromIntegral h) 0.5 20
depthRange $= (0.45, 0.5)
matrixMode $= Modelview 0
loadIdentity
(x, y, z, phi) <- viewData state
lookAt (Vertex3 x y z) (Vertex3 (x+2*sin phi) (0.5) (z+2*cos phi)) (Vector3 0 1 0)
(_, maybeHitRecords) <- getHitRecords 128 $ do
withName (Name 0) $ preservingMatrix $ callList (worldDL state)
flush
-- processHits maybeHitRecords
case maybeHitRecords of
Just [] -> return False
_ -> return True
display state tops = do
(_, Size w h) <- get viewport
matrixMode $= Projection
loadIdentity
--ortho (-2) (2) (-2) 2 (-2) 2
hit <- return False -- get (showHit state)
frustum (-0.2) 0.5 (-0.2) (0.5) (if hit then 0.45 else 0.5) (if hit then 0.5 else 20)
--showHit state $= False
--perspective 60 (fromIntegral w/fromIntegral h) 0.5 20
depthRange $= (0.5, 20)
matrixMode $= Modelview 0
loadIdentity
clear [ ColorBuffer, DepthBuffer ]
loadIdentity
(x, y, z, phi) <- viewData state
--lookAt (Vertex3 x y z) (Vertex3 (x+2*sin phi) (0.5) (z+2*cos phi)) (Vector3 0 1 0)
lookAt (Vertex3 x y z) (Vertex3 (x+1) (0.5) (z+2*cos phi)) (Vector3 0 1 0)
listenerPosition $= Vertex3 (realToFrac x) (realToFrac y) (realToFrac z)
listenerVelocity $= Vector3 0 0 (0 :: ALfloat)
orientation $= (Vector3 0 0 1, Vector3 0 1 0)
preservingMatrix $ callList (worldDL state)
preservingMatrix $ do
ts <- get (topka state)
drawTop ts tops
flush
swapBuffers
reshape :: ReshapeCallback
reshape size@(Size w h) = do
viewport $= (Position 0 0, size)
keyboardMouse state src dev k Down _ _ = keyboard state k src dev
keyboardMouse _ src dev _ _ _ _ = return ()
myMotionCallback state pos = return () -- putStrLn ("pos=" ++ show pos)
syncCamWithTopkata state = do
(x, _, z, phi) <- viewData state
camX state $= x
camY state $= 0.3
camZ state $= z
camPhi state $= phi
switchMode state = do
syncCamWithTopkata state
vmode state $~ \ v ->
case v of
FollowMode -> EgoMode
EgoMode -> ExploringMode
ExploringMode -> FollowMode
camKeyboard state k =
case k of
-- Char 'f' -> zmove state (-0.1)
-- Char 'b' -> zmove state (0.2)
-- Char 'r' -> xTopMove state (-0.2)
SpecialKey KeyRight -> safeMove camTurn state (0.2)
-- Char 'l' -> xTopMove state (0.2)
SpecialKey KeyLeft -> safeMove camTurn state (-0.2)
-- Char 'd' -> yTopMove state (-0.2)
SpecialKey KeyDown -> safeMove camMove state (0.02)
-- Char 'u' -> yTopMove state (0.2)
SpecialKey KeyUp -> safeMove camMove state (-0.02)
{- Char 'x' -> xrot state (10)
Char 'X' -> xrot state (-10)
Char 'y' -> yrot state (10)
Char 'Y' -> yrot state (-10)
Char 'z' -> zrot state (10)
Char 'Z' -> zrot state (-10)
Char 'm' -> openMouth state (0.05)
Char 'M' -> openMouth state (-0.05)
Char '1' -> togglePrimMode state
Char ' ' -> yrot state 10 -}
_ -> return ()
topKeyboard state k =
case k of
SpecialKey KeyRight -> topkaUpd state updateOrientation rotCW
SpecialKey KeyLeft -> topkaUpd state updateOrientation rotCCW
SpecialKey KeyDown -> topkaUpd state updateSpeed (0.005-)
SpecialKey KeyUp -> topkaUpd state updateSpeed (0.005+)
_ -> return ()
followKeyboard state k =
case k of
SpecialKey KeyRight -> topkaUpd state setNextOrientation East
SpecialKey KeyLeft -> topkaUpd state setNextOrientation West
SpecialKey KeyDown -> topkaUpd state setNextOrientation South
SpecialKey KeyUp -> topkaUpd state setNextOrientation North
_ -> return ()
{-
turnTopkaSafe state ts x =
let ts' = turnTopka ts x in
if topInWall (laby state) ts'
then ts
else ts'
-}
keyboard state k src dev =
case k of
Char '\27' -> do
putStrLn "clean up"
deleteObjectNames [src]
mapM_ closeDevice $ maybeToList dev
exitWith ExitSuccess
Char '\t' -> switchMode state
_ -> do
vm <- get (vmode state)
case vm of
EgoMode -> topKeyboard state k
FollowMode -> followKeyboard state k
ExploringMode -> camKeyboard state k
keyboardMouse' a c d e f = do
-- putStrLn $ show c
keyboardMouse a c d e f
topInWall laby ts =
inWall laby (x+1+xd) (y+1) (z+1)
where Vector3 x y z = transVec ts
phi = topPhi ts
sgn = sign (speed ts)
zd = 0.3 * cos phi * sgn
xd = 0.3 * sin phi * sgn
sign x | x > 0 = 1
sign x | x < 0 = -1
sign x = 0
hitGroundSnd src ts ts' = do
--putStrLn $ "ts: " ++ show (yspeed ts) ++ ", ts'= " ++ show (yspeed ts')
when (yspeed ts < 0 && yspeed ts' > 0) $ do
putStrLn "boing"
play [src]
animate src state = do
ts <- readIORef (topka state)
when (not ( topInWall lab ts)) $ do
let ts' = animateTop mstime ts
if (topInWall lab ts')
then topkaUpd state updateOrientation flipOrientation
else do writeIORef (topka state) ts'
hitGroundSnd src ts ts'
addTimerCallback mstime $ animate src state
postRedisplay Nothing
where mstime = 10
lab = laby state
compileWorldDL brickTex laby = defineNewList Compile $ render brickTex laby -- drawWorld world (1, 1, 1)
runMain prog name = do
putStrLn "Topkata..."
dev <- openDevice (Just "'( ( devices '( native null ) ) )")
boing <- createBuffer $ File "boing_1.wav"
--putStrLn (show $ world ! (1, 1, 3) )
getArgsAndInitialize
initialDisplayMode $= [ DoubleBuffered, RGBMode, WithDepthBuffer ]
initialWindowSize $= Size 800 600
initialWindowPosition $= Position 100 100
createWindow "Topkata"
(state, eyeTex) <- myInit
tops <- genTopkataCalllist eyeTex
[sndSrc] <- genObjectNames 1 :: IO [Source]
displayCallback $= display state tops
keyboardMouseCallback $= Just (keyboardMouse state sndSrc dev)
passiveMotionCallback $= Just (myMotionCallback state)
reshapeCallback $= Just reshape
--sndSrc :: Source
pitch sndSrc $= 1.0
--gain sndSrc $= 1.0
--position sndSrc $= Vertex3 0 0 0
--velocity sndSrc $= Vector3 0 0 0
buffer sndSrc $= Just boing
loopingMode sndSrc $= OneShot
addTimerCallback 100 $ animate sndSrc state
deleteObjectNames [boing]
mainLoop
main = ALUT.withProgNameAndArgs ALUT.runALUT runMain